-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathhuman_play.py
More file actions
229 lines (188 loc) · 7.94 KB
/
human_play.py
File metadata and controls
229 lines (188 loc) · 7.94 KB
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
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
# Importing necessary packages and modules
from game import State
from pv_mcts import pv_mcts_action, random_action
from tensorflow.keras.models import load_model
import tkinter as tk
# Loading the best player's model
model = load_model('./model/best.keras')
# Defining the Game UI
class GameUI(tk.Frame):
# Initialization
def __init__(self, master=None, model=None):
tk.Frame.__init__(self, master)
self.master.title('Quoridor')
# Generating the game state
self.state = State()
self.N = self.state.N
self.D = 200 # Cell size (pixels)
self.L = self.N * self.D # Canvas size
self.select = -1 # Selection (-1: none, 0~(N*N-1): square)
self.placing_wall = False # Flag to indicate if we are placing a wall
# Creating the function for action selection using PV MCTS
self.next_action = pv_mcts_action(model) if model else random_action()
# Main frame layout
self.grid()
# Creating the canvas for the game board
self.c = tk.Canvas(self, width=self.L, height=self.L, highlightthickness=0)
self.c.bind('<Button-1>', self.turn_of_human)
self.c.grid(row=1, column=1, padx=10, pady=10)
# Displaying the player's walls on the left
self.player_walls_frame = tk.Frame(self)
self.player_walls_frame.grid(row=1, column=2, padx=10, pady=10)
self.player_walls = tk.Label(self.player_walls_frame, text="Player Walls", anchor="center", justify=tk.CENTER, font=('Helvetica', 24))
self.player_walls.pack()
# Displaying the enemy's walls on the right
self.enemy_walls_frame = tk.Frame(self)
self.enemy_walls_frame.grid(row=1, column=0, padx=10, pady=10)
self.enemy_walls = tk.Label(self.enemy_walls_frame, text="Enemy Walls", anchor="center", justify=tk.CENTER, font=('Helvetica', 24))
self.enemy_walls.pack()
# Displaying the action buttons below the game board
self.controls_frame = tk.Frame(self)
self.controls_frame.grid(row=2, column=1, padx=10, pady=10)
self.wall_button = tk.Button(self.controls_frame, text="Place Wall", command=self.place_wall_mode)
self.wall_button.pack()
self.wall_direction = tk.StringVar(value="horizontal")
self.wall_horizontal_button = tk.Radiobutton(self.controls_frame, text="Horizontal", variable=self.wall_direction, value="horizontal")
self.wall_vertical_button = tk.Radiobutton(self.controls_frame, text="Vertical", variable=self.wall_direction, value="vertical")
self.wall_horizontal_button.pack()
self.wall_vertical_button.pack()
# Result message
self.result_message = tk.Label(self, text="", font=('Helvetica', 60))
self.result_message.grid(row=0, column=1, pady=10)
# Updating the drawing
self.on_draw()
def place_wall_mode(self):
self.placing_wall = not self.placing_wall
self.wall_button.config(text="Move Piece" if self.placing_wall else "Place Wall")
# Human's turn
def turn_of_human(self, event):
N = self.N
D = self.D
# If the game is over
if self.state.is_done():
return
# If it is not the first player's turn
if not self.state.is_first_player():
return
# Calculate the selection and move position
if self.placing_wall:
x, y = (event.x - D // 2) // D, (event.y - D // 2) // D
print(x, y)
if 0 <= x < N - 1 and 0 <= y < N - 1:
self.place_wall(x, y)
else:
x, y = event.x // D, event.y // D
self.select = N * y + x
action = self.select
# Convert selection and move to action
# If the action is not legal
if not (action in self.state.legal_actions()):
self.select = -1
self.on_draw()
return
# Get the next state
self.state = self.state.next(action)
self.select = -1
self.on_draw()
# AI's turn
self.master.after(500, self.turn_of_ai)
def place_wall(self, x, y):
N = self.N
# Adjusted logic for placing walls at grid points
if self.wall_direction.get() == "horizontal":
action = N ** 2 + (N - 1) * y + x
else:
action = N ** 2 + (N - 1) ** 2 + (N - 1) * y + x
# Check if the action is legal
if action in self.state.legal_actions():
# Get the next state
self.state = self.state.next(action)
self.placing_wall = False
self.wall_button.config(text="Place Wall")
self.on_draw()
else:
self.placing_wall = False
self.wall_button.config(text="Place Wall")
self.on_draw()
# AI's turn
def turn_of_ai(self):
# If the game is over
if self.state.is_done():
self.display_result()
self.master.after(1000, self.reset_game)
return
# Get the action
action = self.next_action(self.state)
# Get the next state
self.state = self.state.next(action)
self.on_draw()
if self.state.is_done():
self.display_result()
self.master.after(1000, self.reset_game)
return
def display_result(self):
is_lose = self.state.is_lose() if self.state.is_first_player() else not self.state.is_lose()
if is_lose:
self.result_message.config(text="You Lose", fg="blue")
else:
self.result_message.config(text="You Win", fg="red")
def reset_game(self):
self.state = State()
self.on_draw()
self.result_message.config(text="")
# Draw the piece
def draw_piece(self, index, color):
N = self.N
D = self.D
x = (index % N) * D
y = (index // N) * D
margin = D // 10
self.c.create_oval(x + margin, y + margin, x + D - margin, y + D - margin, fill=color, outline='black')
# Draw the walls
def draw_walls(self):
N = self.N
D = self.D
for i in range(len(self.state.walls)):
x, y = i % (N - 1), i // (N - 1)
if self.state.walls[i] == 1:
x1, y1 = x * D, (y + 1) * D
x2, y2 = (x + 2) * D, (y + 1) * D
self.c.create_line(x1, y1, x2, y2, width=16.0, fill='#D1B575')
elif self.state.walls[i] == 2:
x1, y1 = (x + 1) * D, y * D
x2, y2 = (x + 1) * D, (y + 2) * D
self.c.create_line(x1, y1, x2, y2, width=16.0, fill='#D1B575')
# Update the drawing
def on_draw(self):
N = self.N
D = self.D
L = self.L
is_first_player = self.state.is_first_player()
# Grid
self.c.delete('all')
self.c.create_rectangle(0, 0, L, L, width=0.0, fill='#4B4B4B')
for i in range(1, N):
self.c.create_line(i * D, 0, i * D, L, width=16.0, fill='#8B0000')
self.c.create_line(0, i * D, L, i * D, width=16.0, fill='#8B0000')
# Pieces
p_pos = self.state.player[0] if is_first_player else self.state.enemy[0]
e_pos = self.state.enemy[0] if is_first_player else self.state.player[0]
e_pos = N ** 2 - 1 - e_pos
self.draw_piece(p_pos, '#D2B48C')
self.draw_piece(e_pos, '#5D3A3A')
p_walls = self.state.player[1] if is_first_player else self.state.enemy[1]
e_walls = self.state.enemy[1] if is_first_player else self.state.player[1]
# Update the wall count
self.player_walls.config(text=f"Player Walls\n{p_walls}")
self.enemy_walls.config(text=f"Enemy Walls\n{e_walls}")
if not is_first_player:
self.state.rotate_walls()
# Walls
self.draw_walls()
if not is_first_player:
self.state.rotate_walls()
# Run the game UI
if __name__ == '__main__':
f = GameUI(model=model)
f.pack()
f.mainloop()