mirror of
https://github.com/kuhyx/WUT_Computer_Science.git
synced 2026-07-06 10:23:15 +02:00
feat: fine tuning (WIP)
This commit is contained in:
parent
d4cb446264
commit
474ffde3a5
141
lab2/main.py
141
lab2/main.py
@ -1,7 +1,7 @@
|
|||||||
import re
|
import re
|
||||||
import copy
|
import copy
|
||||||
"""
|
"""
|
||||||
Program that plays draughts (checkers) with user on 8x8 board using min-max with alpha-beta prunning
|
Program that plays draughts (checkers) with user on 8x8 board using min-max with alpha-beta pruning
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
@ -56,10 +56,10 @@ class Game:
|
|||||||
def check_piece_exists(self, coords, color):
|
def check_piece_exists(self, coords, color):
|
||||||
"""Check if a piece of given color exists at a given spot"""
|
"""Check if a piece of given color exists at a given spot"""
|
||||||
if color == "white":
|
if color == "white":
|
||||||
if any(piece in self.white_positions for piece in ((*coords, False), (*coords, False))):
|
if any(piece in self.white_positions for piece in ((*coords, False), (*coords, True))):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
if any(piece in self.black_positions for piece in ((*coords, False), (*coords, False))):
|
if any(piece in self.black_positions for piece in ((*coords, False), (*coords, True))):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
@ -86,7 +86,8 @@ class Game:
|
|||||||
def check_capture(self, from_, to, color):
|
def check_capture(self, from_, to, color):
|
||||||
"""Check if a piece was captured for a given move. Return captured piece coordinates or None"""
|
"""Check if a piece was captured for a given move. Return captured piece coordinates or None"""
|
||||||
# captures can only happen if the player moved twice-diagonally
|
# captures can only happen if the player moved twice-diagonally
|
||||||
if (abs(to[0]-from_[0]) != 2 or abs(to[1]-from_[1] != 2)):
|
|
||||||
|
if (abs(to[0]-from_[0]) != 2 or abs(to[1]-from_[1]) != 2):
|
||||||
return None
|
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)
|
||||||
@ -97,22 +98,19 @@ class Game:
|
|||||||
return middle
|
return middle
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def check_move_legal(self, from_, to, color):
|
def check_move_legal(self, from_, to, color, give_feedback=False):
|
||||||
"""Check if a move is legal. Return a boolean or coordinates of captured piece"""
|
"""Check if a move is legal. Return a boolean or coordinates of captured piece"""
|
||||||
if self.check_move_out_of_bounds(from_, to):
|
if self.check_move_out_of_bounds(from_, to):
|
||||||
if from_ == (2, 3):
|
if give_feedback:
|
||||||
print(f"To: {to}")
|
|
||||||
print("Illegal move! Final position is out of the bounds of the board")
|
print("Illegal move! Final position is out of the bounds of the board")
|
||||||
return False
|
return False
|
||||||
if not self.check_piece_exists(from_, color):
|
if not self.check_piece_exists(from_, color):
|
||||||
if from_ == (2, 3):
|
if give_feedback:
|
||||||
print(f"To: {to}")
|
|
||||||
print(
|
print(
|
||||||
"Illegal move! There is no piece on the starting position that belongs to the player")
|
"Illegal move! There is no piece on the starting position that belongs to the player")
|
||||||
return False
|
return False
|
||||||
if self.check_piece_exists(to, "white") or self.check_piece_exists(to, "black"):
|
if self.check_piece_exists(to, "white") or self.check_piece_exists(to, "black"):
|
||||||
if from_ == (2, 3):
|
if give_feedback:
|
||||||
print(f"To: {to}")
|
|
||||||
print("Illegal move! Cannot move to position taken by another piece")
|
print("Illegal move! Cannot move to position taken by another piece")
|
||||||
return False
|
return False
|
||||||
capture = self.check_capture(from_, to, color)
|
capture = self.check_capture(from_, to, color)
|
||||||
@ -120,9 +118,8 @@ class Game:
|
|||||||
if self.check_move_piece_capable(from_, to, color):
|
if self.check_move_piece_capable(from_, to, color):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
if from_ == (2, 3):
|
if give_feedback:
|
||||||
print(f"To: {to}")
|
print('Illegal move! You can only move diagonally')
|
||||||
print('Illegal move!')
|
|
||||||
return False
|
return False
|
||||||
return capture
|
return capture
|
||||||
|
|
||||||
@ -136,17 +133,24 @@ class Game:
|
|||||||
|
|
||||||
if (color == "white"):
|
if (color == "white"):
|
||||||
self.white_positions.remove((*from_, king))
|
self.white_positions.remove((*from_, king))
|
||||||
self.white_positions.append((*to, king))
|
|
||||||
if (capture):
|
if (capture):
|
||||||
captured_king = self.check_piece_king(capture, "black")
|
captured_king = self.check_piece_king(capture, "black")
|
||||||
self.black_positions.remove((*move_legal, captured_king))
|
self.black_positions.remove((*move_legal, captured_king))
|
||||||
|
if to[1] == self.board_size-1:
|
||||||
|
self.white_positions.append((*to, True))
|
||||||
|
else:
|
||||||
|
self.white_positions.append((*to, king))
|
||||||
|
|
||||||
else:
|
else:
|
||||||
self.black_positions.remove((*from_, king))
|
self.black_positions.remove((*from_, king))
|
||||||
self.black_positions.append((*to, king))
|
|
||||||
if (capture):
|
if (capture):
|
||||||
captured_king = self.check_piece_king(capture, "white")
|
captured_king = self.check_piece_king(capture, "white")
|
||||||
self.white_positions.remove((*move_legal, captured_king))
|
self.white_positions.remove((*move_legal, captured_king))
|
||||||
|
if to[1] == 0:
|
||||||
|
self.black_positions.append((*to, True))
|
||||||
|
else:
|
||||||
|
self.black_positions.append((*to, king))
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def print_board(self):
|
def print_board(self):
|
||||||
@ -196,8 +200,7 @@ class Game:
|
|||||||
print()
|
print()
|
||||||
|
|
||||||
# Ran first in the code
|
# Ran first in the code
|
||||||
def get_possible_move_capture(self, from_, color):
|
def get_possible_moves_capture(self, from_, color):
|
||||||
print(f"Checking piece {from_}")
|
|
||||||
# all capturing moves:
|
# all capturing moves:
|
||||||
legal_moves = []
|
legal_moves = []
|
||||||
move_down_left_two = (from_[0] + 2, from_[1] - 2)
|
move_down_left_two = (from_[0] + 2, from_[1] - 2)
|
||||||
@ -214,7 +217,7 @@ class Game:
|
|||||||
legal_moves.append((from_, move_up_right_two))
|
legal_moves.append((from_, move_up_right_two))
|
||||||
return legal_moves
|
return legal_moves
|
||||||
|
|
||||||
def get_possible_move_non_capture(self, from_, color):
|
def get_possible_moves_non_capture(self, from_, color):
|
||||||
# all non-capturing moves
|
# all non-capturing moves
|
||||||
legal_moves = []
|
legal_moves = []
|
||||||
move_down_left_one = (from_[0] + 1, from_[1] - 1)
|
move_down_left_one = (from_[0] + 1, from_[1] - 1)
|
||||||
@ -238,38 +241,38 @@ class Game:
|
|||||||
if color == "white":
|
if color == "white":
|
||||||
for white_position in self.white_positions:
|
for white_position in self.white_positions:
|
||||||
# print((white_position[0], white_position[1]))
|
# print((white_position[0], white_position[1]))
|
||||||
legal_moves += self.get_possible_move_non_capture(
|
legal_moves += self.get_possible_moves_non_capture(
|
||||||
(white_position[0], white_position[1]), color)
|
(white_position[0], white_position[1]), color)
|
||||||
captures += self.get_possible_move_capture(
|
captures += self.get_possible_moves_capture(
|
||||||
(white_position[0], white_position[1]), color)
|
(white_position[0], white_position[1]), color)
|
||||||
elif color == "black":
|
elif color == "black":
|
||||||
for black_position in self.black_positions:
|
for black_position in self.black_positions:
|
||||||
legal_moves += self.get_possible_move_non_capture(
|
legal_moves += self.get_possible_moves_non_capture(
|
||||||
(black_position[0], black_position[1]), color)
|
(black_position[0], black_position[1]), color)
|
||||||
captures += self.get_possible_move_capture(
|
captures += self.get_possible_moves_capture(
|
||||||
(black_position[0], black_position[1]), color)
|
(black_position[0], black_position[1]), color)
|
||||||
print('CAPTURES: ')
|
|
||||||
print(captures)
|
if len(captures) > 0:
|
||||||
# if len(captures) > 0:
|
return captures
|
||||||
# print('CAPTURE POSSIBLE')
|
|
||||||
# print(captures)
|
|
||||||
# return captures
|
|
||||||
print('LEGAL MOVES')
|
|
||||||
print(legal_moves + captures)
|
|
||||||
return legal_moves + captures
|
return legal_moves + captures
|
||||||
|
|
||||||
def alpha_beta(self, depth, alpha, beta, color):
|
def alpha_beta(self, depth, alpha, beta, color, current_color=None):
|
||||||
if depth == 0:
|
if current_color == None:
|
||||||
return self.evaluate(), None
|
current_color = color
|
||||||
|
|
||||||
if color == 'white':
|
if depth == 0:
|
||||||
|
return self.evaluate(color), None
|
||||||
|
|
||||||
|
opposite_color = 'white' if current_color == 'black' else 'black'
|
||||||
|
if current_color == color:
|
||||||
max_eval = float('-inf')
|
max_eval = float('-inf')
|
||||||
best_move = None
|
best_move = None
|
||||||
|
|
||||||
for move in self.get_possible_moves(color):
|
for move in self.get_possible_moves(current_color):
|
||||||
new_state = copy.deepcopy(self)
|
new_state = copy.deepcopy(self)
|
||||||
new_state.make_move(*move, 'white')
|
new_state.make_move(*move, current_color)
|
||||||
eval, _ = new_state.alpha_beta(depth-1, alpha, beta, 'black')
|
eval, _ = new_state.alpha_beta(
|
||||||
|
depth-1, alpha, beta, color, opposite_color)
|
||||||
|
|
||||||
if eval > max_eval:
|
if eval > max_eval:
|
||||||
max_eval = eval
|
max_eval = eval
|
||||||
@ -286,10 +289,11 @@ class Game:
|
|||||||
min_eval = float('inf')
|
min_eval = float('inf')
|
||||||
best_move = None
|
best_move = None
|
||||||
|
|
||||||
for move in self.get_possible_moves(color):
|
for move in self.get_possible_moves(current_color):
|
||||||
new_state = copy.deepcopy(self)
|
new_state = copy.deepcopy(self)
|
||||||
new_state.make_move(*move, 'black')
|
new_state.make_move(*move, current_color)
|
||||||
eval, _ = new_state.alpha_beta(depth-1, alpha, beta, 'white')
|
eval, _ = new_state.alpha_beta(
|
||||||
|
depth-1, alpha, beta, color, opposite_color)
|
||||||
|
|
||||||
if eval < min_eval:
|
if eval < min_eval:
|
||||||
min_eval = eval
|
min_eval = eval
|
||||||
@ -302,7 +306,7 @@ class Game:
|
|||||||
|
|
||||||
return min_eval, best_move
|
return min_eval, best_move
|
||||||
|
|
||||||
def evaluate(self):
|
def evaluate(self, color):
|
||||||
white_score = 0
|
white_score = 0
|
||||||
black_score = 0
|
black_score = 0
|
||||||
|
|
||||||
@ -318,7 +322,7 @@ class Game:
|
|||||||
else:
|
else:
|
||||||
black_score += 5
|
black_score += 5
|
||||||
|
|
||||||
return white_score - black_score
|
return white_score - black_score if color == 'white' else black_score - white_score
|
||||||
|
|
||||||
def input_to_coordinates(self, input):
|
def input_to_coordinates(self, input):
|
||||||
pos_x = ord(input[0])-ord('a')
|
pos_x = ord(input[0])-ord('a')
|
||||||
@ -327,6 +331,7 @@ class Game:
|
|||||||
|
|
||||||
def handle_player_move(self, color):
|
def handle_player_move(self, color):
|
||||||
has_moved = False
|
has_moved = False
|
||||||
|
possible_moves = self.get_possible_moves(color)
|
||||||
while not has_moved:
|
while not has_moved:
|
||||||
user_input = input('How do you want to move? (format: d6 e5)\n')
|
user_input = input('How do you want to move? (format: d6 e5)\n')
|
||||||
regex = r"^[a-z]\d+\s[a-z]\d+$"
|
regex = r"^[a-z]\d+\s[a-z]\d+$"
|
||||||
@ -337,22 +342,46 @@ class Game:
|
|||||||
[move_from, move_to] = user_input.split(' ')
|
[move_from, move_to] = user_input.split(' ')
|
||||||
from_coordinates = self.input_to_coordinates(move_from)
|
from_coordinates = self.input_to_coordinates(move_from)
|
||||||
to_coordinates = self.input_to_coordinates(move_to)
|
to_coordinates = self.input_to_coordinates(move_to)
|
||||||
has_moved = self.make_move(
|
|
||||||
from_coordinates, to_coordinates, color)
|
if not (from_coordinates, to_coordinates) in possible_moves:
|
||||||
|
legal_no_captures = self.check_move_legal(
|
||||||
|
from_coordinates, to_coordinates, color, True)
|
||||||
|
if legal_no_captures:
|
||||||
|
print("Invalid move! You can capture a piece")
|
||||||
|
continue
|
||||||
|
|
||||||
|
has_moved = self.make_move(from_coordinates, to_coordinates, color)
|
||||||
self.print_board()
|
self.print_board()
|
||||||
|
|
||||||
|
def start_game(self, player_color='black'):
|
||||||
|
if player_color != 'black' and player_color != 'white':
|
||||||
|
print('Invalid color! Color can be black or white')
|
||||||
|
return
|
||||||
|
ai_color = 'black' if player_color == 'white' else 'white'
|
||||||
|
|
||||||
|
game.print_board()
|
||||||
|
if player_color == 'white':
|
||||||
|
game.handle_player_move('white')
|
||||||
|
|
||||||
|
while True:
|
||||||
|
possible_moves_ai = game.get_possible_moves(ai_color)
|
||||||
|
if len(possible_moves_ai) == 0:
|
||||||
|
print(f'Game over, {player_color} wins')
|
||||||
|
return
|
||||||
|
_, ai_move = game.alpha_beta(1, 5, 10, ai_color)
|
||||||
|
game.make_move(*ai_move, ai_color)
|
||||||
|
print(
|
||||||
|
f"AI's move: {chr(ord('a')+ai_move[0][0])}{ai_move[0][1]} {chr(ord('a')+ai_move[1][0])}{ai_move[1][1]}")
|
||||||
|
game.print_board()
|
||||||
|
|
||||||
|
possible_moves_player = game.get_possible_moves(player_color)
|
||||||
|
if len(possible_moves_player) == 0:
|
||||||
|
print(f'Game over, {ai_color} wins')
|
||||||
|
return
|
||||||
|
game.handle_player_move(player_color)
|
||||||
|
|
||||||
|
|
||||||
# Ran first in the code
|
# Ran first in the code
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
game = Game(8)
|
game = Game(8)
|
||||||
while True:
|
game.start_game('white')
|
||||||
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