def evaluate_king_shelter(self, position, colour, king_sqr): # Gets the bitboard of the back two ranks for the given colour shelter_ranks = RANK_1_BB | RANK_2_BB if colour == WHITE else RANK_7_BB | RANK_8_BB # Gets the bitboard for the rank of the king and the ranks in front of the king ranks_in_front = ~forward_ranks[colour ^ 1][king_sqr] # Gets the pawns on the rank of the king and the ranks in front of the king player_pawns = position.piece_bb[(colour << 3) | PAWN] & ranks_in_front enemy_pawns = position.piece_bb[((colour ^ 1) << 3) | PAWN] & ranks_in_front # Give a bonus for enemy edge pawns in front of our king if pawn_shift[colour ^ 1](enemy_pawns, NORTH) & (A_FILE_BB | H_FILE_BB) & shelter_ranks & (1 << king_sqr): safety_score = 374 else: safety_score = 5 # Get the centre file of the king shelter king_file = king_sqr & 7 if king_file == A_FILE: centre = B_FILE elif king_file == H_FILE: centre = G_FILE else: centre = king_file # Iterate through the centre file and the two adjacent files for file_num in range(centre - 1, centre + 2): file_bb = A_FILE_BB << file_num pawns_on_file = player_pawns & file_bb if pawns_on_file: if colour == WHITE: backmost_sq = bit_scan1(pawns_on_file) # Least significant bit else: backmost_sq = pawns_on_file.bit_length() - 1 # Most significant bit player_rank = (backmost_sq >> 3) ^ (colour * 7) else: player_rank = RANK_1 pawns_on_file = enemy_pawns & file_bb if pawns_on_file: if colour == WHITE: frontmost_sq = pawns_on_file.bit_length() - 1 # Most significant bit else: frontmost_sq = bit_scan1(pawns_on_file) # Least significant bit enemy_rank = (frontmost_sq >> 3) ^ (colour * 7) else: enemy_rank = RANK_1 opposite_file = file_num ^ H_FILE min_file = opposite_file if opposite_file < file_num else file_num safety_score += shelter_strength[min_file][player_rank] if player_rank and player_rank == enemy_rank - 1: if enemy_rank == RANK_3: safety_score -= 66 else: safety_score -= unblocked_storm[min_file][enemy_rank] return safety_score
def checkGroup(self, state=None, player=None): if state is None: st = self.state else: st = state if player is None: p = self.currentPlayer else: p = player areGrouped = True if gmpy2.popcount(st[p]) == 1: areGrouped = True else: unvisited = st[p] checklist = 2**gmpy2.bit_scan1(unvisited) unvisited ^= checklist while gmpy2.popcount(checklist) > 0: current = gmpy2.bit_scan1(checklist) checklist ^= 2**current currentNeighbours = self.neighbourhood[current] & unvisited if currentNeighbours: unvisited ^= currentNeighbours checklist |= currentNeighbours if gmpy2.popcount(unvisited) > 0: areGrouped = False return areGrouped
def get_all_valid_moves(self, player): all_candidate_moves = {} result = self.getMoves(self.bitboard[player], self.bitboard[-player]) for m, flipped_square in result: x = gmpy2.bit_scan1(m) / 8 y = gmpy2.bit_scan1(m) % 8 bstr = self.__gererate_new_board(flipped_square, player) new_board = BitBoard(None) new_board.bitboard = bstr new_board.bitboard[player] |= m all_candidate_moves[(x, y)] = new_board return all_candidate_moves avalible_move = 0 moves = self.__valid_moves(player) for m in moves.values(): avalible_move |= m all_candidate_moves = {} idx = gmpy2.bit_scan1(avalible_move, 0) while idx is not None: mask = 1 << idx flipped_square = 0 for dir in direction.keys(): if mask & moves[dir] != 0: flipped_square |= self.__generate_flipped_squares(mask, dir, player) bstr = self.__gererate_new_board(flipped_square, player) new_board = BitBoard(None) new_board.bitboard = bstr all_candidate_moves[(idx / 8, idx % 8)] = new_board idx = gmpy2.bit_scan1(avalible_move, idx + 1) return all_candidate_moves
def paintBoard(moving=None): screen.fill(BGCOLOUR) pygame.draw.rect(screen, BOARDCOLOUR, (XMARGIN, YMARGIN, BOARDWIDTH + BOARDMARGIN * 2, BOARDHEIGHT + BOARDMARGIN * 2)) # squares for i in range(9): pygame.draw.line( screen, BLACK, (XMARGIN + BOARDMARGIN + i * BOXSIZE, YMARGIN + BOARDMARGIN), (XMARGIN + BOARDMARGIN + i * BOXSIZE, WINDOWHEIGHT - YMARGIN - BOARDMARGIN), 1) pygame.draw.line( screen, BLACK, (XMARGIN + BOARDMARGIN, YMARGIN + BOARDMARGIN + i * BOXSIZE), (WINDOWWIDTH - XMARGIN - BOARDMARGIN, YMARGIN + BOARDMARGIN + i * BOXSIZE), 1) # stones pos = gmpy2.bit_scan1(board.state[BLACKVAL]) while pos is not None: x, y = getCoordFromPos(pos) pygame.gfxdraw.filled_circle(screen, x, y, STONESIZE, BLACK) pygame.gfxdraw.aacircle(screen, x, y, STONESIZE, BLACK) pos = gmpy2.bit_scan1(board.state[BLACKVAL], pos + 1) pos = gmpy2.bit_scan1(board.state[WHITEVAL]) while pos is not None: x, y = getCoordFromPos(pos) pygame.gfxdraw.filled_circle(screen, x, y, STONESIZE, WHITE) pygame.gfxdraw.aacircle(screen, x, y, STONESIZE, BLACK) pos = gmpy2.bit_scan1(board.state[WHITEVAL], pos + 1) if moving: pygame.gfxdraw.filled_circle(screen, moving[0], moving[1], STONESIZE, PCOLOUR[board.currentPlayer]) pygame.gfxdraw.aacircle(screen, moving[0], moving[1], STONESIZE, BLACK) else: # fromPos if fromPos is not None: x, y = getCoordFromPos(fromPos) pygame.gfxdraw.filled_circle(screen, x, y, 3, RED) pygame.gfxdraw.aacircle(screen, x, y, 3, RED) if len(availableMoves) > 0: for move in availableMoves: if fromPos == move[0]: x2, y2 = getCoordFromPos(move[1]) pygame.gfxdraw.aacircle(screen, x2, y2, 3, RED) if toPos is not None and fromPos != toPos: x2, y2 = getCoordFromPos(toPos) pygame.gfxdraw.line(screen, x, y, x2, y2, RED) pygame.gfxdraw.filled_circle(screen, x2, y2, 3, RED) pygame.gfxdraw.aacircle(screen, x2, y2, 3, RED)
def is_legal(self, move): colour = self.colour enemy_colour = colour ^ 1 src_index = (move >> 6) & 0x3F dst_index = move & 0x3F src_bb = 1 << src_index dst_bb = 1 << dst_index move_type = move & (0x3 << 14) king_sqr = bit_scan1(self.piece_bb[((colour << 3) | KING)]) occ = self.occupancy enemy_rooks = self.piece_bb[((enemy_colour << 3) | ROOK)] enemy_bishops = self.piece_bb[((enemy_colour << 3) | BISHOP)] enemy_queens = self.piece_bb[((enemy_colour << 3) | QUEEN)] if self.is_square_attacked(king_sqr): return True if move in self.get_check_evasions(colour) else False if move_type == EN_PASSANT: self.make_move(move) in_check = self.is_in_check(colour) self.undo_move() return False if in_check else True if src_index == king_sqr: # Castling legality is checked during move generation if move_type == CASTLING: return True if self.is_square_attacked(dst_index, occ ^ (1 << king_sqr)): return False else: return True sliders = ((pseudo_attacks[ROOK][king_sqr] & (enemy_rooks | enemy_queens)) | (pseudo_attacks[BISHOP][king_sqr] & (enemy_bishops | enemy_queens))) while sliders: slider_sqr = bit_scan1(sliders) sqrs_between = bb_between[king_sqr][slider_sqr] blockers = sqrs_between & occ if src_bb & sqrs_between: # If moving blocker piece if not blockers ^ src_bb: # If no other blockers if not dst_bb & ( sqrs_between | (1 << slider_sqr)): # If not staying on the diagonal return False sliders &= sliders - 1 return True
def next(self): shift = gmpy2.bit_scan1(self.n) if shift==None: return -1 self.global_shift += shift self.n = self.n >> shift self.n = self.n.bit_flip(0) return self.global_shift
def is_in_check(self, colour=None): if colour is None: colour = self.colour king_index = bit_scan1(self.piece_bb[((colour << 3) | KING)]) if self.is_square_attacked(king_index, colour=colour ^ 1): return True return False
def allAvailableMoves(self, state=None, player=None): if state is None: st = self.state else: st = state if player is None: p = self.currentPlayer else: p = player aav = [] n = gmpy2.bit_scan1(st[p]) while n: aav += self.availableMoves(n, st, p) n = gmpy2.bit_scan1(st[p], n + 1) return aav
def decode(grid: numpy.ndarray, n: int) -> numpy.ndarray: """Decodes the given 1D binary grid to a regular 2D numpy array with human readable numbers. Args: :param grid: Grid to be decoded. :param n: Size of the subgrid of the provided grid. Returns: :return: Converted grid. """ converter = numpy.vectorize(lambda x: bit_scan1(int(x)) + 1 if x != 0 else 0, otypes=[numpy.ulonglong]) return converter(grid).reshape(n**2, n**2)
def factor(n, e, d): f = e * d - 1 lsb = gmpy2.bit_scan1(f) t = f >> lsb a = 2 while True: ap = pow(a, t, n) for i in range(1, lsb + 1): if ap == 1: break if ap == n - 1: break ap2 = pow(ap, 2, n) if ap2 == 1: p = gmpy2.gcd(ap - 1, n) return p, n // p ap = ap2
def see(self, from_sq, target_sq): gain = [None] * 32 depth = 0 gain[depth] = MATERIAL[self.squares[target_sq] & 7][MIDGAME] occ = self.occupancy colour = self.colour attackers = self.attacks_to(target_sq, colour ^ 1, occ) attackers |= self.attacks_to(target_sq, colour, occ) from_bb = 1 << from_sq slider_blockers = occ ^ ( self.piece_bb[W_KNIGHT] | self.piece_bb[B_KNIGHT] | self.piece_bb[W_KING] | self.piece_bb[B_KING]) piece = self.squares[from_sq] while from_bb: depth += 1 colour ^= 1 gain[depth] = MATERIAL[piece & 7][MIDGAME] - gain[depth - 1] if max(-gain[depth - 1], gain[depth]) < 0: break attackers ^= from_bb occ ^= from_bb if from_bb & slider_blockers: attackers |= ratk_table[target_sq][ occ & rook_masks[target_sq]] & (self.piece_bb[W_ROOK] | self.piece_bb[B_ROOK] | self.piece_bb[W_QUEEN] | self.piece_bb[B_QUEEN]) & occ attackers |= batk_table[target_sq][ occ & bishop_masks[target_sq]] & ( self.piece_bb[W_BISHOP] | self.piece_bb[B_BISHOP] | self.piece_bb[W_QUEEN] | self.piece_bb[B_QUEEN]) & occ from_bb = self.get_least_valuable_piece(attackers, colour) if from_bb: piece = self.squares[bit_scan1(from_bb)] depth -= 1 while depth: gain[depth - 1] = -max(-gain[depth - 1], gain[depth]) depth -= 1 return gain[0]
def get_king_safety(self, position, colour): king_sqr = bit_scan1(position.piece_bb[(colour << 3) | KING]) # If king square and castling rights have not changed, use the previously saved score if self.prev_king_square[colour] == king_sqr: if self.prev_castling_rights[colour] == position.castling_rights & (0x3 << (colour * 2)): return self.king_safety[colour] player_pawns = position.piece_bb[(colour << 3) | PAWN] # Calculate the distance between the king and the closest pawn of its colour king_pawn_distance = 0 if player_pawns: king_pawn_distance += 1 while not distance_ring[king_sqr][king_pawn_distance] & player_pawns: king_pawn_distance += 1 # Evaluate the shelter for the king square score = self.evaluate_king_shelter(position, colour, king_sqr) # If castling kingside is possible, evaluate the shelter for the kingside castled square # Use the castled score if greater than the original score if position.castling_rights & (KINGSIDE << colour): castled_score = self.evaluate_king_shelter(position, colour, 6 ^ (colour * 56)) if castled_score > score: score = castled_score # If castling queenside is possible, evaluate the shelter for the queenside castled square # Use the castled score if greater than the original score if position.castling_rights & (QUEENSIDE << colour): castled_score = self.evaluate_king_shelter(position, colour, 2 ^ (colour * 56)) if castled_score > score: score = castled_score # Save the king safety information for the current position self.prev_king_square[colour] = king_sqr self.prev_castling_rights[colour] = position.castling_rights & (0x3 << (colour * 2)) self.king_safety[colour] = (score, -16 * king_pawn_distance) return score, -16 * king_pawn_distance
def gen_bitboard_indices(bb): while bb: yield bit_scan1(bb) bb &= bb - 1
def updateUnseen(self): shift = gmpy2.bit_scan1(self.unseen) self.unseen = self.unseen>>shift self.prime = self.prime + shift self.global_shift+=shift
def evaluate(self, state=None, player=None, move=None): if state is None: st = self.state else: st = state if player is None: p = self.currentPlayer else: p = player # the player n = gmpy2.popcount(st[p]) if n == 1: return 50 nn = gmpy2.popcount(st[-p]) if nn == 1: return -50 x = gmpy2.bit_scan1(st[p]) # centre of mass rr, cc = 0, 0 pieces = [] f2p = 0 f7p = 0 left = right = 7 - x % 8 top = bottom = 7 - x // 8 while x is not None: r, c = 7 - x // 8, 7 - x % 8 if r < top: top = r elif r > bottom: bottom = r if c < left: left = c elif c > right: right = c pieces.append((r, c)) rr += r cc += c for i in range(5): if 2**x & self.board_cat[i]: f2p += i - 2 break f7p += gmpy2.popcount(self.neighbourhood[x] & st[p]) x = gmpy2.bit_scan1(st[p], x + 1) rr /= n rr = round(rr) cc /= n cc = round(cc) comp = (rr, cc) # sum of distances dist = 0 for r, c in pieces: dist += max(abs(r - rr), abs(c - cc)) # surplus of distances surplus = dist - self.min_sum_dist[n] f1p = 1 / (surplus + 1) f2p /= n * 2 pos = (7 - rr) * 8 + 7 - cc for i in range(4, -1, -1): if 2**pos & self.board_cat[i]: f3p = 1 / (1 + i) break f4p = 0 for q in self.quads[comp]: if gmpy2.popcount(q & st[p]) == 3: f4p += 1 elif gmpy2.popcount(q & st[p]) == 4: f4p += 2 f4p /= len(self.quads[comp]) f5p = 0 if move: f5p = .5 if self.stoneTaken: f5p *= 2 if 2**move[1] & (self.board_cat[0] | self.board_cat[1]): f5p /= 2 if 2**move[0] & (self.board_cat[0] | self.board_cat[1]): f5p /= 2 f7p /= n f8p = 1 / ((right - left + 1) * (bottom - top + 1)) # the other x = gmpy2.bit_scan1(st[-p]) # centre of mass rr, cc = 0, 0 pieces = [] f2o = 0 f7o = 0 left = right = 7 - x % 8 top = bottom = 7 - x // 8 while x is not None: r, c = 7 - x // 8, 7 - x % 8 if r < top: top = r elif r > bottom: bottom = r if c < left: left = c elif c > right: right = c pieces.append((r, c)) rr += r cc += c for i in range(5): if 2**x & self.board_cat[i]: f2o += i - 2 break f7o += gmpy2.popcount(self.neighbourhood[x] & st[-p]) x = gmpy2.bit_scan1(st[-p], x + 1) rr /= nn rr = round(rr) cc /= nn cc = round(cc) como = (rr, cc) # sum of distances dist = 0 for r, c in pieces: dist += max(abs(r - rr), abs(c - cc)) # surplus of distances surplus = dist - self.min_sum_dist[nn] f1o = 1 / (surplus + 1) f2o /= nn * 2 pos = (7 - rr) * 8 + 7 - cc for i in range(4, -1, -1): if 2**pos & self.board_cat[i]: f3o = 1 / (1 + i) break f4o = 0 for q in self.quads[como]: if gmpy2.popcount(q & st[-p]) == 3: f4o += 1 elif gmpy2.popcount(q & st[-p]) == 4: f4o += 2 f4o /= len(self.quads[como]) f7o /= nn f8o = 1 / ((right - left + 1) * (bottom - top + 1)) val = f1p - f1o + f2p - f2o + f3p - f3o + f4p - f4o + f5p + f7p - f7o + f8p - f8o # f9. player to move bonus if p == self.currentPlayer: val += .2 return val
def update(self, grid, candidate_values, candidate_nums, value, state, filled, found): def bits(x, f, u): return ''.join([f if int(x) & 1 << n else u for n in range(9)]) def nums(x): return ''.join( [str(n + 1) if int(x) & 1 << n else '-' for n in range(9)]) if self.counter > Printer.max_state: return self.file.write(''.join(['#'] * 97)) self.file.write('\n#{} STEP {} {}#\n'.format( ''.join(['='] * ((88 - len(str(self.counter))) // 2)), self.counter, ''.join(['='] * int(math.ceil( (88 - len(str(self.counter))) / 2))))) self.file.write( '\nFILLED:\t\t{}\nSTATE:\t\t{}\nFOUND:\t\t{} / {}\n\n'.format( filled, state, found, self.max)) sep = ''.join(['-'] * 31) self.file.write('GRID:\n') for i in range(self.n**2): if i % self.n == 0: self.file.write('+{}+{}+{}+\n'.format(sep, sep, sep)) self.file.write('| {} {} {} | {} {} {} | {} {} {} |\n'.format(*[ ' ({}) '.format( int(gmpy2.bit_scan1(grid[i * self.n**2 + j])) + 1 if int(grid[i * self.n**2 + j]) != 0 else '?') for j in range(self.n**2) ])) self.file.write('| {} {} {} | {} {} {} | {} {} {} |\n'.format(*[ bits(grid[i * self.n**2 + j], '#', '.') if grid[i * self.n**2 + j] != 0 else ''.join([' '] * 9) for j in range(self.n**2) ])) self.file.write('+{}+{}+{}+\n'.format(sep, sep, sep)) self.file.write('\nCANDIDATES:\n') for i in range(self.n**2): if i % self.n == 0: self.file.write('+{}+{}+{}+\n'.format(sep, sep, sep)) self.file.write('| {} {} {} | {} {} {} | {} {} {} |\n'.format(*[ ' ({}) '.format(candidate_nums[i * self.n**2 + j]) for j in range(self.n**2) ])) self.file.write('| {} {} {} | {} {} {} | {} {} {} |\n'.format(*[ nums(candidate_values[i * self.n**2 + j]) if grid[i * self.n**2 + j] == 0 else ''.join([' '] * 9) for j in range(self.n**2) ])) self.file.write('+{}+{}+{}+\n\n'.format(sep, sep, sep)) self.counter += 1
def rho(j): assert j != 0, "rho is inf" return gmpy2.bit_scan1(j)
def get_check_evasions(self, colour): move_list = deque() occ = self.occupancy king_sqr = bit_scan1(self.piece_bb[((colour << 3) | KING)]) # Try moving king moves = pseudo_attacks[KING][king_sqr] & ~self.player_occ[colour] for sq in gen_bitboard_indices(moves): if not self.is_square_attacked(sq, occ ^ (1 << king_sqr)): move_list.append((king_sqr << 6) + sq) attackers = self.attacks_to(king_sqr, colour ^ 1, occ) # If in double check, only evasions are through king moves if attackers & (attackers - 1): return move_list enemy_colour = colour ^ 1 enemy_rooks = self.piece_bb[((enemy_colour << 3) | ROOK)] enemy_bishops = self.piece_bb[((enemy_colour << 3) | BISHOP)] enemy_queens = self.piece_bb[((enemy_colour << 3) | QUEEN)] sliders = ((pseudo_attacks[ROOK][king_sqr] & (enemy_rooks | enemy_queens)) | (pseudo_attacks[BISHOP][king_sqr] & (enemy_bishops | enemy_queens))) pinned = 0 while sliders: slider_sqr = bit_scan1(sliders) sqrs_between = bb_between[king_sqr][slider_sqr] blockers = sqrs_between & occ if not blockers & (blockers - 1): pinned |= blockers sliders &= sliders - 1 attacker_sqr = bit_scan1(attackers) attacker_piece = self.squares[attacker_sqr] & 7 colour_mask = colour << 3 pawns = self.piece_bb[colour_mask | PAWN] knights = self.piece_bb[colour_mask | KNIGHT] bishops = self.piece_bb[colour_mask | BISHOP] rooks = self.piece_bb[colour_mask | ROOK] queens = self.piece_bb[colour_mask | QUEEN] # Try capturing attacker piece defenders = pawn_attacks[colour ^ 1][attacker_sqr] & pawns defenders |= pseudo_attacks[KNIGHT][attacker_sqr] & knights defenders |= ratk_table[attacker_sqr][ occ & rook_masks[attacker_sqr]] & (rooks | queens) defenders |= batk_table[attacker_sqr][ occ & bishop_masks[attacker_sqr]] & (bishops | queens) defenders &= ~pinned for defender_sq in gen_bitboard_indices(defenders): if self.squares[defender_sq] & 7 == PAWN and attacker_sqr >> 3 == ( RANK_8 if colour == WHITE else RANK_1): generate_promotions(defender_sq, attacker_sqr, move_list) else: move_list.append((defender_sq << 6) + attacker_sqr) # Try blocking attack by slider piece if attacker_piece == BISHOP or attacker_piece == ROOK or attacker_piece == QUEEN: sqrs_between = bb_between[king_sqr][attacker_sqr] for sq in gen_bitboard_indices(sqrs_between): one_step = pawn_shift[colour ^ 1](1 << sq, NORTH) blockers = one_step & pawns blockers |= ((RANK_2_BB if colour == WHITE else RANK_7_BB) & pawn_shift[colour ^ 1](one_step & ~occ, NORTH) & pawns) blockers |= pseudo_attacks[KNIGHT][sq] & knights blockers |= ratk_table[sq][occ & rook_masks[sq]] & (rooks | queens) blockers |= batk_table[sq][occ & bishop_masks[sq]] & (bishops | queens) blockers &= ~pinned for blocker_sq in gen_bitboard_indices(blockers): if self.squares[ blocker_sq] & 7 == PAWN and attacker_sqr >> 3 == ( RANK_8 if colour == WHITE else RANK_1): generate_promotions(blocker_sq, sq, move_list) else: move_list.append((blocker_sq << 6) + sq) if self.ep_square: ep_attackers = pawn_attacks[colour ^ 1][self.ep_square] & pawns if ep_attackers: for sq in gen_bitboard_indices(ep_attackers): ep_move = EN_PASSANT + (sq << 6) + self.ep_square self.make_move(ep_move) in_check = self.is_in_check(colour) self.undo_move() if not in_check: move_list.append(ep_move) return move_list