Skip to content

Commit 795fa99

Browse files
authored
Fen-Generator (#202)
Bench: 1883847 Add fen-generation code for network training, use `make generator` to compile.
1 parent 484fcef commit 795fa99

File tree

15 files changed

+509
-76
lines changed

15 files changed

+509
-76
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
11
.vscode
22
*.exe
3-
src/Bit-Genie
3+
src/Bit-Genie
4+
src/generated_*.txt

src/fen-gen/cmdline.h

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/*
2+
Bit-Genie is an open-source, UCI-compliant chess engine written by
3+
Aryan Parekh - https://github.com/Aryan1508/Bit-Genie
4+
5+
Bit-Genie is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
Bit-Genie is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
#pragma once
19+
#include "../stringparse.h"
20+
21+
class CommandLineParser {
22+
public:
23+
CommandLineParser(int argc, char **argv) {
24+
options = std::vector<std::string>(argv, argv + argc);
25+
}
26+
27+
std::string get_option(std::string_view key) const {
28+
for (std::size_t i = 0; i < options.size(); i++) {
29+
if (options[i] == key)
30+
return options[i + 1];
31+
}
32+
33+
return "";
34+
}
35+
36+
uint64_t get_option(std::string_view key, uint64_t default_value) const {
37+
std::string value = get_option(key);
38+
return value.size() ? std::stoull(value) : default_value;
39+
}
40+
private:
41+
std::vector<std::string> options;
42+
};

src/fen-gen/game.cpp

Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
Bit-Genie is an open-source, UCI-compliant chess engine written by
3+
Aryan Parekh - https://github.com/Aryan1508/Bit-Genie
4+
5+
Bit-Genie is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
Bit-Genie is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
#include "game.h"
19+
20+
#define DRAW_STRING ("[0.5]")
21+
#define WHITE_WIN_STRING ("[1.0]")
22+
#define BLACK_WIN_STRING ("[0.0]")
23+
24+
Game::Game(std::ofstream &output_file, std::mt19937 &rng)
25+
: output_file(output_file), rng(rng) {}
26+
27+
bool Game::filter_position() {
28+
return (qsearch(search, MIN_EVAL, MAX_EVAL) == search.position.static_evaluation()) && !search.position.king_in_check();
29+
}
30+
31+
bool Game::make_book_move() {
32+
if (is_mated())
33+
return false;
34+
35+
Movelist movelist;
36+
search.position.generate_legal(movelist);
37+
38+
std::uniform_int_distribution distribution(0, static_cast<int>(movelist.size() - 1));
39+
int rand_index = distribution(rng);
40+
41+
search.position.apply_move(movelist[rand_index]);
42+
ply++;
43+
return true;
44+
}
45+
46+
void Game::save_position(int search_score) {
47+
saved_positions.push_back({ search.position.get_fen(), search_score });
48+
}
49+
50+
bool Game::is_mated() const {
51+
Movelist movelist;
52+
search.position.generate_legal(movelist);
53+
return movelist.size() == 0;
54+
}
55+
56+
void Game::new_game() {
57+
ply = 0;
58+
search.position.set_fen("rnbqkbnr/pppppppp/8/8/8/8/PPPPPPPP/RNBQKBNR w KQkq - 0 1");
59+
saved_positions.clear();
60+
search.reset();
61+
}
62+
63+
void Game::save_game(std::string const &result) {
64+
for (auto const &saved_position : saved_positions) {
65+
std::string line = saved_position.first + " " + result + " " + std::to_string(saved_position.second);
66+
output_file << line << '\n';
67+
}
68+
}
69+
70+
SearchResult Game::search_position() {
71+
return ::search_position(search, false);
72+
}
73+
74+
void Game::run() {
75+
std::string wdl_string;
76+
int draw_score_counter = 0, win_score_counter = 0;
77+
78+
while (true) {
79+
/* TODO: command line arg */
80+
if (ply < 8) {
81+
if (!make_book_move())
82+
break;
83+
continue;
84+
}
85+
86+
if (search.position.drawn()) {
87+
wdl_string = DRAW_STRING;
88+
break;
89+
}
90+
91+
if (is_mated()) {
92+
if (search.position.king_in_check())
93+
wdl_string = search.position.get_side() == CLR_WHITE ? BLACK_WIN_STRING : WHITE_WIN_STRING;
94+
else
95+
wdl_string = DRAW_STRING;
96+
break;
97+
}
98+
99+
auto result = search_position();
100+
int white_relative_score = search.position.get_side() == CLR_WHITE ? result.score : -result.score;
101+
102+
// TODO command line arg
103+
if (result.score >= 1000 && ply == 8)
104+
return;
105+
106+
if (filter_position())
107+
save_position(white_relative_score);
108+
109+
// TODO command line arg
110+
bool is_draw_score = std::abs(result.score) <= 20;
111+
bool is_win_score = std::abs(result.score) >= 1000;
112+
113+
draw_score_counter = is_draw_score ? draw_score_counter + 1 : 0;
114+
win_score_counter = is_win_score ? win_score_counter + 1 : 0;
115+
116+
// TODO command line arg
117+
if (draw_score_counter >= 12) {
118+
wdl_string = DRAW_STRING;
119+
break;
120+
}
121+
122+
if (win_score_counter >= 4) {
123+
wdl_string = white_relative_score > 0 ? WHITE_WIN_STRING : BLACK_WIN_STRING;
124+
break;
125+
}
126+
127+
search.position.apply_move(result.best_move);
128+
search.reset_counters();
129+
ply++;
130+
}
131+
save_game(wdl_string);
132+
}

