55 size_t moves_played = 0;
58 : board(size_t(16), 0), heights(size_t(4), size_t(0))
74 using State = Connect3State;
76 using Move_Key = size_t;
78 static constexpr size_t Rows = 4;
79 static constexpr size_t Cols = 4;
80 static constexpr size_t Align = 3;
84 return winner(state) != 0
or state.moves_played == Rows*Cols;
87 Score
evaluate(
const State &state)
const
91 return win == state.player ? 1000 : -1000;
93 if (state.moves_played == Rows*Cols)
96 return heuristic(state, state.player) - heuristic(state, -state.player);
104 void apply(State &state,
const Move &move)
const
106 const size_t row = state.heights[move.column];
107 state.board[index(row, move.column)] = state.player;
108 ++state.heights[move.column];
109 state.player = -state.player;
110 ++state.moves_played;
113 void undo(State &state,
const Move &move)
const
115 --state.moves_played;
116 state.player = -state.player;
117 --state.heights[move.column];
118 const size_t row = state.heights[move.column];
119 state.board[index(row, move.column)] = 0;
122 template <
typename Visitor>
123 bool for_each_successor(
const State &state,
Visitor visit)
const
125 static constexpr size_t order[] = {0, 3, 1, 2};
127 for (
const size_t col : order)
135 [[
nodiscard]]
static constexpr size_t index(
const size_t row,
136 const size_t col)
noexcept
138 return row*Cols + col;
143 for (
size_t row = 0; row < Rows; ++row)
144 for (
size_t col = 0; col < Cols; ++col)
146 const int cell = state.board[index(row, col)];
150 if (col + Align <= Cols)
153 for (
size_t k = 1;
k < Align; ++
k)
154 ok =
ok and state.board[index(row, col +
k)] == cell;
159 if (row + Align <= Rows)
162 for (
size_t k = 1;
k < Align; ++
k)
163 ok =
ok and state.board[index(row +
k, col)] == cell;
168 if (row + Align <= Rows
and col + Align <= Cols)
171 for (
size_t k = 1;
k < Align; ++
k)
172 ok =
ok and state.board[index(row +
k, col +
k)] == cell;
180 for (
size_t k = 1;
k < Align; ++
k)
181 ok =
ok and state.board[index(row +
k, col -
k)] == cell;
190 [[
nodiscard]]
static Score heuristic(
const State &state,
const int player)
noexcept
197 const int dc)
noexcept
202 for (
size_t k = 0;
k < Align; ++
k)
204 const size_t row =
static_cast<size_t>(
static_cast<int>(
r0) +
205 dr*
static_cast<int>(
k));
206 const size_t col =
static_cast<size_t>(
static_cast<int>(
c0) +
207 dc*
static_cast<int>(
k));
208 const int cell = state.board[index(row, col)];
219 else if (
mine == 1
and empty == 2)
223 for (
size_t row = 0; row < Rows; ++row)
224 for (
size_t col = 0; col + Align <= Cols; ++col)
227 for (
size_t row = 0; row + Align <= Rows; ++row)
228 for (
size_t col = 0; col < Cols; ++col)
231 for (
size_t row = 0; row + Align <= Rows; ++row)
232 for (
size_t col = 0; col + Align <= Cols; ++col)
235 for (
size_t row = 0; row + Align <= Rows; ++row)
236 for (
size_t col = Align - 1; col < Cols; ++col)
245 for (
size_t row = Connect3Domain::Rows; row > 0; --row)
247 const size_t current = row - 1;
248 for (
size_t col = 0; col < Connect3Domain::Cols; ++col)
250 const int cell = state.board[current*Connect3Domain::Cols + col];
251 std::cout << (cell == 1 ?
'X' : cell == -1 ?
'O' :
'.');
257void drop(Connect3Domain &domain, Connect3State &state,
const size_t column)
259 domain.apply(state, Connect3Domain::Move{column});
266 Connect3Domain domain;
269 drop(domain, state, 1);
270 drop(domain, state, 2);
271 drop(domain, state, 1);
272 drop(domain, state, 2);
273 drop(domain, state, 0);
289 std::cout <<
"Reduced Connect-3 position:\n";
292 std::cout <<
"Alpha-Beta value: " <<
plain.value <<
'\n';
293 std::cout <<
"Best column: " <<
plain.first_move().column <<
'\n';
294 std::cout <<
"Visited states (domain order): " <<
plain.stats.visited_states <<
'\n';
295 std::cout <<
"Cutoffs (domain order): " <<
plain.stats.alpha_beta_cutoffs <<
'\n';
297 std::cout <<
"Best column with ordering: " <<
ordered.first_move().column <<
'\n';
298 std::cout <<
"Visited states (ordered): " <<
ordered.stats.visited_states <<
'\n';
299 std::cout <<
"Cutoffs (ordered): " <<
ordered.stats.alpha_beta_cutoffs <<
'\n';
300 std::cout <<
"Ordered batches: " <<
ordered.stats.move_ordering.ordered_batches <<
'\n';
301 std::cout <<
"Priority estimates: " <<
ordered.stats.move_ordering.priority_estimates <<
'\n';
302 std::cout <<
"Killer hits: " <<
ordered.stats.move_ordering.killer_hits <<
'\n';
303 std::cout <<
"History hits: " <<
ordered.stats.move_ordering.history_hits <<
'\n';
Umbrella header for the implicit state-space search framework.
Adversarial search engine with Alpha-Beta pruning.
static ExplorationPolicy default_policy() noexcept
Return the default exploration policy for Alpha-Beta.
Simple dynamic array with automatic resizing and functional operations.
Main namespace for Aleph-w library functions.
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.
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.