class ClassicGame(): meta = {'title': 'Classic Chess', 'desc': 'Classic Chess as defined by FIDE', 'link': 'http://en.wikipedia.org/wiki/Chess', 'details': 'The goal is to checkmate the king.', 'players': 2} def __init__(self): self.startInit() self.endInit() def startInit(self, fenPos='rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR'): # settings # players self.NUM_PLAYERS = 2 self.COLORS = ['white', 'black'] # general settings self.CHECK_FOR_CHECK = True self.PROMOTION = True self.EN_PASSANT = True self.NO_WATCHERS = False self.SHOW_LAST_MOVE = True self.CAN_PROMOTE_TO_KING = False # castling options self.CASTLING = True self.CASTLING_FROM_CHECK = False self.CASTLING_THROUGH_CHECK = False self.CASTLING_TO_CHECK = False self.CASTLING_MULTIPLE_TIMES = False self.CASTLING_IF_KING_HAS_MOVED = False self.CASTLING_IF_ROOK_HAS_MOVED = False # draw options self.DRAW_X_MOVES = True self.DRAW_X_MOVES_VALUE = 50 self.DRAW_REPETITION = True self.DRAW_REPETITION_VALUE = 3 # crazyhouse self.USE_POCKET = False self.USE_CRAZYHOUSE_POCKET = False self.DROP_NO_PAWN_TO_PROMOTION_FIELDS = True self.DROP_NO_CHECKMATE = True # TODO implement self.DROP_NO_CHECK = False # TODO implement # global board settings self.BLOCKED_FIELDS = [] # piece settings self.PAWN_PIECE_TYPES = ['p'] self.KING_PIECE_TYPES = ['k'] self.PIECE_MAP = {'k': King, 'q': Queen, 'r': Rook, 'b': Bishop, 'n': Knight, 'p': Pawn} self.USED_PIECES = self.PIECE_MAP.keys() # clock settings self.CLOCK_AUTO_CLAIM_EXPIRATION = True # end of settings # handle fen self.fenPos = fenPos self.inferBoardSize(); # setup code self.id = uuid.uuid4().hex self.players = [] self.watchers = [] self.possibleMoves = None self.xMoveDrawCounter = 0 # Debugging self.DEBUG_LEGAL_MOVES_ON_ILLEGAL_MOVE = True # internal stuff self.joinedPlayers = 0 self.finished = False def endInit(self): # create board if it has not yet been done (by overloading and because of special needs) if not hasattr(self, 'board') or self.board == None: self.board = Board(self, width=self.board_width, height=self.board_height) # load position self.board.loadFenPos(self.fenPos) # promotion settings self.promotionFields = {'white': self.board.getRankFields(0), 'black': self.board.getRankFields(self.board_height - 1)} self.allPromotionFields = self.promotionFields['white'] + self.promotionFields['black'] self.possiblePromotionPieces = self.USED_PIECES.difference(self.PAWN_PIECE_TYPES) if not self.CAN_PROMOTE_TO_KING: self.possiblePromotionPieces.difference_update(self.KING_PIECE_TYPES) self.possiblePromotionPieces = list(self.possiblePromotionPieces) # castling information self.castlingPositions = {} # dictionary of lists (keys = color strings) #self.castlingPieces = {} # dictionary of lists (keys = color strings) self.castlingTargetPositions = {} # dictionary of lists (keys = color strings) for color in self.COLORS: # king kingsPos = self.board.findPieces(['k'], [color]) if kingsPos is None or len(kingsPos) > 1: self.board.castlingsPossible[color] = [False, False] kingPos = None else: kingPos = kingsPos[0] # rook rooksPos = self.board.findPieces(['r'], [color]) kingRookPos = None queenRookPos = None for rookPos in rooksPos: if self.board.splitPos(rookPos)[0] > self.board.width / 2: kingRookPos = rookPos else: queenRookPos = rookPos if kingRookPos == None: self.board.castlingsPossible[color][0] = False if queenRookPos == None: self.board.castlingsPossible[color][1] = False self.castlingPositions[color] = [kingPos, kingRookPos, queenRookPos] # [0]: king, [1] king side rook, [2] queen side rook #self.castlingPieces[color] = [None if x is None else self.board.fields[x] for x in self.castlingPositions[color]] kingShort = self.board.addPos(self.board.splitPos(kingPos), [2, 0]) rookShort = self.board.addPos(kingShort, [-1, 0]) kingLong = self.board.addPos(self.board.splitPos(kingPos), [-2, 0]) rookLong = self.board.addPos(kingLong, [1, 0]) self.castlingTargetPositions[color] = [self.board.mergePos(kingShort[0], kingShort[1]), self.board.mergePos(rookShort[0], rookShort[1]), self.board.mergePos(kingLong[0], kingLong[1]), self.board.mergePos(rookLong[0], rookLong[1])] # [0]: king for short, [1] rook for short, [2]: king for long, [3] rook for long # pre-create players self.createPlayers() self.createClocks() # draw preparations if self.DRAW_REPETITION: self.board.drawCountRepetition() # metagame self.metagame = self self.board.metagame = self def broadcastSocket(self, msg_data, filterPlayers=[]): for player in self.getAllPlayers() + self.getAllWatchers(): if not (player in filterPlayers): player.mq.send(msg_data) def getAllPlayers(self): ''' also returns all players from sub-games ''' return self.players def getAllWatchers(self): ''' also returns all players from sub-games ''' return self.watchers def getAllBoards(self): return [self.board] def createPlayers(self): # all of them will be dummys at first and filled via the slot mechanism flipStatus = False # it's fine for "white" players while len(self.players) < self.NUM_PLAYERS: player = Player() player.flipBoard = flipStatus flipStatus = not flipStatus mq = player.mq self.addPlayer(player) # backlinks for the MQ mq.subject = player mq.game = self mq.metagame = self def createClocks(self): for player in self.players: clock = BlitzClock() player.mq.clock = clock def inferBoardSize(self): # get board's height self.board_height = self.fenPos.count('/') + 1 # get board's width chars = list(self.fenPos[:self.fenPos.find('/') + 1]) width = len(chars) addValue = 0 for char in chars: if re.match('\d', char): addValue = addValue * 10 + int(char) elif addValue != 0: width += addValue - 1 addValue = 0 self.board_width = width - 1 def getPieceByString(self, string, board): if not(string.lower() in self.PIECE_MAP): return None pieceClass = self.PIECE_MAP[string.lower()] if string == string.lower(): # meaning: if string is lowercase color = 'black' else: color = 'white' return pieceClass(color, board) def moveNeedsPromotion(self, move, board): # castling move or anything else really special? if move.fromField is None: return False # already defined promotion type? if not(move.toPiece is None): return False # not even a pawn? if not(move.fromPiece.shortName.lower() in self.PAWN_PIECE_TYPES): return False # isn't target a promotion field? if not(move.toField in self.promotionFields[move.fromPiece.color]): return False return True def move(self, move, board, preGeneratePossibleMoves=True, dontHandleCapture=False, realMove=False, input_time=''): ''' Legality checking has to be done before. Only good moves arrive here! ''' # clock handling (part 1) if realMove: for player in self.players: player.mq.clock.deactivate(input_time) # first move needs special love because the clock is not active before finishing it if len(self.board.moveHistory) == 0: self.getCurrentPlayer(board).mq.clock.addTimePerMove() moves = [move] # castling moves first cType = -1 if move.annotation == 'SHORTCASTLING': cType = 0 elif move.annotation == 'LONGCASTLING': cType = 1 if cType != -1: # TODO fix to work with Chess960 # (move might hide one of the pieces, use setPiece feature) # generate moves color = self.getCurrentPlayer(board).color kingMove = Move(self.castlingPositions[color][0], self.castlingTargetPositions[color][cType * 2]) rookMove = Move(self.castlingPositions[color][1 + cType], self.castlingTargetPositions[color][cType * 2 + 1]) moves = [kingMove, rookMove] # mark done if not self.CASTLING_MULTIPLE_TIMES: board.castlingsPossible[color] = [False, False] # delegate the actual moving to the board we are operating on board.moveHistory.append(move) for xMove in moves: # annotate with current player if not isinstance(xMove, NullMove): xMove.player = self.getCurrentPlayer(board) xMove.simpleParse(board) xMove.fullParse(board) board.move(xMove, dontHandleCapture) # TODO parse check here? # parse additional draw conditions # x moves rule if self.DRAW_X_MOVES: board.drawXMoveCounter += 1 if (move.annotation is None) and ((move.fromPiece.getShortName().lower() in self.PAWN_PIECE_TYPES) or not(move.takenPiece is None)): board.drawXMoveCounter = 0 # repetition if self.DRAW_REPETITION: board.drawCountRepetition() if isinstance(move, NullMove): return moves if board == self.board and preGeneratePossibleMoves: # generate possible moves for the next round self.possibleMoves = None self.parsePossibleMoves() # bind to board for move in moves: move.board = self.board # reject all non-used draw offers (auto-decline) for player in self.players: player.offeringDraw = False # clock handling (part 2) if realMove: self.getCurrentPlayer(self.board).mq.clock.activate() return moves def handleCaptureMove(self, move, board): # put the piece to the capturePocket board.capturePockets[self.getLastCurrentPlayer(board).color].add(board.fields[move.toField]) if self.USE_POCKET and self.USE_CRAZYHOUSE_POCKET: self._putPieceToPocket(board.fields[move.toField], board.pockets[self.getLastCurrentPlayer(board).color], flipColor=True) def _putPieceToPocket(self, originalPiece, targetPocket, flipColor=False): # and copy the piece with inverted color to the pocket freshPiece = copy.copy(originalPiece) if flipColor: freshPiece.color = 'white' if freshPiece.color == 'black' else 'black' freshPiece.board = None # make sure the pawn is slow no matter where it is put // TODO rule check! if isinstance(freshPiece, Pawn): freshPiece.endInit() # important to make him look the other side freshPiece.moveCount = 1 freshPiece.changedSpeed = False targetPocket.add(freshPiece) def addPlayer(self, player): player.game = self player.color = self.COLORS[self.joinedPlayers] print(self.joinedPlayers) self.players.append(player) self.joinedPlayers += 1 def addWatcher(self, watcher): self.watchers.append(watcher) def getPromotionOptions(self, color): if color == 'white': return [x.upper() for x in self.sortPieceList(self.possiblePromotionPieces)] else: return self.sortPieceList(self.possiblePromotionPieces) def getSlotsMessageData(self): slotList = [] for i in range(len(self.players)): player = self.players[i] playerDict = dict() playerDict['open'] = player.dummy playerDict['desc'] = self.COLORS[i] playerDict['pname'] = '' if player.dummy else player.name playerDict['joinId'] = player.mq.shortenedId slotList.append(playerDict) return slotList def getPocketMessage(self, onlyNew=True): if not self.USE_POCKET: return None if onlyNew and not self.board.pockets['white'].dirty and not self.board.pockets['black'].dirty: return None result = {} data = {'board_id': self.board.id} data['pockets'] = ''.join([piece.getShortName() for piece in self.board.pockets['white'].getPieces()]) + '/' + ''.join([piece.getShortName() for piece in self.board.pockets['black'].getPieces()]) result['0'] = data return Message('gamesit', result) def getSituationMessage(self, mq, force=False, player=None, init=False): if player is None: subject = mq.subject else: subject = player result = {} send = False counter = 0 # this code is prepared for multi-board games, but it is not used # check boards data = {'board_id': self.board.id} flipTotal = subject.flipBoard != subject.game.board.inherentlyFlipped # collect all players for bottom pocket ("white") bottomPlayers = filter(lambda player: not player.flipBoard, self.players) # collect all players for top pocket ("black") topPlayers = filter(lambda player: player.flipBoard, self.players) if force or self.board.resend: send = True data['flipped'] = flipTotal != self.board.inherentlyFlipped data['fen'] = self.getFenPos(self.board, mq.subject) data['board_size'] = str(self.board.width) + 'x' + str(self.board.height) # add players if init: playerData = '' for player in bottomPlayers: playerData += '%s:%s,' % (player.name, player.mq.shortenedId) playerData = playerData[:-1] + '/' for player in topPlayers: playerData += '%s:%s,' % (player.name, player.mq.shortenedId) playerData = playerData[:-1] # add players to the board data data['players'] = playerData # format: name:id,name:id/name:id,name:id # add current player if applicable if not(self.getCurrentPlayer(self.board) is None): data['currP'] = self.getCurrentPlayer(self.board).mq.shortenedId # add last move if applicable if len(self.board.moveHistory) > 0 and self.SHOW_LAST_MOVE: # TODO select last non-NullMove lastMove = self.board.moveHistory[-1]; if not isinstance(lastMove, NullMove): if lastMove.board is None: data['lmove_from'] = lastMove.fromField data['lmove_to'] = lastMove.toField else: data['lmove_from'] = str(lastMove.board.id) + '_' + str(lastMove.fromField) data['lmove_to'] = str(lastMove.board.id) + '_' + str(lastMove.toField) # pockets for key in self.board.pockets: pocket = self.board.pockets[key] if force or self.board.resend or pocket.dirty: send = True data['pockets'] = ''.join([piece.getShortName() for piece in self.board.pockets['white'].getPieces()]) + ',' + ''.join([piece.getShortName() for piece in self.board.pockets['black'].getPieces()]) for key in self.board.capturePockets: capturePocket = self.board.capturePockets[key] if force or self.board.resend or capturePocket.dirty: send = True data['capturePockets'] = ''.join([piece.getShortName() for piece in self.board.capturePockets['white'].getPieces()]) + ',' + ''.join([piece.getShortName() for piece in self.board.capturePockets['black'].getPieces()]) # clocks send = True data['clocks'] = self.getClockString(topPlayers, bottomPlayers) result[str(counter)] = data counter += 1 if init: result['gameId'] = self.id # add ownPlayers result['playerSelf'] = ','.join(subject.aliases + [subject.mq.shortenedId]) if send: return Message('gamesit', result) return None def getClockString(self, topPlayers, bottomPlayers): playerData = '' for player in bottomPlayers: playerData += '%s:%s' % (player.mq.clock, str(player.mq.clock.is_active)) playerData += '/' for player in topPlayers: playerData += '%s:%s' % (player.mq.clock, str(player.mq.clock.is_active)) return playerData def isRepetitionDraw(self): if not self.DRAW_REPETITION: return return self.getRepetitionCount() >= self.DRAW_REPETITION_VALUE def getRepetitionCount(self): currPos = self.getPositionHash() try: return self.board.positions[currPos] except KeyError: return 0 def getPositionHash(self): # TODO include player's turn, castling options, en passant options in the dict's key return self.getFenPos(self.board, self.players[0]) def isXMoveDraw(self): if not self.DRAW_REPETITION: return return self.board.drawXMoveCounter >= self.DRAW_X_MOVES_VALUE def getGameOverMessage(self): player = self.getNextCurrentPlayer() go = GameOver(self.board) # mate and stalemate if go.noLegalMove(): if go.inCheck(): return self._valueResult(player, 'Checkmate') else: return self._valueResult(player, 'Stalemate') # 50 moves, repetition via claim by user return None def _valueResult(self, player, msg): if msg == 'Checkmate': winner = player.mq.shortenedId result = '1-0' if player.color == self.COLORS[0] else '0-1' elif msg == 'Extincted': winner = player.mq.shortenedId result = '1-0' if player.color == self.COLORS[0] else '0-1' elif msg == 'Stalemate': winner = '' result = '0.5-0.5' elif msg == 'Both players\' time\'s up!': winner = '' result = '0.5-0.5' elif msg == 'Time\'s up!': winner = player.mq.shortenedId result = '0-1' if player.color == self.COLORS[0] else '1-0' return self._generateGameOverMessage(msg, result, winner) def _generateGameOverMessage(self, msg, result, winner): # build message if not(msg is None): return Message('gameover', {'winner': winner, 'msg': msg, 'result': result}) def sortPieceList(self, pieceList): return sorted(pieceList, key=lambda piece: self.PIECE_MAP[piece](None, self.board).value, reverse=True) def getCurrentPlayer(self, board=None): if board == None: board = self.board return self.players[len(board.moveHistory) % self.NUM_PLAYERS] def getNextCurrentPlayer(self, board=None): if board == None: board = self.board return self.players[(len(board.moveHistory) + 1) % self.NUM_PLAYERS] def getLastCurrentPlayer(self, board): return self.players[((len(board.moveHistory) - 1) + self.NUM_PLAYERS) % self.NUM_PLAYERS] def getNextPlayer(self, board, player): found = False for pos in range(len(self.players)): if self.players[pos] == player: found = True break if not found: return None return self.players[(pos + 1) % self.NUM_PLAYERS] def getNextColor(self, color): found = False for pos in range(len(self.COLORS)): if self.COLORS[pos] == color: found = True break if not found: return None return self.COLORS[(pos + 1) % len(self.COLORS)] def parsePossibleMoves(self, force=False): if force: self.possibleMoves = None if self.getCurrentPlayer(self.board) is None: return elif not(self.possibleMoves is None): return moveSet = self.getPossibleMoves(self.board, checkTest=self.CHECK_FOR_CHECK) # debug #for move in moveSet: # move.simpleParse(self.board) # move.fullParse(self.board) #print("I think current player could move like this: " + str(moveSet)) self.possibleMoves = moveSet def getPossibleMoves(self, board, checkTest=True, player=None, noCastlingMoves=False, noDropMoves=False): if not checkTest: oldLogLevel = logger.level logger.setLevel(logging.ERROR) logger.debug('getPossibleMoves') # default if player is None: player = self.getCurrentPlayer(board) logger.debug('Player: %s' % player.color) moveSet = self.findAllPieceMoves(board, player, noDropMoves) # materialize logger.debug('Unfiltered moves generated:') for move in moveSet: move.simpleParse(board) move.fullParse(board) logger.debug(move.str) # filter moveSet = self.filterMovesByRules(moveSet, board, player, noCastlingMoves) logger.debug('After filtering by rules: %s' % str([move.str for move in moveSet])) if checkTest: moveSet = self.filterMovesToCheck(moveSet, board, player) logger.debug('After check filter: %s' % str([move.str for move in moveSet])) logger.debug('=============') if not checkTest: logger.setLevel(oldLogLevel) return moveSet def findAllPieceMoves(self, board, player, noDropMoves=False): # get all the player's pieces pieces = board.findPlayersPieces(player) # get all their candidate moves moveSet = set() for pos in pieces: moveSet |= board.fields[pos].getPossibleMoves(pos) if self.USE_POCKET and not noDropMoves: moveSet = moveSet.union(self.createPocketMoves(board, player)) return moveSet def createPocketMoves(self, board, player): moveSet = set() # generate all moves from pocket playersPocket = board.pockets[player.color] colNo = self.COLORS.index(player.color) # find all empty fields on board emptyFields = [] for i in range(len(self.board.fields)): if self.board.fields[i] is None: emptyFields.append(i) # create the move objects for i in range(len(playersPocket.getPieces())): for j in emptyFields: if self.DROP_NO_PAWN_TO_PROMOTION_FIELDS: # filter posing pawns to promotion fields if (playersPocket.getPieces()[i].shortName in self.PAWN_PIECE_TYPES) and (j in self.allPromotionFields): continue moveSet.add(Move('p' + str(colNo) + str(i), j)) return moveSet def parseCastling(self, moveSet, board, player): if True in board.castlingsPossible[player.color]: # has the king moved already? kingPiece = board.fields[self.castlingPositions[player.color][0]] if not(kingPiece is None) and kingPiece.moveCount > 0 and not self.CASTLING_IF_KING_HAS_MOVED: # destroys both castling possibilities! board.castlingsPossible[player.color] = [False, False] return moveSet # short / long for cType in [0, 1]: if board.castlingsPossible[player.color][cType]: good = True # has the rook moved already? rookPiece = board.fields[self.castlingPositions[player.color][1 + cType]] if not(rookPiece is None) and rookPiece.moveCount > 0 and not self.CASTLING_IF_ROOK_HAS_MOVED: # destroys this castling possibility! board.castlingsPossible[player.color][cType] = False good = False # any relevant field non-empty? if good: # relevant fields are any between rook's and king's start and target fields leftPos = min(self.castlingPositions[player.color][0], self.castlingPositions[player.color][1 + cType], self.castlingTargetPositions[player.color][2 * cType], self.castlingTargetPositions[player.color][2 * cType + 1]) rightPos = max(self.castlingPositions[player.color][0], self.castlingPositions[player.color][1 + cType], self.castlingTargetPositions[player.color][2 * cType], self.castlingTargetPositions[player.color][2 * cType + 1]) # check them for emptiness (moving rook being there is okay [Chess960!]) for pos in range(leftPos, rightPos + 1): if not (board.fields[pos] is None) and pos != self.castlingPositions[player.color][0] and pos != self.castlingPositions[player.color][1 + cType]: # empty, king, rook good = False break # fields for king checked? if good and True: # get all attacked fields opponentMoves = self.getPossibleMoves(board, False, self.getNextPlayer(board, player), noCastlingMoves=True, noDropMoves=True) opponentAttackedFields = set() for oMove in opponentMoves: opponentAttackedFields.add(oMove.toField) # get fields king will cross (including start and end) kingPos = self.castlingPositions[player.color][0] kingTargetPos = self.castlingTargetPositions[player.color][2 * cType] leftPos = min(kingPos, kingTargetPos) rightPos = max(kingPos, kingTargetPos) # compare for pos in range(leftPos, rightPos + 1): if pos in opponentAttackedFields: # test which type of check has happened and obey settings if pos == kingPos and not self.CASTLING_FROM_CHECK: good = False break elif pos == kingTargetPos and not self.CASTLING_TO_CHECK: good = False break elif not self.CASTLING_THROUGH_CHECK: good = False break # good! if good: move = Move(None, None) move.annotation = 'SHORTCASTLING' if cType == 0 else 'LONGCASTLING' moveSet.add(move) return moveSet def parsePromotion(self, moveSet, board, player): resultSet = copy.copy(moveSet) for move in moveSet: if self.moveNeedsPromotion(move, board): for toPiece in self.getPromotionOptions(player.color): promotionMove = copy.copy(move) promotionMove.toPiece = toPiece resultSet.add(promotionMove) return resultSet def parseEnPassant(self, moveSet, board, player): return moveSet def filterMovesByRules(self, moveSet, board, player, noCastlingMoves=False): # add (!) castling options here if self.CASTLING and not noCastlingMoves: self.parseCastling(moveSet, board, player) # add promotion variants if self.PROMOTION: self.parsePromotion(moveSet, board, player) # add en passant moves if self.EN_PASSANT: self.parseEnPassant(moveSet, board, player) # adhere to blocking status of fields for move in set(moveSet): if move.toField in self.BLOCKED_FIELDS: moveSet.remove(move) return moveSet def filterMovesToCheck(self, moveSet, board, player): for move in set(moveSet): # work on a copy to be able to remove inside logger.debug('Testing move %s for check' % move.str) # no more tests for castling (it has already been filtered for moving into check!) if not(move.annotation is None) and 'CASTLING' in move.annotation: continue move.simpleParse(self.board) #print('filtering ' + str(move)) # create a board copy for analysis purposes whatIfBoard = copy.deepcopy(self.board) logger.debug('board vs. whatIfBoard:\n%s\n\n%s' % (str(board), str(whatIfBoard))) self.move(move, whatIfBoard, dontHandleCapture=True) logger.debug('whatIfBoard after:\n%s' % str(whatIfBoard)) #print("what if? \n" + whatIfBoard.__unicode__()) # did the player stay in check? if whatIfBoard.isInCheck(player): logger.debug('still in check -> removing move %s from the list' % move.str) moveSet.remove(move) else: logger.debug('not in check -> keeping move %s in the list' % move.str) return moveSet def getBoard(self, boardId): if str(self.board.id) == str(boardId): return self.board return None def isLegalMove(self, move, boardId=None): self.parsePossibleMoves() if self.possibleMoves is None: return False if move in self.possibleMoves: return True return False def getFenPos(self, board, player): return self._getFenPosFiltered(board, player, []) def _getFenPosFiltered(self, board, player, hiddenList): fenString = '' # build for row in range(board.height): for col in range(board.width): pos = row * board.width + col piece = board.fields[pos] if piece is None or pos in hiddenList: fenString = fenString + '_' else: fenString = fenString + piece.getShortName() fenString = fenString + '/' # shorten for length in range(board.width, 0, -1): fenString = fenString.replace(('_' * length), str(length)) return fenString[:-1] def finish(self): self.finished = True # stop clocks for player in self.players: player.mq.clock.stop() # send final state for player in self.players: msg = self.getSituationMessage(player.mq, False, player, False) if not(msg is None): player.mq.send(msg.data) def __unicode__(self): return "[Game] id=%s, type=%s" % (self.id, self.gameType) def __str__(self): return self.__unicode__()
def endInit(self): # create board if it has not yet been done (by overloading and because of special needs) if not hasattr(self, 'board') or self.board == None: self.board = Board(self, width=self.board_width, height=self.board_height) # load position self.board.loadFenPos(self.fenPos) # promotion settings self.promotionFields = {'white': self.board.getRankFields(0), 'black': self.board.getRankFields(self.board_height - 1)} self.allPromotionFields = self.promotionFields['white'] + self.promotionFields['black'] self.possiblePromotionPieces = self.USED_PIECES.difference(self.PAWN_PIECE_TYPES) if not self.CAN_PROMOTE_TO_KING: self.possiblePromotionPieces.difference_update(self.KING_PIECE_TYPES) self.possiblePromotionPieces = list(self.possiblePromotionPieces) # castling information self.castlingPositions = {} # dictionary of lists (keys = color strings) #self.castlingPieces = {} # dictionary of lists (keys = color strings) self.castlingTargetPositions = {} # dictionary of lists (keys = color strings) for color in self.COLORS: # king kingsPos = self.board.findPieces(['k'], [color]) if kingsPos is None or len(kingsPos) > 1: self.board.castlingsPossible[color] = [False, False] kingPos = None else: kingPos = kingsPos[0] # rook rooksPos = self.board.findPieces(['r'], [color]) kingRookPos = None queenRookPos = None for rookPos in rooksPos: if self.board.splitPos(rookPos)[0] > self.board.width / 2: kingRookPos = rookPos else: queenRookPos = rookPos if kingRookPos == None: self.board.castlingsPossible[color][0] = False if queenRookPos == None: self.board.castlingsPossible[color][1] = False self.castlingPositions[color] = [kingPos, kingRookPos, queenRookPos] # [0]: king, [1] king side rook, [2] queen side rook #self.castlingPieces[color] = [None if x is None else self.board.fields[x] for x in self.castlingPositions[color]] kingShort = self.board.addPos(self.board.splitPos(kingPos), [2, 0]) rookShort = self.board.addPos(kingShort, [-1, 0]) kingLong = self.board.addPos(self.board.splitPos(kingPos), [-2, 0]) rookLong = self.board.addPos(kingLong, [1, 0]) self.castlingTargetPositions[color] = [self.board.mergePos(kingShort[0], kingShort[1]), self.board.mergePos(rookShort[0], rookShort[1]), self.board.mergePos(kingLong[0], kingLong[1]), self.board.mergePos(rookLong[0], rookLong[1])] # [0]: king for short, [1] rook for short, [2]: king for long, [3] rook for long # pre-create players self.createPlayers() self.createClocks() # draw preparations if self.DRAW_REPETITION: self.board.drawCountRepetition() # metagame self.metagame = self self.board.metagame = self