-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathataxx.py
287 lines (253 loc) · 10.2 KB
/
ataxx.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
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
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
import numpy as np
import graphics
import pygame
from time import sleep
class AtaxxBoard:
def __init__(self, dim):
# Initialize the game board
self.size = dim
self.board = np.zeros(shape=(self.size, self.size), dtype=int)
self.player = 1
self.winner = 0
def copy(self):
# Create a deep copy of the current board state
copy = AtaxxBoard(self.size)
copy.board = np.copy(self.board)
copy.player = self.player
copy.winner = self.winner
return copy
def Start(self, render=False):
# Set up the initial pieces on the board
self.board[0][0] = 1
self.board[-1][-1] = 1
self.board[0][-1] = 2
self.board[-1][0] = 2
if render:
# Initialize pygame and set up graphics
pygame.init()
graphics.SET_GLOBALS("a", self.board)
graphics.SET_SCREEN()
def ShowBoard(self, filling=False):
# Display the current state of the game board
if not filling:
print(f"Player: {self.player}")
for i in range(self.size):
line = ""
for j in range(self.size):
line += str(self.board[i][j]) + " "
print(line)
print()
def PossibleMoves(self):
# Generate all possible valid moves for the current player
for row in range(self.size):
for col in range(self.size):
if self.board[row][col] == self.player:
for nextRow in range(row - 2, row + 3):
for nextCol in range(col - 2, col + 3):
if self.ValidMove(row, col, nextRow, nextCol):
yield (row, col, nextRow, nextCol)
def MoveToAction(self, move, fill_size=0):
# Convert a move to an action index
if fill_size == 0 or fill_size == self.size:
i1, j1, i2, j2 = move
size = self.size
action = j1 + i1 * size + j2 * (size ** 2) + i2 * (size ** 3)
return action
else:
i1, j1, i2, j2 = move
action = j1 + i1 * fill_size + j2 * (fill_size ** 2) + i2 * (fill_size ** 3)
return action
def ValidMove(self, row, col, nextRow, nextCol):
# Check if a move is valid
if self.board[row, col] != self.player:
return False
if nextRow < 0 or nextRow >= self.size or nextCol < 0 or nextCol >= self.size:
return False # Check if the next position is within the board
if nextRow == row and nextCol == col:
return False # Check if the play is staying on the same place
if self.board[nextRow, nextCol] != 0:
return False # Check if the next place is free to move to
if abs(nextRow - row) > 2 or abs(nextCol - col) > 2:
return False
if abs(nextRow - row) + abs(nextCol - col) == 3:
return False # Check for invalid moves on range 2
return True
def Move(self, moveList):
# Make a move on the board
x1, y1, x2, y2 = moveList
if abs(x2 - x1) > 1 or abs(y2 - y1) > 1:
self.board[x1][y1] = 0
self.board[x2][y2] = self.player
self.CapturePieces(x2, y2)
def CapturePieces(self, x, y):
# Capture opponent pieces surrounding the moved piece
for x2 in range(x - 1, x + 2):
for y2 in range(y - 1, y + 2):
if not (x2 < 0 or x2 >= self.size or y2 < 0 or y2 >= self.size):
if self.board[x2, y2] == 3 - self.player:
self.board[x2, y2] = self.player
def NextPlayer(self):
# Switch to the next player's turn
self.player = 3 - self.player
def Fill(self, render=False):
# Fill the empty spaces on the board with the opposite player's pieces
for i in range(self.size):
for j in range(self.size):
if self.board[i][j] == 0:
self.board[i][j] = 3 - self.player
if render:
self.ShowBoard(True)
graphics.unselect_piece()
graphics.draw_board(self.board)
pygame.display.flip()
sleep(1 / (2 * self.size))
def PieceCount(self):
# Count the number of pieces for each player
count1 = 0
count2 = 0
for i in range(self.size):
for j in range(self.size):
if self.board[i][j] == 1:
count1 += 1
elif self.board[i][j] == 2:
count2 += 1
return count1, count2
def score(self, player):
# Calculate the score difference between players
points = self.PieceCount()
if player == 1:
return points[0] - points[1]
return points[1] - points[0]
def minimax(self, depth, max_player, alpha, beta, player):
# Minimax algorithm with alpha-beta pruning for finding the best move
self.CheckFinish()
if self.winner == player:
return None, self.size ** 2 + 1
if self.winner == 3 - player:
return None, -(self.size ** 2 + 1)
if depth == 0:
return None, self.score(player)
if max_player:
value = -np.inf
for move in self.PossibleMoves():
copy = self.copy()
copy.Move(move)
copy.NextPlayer()
new = copy.minimax(depth - 1, False, alpha, beta, player)[1]
if new > value:
value = new
best = move
if value >= beta:
break
alpha = max(alpha, value)
return best, value
else:
value = np.inf
for move in self.PossibleMoves():
copy = self.copy()
copy.Move(move)
copy.NextPlayer()
new = copy.minimax(depth - 1, True, alpha, beta, player)[1]
if new < value:
value = new
best = move
if value <= alpha:
break
beta = min(beta, value)
return best, value
def CheckFinish(self, render=False):
# Check if the game has finished and determine the winner
if len(list(self.PossibleMoves())) == 0:
self.Fill(render)
c1, c2 = self.PieceCount()
if c1 > c2:
self.winner = 1
elif c1 < c2:
self.winner = 2
else:
self.winner = 3
def hasFinished(self):
# Check if the game has finished
return self.winner != 0
def rotate90(self, times):
# Rotate the game board 90 degrees clockwise
copy = self.copy()
copy.board = np.rot90(copy.board, times)
return copy
def flip_vertical(self):
# Flip the game board vertically
copy = self.copy()
copy.board = np.flip(copy.board, 0)
return copy
def EncodedGameState(self):
# Encode the current game state for input to a neural network
encoded_state = np.stack(
(self.board == 1, self.board == 2, self.board == 0)
).astype(np.float32)
return encoded_state
def EncodedGameStateChanged(self, fill_size=0):
# Encode the game state depending on the current player for input to a neural network
encoded_state = np.stack(
(self.board == self.player, self.board == 3 - self.player, self.board == 0)
).astype(np.float32)
if fill_size == 0 or fill_size == self.size:
return encoded_state
else:
encoded = []
for state in encoded_state:
copy = np.copy(state)
filled = np.pad(copy, (0, fill_size - self.size), 'constant', constant_values=(-1))
encoded.append(filled)
return np.array(encoded)
def GameLoop():
# Get the size of the board from user input
size = int(input("Size: "))
# Initialize the game board
board = AtaxxBoard(size)
# Set up the initial game state and display
board.Start(render=True)
graphics.draw_board(board.board)
pygame.display.flip()
board.ShowBoard()
# Main game loop
while board.winner == 0:
# Player's move input loop
selected = False
while not selected:
# Get the starting position of the piece to move
y1, x1 = graphics.piece_index_click()
# Check if the selected position contains the player's piece
while board.board[y1][x1] != board.player:
print("Invalid Position")
y1, x1 = graphics.piece_index_click()
# Highlight the selected piece
graphics.set_selected_piece(y1, x1)
graphics.draw_board(board.board)
pygame.display.flip()
# Get the destination position for the move
y2, x2 = graphics.piece_index_click()
# Check if the move is valid
if not board.ValidMove(y1, x1, y2, x2):
print("Invalid Move")
# Unselect the piece and redraw the board
graphics.unselect_piece()
graphics.draw_board(board.board)
pygame.display.flip()
else:
selected = True
# Make the move and update the game state
board.Move([y1, x1, y2, x2])
board.NextPlayer()
graphics.unselect_piece()
board.ShowBoard()
graphics.draw_board(board.board)
pygame.display.flip()
# Check if the game has finished
board.CheckFinish()
# Display the game result
if board.winner == 3:
print("Tie")
else:
print(f"Player {board.winner} wins")
if __name__ == "__main__":
GameLoop()