From 7008c6e55a83a7292284ad6a88a3fe01aabddb8f Mon Sep 17 00:00:00 2001 From: panki27 Date: Sat, 21 Apr 2018 16:13:48 +0200 Subject: [PATCH] code cleanup, added comments, refactoring --- minebash | 163 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 97 insertions(+), 66 deletions(-) diff --git a/minebash b/minebash index 21318b0..be955cf 100755 --- a/minebash +++ b/minebash @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -#TODO: highscore +#TODO: highscore, monochrome, expect ctrl c, flags red if too many import random, io, sys, time, os import curses @@ -12,6 +12,8 @@ UNKNOWN = -3 FLAG_MINE = -4 score_x, score_y = 0, 0 +CURSOR_POSITION=[0,0] + OFFSET = 11 STARTTIME = 0 @@ -21,13 +23,14 @@ SCREEN = 0 firstmove = False FIELD_GENERATED = False -CURSOR_POSITION=[0,0] FIELDS_CLEARED = 0 width, height = 9, 9 MINECOUNT = 10 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' param_error = 'minebash: Invalid parameters. See \'minebash.py ?\' for help ' @@ -81,17 +84,13 @@ if len(sys.argv) > 1: print(param_error) sys.exit(0) playfield = [[UNKNOWN for x in range(width)] for y in range(height)] -#playfield = 0 -#stdscr = curses.initscr() -#curses.noecho() -#curses.cbreak() headline = '┌' midline = '├' tailline = '└' 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 for i in range(colcount): if i == width-1: @@ -111,25 +110,27 @@ def endgame(msg=''): def calculate_hint(col, row): hint = 0 if playfield[row][col] != MINE: + #step through the surrounding 8 (or less) fields for x in range(col-1, col+2): if x >= 0 and x < len(playfield): for y in range(row-1, row+2): if y >= 0 and y < len(playfield[0]): if playfield[y][x] == MINE or playfield[y][x] == FLAG_MINE: + #add the mines together hint+=1 else: hint = MINE return hint 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 minesleft = MINECOUNT + #randomly distribute mines across the field while minesleft > 0: randx = random.randint(0, width-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): playfield[randy][randx] = MINE minesleft -= 1 @@ -183,75 +184,100 @@ def gameover(win): key = SCREEN.getch() if key == ord('q'): return False 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): global score_x, score_y + #print the matrix currentline = 0 - screen.addstr(currentline, 10, headline, curses.color_pair(1)) + screen.addstr(currentline, OFFSET-1, headline, clWhite) currentline +=1 - #print headline for rowindex, row in enumerate(playfield): - screen.addstr(currentline, 10, '│') + screen.addstr(currentline, OFFSET-1, '│') pos = OFFSET for colindex, cell in enumerate(row): - # is the cell selected? - selected = False - if [colindex, rowindex] == CURSOR_POSITION: - screen.addstr(currentline, pos, '[') - selected = True - else: - screen.addstr(currentline, pos, ' ') + part, color = getTileLeft(colindex, rowindex) + screen.addstr(currentline, pos, part, color) pos += 1 - # did we find a hint? - if cell > 0: - if cell == 1: color = curses.color_pair(3) #cyan - elif cell == 2: color = curses.color_pair(4) #blue - else: color = curses.color_pair(5) #yellow - screen.addstr(currentline, pos, str(cell), color) - elif cell == 0: - 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' + part, color = getTileMiddle(colindex, rowindex) + screen.addstr(currentline, pos, part, color) + pos += 1 + part, color = getTileRight(colindex, rowindex) + screen.addstr(currentline, pos, part, color) + pos += 1 + screen.addstr(currentline, pos, '│') pos += 1 - if selected: - screen.addstr(currentline, pos, ']│') - else: - screen.addstr(currentline, pos, ' │') - pos += 2 currentline +=1 if(rowindex < len(row)-1): screen.addstr(currentline, 10, midline) currentline +=1 screen.addstr(currentline, 10, tailline) currentline +=1 + #get a centered position below the playfield: score_y = currentline score_x = int(pos/2) - #print tailline -def hit(x, y, recursive_call=False): +def hit(x, y): global playfield, FIELDS_CLEARED if playfield[y][x] == UNKNOWN: + #get the number that should be printed in the cell hint = calculate_hint(x, y) playfield[y][x] = hint 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 j in range(y-1, y+2): if i >= 0 and i < width: if j >= 0 and j< height: if playfield[j][i] == UNKNOWN: - hint = calculate_hint(i, j) - if hint > 0 and hint<3: - hit(i,j, True) - if hint == 0: - hit(i,j) + #player has not opened this field yet + hit(i,j) elif playfield[y][x] == MINE: - #gameover(False) return True def check_score(): @@ -260,7 +286,6 @@ def check_score(): def place_flag(x, y): global playfield, FLAGCOUNT - #playfield[y][x] = playfield[y][x] if playfield[y][x] == MINE: playfield[y][x] = FLAG_MINE FLAGCOUNT += 1 @@ -297,6 +322,7 @@ def handle_input(k): else: CURSOR_POSITION[1] = 0 elif k == ord('f'): + # only place flags after placing mines if FIELD_GENERATED: place_flag(CURSOR_POSITION[0], CURSOR_POSITION[1]) elif k == ord(' '): @@ -306,26 +332,33 @@ def handle_input(k): return hit(CURSOR_POSITION[0], CURSOR_POSITION[1]) return False -def print_score(screen): +def print_footer(screen): 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' - xpos = int((score_x) - len(controlstr)/2) + int(OFFSET/2) - if xpos < 0: xpos = 0 - screen.addstr(score_y +1, xpos, controlstr) + score_xpos = int((score_x) - (len(scorestr)/2)) + int(OFFSET/2) + control_xpos = int((score_x) - len(controlstr)/2) + int(OFFSET/2) + 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(): + global clWhite, clRed, clCyan, clBlue, clYellow, clGreen, clInverted 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(3, curses.COLOR_CYAN, 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(6, curses.COLOR_GREEN, curses.COLOR_BLACK) 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(): global firstmove, playfield, FIELD_GENERATED, FIELDS_CLEARED, CURSOR_POSITION @@ -335,15 +368,12 @@ def reset(): FIELDS_CLEARED = 0 CURSOR_POSITION = [0,0] - def main(stdscr): global SCREEN, firstmove, FIELD_GENERATED, FIELDS_CLEARED, playfield, CURSOR_POSITION SCREEN = stdscr stdscr.clear() setup_strings(width) setup_colors() - #generate mines: - #TODO: user input while(True): reset() while(True): #game loop @@ -356,18 +386,19 @@ def main(stdscr): break else: endgame() if (firstmove) and not (FIELD_GENERATED): + #generate the field 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) STARTTIME = time.time() if check_score(): + #player wins! restart = gameover(True) # does user want restart? if restart: stdscr.clear() break else: endgame() - print_score(stdscr) - print_controls(stdscr) + print_footer(stdscr) stdscr.refresh() - #stdscr.getkey() if __name__ == "__main__": - wrapper(main) + wrapper(main) \ No newline at end of file