aboutsummaryrefslogtreecommitdiff
path: root/include
diff options
context:
space:
mode:
authortslil clingman <tslil@posteo.de>2021-01-18 19:45:06 -0500
committertslil <tslil@posteo.de>2026-08-28 19:37:41 +0100
commitfa81af89f613987cfe52adb0e41b89379fc9cc0f (patch)
tree3c2f22b7a97dbc12f7746c6ebf9c85ff8f0cad4f /include
parent9c56a2c16bce2a7011994bb9f4a62c3ad2fef2df (diff)
Finally fixed move generation
Diffstat (limited to 'include')
-rw-r--r--include/ct1986.c108
1 files changed, 72 insertions, 36 deletions
diff --git a/include/ct1986.c b/include/ct1986.c
index d5325be..ec73f7c 100644
--- a/include/ct1986.c
+++ b/include/ct1986.c
@@ -17,9 +17,21 @@ static float dense1[DENSE1_NUM];
static float dense2[DENSE2_NUM];
#ifndef DETERMINISTIC
-static uint32_t x = 1;
-#define DOXORSHIFT { x ^= x << 13; x ^= x >> 17; x ^= x << 5; }
-#define FUDGE (((float)(x&255))/255.0-0.5)*0.2
+union u_f {
+ uint32_t u;
+ float f;
+};
+
+static uint32_t state = 1;
+static union u_f fudge;
+
+#define DOXORSHIFT { \
+ state ^= state << 13; \
+ state ^= state >> 17; \
+ state ^= state << 5; \
+ fudge.u = 0x3f800000 | state >> 10; \
+ fudge.f = (fudge.f - 1.5) * 0.01; \
+}
#endif
#define RELU(x) ((x) = ((x)<0)?0:(x))
@@ -101,7 +113,7 @@ ct1986_evaluate_black_win(void) {
output = (12.0+output+50.0*output/(output*output+10.0))/24.0;
#ifndef DETERMINISTIC
DOXORSHIFT;
- output += FUDGE;
+ output += fudge.f;
#endif
if (output > 1.0) return 1.0;
else if (output < 0.0) return 0.0;
@@ -152,8 +164,8 @@ static enum WIN_TYPE w;
previous_ply(); \
} \
/* Update the optimal value */ \
- if (((min > 0) && (val < optimal)) \
- || ((min == 0) && (val > optimal))) { \
+ if (((min > 0) && (val <= optimal)) \
+ || ((min == 0) && (val >= optimal))) { \
optimal = val; \
if (cur_depth == 0) (store); \
} \
@@ -184,7 +196,7 @@ ct1986_minimax(const uint8_t cur_depth, const uint8_t max_depth,
for (uint8_t col = 0; col < 5; 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);
+ const uint8_t count = (COUNT_AT(loc) > 5) ? 5 : 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?
@@ -193,7 +205,9 @@ ct1986_minimax(const uint8_t cur_depth, const uint8_t max_depth,
// compilers?
uint16_t colours_backup[5];
- uint8_t celldat_backup[5], drops[5-1];
+ uint8_t celldat_backup[5], drops[5]; // we only use 4, the
+ // fifth is to skip a
+ // bounds check at (*)
// Back up the row of the board
for (uint8_t y = 0; y < 5; y++) {
colours_backup[y] = colours[THE_COORDS(col, y)];
@@ -207,34 +221,48 @@ ct1986_minimax(const uint8_t cur_depth, const uint8_t max_depth,
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 < 5 && 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) {
- idx++;
- if (idx >= steps) break;
- drops[idx]++;
- carry = 0;
- }
- if (drops[idx] > count || drops[idx] > 5) {
- drops[idx] = 1; carry = 1;
+ /*
+ * We don't do anything terribly efficient or smart here,
+ * just try all the ordered partitions of num ∈ {1,…,count}
+ * that will fit on the board in the current direction.
+ * Recall that count = max(stack height, carry limit).
+ */
+ uint8_t upper;
+ switch (dir) {
+ case M_UP: {
+ upper = (4-row > count) ? count : 4-row;
+ break;
+ }
+ case M_DOWN: {
+ upper = (row > count) ? count : row;
+ break;
+ }
+ case M_LEFT: {
+ upper = (col > count) ? count : col;
+ break;
+ }
+ case M_RIGHT: {
+ upper = (4-col > count) ? count : 4-col;
+ break;
+ }
+ }
+ uint8_t gaps, t, idx, mask;
+ for (uint8_t num = 1; num <= count; num++) {
+ for (uint8_t steps = 1; steps <= upper && steps <= num; steps++) {
+ gaps = 0b00000111 >> (4-steps);
+ do {
+ // Translate to a drop sequence
+ drops[0] = 1; mask = 1; idx = 0;
+ for (uint8_t d = 0; d + 1 < num; d++) {
+ if (gaps & mask) {
+ idx++;
+ drops[idx] = 1; // (*) we don't need to bounds check
+ } else {
+ drops[idx] += 1;
+ }
+ mask <<= 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, but we do
- // manually.
+ // Try it, and manually check for win if it's valid
r = try_move(loc, dir, steps, drops);
if (r == ACT_OK) {
// First check for wins, if we're at the bottom
@@ -259,7 +287,15 @@ ct1986_minimax(const uint8_t cur_depth, const uint8_t max_depth,
}
// Prune
if (alpha >= beta) return optimal;
- }
+ /*
+ * With thanks to
+ * https://graphics.stanford.edu/~seander/bithacks.html#NextBitPermutation
+ * we have the following magic to generate the next
+ * permutation of steps-many set bits
+ */
+ t = (gaps | (gaps - 1));
+ gaps = (t + 1) | (((~t & -~t) - 1) >> (__builtin_ctz(gaps) + 1));
+ } while (gaps && (gaps + 1 <= (1<<(num-1))));
}
}
}