src/fen-gen/game.h

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
/*
2+
Bit-Genie is an open-source, UCI-compliant chess engine written by
3+
Aryan Parekh - https://github.com/Aryan1508/Bit-Genie
4+
5+
Bit-Genie is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
Bit-Genie is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
#pragma once
19+
#include "../search.h"
20+
#include "../position.h"
21+
22+
#include <vector>
23+
#include <random>
24+
#include <memory>
25+
#include <fstream>
26+
#include <utility>
27+
#include <string_view>
28+
29+
class Game {
30+
public:
31+
Game(std::ofstream &output_file, std::mt19937 &rng);
32+
33+
bool filter_position();
34+
35+
bool make_book_move();
36+
37+
void save_position(int search_score);
38+
39+
bool is_mated() const;
40+
41+
void save_game(std::string const &result);
42+
43+
void new_game();
44+
45+
SearchResult search_position();
46+
47+
void run();
48+
49+
std::size_t total_fens_written() const {
50+
return saved_positions.size();
51+
}
52+
53+
private:
54+
int ply = 0;
55+
SearchInfo search;
56+
std::ofstream &output_file;
57+
std::mt19937 &rng;
58+
std::vector<std::pair<std::string, int>> saved_positions;
59+
};

src/fen-gen/generator.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
/*
2+
Bit-Genie is an open-source, UCI-compliant chess engine written by
3+
Aryan Parekh - https://github.com/Aryan1508/Bit-Genie
4+
5+
Bit-Genie is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
Bit-Genie is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
#include "generator.h"
19+
20+
#include <chrono>
21+
#include <time.h>
22+
#include <iomanip>
23+
#include <sstream>
24+
25+
std::string get_current_date_time() {
26+
auto now = std::chrono::system_clock::now();
27+
auto in_time_t = std::chrono::system_clock::to_time_t(now);
28+
29+
std::stringstream ss;
30+
ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %X");
31+
return ss.str();
32+
}
33+
34+
void GamePool::run_batch(std::string_view output_file_path, std::uint64_t target_fens, std::uint64_t seed) {
35+
std::mt19937 rng(seed);
36+
std::ofstream output_file(output_file_path.data());
37+
38+
if (!output_file) {
39+
std::cerr << "Couldn't open " << output_file_path << std::endl;
40+
std::terminate();
41+
}
42+
43+
Game game(output_file, rng);
44+
45+
for (std::uint64_t n_batch_fens = 0; n_batch_fens <= target_fens;) {
46+
game.run();
47+
n_games++;
48+
49+
n_batch_fens += game.total_fens_written();
50+
n_fens += game.total_fens_written();
51+
game.new_game();
52+
}
53+
output_file.close();
54+
}
55+
56+
void GamePool::run(std::uint64_t target_fens) {
57+
std::random_device rd;
58+
59+
std::uint64_t batch_size = target_fens / FEN_GENERATOR_THREADS;
60+
for (int i = 0; i < FEN_GENERATOR_THREADS; i++) {
61+
std::string output_path = "generated_" + std::to_string(i) + ".txt";
62+
workers.emplace_back(&GamePool::run_batch, this, output_path, batch_size, rd());
63+
}
64+
65+
std::cout << "\n{" << get_current_date_time() << "}: started generation\n";
66+
67+
std::uint64_t n_previous_fens = 0;
68+
while (n_fens <= (batch_size * FEN_GENERATOR_THREADS)) {
69+
std::this_thread::sleep_for(std::chrono::seconds(1));
70+
71+
std::uint64_t n_batch_fens = n_fens - n_previous_fens;
72+
std::cout << "\r{" << get_current_date_time() << "}: generating... [total=" << n_fens << ", speed=" << n_batch_fens << "]" << std::flush;
73+
n_previous_fens = n_fens;
74+
}
75+
std::cout << '\n';
76+
std::cout << "{" << get_current_date_time() << "}: finished generation" << std::endl;
77+
78+
for (auto &worker : workers)
79+
worker.join();
80+
}

src/fen-gen/generator.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
Bit-Genie is an open-source, UCI-compliant chess engine written by
3+
Aryan Parekh - https://github.com/Aryan1508/Bit-Genie
4+
5+
Bit-Genie is free software: you can redistribute it and/or modify
6+
it under the terms of the GNU General Public License as published by
7+
the Free Software Foundation, either version 3 of the License, or
8+
(at your option) any later version.
9+
10+
Bit-Genie is distributed in the hope that it will be useful,
11+
but WITHOUT ANY WARRANTY; without even the implied warranty of
12+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13+
GNU General Public License for more details.
14+
15+
You should have received a copy of the GNU General Public License
16+
along with this program. If not, see <http://www.gnu.org/licenses/>.
17+
*/
18+
#pragma once
19+
#include "game.h"
20+
#include "cmdline.h"
21+
22+
#include <thread>
23+
#include <atomic>
24+
25+
inline int FEN_GENERATOR_THREADS = 1;
26+
27+
class GamePool {
28+
public:
29+
GamePool() = default;
30+
31+
void run(std::uint64_t target_fens);
32+
33+
void run_batch(std::string_view output_file, std::uint64_t target_fens, std::uint64_t seed);
34+
35+
private:
36+
std::vector<std::thread> workers;
37+
std::atomic_uint64_t n_games = { 0 };
38+
std::atomic_uint64_t n_fens = { 0 };
39+
};

0 commit comments

Comments
 (0)