68 size_t moves_played = 0;
70 TicTacToeState() : board(size_t(9), 0)
86 using State = TicTacToeState;
88 using State_Key = std::uint64_t;
93 return winner(state) != 0
or state.moves_played == 9;
97 Score
evaluate(
const State &state)
const
101 return win == state.player ? 100 : -100;
103 if (state.moves_played == 9)
106 return heuristic(state, state.player) - heuristic(state, -state.player);
110 [[
nodiscard]] State_Key state_key(
const State &state)
const noexcept
112 State_Key key =
static_cast<State_Key
>(state.player > 0 ? 1 : 2);
113 for (
size_t i = 0; i < state.board.size(); ++i)
116 if (state.board[i] == 1)
118 else if (state.board[i] == -1)
126 void apply(State &state,
const Move &move)
const
128 state.board[move.cell] = state.player;
129 state.player = -state.player;
130 ++state.moves_played;
134 void undo(State &state,
const Move &move)
const
136 --state.moves_played;
137 state.player = -state.player;
138 state.board[move.cell] = 0;
142 template <
typename Visitor>
143 bool for_each_successor(
const State &state,
Visitor visit)
const
145 static constexpr size_t order[] = {4, 0, 2, 6, 8, 1, 3, 5, 7};
147 for (
const size_t cell : order)
157 static constexpr size_t lines[8][3] = {
158 {0, 1, 2}, {3, 4, 5}, {6, 7, 8},
159 {0, 3, 6}, {1, 4, 7}, {2, 5, 8},
163 for (
const auto &line :
lines)
165 const int a = state.board[line[0]];
166 if (a != 0
and a == state.board[line[1]]
and a == state.board[line[2]])
173 [[
nodiscard]]
static Score heuristic(
const State &state,
const int player)
noexcept
175 static constexpr size_t lines[8][3] = {
176 {0, 1, 2}, {3, 4, 5}, {6, 7, 8},
177 {0, 3, 6}, {1, 4, 7}, {2, 5, 8},
182 for (
const auto &line :
lines)
186 bool blocked =
false;
188 for (
const size_t cell : line)
189 if (state.board[cell] == player)
191 else if (state.board[cell] == 0)
204 else if (
mine == 1
and empty == 2)
214 for (
size_t row = 0; row < 3; ++row)
216 for (
size_t col = 0; col < 3; ++col)
218 const int cell = state.board[row*3 + col];
219 std::cout << (cell == 1 ?
'X' : cell == -1 ?
'O' :
'.');
231 TicTacToeDomain::Move,
232 TicTacToeDomain::Score>;
234 TicTacToeDomain::Score>;
236 auto negamax = Search::negamax_search(TicTacToeDomain{},
root);
240 auto alpha_beta = Search::alpha_beta_search(TicTacToeDomain{},
root);
245 ordered_policy.move_ordering = Search::MoveOrderingMode::Estimated_Score;
259 auto iterative = Search::iterative_deepening_alpha_beta_search(TicTacToeDomain{},
267 std::cout <<
"Tic-Tac-Toe reference example\n";
268 std::cout <<
"Root position:\n";
272 std::cout <<
"Negamax root value: " <<
negamax.value <<
'\n';
273 std::cout <<
"Negamax best move: " <<
negamax.first_move().cell <<
'\n';
274 std::cout <<
"Negamax visited states: " <<
negamax.stats.visited_states <<
'\n';
275 std::cout <<
"Negamax + TT visited states: "
277 std::cout <<
"Negamax + TT hits: "
278 <<
negamax_tt.stats.transpositions.hits <<
'\n';
279 std::cout <<
"Negamax + TT cutoffs: "
280 <<
negamax_tt.stats.transpositions.cutoffs <<
'\n';
283 std::cout <<
"Alpha-Beta root value: " <<
alpha_beta.value <<
'\n';
284 std::cout <<
"Alpha-Beta best move: " <<
alpha_beta.first_move().cell <<
'\n';
285 std::cout <<
"Alpha-Beta visited states: " <<
alpha_beta.stats.visited_states <<
'\n';
286 std::cout <<
"Alpha-Beta cutoffs: " <<
alpha_beta.stats.alpha_beta_cutoffs <<
'\n';
287 std::cout <<
"Alpha-Beta + TT visited states: "
289 std::cout <<
"Alpha-Beta + TT hits: "
291 std::cout <<
"Alpha-Beta + TT cutoffs: "
295 std::cout <<
"Negamax + TT table stores: "
297 std::cout <<
"Alpha-Beta + TT table stores: "
299 std::cout <<
"Iterative Alpha-Beta best move: "
300 <<
iterative.result.first_move().cell <<
'\n';
301 std::cout <<
"Iterative Alpha-Beta iterations: "
302 <<
iterative.completed_iterations <<
'\n';
303 std::cout <<
"Iterative Alpha-Beta aspiration retries: "
304 <<
iterative.aspiration_researches <<
'\n';
305 std::cout <<
"Iterative Alpha-Beta visited states: "
306 <<
iterative.total_stats.visited_states <<
'\n';
307 std::cout <<
"Iterative Alpha-Beta TT stores: "
309 std::cout <<
"Trace events captured: " <<
trace.size() <<
'\n';
Umbrella header for the implicit state-space search framework.
Collector that stores trace events in an Aleph list.
static ExplorationPolicy default_policy() noexcept
Return the default exploration policy for Alpha-Beta.
Simple dynamic array with automatic resizing and functional operations.
Transposition-table container built on top of Aleph hash maps.
RAII helper for function entry/exit tracing.
__gmp_expr< T, __gmp_binary_expr< __gmp_expr< T, U >, unsigned long int, __gmp_root_function > > root(const __gmp_expr< T, U > &expr, unsigned long int l)
User-facing facade exported by the umbrella search header.
and
Check uniqueness with explicit hash + equality functors.
bool operator==(const DynList< T > &l1, const DynList< T > &l2)
Equality operator for DynList.
Divide_Conquer_DP_Result< Cost > divide_and_conquer_partition_dp(const size_t groups, const size_t n, Transition_Cost_Fn transition_cost, const Cost inf=dp_optimization_detail::default_inf< Cost >())
Optimize partition DP using divide-and-conquer optimization.
Controls for adversarial iterative deepening.
size_t initial_depth
First horizon to search.
Exploration controls shared across engines.
Hard bounds applied by the search engine.
size_t max_depth
Maximum expansion depth.
Dynamic array container with automatic resizing.