TDT4109/Exercise 10/chess/piece.py

141 lines
4.6 KiB
Python
Raw Normal View History

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-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-13 18:48:48 +01:00
# Unused code. I'm missing the font for my terminal, but go ahead and use piece.symbol instead of str(symbol) in board.draw if you'd like
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 23:43:39 +01:00
def possibleMoves(
x,
y,
board,
legalMoves=None,
simulation=False,
) -> Callable[[int, int], Iterable[tuple]]:
"""
Calculate all possible moves for a piece at (x, y) given a board in a certain state.
If there is restrictions for where the piece can go, the legal moves can be set to these.
If the function is part of a simulation, simulation needs to be set to True so that it doesn't keep on recursing simulation indefinetely.
"""
2020-11-10 23:56:21 +01:00
piece = board.getPieceAt(x, y)
moves = []
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-13 18:48:48 +01:00
def addMoveIfTrue(xOffset, yOffset, condition: Callable[[Piece], bool]) -> bool:
"""Tests a condition against a position away from self. Adds move if condition returns true. Returns condition result"""
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 00:17:02 +01:00
def assertNotCheck(newX, newY) -> bool:
2020-11-12 23:43:39 +01:00
"""Simulate a move and return whether or not the move will result in check"""
2020-11-12 00:17:02 +01:00
testBoard = deepcopy(board)
2020-11-12 19:17:16 +01:00
testBoard.movePiece((x, y), (newX, newY))
2020-11-12 23:43:39 +01:00
return not testBoard.checkCheck(piece.color, simulation=True)
2020-11-12 00:17:02 +01:00
def addWhileInsideBoard(direction: tuple):
2020-11-12 23:43:39 +01:00
"""Adds moves in direction until it either hits a piece or the edge"""
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]
2020-11-13 18:48:48 +01:00
currentPiece = board.getPieceAt(localX, localY)
if pieceIsEmpty(currentPiece):
2020-11-10 23:56:21 +01:00
moves.append((localX, localY))
else:
2020-11-13 18:48:48 +01:00
if pieceIsEnemyColor(currentPiece):
2020-11-10 23:56:21 +01:00
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
2020-11-13 18:48:48 +01:00
pieceAtStartPosition = lambda pieceToCheck: pieceToCheck == None and y == startPosition
2020-11-12 19:17:16 +01:00
addMoveIfTrue(1, localY, pieceIsEnemyColor)
addMoveIfTrue(-1, localY, pieceIsEnemyColor)
if addMoveIfTrue(0, localY, pieceIsEmpty):
2020-11-13 18:48:48 +01:00
addMoveIfTrue(0, localY * 2, pieceAtStartPosition)
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-13 18:48:48 +01:00
# Remove moves that is not included in the legal moves (moves to block 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
2020-11-12 23:43:39 +01:00
if not simulation:
moves = [position for position in moves if assertNotCheck(*position)]
2020-11-12 19:17:16 +01:00
2020-11-10 23:56:21 +01:00
return moves