aboutsummaryrefslogtreecommitdiff
path: root/include/negamax.c
diff options
context:
space:
mode:
authortslil clingman <tslil@posteo.de>2023-01-15 16:03:37 +0100
committertslil <tslil@posteo.de>2026-08-28 19:37:41 +0100
commitee216c008a188a9436fedb85c70ee5d1719733b1 (patch)
treef1d8fa5efd71851dd4f8d3e2b26b1f95a6086cb9 /include/negamax.c
parent7cf3a656d0c923dd92025c09747461f2f2d1bed0 (diff)
new neural network arch (faster + better) & minor changes + fixes
Gone is the convolutional neural network, for it turns out not only is it more difficult to train, but all of the extra information about board layers didn't make much of a difference at this size. So cnn1986 has been replaced by nn1986, a standard, two-layer, dense nn configured as a binary classifier and (mis)used in that capacity. Note: total number of parameters is unchanged. HARK: this new nn exposes a bug somewhere in ctak. Run ctlm with self-play to see the completely borked board state at the end.
Diffstat (limited to 'include/negamax.c')
-rw-r--r--include/negamax.c232
1 files changed, 116 insertions, 116 deletions
diff --git a/include/negamax.c b/include/negamax.c
index 30aed05..e85aba9 100644
--- a/include/negamax.c
+++ b/include/negamax.c
@@ -1,18 +1,18 @@
/*
- This file is part of ct.
+ This file is part of ct.
- This program is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
+ This program is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
- This program is distributed in the hope that it will be useful, but
- WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- General Public License for more details.
+ This program is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
- You should have received a copy of the GNU General Public License
- along with ct. If not, see <https://www.gnu.org/licenses/>.
+ You should have received a copy of the GNU General Public License
+ along with ct. If not, see <https://www.gnu.org/licenses/>.
*/
#include "negamax.h"
@@ -31,46 +31,46 @@ static uint64_t negamax_best_action;
// ===================================================================
static float negamax(const uint8_t cur_depth, const uint8_t init_depth,
- float alpha, float beta,
- const float colour, const uint64_t hash);
+ float alpha, float beta,
+ const float colour, const uint64_t hash);
// ===================================================================
// Exported functions
// ===================================================================
void negamax_init(const uint8_t new_board_size) {
- board_size = new_board_size;
- action_list_init();
- zobrist_init();
- tt_init();
+ board_size = new_board_size;
+ action_list_init();
+ zobrist_init();
+ tt_init();
}
void negamax_free(void) {
- zobrist_free();
+ zobrist_free();
}
float negamax_generate(void) {
- // We need to start with something outside of [-∞,∞] because those
- // values are wins
- const float safe_infty = infty + 1;
- float result = -infty;
+ // We need to start with something outside of [-∞,∞] because those
+ // values are wins
+ const float safe_infty = infty + 1;
+ float result = -infty;
- negamax_best_action = -1;
+ negamax_best_action = -1;
- tt_init();
- for (int d = 1; d <= negamax_search_depth; d++) {
- result = negamax(d, d, -safe_infty, safe_infty,
- (ply & 1) ? +1.0 : -1.0, zobrist_compute());
- }
- tt_free();
+ tt_init();
+ for (int d = 1; d <= negamax_search_depth; d++) {
+ result = negamax(d, d, -safe_infty, safe_infty,
+ (ply & 1) ? +1.0 : -1.0, zobrist_compute());
+ }
+ tt_free();
- action_to_ptn(negamax_best_action, negamax_ptn);
+ action_to_ptn(negamax_best_action, negamax_ptn);
- return result;
+ return result;
}
// ===================================================================
-// α-β negamax with iterative deepening, using the cnn1986 evaluation
+// α-β negamax with iterative deepening, using the nn1986 evaluation
// function and transposition tables using Zobrist hasing and a
// chaining hash table
// ===================================================================
@@ -79,88 +79,88 @@ static enum TT_FLAG flag;
static enum WIN_TYPE w;
static float negamax(const uint8_t cur_depth, const uint8_t init_depth,
- float alpha, float beta,
- const float colour, const uint64_t hash) {
-
- tt_entry_t *entry = tt_seek(hash);
-
- // CAUTION: ≥ breaks search stability (vs =) on shallow depths
- if (entry != NULL && entry->depth >= cur_depth) {
- if (entry->flag == TT_EXACT) {
- return entry->value;
- } else if (entry->flag == TT_LOWERBOUND && entry->value > alpha) {
- alpha = entry->value;
- } else if (entry->flag == TT_UPPERBOUND && entry->value < beta) {
- beta = entry->value;
- }
- if (alpha >= beta) return entry->value;
- }
-
- action_list_t *list;
- if ((list = action_list_generate()) == NULL)
- return alpha; // should never happen!
-
- if (entry != NULL) {
- action_move_to_front(entry->action, list);
- }
-
- if (init_depth > 1 && cur_depth == init_depth) {
- action_move_to_front(negamax_best_action, list);
- }
-
- // TODO: what to do if this is never written to?
- action_t best_action = list->head->action;
- float best_value = -infty;
-
- for (action_node_t *node=list->head; node!=NULL; node=node->next) {
- negamax_display_progress(cur_depth, init_depth, list->length);
-
- action_take(node->action);
-
- // Compute the value of the node
- float node_value;
- if (ply >= 2*board_size - 2 && (w = check_win()) < 0xFF) {
- node_value = -colour*infty;
- // Check win if far enough into the game
- if (w == WIN_ROAD_BLACK || w == WIN_FLAT_BLACK) {
- node_value = colour*infty;
- } else if (w == WIN_DRAW) {
- node_value = 0;
- }
- } else if (cur_depth > 1) {
- // If nobody won, or too early and not leaf, recurse
- node_value = -negamax(cur_depth - 1, init_depth,
- -beta, -alpha,
- -colour, zobrist_compute());
- } else {
- node_value = colour * cnn1986_evaluate_black_win();
- }
- action_undo(node->action);
-
- if (node_value > best_value) {
- best_value = node_value;
- best_action = node->action;
- }
-
- if (best_value > alpha) alpha = best_value;
- if (alpha >= beta) break;
- }
-
- action_list_free(list);
- if (cur_depth == init_depth) negamax_best_action = best_action;
-
- flag = TT_EXACT;
- if (best_value >= beta) flag = TT_LOWERBOUND;
- else if (best_value <= alpha) flag = TT_UPPERBOUND;
-
- if (entry == NULL) {
- tt_insert(hash, flag, cur_depth, best_value, best_action);
- } else {
- entry->flag = flag;
- entry->value = best_value;
- entry->depth = cur_depth;
- entry->action = best_action;
- }
-
- return best_value;
+ float alpha, float beta,
+ const float colour, const uint64_t hash) {
+
+ tt_entry_t *entry = tt_seek(hash);
+
+ // CAUTION: ≥ breaks search stability (vs =) on shallow depths
+ if (entry != NULL && entry->depth >= cur_depth) {
+ if (entry->flag == TT_EXACT) {
+ return entry->value;
+ } else if (entry->flag == TT_LOWERBOUND && entry->value > alpha) {
+ alpha = entry->value;
+ } else if (entry->flag == TT_UPPERBOUND && entry->value < beta) {
+ beta = entry->value;
+ }
+ if (alpha >= beta) return entry->value;
+ }
+
+ action_list_t *list;
+ if ((list = action_list_generate()) == NULL)
+ return alpha; // should never happen!
+
+ if (entry != NULL) {
+ action_move_to_front(entry->action, list);
+ }
+
+ if (init_depth > 1 && cur_depth == init_depth) {
+ action_move_to_front(negamax_best_action, list);
+ }
+
+ // TODO: what to do if this is never written to?
+ action_t best_action = list->head->action;
+ float best_value = -infty;
+
+ for (action_node_t *node=list->head; node!=NULL; node=node->next) {
+ negamax_display_progress(cur_depth, init_depth, list->length);
+
+ action_take(node->action);
+
+ // Compute the value of the node
+ float node_value;
+ if (ply >= 2*board_size - 2 && (w = check_win()) < 0xFF) {
+ node_value = -colour*infty;
+ // Check win if far enough into the game
+ if (w == WIN_ROAD_BLACK || w == WIN_FLAT_BLACK) {
+ node_value = colour*infty;
+ } else if (w == WIN_DRAW) {
+ node_value = 0;
+ }
+ } else if (cur_depth > 1) {
+ // If nobody won, or too early and not leaf, recurse
+ node_value = -negamax(cur_depth - 1, init_depth,
+ -beta, -alpha,
+ -colour, zobrist_compute());
+ } else {
+ node_value = colour * nn1986_evaluate_black_win();
+ }
+ action_undo(node->action);
+
+ if (node_value > best_value) {
+ best_value = node_value;
+ best_action = node->action;
+ }
+
+ if (best_value > alpha) alpha = best_value;
+ if (alpha >= beta) break;
+ }
+
+ action_list_free(list);
+ if (cur_depth == init_depth) negamax_best_action = best_action;
+
+ flag = TT_EXACT;
+ if (best_value >= beta) flag = TT_LOWERBOUND;
+ else if (best_value <= alpha) flag = TT_UPPERBOUND;
+
+ if (entry == NULL) {
+ tt_insert(hash, flag, cur_depth, best_value, best_action);
+ } else {
+ entry->flag = flag;
+ entry->value = best_value;
+ entry->depth = cur_depth;
+ entry->action = best_action;
+ }
+
+ return best_value;
}