code cleanup, added comments, refactoring
This commit is contained in:
parent
2ee705a6ff
commit
7008c6e55a
159
minebash
159
minebash
@ -1,5 +1,5 @@
|
|||||||
#!/usr/bin/env python3
|
#!/usr/bin/env python3
|
||||||
#TODO: highscore
|
#TODO: highscore, monochrome, expect ctrl c, flags red if too many
|
||||||
import random, io, sys, time, os
|
import random, io, sys, time, os
|
||||||
|
|
||||||
import curses
|
import curses
|
||||||
@ -12,6 +12,8 @@ UNKNOWN = -3
|
|||||||
FLAG_MINE = -4
|
FLAG_MINE = -4
|
||||||
|
|
||||||
score_x, score_y = 0, 0
|
score_x, score_y = 0, 0
|
||||||
|
CURSOR_POSITION=[0,0]
|
||||||
|
|
||||||
OFFSET = 11
|
OFFSET = 11
|
||||||
|
|
||||||
STARTTIME = 0
|
STARTTIME = 0
|
||||||
@ -21,13 +23,14 @@ SCREEN = 0
|
|||||||
firstmove = False
|
firstmove = False
|
||||||
FIELD_GENERATED = False
|
FIELD_GENERATED = False
|
||||||
|
|
||||||
CURSOR_POSITION=[0,0]
|
|
||||||
FIELDS_CLEARED = 0
|
FIELDS_CLEARED = 0
|
||||||
|
|
||||||
width, height = 9, 9
|
width, height = 9, 9
|
||||||
MINECOUNT = 10
|
MINECOUNT = 10
|
||||||
FLAGCOUNT = 0
|
FLAGCOUNT = 0
|
||||||
|
|
||||||
|
clWhite, clRed, clCyan, clBlue, clYellow, clGreen, clInverted = None, None, None, None, None, None, None #can only be setup after curses has loaded
|
||||||
|
|
||||||
difficulty = 'medium'
|
difficulty = 'medium'
|
||||||
|
|
||||||
param_error = 'minebash: Invalid parameters. See \'minebash.py ?\' for help '
|
param_error = 'minebash: Invalid parameters. See \'minebash.py ?\' for help '
|
||||||
@ -81,17 +84,13 @@ if len(sys.argv) > 1:
|
|||||||
print(param_error)
|
print(param_error)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
playfield = [[UNKNOWN for x in range(width)] for y in range(height)]
|
playfield = [[UNKNOWN for x in range(width)] for y in range(height)]
|
||||||
#playfield = 0
|
|
||||||
|
|
||||||
#stdscr = curses.initscr()
|
|
||||||
#curses.noecho()
|
|
||||||
#curses.cbreak()
|
|
||||||
headline = '┌'
|
headline = '┌'
|
||||||
midline = '├'
|
midline = '├'
|
||||||
tailline = '└'
|
tailline = '└'
|
||||||
|
|
||||||
def setup_strings(colcount):
|
def setup_strings(colcount):
|
||||||
#setup lines to print
|
#setup lines to print at the end and start of the matrix, aswell as between rows
|
||||||
global headline, midline, tailline
|
global headline, midline, tailline
|
||||||
for i in range(colcount):
|
for i in range(colcount):
|
||||||
if i == width-1:
|
if i == width-1:
|
||||||
@ -111,25 +110,27 @@ def endgame(msg=''):
|
|||||||
def calculate_hint(col, row):
|
def calculate_hint(col, row):
|
||||||
hint = 0
|
hint = 0
|
||||||
if playfield[row][col] != MINE:
|
if playfield[row][col] != MINE:
|
||||||
|
#step through the surrounding 8 (or less) fields
|
||||||
for x in range(col-1, col+2):
|
for x in range(col-1, col+2):
|
||||||
if x >= 0 and x < len(playfield):
|
if x >= 0 and x < len(playfield):
|
||||||
for y in range(row-1, row+2):
|
for y in range(row-1, row+2):
|
||||||
if y >= 0 and y < len(playfield[0]):
|
if y >= 0 and y < len(playfield[0]):
|
||||||
if playfield[y][x] == MINE or playfield[y][x] == FLAG_MINE:
|
if playfield[y][x] == MINE or playfield[y][x] == FLAG_MINE:
|
||||||
|
#add the mines together
|
||||||
hint+=1
|
hint+=1
|
||||||
else:
|
else:
|
||||||
hint = MINE
|
hint = MINE
|
||||||
return hint
|
return hint
|
||||||
|
|
||||||
def setup_playfield(w, h, x, y):
|
def setup_playfield(w, h, x, y):
|
||||||
#do this only once [AFTER THE FIRST GUESS] -> Done
|
|
||||||
#randomly distribute mines across the field
|
|
||||||
global playfield, FIELD_GENERATED, STARTTIME
|
global playfield, FIELD_GENERATED, STARTTIME
|
||||||
|
|
||||||
minesleft = MINECOUNT
|
minesleft = MINECOUNT
|
||||||
|
#randomly distribute mines across the field
|
||||||
while minesleft > 0:
|
while minesleft > 0:
|
||||||
randx = random.randint(0, width-1)
|
randx = random.randint(0, width-1)
|
||||||
randy = random.randint(0, height-1)
|
randy = random.randint(0, height-1)
|
||||||
|
#make sure the users first guess isn't a mine
|
||||||
if playfield[randy][randx] != MINE and ([randx, randy] != CURSOR_POSITION):
|
if playfield[randy][randx] != MINE and ([randx, randy] != CURSOR_POSITION):
|
||||||
playfield[randy][randx] = MINE
|
playfield[randy][randx] = MINE
|
||||||
minesleft -= 1
|
minesleft -= 1
|
||||||
@ -183,75 +184,100 @@ def gameover(win):
|
|||||||
key = SCREEN.getch()
|
key = SCREEN.getch()
|
||||||
if key == ord('q'): return False
|
if key == ord('q'): return False
|
||||||
elif key == ord('r'): return True
|
elif key == ord('r'): return True
|
||||||
# os.execl(sys.executable, sys.executable, *sys.argv)
|
|
||||||
|
def getTileLeft(x, y):
|
||||||
|
cell = playfield[y][x]
|
||||||
|
s = ''
|
||||||
|
if [x, y] == CURSOR_POSITION:
|
||||||
|
s += '['
|
||||||
|
color = clGreen
|
||||||
|
else:
|
||||||
|
s += ' '
|
||||||
|
color = clWhite
|
||||||
|
return s, color
|
||||||
|
|
||||||
|
def getTileMiddle(x,y):
|
||||||
|
cell = playfield[y][x]
|
||||||
|
s = ''
|
||||||
|
if cell == 0:
|
||||||
|
s += ' '
|
||||||
|
color = clWhite
|
||||||
|
elif cell == UNKNOWN or cell == MINE:
|
||||||
|
s += ' '
|
||||||
|
color = clInverted
|
||||||
|
elif cell == FLAG_MINE or cell == FLAG:
|
||||||
|
s += 'P'
|
||||||
|
color = clRed
|
||||||
|
else:
|
||||||
|
# coloring the hints
|
||||||
|
if cell == 1: color = clCyan #cyan
|
||||||
|
elif cell == 2: color = clBlue #blue
|
||||||
|
else: color = clYellow #yellow
|
||||||
|
s += str(cell)
|
||||||
|
|
||||||
|
return s, color
|
||||||
|
|
||||||
|
def getTileRight(x, y):
|
||||||
|
cell = playfield[y][x]
|
||||||
|
selected = False
|
||||||
|
s = ''
|
||||||
|
if [x, y] == CURSOR_POSITION:
|
||||||
|
s += ']'
|
||||||
|
color = clGreen
|
||||||
|
else:
|
||||||
|
s += ' '
|
||||||
|
color = clWhite
|
||||||
|
return s, color
|
||||||
|
|
||||||
def print_playfield(playfield, screen):
|
def print_playfield(playfield, screen):
|
||||||
global score_x, score_y
|
global score_x, score_y
|
||||||
|
#print the matrix
|
||||||
currentline = 0
|
currentline = 0
|
||||||
screen.addstr(currentline, 10, headline, curses.color_pair(1))
|
screen.addstr(currentline, OFFSET-1, headline, clWhite)
|
||||||
currentline +=1
|
currentline +=1
|
||||||
#print headline
|
|
||||||
for rowindex, row in enumerate(playfield):
|
for rowindex, row in enumerate(playfield):
|
||||||
screen.addstr(currentline, 10, '│')
|
screen.addstr(currentline, OFFSET-1, '│')
|
||||||
pos = OFFSET
|
pos = OFFSET
|
||||||
for colindex, cell in enumerate(row):
|
for colindex, cell in enumerate(row):
|
||||||
# is the cell selected?
|
part, color = getTileLeft(colindex, rowindex)
|
||||||
selected = False
|
screen.addstr(currentline, pos, part, color)
|
||||||
if [colindex, rowindex] == CURSOR_POSITION:
|
|
||||||
screen.addstr(currentline, pos, '[')
|
|
||||||
selected = True
|
|
||||||
else:
|
|
||||||
screen.addstr(currentline, pos, ' ')
|
|
||||||
pos += 1
|
pos += 1
|
||||||
# did we find a hint?
|
part, color = getTileMiddle(colindex, rowindex)
|
||||||
if cell > 0:
|
screen.addstr(currentline, pos, part, color)
|
||||||
if cell == 1: color = curses.color_pair(3) #cyan
|
pos += 1
|
||||||
elif cell == 2: color = curses.color_pair(4) #blue
|
part, color = getTileRight(colindex, rowindex)
|
||||||
else: color = curses.color_pair(5) #yellow
|
screen.addstr(currentline, pos, part, color)
|
||||||
screen.addstr(currentline, pos, str(cell), color)
|
pos += 1
|
||||||
elif cell == 0:
|
screen.addstr(currentline, pos, '│')
|
||||||
screen.addstr(currentline, pos, ' ')
|
|
||||||
elif cell == UNKNOWN or cell == MINE:
|
|
||||||
screen.addstr(currentline, pos, '#', curses.color_pair(7)) #rowstring+= '#'
|
|
||||||
elif cell == FLAG_MINE or cell == FLAG:
|
|
||||||
screen.addstr(currentline, pos, 'P', curses.color_pair(2)) #rowstring += 'P'
|
|
||||||
#elif cell == MINE:
|
|
||||||
# rowstring += 'X'
|
|
||||||
pos += 1
|
pos += 1
|
||||||
if selected:
|
|
||||||
screen.addstr(currentline, pos, ']│')
|
|
||||||
else:
|
|
||||||
screen.addstr(currentline, pos, ' │')
|
|
||||||
pos += 2
|
|
||||||
currentline +=1
|
currentline +=1
|
||||||
if(rowindex < len(row)-1):
|
if(rowindex < len(row)-1):
|
||||||
screen.addstr(currentline, 10, midline)
|
screen.addstr(currentline, 10, midline)
|
||||||
currentline +=1
|
currentline +=1
|
||||||
screen.addstr(currentline, 10, tailline)
|
screen.addstr(currentline, 10, tailline)
|
||||||
currentline +=1
|
currentline +=1
|
||||||
|
#get a centered position below the playfield:
|
||||||
score_y = currentline
|
score_y = currentline
|
||||||
score_x = int(pos/2)
|
score_x = int(pos/2)
|
||||||
#print tailline
|
|
||||||
|
|
||||||
def hit(x, y, recursive_call=False):
|
def hit(x, y):
|
||||||
global playfield, FIELDS_CLEARED
|
global playfield, FIELDS_CLEARED
|
||||||
if playfield[y][x] == UNKNOWN:
|
if playfield[y][x] == UNKNOWN:
|
||||||
|
#get the number that should be printed in the cell
|
||||||
hint = calculate_hint(x, y)
|
hint = calculate_hint(x, y)
|
||||||
playfield[y][x] = hint
|
playfield[y][x] = hint
|
||||||
FIELDS_CLEARED += 1
|
FIELDS_CLEARED += 1
|
||||||
if not recursive_call and hint == NOTHING:
|
if hint == NOTHING:
|
||||||
|
#we hit a 0 or empty field, so we need to hit all the fields around it
|
||||||
|
#first, step through the 8 or less surrounding fields
|
||||||
for i in range(x-1, x+2):
|
for i in range(x-1, x+2):
|
||||||
for j in range(y-1, y+2):
|
for j in range(y-1, y+2):
|
||||||
if i >= 0 and i < width:
|
if i >= 0 and i < width:
|
||||||
if j >= 0 and j< height:
|
if j >= 0 and j< height:
|
||||||
if playfield[j][i] == UNKNOWN:
|
if playfield[j][i] == UNKNOWN:
|
||||||
hint = calculate_hint(i, j)
|
#player has not opened this field yet
|
||||||
if hint > 0 and hint<3:
|
|
||||||
hit(i,j, True)
|
|
||||||
if hint == 0:
|
|
||||||
hit(i,j)
|
hit(i,j)
|
||||||
elif playfield[y][x] == MINE:
|
elif playfield[y][x] == MINE:
|
||||||
#gameover(False)
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def check_score():
|
def check_score():
|
||||||
@ -260,7 +286,6 @@ def check_score():
|
|||||||
|
|
||||||
def place_flag(x, y):
|
def place_flag(x, y):
|
||||||
global playfield, FLAGCOUNT
|
global playfield, FLAGCOUNT
|
||||||
#playfield[y][x] = playfield[y][x]
|
|
||||||
if playfield[y][x] == MINE:
|
if playfield[y][x] == MINE:
|
||||||
playfield[y][x] = FLAG_MINE
|
playfield[y][x] = FLAG_MINE
|
||||||
FLAGCOUNT += 1
|
FLAGCOUNT += 1
|
||||||
@ -297,6 +322,7 @@ def handle_input(k):
|
|||||||
else:
|
else:
|
||||||
CURSOR_POSITION[1] = 0
|
CURSOR_POSITION[1] = 0
|
||||||
elif k == ord('f'):
|
elif k == ord('f'):
|
||||||
|
# only place flags after placing mines
|
||||||
if FIELD_GENERATED:
|
if FIELD_GENERATED:
|
||||||
place_flag(CURSOR_POSITION[0], CURSOR_POSITION[1])
|
place_flag(CURSOR_POSITION[0], CURSOR_POSITION[1])
|
||||||
elif k == ord(' '):
|
elif k == ord(' '):
|
||||||
@ -306,26 +332,33 @@ def handle_input(k):
|
|||||||
return hit(CURSOR_POSITION[0], CURSOR_POSITION[1])
|
return hit(CURSOR_POSITION[0], CURSOR_POSITION[1])
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def print_score(screen):
|
def print_footer(screen):
|
||||||
scorestr = 'Mines: {} Flags: {} Difficulty: {}'.format(MINECOUNT, FLAGCOUNT, difficulty)
|
scorestr = 'Mines: {} Flags: {} Difficulty: {}'.format(MINECOUNT, FLAGCOUNT, difficulty)
|
||||||
xpos = int((score_x) - (len(scorestr)/2)) + int(OFFSET/2)
|
|
||||||
if xpos < 0: xpos = 0
|
|
||||||
screen.addstr(score_y, xpos, scorestr)
|
|
||||||
|
|
||||||
def print_controls(screen):
|
|
||||||
controlstr = 'Arrow Keys: Move F: Place flag Space: Open field'
|
controlstr = 'Arrow Keys: Move F: Place flag Space: Open field'
|
||||||
xpos = int((score_x) - len(controlstr)/2) + int(OFFSET/2)
|
score_xpos = int((score_x) - (len(scorestr)/2)) + int(OFFSET/2)
|
||||||
if xpos < 0: xpos = 0
|
control_xpos = int((score_x) - len(controlstr)/2) + int(OFFSET/2)
|
||||||
screen.addstr(score_y +1, xpos, controlstr)
|
if score_xpos < 0: score_xpos = 0
|
||||||
|
if control_xpos < 0: control_xpos = 0
|
||||||
|
screen.addstr(score_y, score_xpos, scorestr)
|
||||||
|
screen.addstr(score_y+1, control_xpos, controlstr)
|
||||||
|
|
||||||
def setup_colors():
|
def setup_colors():
|
||||||
|
global clWhite, clRed, clCyan, clBlue, clYellow, clGreen, clInverted
|
||||||
curses.start_color()
|
curses.start_color()
|
||||||
curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK)
|
#curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLACK)
|
||||||
curses.init_pair(2, curses.COLOR_RED, curses.COLOR_BLACK)
|
curses.init_pair(2, curses.COLOR_RED, curses.COLOR_BLACK)
|
||||||
curses.init_pair(3, curses.COLOR_CYAN, curses.COLOR_BLACK)
|
curses.init_pair(3, curses.COLOR_CYAN, curses.COLOR_BLACK)
|
||||||
curses.init_pair(4, curses.COLOR_BLUE, curses.COLOR_BLACK)
|
curses.init_pair(4, curses.COLOR_BLUE, curses.COLOR_BLACK)
|
||||||
curses.init_pair(5, curses.COLOR_YELLOW, curses.COLOR_BLACK)
|
curses.init_pair(5, curses.COLOR_YELLOW, curses.COLOR_BLACK)
|
||||||
|
curses.init_pair(6, curses.COLOR_GREEN, curses.COLOR_BLACK)
|
||||||
curses.init_pair(7, curses.COLOR_WHITE, curses.COLOR_WHITE)
|
curses.init_pair(7, curses.COLOR_WHITE, curses.COLOR_WHITE)
|
||||||
|
clWhite = curses.color_pair(0) #hardwired
|
||||||
|
clRed = curses.color_pair(2)
|
||||||
|
clCyan = curses.color_pair(3)
|
||||||
|
clBlue = curses.color_pair(4)
|
||||||
|
clYellow = curses.color_pair(5)
|
||||||
|
clGreen = curses.color_pair(6)
|
||||||
|
clInverted = curses.color_pair(7)
|
||||||
|
|
||||||
def reset():
|
def reset():
|
||||||
global firstmove, playfield, FIELD_GENERATED, FIELDS_CLEARED, CURSOR_POSITION
|
global firstmove, playfield, FIELD_GENERATED, FIELDS_CLEARED, CURSOR_POSITION
|
||||||
@ -335,15 +368,12 @@ def reset():
|
|||||||
FIELDS_CLEARED = 0
|
FIELDS_CLEARED = 0
|
||||||
CURSOR_POSITION = [0,0]
|
CURSOR_POSITION = [0,0]
|
||||||
|
|
||||||
|
|
||||||
def main(stdscr):
|
def main(stdscr):
|
||||||
global SCREEN, firstmove, FIELD_GENERATED, FIELDS_CLEARED, playfield, CURSOR_POSITION
|
global SCREEN, firstmove, FIELD_GENERATED, FIELDS_CLEARED, playfield, CURSOR_POSITION
|
||||||
SCREEN = stdscr
|
SCREEN = stdscr
|
||||||
stdscr.clear()
|
stdscr.clear()
|
||||||
setup_strings(width)
|
setup_strings(width)
|
||||||
setup_colors()
|
setup_colors()
|
||||||
#generate mines:
|
|
||||||
#TODO: user input
|
|
||||||
while(True):
|
while(True):
|
||||||
reset()
|
reset()
|
||||||
while(True): #game loop
|
while(True): #game loop
|
||||||
@ -356,18 +386,19 @@ def main(stdscr):
|
|||||||
break
|
break
|
||||||
else: endgame()
|
else: endgame()
|
||||||
if (firstmove) and not (FIELD_GENERATED):
|
if (firstmove) and not (FIELD_GENERATED):
|
||||||
|
#generate the field
|
||||||
setup_playfield(width, height, CURSOR_POSITION[0], CURSOR_POSITION[1])
|
setup_playfield(width, height, CURSOR_POSITION[0], CURSOR_POSITION[1])
|
||||||
|
#do this a second time, because we didn't know what the field would be the first time
|
||||||
handle_input(key)
|
handle_input(key)
|
||||||
STARTTIME = time.time()
|
STARTTIME = time.time()
|
||||||
if check_score():
|
if check_score():
|
||||||
|
#player wins!
|
||||||
restart = gameover(True) # does user want restart?
|
restart = gameover(True) # does user want restart?
|
||||||
if restart:
|
if restart:
|
||||||
stdscr.clear()
|
stdscr.clear()
|
||||||
break
|
break
|
||||||
else: endgame()
|
else: endgame()
|
||||||
print_score(stdscr)
|
print_footer(stdscr)
|
||||||
print_controls(stdscr)
|
|
||||||
stdscr.refresh()
|
stdscr.refresh()
|
||||||
#stdscr.getkey()
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
wrapper(main)
|
wrapper(main)
|
Loading…
Reference in New Issue
Block a user