extern "C" uint8_t* fastgo_extract_features(uint8_t* raw_board, int* output_length, int perspective_player) { assert(perspective_player == 1 or perspective_player == 2); // Copy all of the moves into a board. GoBoard board; for (int y = 0; y < BOARD_SIZE; y++) { for (int x = 0; x < BOARD_SIZE; x++) { // uint8_t piece = piece_at(*reinterpret_cast<std::array<Cell, BOARD_SIZE * BOARD_SIZE>*>(raw_board), {x, y}); uint8_t piece = raw_board[x + y * BOARD_SIZE]; assert(piece == 0 or piece == 1 or piece == 2); if (piece == 1 or piece == 2) board.place_stone((Player)piece, {x, y}); } } // Get features out. string main_feature_block; filtering_ostream features_out(std::back_inserter(main_feature_block)); write_features(features_out, board, (Player)perspective_player); features_out.flush(); // Copy into a C-style array for returning. uint8_t* result = new uint8_t[main_feature_block.size()]; std::copy(main_feature_block.begin(), main_feature_block.end(), result); *output_length = main_feature_block.size(); return result; }
void write_all_samples( filtering_ostream& features_out, filtering_ostream& targets_out, filtering_ostream& winners_out, filtering_ostream& territory_out, string path ) { #else void write_all_samples(filtering_ostream& features_out, filtering_ostream& targets_out, filtering_ostream& winners_out, string path) { #endif // Read in the SGF file. Game game; if (not parse_sgf(path, game)) return; // If both players are too low rank then skip. if (game.white_rank < RANK_THRESHOLD and game.black_rank < RANK_THRESHOLD) { // cerr << "Both players too low rank: " << game.white_rank << " " << game.black_rank << endl; return; } // If just one player is too low rank then print a warning. // if (game.white_rank < RANK_THRESHOLD) // cerr << "White too low rank: " << game.white_rank << " " << game.black_rank << endl; // if (game.black_rank < RANK_THRESHOLD) // cerr << "Black too low rank: " << game.white_rank << " " << game.black_rank << endl; #ifdef SCORING string final_territory_black = slurp_file(path + "-FINAL"); assert(final_territory_black.size() == 361); string final_territory_white = final_territory_black; std::replace(final_territory_white.begin(), final_territory_white.end(), '\1', 'x'); std::replace(final_territory_white.begin(), final_territory_white.end(), '\xff', '\1'); std::replace(final_territory_white.begin(), final_territory_white.end(), 'x', '\xff'); #endif // Keep move histories in a pair of circular buffers. vector<vector<std::array<uint8_t, BOARD_SIZE * BOARD_SIZE>>> histories{{}, {}}; vector<int> history_rotations{0, 0}; std::array<uint8_t, BOARD_SIZE * BOARD_SIZE> blank_board{}; for (int which_history = 0; which_history < 2; which_history++) { for (int i = 0; i < HISTORY_LENGTH; i++) { histories[which_history].push_back(blank_board); histories[which_history].push_back(blank_board); } } GoBoard board; std::array<uint8_t, BOARD_SIZE * BOARD_SIZE> one_hot_winning_move = {}; std::uniform_int_distribution<int> uni(0, game.moves.size() - 1); int random_integer1 = uni(rng); int random_integer2 = uni(rng); for (int move_index = 0; move_index < game.moves.size(); move_index++) { Move& m = game.moves[move_index]; bool do_write_this_move = true; #ifdef SELECT_JUST_ONE_MOVE do_write_this_move = (move_index == random_integer1) or (move_index == random_integer2); #endif #ifdef SELF_PLAY do_write_this_move = (move_index > 0) and game.moves[move_index - 1].is_random_self_play_move; #endif // Disable writing this move if the player is too low rank. if (m.who_moved == Player::BLACK and game.black_rank < RANK_THRESHOLD) do_write_this_move = false; if (m.who_moved == Player::WHITE and game.white_rank < RANK_THRESHOLD) do_write_this_move = false; // Currently we generate no samples on a pass. if (m.pass) continue; // Get out features for the board right BEFORE the move. if (do_write_this_move) write_features(features_out, board, m.who_moved); // If the current perspective player is WHITE then we grab the histories swapped. int who_moved_as_index = m.who_moved == Player::BLACK ? 0 : 1; // Order the histories to have the perspective player first. for (int which_history = 0; which_history < 2; which_history++) { auto& history = histories[(which_history + who_moved_as_index) % 2]; int history_rotation = history_rotations[(which_history + who_moved_as_index) % 2]; for (int i = 0; i < HISTORY_LENGTH; i++) { std::array<uint8_t, BOARD_SIZE * BOARD_SIZE>& entry = history[(i + history_rotation) % HISTORY_LENGTH]; if (do_write_this_move) features_out.write(reinterpret_cast<const char*>(&entry[0]), BOARD_SIZE * BOARD_SIZE); } } // Write the winning move out. Cell& winning_move_cell = piece_at(one_hot_winning_move, m.xy); winning_move_cell = 1; if (do_write_this_move) targets_out.write(reinterpret_cast<const char*>(&one_hot_winning_move[0]), BOARD_SIZE * BOARD_SIZE); winning_move_cell = 0; // Write the winner of the game out. int our_komi = std::round(game.komi * 2); if (m.who_moved == Player::BLACK) our_komi = -our_komi; char game_winner[] = {0, 0, our_komi}; if (game.who_won == m.who_moved) //Player::BLACK) game_winner[0] = 1; if (game.who_won == opponent_of(m.who_moved))//Player::WHITE) game_winner[1] = 1; if (do_write_this_move) winners_out.write(game_winner, 2); #ifdef SCORING // Write a copy of this game's final territory out. // territory_out.write(final_territory, final_territory.size()); if (m.who_moved == Player::BLACK) { territory_out << final_territory_black; } else if (m.who_moved == Player::WHITE) { territory_out << final_territory_white; } else { assert(false); } #endif // Update the board. board.place_stone(m.who_moved, m.xy); // Insert into the history. int& history_rotation = history_rotations[who_moved_as_index]; std::array<uint8_t, 361>& this_history_entry = histories[who_moved_as_index][history_rotation]; this_history_entry = blank_board; piece_at(this_history_entry, m.xy) = 1; // Increment the rotation of the appropriate history entry. history_rotation = (history_rotation + 1) % HISTORY_LENGTH; } }