Add files via upload
This commit is contained in:
commit
3eb99eb809
3 changed files with 1264 additions and 0 deletions
414
chess_ai.py
Normal file
414
chess_ai.py
Normal file
|
@ -0,0 +1,414 @@
|
|||
from chess_tools import get_possible_moves, move_piece, init_board, get_time
|
||||
import numpy as np
|
||||
import time
|
||||
import random
|
||||
|
||||
|
||||
|
||||
|
||||
inf = float("inf")
|
||||
|
||||
score_table = {"Q" : 9, "R" : 5, "B" : 3.3, "N" : 3.2, "P" : 1, "K" : 10**5 ,"q" : -9, "r" : -5, "b" : -3, "n" : -3, "p" : -1, "k" : -10**5, '':0, '.':0}
|
||||
|
||||
count = 0
|
||||
|
||||
|
||||
|
||||
class Heuristics:
|
||||
|
||||
# The tables denote the points scored for the position of the chess pieces on the board.
|
||||
# Source : https://www.chessprogramming.org/Simplified_Evaluation_Function
|
||||
|
||||
PAWN_TABLE = [
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0],
|
||||
[ 5, 10, 10,-20,-20, 10, 10, 5],
|
||||
[ 5, -5,-10, 0, 0,-10, -5, 5],
|
||||
[ 0, 0, 0, 20, 20, 0, 0, 0],
|
||||
[ 5, 5, 10, 25, 25, 10, 5, 5],
|
||||
[10, 10, 20, 30, 30, 20, 10, 10],
|
||||
[50, 50, 50, 50, 50, 50, 50, 50],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
]
|
||||
|
||||
KNIGHT_TABLE = [
|
||||
[-50, -40, -30, -30, -30, -30, -40, -50],
|
||||
[-40, -20, 0, 5, 5, 0, -20, -40],
|
||||
[-30, 5, 10, 15, 15, 10, 5, -30],
|
||||
[-30, 0, 15, 20, 20, 15, 0, -30],
|
||||
[-30, 5, 15, 20, 20, 15, 0, -30],
|
||||
[-30, 0, 10, 15, 15, 10, 0, -30],
|
||||
[-40, -20, 0, 0, 0, 0, -20, -40],
|
||||
[-50, -40, -30, -30, -30, -30, -40, -50]
|
||||
]
|
||||
|
||||
BISHOP_TABLE = [
|
||||
[-20, -10, -10, -10, -10, -10, -10, -20],
|
||||
[-10, 5, 0, 0, 0, 0, 5, -10],
|
||||
[-10, 10, 10, 10, 10, 10, 10, -10],
|
||||
[-10, 0, 10, 10, 10, 10, 0, -10],
|
||||
[-10, 5, 5, 10, 10, 5, 5, -10],
|
||||
[-10, 0, 5, 10, 10, 5, 0, -10],
|
||||
[-10, 0, 0, 0, 0, 0, 0, -10],
|
||||
[-20, -10, -10, -10, -10, -10, -10, -20]
|
||||
]
|
||||
|
||||
ROOK_TABLE = [
|
||||
[ 0, 0, 0, 5, 5, 0, 0, 0],
|
||||
[-5, 0, 0, 0, 0, 0, 0, -5],
|
||||
[-5, 0, 0, 0, 0, 0, 0, -5],
|
||||
[-5, 0, 0, 0, 0, 0, 0, -5],
|
||||
[-5, 0, 0, 0, 0, 0, 0, -5],
|
||||
[-5, 0, 0, 0, 0, 0, 0, -5],
|
||||
[ 5, 10, 10, 10, 10, 10, 10, 5],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, 0]
|
||||
]
|
||||
|
||||
QUEEN_TABLE = [
|
||||
[-20, -10, -10, -5, -5, -10, -10, -20],
|
||||
[-10, 0, 0, 0, 0, 0, 0, -10],
|
||||
[-10, 0, 0, 0, 0, 0, 0, -10],
|
||||
[ 0, 0, 0, 0, 0, 0, 0, -5],
|
||||
[ -5, 0, 0, 0, 0, 0, 0, -5],
|
||||
[-10, 0, 0, 0, 0, 0, 0, -10],
|
||||
[-10, 0, 0, 0, 0, 0, 0, -10],
|
||||
[-20, -10, -10, -5, -5, -10, -10, -20]
|
||||
]
|
||||
|
||||
KING_TABLE_MG = [
|
||||
[ 20, 50, 10, 0, 0, 10, 10, 20],
|
||||
[ 20, 20, 0, 0, 0, 0, 20, 20],
|
||||
[-10, -20, -20, -20, -20, -20, -20, -10],
|
||||
[-20, -30, -30, -40, -40, -30, -30, -20],
|
||||
[-30, -40, -40, -50, -50, -40, -40, -30],
|
||||
[-30, -40, -40, -50, -50, -40, -40, -30],
|
||||
[-30, -40, -40, -50, -50, -40, -40, -30],
|
||||
[-30, -40, -40, -50, -50, -40, -40, -30]
|
||||
]
|
||||
|
||||
KING_TABLE_EG = [
|
||||
[-50, -30, -30, -30, -30, -30, -30, -50],
|
||||
[-30, -30, 0, 0, 0, 0, -30, -30],
|
||||
[-30, -10, 20, 30, 30, 20, -10, -30],
|
||||
[-30, -10, 30, 40, 40, 30, -10, -30],
|
||||
[-30, -10, 30, 40, 40, 30, -10, -30],
|
||||
[-30, -10, 20, 30, 30, 20, -10, -30],
|
||||
[-30, -20, -10, 0, 0, -10, -20, -30],
|
||||
[-50, -40, -30, -20, -20, -30, -40, -50]
|
||||
]
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
def get_position_score(start, board):
|
||||
"""Evaluate the position of a piece on the board."""
|
||||
global game_phase
|
||||
piece = board[start]
|
||||
if piece == 'P':
|
||||
return Heuristics.PAWN_TABLE[start[0]][start[1]]
|
||||
if piece == 'p':
|
||||
return -Heuristics.PAWN_TABLE[7 - start[0]][start[1]]
|
||||
if piece == 'N':
|
||||
return Heuristics.KNIGHT_TABLE[start[0]][start[1]]
|
||||
if piece == 'n':
|
||||
return -Heuristics.KNIGHT_TABLE[7 - start[0]][start[1]]
|
||||
if piece == 'B':
|
||||
return Heuristics.BISHOP_TABLE[start[0]][start[1]]
|
||||
if piece == 'b':
|
||||
return -Heuristics.BISHOP_TABLE[7 - start[0]][start[1]]
|
||||
if piece == 'R':
|
||||
return Heuristics.ROOK_TABLE[start[0]][start[1]]
|
||||
if piece == 'r':
|
||||
return -Heuristics.ROOK_TABLE[7 - start[0]][start[1]]
|
||||
if piece == 'Q':
|
||||
return Heuristics.QUEEN_TABLE[start[0]][start[1]]
|
||||
if piece == 'q':
|
||||
return -Heuristics.QUEEN_TABLE[7 - start[0]][start[1]]
|
||||
if piece == 'K':
|
||||
if game_phase < 45:
|
||||
return Heuristics.KING_TABLE_EG[start[0]][start[1]]
|
||||
else:
|
||||
return Heuristics.KING_TABLE_MG[start[0]][start[1]]
|
||||
if piece == 'k':
|
||||
if game_phase < 45:
|
||||
return -Heuristics.KING_TABLE_EG[7 - start[0]][start[1]]
|
||||
else:
|
||||
return -Heuristics.KING_TABLE_MG[7 - start[0]][start[1]]
|
||||
|
||||
return 0
|
||||
|
||||
|
||||
def get_king_safety(start, board):
|
||||
"""King is safe if protected by a wall of pawns"""
|
||||
x, y = start
|
||||
piece = board[x, y]
|
||||
if piece == "K":
|
||||
king_safety = (board[max(x-1, 0), y+1] == "P") + (board[x, y+1] == "P") + (board[min(x+1, 7), y+1] == "P")
|
||||
elif piece == "k":
|
||||
king_safety = (board[max(x-1, 0), y-1] == "p") + (board[x, y-1] == "p") + (board[min(x+1, 7), y-1] == "p")
|
||||
king_safety = -king_safety
|
||||
else:
|
||||
king_safety = 0
|
||||
return king_safety
|
||||
|
||||
def get_pawn_defend(start, board):
|
||||
"""A good pawn is a pawn that defend another piece"""
|
||||
x, y = start
|
||||
piece = board[x, y]
|
||||
if piece == "P" and y+1<=7:
|
||||
if x-1 >= 0:
|
||||
if x+1<=7:
|
||||
pawn_defend = board[x-1, y+1].isupper() + board[x+1, y+1].isupper()
|
||||
else:
|
||||
pawn_defend = board[x-1, y+1].isupper()
|
||||
else:
|
||||
pawn_defend = board[x+1, y+1].isupper()
|
||||
|
||||
elif piece == "p" and y-1>=0:
|
||||
if x-1 >= 0:
|
||||
if x+1<=7:
|
||||
pawn_defend = board[x-1, y-1].islower() + board[x+1, y-1].islower()
|
||||
else:
|
||||
pawn_defend = board[x-1, y-1].islower()
|
||||
else:
|
||||
pawn_defend = board[x+1, y-1].islower()
|
||||
|
||||
else:
|
||||
pawn_defend = 0
|
||||
|
||||
return pawn_defend
|
||||
|
||||
def get_rook_score(start, board):
|
||||
"""A good rook is a rook that can move in a straight line"""
|
||||
x, y = start
|
||||
piece = board[x, y]
|
||||
rook_score = 0
|
||||
if piece == "R" or piece == "r":
|
||||
for i in range(1, 8):
|
||||
if x-i >= 0:
|
||||
if board[x-i, y] == ".":
|
||||
rook_score += 1
|
||||
else:
|
||||
break
|
||||
else:
|
||||
break
|
||||
for i in range(1, 8):
|
||||
if x+i <= 7:
|
||||
if board[x+i, y] == ".":
|
||||
rook_score += 1
|
||||
else:
|
||||
break
|
||||
else:
|
||||
break
|
||||
for i in range(1, 8):
|
||||
if y-i >= 0:
|
||||
if board[x, y-i] == ".":
|
||||
rook_score += 2
|
||||
else:
|
||||
break
|
||||
else:
|
||||
break
|
||||
for i in range(1, 8):
|
||||
if y+i <= 7:
|
||||
if board[x, y+i] == ".":
|
||||
rook_score += 2
|
||||
else:
|
||||
break
|
||||
else:
|
||||
break
|
||||
return rook_score
|
||||
|
||||
|
||||
|
||||
def get_other_eval(start, board):
|
||||
"""Evaluation not based on the conventional chess pieces values and on the position"""
|
||||
king_safety = get_king_safety(start, board)
|
||||
pawn_defend = get_pawn_defend(start, board)
|
||||
rook_score = get_rook_score(start, board)
|
||||
|
||||
return king_safety*10 + pawn_defend*10 + rook_score*2
|
||||
|
||||
|
||||
def leaf_eval(board):
|
||||
"""Evaluate a certain board position"""
|
||||
global count
|
||||
count += 1
|
||||
#evalute using score_table, position score and other eval
|
||||
evaluate = lambda x,y: score_table[board[x, y]] * 100 + get_position_score((x, y), board) + get_other_eval((x, y), board)
|
||||
return sum(evaluate(x, y) for y in range(8) for x in range(8)) + random.random()*10
|
||||
|
||||
game_phase = 0
|
||||
def get_game_phase(board):
|
||||
global game_phase
|
||||
game_phase = sum(abs(score_table[board[x, y]]) for y in range(8) for x in range(8)) - 2 * score_table['K']
|
||||
|
||||
|
||||
def minmax(node, depth, alpha=-inf, beta=inf):
|
||||
is_maximizing = node.color == 'white'
|
||||
if depth == 0 or node.is_leaf:
|
||||
return node.score
|
||||
if is_maximizing:
|
||||
score = -inf
|
||||
for child in node.children:
|
||||
score = max(score, minmax(child, depth - 1, alpha, beta))
|
||||
alpha = max(alpha, score)
|
||||
if beta <= alpha:
|
||||
break
|
||||
return score
|
||||
else:
|
||||
score = inf
|
||||
for child in node.children:
|
||||
score = min(score, minmax(child, depth - 1, alpha, beta))
|
||||
beta = min(beta, score)
|
||||
if beta <= alpha:
|
||||
break
|
||||
return score
|
||||
|
||||
def evaluate_move(start, end, board):
|
||||
"""Evaluate a move. Just used in "create_and_evaluate_tree" to sort the moves to optimize alpha-beta pruning"""
|
||||
position_score = get_position_score(start, board)
|
||||
take_score = score_table[board[end]]*100
|
||||
return position_score + take_score
|
||||
|
||||
|
||||
class Node:
|
||||
__slots__ = 'board', 'move', 'parent', 'children', 'score', 'depth', 'color', 'board_score'
|
||||
def __init__(self, board, move, parent, children, score, depth, color):
|
||||
self.board = board
|
||||
self.move = move
|
||||
self.parent = parent
|
||||
self.children = children
|
||||
self.score = score
|
||||
self.depth = depth
|
||||
self.color = color
|
||||
|
||||
@property
|
||||
def is_leaf(self):
|
||||
return not self.children
|
||||
|
||||
@property
|
||||
def is_root(self):
|
||||
return self.parent is None
|
||||
|
||||
def evalute_tree(self):
|
||||
if self.is_leaf:
|
||||
self.score = leaf_eval(self.board)
|
||||
else:
|
||||
for child in self.children:
|
||||
child.evalute_tree()
|
||||
self.score = minmax(self, self.depth)
|
||||
|
||||
|
||||
def create_and_evaluate_tree(self, depth, alpha=-inf, beta=inf):
|
||||
if depth == 0:
|
||||
self.score = leaf_eval(self.board)
|
||||
else:
|
||||
movable_pieces = ((x, y) for x in range(8) for y in range(8) if
|
||||
self.board[x, y] != '.' and self.color == 'white' and self.board[x, y].isupper() or self.color == 'black' and self.board[x, y].islower())
|
||||
for piece in movable_pieces:
|
||||
possible_moves = get_possible_moves(piece, self.board)
|
||||
possible_moves.sort(key=lambda x: abs(evaluate_move(piece, x, self.board)), reverse=True)
|
||||
#possible_moves.sort(key=lambda x: evaluate_move(piece, x, self.board), reverse= self.color == "white")
|
||||
for move in possible_moves:
|
||||
new_board = move_piece(piece, move, self.board)
|
||||
new_move = (piece, move)
|
||||
new_node = Node(new_board, new_move, self, [], 0, self.depth - 1, 'white' if self.color == 'black' else 'black')
|
||||
self.children.append(new_node)
|
||||
if self.color == 'white':
|
||||
alpha = max(alpha, new_node.create_and_evaluate_tree(depth - 1, alpha, beta))
|
||||
if beta <= alpha:
|
||||
break
|
||||
#To stop exploring if one king is taken
|
||||
if abs(self.score) > 10**4:
|
||||
break
|
||||
else:
|
||||
beta = min(beta, new_node.create_and_evaluate_tree(depth - 1, alpha, beta))
|
||||
if beta <= alpha:
|
||||
break
|
||||
if abs(self.score) > 10**4:
|
||||
break
|
||||
if not self.children:
|
||||
self.score = leaf_eval(self.board)
|
||||
else:
|
||||
self.score = max(child.score for child in self.children if child.score is not None) if self.color == 'white' else min(child.score for child in self.children if child.score is not None)
|
||||
return self.score
|
||||
|
||||
|
||||
def get_best_move(self):
|
||||
self.evalute_tree()
|
||||
maximazing = self.color == 'white'
|
||||
if maximazing:
|
||||
maxi = max(self.children, key=lambda x: x.score)
|
||||
print(f"maxi: {maxi.score}")
|
||||
return maxi.move
|
||||
else:
|
||||
mini = min(self.children, key=lambda x: x.score)
|
||||
print(f"mini: {mini.score}")
|
||||
return mini.move
|
||||
|
||||
def pretty(self):
|
||||
string = f"Node: {self.move} {self.score} {self.depth} {self.color} \n"
|
||||
for child in self.children:
|
||||
string += "| " + child.pretty() + "\n"
|
||||
return string
|
||||
|
||||
def __repr__(self):
|
||||
return self.pretty()
|
||||
|
||||
|
||||
get_deeper = 0
|
||||
|
||||
def get_best_move(board, color, depth):
|
||||
global count, get_deeper
|
||||
get_game_phase(board)
|
||||
print(f"End game: {game_phase}")
|
||||
depth += get_deeper
|
||||
print(f"curent depth: {depth}")
|
||||
a = time.time()
|
||||
root = Node(board, None, None, [], 0, 0, color)
|
||||
root.create_and_evaluate_tree(depth)
|
||||
best_move = root.get_best_move()
|
||||
exec_time = time.time() - a
|
||||
print(f"Tree evaluated in {time.time() - a} seconds")
|
||||
print(f"Number of leafs: {count}")
|
||||
|
||||
if exec_time < 1.5:
|
||||
get_deeper += 1
|
||||
print("Getting deeper, current depth: ", depth)
|
||||
elif exec_time > 20:
|
||||
get_deeper += -1
|
||||
print("Getting shallower, current depth: ", depth)
|
||||
|
||||
print(f"Mean number of children : {count**(1/depth)}")
|
||||
count = 0
|
||||
|
||||
|
||||
return best_move
|
||||
|
||||
if __name__ == '__main__':
|
||||
board = init_board()
|
||||
b = get_best_move(board, 'white', 2)
|
||||
print(b)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
182
chess_interface.py
Normal file
182
chess_interface.py
Normal file
|
@ -0,0 +1,182 @@
|
|||
from tkinter import *
|
||||
from chess_tools import *
|
||||
from chess_ai import get_best_move
|
||||
from PIL import Image, ImageTk, ImageDraw
|
||||
import time
|
||||
|
||||
pieces = {"P": "♙", "R": "♖", "N": "♘", "B": "♗", "Q": "♕", "K": "♔", "p": "♟", "r": "♜", "n": "♞", "b": "♝", "q": "♛", "k": "♚", '':'', '.':'', '\x00': ' '}
|
||||
|
||||
|
||||
MODE = "HUMAN VS AI"
|
||||
COLOR = "BLACK"
|
||||
|
||||
|
||||
def create_circle(x, y, r, canvas, fill="yellow"): #center coordinates, radius
|
||||
x0 = x - r
|
||||
y0 = y - r
|
||||
x1 = x + r
|
||||
y1 = y + r
|
||||
return canvas.create_oval(x0, y0, x1, y1, fill=fill)
|
||||
|
||||
|
||||
class ChessInterface:
|
||||
|
||||
__slots__ = 'root', 'canvas', 'size', 'square_size', 'selected', 'chess_board', 'count'
|
||||
def __init__(self, chess_board):
|
||||
self.root = Tk()
|
||||
self.root.title("Chess")
|
||||
self.canvas = Canvas(self.root, width=600, height=600, bg="white")
|
||||
self.size = 600 // 8 * 8
|
||||
self.square_size = self.size // 8
|
||||
self.selected = None
|
||||
self.chess_board = chess_board
|
||||
self.count = 0
|
||||
|
||||
def ai_turn(color_ai):
|
||||
print(f"AI turn {color_ai}d")
|
||||
move = get_best_move(self.chess_board, color_ai, 3)
|
||||
print(f"AI move: {move}")
|
||||
self.chess_board = move_piece(move[0], move[1], self.chess_board)
|
||||
self.canvas.delete("all")
|
||||
self.draw_all(self.chess_board)
|
||||
self.count += 1
|
||||
self.selected = None
|
||||
|
||||
|
||||
def on_click_human_vs_ai_black(event):
|
||||
"""AI play black"""
|
||||
global color
|
||||
x = event.x // self.square_size
|
||||
y = event.y // self.square_size
|
||||
self.canvas.delete("all")
|
||||
|
||||
if self.selected is None:
|
||||
piece = self.chess_board[7-y, x]
|
||||
print(piece)
|
||||
color = "white" if piece.isupper() else "black"
|
||||
self.selected = (7-y, x)
|
||||
else:
|
||||
color_turn = "white" if self.count % 2 == 0 else "black"
|
||||
print(color, color_turn)
|
||||
if color == color_turn == "white":
|
||||
print("Player turn")
|
||||
if (7-y, x) in get_possible_moves(self.selected, self.chess_board):
|
||||
self.count += 1
|
||||
move = (self.selected, (7-y, x))
|
||||
print(f"Player move: {move}")
|
||||
self.chess_board = move_piece(move[0], move[1], self.chess_board)
|
||||
self.draw_all(self.chess_board)
|
||||
self.root.update()
|
||||
|
||||
# AI turn
|
||||
ai_turn("black")
|
||||
|
||||
self.selected = None
|
||||
self.draw_all(self.chess_board)
|
||||
|
||||
first_round = True
|
||||
def on_click_human_vs_ai_white(event):
|
||||
"""AI play white"""
|
||||
global color
|
||||
nonlocal first_round
|
||||
|
||||
if first_round:
|
||||
# AI turn
|
||||
print("AI turn")
|
||||
move = get_best_move(self.chess_board, "white", 3)
|
||||
self.canvas.delete("all")
|
||||
self.draw_all(self.chess_board)
|
||||
print(f"AI move: {move}")
|
||||
self.chess_board = move_piece(move[0], move[1], self.chess_board)
|
||||
self.count += 1
|
||||
self.selected = None
|
||||
first_round = False
|
||||
|
||||
x = event.x // self.square_size
|
||||
y = event.y // self.square_size
|
||||
self.canvas.delete("all")
|
||||
|
||||
if self.selected is None:
|
||||
piece = self.chess_board[7-y, x]
|
||||
print(piece)
|
||||
color = "white" if piece.isupper() else "black"
|
||||
self.selected = (7-y, x)
|
||||
else:
|
||||
color_turn = "white" if self.count % 2 == 0 else "black"
|
||||
print(color, color_turn)
|
||||
if color == color_turn == "black":
|
||||
print("Player turn")
|
||||
if (7-y, x) in get_possible_moves(self.selected, self.chess_board):
|
||||
self.count += 1
|
||||
move = (self.selected, (7-y, x))
|
||||
print(f"Player move: {move}")
|
||||
self.chess_board = move_piece(move[0], move[1], self.chess_board)
|
||||
self.draw_all(self.chess_board)
|
||||
self.root.update()
|
||||
|
||||
# AI turn
|
||||
ai_turn("white")
|
||||
|
||||
self.selected = None
|
||||
self.draw_all(self.chess_board)
|
||||
|
||||
def on_click_ai_vs_ai(event):
|
||||
"""start AI vs AI when one click on the board"""
|
||||
while True:
|
||||
ai_turn("white")
|
||||
self.root.update()
|
||||
time.sleep(0.5)
|
||||
ai_turn("black")
|
||||
self.root.update()
|
||||
time.sleep(0.5)
|
||||
self.canvas.bind("<Button-1>", on_click_ai_vs_ai)
|
||||
|
||||
game_modes = [on_click_human_vs_ai_black, on_click_human_vs_ai_white, on_click_ai_vs_ai]
|
||||
self.canvas.bind("<Button-1>", game_modes[1])
|
||||
self.canvas.pack()
|
||||
self.run()
|
||||
|
||||
|
||||
def draw_board(self):
|
||||
for i in range(8):
|
||||
for j in range(8):
|
||||
if (i + j) % 2 == 0:
|
||||
color = "white"
|
||||
else:
|
||||
color = "grey"
|
||||
self.canvas.create_rectangle(i * self.square_size, j * self.square_size, (i + 1) * self.square_size, (j + 1) * self.square_size, fill=color)
|
||||
|
||||
|
||||
def draw_pieces(self, chess_board):
|
||||
for i in range(8):
|
||||
for j in range(8):
|
||||
if chess_board[i, j] != " ":
|
||||
self.canvas.create_text((j + 0.5) * self.square_size, (7-i + 0.5) * self.square_size, text=pieces[chess_board[i, j]], font=("Arial", 60))
|
||||
|
||||
|
||||
def draw_possible_moves(self, selected, chess_board):
|
||||
moves = get_possible_moves(selected, chess_board)
|
||||
if moves:
|
||||
for move in moves:
|
||||
#circle
|
||||
create_circle(move[1] * self.square_size + self.square_size // 2, (7-move[0]) * self.square_size + self.square_size // 2, self.square_size // 8, self.canvas)
|
||||
|
||||
def draw_all(self, chess_board):
|
||||
self.draw_board()
|
||||
self.draw_pieces(chess_board)
|
||||
if self.selected is not None:
|
||||
self.draw_possible_moves(self.selected, chess_board)
|
||||
self.canvas.pack()
|
||||
|
||||
def run(self):
|
||||
self.draw_all(self.chess_board)
|
||||
self.root.mainloop()
|
||||
|
||||
if __name__ == "__main__":
|
||||
chess_board = init_board()
|
||||
print(chess_board)
|
||||
ChessInterface(chess_board)
|
||||
|
||||
|
||||
|
||||
|
668
chess_tools.py
Normal file
668
chess_tools.py
Normal file
|
@ -0,0 +1,668 @@
|
|||
import typing
|
||||
from typing import Tuple, List, Iterable, Union
|
||||
import numpy as np
|
||||
from numpy import ndarray
|
||||
import time
|
||||
from array import array
|
||||
|
||||
# The chess board is an 8x8 array of 1-character strings. uppercase = white, lowercase = black
|
||||
#chess_board = np.empty((8, 8), dtype='U1')
|
||||
|
||||
def is_in_board(pos: Tuple[int, int]) -> bool:
|
||||
# Check if a position is in the board.
|
||||
row, col = pos
|
||||
return 0 <= row < 8 and 0 <= col < 8
|
||||
|
||||
|
||||
class CharArray2D:
|
||||
"""Fast 2D array of characters. Works like a numpy array of chr, but is much faster and memory efficient with pypy"""
|
||||
__slots__ = ('width', 'height', 'array')
|
||||
|
||||
def __init__(self, width, height):
|
||||
self.width = width
|
||||
self.height = height
|
||||
self.array = bytearray(width * height)
|
||||
|
||||
def __getitem__(self, index):
|
||||
y, x = index
|
||||
return chr(self.array[y * self.width + x])
|
||||
|
||||
def __setitem__(self, index, value):
|
||||
y, x = index
|
||||
self.array[y * self.width + x] = ord(value)
|
||||
|
||||
def copy(self):
|
||||
new_array = CharArray2D(self.width, self.height)
|
||||
new_array.array = self.array.copy()
|
||||
return new_array
|
||||
|
||||
@classmethod
|
||||
def from_ndarray(cls, array):
|
||||
self = cls(array.shape[1], array.shape[0])
|
||||
for y in range(self.height):
|
||||
for x in range(self.width):
|
||||
self[y, x] = array[y, x]
|
||||
return self
|
||||
|
||||
def __str__(self):
|
||||
return str(self.array)
|
||||
|
||||
|
||||
|
||||
def init_board():
|
||||
# Initialize the board with the starting positions of the pieces.
|
||||
chess_board = np.array([['R', 'N', 'B', 'Q', 'K', 'B', 'N', 'R'],
|
||||
['P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'],
|
||||
['.', '.', '.', '.', '.', '.', '.', '.'],
|
||||
['.', '.', '.', '.', '.', '.', '.', '.'],
|
||||
['.', '.', '.', '.', '.', '.', '.', '.'],
|
||||
['.', '.', '.', '.', '.', '.', '.', '.'],
|
||||
['p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'],
|
||||
['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r']])
|
||||
chess_board = CharArray2D.from_ndarray(np.array(chess_board))
|
||||
return chess_board
|
||||
|
||||
def move_piece(start: Tuple[int, int], end: Tuple[int, int], chess_board: ndarray) -> ndarray:
|
||||
# Move a piece from start to end.
|
||||
chess_board = chess_board.copy()
|
||||
|
||||
|
||||
if chess_board[start] not in 'KkPp':
|
||||
chess_board[end] = chess_board[start]
|
||||
chess_board[start] = '.'
|
||||
return chess_board
|
||||
|
||||
# Check for pawn
|
||||
if chess_board[start] == "P":
|
||||
if end[0] == 7:
|
||||
chess_board[end] = "Q"
|
||||
chess_board[start] = '.'
|
||||
return chess_board
|
||||
#Check for en passant
|
||||
if start[1] != end[1] and chess_board[end] == '.':
|
||||
chess_board[end[0], end[1]] = 'P'
|
||||
chess_board[start[0], start[1]] = '.'
|
||||
chess_board[end[0] - 1, end[1]] = '.'
|
||||
return chess_board
|
||||
|
||||
chess_board[end] = chess_board[start]
|
||||
chess_board[start] = '.'
|
||||
return chess_board
|
||||
|
||||
elif chess_board[start] == "p":
|
||||
if end[0] == 0:
|
||||
chess_board[end] = "q"
|
||||
chess_board[start] = '.'
|
||||
return chess_board
|
||||
#Check for en passant
|
||||
if start[1] != end[1] and chess_board[end] == '.':
|
||||
chess_board[end[0], end[1]] = 'p'
|
||||
chess_board[start[0], start[1]] = '.'
|
||||
chess_board[end[0] + 1, end[1]] = '.'
|
||||
return chess_board
|
||||
chess_board[end] = chess_board[start]
|
||||
chess_board[start] = '.'
|
||||
return chess_board
|
||||
|
||||
|
||||
# Check for castling.
|
||||
if chess_board[start] == "K":
|
||||
if start == (0, 4) and end == (0, 0):
|
||||
chess_board[0, 2] = "K"
|
||||
chess_board[0, 3] = "R"
|
||||
chess_board[0, 0] = '.'
|
||||
chess_board[0, 1] = '.'
|
||||
chess_board[0, 4] = '.'
|
||||
|
||||
elif start == (0, 4) and end == (0, 7):
|
||||
chess_board[0, 5] = "R"
|
||||
chess_board[0, 6] = "K"
|
||||
chess_board[0, 7] = '.'
|
||||
chess_board[0, 4] = '.'
|
||||
|
||||
else:
|
||||
chess_board[end] = chess_board[start]
|
||||
chess_board[start] = '.'
|
||||
return chess_board
|
||||
|
||||
elif chess_board[start] == "k":
|
||||
if start == (7, 4) and end == (7, 0):
|
||||
chess_board[7, 2] = "k"
|
||||
chess_board[7, 3] = "r"
|
||||
chess_board[7, 0] = '.'
|
||||
chess_board[7, 1] = '.'
|
||||
chess_board[7, 4] = '.'
|
||||
|
||||
elif start == (7, 4) and end == (7, 7):
|
||||
chess_board[7, 4] = '.'
|
||||
chess_board[7, 5] = "r"
|
||||
chess_board[7, 6] = "k"
|
||||
chess_board[7, 7] = '.'
|
||||
|
||||
|
||||
else:
|
||||
chess_board[end] = chess_board[start]
|
||||
chess_board[start] = '.'
|
||||
return chess_board
|
||||
|
||||
|
||||
return chess_board
|
||||
|
||||
|
||||
|
||||
def get_moves_pawn_white(start: Tuple[int, int], chess_board: ndarray) -> List[Tuple[int, int]]:
|
||||
# Get all possible moves for a white pawn at start.
|
||||
first_move = (start[0] == 1)
|
||||
moves = []
|
||||
|
||||
# Move forward if not blocked
|
||||
if chess_board[start[0] + 1, start[1]] == '.':
|
||||
moves.append((start[0] + 1, start[1]))
|
||||
if first_move and chess_board[start[0] + 2, start[1]] == '.':
|
||||
moves.append((start[0] + 2, start[1]))
|
||||
|
||||
# Capture diagonally.
|
||||
if start[1] > 0 and chess_board[start[0] + 1, start[1] - 1].islower():
|
||||
moves.append((start[0] + 1, start[1] - 1))
|
||||
if start[1] < 7 and chess_board[start[0] + 1, start[1] + 1].islower():
|
||||
moves.append((start[0] + 1, start[1] + 1))
|
||||
|
||||
# Check for en passant
|
||||
if start[0] == 4:
|
||||
if start[1] > 0 and chess_board[start[0], start[1] - 1] == 'p':
|
||||
moves.append((start[0] + 1, start[1] - 1))
|
||||
if start[1] < 7 and chess_board[start[0], start[1] + 1] == 'p':
|
||||
moves.append((start[0] + 1, start[1] + 1))
|
||||
|
||||
return moves
|
||||
|
||||
def get_moves_pawn_black(start: Tuple[int, int], chess_board: ndarray) -> List[Tuple[int, int]]:
|
||||
# Get all possible moves for a black pawn at start.
|
||||
first_move = (start[0] == 6)
|
||||
moves = []
|
||||
|
||||
# Move forward if not blocked.
|
||||
if chess_board[start[0] - 1, start[1]] == '.':
|
||||
moves.append((start[0] - 1, start[1]))
|
||||
if first_move and chess_board[start[0] - 2, start[1]] == '.':
|
||||
moves.append((start[0] - 2, start[1]))
|
||||
|
||||
# Capture diagonally.
|
||||
if start[1] > 0 and chess_board[start[0] - 1, start[1] - 1].isupper():
|
||||
moves.append((start[0] - 1, start[1] - 1))
|
||||
if start[1] < 7 and chess_board[start[0] - 1, start[1] + 1].isupper():
|
||||
moves.append((start[0] - 1, start[1] + 1))
|
||||
|
||||
# Check for en passant
|
||||
if start[0] == 3:
|
||||
if start[1] > 0 and chess_board[start[0], start[1] - 1] == 'P':
|
||||
moves.append((start[0] - 1, start[1] - 1))
|
||||
if start[1] < 7 and chess_board[start[0], start[1] + 1] == 'P':
|
||||
moves.append((start[0] - 1, start[1] + 1))
|
||||
|
||||
return moves
|
||||
|
||||
def get_moves_rook_white(start: Tuple[int, int], chess_board: ndarray) -> List[Tuple[int, int]]:
|
||||
# Get all possible moves for a white rook at start.
|
||||
moves = []
|
||||
|
||||
# Move up.
|
||||
for i in range(start[0] + 1, 8):
|
||||
if chess_board[i, start[1]] == '.':
|
||||
moves.append((i, start[1]))
|
||||
elif chess_board[i, start[1]].islower():
|
||||
moves.append((i, start[1]))
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
# Move down.
|
||||
for i in range(start[0] - 1, -1, -1):
|
||||
if chess_board[i, start[1]] == '.':
|
||||
moves.append((i, start[1]))
|
||||
elif chess_board[i, start[1]].islower():
|
||||
moves.append((i, start[1]))
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
# Move right.
|
||||
for i in range(start[1] + 1, 8):
|
||||
if chess_board[start[0], i] == '.':
|
||||
moves.append((start[0], i))
|
||||
elif chess_board[start[0], i].islower():
|
||||
moves.append((start[0], i))
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
# Move left.
|
||||
for i in range(start[1] - 1, -1, -1):
|
||||
if chess_board[start[0], i] == '.':
|
||||
moves.append((start[0], i))
|
||||
elif chess_board[start[0], i].islower():
|
||||
moves.append((start[0], i))
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
return moves
|
||||
|
||||
def get_moves_rook_black(start: Tuple[int, int], chess_board: ndarray) -> List[Tuple[int, int]]:
|
||||
# Get all possible moves for a black rook at start.
|
||||
moves = []
|
||||
|
||||
# Move up.
|
||||
for i in range(start[0] + 1, 8):
|
||||
if chess_board[i, start[1]] == '.':
|
||||
moves.append((i, start[1]))
|
||||
elif chess_board[i, start[1]].isupper():
|
||||
moves.append((i, start[1]))
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
# Move down.
|
||||
for i in range(start[0] - 1, -1, -1):
|
||||
if chess_board[i, start[1]] == '.':
|
||||
moves.append((i, start[1]))
|
||||
elif chess_board[i, start[1]].isupper():
|
||||
moves.append((i, start[1]))
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
# Move right.
|
||||
for i in range(start[1] + 1, 8):
|
||||
if chess_board[start[0], i] == '.':
|
||||
moves.append((start[0], i))
|
||||
elif chess_board[start[0], i].isupper():
|
||||
moves.append((start[0], i))
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
# Move left.
|
||||
for i in range(start[1] - 1, -1, -1):
|
||||
if chess_board[start[0], i] == '.':
|
||||
moves.append((start[0], i))
|
||||
elif chess_board[start[0], i].isupper():
|
||||
moves.append((start[0], i))
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
return moves
|
||||
|
||||
def get_moves_knight_white(start: Tuple[int, int], chess_board: ndarray) -> List[Tuple[int, int]]:
|
||||
# Get all possible moves for a white knight at start.
|
||||
moves = []
|
||||
verify = lambda move: is_in_board(move) and (chess_board[move].islower() or chess_board[move] == '.')
|
||||
|
||||
# Move up right.
|
||||
move = (start[0] + 2, start[1] + 1)
|
||||
if verify(move):
|
||||
moves.append(move)
|
||||
|
||||
# Move up left.
|
||||
move = (start[0] + 2, start[1] - 1)
|
||||
if verify(move):
|
||||
moves.append(move)
|
||||
|
||||
# Move down right.
|
||||
move = (start[0] - 2, start[1] + 1)
|
||||
if verify(move):
|
||||
moves.append(move)
|
||||
|
||||
# Move down left.
|
||||
move = (start[0] - 2, start[1] - 1)
|
||||
if verify(move):
|
||||
moves.append(move)
|
||||
|
||||
# Move right up.
|
||||
move = (start[0] + 1, start[1] + 2)
|
||||
if verify(move):
|
||||
moves.append(move)
|
||||
|
||||
# Move right down.
|
||||
move = (start[0] - 1, start[1] + 2)
|
||||
if verify(move):
|
||||
moves.append(move)
|
||||
|
||||
# Move left up.
|
||||
move = (start[0] + 1, start[1] - 2)
|
||||
if verify(move):
|
||||
moves.append(move)
|
||||
|
||||
# Move left down.
|
||||
move = (start[0] - 1, start[1] - 2)
|
||||
if verify(move):
|
||||
moves.append(move)
|
||||
|
||||
return moves
|
||||
|
||||
def get_moves_knight_black(start: Tuple[int, int], chess_board: ndarray) -> List[Tuple[int, int]]:
|
||||
# Get all possible moves for a black knight at start.
|
||||
moves = []
|
||||
verify = lambda move: is_in_board(move) and (chess_board[move].isupper() or chess_board[move] == '.')
|
||||
|
||||
# Move up right.
|
||||
move = (start[0] + 2, start[1] + 1)
|
||||
if verify(move):
|
||||
moves.append(move)
|
||||
|
||||
# Move up left.
|
||||
move = (start[0] + 2, start[1] - 1)
|
||||
if verify(move):
|
||||
moves.append(move)
|
||||
|
||||
# Move down right.
|
||||
move = (start[0] - 2, start[1] + 1)
|
||||
if verify(move):
|
||||
moves.append(move)
|
||||
|
||||
# Move down left.
|
||||
move = (start[0] - 2, start[1] - 1)
|
||||
if verify(move):
|
||||
moves.append(move)
|
||||
|
||||
# Move right up.
|
||||
move = (start[0] + 1, start[1] + 2)
|
||||
if verify(move):
|
||||
moves.append(move)
|
||||
|
||||
# Move right down.
|
||||
move = (start[0] - 1, start[1] + 2)
|
||||
if verify(move):
|
||||
moves.append(move)
|
||||
|
||||
# Move left up.
|
||||
move = (start[0] + 1, start[1] - 2)
|
||||
if verify(move):
|
||||
moves.append(move)
|
||||
|
||||
# Move left down.
|
||||
move = (start[0] - 1, start[1] - 2)
|
||||
if verify(move):
|
||||
moves.append(move)
|
||||
|
||||
return moves
|
||||
|
||||
|
||||
def get_moves_bishop_white(start: Tuple[int, int], chess_board: ndarray) -> List[Tuple[int, int]]:
|
||||
# Get all possible moves for a white bishop at start.
|
||||
moves = []
|
||||
|
||||
# Move up and right.
|
||||
for i in range(1, 8):
|
||||
if start[0] + i > 7 or start[1] + i > 7:
|
||||
break
|
||||
if chess_board[start[0] + i, start[1] + i] == '.':
|
||||
moves.append((start[0] + i, start[1] + i))
|
||||
elif chess_board[start[0] + i, start[1] + i].islower():
|
||||
moves.append((start[0] + i, start[1] + i))
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
# Move up and left.
|
||||
for i in range(1, 8):
|
||||
if start[0] + i > 7 or start[1] - i < 0:
|
||||
break
|
||||
if chess_board[start[0] + i, start[1] - i] == '.':
|
||||
moves.append((start[0] + i, start[1] - i))
|
||||
elif chess_board[start[0] + i, start[1] - i].islower():
|
||||
moves.append((start[0] + i, start[1] - i))
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
# Move down and right.
|
||||
for i in range(1, 8):
|
||||
if start[0] - i < 0 or start[1] + i > 7:
|
||||
break
|
||||
if chess_board[start[0] - i, start[1] + i] == '.':
|
||||
moves.append((start[0] - i, start[1] + i))
|
||||
elif chess_board[start[0] - i, start[1] + i].islower():
|
||||
moves.append((start[0] - i, start[1] + i))
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
# Move down and left.
|
||||
for i in range(1, 8):
|
||||
if start[0] - i < 0 or start[1] - i < 0:
|
||||
break
|
||||
if chess_board[start[0] - i, start[1] - i] == '.':
|
||||
moves.append((start[0] - i, start[1] - i))
|
||||
elif chess_board[start[0] - i, start[1] - i].islower():
|
||||
moves.append((start[0] - i, start[1] - i))
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
return moves
|
||||
|
||||
def get_moves_bishop_black(start: Tuple[int, int], chess_board: ndarray) -> List[Tuple[int, int]]:
|
||||
# Get all possible moves for a black bishop at start.
|
||||
moves = []
|
||||
|
||||
# Move up and right.
|
||||
for i in range(1, 8):
|
||||
if start[0] + i > 7 or start[1] + i > 7:
|
||||
break
|
||||
if chess_board[start[0] + i, start[1] + i] == '.':
|
||||
moves.append((start[0] + i, start[1] + i))
|
||||
elif chess_board[start[0] + i, start[1] + i].isupper():
|
||||
moves.append((start[0] + i, start[1] + i))
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
# Move up and left.
|
||||
for i in range(1, 8):
|
||||
if start[0] + i > 7 or start[1] - i < 0:
|
||||
break
|
||||
if chess_board[start[0] + i, start[1] - i] == '.':
|
||||
moves.append((start[0] + i, start[1] - i))
|
||||
elif chess_board[start[0] + i, start[1] - i].isupper():
|
||||
moves.append((start[0] + i, start[1] - i))
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
# Move down and right.
|
||||
for i in range(1, 8):
|
||||
if start[0] - i < 0 or start[1] + i > 7:
|
||||
break
|
||||
if chess_board[start[0] - i, start[1] + i] == '.':
|
||||
moves.append((start[0] - i, start[1] + i))
|
||||
elif chess_board[start[0] - i, start[1] + i].isupper():
|
||||
moves.append((start[0] - i, start[1] + i))
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
# Move down and left.
|
||||
for i in range(1, 8):
|
||||
if start[0] - i < 0 or start[1] - i < 0:
|
||||
break
|
||||
if chess_board[start[0] - i, start[1] - i] == '.':
|
||||
moves.append((start[0] - i, start[1] - i))
|
||||
elif chess_board[start[0] - i, start[1] - i].isupper():
|
||||
moves.append((start[0] - i, start[1] - i))
|
||||
break
|
||||
else:
|
||||
break
|
||||
|
||||
return moves
|
||||
|
||||
def get_moves_queen_white(start: Tuple[int, int], chess_board: ndarray) -> List[Tuple[int, int]]:
|
||||
# Get all possible moves for a white queen at start.
|
||||
return get_moves_bishop_white(start, chess_board) + get_moves_rook_white(start, chess_board)
|
||||
|
||||
def get_moves_queen_black(start: Tuple[int, int], chess_board: ndarray) -> List[Tuple[int, int]]:
|
||||
# Get all possible moves for a black queen at start.
|
||||
return get_moves_bishop_black(start, chess_board) + get_moves_rook_black(start, chess_board)
|
||||
|
||||
def get_moves_king_white(start: Tuple[int, int], chess_board: ndarray) -> List[Tuple[int, int]]:
|
||||
# Get all possible moves for a black king at start.
|
||||
moves = []
|
||||
# Move up if possible.
|
||||
if start[0] < 7:
|
||||
if chess_board[start[0] + 1, start[1]] == '.' or chess_board[start[0] + 1, start[1]].islower():
|
||||
moves.append((start[0] + 1, start[1]))
|
||||
|
||||
# Move down.
|
||||
if start[0] > 0:
|
||||
if chess_board[start[0] - 1, start[1]] == '.' or chess_board[start[0] - 1, start[1]].islower():
|
||||
moves.append((start[0] - 1, start[1]))
|
||||
|
||||
# Move right.
|
||||
if start[1] < 7:
|
||||
if chess_board[start[0], start[1] + 1] == '.' or chess_board[start[0], start[1] + 1].islower():
|
||||
moves.append((start[0], start[1] + 1))
|
||||
|
||||
# Move left.
|
||||
if start[1] > 0:
|
||||
if chess_board[start[0], start[1] - 1] == '.' or chess_board[start[0], start[1] - 1].islower():
|
||||
moves.append((start[0], start[1] - 1))
|
||||
|
||||
# Move up and right.
|
||||
if start[0] < 7:
|
||||
if start[1] < 7:
|
||||
if chess_board[start[0] + 1, start[1] + 1] == '.' or chess_board[start[0] + 1, start[1] + 1].islower():
|
||||
moves.append((start[0] + 1, start[1] + 1))
|
||||
|
||||
# Move up and left.
|
||||
if start[0] < 7:
|
||||
if start[1] > 0:
|
||||
if chess_board[start[0] + 1, start[1] - 1] == '.' or chess_board[start[0] + 1, start[1] - 1].islower():
|
||||
moves.append((start[0] + 1, start[1] - 1))
|
||||
|
||||
# Move down and right.
|
||||
if start[0] > 0:
|
||||
if start[1] < 7:
|
||||
if chess_board[start[0] - 1, start[1] + 1] == '.' or chess_board[start[0] - 1, start[1] + 1].islower():
|
||||
moves.append((start[0] - 1, start[1] + 1))
|
||||
|
||||
# Move down and left.
|
||||
if start[0] > 0:
|
||||
if start[1] > 0:
|
||||
if chess_board[start[0] - 1, start[1] - 1] == '.' or chess_board[start[0] - 1, start[1] - 1].islower():
|
||||
moves.append((start[0] - 1, start[1] - 1))
|
||||
|
||||
# Castling.
|
||||
if start == (0, 4):
|
||||
# White king side.
|
||||
if chess_board[0, 5] == '.' and chess_board[0, 6] == '.' and chess_board[0, 7] == 'R':
|
||||
moves.append((0, 7))
|
||||
|
||||
# White queen side.
|
||||
if chess_board[0, 3] == '.' and chess_board[0, 2] == '.' and chess_board[0, 1] == '.' and chess_board[0, 0] == 'R':
|
||||
moves.append((0, 0))
|
||||
|
||||
|
||||
return moves
|
||||
|
||||
def get_moves_king_black(start: Tuple[int, int], chess_board: ndarray) -> List[Tuple[int, int]]:
|
||||
# Get all possible moves for a black king at start.
|
||||
moves = []
|
||||
# Move up if possible.
|
||||
if start[0] < 7:
|
||||
if chess_board[start[0] + 1, start[1]] == '.' or chess_board[start[0] + 1, start[1]].isupper():
|
||||
moves.append((start[0] + 1, start[1]))
|
||||
|
||||
# Move down.
|
||||
if start[0] > 0:
|
||||
if chess_board[start[0] - 1, start[1]] == '.' or chess_board[start[0] - 1, start[1]].isupper():
|
||||
moves.append((start[0] - 1, start[1]))
|
||||
|
||||
# Move right.
|
||||
if start[1] < 7:
|
||||
if chess_board[start[0], start[1] + 1] == '.' or chess_board[start[0], start[1] + 1].isupper():
|
||||
moves.append((start[0], start[1] + 1))
|
||||
|
||||
# Move left.
|
||||
if start[1] > 0:
|
||||
if chess_board[start[0], start[1] - 1] == '.' or chess_board[start[0], start[1] - 1].isupper():
|
||||
moves.append((start[0], start[1] - 1))
|
||||
|
||||
# Move up and right.
|
||||
if start[0] < 7:
|
||||
if start[1] < 7:
|
||||
if chess_board[start[0] + 1, start[1] + 1] == '.' or chess_board[start[0] + 1, start[1] + 1].isupper():
|
||||
moves.append((start[0] + 1, start[1] + 1))
|
||||
|
||||
# Move up and left.
|
||||
if start[0] < 7:
|
||||
if start[1] > 0:
|
||||
if chess_board[start[0] + 1, start[1] - 1] == '.' or chess_board[start[0] + 1, start[1] - 1].isupper():
|
||||
moves.append((start[0] + 1, start[1] - 1))
|
||||
|
||||
# Move down and right.
|
||||
if start[0] > 0:
|
||||
if start[1] < 7:
|
||||
if chess_board[start[0] - 1, start[1] + 1] == '.' or chess_board[start[0] - 1, start[1] + 1].isupper():
|
||||
moves.append((start[0] - 1, start[1] + 1))
|
||||
|
||||
# Move down and left.
|
||||
if start[0] > 0:
|
||||
if start[1] > 0:
|
||||
if chess_board[start[0] - 1, start[1] - 1] == '.' or chess_board[start[0] - 1, start[1] - 1].isupper():
|
||||
moves.append((start[0] - 1, start[1] - 1))
|
||||
|
||||
# Castling.
|
||||
if start == (7, 4):
|
||||
# Castling kingside.
|
||||
if chess_board[7, 5] == '.' and chess_board[7, 6] == '.' and chess_board[7, 7] == 'r':
|
||||
moves.append((7, 7))
|
||||
# Castling queenside.
|
||||
if chess_board[7, 3] == '.' and chess_board[7, 2] == '.' and chess_board[7, 1] == '.' and chess_board[7, 0] == 'r':
|
||||
moves.append((7, 0))
|
||||
|
||||
|
||||
return moves
|
||||
|
||||
def get_possible_moves(start: Tuple[int, int], chess_board: ndarray) -> List[Tuple[int, int]]:
|
||||
# Get all possible moves for a piece at start.
|
||||
piece = chess_board[start]
|
||||
|
||||
if piece == 'P':
|
||||
return get_moves_pawn_white(start, chess_board)
|
||||
elif piece == 'p':
|
||||
return get_moves_pawn_black(start, chess_board)
|
||||
elif piece == 'R':
|
||||
return get_moves_rook_white(start, chess_board)
|
||||
elif piece == 'r':
|
||||
return get_moves_rook_black(start, chess_board)
|
||||
elif piece == 'N':
|
||||
return get_moves_knight_white(start, chess_board)
|
||||
elif piece == 'n':
|
||||
return get_moves_knight_black(start, chess_board)
|
||||
elif piece == 'B':
|
||||
return get_moves_bishop_white(start, chess_board)
|
||||
elif piece == 'b':
|
||||
return get_moves_bishop_black(start, chess_board)
|
||||
elif piece == 'Q':
|
||||
return get_moves_queen_white(start, chess_board)
|
||||
elif piece == 'q':
|
||||
return get_moves_queen_black(start, chess_board)
|
||||
elif piece == 'K':
|
||||
return get_moves_king_white(start, chess_board)
|
||||
elif piece == 'k':
|
||||
return get_moves_king_black(start, chess_board)
|
||||
|
||||
return []
|
||||
|
||||
|
||||
|
||||
def get_time(func):
|
||||
def wrapper(*args, **kwargs):
|
||||
start = time.perf_counter()
|
||||
result = func(*args, **kwargs)
|
||||
print(f"{func.__name__} took {time.perf_counter() - start} seconds")
|
||||
return result
|
||||
|
||||
return wrapper
|
||||
|
Loading…
Add table
Add a link
Reference in a new issue