rendered paste bodyimport unittestclass Cell(object): def __init__(self, containsMine): self.containsMine = containsMine self.dug = False def dig(self): self.dug = True def toBePlayedYet(self): return not self.containsMine and not self.dugclass MineField(object): @classmethod def fromStrings(cls, *rows): return cls([[Cell(ch != '.') for ch in row] for row in rows]) def __init__(self, rows): self.rows = rows self.gameOver = False self.win = True def dig(self, row, col): thisCell = self.rows[row][col] if thisCell.containsMine: self._looseGame() return if thisCell.dug: return thisCell.dig() self._digNeighborCells(row, col) if self._noFreeCellsLeft(): self._winGame() def _noFreeCellsLeft(self): for row in self.rows: for cell in row: if cell.toBePlayedYet(): return False return True def _digNeighborCells(self, row, col): for neighborRow, neighborCol in self.getDiggingNeighborsCoordinates(row, col): neighbor = self.rows[neighborRow][neighborCol] if not neighbor.containsMine: self.dig(neighborRow, neighborCol) def isCellDug(self, row, col): return self.rows[row][col].dug def _looseGame(self): self.gameOver = True self.win = False def _winGame(self): self.gameOver = True self.win = True def getDiggingNeighborsCoordinates(self, row, col): return list(self._iterDiggingNeighborsCoordinates(row, col)) def _iterDiggingNeighborsCoordinates(self, row, col): if row-1 >= 0: yield (row-1, col) if col-1 >= 0: yield (row, col-1) if row+1 < self.numRows: yield (row+1, col) if col+1 < self.numCols: yield (row, col+1) def getCountingNeighborsCoordinates(self, row, col): return list(self._iterCountingNeighborsCoordinates(row, col)) def _iterCountingNeighborsCoordinates(self, centerRow, centerCol): for row, col in self._iterAllCountingNeighbors(centerRow, centerCol): if (row >= 0 and row < self.numRows and col >= 0 and col < self.numCols): yield row, col def _iterAllCountingNeighbors(self, row, col): yield row-1, col yield row-1, col-1 yield row, col-1 yield row+1, col-1 yield row+1, col yield row+1, col+1 yield row, col+1 yield row-1, col+1 @property def numRows(self): return len(self.rows) @property def numCols(self): return len(self.rows[0]) def getNumberOfMinesAround(self, centerRow, centerCol): numMines = 0 for row, col in self.getCountingNeighborsCoordinates(centerRow, centerCol): if self.rows[row][col].containsMine: numMines += 1 return numMines def toString(self): return ''.join(self._iterCellStrings()) def _iterCellStrings(self): for rowNum in xrange(self.numRows): for colNum in xrange(self.numCols): yield self._renderCell(rowNum, colNum) def _renderCell(self, rowNum, colNum): cell = self.rows[rowNum][colNum] if not cell.dug: return '#' numMines = self.getNumberOfMinesAround(rowNum, colNum) if numMines == 0: return '.' else: return str(numMines) class MineFieldTestCase(unittest.TestCase): def test_one_cell_with_no_mine_dig_the_cell_and_win(self): f = MineField.fromStrings('.') self.assertFalse(f.isCellDug(0,0)) f.dig(0,0) self.assertTrue(f.gameOver) self.assertTrue(f.win) self.assertTrue(f.isCellDug(0,0)) def test_one_cell_with_one_mine_dig_the_cell_and_loose(self): f = MineField.fromStrings('X') f.dig(0,0) self.assertTrue(f.gameOver) self.assertFalse(f.win) def test_4_cells_with_3_mines_dig_the_free_cell_and_win(self): f = MineField.fromStrings('.X', 'XX') f.dig(0,0) self.assertTrue(f.gameOver) self.assertTrue(f.win) self.assertEquals(f.getNumberOfMinesAround(0,0), 3) def test_4_cells_with_2_mines_dig_top_left_and_find_both_dug(self): f = MineField.fromStrings('.X', '.X') f.dig(0,0) self.assertTrue(f.isCellDug(0,0)) self.assertTrue(f.isCellDug(1,0)) self.assertFalse(f.isCellDug(0,1)) self.assertFalse(f.isCellDug(1,1)) self.assertTrue(f.gameOver) self.assertTrue(f.win) self.assertEquals(f.getNumberOfMinesAround(0,0), 2) def test_9_cells_with_1_mine_in_the_middle_dig_top_left_and_find_all_dug_but_mine(self): f = MineField.fromStrings('...', '.X.', '...') f.dig(0,0) self.assertTrue(f.isCellDug(0,0)) self.assertTrue(f.isCellDug(1,0)) self.assertTrue(f.isCellDug(2,0)) self.assertTrue(f.isCellDug(2,1)) self.assertTrue(f.isCellDug(2,2)) self.assertTrue(f.isCellDug(1,2)) self.assertTrue(f.isCellDug(0,2)) self.assertTrue(f.isCellDug(0,1)) self.assertFalse(f.isCellDug(1,1)) self.assertTrue(f.gameOver) self.assertTrue(f.win) self.assertEquals(f.getNumberOfMinesAround(0,0), 1) def test_9_cells_with_3_mine_acting_as_barrier_in_the_middle_dig_top_left_keep_playing(self): f = MineField.fromStrings('.X.', '.X.', '.X.') f.dig(0,0) self.assertFalse(f.gameOver) self.assertEquals(f.getNumberOfMinesAround(0,0), 2) self.assertEquals(f.getNumberOfMinesAround(1,0), 3) def test_real_game(self): f = MineField.fromStrings('..X..', '..XXX', '.....', '.....') f.dig(0,0) self.assertEquals(f.toString(), ''.join(['.2###', '.2###', '.1232', '.....', ])) class NeighborsTestCase(unittest.TestCase): def setUp(self): self.f = MineField.fromStrings('...', '...', '...') def test_9_cells_cell_in_the_middle_has_8_counting_neighbors(self): neighbors = self.f.getCountingNeighborsCoordinates(1,1) self.assertEquals(neighbors, [(0,1), (0,0), (1,0), (2,0), (2,1), (2,2), (1,2), (0, 2)]) def test_9_cells_cell_in_the_middle_has_4_digging_neighbors(self): neighbors = self.f.getDiggingNeighborsCoordinates(1,1) self.assertEquals(neighbors, [(0,1), (1,0), (2,1), (1,2)]) def test_9_cells_cell_left_has_3_digging_neighbors(self): neighbors = self.f.getDiggingNeighborsCoordinates(1,0) self.assertEquals(neighbors, [(0,0), (2,0), (1,1)]) def test_9_cells_cell_top_has_3_digging_neighbors(self): neighbors = self.f.getDiggingNeighborsCoordinates(0,1) self.assertEquals(neighbors, [(0,0), (1,1), (0,2)]) def test_9_cells_cell_right_has_3_digging_neighbors(self): neighbors = self.f.getDiggingNeighborsCoordinates(1,2) self.assertEquals(neighbors, [(0,2), (1,1), (2,2)]) def test_9_cells_cell_top_left_has_2_digging_neighbors(self): neighbors = self.f.getDiggingNeighborsCoordinates(0,0) self.assertEquals(neighbors, [(1,0), (0,1)])if __name__ == '__main__': unittest.main()