2020-11-10 23:56:21 +01:00
from typing import Iterable , Callable
from itertools import product
2020-11-12 00:17:02 +01:00
from copy import deepcopy
2020-11-12 19:17:16 +01:00
from sys import setrecursionlimit as setRecursionLimit
setRecursionLimit ( 100000 )
2020-11-10 23:56:21 +01:00
class Piece :
def __init__ ( self , type , color ) :
self . type = type
self . color = color
def __str__ ( self ) :
return self . type . upper ( ) if self . color == ' white ' else self . type
2020-11-12 19:17:16 +01:00
@property
def symbol ( self ) :
symbols = [ {
' p ' : ' ♙ ' ,
' r ' : ' ♖ ' ,
' n ' : ' ♘ ' ,
' b ' : ' ♗ ' ,
' q ' : ' ♕ ' ,
' k ' : ' ♔ ' ,
} , {
' p ' : ' ♟︎ ' ,
' r ' : ' ♜ ' ,
' n ' : ' ♞ ' ,
' b ' : ' ♝ ' ,
' q ' : ' ♛ ' ,
' k ' : ' ♚ ' ,
} ]
return symbols [ 0 if self . color == ' white ' else 1 ] [ self . type ]
2020-11-10 23:56:21 +01:00
@staticmethod
2020-11-12 00:17:02 +01:00
def possibleMoves ( x , y , board , legalMoves = None ) - > Callable [ [ int , int ] , Iterable [ tuple ] ] :
2020-11-10 23:56:21 +01:00
piece = board . getPieceAt ( x , y )
moves = [ ]
2020-11-12 00:17:02 +01:00
# TODO: If player is in check, only validate the moves that are leading to places that can be blocked in order to save the king. Make a function that overwrites `moves`. Notice: The king is the only piece that can 'Escape' to a position that may or may not be in the list of positions to be blocked. Make an exception
2020-11-10 23:56:21 +01:00
pieceIsEnemyColor = lambda pieceToCheck : pieceToCheck != None and pieceToCheck . color != piece . color
pieceIsEmpty = lambda pieceToCheck : pieceToCheck == None
pieceIsEmptyOrEnemyColor = lambda pieceToCheck : pieceToCheck == None or pieceToCheck . color != piece . color
2020-11-12 19:17:16 +01:00
positionInsideBounds = lambda x , y : x in range ( 8 ) and y in range ( 8 )
2020-11-10 23:56:21 +01:00
2020-11-12 00:17:02 +01:00
def addMoveIfTrue ( xOffset , yOffset , condition : Callable [ [ Piece ] , bool ] ) :
""" Tests a condition against a position away from self. Adds if condition returns true """
2020-11-10 23:56:21 +01:00
if condition ( board . getPieceAt ( x + xOffset , y + yOffset ) ) :
moves . append ( ( x + xOffset , y + yOffset ) )
return True
return False
2020-11-12 19:17:16 +01:00
# TODO: fix deepcopy segfault and recursion error
2020-11-12 00:17:02 +01:00
def assertNotCheck ( newX , newY ) - > bool :
testBoard = deepcopy ( board )
2020-11-12 19:17:16 +01:00
testBoard . movePiece ( ( x , y ) , ( newX , newY ) )
return not testBoard . checkCheck ( piece . color )
2020-11-12 00:17:02 +01:00
def addWhileInsideBoard ( direction : tuple ) :
2020-11-12 19:17:16 +01:00
localX , localY = x , y
while positionInsideBounds ( localX , localY ) :
2020-11-10 23:56:21 +01:00
localX + = direction [ 0 ]
localY + = direction [ 1 ]
if board . getPieceAt ( localX , localY ) == None :
moves . append ( ( localX , localY ) )
else :
if board . getPieceAt ( localX , localY ) . color != piece . color :
moves . append ( ( localX , localY ) )
return
if piece . type == ' p ' :
2020-11-12 19:17:16 +01:00
localY = 1 if piece . color == ' black ' else - 1
startPosition = 1 if piece . color == ' black ' else 6
addMoveIfTrue ( 1 , localY , pieceIsEnemyColor )
addMoveIfTrue ( - 1 , localY , pieceIsEnemyColor )
if addMoveIfTrue ( 0 , localY , pieceIsEmpty ) :
addMoveIfTrue ( 0 , localY * 2 ,
lambda pieceToCheck : pieceToCheck == None and y == startPosition )
2020-11-10 23:56:21 +01:00
elif piece . type == ' n ' :
2020-11-12 19:17:16 +01:00
positions = [
( - 2 , - 1 ) ,
( - 2 , 1 ) ,
( - 1 , - 2 ) ,
( - 1 , 2 ) ,
( 1 , - 2 ) ,
( 1 , 2 ) ,
( 2 , - 1 ) ,
( 2 , 1 ) ,
]
2020-11-10 23:56:21 +01:00
for position in positions :
addMoveIfTrue ( * position , pieceIsEmptyOrEnemyColor )
elif piece . type == ' k ' :
positions = list ( product ( [ - 1 , 0 , 1 ] , repeat = 2 ) )
positions . remove ( ( 0 , 0 ) )
for position in positions :
addMoveIfTrue ( * position , pieceIsEmptyOrEnemyColor )
2020-11-12 00:17:02 +01:00
2020-11-10 23:56:21 +01:00
elif piece . type == ' r ' :
2020-11-12 19:17:16 +01:00
for direction in [ ( 1 , 0 ) , ( - 1 , 0 ) , ( 0 , 1 ) , ( 0 , - 1 ) ] :
2020-11-10 23:56:21 +01:00
addWhileInsideBoard ( direction )
elif piece . type == ' b ' :
for direction in product ( [ - 1 , 1 ] , repeat = 2 ) :
addWhileInsideBoard ( direction )
elif piece . type == ' q ' :
directions = list ( product ( [ - 1 , 0 , 1 ] , repeat = 2 ) )
directions . remove ( ( 0 , 0 ) )
for direction in directions :
addWhileInsideBoard ( direction )
2020-11-12 19:17:16 +01:00
# Remove moves that will lead the piece out of the board
moves = [ move for move in moves if positionInsideBounds ( * move ) ]
2020-11-12 00:17:02 +01:00
2020-11-12 19:17:16 +01:00
# Remove moves that won't block the path for whichever piece has caused the game to check
2020-11-12 00:17:02 +01:00
if legalMoves != None and piece . type != ' k ' :
moves = [ move for move in moves if move in legalMoves ]
2020-11-12 19:17:16 +01:00
# Remove moves that will put the king in check
# moves = [position for position in moves if assertNotCheck(*position)]
2020-11-10 23:56:21 +01:00
return moves