add some more logic
This commit is contained in:
parent
4335edea97
commit
d630115cce
|
@ -1,4 +1,6 @@
|
||||||
|
from typing import Callable, Iterable
|
||||||
from os import system
|
from os import system
|
||||||
|
from shutil import get_terminal_size as getTerminalSize
|
||||||
|
|
||||||
from piece import Piece
|
from piece import Piece
|
||||||
|
|
||||||
|
@ -7,13 +9,11 @@ class Board:
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.boardArray = [
|
self.boardArray = [
|
||||||
# [],
|
[Piece(type, 'black') for type in ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r']],
|
||||||
[Piece('p', 'black') for _ in range(8)],
|
|
||||||
[Piece('p', 'black') for _ in range(8)],
|
[Piece('p', 'black') for _ in range(8)],
|
||||||
*[[None for _ in range(8)] for _ in range(4)],
|
*[[None for _ in range(8)] for _ in range(4)],
|
||||||
[Piece('p', 'white') for _ in range(8)],
|
[Piece('p', 'white') for _ in range(8)],
|
||||||
[Piece('p', 'white') for _ in range(8)],
|
[Piece(type, 'white') for type in ['r', 'n', 'b', 'q', 'k', 'b', 'n', 'r']],
|
||||||
# [],
|
|
||||||
]
|
]
|
||||||
|
|
||||||
def draw(
|
def draw(
|
||||||
|
@ -22,7 +22,6 @@ class Board:
|
||||||
'highlightedContent': [],
|
'highlightedContent': [],
|
||||||
'highlightEscapeCodes': ('\033[32;5;7m', '\033[0m'),
|
'highlightEscapeCodes': ('\033[32;5;7m', '\033[0m'),
|
||||||
'highlightedBoxes': [],
|
'highlightedBoxes': [],
|
||||||
'center': False
|
|
||||||
}
|
}
|
||||||
) -> str:
|
) -> str:
|
||||||
"""Returns a string representing the board
|
"""Returns a string representing the board
|
||||||
|
@ -31,7 +30,6 @@ class Board:
|
||||||
highlightedContent: [(x,y)] - Pieces to color
|
highlightedContent: [(x,y)] - Pieces to color
|
||||||
highlightEscapeCodes: (str, str) - Terminal escape codes to color highlightedContent with
|
highlightEscapeCodes: (str, str) - Terminal escape codes to color highlightedContent with
|
||||||
highlightedBoxes: [(x,y)] - Boxes to make bold
|
highlightedBoxes: [(x,y)] - Boxes to make bold
|
||||||
center: Bool - Whether or not to indent the board into the center
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def fillConfigDefaultValue(key, defaultValue):
|
def fillConfigDefaultValue(key, defaultValue):
|
||||||
|
@ -41,7 +39,6 @@ class Board:
|
||||||
fillConfigDefaultValue('highlightedContent', [])
|
fillConfigDefaultValue('highlightedContent', [])
|
||||||
fillConfigDefaultValue('highlightedBoxes', [])
|
fillConfigDefaultValue('highlightedBoxes', [])
|
||||||
fillConfigDefaultValue('highlightEscapeCodes', ('\033[32;5;7m', '\033[0m'))
|
fillConfigDefaultValue('highlightEscapeCodes', ('\033[32;5;7m', '\033[0m'))
|
||||||
fillConfigDefaultValue('center', False)
|
|
||||||
|
|
||||||
# Draw general outline
|
# Draw general outline
|
||||||
stringArray = [list('┼' + '───┼' * 8)] + [[None] for _ in range(8 * 2)]
|
stringArray = [list('┼' + '───┼' * 8)] + [[None] for _ in range(8 * 2)]
|
||||||
|
@ -107,20 +104,34 @@ class Board:
|
||||||
for x, y in config['highlightedContent']:
|
for x, y in config['highlightedContent']:
|
||||||
highlightContent(x, y)
|
highlightContent(x, y)
|
||||||
|
|
||||||
# TODO: Center board
|
|
||||||
|
|
||||||
return '\n'.join([''.join(line) for line in stringArray])
|
return '\n'.join([''.join(line) for line in stringArray])
|
||||||
|
|
||||||
def selectPiece(self, player) -> tuple:
|
def selectPiece(self, player, x=0, y=0, centering=True) -> tuple:
|
||||||
"""Lets the user select a piece from a graphic board"""
|
"""Lets the user select a piece from a graphic board"""
|
||||||
|
|
||||||
x, y = 0, 0
|
|
||||||
while True:
|
while True:
|
||||||
system('clear')
|
system('clear')
|
||||||
print(player.name, '\n')
|
menuString = '\n' + player.name + '\n\n'
|
||||||
print(self.draw({'highlightedBoxes': [(x, y)]}), '\n')
|
menuString += self.draw({'highlightedBoxes': [(x, y)]}) + '\n'
|
||||||
|
inputString = f" W E\nA S D <- Enter : "
|
||||||
|
|
||||||
key = input(f" W E\n A S D <- Enter : ")[0]
|
def centerText(text):
|
||||||
|
terminalWidth = getTerminalSize((60, 0))[0] # Column size 60 as fallback
|
||||||
|
return "\n".join(line.center(terminalWidth) for line in text.split('\n'))
|
||||||
|
|
||||||
|
def centerBlockText(text):
|
||||||
|
terminalWidth = getTerminalSize((60, 0))[0] # Column size 60 as fallback
|
||||||
|
textArray = text.split('\n')
|
||||||
|
offset = int((terminalWidth - len(textArray[0])) / 2)
|
||||||
|
return "\n".join(offset * ' ' + line for line in textArray)
|
||||||
|
|
||||||
|
if centering:
|
||||||
|
menuString = centerText(menuString)
|
||||||
|
inputString = centerBlockText(inputString)
|
||||||
|
|
||||||
|
print(menuString)
|
||||||
|
|
||||||
|
key = input(inputString)[0]
|
||||||
if key in ['s', 'j'] and y != 7: y += 1
|
if key in ['s', 'j'] and y != 7: y += 1
|
||||||
elif key in ['w', 'k'] and y != 0: y -= 1
|
elif key in ['w', 'k'] and y != 0: y -= 1
|
||||||
elif key in ['d', 'l'] and x != 7: x += 1
|
elif key in ['d', 'l'] and x != 7: x += 1
|
||||||
|
@ -129,3 +140,77 @@ class Board:
|
||||||
|
|
||||||
def getPieceAt(self, x, y) -> Piece:
|
def getPieceAt(self, x, y) -> Piece:
|
||||||
return self.boardArray[y][x]
|
return self.boardArray[y][x]
|
||||||
|
|
||||||
|
def getPositionsWhere(self, condition: Callable[[Piece], bool]) -> Iterable[tuple]:
|
||||||
|
""" Returns a list of xy pairs of the pieces where a condition is met """
|
||||||
|
|
||||||
|
result = []
|
||||||
|
for y, row in enumerate(self.boardArray):
|
||||||
|
for x, piece in enumerate(row):
|
||||||
|
if condition(piece):
|
||||||
|
result.append((x, y))
|
||||||
|
return result
|
||||||
|
|
||||||
|
def checkCheck(self, color) -> bool:
|
||||||
|
"""Check whether a team is caught in check. The color is the color of the team to check"""
|
||||||
|
king = self.getPositionsWhere(lambda piece: piece.type == 'k' and piece.color == color)
|
||||||
|
piecesToCheck = self.getPositionsWhere(lambda piece: piece.color != color)
|
||||||
|
return any([king in Piece.possibleMoves(*piece, self) for piece in piecesToCheck])
|
||||||
|
|
||||||
|
def getPositionsToProtectKing(self, color) -> Iterable[tuple]:
|
||||||
|
"""Get a list of the positions to protect in order to protect the king when in check. The color is the color of the team who's in check"""
|
||||||
|
king = self.getPositionsWhere(lambda piece: piece.type == 'k' and piece.color == color)
|
||||||
|
piecesToCheck = self.getPositionsWhere(lambda piece: piece.color != color)
|
||||||
|
for piece in piecesToCheck:
|
||||||
|
if king not in Piece.possibleMoves(*piece, self):
|
||||||
|
piecesToCheck.remove(piece)
|
||||||
|
result = []
|
||||||
|
for piece in piecesToCheck:
|
||||||
|
result.append(piece)
|
||||||
|
if self.getPieceAt(*piece).type not in ['p', 'n', 'k']:
|
||||||
|
|
||||||
|
def getDirection(fromPosition, toPosition) -> tuple:
|
||||||
|
x = -1 if toPosition[0] > fromPosition[0] else \
|
||||||
|
0 if toPosition[0] == fromPosition[0] else 1
|
||||||
|
y = -1 if toPosition[1] > fromPosition[1] else \
|
||||||
|
0 if toPosition[1] == fromPosition[1] else 1
|
||||||
|
return (x, y)
|
||||||
|
|
||||||
|
direction = getDirection(piece, king)
|
||||||
|
|
||||||
|
def getPositionsUntilKing(x, y, direction):
|
||||||
|
result = []
|
||||||
|
while self.getPieceAt(x, y) == None:
|
||||||
|
result.append((x, y))
|
||||||
|
x += direction[0]
|
||||||
|
y += direction[1]
|
||||||
|
return result
|
||||||
|
|
||||||
|
result += getPositionsUntilKing(*piece, direction)
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
def checkStaleMate(self, color) -> bool:
|
||||||
|
"""Check whether a team is caught in stalemate. The color is the color of the team to check"""
|
||||||
|
enemyPieces = self.getPositionsWhere(lambda piece: piece.color == color)
|
||||||
|
getLegalMoves = lambda piece: Piece.possibleMoves(*piece, self, legalMoves = self.getPositionsToProtectKing(color))
|
||||||
|
piecesHasNoLegalMoves = any( getLegalMoves(piece) == None for piece in enemyPieces)
|
||||||
|
return (not self.checkCheck(color)) and piecesHasNoLegalMoves
|
||||||
|
|
||||||
|
def checkCheckMate(self, color) -> bool:
|
||||||
|
"""Check whether a team is caught in checkmate. The color is the color of the team to check"""
|
||||||
|
enemyPieces = self.getPositionsWhere(lambda piece: piece.color == color)
|
||||||
|
getLegalMoves = lambda piece: Piece.possibleMoves(*piece, self, legalMoves = self.getPositionsToProtectKing(color))
|
||||||
|
piecesHasNoLegalMoves = any( getLegalMoves(piece) == None for piece in enemyPieces)
|
||||||
|
return self.checkCheck(color) and piecesHasNoLegalMoves
|
||||||
|
|
||||||
|
def movePiece(self, position, toPosition, piecesToRemove=None):
|
||||||
|
x, y = position
|
||||||
|
toX, toY = toPosition
|
||||||
|
self.boardArray[toY][toX] = self.boardArray[y][x]
|
||||||
|
self.boardArray[y][x] = None
|
||||||
|
|
||||||
|
if piecesToRemove != None:
|
||||||
|
for x, y in piecesToRemove:
|
||||||
|
self.boardArray[y][x] = None
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
from board import Board
|
|
||||||
from os import system
|
from os import system
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
|
|
||||||
|
from board import Board
|
||||||
|
|
||||||
@dataclass
|
@dataclass
|
||||||
class Player:
|
class Player:
|
||||||
name: str
|
name: str
|
||||||
|
@ -12,8 +13,20 @@ class Chess:
|
||||||
def __init__(self, players):
|
def __init__(self, players):
|
||||||
self.players = players
|
self.players = players
|
||||||
|
|
||||||
|
def lose(self, player):
|
||||||
|
print(player.name, 'lost.')
|
||||||
|
exit(0)
|
||||||
|
|
||||||
def update(self):
|
def update(self):
|
||||||
system('clear')
|
system('clear')
|
||||||
|
board.selectPiece(players[0])
|
||||||
|
if board.checkCheckMate(players[1].color):
|
||||||
|
self.lose(players[1])
|
||||||
|
|
||||||
|
system('clear')
|
||||||
|
board.selectPiece(players[1])
|
||||||
|
if board.checkCheckMate(players[0].color):
|
||||||
|
self.lose(players[0])
|
||||||
|
|
||||||
def loop(self):
|
def loop(self):
|
||||||
while True:
|
while True:
|
||||||
|
@ -30,4 +43,4 @@ if __name__ == "__main__":
|
||||||
# game = Chess(('Spiller 1', 'Spiller 2'))
|
# game = Chess(('Spiller 1', 'Spiller 2'))
|
||||||
# game.loop()
|
# game.loop()
|
||||||
board = Board()
|
board = Board()
|
||||||
print(board.selectPiece(players[0]))
|
print(board.selectPiece(players[0], centering=True))
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from typing import Iterable, Callable
|
from typing import Iterable, Callable
|
||||||
from itertools import product
|
from itertools import product
|
||||||
|
from copy import deepcopy
|
||||||
|
|
||||||
|
|
||||||
class Piece:
|
class Piece:
|
||||||
|
@ -12,21 +13,29 @@ class Piece:
|
||||||
return self.type.upper() if self.color == 'white' else self.type
|
return self.type.upper() if self.color == 'white' else self.type
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def possibleMoves(x, y, board) -> Callable[[int, int], Iterable[tuple]]:
|
def possibleMoves(x, y, board, legalMoves=None) -> Callable[[int, int], Iterable[tuple]]:
|
||||||
piece = board.getPieceAt(x, y)
|
piece = board.getPieceAt(x, y)
|
||||||
moves = []
|
moves = []
|
||||||
|
|
||||||
|
# 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
|
||||||
|
|
||||||
pieceIsEnemyColor = lambda pieceToCheck: pieceToCheck != None and pieceToCheck.color != piece.color
|
pieceIsEnemyColor = lambda pieceToCheck: pieceToCheck != None and pieceToCheck.color != piece.color
|
||||||
pieceIsEmpty = lambda pieceToCheck: pieceToCheck == None
|
pieceIsEmpty = lambda pieceToCheck: pieceToCheck == None
|
||||||
pieceIsEmptyOrEnemyColor = lambda pieceToCheck: pieceToCheck == None or pieceToCheck.color != piece.color
|
pieceIsEmptyOrEnemyColor = lambda pieceToCheck: pieceToCheck == None or pieceToCheck.color != piece.color
|
||||||
|
|
||||||
def addMoveIfTrue(xOffset, yOffset, condition):
|
def addMoveIfTrue(xOffset, yOffset, condition: Callable[[Piece], bool]):
|
||||||
|
"""Tests a condition against a position away from self. Adds if condition returns true"""
|
||||||
if condition(board.getPieceAt(x + xOffset, y + yOffset)):
|
if condition(board.getPieceAt(x + xOffset, y + yOffset)):
|
||||||
moves.append((x + xOffset, y + yOffset))
|
moves.append((x + xOffset, y + yOffset))
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def addWhileInsideBoard(direction):
|
def assertNotCheck(newX, newY) -> bool:
|
||||||
|
testBoard = deepcopy(board)
|
||||||
|
testBoard.movePiece((x, y), (newX, newY)) # J
|
||||||
|
return not testBoard.checkCheck()
|
||||||
|
|
||||||
|
def addWhileInsideBoard(direction: tuple):
|
||||||
localX = x
|
localX = x
|
||||||
localY = y
|
localY = y
|
||||||
while localX not in [0, 7] and localY not in [0, 7]:
|
while localX not in [0, 7] and localY not in [0, 7]:
|
||||||
|
@ -55,7 +64,8 @@ class Piece:
|
||||||
positions.remove((0, 0))
|
positions.remove((0, 0))
|
||||||
for position in positions:
|
for position in positions:
|
||||||
addMoveIfTrue(*position, pieceIsEmptyOrEnemyColor)
|
addMoveIfTrue(*position, pieceIsEmptyOrEnemyColor)
|
||||||
# TODO: Check moves for check and remove the bad ones
|
|
||||||
|
moves = [position for position in moves if assertNotCheck(*position)]
|
||||||
|
|
||||||
elif piece.type == 'r':
|
elif piece.type == 'r':
|
||||||
for direction in product([0, 1], repeat=2):
|
for direction in product([0, 1], repeat=2):
|
||||||
|
@ -71,4 +81,9 @@ class Piece:
|
||||||
for direction in directions:
|
for direction in directions:
|
||||||
addWhileInsideBoard(direction)
|
addWhileInsideBoard(direction)
|
||||||
|
|
||||||
|
#TODO: remove moves that will put the king in check
|
||||||
|
|
||||||
|
if legalMoves != None and piece.type != 'k':
|
||||||
|
moves = [move for move in moves if move in legalMoves]
|
||||||
|
|
||||||
return moves
|
return moves
|
||||||
|
|
Loading…
Reference in New Issue