2023-03-31 12:41:46 +02:00
"""
2023-04-08 19:00:23 +02:00
Program that plays draughts ( checkers ) with user on 8 x8 board using min - max with alpha - beta pruning
2023-03-31 12:41:46 +02:00
"""
2023-04-08 19:54:56 +02:00
import re
import copy
2023-04-10 05:15:20 +02:00
import math
2023-04-10 06:19:56 +02:00
import sys
2023-03-31 12:41:46 +02:00
2023-04-08 15:57:10 +02:00
2023-03-31 12:41:46 +02:00
class Game :
2023-04-08 15:57:10 +02:00
""" Game """
2023-03-31 12:41:46 +02:00
2023-04-08 15:57:10 +02:00
def __init__ ( self , size ) :
self . board_size = size
self . white_positions = self . initialize_white ( )
self . black_positions = self . initialize_black ( )
2023-03-31 12:41:46 +02:00
2023-04-08 15:57:10 +02:00
def initialize_white ( self ) :
""" Initialize white pieces """
white_positions = [ ]
2023-04-10 05:15:20 +02:00
for y_coordinate in range ( math . floor ( ( self . board_size - 2 ) / 2 ) ) :
2023-04-08 15:57:10 +02:00
for x_coordinate in range ( self . board_size ) :
if y_coordinate % 2 == 0 :
if x_coordinate % 2 == 1 :
white_positions . append (
( x_coordinate , y_coordinate , False ) )
else :
if x_coordinate % 2 == 0 :
white_positions . append (
( x_coordinate , y_coordinate , False ) )
return white_positions
2023-03-31 12:41:46 +02:00
2023-04-08 15:57:10 +02:00
def initialize_black ( self ) :
""" Initialize black pieces """
black_positions = [ ]
2023-04-10 05:15:20 +02:00
for y_coordinate in range ( self . board_size - math . floor ( ( self . board_size - 2 ) / 2 ) , self . board_size ) :
2023-04-08 15:57:10 +02:00
for x_coordinate in range ( self . board_size ) :
if y_coordinate % 2 == 0 :
if x_coordinate % 2 == 1 :
black_positions . append (
( x_coordinate , y_coordinate , False ) )
else :
if x_coordinate % 2 == 0 :
black_positions . append (
( x_coordinate , y_coordinate , False ) )
return black_positions
2023-03-31 12:41:46 +02:00
2023-04-08 19:54:56 +02:00
def check_move_out_of_bounds ( self , to_ ) :
2023-04-08 17:50:31 +02:00
""" Check if the move destination is out of the bounds of the board """
2023-04-08 19:54:56 +02:00
if to_ [ 0 ] < 0 or to_ [ 0 ] > self . board_size - 1 :
2023-04-08 17:50:31 +02:00
# print(f"Illegal move! Final x coordinate must be between 0 and {self.board_size-1}!")
return True
2023-04-08 19:54:56 +02:00
if to_ [ 1 ] < 0 or to_ [ 1 ] > self . board_size - 1 :
2023-04-08 17:50:31 +02:00
# print(f"Illegal move! Final y coordinate must be between 0 and {self.board_size-1}!")
return True
return False
2023-03-31 12:41:46 +02:00
2023-04-08 15:57:10 +02:00
def check_piece_exists ( self , coords , color ) :
""" Check if a piece of given color exists at a given spot """
if color == " white " :
2023-04-08 19:00:23 +02:00
if any ( piece in self . white_positions for piece in ( ( * coords , False ) , ( * coords , True ) ) ) :
2023-04-08 15:57:10 +02:00
return True
2023-03-31 12:41:46 +02:00
else :
2023-04-08 19:00:23 +02:00
if any ( piece in self . black_positions for piece in ( ( * coords , False ) , ( * coords , True ) ) ) :
2023-04-08 15:57:10 +02:00
return True
return False
2023-03-31 12:41:46 +02:00
2023-04-08 15:57:10 +02:00
def check_piece_king ( self , coords , color ) :
2023-04-08 19:54:56 +02:00
""" Check if a piece of in a given spot and of a given color is a king.
Return false if no piece is found """
2023-03-31 14:29:09 +02:00
if color == " white " :
2023-04-08 15:57:10 +02:00
return ( * coords , True ) in self . white_positions
2023-04-08 19:54:56 +02:00
if color == " black " :
2023-04-08 15:57:10 +02:00
return ( * coords , True ) in self . black_positions
2023-04-08 19:54:56 +02:00
return False
2023-03-31 12:41:46 +02:00
2023-04-08 15:57:10 +02:00
# https://stackoverflow.com/a/2191707
2023-04-08 19:54:56 +02:00
def check_move_piece_capable ( self , from_ , to_ , color ) :
2023-04-08 15:57:10 +02:00
""" Check if the move is exactly one square diagonally """
2023-04-08 19:54:56 +02:00
if abs ( to_ [ 0 ] - from_ [ 0 ] ) == 1 :
2023-04-08 15:57:10 +02:00
if self . check_piece_king ( from_ , color ) :
return True
2023-04-08 19:54:56 +02:00
if color == " white " :
return to_ [ 1 ] == from_ [ 1 ] + 1
if color == " black " :
return to_ [ 1 ] == from_ [ 1 ] - 1
2023-04-08 15:57:10 +02:00
return False
2023-03-31 12:41:46 +02:00
2023-04-08 19:54:56 +02:00
def check_capture ( self , from_ , to_ , color ) :
""" Check if a piece was captured for a given move.
Return captured piece coordinates or None """
2023-04-08 15:57:10 +02:00
# captures can only happen if the player moved twice-diagonally
2023-04-08 19:00:23 +02:00
2023-04-08 19:54:56 +02:00
if ( abs ( to_ [ 0 ] - from_ [ 0 ] ) != 2 or abs ( to_ [ 1 ] - from_ [ 1 ] ) != 2 ) :
2023-04-08 15:57:10 +02:00
return None
2023-03-31 14:29:09 +02:00
2023-04-08 19:54:56 +02:00
middle = ( abs ( to_ [ 0 ] + from_ [ 0 ] ) / / 2 , abs ( to_ [ 1 ] + from_ [ 1 ] ) / / 2 )
2023-03-31 12:41:46 +02:00
2023-04-08 15:57:10 +02:00
if color == " white " and self . check_piece_exists ( middle , " black " ) :
return middle
2023-04-08 19:54:56 +02:00
if color == " black " and self . check_piece_exists ( middle , " white " ) :
2023-04-08 15:57:10 +02:00
return middle
return None
2023-04-08 19:54:56 +02:00
def check_move_legal ( self , from_ , to_ , color , give_feedback = False ) :
2023-04-08 15:57:10 +02:00
""" Check if a move is legal. Return a boolean or coordinates of captured piece """
2023-04-08 19:54:56 +02:00
if self . check_move_out_of_bounds ( to_ ) :
2023-04-08 19:00:23 +02:00
if give_feedback :
2023-04-08 17:50:31 +02:00
print ( " Illegal move! Final position is out of the bounds of the board " )
2023-04-08 15:57:10 +02:00
return False
if not self . check_piece_exists ( from_ , color ) :
2023-04-08 19:00:23 +02:00
if give_feedback :
2023-04-08 17:50:31 +02:00
print (
2023-04-08 19:54:56 +02:00
" Illegal move! There is no piece on the "
" starting position that belongs to the player " )
2023-04-08 15:57:10 +02:00
return False
2023-04-08 19:54:56 +02:00
if self . check_piece_exists ( to_ , " white " ) or self . check_piece_exists ( to_ , " black " ) :
2023-04-08 19:00:23 +02:00
if give_feedback :
2023-04-08 17:50:31 +02:00
print ( " Illegal move! Cannot move to position taken by another piece " )
2023-04-08 15:57:10 +02:00
return False
2023-04-08 19:54:56 +02:00
capture = self . check_capture ( from_ , to_ , color )
if capture is None :
if self . check_move_piece_capable ( from_ , to_ , color ) :
2023-04-08 15:57:10 +02:00
return True
2023-04-08 19:54:56 +02:00
if give_feedback :
print ( ' Illegal move! You can only move diagonally ' )
return False
2023-04-08 15:57:10 +02:00
return capture
2023-04-08 19:54:56 +02:00
def make_move ( self , from_ , to_ , color ) :
2023-04-08 15:57:10 +02:00
""" Move a piece on the board and remove any captured pieces """
2023-04-08 19:54:56 +02:00
move_legal = self . check_move_legal ( from_ , to_ , color )
if move_legal is False :
2023-04-08 15:57:10 +02:00
return False
2023-04-08 19:54:56 +02:00
capture = move_legal if isinstance ( move_legal , tuple ) else None
2023-04-08 15:57:10 +02:00
king = self . check_piece_king ( from_ , color )
2023-04-08 19:54:56 +02:00
if color == " white " :
2023-04-08 15:57:10 +02:00
self . white_positions . remove ( ( * from_ , king ) )
2023-04-08 19:54:56 +02:00
if capture :
2023-04-08 15:57:10 +02:00
captured_king = self . check_piece_king ( capture , " black " )
self . black_positions . remove ( ( * move_legal , captured_king ) )
2023-04-08 19:54:56 +02:00
if to_ [ 1 ] == self . board_size - 1 :
self . white_positions . append ( ( * to_ , True ) )
2023-04-08 19:00:23 +02:00
else :
2023-04-08 19:54:56 +02:00
self . white_positions . append ( ( * to_ , king ) )
2023-04-08 15:57:10 +02:00
else :
self . black_positions . remove ( ( * from_ , king ) )
2023-04-08 19:54:56 +02:00
if capture :
2023-04-08 15:57:10 +02:00
captured_king = self . check_piece_king ( capture , " white " )
self . white_positions . remove ( ( * move_legal , captured_king ) )
2023-04-08 19:54:56 +02:00
if to_ [ 1 ] == 0 :
self . black_positions . append ( ( * to_ , True ) )
2023-04-08 19:00:23 +02:00
else :
2023-04-08 19:54:56 +02:00
self . black_positions . append ( ( * to_ , king ) )
2023-04-08 19:00:23 +02:00
2023-03-31 12:41:46 +02:00
return True
2023-04-08 15:57:10 +02:00
2023-04-08 20:37:01 +02:00
def print_board ( self , rotate = False ) :
2023-04-08 17:50:31 +02:00
""" Print the board in the console """
2023-04-08 19:54:56 +02:00
2023-04-08 20:37:01 +02:00
def print_letters ( ) :
""" Print the letters above or under the board """
print ( " " , end = " " )
for col in range ( self . board_size ) :
if rotate :
print ( f " { chr ( ord ( ' a ' ) + self . board_size - 1 - col ) } " , end = " " )
else :
print ( f " { chr ( ord ( ' a ' ) + col ) } " , end = " " )
def get_square_code ( pos , background ) :
""" Return the code of a given square on the board """
2023-04-08 19:54:56 +02:00
if ( * pos , False ) in self . white_positions :
return " w "
if ( * pos , True ) in self . white_positions :
return " W "
if ( * pos , False ) in self . black_positions :
return " b "
if ( * pos , True ) in self . black_positions :
return " B "
return background
2023-04-08 20:37:01 +02:00
print_letters ( )
2023-04-08 17:50:31 +02:00
print ( " " )
2023-04-08 20:37:01 +02:00
row_range = range (
self . board_size * 4 ) if not rotate else reversed ( range ( self . board_size * 4 ) )
2023-04-08 17:50:31 +02:00
line = 0
2023-04-08 20:37:01 +02:00
for row in row_range :
2023-04-08 19:54:56 +02:00
for col in range ( self . board_size ) :
2023-04-08 20:37:01 +02:00
background = " # " if ( col % 2 == ( row / / 4 ) % 2 ) != rotate \
else " "
checker = get_square_code ( ( col , row / / 4 ) , background ) \
if not rotate else get_square_code ( ( self . board_size - 1 - col , row / / 4 ) , background )
2023-04-08 19:54:56 +02:00
if col == 0 :
if line % 4 == 2 :
print ( f " { row / / 4 : 3d } " , end = " " )
2023-04-08 17:50:31 +02:00
else :
print ( " " , end = " " )
match line % 4 :
case 0 :
print ( " +--- " , end = " " )
case 1 | 3 :
2023-04-08 19:54:56 +02:00
print ( f " | { 3 * background } " , end = " " )
2023-04-08 17:50:31 +02:00
case 2 :
2023-04-08 19:54:56 +02:00
print ( f " | { background } { checker } { background } " , end = " " )
if line % 4 == 0 :
2023-04-08 17:50:31 +02:00
print ( " + " )
2023-04-08 16:07:55 +02:00
else :
2023-04-08 19:54:56 +02:00
print ( f " | { row / / 4 } " if line % 4 == 2 else " | " )
2023-04-08 17:50:31 +02:00
line + = 1
print ( " " , end = " " )
2023-04-08 19:54:56 +02:00
for col in range ( self . board_size ) :
2023-04-08 17:50:31 +02:00
print ( " +--- " , end = " " )
2023-04-08 20:37:01 +02:00
print ( " + " )
print_letters ( )
2023-04-08 17:50:31 +02:00
print ( )
2023-03-31 12:41:46 +02:00
# Ran first in the code
2023-04-08 19:00:23 +02:00
def get_possible_moves_capture ( self , from_ , color ) :
2023-04-08 19:54:56 +02:00
""" Return all possible captures for a piece """
2023-04-08 17:50:31 +02:00
# 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 )
2023-04-08 19:54:56 +02:00
if self . check_move_legal ( from_ , move_down_left_two , color ) is not False :
2023-04-08 17:50:31 +02:00
legal_moves . append ( ( from_ , move_down_left_two ) )
2023-04-08 19:54:56 +02:00
if self . check_move_legal ( from_ , move_down_right_two , color ) is not False :
2023-04-08 17:50:31 +02:00
legal_moves . append ( ( from_ , move_down_right_two ) )
2023-04-08 19:54:56 +02:00
if self . check_move_legal ( from_ , move_up_left_two , color ) is not False :
2023-04-08 17:50:31 +02:00
legal_moves . append ( ( from_ , move_up_left_two ) )
2023-04-08 19:54:56 +02:00
if self . check_move_legal ( from_ , move_up_right_two , color ) is not False :
2023-04-08 17:50:31 +02:00
legal_moves . append ( ( from_ , move_up_right_two ) )
return legal_moves
2023-04-08 19:00:23 +02:00
def get_possible_moves_non_capture ( self , from_ , color ) :
2023-04-08 19:54:56 +02:00
""" Return all possible moves that are not captures for a piece """
2023-04-08 17:50:31 +02:00
# 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 )
2023-04-08 19:54:56 +02:00
if self . check_move_legal ( from_ , move_down_left_one , color ) is not False :
2023-04-08 17:50:31 +02:00
legal_moves . append ( ( from_ , move_down_left_one ) )
2023-04-08 19:54:56 +02:00
if self . check_move_legal ( from_ , move_down_right_one , color ) is not False :
2023-04-08 17:50:31 +02:00
legal_moves . append ( ( from_ , move_down_right_one ) )
2023-04-08 19:54:56 +02:00
if self . check_move_legal ( from_ , move_up_left_one , color ) is not False :
2023-04-08 17:50:31 +02:00
legal_moves . append ( ( from_ , move_up_left_one ) )
2023-04-08 19:54:56 +02:00
if self . check_move_legal ( from_ , move_up_right_one , color ) is not False :
2023-04-08 17:50:31 +02:00
legal_moves . append ( ( from_ , move_up_right_one ) )
return legal_moves
2023-03-31 14:29:09 +02:00
2023-04-08 16:07:55 +02:00
def get_possible_moves ( self , color ) :
2023-04-08 19:54:56 +02:00
""" Return all possible moves for a given piece color """
2023-04-08 17:50:31 +02:00
legal_moves = [ ]
captures = [ ]
if color == " white " :
for white_position in self . white_positions :
# print((white_position[0], white_position[1]))
2023-04-08 19:00:23 +02:00
legal_moves + = self . get_possible_moves_non_capture (
2023-04-08 17:50:31 +02:00
( white_position [ 0 ] , white_position [ 1 ] ) , color )
2023-04-08 19:00:23 +02:00
captures + = self . get_possible_moves_capture (
2023-04-08 17:50:31 +02:00
( white_position [ 0 ] , white_position [ 1 ] ) , color )
elif color == " black " :
for black_position in self . black_positions :
2023-04-08 19:00:23 +02:00
legal_moves + = self . get_possible_moves_non_capture (
2023-04-08 17:50:31 +02:00
( black_position [ 0 ] , black_position [ 1 ] ) , color )
2023-04-08 19:00:23 +02:00
captures + = self . get_possible_moves_capture (
2023-04-08 17:50:31 +02:00
( black_position [ 0 ] , black_position [ 1 ] ) , color )
2023-04-08 19:00:23 +02:00
if len ( captures ) > 0 :
2023-04-10 05:15:20 +02:00
return ( captures , True )
return ( legal_moves + captures , False )
2023-04-08 17:50:31 +02:00
2023-04-08 19:54:56 +02:00
def alpha_beta ( self , depth , alpha_beta , color , current_color = None ) :
""" Do alpha beta pruning for given parameters
and return the best move and its evaluated points """
if current_color is None :
2023-04-08 19:00:23 +02:00
current_color = color
2023-04-08 17:50:31 +02:00
if depth == 0 :
2023-04-08 19:00:23 +02:00
return self . evaluate ( color ) , None
2023-04-08 17:50:31 +02:00
2023-04-08 19:54:56 +02:00
alpha , beta = alpha_beta
2023-04-08 19:00:23 +02:00
opposite_color = ' white ' if current_color == ' black ' else ' black '
if current_color == color :
2023-04-08 17:50:31 +02:00
max_eval = float ( ' -inf ' )
best_move = None
2023-04-10 05:15:20 +02:00
for move in self . get_possible_moves ( current_color ) [ 0 ] :
2023-04-08 17:50:31 +02:00
new_state = copy . deepcopy ( self )
2023-04-08 19:00:23 +02:00
new_state . make_move ( * move , current_color )
2023-04-08 19:54:56 +02:00
eval_ , _ = new_state . alpha_beta (
depth - 1 , alpha_beta , color , opposite_color )
2023-04-08 17:50:31 +02:00
2023-04-08 19:54:56 +02:00
if eval_ > max_eval :
max_eval = eval_
2023-04-08 17:50:31 +02:00
best_move = move
2023-04-08 19:54:56 +02:00
alpha = max ( alpha , eval_ )
2023-04-08 17:50:31 +02:00
if alpha > = beta :
break
return max_eval , best_move
2023-04-08 19:54:56 +02:00
if opposite_color == color :
2023-04-08 17:50:31 +02:00
min_eval = float ( ' inf ' )
best_move = None
2023-04-10 05:15:20 +02:00
for move in self . get_possible_moves ( current_color ) [ 0 ] :
2023-04-08 17:50:31 +02:00
new_state = copy . deepcopy ( self )
2023-04-08 19:00:23 +02:00
new_state . make_move ( * move , current_color )
2023-04-08 19:54:56 +02:00
eval_ , _ = new_state . alpha_beta (
depth - 1 , alpha_beta , color , opposite_color )
2023-04-08 17:50:31 +02:00
2023-04-08 19:54:56 +02:00
if eval_ < min_eval :
min_eval = eval_
2023-04-08 17:50:31 +02:00
best_move = move
2023-04-08 19:54:56 +02:00
beta = min ( beta , eval_ )
2023-04-08 17:50:31 +02:00
if beta < = alpha :
break
return min_eval , best_move
2023-04-08 19:54:56 +02:00
return None
2023-04-08 17:50:31 +02:00
2023-04-08 19:00:23 +02:00
def evaluate ( self , color ) :
2023-04-08 19:54:56 +02:00
""" Evaluates the state of the board for a given color """
2023-04-08 17:50:31 +02:00
white_score = 0
black_score = 0
2023-04-08 16:07:55 +02:00
for white_position in self . white_positions :
2023-04-08 17:50:31 +02:00
if white_position [ 2 ] :
white_score + = 10
else :
white_score + = 5
2023-04-08 16:07:55 +02:00
for black_position in self . black_positions :
2023-04-08 17:50:31 +02:00
if black_position [ 2 ] :
black_score + = 10
else :
black_score + = 5
2023-04-08 19:00:23 +02:00
return white_score - black_score if color == ' white ' else black_score - white_score
2023-03-31 14:29:09 +02:00
2023-04-08 19:54:56 +02:00
def input_to_coordinates ( self , user_input ) :
""" Change input from a1 form to tuple form """
pos_x = ord ( user_input [ 0 ] ) - ord ( ' a ' )
pos_y = int ( user_input [ 1 : : ] )
2023-04-08 17:50:31 +02:00
return pos_x , pos_y
2023-04-08 16:03:47 +02:00
2023-04-08 16:07:55 +02:00
def handle_player_move ( self , color ) :
2023-04-08 19:54:56 +02:00
""" Prompt player to move, validate their input and make move """
2023-04-08 16:07:55 +02:00
has_moved = False
2023-04-10 05:15:20 +02:00
possible_moves = self . get_possible_moves ( color ) [ 0 ]
2023-04-08 16:07:55 +02:00
while not has_moved :
2023-04-08 20:00:00 +02:00
user_input = input (
f ' You are { color } . How do you want to move? (format: d6 e5) \n ' )
2023-04-08 17:50:31 +02:00
regex = r " ^[a-z] \ d+ \ s[a-z] \ d+$ "
2023-04-08 16:07:55 +02:00
match = re . search ( regex , user_input )
if not match :
print ( ' Invalid input, try again ' )
continue
[ move_from , move_to ] = user_input . split ( ' ' )
from_coordinates = self . input_to_coordinates ( move_from )
to_coordinates = self . input_to_coordinates ( move_to )
2023-04-08 19:00:23 +02:00
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 )
2023-04-08 20:37:01 +02:00
self . print_board ( color == ' white ' )
2023-04-08 15:57:10 +02:00
2023-04-10 06:19:56 +02:00
def start_game ( self , player_color = ' black ' , algorithm_depth = 5 ) :
2023-04-08 19:54:56 +02:00
""" Start the main loop of the game """
if player_color not in ( ' black ' , ' white ' ) :
2023-04-08 19:00:23 +02:00
print ( ' Invalid color! Color can be black or white ' )
return
ai_color = ' black ' if player_color == ' white ' else ' white '
2023-04-08 20:37:01 +02:00
game . print_board ( player_color == ' white ' )
2023-04-08 19:00:23 +02:00
if player_color == ' white ' :
game . handle_player_move ( ' white ' )
while True :
2023-04-10 05:15:20 +02:00
ai_turn = True
2023-04-10 06:19:56 +02:00
while ai_turn :
2023-04-10 05:15:20 +02:00
possible_moves_ai = game . get_possible_moves ( ai_color )
if len ( possible_moves_ai [ 0 ] ) == 0 :
print ( f ' Game over, { player_color } wins ' )
return
_ , ai_move = game . alpha_beta ( algorithm_depth , ( 5 , 10 ) , ai_color )
game . make_move ( * ai_move , ai_color )
print (
" AI ' s move: "
f " { chr ( ord ( ' a ' ) + ai_move [ 0 ] [ 0 ] ) } { ai_move [ 0 ] [ 1 ] } "
f " { chr ( ord ( ' a ' ) + ai_move [ 1 ] [ 0 ] ) } { ai_move [ 1 ] [ 1 ] } " )
game . print_board ( player_color == ' white ' )
ai_turn = game . get_possible_moves ( ai_color ) [ 1 ] and possible_moves_ai [ 1 ]
player_turn = True
while player_turn :
possible_moves_player = game . get_possible_moves ( player_color )
if len ( possible_moves_player [ 0 ] ) == 0 :
print ( f ' Game over, { ai_color } wins ' )
return
game . handle_player_move ( player_color )
player_turn = game . get_possible_moves ( player_color ) [ 1 ] and possible_moves_player [ 1 ]
2023-04-08 19:00:23 +02:00
2023-04-10 06:19:56 +02:00
def ai_turn ( self , ai_color , algorithm_depth , possible_moves_ai , print_info ) :
if len ( possible_moves_ai ) == 0 :
if print_info :
print ( f ' Game over, { ai_color } loses ' )
return True
_ , ai_move = game . alpha_beta ( algorithm_depth , ( 5 , 10 ) , ai_color )
if ai_move == None :
ai_move = possible_moves_ai [ 0 ]
game . make_move ( * ai_move , ai_color )
if print_info :
print (
" AI ' s move: "
f " { chr ( ord ( ' a ' ) + ai_move [ 0 ] [ 0 ] ) } { ai_move [ 0 ] [ 1 ] } "
f " { chr ( ord ( ' a ' ) + ai_move [ 1 ] [ 0 ] ) } { ai_move [ 1 ] [ 1 ] } " )
game . print_board ( True )
return False
def auto_game ( self , white_depth , black_depth ) :
""" Auto game mode between two bots """
game_turn = 0
max_turns = 250
while True and game_turn < max_turns :
bot_white_turn = True
while bot_white_turn :
possible_moves_ai = game . get_possible_moves ( ' white ' )
if self . ai_turn ( ' white ' , white_depth , possible_moves_ai [ 0 ] , False ) :
return ' white '
bot_white_turn = game . get_possible_moves ( ' white ' ) [ 1 ] and possible_moves_ai [ 1 ]
bot_black_turn = True
while bot_black_turn :
possible_moves_ai = game . get_possible_moves ( ' black ' )
if self . ai_turn ( ' black ' , black_depth , possible_moves_ai [ 0 ] , False ) :
return ' black '
bot_black_turn = game . get_possible_moves ( ' black ' ) [ 1 ] and possible_moves_ai [ 1 ]
game_turn + = 1
if game_turn > = max_turns :
print ( f " Game ended after { max_turns } turns! " )
return ' '
def auto_simulation ( self , white_depth , black_depth , iterations ) :
print ( f " Running { iterations } simulations with white depth = { white_depth } , black depth = { black_depth } " )
white_wins = 0
black_wins = 0
white_pieces_captured = 0
black_pieces_captured = 0
i = 0
while i < iterations :
result = game . auto_game ( white_depth , black_depth )
if result == ' white ' :
black_wins + = 1
if result == ' black ' :
white_wins + = 1
if result == ' ' :
break
white_pieces_captured + = 16 - len ( game . white_positions )
black_pieces_captured + = 16 - len ( game . black_positions )
i + = 1
print ( f " White wins = { white_wins } , Black wins = { black_wins } , white pieces captured in total = { white_pieces_captured } , black pieces captured in total = { black_pieces_captured } " )
print ( )
def print_help ( ) :
""" prints help """
print (
""" python main.py [algorithm_depth] - play the game against the bot as black, if no algorithm depth is specified the default (5) will be set
python main . py - h - - help print this prompt
python main . py - t - - test [ max_white_depth ] [ max_black_depth ] non interactive ( does not print moves ) for testing how different bot depth play against eachother , if depths are not provided default value of 5 is set
compares heuristic speed and path length
python main . py - w - - white [ algorithm_depth ] play as white pieces , if no algorithm depth is specified the default ( 5 ) will be set
python main . py - b - - black [ algorithm_depth ] play as black pieces , if no algorithm depth is specified the default ( 5 ) will be set
"""
)
def default ( color = ' black ' , algorithm_depth = 5 ) :
""" default program function -> allows to play a game against bot (by default as black) """
game . start_game ( color , algorithm_depth )
2023-04-08 15:57:10 +02:00
2023-04-08 15:48:55 +02:00
# Ran first in the code
2023-03-31 12:41:46 +02:00
if __name__ == " __main__ " :
2023-04-08 15:57:10 +02:00
game = Game ( 8 )
2023-04-10 06:19:56 +02:00
if len ( sys . argv ) > 1 :
if sys . argv [ 1 ] == " -h " or sys . argv [ 1 ] == " --help " :
print_help ( )
sys . exit ( )
if sys . argv [ 1 ] == " -t " or sys . argv [ 1 ] == " --test " :
max_white_depth = 4
max_black_depth = 4
if len ( sys . argv ) > 2 :
max_white_depth = int ( sys . argv [ 2 ] )
if len ( sys . argv ) > 3 :
max_black_depth = int ( sys . argv [ 3 ] )
for i in range ( max_white_depth + 1 ) :
for j in range ( max_black_depth + 1 ) :
game = Game ( 8 )
game . auto_simulation ( i , j , 10 )
sys . exit ( )
if sys . argv [ 1 ] == " -w " or sys . argv [ 1 ] == " --white " :
algorithm_depth = 5
if len ( sys . argv ) > 2 :
algorithm_depth = int ( sys . argv [ 2 ] )
default ( ' white ' , algorithm_depth )
sys . exit ( )
if sys . argv [ 1 ] == " -b " or sys . argv [ 1 ] == " --black " :
algorithm_depth = 5
if len ( sys . argv ) > 2 :
algorithm_depth = int ( sys . argv [ 2 ] )
default ( ' black ' , algorithm_depth )
sys . exit ( )
if len ( sys . argv ) > 1 :
default ( ' black ' , int ( sys . argv [ 1 ] ) )
default ( )