mirror of
https://github.com/kuhyx/WUT_Computer_Science.git
synced 2026-07-04 21:03:07 +02:00
feat: set up alpha beta
This commit is contained in:
parent
108121a4ed
commit
d4cb446264
362
lab2/main.py
362
lab2/main.py
@ -1,4 +1,5 @@
|
||||
import re
|
||||
import copy
|
||||
"""
|
||||
Program that plays draughts (checkers) with user on 8x8 board using min-max with alpha-beta prunning
|
||||
"""
|
||||
@ -11,8 +12,6 @@ class Game:
|
||||
self.board_size = size
|
||||
self.white_positions = self.initialize_white()
|
||||
self.black_positions = self.initialize_black()
|
||||
# print(self.white_positions)
|
||||
# print(self.black_positions)
|
||||
|
||||
def initialize_white(self):
|
||||
"""Initialize white pieces"""
|
||||
@ -45,14 +44,14 @@ class Game:
|
||||
return black_positions
|
||||
|
||||
def check_move_out_of_bounds(self, from_, to):
|
||||
"""Check if the move destination is out of the bounds of the board"""
|
||||
if to[0] < 0 or to[0] >= self.board_size - 1:
|
||||
print(f"Illegal move! Final x coordinate must be between 0 and {self.board_size}!")
|
||||
return True
|
||||
if to[1] < 0 or to[1] >= self.board_size - 1:
|
||||
print(f"Illegal move! Final y coordinate must be between 0 and {self.board_size}!")
|
||||
return True
|
||||
return False
|
||||
"""Check if the move destination is out of the bounds of the board"""
|
||||
if to[0] < 0 or to[0] > self.board_size - 1:
|
||||
# print(f"Illegal move! Final x coordinate must be between 0 and {self.board_size-1}!")
|
||||
return True
|
||||
if to[1] < 0 or to[1] > self.board_size - 1:
|
||||
# print(f"Illegal move! Final y coordinate must be between 0 and {self.board_size-1}!")
|
||||
return True
|
||||
return False
|
||||
|
||||
def check_piece_exists(self, coords, color):
|
||||
"""Check if a piece of given color exists at a given spot"""
|
||||
@ -90,7 +89,7 @@ class Game:
|
||||
if (abs(to[0]-from_[0]) != 2 or abs(to[1]-from_[1] != 2)):
|
||||
return None
|
||||
|
||||
middle = (abs(to[0]-from_[0])//2, abs(to[1]-from_[1])//2)
|
||||
middle = (abs(to[0]+from_[0])//2, abs(to[1]+from_[1])//2)
|
||||
|
||||
if color == "white" and self.check_piece_exists(middle, "black"):
|
||||
return middle
|
||||
@ -101,21 +100,29 @@ class Game:
|
||||
def check_move_legal(self, from_, to, color):
|
||||
"""Check if a move is legal. Return a boolean or coordinates of captured piece"""
|
||||
if self.check_move_out_of_bounds(from_, to):
|
||||
print("Illegal move! Final position is out of the bounds of the board")
|
||||
if from_ == (2, 3):
|
||||
print(f"To: {to}")
|
||||
print("Illegal move! Final position is out of the bounds of the board")
|
||||
return False
|
||||
if not self.check_piece_exists(from_, color):
|
||||
print(
|
||||
"Illegal move! There is no piece on the starting position that belongs to the player")
|
||||
if from_ == (2, 3):
|
||||
print(f"To: {to}")
|
||||
print(
|
||||
"Illegal move! There is no piece on the starting position that belongs to the player")
|
||||
return False
|
||||
if self.check_piece_exists(to, "white") or self.check_piece_exists(to, "black"):
|
||||
print("Illegal move! Cannot move to position taken by another piece")
|
||||
if from_ == (2, 3):
|
||||
print(f"To: {to}")
|
||||
print("Illegal move! Cannot move to position taken by another piece")
|
||||
return False
|
||||
capture = self.check_capture(from_, to, color)
|
||||
if capture == None:
|
||||
if self.check_move_piece_capable(from_, to, color):
|
||||
return True
|
||||
else:
|
||||
print('Illegal move!')
|
||||
if from_ == (2, 3):
|
||||
print(f"To: {to}")
|
||||
print('Illegal move!')
|
||||
return False
|
||||
return capture
|
||||
|
||||
@ -143,163 +150,186 @@ class Game:
|
||||
return True
|
||||
|
||||
def print_board(self):
|
||||
"""Print the board in the console"""
|
||||
print(" ", end="")
|
||||
for x in range(self.board_size):
|
||||
print(f" {chr(ord('a')+x)} ", end="")
|
||||
print(" ")
|
||||
line = 0
|
||||
for y in range(self.board_size*4):
|
||||
"""Print the board in the console"""
|
||||
print(" ", end="")
|
||||
for x in range(self.board_size):
|
||||
bg = "#" if x%2==(y//4)%2 else " "
|
||||
checker = bg
|
||||
if((x, y//4, False) in self.white_positions):
|
||||
checker = "w"
|
||||
elif((x, y//4, True) in self.white_positions):
|
||||
checker = "W"
|
||||
elif((x, y//4, False) in self.black_positions):
|
||||
checker = "b"
|
||||
elif((x, y//4, True) in self.black_positions):
|
||||
checker = "B"
|
||||
print(f" {chr(ord('a')+x)} ", end="")
|
||||
print(" ")
|
||||
line = 0
|
||||
for y in range(self.board_size*4):
|
||||
for x in range(self.board_size):
|
||||
bg = "#" if x % 2 == (y//4) % 2 else " "
|
||||
checker = bg
|
||||
if ((x, y//4, False) in self.white_positions):
|
||||
checker = "w"
|
||||
elif ((x, y//4, True) in self.white_positions):
|
||||
checker = "W"
|
||||
elif ((x, y//4, False) in self.black_positions):
|
||||
checker = "b"
|
||||
elif ((x, y//4, True) in self.black_positions):
|
||||
checker = "B"
|
||||
|
||||
if(x==0):
|
||||
if(line%4==2):
|
||||
print('{:3d}'.format(y//4), end="")
|
||||
if (x == 0):
|
||||
if (line % 4 == 2):
|
||||
print('{:3d}'.format(y//4), end="")
|
||||
else:
|
||||
print(" ", end="")
|
||||
|
||||
match line % 4:
|
||||
case 0:
|
||||
print("+---", end="")
|
||||
case 1 | 3:
|
||||
print(f"|{3*bg}", end="")
|
||||
case 2:
|
||||
print(f"|{bg}{checker}{bg}", end="")
|
||||
if (line % 4 == 0):
|
||||
print("+")
|
||||
else:
|
||||
print(" ", end="")
|
||||
|
||||
match line%4:
|
||||
case 0:
|
||||
print("+---", end="")
|
||||
case 1 | 3:
|
||||
print(f"|{3*bg}", end="")
|
||||
case 2:
|
||||
print(f"|{bg}{checker}{bg}", end="")
|
||||
if(line%4==0):
|
||||
print("+")
|
||||
else:
|
||||
print(f"|{y//4}" if line%4==2 else "|")
|
||||
line+=1
|
||||
print(" ", end="")
|
||||
for x in range(self.board_size):
|
||||
print("+---", end="")
|
||||
print("+\n ", end="")
|
||||
for x in range(self.board_size):
|
||||
print(f" {chr(ord('a')+x)}", end=" ")
|
||||
print()
|
||||
|
||||
# Ran first in the code
|
||||
def get_possible_move_down(self, from_, color):
|
||||
# all moves "down":
|
||||
legal_moves = []
|
||||
move_down_left_two = (from_[0] + 2, from_[1] - 2)
|
||||
move_down_right_two = (from_[0] + 2, from_[1] + 2)
|
||||
move_down_left_one = (from_[0] + 1, from_[1] - 1)
|
||||
move_down_right_one = (from_[0] + 1, from_[1] + 1)
|
||||
if self.check_move_legal(from_, move_down_left_two, color) != False:
|
||||
legal_moves.append((from_, move_down_two))
|
||||
elif self.check_move_legal(from_, move_down_right_two, color) != False:
|
||||
legal_moves.append((from_, move_down_right_two))
|
||||
elif self.check_move_legal(from_, move_down_left_one, color) != False:
|
||||
legal_moves.append((from_, move_down_left_one))
|
||||
elif self.check_move_legal(from_, move_down_right_one, color) != False:
|
||||
legal_moves.append((from_, move_down_right_one))
|
||||
return legal_moves
|
||||
print(f"|{y//4}" if line % 4 == 2 else "|")
|
||||
line += 1
|
||||
print(" ", end="")
|
||||
for x in range(self.board_size):
|
||||
print("+---", end="")
|
||||
print("+\n ", end="")
|
||||
for x in range(self.board_size):
|
||||
print(f" {chr(ord('a')+x)}", end=" ")
|
||||
print()
|
||||
|
||||
def get_possible_move(self, from_, color):
|
||||
# get all possible moves from the pawn in from_ position
|
||||
# "-" in first position -> move to the "left"
|
||||
# "+" in first postiin -> move to the "right"
|
||||
# "-" in second postion -> move "up"
|
||||
# "+" in second position -> move "down"
|
||||
legal_moves = self.get_possible_move_up(from_, color)
|
||||
legal_moves.append(self.get_possible_move_down(from_, color))
|
||||
return legal_moves
|
||||
# Ran first in the code
|
||||
def get_possible_move_capture(self, from_, color):
|
||||
print(f"Checking piece {from_}")
|
||||
# all capturing moves:
|
||||
legal_moves = []
|
||||
move_down_left_two = (from_[0] + 2, from_[1] - 2)
|
||||
move_down_right_two = (from_[0] + 2, from_[1] + 2)
|
||||
move_up_left_two = (from_[0] - 2, from_[1] - 2)
|
||||
move_up_right_two = (from_[0] - 2, from_[1] + 2)
|
||||
if self.check_move_legal(from_, move_down_left_two, color) != False:
|
||||
legal_moves.append((from_, move_down_left_two))
|
||||
if self.check_move_legal(from_, move_down_right_two, color) != False:
|
||||
legal_moves.append((from_, move_down_right_two))
|
||||
if self.check_move_legal(from_, move_up_left_two, color) != False:
|
||||
legal_moves.append((from_, move_up_left_two))
|
||||
if self.check_move_legal(from_, move_up_right_two, color) != False:
|
||||
legal_moves.append((from_, move_up_right_two))
|
||||
return legal_moves
|
||||
|
||||
def get_possible_move_non_capture(self, from_, color):
|
||||
# all non-capturing moves
|
||||
legal_moves = []
|
||||
move_down_left_one = (from_[0] + 1, from_[1] - 1)
|
||||
move_down_right_one = (from_[0] + 1, from_[1] + 1)
|
||||
move_up_left_one = (from_[0] - 1, from_[1] - 1)
|
||||
move_up_right_one = (from_[0] - 1, from_[1] + 1)
|
||||
if self.check_move_legal(from_, move_down_left_one, color) != False:
|
||||
legal_moves.append((from_, move_down_left_one))
|
||||
if self.check_move_legal(from_, move_down_right_one, color) != False:
|
||||
legal_moves.append((from_, move_down_right_one))
|
||||
if self.check_move_legal(from_, move_up_left_one, color) != False:
|
||||
legal_moves.append((from_, move_up_left_one))
|
||||
if self.check_move_legal(from_, move_up_right_one, color) != False:
|
||||
legal_moves.append((from_, move_up_right_one))
|
||||
return legal_moves
|
||||
|
||||
def get_possible_moves(self, color):
|
||||
# toDo: get possible moves for single piece, then iterate through all pieces
|
||||
legal_moves = []
|
||||
if color == "white":
|
||||
for white_position in self.white_positions:
|
||||
print((white_position[0], white_position[1]))
|
||||
legal_moves.append(self.get_possible_move((white_position[0], white_position[1]), color))
|
||||
elif color == "black":
|
||||
for black_position in self.black_positions:
|
||||
legal_moves.append(self.get_possible_move((black_position[0], black_position[1]), color))
|
||||
return legal_moves
|
||||
|
||||
def alpha_beta(self, depth, alpha, beta, player):
|
||||
if depth == 0:
|
||||
return self.evaluate(), None
|
||||
|
||||
if player == 'x':
|
||||
max_eval = float('-inf')
|
||||
best_move = None
|
||||
|
||||
for move in self.get_possible_moves(player):
|
||||
new_state = copy.deepcopy(self)
|
||||
new_state.move(*move)
|
||||
eval, _ = alpha_beta(new_state, depth-1, alpha, beta, 'o')
|
||||
|
||||
if eval > max_eval:
|
||||
max_eval = eval
|
||||
best_move = move
|
||||
|
||||
alpha = max(alpha, eval)
|
||||
|
||||
if alpha >= beta:
|
||||
break
|
||||
|
||||
return max_eval, best_move
|
||||
|
||||
else:
|
||||
min_eval = float('inf')
|
||||
best_move = None
|
||||
|
||||
for move in state.get_possible_moves(player):
|
||||
new_state = copy.deepcopy(state)
|
||||
new_state.move(*move)
|
||||
eval, _ = alpha_beta(new_state, depth-1, alpha, beta, 'x')
|
||||
|
||||
if eval < min_eval:
|
||||
min_eval = eval
|
||||
best_move = move
|
||||
|
||||
beta = min(beta, eval)
|
||||
|
||||
if beta <= alpha:
|
||||
break
|
||||
|
||||
return min_eval, best_move
|
||||
|
||||
# toDo: get possible moves for single piece, then iterate through all pieces
|
||||
legal_moves = []
|
||||
captures = []
|
||||
if color == "white":
|
||||
for white_position in self.white_positions:
|
||||
# print((white_position[0], white_position[1]))
|
||||
legal_moves += self.get_possible_move_non_capture(
|
||||
(white_position[0], white_position[1]), color)
|
||||
captures += self.get_possible_move_capture(
|
||||
(white_position[0], white_position[1]), color)
|
||||
elif color == "black":
|
||||
for black_position in self.black_positions:
|
||||
legal_moves += self.get_possible_move_non_capture(
|
||||
(black_position[0], black_position[1]), color)
|
||||
captures += self.get_possible_move_capture(
|
||||
(black_position[0], black_position[1]), color)
|
||||
print('CAPTURES: ')
|
||||
print(captures)
|
||||
# if len(captures) > 0:
|
||||
# print('CAPTURE POSSIBLE')
|
||||
# print(captures)
|
||||
# return captures
|
||||
print('LEGAL MOVES')
|
||||
print(legal_moves + captures)
|
||||
return legal_moves + captures
|
||||
|
||||
def alpha_beta(self, depth, alpha, beta, color):
|
||||
if depth == 0:
|
||||
return self.evaluate(), None
|
||||
|
||||
if color == 'white':
|
||||
max_eval = float('-inf')
|
||||
best_move = None
|
||||
|
||||
for move in self.get_possible_moves(color):
|
||||
new_state = copy.deepcopy(self)
|
||||
new_state.make_move(*move, 'white')
|
||||
eval, _ = new_state.alpha_beta(depth-1, alpha, beta, 'black')
|
||||
|
||||
if eval > max_eval:
|
||||
max_eval = eval
|
||||
best_move = move
|
||||
|
||||
alpha = max(alpha, eval)
|
||||
|
||||
if alpha >= beta:
|
||||
break
|
||||
|
||||
return max_eval, best_move
|
||||
|
||||
else:
|
||||
min_eval = float('inf')
|
||||
best_move = None
|
||||
|
||||
for move in self.get_possible_moves(color):
|
||||
new_state = copy.deepcopy(self)
|
||||
new_state.make_move(*move, 'black')
|
||||
eval, _ = new_state.alpha_beta(depth-1, alpha, beta, 'white')
|
||||
|
||||
if eval < min_eval:
|
||||
min_eval = eval
|
||||
best_move = move
|
||||
|
||||
beta = min(beta, eval)
|
||||
|
||||
if beta <= alpha:
|
||||
break
|
||||
|
||||
return min_eval, best_move
|
||||
|
||||
def evaluate(self):
|
||||
white_score = 0
|
||||
black_score = 0
|
||||
|
||||
for white_position in self.white_positions:
|
||||
if white_position[2]:
|
||||
white_score += 2
|
||||
else:
|
||||
white_score += 2
|
||||
|
||||
for black_position in self.black_positions:
|
||||
if black_position[2]:
|
||||
black_score += 2
|
||||
else:
|
||||
black_score += 2
|
||||
|
||||
return x_score - o_score
|
||||
white_score = 0
|
||||
black_score = 0
|
||||
|
||||
for white_position in self.white_positions:
|
||||
if white_position[2]:
|
||||
white_score += 10
|
||||
else:
|
||||
white_score += 5
|
||||
|
||||
for black_position in self.black_positions:
|
||||
if black_position[2]:
|
||||
black_score += 10
|
||||
else:
|
||||
black_score += 5
|
||||
|
||||
return white_score - black_score
|
||||
|
||||
def input_to_coordinates(self, input):
|
||||
pos_x = ord(input[0])-ord('a')
|
||||
pos_y = int(input[1::])
|
||||
return pos_x, pos_y
|
||||
pos_x = ord(input[0])-ord('a')
|
||||
pos_y = int(input[1::])
|
||||
return pos_x, pos_y
|
||||
|
||||
def handle_player_move(self, color):
|
||||
has_moved = False
|
||||
while not has_moved:
|
||||
user_input = input('How do you want to move? (format: d6 e5)\n')
|
||||
regex = r"^[A-Za-z]\d+\s[A-Za-z]\d+$"
|
||||
regex = r"^[a-z]\d+\s[a-z]\d+$"
|
||||
match = re.search(regex, user_input)
|
||||
if not match:
|
||||
print('Invalid input, try again')
|
||||
@ -312,9 +342,17 @@ class Game:
|
||||
self.print_board()
|
||||
|
||||
|
||||
|
||||
# Ran first in the code
|
||||
if __name__ == "__main__":
|
||||
game = Game(8)
|
||||
game.print_board()
|
||||
game.handle_player_move('black')
|
||||
while True:
|
||||
game.print_board()
|
||||
possible_moves_black = game.get_possible_moves('black')
|
||||
if len(possible_moves_black) == 0:
|
||||
print('Game over. White wins')
|
||||
game.handle_player_move('black')
|
||||
possible_moves_white = game.get_possible_moves('black')
|
||||
if len(possible_moves_white) == 0:
|
||||
print('Game over. Black wins')
|
||||
_, ai_move = game.alpha_beta(1, 5, 10, 'white')
|
||||
game.make_move(ai_move[0], ai_move[1], 'white')
|
||||
|
||||
Loading…
Reference in New Issue
Block a user