SmokeyBandit commited on
Commit
d805da2
·
verified ·
1 Parent(s): ecd7e7e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +25 -96
app.py CHANGED
@@ -32,10 +32,8 @@ class GoGame:
32
  color = self.board[x, y]
33
  if color == 0:
34
  return set()
35
-
36
  group = {(x, y)}
37
  frontier = [(x, y)]
38
-
39
  while frontier:
40
  current = frontier.pop()
41
  for nx, ny in self.get_neighbors(*current):
@@ -66,28 +64,23 @@ class GoGame:
66
  def is_valid_move(self, x, y):
67
  if self.board[x, y] != 0:
68
  return False
69
-
70
  # Make a temporary move.
71
  self.board[x, y] = self.current_player
72
  valid = self.has_liberties(x, y)
73
  captures_enemy = self.capture_stones() > 0
74
  # Undo temporary move.
75
  self.board[x, y] = 0
76
-
77
  return valid or captures_enemy
78
 
79
  def make_move(self, x, y):
80
  if not self.is_valid_move(x, y):
81
  return False
82
-
83
  self.board[x, y] = self.current_player
84
  captured = self.capture_stones()
85
-
86
  if self.current_player == 1:
87
  self.captured_white += captured
88
  else:
89
  self.captured_black += captured
90
-
91
  self.last_move = (x, y)
92
  self.current_player *= -1
93
  return True
@@ -96,18 +89,15 @@ class GoGame:
96
  empty_positions = list(zip(*np.where(self.board == 0)))
97
  if not empty_positions:
98
  return None
99
-
100
  valid_moves = [pos for pos in empty_positions if self.is_valid_move(*pos)]
101
  if not valid_moves:
102
  return None
103
-
104
  if difficulty == Difficulty.EASY:
105
  move = random.choice(valid_moves)
106
  elif difficulty == Difficulty.MEDIUM:
107
  move = self.medium_ai(valid_moves)
108
  else:
109
  move = self.hard_ai(valid_moves)
110
-
111
  self.make_move(*move)
112
  return move
113
 
@@ -130,7 +120,6 @@ class GoGame:
130
  self.board[move] = 0
131
  return move
132
  self.board[move] = 0
133
-
134
  opponent = -self.current_player
135
  self.current_player = opponent
136
  for move in valid_moves:
@@ -142,90 +131,49 @@ class GoGame:
142
  return move
143
  self.board[move] = 0
144
  self.current_player = -opponent
145
-
146
  return self.medium_ai(valid_moves)
147
 
148
  def create_board_image(board, last_move=None, hover_pos=None):
149
  cell_size = 60
150
  margin = 40
151
  total_size = board.shape[0] * cell_size + 2 * margin
152
-
153
  # Create wooden background.
154
  image = np.full((total_size, total_size, 3), [219, 179, 119], dtype=np.uint8)
155
-
156
- # Draw grid with thicker lines.
157
  for i in range(board.shape[0]):
158
- cv2.line(image,
159
- (margin + i * cell_size, margin),
160
- (margin + i * cell_size, total_size - margin),
161
- (0, 0, 0),
162
- 2)
163
- cv2.line(image,
164
- (margin, margin + i * cell_size),
165
- (total_size - margin, margin + i * cell_size),
166
- (0, 0, 0),
167
- 2)
168
-
169
- # Draw star points (hoshi).
170
  star_points = [(2, 2), (2, 6), (4, 4), (6, 2), (6, 6)]
171
  for point in star_points:
172
- cv2.circle(image,
173
- (margin + point[1] * cell_size, margin + point[0] * cell_size),
174
- 4,
175
- (0, 0, 0),
176
- -1,
177
- cv2.LINE_AA)
178
-
179
- # Draw hover position indicator.
180
  if hover_pos:
181
  hover_row, hover_col = hover_pos
182
  center = (margin + hover_col * cell_size, margin + hover_row * cell_size)
183
  overlay = image.copy()
184
- # Draw a semi-transparent preview.
185
  cv2.circle(overlay, center, 23, (0, 0, 0), -1, cv2.LINE_AA)
186
  image = cv2.addWeighted(overlay, 0.5, image, 0.5, 0)
187
  cv2.circle(image, center, 23, (255, 255, 255), 1, cv2.LINE_AA)
188
-
189
- # Draw stones with enhanced 3D effect.
190
  for i in range(board.shape[0]):
191
  for j in range(board.shape[1]):
192
  if board[i, j] != 0:
193
  center = (margin + j * cell_size, margin + i * cell_size)
