summaryrefslogtreecommitdiff
path: root/include/ct1975.c
diff options
context:
space:
mode:
Diffstat (limited to 'include/ct1975.c')
-rw-r--r--include/ct1975.c219
1 files changed, 0 insertions, 219 deletions
diff --git a/include/ct1975.c b/include/ct1975.c
deleted file mode 100644
index 5cb47e9..0000000
--- a/include/ct1975.c
+++ /dev/null
@@ -1,219 +0,0 @@
-#include "ct1975.h"
-
-// ===================================================================
-// Implementation of a small convolutional neural network
-// ===================================================================
-
-static float flattened[CONV_NUM+2];
-static float dense1[DENSE1_NUM];
-static float dense2[DENSE2_NUM];
-
-#define RELU(x) ((x) = ((x)<0)?0:(x))
-
-float
-ct1975_evaluate_black_win(void) {
- /* ------------------ *
- * Convolution layer *
- * ------------------ */
- // for each kernel
- for (uint8_t kern = 0; kern < KERN_NUM; kern++) {
- // the stride is 1, march across the board
- for (uint8_t bx = 0; bx < KERN_OSIZE; bx++) {
- for (uint8_t by = 0; by < KERN_OSIZE; by++) {
- flattened[kern+KERN_NUM*(bx+by*KERN_OSIZE)] =
- conv2d_biases[kern];
- // Compute the convolution for this position
- for (uint8_t ky = 0; ky < KERN_SIZE; ky++) {
- for (uint8_t kx = 0; kx < KERN_SIZE; kx++) {
- for (uint8_t c = 0; c < KERN_CHAN; c++) {
- // Where we are on the board
- const uint8_t loc = kx+by+(ky+bx)*board_size;
- // Look up what's on the board at this location, and
- // multiply it. For c=0 we have to do some extra work
- float lookup = 0;
- if (COUNT_AT(loc)>c) {
- if (c==0) {
- if (STONE_AT(loc) == STONE_STANDING) {
- lookup = (colours[loc] & 1) ? +0.25 : -0.25;
- } else if (STONE_AT(loc) == STONE_CAPSTONE) {
- lookup = (colours[loc] & 1) ? +1.00 : -1.00;
- } else {
- lookup = (colours[loc] & 1) ? +0.50 : -0.50;
- }
- } else {
- lookup = (colours[loc] & (1<<c)) ? +0.50 : -0.50;
- }
- }
- flattened[kern+KERN_NUM*(bx+by*KERN_OSIZE)]
- += lookup*conv2d_weights[kern][ky][kx][c];
- }
- }
- }
- RELU(flattened[kern+KERN_NUM*(bx+by*KERN_OSIZE)]);
- }
- }
- }
- // Add input of flat counts
- flattened[CONV_NUM] = (float)(white_count & 127)/21.0;
- flattened[CONV_NUM+1] = (float)(black_count & 127)/21.0;
- /* ------------------ *
- * First dense layer *
- * ------------------ */
- for (uint8_t d1 = 0; d1 < DENSE1_NUM; d1++) {
- dense1[d1] = dense1_biases[d1];
- for (uint8_t fl = 0; fl < CONV_NUM+2; fl++) {
- dense1[d1] += flattened[fl]*dense1_weights[d1][fl];
- }
- RELU(dense1[d1]);
- }
- /* ------------------- *
- * Second dense layer *
- * ------------------- */
- for (uint8_t d2 = 0; d2 < DENSE2_NUM; d2++) {
- dense2[d2] = dense2_biases[d2];
- for (uint8_t d1 = 0; d1 < DENSE1_NUM; d1++) {
- dense2[d2] += dense1[d1]*dense2_weights[d2][d1];
- }
- RELU(dense2[d2]);
- }
- /* ------------- *
- * Output layer *
- * ------------- */
- float output = output_bias;
- for (uint8_t d2 = 0; d2 < DENSE2_NUM; d2++) {
- output += dense2[d2]*output_weights[d2];
- }
-
- // Truncated Pade approximant of logistic function
- output = (12.0+output+50.0*output/(output*output+10.0))/24.0;
- if (output > 1.0) return 1.0;
- else if (output < 0.0) return 0.0;
-
- return output;
-}
-
-// ===================================================================
-// Select best move according the CNN above
-// ===================================================================
-
-char ct1975_ptn[9];
-float ct1975_optimal;
-
-#define BETTER(t,o) ((ply>2 && ((current_colour == C_BLACK && (t) > (o)) || (current_colour == C_WHITE && (t) < (o)))) || (ply<3 && ((current_colour == C_BLACK && (o) > (t)) || (current_colour == C_WHITE && (o) < (t)))))
-
-void
-ct1975_generate_ptn(void display_progress(void)) {
- float this = 0;
- enum E_RESULT r;
- uint16_t colours_backup[board_size];
- uint8_t celldat_backup[board_size], drops[board_size];
- const uint8_t white_count_backup = white_count,
- black_count_backup = black_count;
-
- // 1.0 is a `certain' black win, 0.0 is a `certain' white win.
- if (ply > 2)
- ct1975_optimal = (current_colour == C_BLACK) ? 0.0 : 1.0;
- else
- ct1975_optimal = (current_colour == C_BLACK) ? 0.1 : 0.0;
-
- // Step across the board
- for (uint8_t row = 0; row < board_size; row++) {
- for (uint8_t col = 0; col < board_size; col++) {
- // Try all valid actions for this square. Is it empty?
- const uint8_t loc = THE_COORDS(col, row);
- const uint8_t count = COUNT_AT(loc);
- // Only try moves after CPS
- if (count && ((colours[loc] & 1) == current_colour) && ply>2) {
- // There are stones, can we move them in a given direction?
- // I'm not a huge fan of looping through enums, but it's
- // better than manually unrolling this. Sufficiently smart
- // compilers?
- for (enum MOVE_DIRECTION dir = M_UP; dir <= M_RIGHT; dir++) {
- // Back-up the row/column of the board
- if (dir == M_UP || dir == M_DOWN) {
- for (uint8_t y = 0; y < board_size; y++) {
- colours_backup[y] = colours[THE_COORDS(col, y)];
- celldat_backup[y] = celldat[THE_COORDS(col, y)];
- }
- } else {
- for (uint8_t x = 0; x < board_size; x++) {
- colours_backup[x] = colours[THE_COORDS(x, row)];
- celldat_backup[x] = celldat[THE_COORDS(x, row)];
- }
- }
- // We don't do anything terribly efficient or smart here,
- // just try everything...
-
- // For every number of steps
- for (uint8_t steps = 1; steps < board_size && steps <= count; steps++) {
- uint8_t idx, carry;
- for (idx = 0; idx < steps; idx++) drops[idx]=0;
- idx = 0;
- while (idx < steps) {
- // Increment the drop sequence
- carry = 0;
- drops[idx]++;
- do {
- if (carry) { drops[++idx]++; carry = 0;}
- if (drops[idx] > count || drops[idx] > board_size) {
- drops[idx] = 1; carry = 1;
- }
- } while (carry && idx < steps);
- // If carry is still set here we're done
- if (carry == 0) {
- // Try it, and note that try_move will never return
- // GAME_END. It does not check for winners. We don't
- // presently do that either, trust in the magic
- // numbers :)
- r = try_move(loc, dir, steps, drops);
- if (r == ACT_OK) {
- this = ct1975_evaluate_black_win();
- if (BETTER(this, ct1975_optimal)) {
- // Update the chosen action
- ct1975_optimal = this;
- generate_move(board_size, loc, dir, steps, drops, ct1975_ptn);
- }
- // Reset the board data
- if (dir == M_UP || dir == M_DOWN) {
- for (uint8_t y = 0; y < board_size; y++) {
- colours[THE_COORDS(col, y)] = colours_backup[y];
- celldat[THE_COORDS(col, y)] = celldat_backup[y];
- }
- } else {
- for (uint8_t x = 0; x < board_size; x++) {
- colours[THE_COORDS(x, row)] = colours_backup[x];
- celldat[THE_COORDS(x, row)] = celldat_backup[x];
- }
- }
- }
- }
- }
- }
- }
- } else if (count == 0) {
- // Empty square, try the three placements. Again, looping
- // through enums, sigh.
- for (enum STONE_VARIANT stone = STONE_FLAT;
- stone <= STONE_CAPSTONE; stone++) {
- // try_place will never check for winning, and we don't do
- // that either here
- r = try_place(loc, current_colour, stone);
- // Legal placement, evaluate it
- if (r == ACT_OK) {
- this = ct1975_evaluate_black_win();
- if (BETTER(this, ct1975_optimal)) {
- // Update the chosen action
- ct1975_optimal = this;
- generate_place(board_size, loc, stone, ct1975_ptn);
- }
- // Reset the state
- celldat[loc] = 0;
- white_count = white_count_backup;
- black_count = black_count_backup;
- }
- }
- }
- display_progress();
- }
- }
-}