BitBully 0.0.78
A fast, perfect-play Connect-4 engine in modern C++
Loading...
Searching...
No Matches
main.cpp
Go to the documentation of this file.
1
17#include <chrono>
18#include <fstream>
19#include <iomanip> // For setting precision
20#include <iostream>
21#include <numeric>
22#include <sstream>
23#include <string>
24#include <thirdParty/connect4/Solver.hpp>
25#include <tuple>
26#include <unordered_map>
27#include <vector>
28
29#include "BitBully.h"
30#include "Board.h"
31
39#ifdef _WIN32
40using Clock = std::chrono::steady_clock;
41#else
42using Clock = std::chrono::high_resolution_clock;
43#endif
44
54void writeToCSV(const std::vector<std::tuple<float, float>>& data,
55 const std::string& filename) {
56 std::ofstream file(filename); // Open file for writing
57 if (!file.is_open()) {
58 std::cerr << "Error: Unable to open file " << filename << std::endl;
59 return;
60 }
61
62 // Write header
63 file << "Bitbully,Pons-C4\n";
64
65 // Write data
66 for (const auto& [val1, val2] : data) {
67 file << std::fixed << std::setprecision(5) // Control float precision
68 << val1 << "," << val2 << "\n";
69 }
70
71 file.close();
72 std::cout << "Data successfully written to " << filename << std::endl;
73}
74
84std::unordered_map<std::string, std::string> parseArgs(
85 const int argc, const char* const argv[]) {
86 std::unordered_map<std::string, std::string> args;
87
88 for (int i = 1; i < argc; i += 2) {
89 if (i + 1 < argc) {
90 args[argv[i]] = argv[i + 1];
91 } else {
92 std::cerr << "Error: Missing value for argument " << argv[i] << std::endl;
93 exit(EXIT_FAILURE);
94 }
95 }
96
97 return args;
98}
99
108int main(const int argc, const char* const argv[]) {
109 // Default values
110 int nPly = 8;
111 int nRepeats = 1000;
112 std::string filename;
113 int reset_tt = 0;
114
115 // Parse command-line arguments
116 auto args = parseArgs(argc, argv);
117 if (args.find("--nply") != args.end()) nPly = std::stoi(args["--nply"]);
118 if (args.find("--nrepeats") != args.end())
119 nRepeats = std::stoi(args["--nrepeats"]);
120 if (args.find("--filename") != args.end())
121 filename = args["--filename"];
122 else
123 filename = "../times_" + std::to_string(nPly) + "_ply_" +
124 std::to_string(nRepeats) + "_pos.csv";
125 if (args.find("--reset_tt") !=
126 args.end()) // reset transposition table every N moves
127 reset_tt = std::stoi(args["--reset_tt"]);
128
129 std::vector<std::tuple<float, float>> times = {};
130
131 using duration = std::chrono::duration<float>;
132
133 GameSolver::Connect4::Solver solverPonsC4;
135
136 for (auto i = 0; i < nRepeats; i++) {
137 auto [b, mvSequence] = BitBully::Board::randomBoard(nPly, true);
138
139 if (reset_tt > 0 && i % reset_tt == 0) {
140 solverPonsC4.reset();
142 }
143
144 // Bitbully:
145 auto tStart = Clock::now();
146 const int scoreBitbully = bb.mtdf(b, 0);
147 auto tEnd = Clock::now();
148 auto timeBitbully = static_cast<float>(duration(tEnd - tStart).count());
149
150 // Pons-C4:
152 // Convert move sequence into a string representation:
153 auto mvSequenceStr =
154 std::accumulate(mvSequence.begin(), mvSequence.end(), std::string(""),
155 [](const std::string& a, const int mv) {
156 return a + std::to_string(mv + 1);
157 });
158 if (P.play(mvSequenceStr) != b.countTokens()) {
159 std::cerr << "Error: (P.play(mvSequenceStr) != b.countTokens())";
160 exit(EXIT_FAILURE);
161 }
162 tStart = Clock::now();
163 const int scorePonsC4 = solverPonsC4.solve(P, false);
164 tEnd = Clock::now();
165 auto timePonsC4 = static_cast<float>(duration(tEnd - tStart).count());
166 times.emplace_back(timeBitbully, timePonsC4);
167
168 if (scorePonsC4 != scoreBitbully) {
169 std::cerr << "Error: " << b.toString() << "Pons-C4: " << scorePonsC4
170 << " BitBully: " << scoreBitbully << std::endl;
171 exit(EXIT_FAILURE);
172 }
173
174 if (i % (std::max(nRepeats, 100) / 100) == 0) {
175 std::cout << "Done with " << i << " iterations" << std::endl;
176 }
177 }
178 writeToCSV(times, filename);
179
180 std::cout << "Node Count Pons-C4: " << solverPonsC4.getNodeCount() << ", "
181 << "BitBully: " << bb.getNodeCounter() << " Percent: "
182 << static_cast<double>(bb.getNodeCounter() -
183 solverPonsC4.getNodeCount()) /
184 bb.getNodeCounter() * 100.0
185 << " %" << std::endl;
186 return 0;
187}
Connect-4 search engine that operates on BitBully::Board.
Bitboard-based representation of a Connect-4 position.
Perfect-play Connect-4 solver.
Definition BitBully.h:45
int mtdf(const Board &b, const int firstGuess, const int maxDepth=-1) noexcept
Solve a position using the MTD(f) driver.
Definition BitBully.h:188
void resetTranspositionTable()
Discard the contents of the transposition table.
Definition BitBully.h:242
auto getNodeCounter() const
Number of nodes visited since resetNodeCounter().
Definition BitBully.h:248
static std::pair< Board, std::vector< int > > randomBoard(const int nPly, const bool forbidDirectWin=true)
Generate a random reachable position with a fixed number of plies.
Definition Board.h:689
A class storing a Connect 4 position.
Definition Position.hpp:84
void play(position_t move)
Plays a possible move given by its bitmap representation.
Definition Position.hpp:113
void writeToCSV(const std::vector< std::tuple< float, float > > &data, const std::string &filename)
Persist per-position timing pairs to a CSV file.
Definition main.cpp:54
std::unordered_map< std::string, std::string > parseArgs(const int argc, const char *const argv[])
Parse --key value pairs from the program command line.
Definition main.cpp:84
std::chrono::high_resolution_clock Clock
Wall-clock used for the per-call timing measurements.
Definition main.cpp:42
int main(const int argc, const char *const argv[])
Entry point of the benchmark binary.
Definition main.cpp:108