Skip to content

Commit

Permalink
updated the Game.takeback(), Game.updateOpening(), Game.loadFEN() Gam…
Browse files Browse the repository at this point in the history
…e.legal_moves() functions and created Game.minimax() function
  • Loading branch information
DanielMiao1 committed Jan 28, 2022
1 parent cc20dd3 commit b17f48c
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 35 deletions.
104 changes: 74 additions & 30 deletions chess/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -220,7 +220,7 @@ def __init__(self, name, old_position, new_position, piece, is_capture=False, ch
:type double_pawn_move: bool
:type en_passant: bool
:type en_passant_position: str | None
:type promotion: bool
:type promotion: bool | str
:return: None
"""
self.piece = piece
Expand Down Expand Up @@ -871,15 +871,11 @@ def __init__(self, fen="rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1
self.tags = {"Result": "*"}
self.properties = {
"promotions": {"N": PieceEnum.knight, "B": PieceEnum.bishop, "R": PieceEnum.rook, "Q": PieceEnum.queen}
} # type: dict
}
self.pieces_class = pieces
self.opening = "" # Opening
self.evaluate_openings = evaluate_openings
self.squares, self.pieces = [], [] # Pieces and squares
self.squares_hashtable = {} # Squares hashtable
for x in "abcdefgh":
for y in range(1, 9):
self.squares_hashtable[x + str(y)] = False
self.in_check = False # False if neither side is in check, Color.white if white is in check, otherwise Color.black if black is in check
self.game_over = False
self.is_checkmate = False
Expand Down Expand Up @@ -937,6 +933,11 @@ def loadFEN(self, fen, evaluate_opening=True, evaluate_checks=True):
self.en_passant_positions = fen.split()[3]
else:
self.en_passant_positions = None
# Clear squares_hashtable
self.squares_hashtable = {} # Squares hashtable
for x in "abcdefgh":
for y in range(1, 9):
self.squares_hashtable[x + str(y)] = False
# Add pieces
for i, j in enumerate(functions.splitNumbers(fen.split()[0]).split("/")):
for x, y in enumerate(j):
Expand Down Expand Up @@ -1074,7 +1075,7 @@ def placePiece(self, coordinate, color, piece_type):
def removePiece(self, coordinate):
"""
Removes the piece at the specified coordinate
:param coordinate: str
:type coordinate: str
:return: Piece | None
"""
piece = self.pieceAt(coordinate)
Expand Down Expand Up @@ -1271,19 +1272,13 @@ def legal_moves(self, show_data=False, color=Color.current, evaluate_checks=True
color = self.turn

if isinstance(piece_type, (list, set, tuple)): # If the piece_type parameter is an iterable
pieces = {"pawn": [], "knight": [], "bishop": [], "rook": [], "queen": [], "king": []} # Define hashtable of piece types

for i in self.pieces: # Iterate through pieces
if color == Color.any or i.color == color:
pieces[i.piece_type].append(i) # Append piece to respective list in pieces hashtable

for x in piece_type: # Iterate through the piece types
for y in pieces[x]: # Iterate through the pieces of this type
moves.extend(y.moves(show_data, evaluate_checks=evaluate_checks)) # Append the piece moves
if (color == Color.any or i.color == color) and i.piece_type in piece_type:
moves.extend(i.moves(show_data=show_data, evaluate_checks=evaluate_checks)) # Append the piece moves
elif piece_type in PieceEnum.all(): # If the piece type is a single type
for i in self.pieceType(piece_type): # Iterate through the pieces of the specified type
if color == Color.any or i.color == color: # If the specified color(s) includes the piece color
moves.extend(i.moves(show_data, evaluate_checks=evaluate_checks)) # Append the piece moves
moves.extend(i.moves(show_data=show_data, evaluate_checks=evaluate_checks)) # Append the piece moves

return moves # Return result

Expand Down Expand Up @@ -1370,18 +1365,19 @@ def pieceAt(self, coordinate):
return self.squares_hashtable[coordinate]
return None

def takeback(self):
def takeback(self, evaluate_openings=True, evaluate_checks=True):
"""
Take backs one move. To take back multiple moves, call the function multiple times.
:type evaluate_openings: bool
:type evaluate_checks: bool
:return: None
"""
# TODO: update fullmove and halfmove counters
# TODO: update in_check variable
if not self.raw_move_list: # If there has not been any moves, return
return

# Reset the moved piece's position
self.squares_hashtable[self.raw_move_list[-1].piece.position], self.squares_hashtable[self.raw_move_list[-1].old_position] = self.squares_hashtable[self.raw_move_list[-1].old_position], self.squares_hashtable[self.raw_move_list[-1].piece.position]
self.squares_hashtable[self.raw_move_list[-1].new_position], self.squares_hashtable[self.raw_move_list[-1].old_position] = self.squares_hashtable[self.raw_move_list[-1].old_position], self.squares_hashtable[self.raw_move_list[-1].new_position]
self.raw_move_list[-1].piece.position = self.raw_move_list[-1].old_position

if self.raw_move_list[-1].is_capture: # If the last move was a capture
Expand Down Expand Up @@ -1414,27 +1410,46 @@ def takeback(self):
# Set opening
if self.move_list == "":
self.opening = ""
else:
elif evaluate_openings:
self.updateOpening()

# Update self.positions list
self.positions.pop()
# Update in_check variable
if evaluate_checks:
in_check = False
for x in self.pieces:
if x.color == self.turn or x.piece_type == PieceEnum.king:
continue
if x.piece_type == PieceEnum.pawn:
generate_function = self.generatePawnCaptures
elif x.piece_type == PieceEnum.knight:
generate_function = self.generateKnightMoves
elif x.piece_type == PieceEnum.bishop:
generate_function = self.generateBishopMoves
elif x.piece_type == PieceEnum.rook:
generate_function = self.generateRookMoves
else:
generate_function = self.generateQueenMoves
for y in generate_function(x.position, x.color):
if y.new_position == self.getKing(self.turn).position:
self.in_check = self.turn
in_check = True
break
if not in_check:
self.in_check = False

def updateOpening(self):
"""
Updates the opening if allowed
:return: bool | str
:return: False | str
"""
if self.evaluate_openings:
opening = False
position = self.FEN().split()[0]
for i in openings.openings:
if i["position"] == self.FEN().split()[0]:
if i["position"] == position:
self.opening = i["eco"] + " " + i["name"]
opening = i["eco"] + " " + i["name"]
break
return opening
else:
return False
return self.opening
return False

def attackers(self, coordinate, color):
"""
Expand Down Expand Up @@ -2241,6 +2256,36 @@ def generateKingCastles(self, position, piece=None):

return moves

def minimax_evaluation(self, depth, alpha=float("-inf"), beta=float("inf"), maximizing=True, color=Color.current):
if depth == 0 or self.game_over:
return {"move": None, "evaluation": self.evaluate() if color == Color.white else -self.evaluate()}

best_move = None
if maximizing:
max_eval = float("-inf")
for i in self.legal_moves():
self.move(i)
current_eval = self.minimax_evaluation(depth - 1, alpha, beta, False, color)["evaluation"]
self.takeback()
if current_eval > max_eval:
max_eval = current_eval
best_move = i
if beta <= max(alpha, current_eval):
break
return {"move": best_move, "evaluation": max_eval}
else:
min_eval = float("inf")
for i in self.legal_moves():
self.move(i)
current_eval = self.minimax_evaluation(depth - 1, alpha, beta, True, color)["evaluation"]
self.takeback()
if current_eval < min_eval:
min_eval = current_eval
best_move = i
if min(beta, current_eval) <= alpha:
break
return {"move": best_move, "evaluation": min_eval}

def __str__(self):
"""
:return: str
Expand Down Expand Up @@ -2530,7 +2575,6 @@ def move(self, move, evaluate_checks=True, evaluate_opening=True, evaluate_move_
move = super(Atomic, self).move(move, evaluate_checks=evaluate_checks, evaluate_opening=evaluate_opening, evaluate_move_checks=evaluate_move_checks, evaluate_move_checkmate=evaluate_move_checkmate)
if move.is_capture:
for i in self.generateExplosionRadius(move.new_position):
print(i)
if self.pieceAt(i) is not None:
if self.pieceAt(i).piece_type != PieceEnum.pawn:
if self.removePiece(i).piece_type == PieceEnum.king:
Expand Down
7 changes: 2 additions & 5 deletions setup.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
"""
setup.py
Setup
"""
# -*- coding: utf-8 -*-

import setuptools
import sys
Expand All @@ -11,7 +8,7 @@

setuptools.setup(
name="chess",
version="v0.3.14-dev",
version="v0.3.15.dev0",
author="Daniel M",
author_email="danielmiao2019@icloud.com",
description="Chess Library for Python 3 and Python 2",
Expand Down

0 comments on commit b17f48c

Please sign in to comment.