// Marks the path of the laser until it hits a piece or goes off the board. // Returns the number of unpinned pawns. // // p : current board state // laser_map : end result will be stored here. Every square on the // path of the laser is marked with mark_mask // c : color of king shooting laser // mark_mask: what each square is marked with int mark_laser_path(position_t *p, char *laser_map, color_t c, char mark_mask) { int pinned_pawns = 0; uint8_t total_pawns; color_t color = opp_color(c); square_t o_king_sq = p->kloc[color]; if (c == WHITE) { // opposing king pins our pawns total_pawns = p->pawn_count[BLACK]; } else { total_pawns = p->pawn_count[WHITE]; } // Fire laser, recording in laser_map square_t sq = p->kloc[c]; int bdir = ori_of(p->board[sq]); int beam = beam_of(bdir); tbassert(ptype_of(p->board[sq]) == KING, "ptype: %d\n", ptype_of(p->board[sq])); laser_map[sq] |= mark_mask; // we update h_attackable here h_attackable = h_dist(sq, o_king_sq); while (true) { sq += beam; laser_map[sq] |= mark_mask; tbassert(sq < ARR_SIZE && sq >= 0, "sq: %d\n", sq); switch (ptype_of(p->board[sq])) { case EMPTY: // empty square h_attackable += h_dist(sq, o_king_sq); break; case PAWN: // Pawn h_attackable += h_dist(sq, o_king_sq); if (color_of(p->board[sq]) == color) { pinned_pawns += 1; } bdir = reflect_of(bdir, ori_of(p->board[sq])); if (bdir < 0) { // Hit back of Pawn return total_pawns - pinned_pawns; } beam = beam_of(bdir); break; case KING: // King h_attackable += h_dist(sq, o_king_sq); return total_pawns - pinned_pawns; break; case INVALID: // Ran off edge of board return total_pawns - pinned_pawns; break; default: // Shouldna happen, man! tbassert(false, "Not cool, man. Not cool.\n"); break; } } }
static void update_best_move_history(position_t *p, int index_of_best, sortable_move_t* lst, int count) { tbassert(ENABLE_TABLES, "Tables weren't enabled.\n"); int color_to_move = color_to_move_of(p); for (int i = 0; i < count; i++) { move_t mv = get_move(lst[i]); ptype_t pce = ptype_mv_of(mv); rot_t ro = rot_of(mv); // rotation square_t fs = from_square(mv); int ot = ORI_MASK & (ori_of(p->board[fs]) + ro); square_t ts = to_square(mv); int s = best_move_history[BMH(color_to_move, pce, ts, ot)]; if (index_of_best == i) { s = s + 11200; // number will never exceed 1017 } s = s * 0.90; // decay score over time tbassert(s < 102000, "s = %d\n", s); // or else sorting will fail best_move_history[BMH(color_to_move, pce, ts, ot)] = s; } }
// KFACE heuristic: bonus (or penalty) for King facing toward the other King ev_score_t kface(position_t *p, rnk_t r, fil_t f, full_board_t* board) { piece_t x = get_piece(p, r, f, board); color_t c = color_of(x); square_t opp_sq = board->pieces[opp_color(c)][0]; int delta_fil = fil_of(opp_sq) - f; int delta_rnk = rnk_of(opp_sq) - r; int bonus; switch (ori_of(x)) { case NN: bonus = delta_rnk; break; case EE: bonus = delta_fil; break; case SS: bonus = -delta_rnk; break; case WW: bonus = -delta_fil; break; default: bonus = 0; tbassert(false, "Illegal King orientation.\n"); } return (bonus * KFACE) / (abs(delta_rnk) + abs(delta_fil)); }
// Obtain a sorted move list. static int get_sortable_move_list(searchNode *node, sortable_move_t * move_list, int hash_table_move) { // number of moves in list int num_of_moves = generate_all_opt(&(node->position), move_list, false); color_t fake_color_to_move = color_to_move_of(&(node->position)); move_t killer_a = killer[KMT(node->ply, 0)]; move_t killer_b = killer[KMT(node->ply, 1)]; // sort special moves to the front for (int mv_index = 0; mv_index < num_of_moves; mv_index++) { move_t mv = get_move(move_list[mv_index]); if (mv == hash_table_move) { set_sort_key(&move_list[mv_index], SORT_MASK); } else if (mv == killer_a) { set_sort_key(&move_list[mv_index], SORT_MASK - 1); } else if (mv == killer_b) { set_sort_key(&move_list[mv_index], SORT_MASK - 2); } else { ptype_t pce = ptype_mv_of(mv); rot_t ro = rot_of(mv); // rotation square_t fs = from_square(mv); int ot = ORI_MASK & (ori_of(node->position.board[fs]) + ro); square_t ts = to_square(mv); set_sort_key(&move_list[mv_index], best_move_history[BMH(fake_color_to_move, pce, ts, ot)]); } } return num_of_moves; }
// KFACE heuristic: bonus (or penalty) for King facing toward the other King ev_score_t kface(position_t *p, fil_t f, rnk_t r) { square_t sq = square_of(f, r); piece_t x = p->board[sq]; color_t c = color_of(x); square_t opp_sq = p->kloc[opp_color(c)]; int delta_fil = fil_of(opp_sq) - f; int delta_rnk = rnk_of(opp_sq) - r; int bonus; switch (ori_of(x)) { case NN: bonus = delta_rnk; break; case EE: bonus = delta_fil; break; case SS: bonus = -delta_rnk; break; case WW: bonus = -delta_fil; break; default: bonus = 0; tbassert(false, "Illegal King orientation.\n"); } return (bonus * KFACE) / (abs(delta_rnk) + abs(delta_fil)); }
// Translate a position struct into a fen string // NOTE: When you use the test framework in search.c, you should modify this // function to match your optimized board representation in move_gen.c // // Input: (populated) position struct // empty string where FEN characters will be written // Output: null int pos_to_fen(position_t *p, char *fen) { int pos = 0; int i; for (rnk_t r = BOARD_WIDTH - 1; r >=0 ; --r) { int empty_in_a_row = 0; for (fil_t f = 0; f < BOARD_WIDTH; ++f) { piece_t piece = get_piece(p, r, f, &base_board); if (ptype_of(piece) == INVALID) { // invalid square tbassert(false, "Bad news, yo.\n"); // This is bad! } if (ptype_of(piece) == EMPTY) { // empty square empty_in_a_row++; continue; } else { if (empty_in_a_row) fen[pos++] = '0' + empty_in_a_row; empty_in_a_row = 0; int ori = ori_of(piece); // orientation color_t c = color_of(piece); if (ptype_of(piece) == KING) { for (i = 0; i < 2; i++) fen[pos++] = king_ori_to_rep[c][ori][i]; continue; } if (ptype_of(piece) == PAWN) { for (i = 0; i < 2; i++) fen[pos++] = pawn_ori_to_rep[c][ori][i]; continue; } } } // assert: for larger boards, we need more general solns tbassert(BOARD_WIDTH <= 10, "BOARD_WIDTH = %d\n", BOARD_WIDTH); if (empty_in_a_row == 10) { fen[pos++] = '1'; fen[pos++] = '0'; } else if (empty_in_a_row) { fen[pos++] = '0' + empty_in_a_row; } if (r) fen[pos++] = '/'; } fen[pos++] = ' '; fen[pos++] = 'W'; fen[pos++] = '\0'; return pos; }
// Obtain a sorted move list. static int get_sortable_move_list(searchNode *node, sortable_move_t * move_list, int hash_table_move) { // number of moves in list int num_of_moves = generate_all(&(node->position), move_list, false); color_t fake_color_to_move = color_to_move_of(&(node->position)); move_t killer_a = killer[KMT(node->ply, 0)]; move_t killer_b = killer[KMT(node->ply, 1)]; for (int mv_index = 0; mv_index < num_of_moves; mv_index++) { move_t mv = move_list[mv_index]; // don't use get_move. assumes generate_all doesn't bungle up high bits if (mv == hash_table_move) { set_sort_key(&move_list[mv_index], SORT_MASK); } else if (mv == killer_a) { set_sort_key(&move_list[mv_index], SORT_MASK - 1); } else if (mv == killer_b) { set_sort_key(&move_list[mv_index], SORT_MASK - 2); } else { ptype_t pce = ptype_mv_of(mv); rot_t ro = rot_of(mv); // rotation square_t fs = from_square(mv); int ot = ORI_MASK & (ori_of(node->position.board[fs]) + ro); square_t ts = to_square(mv); set_sort_key(&move_list[mv_index], best_move_history[BMH(fake_color_to_move, pce, ts, ot)]); } sortable_move_t insert = move_list[mv_index]; // TODO: enable this optimization for final since node counts change // if (insert > SORT_MASK) { int hole = mv_index; while (hole > 0 && insert > move_list[hole-1]) { move_list[hole] = move_list[hole-1]; hole--; } move_list[hole] = insert; // } } return num_of_moves; }