void hdist_table_generate() { int size = 10; int size2 = 205; float hdist_table[size2][size2]; memset(&hdist_table, 0, sizeof(hdist_table)); for (int af = 0; af < size; af++) { for (int ar = 0; ar < size; ar++) { for (int bf = 0; bf < size; bf++) { for (int br = 0; br < size; br++) { square_t a = square_of(af, ar); square_t b = square_of(bf, br); int df = abs(af - bf); int dr = abs(ar - br); hdist_table[a][b] = (1.0 / (df + 1)) + (1.0 / (dr + 1)); printf("%d %d %d %d %.6f\n", a, b, df, dr, hdist_table[a][b]); } } } } FILE *f = fopen("hdist_table.h", "w"); fprintf(f, "static float hdist_table[%d][%d] = {\n", size2, size2); for (int a = 0; a < size2; a++) { fprintf(f, "{"); for (int b = 0; b < size2; b++) { fprintf(f, "%.6f,", hdist_table[a][b]); } fprintf(f, "},\n"); } fprintf(f, "};\n"); fclose(f); }
// 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)); }
// KAGGRESSIVE heuristic: bonus for King with more space to back ev_score_t kaggressive_old(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); tbassert(ptype_of(x) == KING, "ptype_of(x) = %d\n", ptype_of(x)); square_t opp_sq = p->kloc[opp_color(c)]; fil_t of = fil_of(opp_sq); rnk_t _or = (rnk_t) rnk_of(opp_sq); int delta_fil = of - f; int delta_rnk = _or - r; int bonus = 0; if (delta_fil >= 0 && delta_rnk >= 0) { bonus = (f + 1) * (r + 1); } else if (delta_fil <= 0 && delta_rnk >= 0) { bonus = (BOARD_WIDTH - f) * (r + 1); } else if (delta_fil <= 0 && delta_rnk <= 0) { bonus = (BOARD_WIDTH - f) * (BOARD_WIDTH - r); } else if (delta_fil >= 0 && delta_rnk <= 0) { bonus = (f + 1) * (BOARD_WIDTH - r); } return (KAGGRESSIVE * bonus) / (BOARD_WIDTH * BOARD_WIDTH); }
// returns 0 if no error static int get_sq_from_str(char *fen, int *c_count, int *sq) { char c, next_c; while ((c = fen[*c_count++]) != '\0') { // skip whitespace if ((c == ' ') || (c == '\t') || (c == '\n') || (c == '\r')) { continue; } else { break; // found nonwhite character } } if (c == '\0') { *sq = 0; return 0; } // get file and rank if ((c - 'a' < 0) || (c - 'a') > BOARD_WIDTH) { fen_error(fen, *c_count, "Illegal specification of last move"); return 1; } next_c = fen[*c_count++]; if (next_c == '\0') { fen_error(fen, *c_count, "FEN ended before last move fully specified"); if ((next_c - '0' < 0) || (next_c - '0') > BOARD_WIDTH) { fen_error(fen, *c_count, "Illegal specification of last move"); return 1; } *sq = square_of(next_c - '0', c - 'a'); return 0; } return 0; }
// H_SQUARES_ATTACKABLE heuristic: for shooting the enemy king int h_squares_attackable(position_t *p, color_t c) { char* laser_map; if (c == WHITE) { laser_map = laser_map_white; } else { laser_map = laser_map_black; } square_t o_king_sq = p->kloc[opp_color(c)]; tbassert(ptype_of(p->board[o_king_sq]) == KING, "ptype: %d\n", ptype_of(p->board[o_king_sq])); tbassert(color_of(p->board[o_king_sq]) != c, "color: %d\n", color_of(p->board[o_king_sq])); float h_attackable_temp = 0; for (fil_t f = 0; f < BOARD_WIDTH; f++) { for (rnk_t r = 0; r < BOARD_WIDTH; r++) { square_t sq = square_of(f, r); if (laser_map[sq] != 0) { h_attackable_temp += h_dist(sq, o_king_sq); } } } return h_attackable_temp; }
// Translate a fen string into a board position struct // int fen_to_pos(position_t *p, char *fen) { static position_t dmy1, dmy2; // these sentinels simplify checking previous // states without stepping past null pointers. dmy1.key = 0; dmy1.victims.stomped = 1; dmy1.victims.zapped = 1; dmy1.history = NULL; dmy2.key = 0; dmy2.victims.stomped = 1; dmy2.victims.zapped = 1; dmy2.history = &dmy1; p->key = 0; // hash key p->victims.stomped = 0; // piece destroyed by stomper p->victims.zapped = 0; // piece destroyed by shooter p->history = &dmy2; // history memset(&base_board, 0, sizeof(full_board_t)); if (fen[0] == '\0') { // Empty FEN => use starting position fen = "ss3nw5/3nw2nw3/2nw7/1nw6SE1/nw9/9SE/1nw6SE1/7SE2/3SE2SE3/5SE3NN W"; } int c_count = 0; // Invariant: fen[c_count] is next char to be read for (int i = 0; i < ARR_SIZE; ++i) { base_board.board[i] = INVALID_PIECE; } c_count = parse_fen_board(p, fen); if (!c_count) { return 1; // parse error of board } // King check int Kings[2] = {0, 0}; for (fil_t f = 0; f < BOARD_WIDTH; ++f) { for (rnk_t r = 0; r < BOARD_WIDTH; ++r) { square_t sq = square_of(r, f); piece_t x = base_board.board[sq]; ptype_t typ = ptype_of(x); if (typ == KING) { Kings[color_of(x)]++; } } } if (Kings[WHITE] == 0) { fen_error(fen, c_count, "No White Kings"); return 1; } else if (Kings[WHITE] > 1) { fen_error(fen, c_count, "Too many White Kings"); return 1; } else if (Kings[BLACK] == 0) { fen_error(fen, c_count, "No Black Kings"); return 1; } else if (Kings[BLACK] > 1) { fen_error(fen, c_count, "Too many Black Kings"); return 1; } char c; bool done = false; // Look for color to move and set ply accordingly while (!done && (c = fen[c_count++]) != '\0') { switch (c) { // ignore whitespace until the end case ' ': case '\t': case '\n': case '\r': break; // White to move case 'W': case 'w': p->ply = 0; base_board.ply = 0; done = true; break; // Black to move case 'B': case 'b': p->ply = 1; base_board.ply = 1; done = true; break; default: fen_error(fen, c_count, "Must specify White (W) or Black (B) to move"); return 1; break; } } // Look for last move, if it exists int lm_from_sq, lm_to_sq, lm_rot; if (get_sq_from_str(fen, &c_count, &lm_from_sq) != 0) { // parse error return 1; } if (lm_from_sq == 0) { // from-square of last move p->last_move = 0; // no last move specified p->key = compute_zob_key(p, &base_board); return 0; } c = fen[c_count]; switch (c) { case 'R': case 'r': lm_to_sq = lm_from_sq; lm_rot = RIGHT; break; case 'U': case 'u': lm_to_sq = lm_from_sq; lm_rot = UTURN; break; case 'L': case 'l': lm_to_sq = lm_from_sq; lm_rot = LEFT; break; default: // Not a rotation lm_rot = NONE; if (get_sq_from_str(fen, &c_count, &lm_to_sq) != 0) { return 1; } break; } p->last_move = move_of(EMPTY, lm_rot, lm_from_sq, lm_to_sq); p->key = compute_zob_key(p, &base_board); return 0; // everything is okay }
// parse_fen_board // Input: board representation as a fen string // unpopulated board position struct // Output: index of where board description ends or 0 if parsing error // (populated) board position struct static int parse_fen_board(position_t *p, char *fen) { // Invariant: square (f, r) is last square filled. // Fill from last rank to first rank, from first file to last file fil_t f = -1; rnk_t r = BOARD_WIDTH - 1; // Current and next characters from input FEN description char c, next_c; // Invariant: fen[c_count] is next character to be read int c_count = 0; // Loop also breaks internally if (f, r) == (BOARD_WIDTH-1, 0) while ((c = fen[c_count++]) != '\0') { int ori; ptype_t typ; switch (c) { // ignore whitespace until the end case ' ': case '\t': case '\n': case '\r': if ((f == BOARD_WIDTH - 1) && (r == 0)) { // our job is done return c_count; } break; // digits case '1': if (fen[c_count] == '0') { c_count++; c += 9; } case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': while (c > '0') { if (++f >= BOARD_WIDTH) { fen_error(fen, c_count, "Too many squares in rank.\n"); return 0; } base_board.board[square_of(r, f)] = EMPTY_PIECE; c--; } break; // pieces case 'N': if (++f >= BOARD_WIDTH) { fen_error(fen, c_count, "Too many squares in rank"); return 0; } next_c = fen[c_count++]; if (next_c == 'N') { // White King facing North ori = NN; typ = KING; } else if (next_c == 'W') { // White Pawn facing NW ori = NW; typ = PAWN; } else if (next_c == 'E') { // White Pawn facing NE ori = NE; typ = PAWN; } else { fen_error(fen, c_count+1, "Syntax error"); return 0; } set_piece(p, r, f, typ, ori, WHITE, &base_board); break; case 'n': if (++f >= BOARD_WIDTH) { fen_error(fen, c_count, "Too many squares in rank"); return 0; } next_c = fen[c_count++]; if (next_c == 'n') { // Black King facing North ori = NN; typ = KING; } else if (next_c == 'w') { // Black Pawn facing NW ori = NW; typ = PAWN; } else if (next_c == 'e') { // Black Pawn facing NE ori = NE; typ = PAWN; } else { fen_error(fen, c_count+1, "Syntax error"); return 0; } set_piece(p, r, f, typ, ori, BLACK, &base_board); break; case 'S': if (++f >= BOARD_WIDTH) { fen_error(fen, c_count, "Too many squares in rank"); return 0; } next_c = fen[c_count++]; if (next_c == 'S') { // White King facing SOUTH ori = SS; typ = KING; } else if (next_c == 'W') { // White Pawn facing SW ori = SW; typ = PAWN; } else if (next_c == 'E') { // White Pawn facing SE ori = SE; typ = PAWN; } else { fen_error(fen, c_count+1, "Syntax error"); return 0; } set_piece(p, r, f, typ, ori, WHITE, &base_board); break; case 's': if (++f >= BOARD_WIDTH) { fen_error(fen, c_count, "Too many squares in rank"); return 0; } next_c = fen[c_count++]; if (next_c == 's') { // Black King facing South ori = SS; typ = KING; } else if (next_c == 'w') { // Black Pawn facing SW ori = SW; typ = PAWN; } else if (next_c == 'e') { // Black Pawn facing SE ori = SE; typ = PAWN; } else { fen_error(fen, c_count+1, "Syntax error"); return 0; } set_piece(p, r, f, typ, ori, BLACK, &base_board); break; case 'E': if (++f >= BOARD_WIDTH) { fen_error(fen, c_count, "Too many squares in rank"); return 0; } next_c = fen[c_count++]; if (next_c == 'E') { // White King facing East set_piece(p, r, f, KING, EE, WHITE, &base_board); } else { fen_error(fen, c_count+1, "Syntax error"); return 0; } break; case 'W': if (++f >= BOARD_WIDTH) { fen_error(fen, c_count, "Too many squares in rank"); return 0; } next_c = fen[c_count++]; if (next_c == 'W') { // White King facing West set_piece(p, r, f, KING, WW, WHITE, &base_board); } else { fen_error(fen, c_count+1, "Syntax error"); return 0; } break; case 'e': if (++f >= BOARD_WIDTH) { fen_error(fen, c_count, "Too many squares in rank"); return 0; } next_c = fen[c_count++]; if (next_c == 'e') { // Black King facing East set_piece(p, r, f, KING, EE, BLACK, &base_board); } else { fen_error(fen, c_count+1, "Syntax error"); return 0; } break; case 'w': if (++f >= BOARD_WIDTH) { fen_error(fen, c_count, "Too many squares in rank"); return 0; } next_c = fen[c_count++]; if (next_c == 'w') { // Black King facing West set_piece(p, r, f, KING, WW, BLACK, &base_board); } else { fen_error(fen, c_count+1, "Syntax error"); return 0; } break; // end of rank case '/': if (f == BOARD_WIDTH - 1) { f = -1; if (--r < 0) { fen_error(fen, c_count, "Too many ranks"); return 0; } } else { fen_error(fen, c_count, "Too few squares in rank"); return 0; } break; default: fen_error(fen, c_count, "Syntax error"); return 0; break; } // end switch } // end while if ((f == BOARD_WIDTH - 1) && (r == 0)) { return c_count; } else { fen_error(fen, c_count, "Too few squares specified"); return 0; } }
// Static evaluation. Returns score score_t eval(position_t *p, bool verbose) { // seed rand_r with a value of 1, as per // http://linux.die.net/man/3/rand_r static __thread unsigned int seed = 1; // verbose = true: print out components of score ev_score_t score[2] = { 0, 0 }; // int corner[2][2] = { {INF, INF}, {INF, INF} }; ev_score_t bonus; //char buf[MAX_CHARS_IN_MOVE]; color_t c; for (fil_t f = 0; f < BOARD_WIDTH; f++) { for (rnk_t r = 0; r < BOARD_WIDTH; r++) { square_t sq = square_of(f, r); piece_t x = p->board[sq]; //if (verbose) { // square_to_str(sq, buf, MAX_CHARS_IN_MOVE); //} switch (ptype_of(x)) { case EMPTY: break; case PAWN: c = color_of(x); // MATERIAL heuristic: Bonus for each Pawn bonus = PAWN_EV_VALUE; // if (verbose) { // printf("MATERIAL bonus %d for %s Pawn on %s\n", bonus, color_to_str(c), buf); // } score[c] += bonus; // PBETWEEN heuristic bonus = pbetween(p, f, r); // if (verbose) { // printf("PBETWEEN bonus %d for %s Pawn on %s\n", bonus, color_to_str(c), buf); // } score[c] += bonus; // PCENTRAL heuristic bonus = pcentral(f, r); // if (verbose) { // printf("PCENTRAL bonus %d for %s Pawn on %s\n", bonus, color_to_str(c), buf); // } score[c] += bonus; break; case KING: c = color_of(x); // KFACE heuristic bonus = kface(p, f, r); // if (verbose) { // printf("KFACE bonus %d for %s King on %s\n", bonus, // color_to_str(c), buf); // } score[c] += bonus; // KAGGRESSIVE heuristic color_t othercolor = opp_color(c); square_t otherking = p->kloc[othercolor]; fil_t otherf = fil_of(otherking); rnk_t otherr = rnk_of(otherking); bonus = kaggressive(f, r, otherf, otherr); assert(bonus == kaggressive_old(p, f, r)); // if (verbose) { // printf("KAGGRESSIVE bonus %d for %s King on %s\n", bonus, color_to_str(c), buf); // } score[c] += bonus; break; case INVALID: break; default: tbassert(false, "Jose says: no way!\n"); // No way, Jose! } laser_map_black[sq] = 0; laser_map_white[sq] = 0; } } int black_pawns_unpinned = mark_laser_path(p, laser_map_white, WHITE, 1); // 1 = path of laser with no moves ev_score_t w_hattackable = HATTACK * (int) h_attackable; score[WHITE] += w_hattackable; // if (verbose) { // printf("HATTACK bonus %d for White\n", w_hattackable); // } // PAWNPIN Heuristic --- is a pawn immobilized by the enemy laser. int b_pawnpin = PAWNPIN * black_pawns_unpinned; score[BLACK] += b_pawnpin; int b_mobility = MOBILITY * mobility(p, BLACK); score[BLACK] += b_mobility; // if (verbose) { // printf("MOBILITY bonus %d for Black\n", b_mobility); // } int white_pawns_unpinned = mark_laser_path(p, laser_map_black, BLACK, 1); // 1 = path of laser with no moves ev_score_t b_hattackable = HATTACK * (int) h_attackable; score[BLACK] += b_hattackable; // if (verbose) { // printf("HATTACK bonus %d for Black\n", b_hattackable); // } int w_mobility = MOBILITY * mobility(p, WHITE); score[WHITE] += w_mobility; // if (verbose) { // printf("MOBILITY bonus %d for White\n", w_mobility); // } int w_pawnpin = PAWNPIN * white_pawns_unpinned; score[WHITE] += w_pawnpin; // score from WHITE point of view ev_score_t tot = score[WHITE] - score[BLACK]; if (RANDOMIZE) { ev_score_t z = rand_r(&seed) % (RANDOMIZE*2+1); tot = tot + z - RANDOMIZE; } if (color_to_move_of(p) == BLACK) { tot = -tot; } return tot / EV_SCORE_RATIO; }