194
-
195
  # Shadow effect.
196
- cv2.circle(image,
197
- (center[0] + 2, center[1] + 2),
198
- 23,
199
- (0, 0, 0),
200
- -1,
201
- cv2.LINE_AA)
202
-
203
- # Stone.
204
  stone_color = (0, 0, 0) if board[i, j] == 1 else (255, 255, 255)
205
  cv2.circle(image, center, 23, stone_color, -1, cv2.LINE_AA)
206
-
207
- # Highlight for 3D effect.
208
- if board[i, j] == -1: # White stone highlight.
209
- cv2.circle(image,
210
- (center[0] - 5, center[1] - 5),
211
- 8,
212
- (240, 240, 240),
213
- -1,
214
- cv2.LINE_AA)
215
- else: # Black stone highlight.
216
- cv2.circle(image,
217
- (center[0] - 5, center[1] - 5),
218
- 8,
219
- (40, 40, 40),
220
- -1,
221
- cv2.LINE_AA)
222
-
223
  # Highlight last move.
224
  if last_move:
225
  row, col = last_move
226
  center = (margin + col * cell_size, margin + row * cell_size)
227
  cv2.circle(image, center, 5, (255, 0, 0), -1, cv2.LINE_AA)
228
-
229
  return Image.fromarray(image)
230
 
231
  class GradioGoGame:
@@ -233,47 +181,35 @@ class GradioGoGame:
233
  self.game = GoGame()
234
  self.difficulty = Difficulty.EASY
235
  self.hover_pos = None
236
-
237
- def process_click(self, evt: gr.SelectData, difficulty):
238
  cell_size = 60
239
  margin = 40
240
-
241
- # evt.index returns pixel coordinates as (x, y).
242
- # Convert to board coordinates:
243
  col = round((evt.index[0] - margin) / cell_size)
244
  row = round((evt.index[1] - margin) / cell_size)
245
-
246
  if not (0 <= row < self.game.size and 0 <= col < self.game.size):
247
  return create_board_image(self.game.board, self.game.last_move), "Invalid click position"
248
-
249
- self.difficulty = Difficulty(difficulty)
250
-
251
  if not self.game.make_move(row, col):
252
  return create_board_image(self.game.board, self.game.last_move), "Invalid move (occupied or suicide)"
253
-
254
  # AI Move.
255
  ai_move = self.game.ai_move(self.difficulty)
256
  if ai_move is None:
257
  return create_board_image(self.game.board, self.game.last_move), "Game Over"
258
-
259
  status = f"Black captures: {self.game.captured_white} | White captures: {self.game.captured_black}"
260
  return create_board_image(self.game.board, self.game.last_move), f"AI moved to: {ai_move}\n{status}"
261
-
262
  def update_hover(self, evt: gr.SelectData):
263
  cell_size = 60
264
  margin = 40
265
-
266
- # Convert pixel position to board coordinates.
267
  col = round((evt.index[0] - margin) / cell_size)
268
  row = round((evt.index[1] - margin) / cell_size)
269
-
270
  if 0 <= row < self.game.size and 0 <= col < self.game.size:
271
  self.hover_pos = (row, col)
272
  else:
273
  self.hover_pos = None
274
-
275
  return create_board_image(self.game.board, self.game.last_move, self.hover_pos)
276
-
277
  def reset_game(self):
278
  self.game = GoGame()
279
  self.hover_pos = None
@@ -281,7 +217,6 @@ class GradioGoGame:
281
 
282
  def create_interface():
283
  game = GradioGoGame()
284
-
285
  with gr.Blocks(theme=gr.themes.Soft()) as interface:
286
  gr.Markdown("""
287
  # Go Game vs AI
@@ -292,7 +227,6 @@ def create_interface():
292
  - Capture enemy stones by surrounding them.
293
  - Stones must have liberties (empty adjacent points) to survive.
294
  """)
295
-
296
  with gr.Row():
297
  with gr.Column(scale=2):
298
  board_output = gr.Image(
@@ -308,7 +242,6 @@ def create_interface():
308
  value="Click on an intersection to place a stone",
309
  lines=2
310
  )
311
-
312
  with gr.Column(scale=1):
313
  difficulty = gr.Radio(
314
  choices=[d.value for d in Difficulty],
@@ -316,26 +249,22 @@ def create_interface():
316
  label="AI Difficulty"
317
  )
318
  reset_btn = gr.Button("Reset Game", variant="secondary")
319
-
 
 
 
 
320
  # Use .select() for click events on the interactive image.
321
  board_output.select(
322
  game.process_click,
323
- inputs=[board_output, difficulty],
324
  outputs=[board_output, msg_output]
325
  )
326
-
327
- # Use .mousemove() for hover updates (if supported by your Gradio version).
328
- board_output.mousemove(
329
- game.update_hover,
330
- inputs=board_output,
331
- outputs=board_output
332
- )
333
-
334
  reset_btn.click(
335
  game.reset_game,
336
  outputs=[board_output, msg_output]
337
  )
338
-
339
  return interface
340
 
341
  if __name__ == "__main__":
 
32
  color = self.board[x, y]
33
  if color == 0:
34
  return set()
 
35
  group = {(x, y)}
36
  frontier = [(x, y)]
 
37
  while frontier:
38
  current = frontier.pop()
39
  for nx, ny in self.get_neighbors(*current):
 
64
  def is_valid_move(self, x, y):
65
  if self.board[x, y] != 0:
66
  return False
 
67
  # Make a temporary move.
68
  self.board[x, y] = self.current_player
69
  valid = self.has_liberties(x, y)
70
  captures_enemy = self.capture_stones() > 0
71
  # Undo temporary move.
72
  self.board[x, y] = 0
 
73
  return valid or captures_enemy
74
 
75
  def make_move(self, x, y):
76
  if not self.is_valid_move(x, y):
77
  return False
 
78
  self.board[x, y] = self.current_player
79
  captured = self.capture_stones()
 
80
  if self.current_player == 1:
81
  self.captured_white += captured
82
  else:
83
  self.captured_black += captured
 
84
  self.last_move = (x, y)
85
  self.current_player *= -1
86
  return True
 
89
  empty_positions = list(zip(*np.where(self.board == 0)))
90
  if not empty_positions:
91
  return None
 
92
  valid_moves = [pos for pos in empty_positions if self.is_valid_move(*pos)]
93
  if not valid_moves:
94
  return None
 
95
  if difficulty == Difficulty.EASY:
96
  move = random.choice(valid_moves)
97
  elif difficulty == Difficulty.MEDIUM:
98
  move = self.medium_ai(valid_moves)
99
  else:
100
  move = self.hard_ai(valid_moves)
 
101
  self.make_move(*move)
102
  return move
103
 
 
120
  self.board[move] = 0
121
  return move
122
  self.board[move] = 0
 
123
  opponent = -self.current_player
124
  self.current_player = opponent
125
  for move in valid_moves:
 
131
  return move
132
  self.board[move] = 0
133
  self.current_player = -opponent
 
134
  return self.medium_ai(valid_moves)
135
 
136
  def create_board_image(board, last_move=None, hover_pos=None):
137
  cell_size = 60
138
  margin = 40
139
  total_size = board.shape[0] * cell_size + 2 * margin
 
140
  # Create wooden background.
141
  image = np.full((total_size, total_size, 3), [219, 179, 119], dtype=np.uint8)
142
+ # Draw grid.
 
143
  for i in range(board.shape[0]):
144
+ cv2.line(image, (margin + i * cell_size, margin), (margin + i * cell_size, total_size - margin), (0, 0, 0), 2)
145
+ cv2.line(image, (margin, margin + i * cell_size), (total_size - margin, margin + i * cell_size), (0, 0, 0), 2)
146
+ # Draw star points.
 
 
 
 
 
 
 
 
 
147
  star_points = [(2, 2), (2, 6), (4, 4), (6, 2), (6, 6)]
148
  for point in star_points:
149
+ cv2.circle(image, (margin + point[1] * cell_size, margin + point[0] * cell_size), 4, (0, 0, 0), -1, cv2.LINE_AA)
150
+ # Draw hover indicator, if any.
 
 
 
 
 
 
151
  if hover_pos:
152
  hover_row, hover_col = hover_pos
153
  center = (margin + hover_col * cell_size, margin + hover_row * cell_size)
154
  overlay = image.copy()
 
155
  cv2.circle(overlay, center, 23, (0, 0, 0), -1, cv2.LINE_AA)
156
  image = cv2.addWeighted(overlay, 0.5, image, 0.5, 0)
157
  cv2.circle(image, center, 23, (255, 255, 255), 1, cv2.LINE_AA)
158
+ # Draw stones.
 
159
  for i in range(board.shape[0]):
160
  for j in range(board.shape[1]):
161
  if board[i, j] != 0:
162
  center = (margin + j * cell_size, margin + i * cell_size)
 
163
  # Shadow effect.
164
+ cv2.circle(image, (center[0] + 2, center[1] + 2), 23, (0, 0, 0), -1, cv2.LINE_AA)
 
 
 
 
 
 
 
165
  stone_color = (0, 0, 0) if board[i, j] == 1 else (255, 255, 255)
166
  cv2.circle(image, center, 23, stone_color, -1, cv2.LINE_AA)
167
+ # 3D highlight.
168
+ if board[i, j] == -1:
169
+ cv2.circle(image, (center[0] - 5, center[1] - 5), 8, (240, 240, 240), -1, cv2.LINE_AA)
170
+ else:
171
+ cv2.circle(image, (center[0] - 5, center[1] - 5), 8, (40, 40, 40), -1, cv2.LINE_AA)
 
 
 
 
 
 
 
 
 
 
 
 
172
  # Highlight last move.
173
  if last_move:
174
  row, col = last_move
175
  center = (margin + col * cell_size, margin + row * cell_size)
176
  cv2.circle(image, center, 5, (255, 0, 0), -1, cv2.LINE_AA)
 
177
  return Image.fromarray(image)
178
 
179
  class GradioGoGame:
 
181
  self.game = GoGame()
182
  self.difficulty = Difficulty.EASY
183
  self.hover_pos = None
184
+
185
+ def process_click(self, evt: gr.SelectData):
186
  cell_size = 60
187
  margin = 40
188
+ # evt.index returns pixel coordinates as (x, y). Convert to board coordinates.
 
 
189
  col = round((evt.index[0] - margin) / cell_size)
190
  row = round((evt.index[1] - margin) / cell_size)
 
191
  if not (0 <= row < self.game.size and 0 <= col < self.game.size):
192
  return create_board_image(self.game.board, self.game.last_move), "Invalid click position"
 
 
 
193
  if not self.game.make_move(row, col):
194
  return create_board_image(self.game.board, self.game.last_move), "Invalid move (occupied or suicide)"
 
195
  # AI Move.
196
  ai_move = self.game.ai_move(self.difficulty)
197
  if ai_move is None:
198
  return create_board_image(self.game.board, self.game.last_move), "Game Over"
 
199
  status = f"Black captures: {self.game.captured_white} | White captures: {self.game.captured_black}"
200
  return create_board_image(self.game.board, self.game.last_move), f"AI moved to: {ai_move}\n{status}"
201
+
202
  def update_hover(self, evt: gr.SelectData):
203
  cell_size = 60
204
  margin = 40
 
 
205
  col = round((evt.index[0] - margin) / cell_size)
206
  row = round((evt.index[1] - margin) / cell_size)
 
207
  if 0 <= row < self.game.size and 0 <= col < self.game.size:
208
  self.hover_pos = (row, col)
209
  else:
210
  self.hover_pos = None
 
211
  return create_board_image(self.game.board, self.game.last_move, self.hover_pos)
212
+
213
  def reset_game(self):
214
  self.game = GoGame()
215
  self.hover_pos = None
 
217
 
218
  def create_interface():
219
  game = GradioGoGame()
 
220
  with gr.Blocks(theme=gr.themes.Soft()) as interface:
221
  gr.Markdown("""
222
  # Go Game vs AI
 
227
  - Capture enemy stones by surrounding them.
228
  - Stones must have liberties (empty adjacent points) to survive.
229
  """)
 
230
  with gr.Row():
231
  with gr.Column(scale=2):
232
  board_output = gr.Image(
 
242
  value="Click on an intersection to place a stone",
243
  lines=2
244
  )
 
245
  with gr.Column(scale=1):
246
  difficulty = gr.Radio(
247
  choices=[d.value for d in Difficulty],
 
249
  label="AI Difficulty"
250
  )
251
  reset_btn = gr.Button("Reset Game", variant="secondary")
252
+ # Update difficulty when the radio value changes.
253
+ def update_difficulty(value):
254
+ game.difficulty = Difficulty(value)
255
+ return value
256
+ difficulty.change(update_difficulty, inputs=[difficulty], outputs=[difficulty])
257
  # Use .select() for click events on the interactive image.
258
  board_output.select(
259
  game.process_click,
260
+ inputs=[], # No additional inputs: the event data is passed automatically.
261
  outputs=[board_output, msg_output]
262
  )
263
+ # (Removed mousemove event since gr.Image doesn't support it in your version.)
 
 
 
 
 
 
 
264
  reset_btn.click(
265
  game.reset_game,
266
  outputs=[board_output, msg_output]
267
  )
 
268
  return interface
269
 
270
  if __name__ == "__main__":