Spaces:
Sleeping
Sleeping
Update app.py
Browse files
app.py
CHANGED
|
@@ -2,6 +2,7 @@ import gradio as gr
|
|
| 2 |
import numpy as np
|
| 3 |
from enum import Enum
|
| 4 |
import random
|
|
|
|
| 5 |
|
| 6 |
class Difficulty(Enum):
|
| 7 |
EASY = "Easy"
|
|
@@ -15,135 +16,62 @@ class GoGame:
|
|
| 15 |
self.current_player = 1 # 1 for black, -1 for white
|
| 16 |
self.last_move = None
|
| 17 |
self.pass_count = 0
|
| 18 |
-
|
| 19 |
-
def is_valid_move(self, x, y):
|
| 20 |
-
if x < 0 or x >= self.size or y < 0 or y >= self.size:
|
| 21 |
-
return False
|
| 22 |
-
if self.board[x][y] != 0:
|
| 23 |
-
return False
|
| 24 |
-
# Make move temporarily to check for suicide rule
|
| 25 |
-
self.board[x][y] = self.current_player
|
| 26 |
-
has_liberties = self.has_liberties(x, y)
|
| 27 |
-
self.board[x][y] = 0
|
| 28 |
-
return has_liberties
|
| 29 |
-
|
| 30 |
-
def get_neighbors(self, x, y):
|
| 31 |
-
neighbors = []
|
| 32 |
-
for dx, dy in [(0, 1), (1, 0), (0, -1), (-1, 0)]:
|
| 33 |
-
nx, ny = x + dx, y + dy
|
| 34 |
-
if 0 <= nx < self.size and 0 <= ny < self.size:
|
| 35 |
-
neighbors.append((nx, ny))
|
| 36 |
-
return neighbors
|
| 37 |
-
|
| 38 |
-
def has_liberties(self, x, y):
|
| 39 |
-
color = self.board[x][y]
|
| 40 |
-
checked = set()
|
| 41 |
-
|
| 42 |
-
def check_group(x, y):
|
| 43 |
-
if (x, y) in checked:
|
| 44 |
-
return False
|
| 45 |
-
checked.add((x, y))
|
| 46 |
-
|
| 47 |
-
for nx, ny in self.get_neighbors(x, y):
|
| 48 |
-
if self.board[nx][ny] == 0:
|
| 49 |
-
return True
|
| 50 |
-
if self.board[nx][ny] == color and check_group(nx, ny):
|
| 51 |
-
return True
|
| 52 |
-
return False
|
| 53 |
-
|
| 54 |
-
return check_group(x, y)
|
| 55 |
-
|
| 56 |
-
def make_move(self, x, y):
|
| 57 |
-
if not self.is_valid_move(x, y):
|
| 58 |
-
return False
|
| 59 |
-
|
| 60 |
-
self.board[x][y] = self.current_player
|
| 61 |
-
self.last_move = (x, y)
|
| 62 |
-
self.pass_count = 0
|
| 63 |
-
self.current_player *= -1
|
| 64 |
-
return True
|
| 65 |
-
|
| 66 |
-
def ai_move(self, difficulty):
|
| 67 |
-
if difficulty == Difficulty.EASY:
|
| 68 |
-
return self.random_move()
|
| 69 |
-
elif difficulty == Difficulty.MEDIUM:
|
| 70 |
-
return self.medium_move()
|
| 71 |
-
else:
|
| 72 |
-
return self.hard_move()
|
| 73 |
-
|
| 74 |
-
def random_move(self):
|
| 75 |
-
valid_moves = []
|
| 76 |
-
for i in range(self.size):
|
| 77 |
-
for j in range(self.size):
|
| 78 |
-
if self.is_valid_move(i, j):
|
| 79 |
-
valid_moves.append((i, j))
|
| 80 |
-
|
| 81 |
-
if not valid_moves:
|
| 82 |
-
return None
|
| 83 |
-
|
| 84 |
-
move = random.choice(valid_moves)
|
| 85 |
-
self.make_move(*move)
|
| 86 |
-
return move
|
| 87 |
-
|
| 88 |
-
def medium_move(self):
|
| 89 |
-
# Medium difficulty tries to play near the last human move
|
| 90 |
-
if self.last_move is None:
|
| 91 |
-
return self.random_move()
|
| 92 |
-
|
| 93 |
-
last_x, last_y = self.last_move
|
| 94 |
-
for radius in range(1, self.size):
|
| 95 |
-
valid_moves = []
|
| 96 |
-
for dx in range(-radius, radius + 1):
|
| 97 |
-
for dy in range(-radius, radius + 1):
|
| 98 |
-
if abs(dx) + abs(dy) == radius:
|
| 99 |
-
x, y = last_x + dx, last_y + dy
|
| 100 |
-
if 0 <= x < self.size and 0 <= y < self.size and self.is_valid_move(x, y):
|
| 101 |
-
valid_moves.append((x, y))
|
| 102 |
-
|
| 103 |
-
if valid_moves:
|
| 104 |
-
move = random.choice(valid_moves)
|
| 105 |
-
self.make_move(*move)
|
| 106 |
-
return move
|
| 107 |
-
|
| 108 |
-
return self.random_move()
|
| 109 |
-
|
| 110 |
-
def hard_move(self):
|
| 111 |
-
# Hard difficulty tries to control the center and corners
|
| 112 |
-
center = self.size // 2
|
| 113 |
-
priority_moves = [
|
| 114 |
-
(center, center),
|
| 115 |
-
(1, 1), (1, self.size-2), (self.size-2, 1), (self.size-2, self.size-2),
|
| 116 |
-
(center, 1), (1, center), (center, self.size-2), (self.size-2, center)
|
| 117 |
-
]
|
| 118 |
-
|
| 119 |
-
for x, y in priority_moves:
|
| 120 |
-
if self.is_valid_move(x, y):
|
| 121 |
-
self.make_move(x, y)
|
| 122 |
-
return (x, y)
|
| 123 |
-
|
| 124 |
-
return self.medium_move()
|
| 125 |
|
| 126 |
def create_board_image(board):
|
| 127 |
-
#
|
| 128 |
-
|
| 129 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 130 |
|
| 131 |
# Draw grid lines
|
| 132 |
for i in range(board.shape[0]):
|
| 133 |
-
|
| 134 |
-
image
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
|
| 136 |
# Draw stones
|
| 137 |
for i in range(board.shape[0]):
|
| 138 |
for j in range(board.shape[1]):
|
| 139 |
-
if board[i, j]
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
|
| 144 |
-
#
|
| 145 |
-
|
| 146 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 147 |
|
| 148 |
class GradioGoGame:
|
| 149 |
def __init__(self):
|
|
@@ -154,7 +82,13 @@ class GradioGoGame:
|
|
| 154 |
if x is None or y is None:
|
| 155 |
return create_board_image(self.game.board), "Invalid move"
|
| 156 |
|
| 157 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 158 |
self.difficulty = Difficulty(difficulty)
|
| 159 |
|
| 160 |
if not self.game.make_move(x, y):
|
|
@@ -178,25 +112,25 @@ def create_interface():
|
|
| 178 |
gr.Markdown("# Go Game vs AI")
|
| 179 |
|
| 180 |
with gr.Row():
|
| 181 |
-
with gr.Column():
|
| 182 |
board_output = gr.Image(
|
| 183 |
value=create_board_image(game.game.board),
|
| 184 |
label="Go Board",
|
| 185 |
-
height=
|
| 186 |
-
width=
|
| 187 |
)
|
| 188 |
-
msg_output = gr.Textbox(label="Message", value="
|
| 189 |
|
| 190 |
-
with gr.Column():
|
| 191 |
-
x_input = gr.Number(label="X coordinate (0-8)")
|
| 192 |
-
y_input = gr.Number(label="Y coordinate (0-8)")
|
| 193 |
difficulty = gr.Radio(
|
| 194 |
choices=[d.value for d in Difficulty],
|
| 195 |
value=Difficulty.EASY.value,
|
| 196 |
label="Difficulty"
|
| 197 |
)
|
| 198 |
-
move_btn = gr.Button("Make Move")
|
| 199 |
-
reset_btn = gr.Button("Reset Game")
|
| 200 |
|
| 201 |
move_btn.click(
|
| 202 |
game.make_move,
|
|
|
|
| 2 |
import numpy as np
|
| 3 |
from enum import Enum
|
| 4 |
import random
|
| 5 |
+
import cv2
|
| 6 |
|
| 7 |
class Difficulty(Enum):
|
| 8 |
EASY = "Easy"
|
|
|
|
| 16 |
self.current_player = 1 # 1 for black, -1 for white
|
| 17 |
self.last_move = None
|
| 18 |
self.pass_count = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
def create_board_image(board):
|
| 21 |
+
# Create a larger base image (19x19 cells, each 40 pixels)
|
| 22 |
+
cell_size = 40
|
| 23 |
+
margin = 30
|
| 24 |
+
total_size = board.shape[0] * cell_size + 2 * margin
|
| 25 |
+
|
| 26 |
+
# Create base image with wooden texture color
|
| 27 |
+
image = np.full((total_size, total_size, 3), [219, 179, 119], dtype=np.uint8)
|
| 28 |
|
| 29 |
# Draw grid lines
|
| 30 |
for i in range(board.shape[0]):
|
| 31 |
+
# Vertical lines
|
| 32 |
+
cv2.line(image,
|
| 33 |
+
(margin + i * cell_size, margin),
|
| 34 |
+
(margin + i * cell_size, total_size - margin),
|
| 35 |
+
(0, 0, 0),
|
| 36 |
+
1)
|
| 37 |
+
# Horizontal lines
|
| 38 |
+
cv2.line(image,
|
| 39 |
+
(margin, margin + i * cell_size),
|
| 40 |
+
(total_size - margin, margin + i * cell_size),
|
| 41 |
+
(0, 0, 0),
|
| 42 |
+
1)
|
| 43 |
|
| 44 |
# Draw stones
|
| 45 |
for i in range(board.shape[0]):
|
| 46 |
for j in range(board.shape[1]):
|
| 47 |
+
if board[i, j] != 0:
|
| 48 |
+
center = (margin + j * cell_size, margin + i * cell_size)
|
| 49 |
+
color = (0, 0, 0) if board[i, j] == 1 else (255, 255, 255)
|
| 50 |
+
# Draw stone with anti-aliasing
|
| 51 |
+
cv2.circle(image, center, 15, color, -1, cv2.LINE_AA)
|
| 52 |
+
# Add a shine effect for 3D appearance
|
| 53 |
+
if board[i, j] == -1: # Only for white stones
|
| 54 |
+
cv2.circle(image,
|
| 55 |
+
(center[0] - 3, center[1] - 3),
|
| 56 |
+
5,
|
| 57 |
+
(240, 240, 240),
|
| 58 |
+
-1,
|
| 59 |
+
cv2.LINE_AA)
|
| 60 |
|
| 61 |
+
# Draw star points (hoshi)
|
| 62 |
+
star_points = []
|
| 63 |
+
if board.shape[0] == 9:
|
| 64 |
+
star_points = [(2, 2), (2, 6), (4, 4), (6, 2), (6, 6)]
|
| 65 |
+
|
| 66 |
+
for point in star_points:
|
| 67 |
+
cv2.circle(image,
|
| 68 |
+
(margin + point[1] * cell_size, margin + point[0] * cell_size),
|
| 69 |
+
3,
|
| 70 |
+
(0, 0, 0),
|
| 71 |
+
-1,
|
| 72 |
+
cv2.LINE_AA)
|
| 73 |
+
|
| 74 |
+
return image
|
| 75 |
|
| 76 |
class GradioGoGame:
|
| 77 |
def __init__(self):
|
|
|
|
| 82 |
if x is None or y is None:
|
| 83 |
return create_board_image(self.game.board), "Invalid move"
|
| 84 |
|
| 85 |
+
try:
|
| 86 |
+
x, y = int(x), int(y)
|
| 87 |
+
if x < 0 or x >= self.game.size or y < 0 or y >= self.game.size:
|
| 88 |
+
return create_board_image(self.game.board), "Invalid coordinates"
|
| 89 |
+
except:
|
| 90 |
+
return create_board_image(self.game.board), "Invalid input"
|
| 91 |
+
|
| 92 |
self.difficulty = Difficulty(difficulty)
|
| 93 |
|
| 94 |
if not self.game.make_move(x, y):
|
|
|
|
| 112 |
gr.Markdown("# Go Game vs AI")
|
| 113 |
|
| 114 |
with gr.Row():
|
| 115 |
+
with gr.Column(scale=2):
|
| 116 |
board_output = gr.Image(
|
| 117 |
value=create_board_image(game.game.board),
|
| 118 |
label="Go Board",
|
| 119 |
+
height=500,
|
| 120 |
+
width=500
|
| 121 |
)
|
| 122 |
+
msg_output = gr.Textbox(label="Message", value="Enter coordinates and click Make Move")
|
| 123 |
|
| 124 |
+
with gr.Column(scale=1):
|
| 125 |
+
x_input = gr.Number(label="X coordinate (0-8)", minimum=0, maximum=8)
|
| 126 |
+
y_input = gr.Number(label="Y coordinate (0-8)", minimum=0, maximum=8)
|
| 127 |
difficulty = gr.Radio(
|
| 128 |
choices=[d.value for d in Difficulty],
|
| 129 |
value=Difficulty.EASY.value,
|
| 130 |
label="Difficulty"
|
| 131 |
)
|
| 132 |
+
move_btn = gr.Button("Make Move", variant="primary")
|
| 133 |
+
reset_btn = gr.Button("Reset Game", variant="secondary")
|
| 134 |
|
| 135 |
move_btn.click(
|
| 136 |
game.make_move,
|