-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathminesweeper.py
executable file
·163 lines (146 loc) · 5.23 KB
/
minesweeper.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
import random
import numpy as np
class MinesweeperCore:
"""
Represents the core game rules.
"""
BOMB = -2
UNKNOWN_CELL = -1
CLEAR_CELL = 0
def __init__(self, height, width, num_bombs, win_threshold=1.0):
"""
Defines game parameters and initializes a new minesweeper board.
Where the x coordinate represents the rows ranging from 0 to self.height - 1
and y coordinate represents the columns ranging from 0 to self.width - 1.
:param height: height of the board.
:type height: int.
:param width: width of the board.
:type width: int.
:param num_bombs: number of bomb on the board.
:type num_bombs: int.
:param win_threshold: percentage of the game board discovered to consider victory.
:type win_threshold: float.
"""
self.height = height
self.width = width
self.num_bombs = num_bombs
self.win_threshold = win_threshold
self.table = np.matrix(np.full((self.height, self.width), self.UNKNOWN_CELL))
self.victory = False
self.still_playing = True
self.bomb_positions = []
self.unexplored = self.height * self.width - self.num_bombs
self.first_play = True
for i in range(self.num_bombs):
while True:
x = random.randint(0, self.height - 1)
y = random.randint(0, self.width - 1)
if (x, y) not in self.bomb_positions:
self.bomb_positions.append((x, y))
break
def reset(self):
"""
Resets the minesweeper board.
:return: reset board.
:rtype: numpy matrix.
"""
self.table = np.matrix(np.full((self.height, self.width), self.UNKNOWN_CELL))
self.victory = False
self.still_playing = True
self.bomb_positions = []
self.unexplored = self.height * self.width - self.num_bombs
self.first_play = True
for i in range(self.num_bombs):
while True:
x = random.randint(0, self.height - 1)
y = random.randint(0, self.width - 1)
if (x, y) not in self.bomb_positions:
self.bomb_positions.append((x, y))
break
return self.table
def isVictory(self):
"""
Returns the state of the game.
:return: victory flag.
:rtype: bool.
"""
return self.victory
def isPlaying(self):
"""
Returns if the agent is still playing the game.
:return: still playing flag.
:rtype: bool.
"""
return self.still_playing
def play(self, x, y):
"""
Executes discovering action on the (x, y) position on the game board and updates it accordingly.
:param x: x coordinate on the game board.
:type x: int.
:param y: y coordinate on the game board.
:type y: int.
:return: output of the discovering decision.
True for successful action and False for incorrect action.
:rtype: bool.
"""
if not (0 <= x < self.height and 0 <= y < self.width):
return False
if (self.table[x, y] != self.UNKNOWN_CELL) or (not self.isPlaying()):
return False
if (x, y) in self.bomb_positions:
if self.first_play is True:
self.reset()
return self.play(x, y)
self.still_playing = False
return True
self.first_play = False
neighbour_bombs = self.neighbour_bombs(x, y)
self.unexplored -= 1
self.table[x, y] = neighbour_bombs
if neighbour_bombs == 0:
displacement = [-1, 0, 1]
for i in displacement:
for j in displacement:
self.play(x + i, y + j)
self.verify_win()
return True
def verify_win(self):
"""
Verifies the current state of the game and updates the state variables accordingly.
"""
if self.unexplored > (1 - self.win_threshold) * self.height * self.width:
return False
self.victory = True
self.still_playing = False
def neighbour_bombs(self, x, y):
"""
Counts how many neighbours of (x,y) are bombs.
:param x: x coordinate on the game board.
:type x: int.
:param y: y coordinate on the game board.
:type y: int.
:return: number of bombs adjacent to the position (x,y).
:rtype: int.
"""
neighbours = 0
displacement = [-1, 0, 1]
for i in displacement:
for j in displacement:
if (x + i, y + j) in self.bomb_positions:
neighbours += 1
return neighbours
def get_board(self, xray=False):
"""
Returns the board.
:param xray: if the board should show all bomb positions or not.
:type xray: bool.
:return: game board.
:rtype: numpy matrix.
"""
if not xray:
return self.table
else:
xray_table = np.copy(self.table)
for position in self.bomb_positions:
xray_table[position[0], position[1]] = self.BOMB
return xray_table