1""" This module contains chess logic functins for the pychess client. They are 2 based upon the lutils modules, but supports standard object types and is 3 therefore not as fast. """ 4 5from .lutils import lmovegen 6from .lutils.validator import validateMove 7from .lutils.lmove import FCORD, TCORD 8from .lutils import ldraw 9from .Cord import Cord 10from .Move import Move 11from .const import LOSERSCHESS, WHITE, WHITEWON, BLACKWON, WON_NOMATERIAL, KING, HORDECHESS, \ 12 SUICIDECHESS, GIVEAWAYCHESS, ATOMICCHESS, WON_KINGEXPLODE, KINGOFTHEHILLCHESS, BLACK, DRAW, \ 13 CRAZYHOUSECHESS, WON_KINGINCENTER, THREECHECKCHESS, WON_THREECHECK, WON_MATE, DRAW_STALEMATE, \ 14 DRAW_INSUFFICIENT, DRAW_EQUALMATERIAL, WON_LESSMATERIAL, WON_WIPEOUT, DRAW_REPETITION, \ 15 WON_KINGINEIGHTROW, RACINGKINGSCHESS, DRAW_50MOVES, DRAW_KINGSINEIGHTROW, RUNNING, ENPASSANT, UNKNOWN_REASON 16 17from .lutils.bitboard import iterBits 18from .lutils.attack import getAttacks 19from pychess.Variants.suicide import pieceCount 20from pychess.Variants.losers import testKingOnly 21from pychess.Variants.atomic import kingExplode 22from pychess.Variants.kingofthehill import testKingInCenter 23from pychess.Variants.threecheck import checkCount 24from pychess.Variants.racingkings import testKingInEightRow, test2KingInEightRow 25 26 27def getDestinationCords(board, cord): 28 tcords = [] 29 for move in lmovegen.genAllMoves(board.board): 30 if FCORD(move) == cord.cord: 31 if not board.board.willLeaveInCheck(move): 32 tcords.append(Cord(TCORD(move))) 33 return tcords 34 35 36def isClaimableDraw(board): 37 lboard = board.board 38 if lboard.repetitionCount() >= 3: 39 return True 40 if ldraw.testFifty(lboard): 41 return True 42 return False 43 44 45def playerHasMatingMaterial(board, playercolor): 46 if board.variant == CRAZYHOUSECHESS: 47 return True 48 lboard = board.board 49 return ldraw.testPlayerMatingMaterial(lboard, playercolor) 50 51 52def getStatus(board): 53 lboard = board.board 54 55 if board.variant == LOSERSCHESS: 56 if testKingOnly(lboard): 57 if board.color == WHITE: 58 status = WHITEWON 59 else: 60 status = BLACKWON 61 return status, WON_NOMATERIAL 62 elif board.variant == SUICIDECHESS or board.variant == GIVEAWAYCHESS: 63 if pieceCount(lboard, lboard.color) == 0: 64 if board.color == WHITE: 65 status = WHITEWON 66 else: 67 status = BLACKWON 68 return status, WON_NOMATERIAL 69 elif board.variant == HORDECHESS: 70 if pieceCount(lboard, lboard.color) == 0 and board.color == WHITE: 71 status = BLACKWON 72 return status, WON_WIPEOUT 73 elif board.variant == ATOMICCHESS: 74 if lboard.boards[board.color][KING] == 0: 75 if board.color == WHITE: 76 status = BLACKWON 77 else: 78 status = WHITEWON 79 return status, WON_KINGEXPLODE 80 elif board.variant == KINGOFTHEHILLCHESS: 81 if testKingInCenter(lboard): 82 if board.color == BLACK: 83 status = WHITEWON 84 else: 85 status = BLACKWON 86 return status, WON_KINGINCENTER 87 elif board.variant == THREECHECKCHESS: 88 if checkCount(lboard, lboard.color) == 3: 89 if board.color == BLACK: 90 status = WHITEWON 91 else: 92 status = BLACKWON 93 return status, WON_THREECHECK 94 elif board.variant == RACINGKINGSCHESS: 95 if test2KingInEightRow(lboard): 96 return DRAW, DRAW_KINGSINEIGHTROW 97 elif testKingInEightRow(lboard): 98 can_save = False 99 for move in lmovegen.genAllMoves(lboard): 100 if lboard.willGiveCheck(move) or lboard.willLeaveInCheck(move): 101 continue 102 103 lboard.applyMove(move) 104 if testKingInEightRow(lboard): 105 can_save = True 106 lboard.popMove() 107 break 108 lboard.popMove() 109 if not can_save: 110 if board.color == BLACK: 111 status = WHITEWON 112 else: 113 status = BLACKWON 114 return status, WON_KINGINEIGHTROW 115 else: 116 if ldraw.testMaterial(lboard): 117 return DRAW, DRAW_INSUFFICIENT 118 119 hasMove = False 120 for move in lmovegen.genAllMoves(lboard): 121 if board.variant == ATOMICCHESS: 122 if kingExplode(lboard, move, 1 - board.color) and not kingExplode( 123 lboard, move, board.color): 124 hasMove = True 125 break 126 elif kingExplode(lboard, move, board.color): 127 continue 128 lboard.applyMove(move) 129 if lboard.opIsChecked(): 130 lboard.popMove() 131 continue 132 hasMove = True 133 lboard.popMove() 134 break 135 136 if not hasMove: 137 if lboard.isChecked(): 138 if board.variant == LOSERSCHESS: 139 if board.color == WHITE: 140 status = WHITEWON 141 else: 142 status = BLACKWON 143 else: 144 if board.color == WHITE: 145 status = BLACKWON 146 else: 147 status = WHITEWON 148 return status, WON_MATE 149 else: 150 if board.variant == LOSERSCHESS or board.variant == GIVEAWAYCHESS: 151 if board.color == WHITE: 152 status = WHITEWON 153 else: 154 status = BLACKWON 155 return status, DRAW_STALEMATE 156 elif board.variant == SUICIDECHESS: 157 if pieceCount(lboard, WHITE) == pieceCount(lboard, BLACK): 158 return status, DRAW_EQUALMATERIAL 159 else: 160 if board.color == WHITE and pieceCount( 161 lboard, WHITE) < pieceCount(lboard, BLACK): 162 status = WHITEWON 163 else: 164 status = BLACKWON 165 return status, WON_LESSMATERIAL 166 else: 167 return DRAW, DRAW_STALEMATE 168 169 if lboard.repetitionCount() >= 3: 170 return DRAW, DRAW_REPETITION 171 172 if ldraw.testFifty(lboard): 173 return DRAW, DRAW_50MOVES 174 175 return RUNNING, UNKNOWN_REASON 176 177 178def standard_validate(board, move): 179 return validateMove(board.board, move.move) and \ 180 not board.board.willLeaveInCheck(move.move) 181 182 183def validate(board, move): 184 if board.variant == LOSERSCHESS: 185 capture = move.flag == ENPASSANT or board[move.cord1] is not None 186 if capture: 187 return standard_validate(board, move) 188 else: 189 can_capture = False 190 can_escape_with_capture = False 191 ischecked = board.board.isChecked() 192 for c in lmovegen.genCaptures(board.board): 193 if board.board.willLeaveInCheck(c): 194 continue 195 else: 196 can_capture = True 197 if ischecked: 198 can_escape_with_capture = True 199 break 200 if can_capture: 201 if ischecked and not can_escape_with_capture: 202 return standard_validate(board, move) 203 else: 204 return False 205 else: 206 return standard_validate(board, move) 207 elif board.variant == SUICIDECHESS: 208 capture = move.flag == ENPASSANT or board[move.cord1] is not None 209 if capture: 210 return standard_validate(board, move) 211 else: 212 can_capture = False 213 for c in lmovegen.genCaptures(board.board): 214 can_capture = True 215 if can_capture: 216 return False 217 else: 218 return standard_validate(board, move) 219 elif board.variant == ATOMICCHESS: 220 # Moves exploding our king are not allowed 221 if kingExplode(board.board, move.move, board.color): 222 return False 223 # Exploding oppont king takes precedence over mate 224 elif kingExplode(board.board, move.move, 1 - 225 board.color) and validateMove(board.board, move.move): 226 return True 227 else: 228 return standard_validate(board, move) 229 elif board.variant == RACINGKINGSCHESS: 230 # Giving check is forbidden 231 if board.board.willGiveCheck(move.move): 232 return False 233 else: 234 return standard_validate(board, move) 235 else: 236 return standard_validate(board, move) 237 238 239def getMoveKillingKing(board): 240 """ Returns a move from the current color, able to capture the opponent 241 king """ 242 243 lboard = board.board 244 color = lboard.color 245 opking = lboard.kings[1 - color] 246 247 for cord in iterBits(getAttacks(lboard, opking, color)): 248 return Move(Cord(cord), Cord(opking), board) 249 250 251def genCastles(board): 252 for move in lmovegen.genCastles(board.board): 253 yield Move(move) 254 255 256def legalMoveCount(board): 257 moves = 0 258 for move in lmovegen.genAllMoves(board.board): 259 if not board.board.willLeaveInCheck(move): 260 moves += 1 261 return moves 262