Created
November 1, 2012 01:16
-
-
Save bobmurder/3991003 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
from collections import defaultdict | |
from itertools import chain, repeat | |
from random import choice | |
################## | |
# Map Generation # | |
################## | |
#============== | |
# Lookup Tables | |
#============== | |
def operations_table(): | |
return dict(zip( | |
range(1, 10), | |
[(-1, -1), (0, -1), (1, -1), | |
(-1, 0), (0, 0), (1, 0), | |
(-1, 1), (0, 1), (1, 1)] | |
)) | |
def edge_table(): | |
return (tuple([4] + range(6, 10)), tuple(range(1, 5) + [6]), | |
(2, 3, 6, 8, 9), (1, 2, 4, 7, 8)) | |
def corner_operations(height, width): | |
height, width = map(lambda x: x - 1, [height, width]) | |
return {(0, 0): [operations_table[n] for n in (6, 8, 9)], | |
(width, 0): [operations_table[n] for n in (4, 7, 8)], | |
(0, height): [operations_table[n] for n in (2, 3, 6)], | |
(width, height): [operations_table[n] for n in (1, 2, 4)]} | |
def edge_operations(height, width): | |
top, bottom, left, right = edge_table() | |
max_x, max_y = map(lambda x: x - 1, [height, width]) | |
edge_cells = {} | |
for cell in [(n, 0) for n in range(1, width)]: | |
edge_cells[cell] = [operations_table[n] for n in top] | |
for cell in [(n, max_y) for n in range(1, width)]: | |
edge_cells[cell] = [operations_table[n] for n in bottom] | |
for cell in [(0, n) for n in range(1, height)]: | |
edge_cells[cell] = [operations_table[n] for n in left] | |
for cell in [(max_x, n) for n in range(1, height)]: | |
edge_cells[cell] = [operations_table[n] for n in right] | |
return edge_cells | |
#================ | |
# Cell Operations | |
#================ | |
def check_space(grid, cell): | |
x, y = cell | |
if grid[x][y] == empty_cell: | |
return True | |
return False | |
def protect_cells(cell, operations): | |
pass | |
def flag_cell(mine_cell, operation): | |
x, y = mine_cell | |
add_x, add_y = operation | |
return (x + add_x, y + add_y) | |
def increment(grid, mine_cell, flag_cells, operations): | |
for operation in operations: | |
cell = tuple(flag_cell(mine_cell, operation)) | |
if check_space(grid, cell): | |
flag_cells[cell] += 1 | |
#================ | |
# Grid Operations | |
#================ | |
def blank_grid(height, width): | |
return [[empty_cell for n in range(width)] for n in range(height)] | |
def place_mines(grid, mines, choice): | |
height, width = len(grid), len(grid[0]) | |
mine_cells = [] | |
while mines: | |
x, y = choice(range(height)), choice(range(width)) | |
if grid[x][y] == empty_cell: | |
grid[x][y] = bomb_symbol | |
mine_cells.append((x, y)) | |
mines -= 1 | |
return grid, mine_cells | |
def place_flags(grid, mine_cells): | |
corner_cells = corner_operations(height, width) | |
edge_cells = edge_operations(height, width) | |
flag_cells = defaultdict(int) | |
for mine_cell in mine_cells: | |
if mine_cell in corner_cells: | |
operations = corner_cells[mine_cell] | |
elif mine_cell in edge_cells: | |
operations = edge_cells[mine_cell] | |
else: | |
operations = operations_table.values() | |
increment(grid, mine_cell, flag_cells, operations) | |
for k, v in flag_cells.iteritems(): | |
x, y = k | |
grid[x][y] = str(v) | |
return grid | |
#============= | |
# Map Creation | |
#============= | |
def create_map(height, width, mines): | |
initial_grid = blank_grid(height, width) | |
grid, mine_cells = place_mines(initial_grid, mines) | |
return place_flags(grid, mine_cells), mine_cells | |
################ | |
# Game Display # | |
################ | |
# prompt message | |
def prompt_message(mark_list): | |
if mark_list: | |
return "(R)eveal, (M)ark, (U)nmark, (Q)uit" | |
return "(R)eveal, (M)ark, (Q)uit" | |
# status bar | |
def status_bar(mines): | |
return 'Mines: %s' % mines | |
# create a grid matching the height and width of the game map | |
def initial_display(height, width): | |
from string import uppercase | |
head = 0 | |
header = [uppercase[i] for i in range(width)] | |
header.insert(head, ' ') | |
header.append(' ') | |
header = '|'.join(header) | |
grid = [['#' for i in range(width)] for j in range(height)] | |
for idx, row in enumerate(grid): | |
i = idx + 1 | |
if len(str(i)) == 1: | |
row.insert(head, ' %s' % i) | |
else: | |
row.insert(head, '%s' % i) | |
row.append(' ') | |
return grid, header | |
def update_display(current_display, cell): | |
pass | |
######### | |
# Tests # | |
######### | |
def won(mine_list, mark_list, current_display): | |
''' Returns True if both conditions are true ''' | |
cells = chain(*current_display) | |
# true if they match, false otherwise | |
matching = sorted(mine_list) == sorted(mark_list) | |
# true if '#' isn't a character in the current display | |
uncovered = all([cell != '#' for cell in cells]) | |
return matching and uncovered | |
minesweeper_rules = """ | |
global variables: | |
mine/marker counter | |
marks list | |
bombs list | |
User Starts Game | |
-prompt for initial choice | |
Initial Choice | |
-The flag and bomb positions aren't generated yet. | |
-User selects an initial cell to check | |
-Create the flag and bomb grid, while making sure that | |
the cell the user chose and all adjacent cells are not bombs. | |
-Do this by setting the cells affected by the user's cell to | |
a unique symbol during bomb placement, then changing them back | |
to the default symbol afterwards before flag placement | |
Subsequent Choices | |
Reveal: | |
while alive: | |
prompt for choice | |
if it is a bomb: game over | |
if it is an empty space: | |
reveal the empty space | |
add the coordinates to the memo dict | |
for each adjacent space: | |
if it is a number: | |
reveal the number | |
add the coordinates to the memo dict | |
continue to the next space | |
if it is an empty space: | |
if it is not in the memo dict: | |
recurse | |
else: | |
continue | |
if it is a number: | |
reveal the number | |
add the coordinates to the memo dict | |
Mark: | |
mark the space | |
add the coords to the marks list | |
decrement the counter | |
Unmark: | |
unmark the space | |
remove the coords from the marks list | |
increment the counter | |
Quit: | |
quit da game | |
Convert [letter][number] coords to (x, y) tuples | |
-example: A1 = (0, 0), C5 = (3, 5) | |
-function: lambda x: tuple(alpha_table[x[0]], int(x[1:]) | |
-regex validation: '^[A-Za-z]{1}\d{1,2}$' | |
Tests | |
#done | |
Winning: | |
if bomb coords == mark coords and # not in display grid: | |
you win | |
else: | |
you are alive | |
""" | |
############### | |
# Main Script # | |
############### | |
if __name__ == '__main__': | |
# imports used during execution as a script | |
from pprint import pprint | |
import sys | |
# testing | |
DEBUG = False | |
INFO = True | |
# global variables | |
empty_cell = ' ' | |
bomb_symbol = 'X' | |
operations_table = operations_table() | |
height, width = 15, 15 | |
mines = 20 | |
marked_cells = [] | |
revealed_cells = {} | |
mines_left = mines | |
# initial map | |
grid, mine_cells = create_map(height, width, mines) | |
initial_status = status_bar(mines) | |
display, header = initial_display(height, width) | |
if DEBUG: | |
pprint(grid) | |
pprint(display) | |
if INFO: | |
for row in grid: | |
print ''.join(row) | |
print status_bar(mines) | |
print header | |
for row in display: | |
print '|'.join(row) | |
print prompt_message(marked_cells) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment