crooks

combinatorial rooks
git clone https://a3nm.net/git/crooks/
Log | Files | Refs

commit 91f6b1d21fef43a2cc155c34edae3cb0683ecf35
parent 9e8f1fd929ec3c8e061d82081b1a0dcd0a02c910
Author: Antoine Amarilli <a3nm@a3nm.net>
Date:   Tue, 10 Jun 2014 18:14:24 +0200

Merge gitorious.org:crooks/arcaseus-crooks

Diffstat:
extract.sh | 0
main.c | 343+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
run.sh | 7+++----
3 files changed, 236 insertions(+), 114 deletions(-)

diff --git a/extract.sh b/extract.sh diff --git a/main.c b/main.c @@ -2,6 +2,7 @@ #include <stdio.h> #include <stdlib.h> #include <time.h> +#include <inttypes.h> // compilation: // gcc -std=c99 -O2 -Wall main.c @@ -24,9 +25,9 @@ // TODO: optimize UBOUND // upper bound on grid size (N) for static allocation -#define MAXN 20 +#define MAXN 64 // number of useless steps to make before doing a random move -#define UBOUND 30 +#define UBOUND 10 // C1/C2 is the probability of going on doing an additional random move #define C1 3 @@ -52,6 +53,46 @@ unsigned char R[MAXN*MAXN][3]; // other rooks) for each cell (no matter whether it contains a rook or not) unsigned char V[MAXN][MAXN][MAXN]; +// proj_xy[i]&(1<<j) is true, iff there is a rook of coordinates +// (x=i, y=j, z=anything) +// In other words, it is the projection along the z axis giving a matrice of +// booleans, with the booleans then grouped in bitfields along the y axis. +// Similarily for the others. +uint64_t proj_xy[MAXN]; +uint64_t proj_xz[MAXN]; +uint64_t proj_yx[MAXN]; +uint64_t proj_yz[MAXN]; +uint64_t proj_zx[MAXN]; +uint64_t proj_zy[MAXN]; +// v_zx[k][i] is the number of cases already attacked in both the z and the +// x direction, in the line with coordinates (z=k, x=i). +// Interesting invariants: v_zx[z][x] = popcount(proj_xy[x] & proj_zy[z]) +// V[z][x][y] = v_zx[z][x] + v_xy[x][y] + v_zy[z][y] +uint8_t v_zx[MAXN][MAXN]; +uint8_t v_xy[MAXN][MAXN]; +uint8_t v_zy[MAXN][MAXN]; + +void print(); + +int popcount64(uint64_t bitfield) { + return __builtin_popcountll((unsigned long long) bitfield); +} + +int getVEmpty(int z, int x, int y) { + int result; + result = (v_zx[z][x] + v_xy[x][y] + v_zy[z][y]); + return result; +} + +// TODO: optimise in position where we know that !G[z][x][y] +int getV(int z, int x, int y) { + int result; + result = getVEmpty(z, x, y); + if (G[z][x][y]) + result -= 3; // Do not count a rook as being in violation with itself. + return result; +} + void intHandler() { // needed for profiling printf("SIGINT caught, exiting\n"); @@ -128,53 +169,60 @@ void print_violations() { printf("=> %d total violations\n", tot); } -int upd_vio(int z, int x, int y, int v, int d) { - int delta = 0; - int old = A[z][x][y]; - int val, new; - if (v > 0) - new = A[z][x][y] | (1 << d); - else - new = A[z][x][y] & ~(1 << d); - if (old == new) - return 0; // nothing changed - for (int p = 0; p < N; p++) { - if (p != z) { - val = ((new | (1 << 0)) == 7 ? 1 : 0) - ((old | (1 << 0)) == 7 ? 1 : 0); - V[p][x][y] += val; - if (G[p][x][y]) delta += val; - } - if (p != x) { - val = ((new | (1 << 1)) == 7 ? 1 : 0) - ((old | (1 << 1)) == 7 ? 1 : 0); - V[z][p][y] += val; - if (G[z][p][y]) delta += val; - } - if (p != y) { - val = ((new | (1 << 2)) == 7 ? 1 : 0) - ((old | (1 << 2)) == 7 ? 1 : 0); - V[z][x][p] += val; - if (G[z][x][p]) delta += val; - } - } - return delta; -} - int upd_atk_rm(int i) { // update A to reflect the fact that rook i is about to be removed int z = R[i][0]; int x = R[i][1]; int y = R[i][2]; - //printf("rmv rook at %d %d %d\n", z, x, y); - int delta = 0; + uint64_t mask_z = 1ull << z; + uint64_t mask_x = 1ull << x; + uint64_t mask_y = 1ull << y; + uint64_t old_vzx1, old_vzx2, old_vzy1, old_vzy2, old_vxy1, old_vxy2; + proj_xy[x] &= ~mask_y; + proj_xz[x] &= ~mask_z; + proj_yx[y] &= ~mask_x; + proj_yz[y] &= ~mask_z; + proj_zx[z] &= ~mask_x; + proj_zy[z] &= ~mask_y; + +//printf("rmv rook at %d %d %d\n", z, x, y); + int new_delta = 0; for (int p = 0; p < N; p++) { - delta += upd_vio(p, x, y, -1, 0); A[p][x][y] &= ~(1 << 0); // p x y attacked by z x y along direction z - delta += upd_vio(z, p, y, -1, 1); A[z][p][y] &= ~(1 << 1); // z p y attacked by z x y along direction x - delta += upd_vio(z, x, p, -1, 2); A[z][x][p] &= ~(1 << 2); // z x p attacked by z x y along direction y + +// Interesting invariants: v_zx[z][x] = popcount(proj_xy[x] & proj_zy[z]) +// v_zy[z][y] = popcount(proj_yx[y] & proj_zx[z]) +// v_xy[x][y] = popcount(proj_yz[y] & proj_xz[x]); + // TODO: refactor + old_vzx1 = v_zx[z][p]; + old_vzx2 = v_zx[p][x]; + old_vzy1 = v_zy[z][p]; + old_vzy2 = v_zy[p][y]; + old_vxy1 = v_xy[x][p]; + old_vxy2 = v_xy[p][y]; + v_zx[z][p] = popcount64(proj_xy[p] & proj_zy[z]); + v_zx[p][x] = popcount64(proj_xy[x] & proj_zy[p]); + v_zy[z][p] = popcount64(proj_yx[p] & proj_zx[z]); + v_zy[p][y] = popcount64(proj_yx[y] & proj_zx[p]); + v_xy[x][p] = popcount64(proj_yz[p] & proj_xz[x]); + v_xy[p][y] = popcount64(proj_yz[y] & proj_xz[p]); + if (proj_xz[p] & mask_z) + new_delta += v_zx[z][p] - old_vzx1; + if (proj_zx[p] & mask_x) + new_delta += v_zx[p][x] - old_vzx2; + if (proj_yz[p] & mask_z) + new_delta += v_zy[z][p] - old_vzy1; + if (proj_zy[p] & mask_y) + new_delta += v_zy[p][y] - old_vzy2; + if (proj_yx[p] & mask_x) + new_delta += v_xy[x][p] - old_vxy1; + if (proj_xy[p] & mask_y) + new_delta += v_xy[p][y] - old_vxy2; } - delta -= V[z][x][y]; - return delta; + new_delta -= getV(z, x, y); + return new_delta; } int upd_atk_add(int i) { @@ -182,18 +230,60 @@ int upd_atk_add(int i) { int z = R[i][0]; int x = R[i][1]; int y = R[i][2]; + uint64_t mask_z = 1ull << z; + uint64_t mask_x = 1ull << x; + uint64_t mask_y = 1ull << y; + uint64_t old_vzx1, old_vzx2, old_vzy1, old_vzy2, old_vxy1, old_vxy2; + proj_xy[x] |= mask_y; + proj_xz[x] |= mask_z; + proj_yx[y] |= mask_x; + proj_yz[y] |= mask_z; + proj_zx[z] |= mask_x; + proj_zy[z] |= mask_y; //printf("added rook at %d %d %d\n", z, x, y); - int delta = 0; + int new_delta = 0; for (int p = 0; p < N; p++) { - delta += upd_vio(p, x, y, 1, 0); A[p][x][y] |= 1 << 0; - delta += upd_vio(z, p, y, 1, 1); A[z][p][y] |= 1 << 1; - delta += upd_vio(z, x, p, 1, 2); A[z][x][p] |= 1 << 2; + +// Interesting invariants: v_zx[z][x] = popcount(proj_xy[x] & proj_zy[z]) +// v_zy[z][y] = popcount(proj_yx[y] & proj_zx[z]) +// v_xy[x][y] = popcount(proj_yz[y] & proj_xz[x]); + // TODO: refactor + old_vzx1 = v_zx[z][p]; + old_vzx2 = v_zx[p][x]; + old_vzy1 = v_zy[z][p]; + old_vzy2 = v_zy[p][y]; + old_vxy1 = v_xy[x][p]; + old_vxy2 = v_xy[p][y]; + v_zx[z][p] = popcount64(proj_xy[p] & proj_zy[z]); + v_zx[p][x] = popcount64(proj_xy[x] & proj_zy[p]); + v_zy[z][p] = popcount64(proj_yx[p] & proj_zx[z]); + v_zy[p][y] = popcount64(proj_yx[y] & proj_zx[p]); + v_xy[x][p] = popcount64(proj_yz[p] & proj_xz[x]); + v_xy[p][y] = popcount64(proj_yz[y] & proj_xz[p]); + if (p != z) { + if (proj_zx[p] & mask_x) + new_delta += v_zx[p][x] - old_vzx2; + if (proj_zy[p] & mask_y) + new_delta += v_zy[p][y] - old_vzy2; + } + if (p != x) { + if (proj_xz[p] & mask_z) + new_delta += v_zx[z][p] - old_vzx1; + if (proj_xy[p] & mask_y) + new_delta += v_xy[p][y] - old_vxy2; + } + if (p != y) { + if (proj_yz[p] & mask_z) + new_delta += v_zy[z][p] - old_vzy1; + if (proj_yx[p] & mask_x) + new_delta += v_xy[x][p] - old_vxy1; + } } - delta += V[z][x][y]; - return delta; + new_delta += getV(z, x, y); + return new_delta; } void num_attacks() { @@ -208,6 +298,24 @@ void num_attacks() { } } +void check_attacks() { + // debug: regenerate A from scratch + // and check that the previous version was already correct + int backup[MAXN][MAXN][MAXN]; + for (int i = 0; i < N; i++) + for (int j = 0; j < N; j++) + for (int k = 0; k < N; k++) + backup[i][j][k] = A[i][j][k]; + num_attacks(); + for (int i = 0; i < N; i++) + for (int j = 0; j < N; j++) + for (int k = 0; k < N; k++) + if (backup[i][j][k] != A[i][j][k]) { + print_attacks(); + printf("ERROR at %d %d %d\n", i, j, k); + exit(1); + } +} int num_violations() { // regenerate V from scratch @@ -319,15 +427,11 @@ int choose_rook(int prev) { for (int i = 0; i < K; i++) { if (i == prev) continue; - if ((nv = V[R[i][0]][R[i][1]][R[i][2]]) > best) { + if ((nv = getV(R[i][0], R[i][1], R[i][2])) > best) { best = nv; - } - } - // best is now the maximal num of violations - for (int i = 0; i < K; i++) { - if (i == prev) - continue; - if ((nv = V[R[i][0]][R[i][1]][R[i][2]]) == best) { + nvs[0] = i; + nnvs = 1; + } else if (nv == best) { nvs[nnvs++] = i; } } @@ -346,15 +450,24 @@ int random_move() { G[z][x][y] = 0; delta += upd_atk_rm(rook); int nx, ny, nz; - while (1) { + uint64_t mask_x, mask_y; + do { nx = (rand() % N); ny = (rand() % N); nz = (rand() % N); - if (!A[nz][nx][ny] && !G[nz][nx][ny]) + mask_x = 1ull << x; + mask_y = 1ull << y; + if (!A[nz][nx][ny] && !G[nz][nx][ny]) { +#if 0 + printf("proj_zy[nz]:%lx, mask_y: %lx:%lx -- ", proj_zy[nz], mask_y, proj_zy[nz] & mask_y); + printf("proj_xy[nx]:%lx, mask_y: %lx:%lx -- ", proj_xy[nx], mask_y, proj_xy[nx] & mask_y); + printf("proj_zx[nz]:%lx, mask_x: %lx:%lx\n", proj_zx[nz], mask_x, proj_zx[nz] & mask_x); +#endif break; // found a spot that's both empty and not in direct attack + } // TODO: nothing tests if this loop stands a chance of terminating, it would // be neater to exit the program then - } + } while (1); // ((proj_zy[nz] & mask_y) || (proj_xy[nx] & mask_y) || (proj_zx[nz] & mask_x)); G[nz][nx][ny] = 1; R[rook][0] = nz; R[rook][1] = nx; @@ -364,56 +477,70 @@ int random_move() { } int position(int i) { - // position rook i that was just removed - // TODO: more efficient (pqueue?) - // TODO more clever (chance to select cell with non-optimal number of - // violations) - int best = N*N*N+1; - int bs[MAXN*MAXN*MAXN][3]; - int nbs = 0; - for (int z = 0; z < N; z++) - for (int x = 0; x < N; x++) - for (int y = 0; y < N; y++) - if (!A[z][x][y] && !G[z][x][y]) { - if (V[z][x][y] < best) { - best = V[z][x][y]; - nbs = 0; - } - if (V[z][x][y] == best) { - bs[nbs][0] = z; - bs[nbs][1] = x; - bs[nbs][2] = y; - nbs++; - if (debug) printf("%d %d %d is a fitting position\n", z, x, y); - } - } - // choose random cell among optimal ones - int npos = rand() % nbs; - int bz = bs[npos][0]; - int bx = bs[npos][1]; - int by = bs[npos][2]; - if(debug) printf("position %d to %d %d %d with viol %d\n", i, bx, by, bz, V[bz][by][by]); - G[bz][bx][by] = 1; - R[i][0] = bz; - R[i][1] = bx; - R[i][2] = by; - return upd_atk_add(i); -} - -void spawn_rook() { - // add a new rook to the problem - position(K-1); + // position rook i that was just removed + // TODO: more efficient (pqueue?) + // TODO more clever (chance to select cell with non-optimal number of + // violations) + int best = N*N*N+1; + int bs[MAXN*MAXN*MAXN][3]; + int nbs = 0; + int v_zxy; + uint64_t mask_x, mask_y; + for (int z = 0; z < N; z++) { + for (int x = 0; x < N; x++) { + mask_x = 1ull << x; + if((proj_zx[z] & mask_x) || (v_zx[z][x] > best)) + {continue;} + for (int y = 0; y < N; y++) { + mask_y = 1ull << y; + if ((proj_zy[z] | proj_xy[x]) & mask_y) + {continue;} + // getVEmpty, because we know that the cell is + // not attacked, thus empty. + v_zxy = getVEmpty(z, x, y); + if (v_zxy < best) { + best = v_zxy; + bs[0][0] = z; + bs[0][1] = x; + bs[0][2] = y; + nbs = 1; + if (debug) + printf("%d %d %d is a fitting position\n", z, x, y); + } + else if (v_zxy == best) { + bs[nbs][0] = z; + bs[nbs][1] = x; + bs[nbs][2] = y; + nbs++; + if (debug) + printf("%d %d %d is a fitting position\n", z, x, y); + } + } + } + } + // choose random cell among optimal ones + int npos = rand() % nbs; + int bz = bs[npos][0]; + int bx = bs[npos][1]; + int by = bs[npos][2]; + if(debug) printf("position %d to %d %d %d with viol %d\n", i, bx, by, bz, getV(bz, by, bx)); + G[bz][bx][by] = 1; + R[i][0] = bz; + R[i][1] = bx; + R[i][2] = by; + return upd_atk_add(i); } - int main(int argc, char **argv) { int oK; + int stopK; int first = 1; int seed; N = atoi(argv[1]); oK = atoi(argv[2]); - if (argc == 4) - seed = atoi(argv[3]); + stopK = atoi(argv[3]); + if (argc == 5) + seed = atoi(argv[4]); else { seed = time(NULL); } @@ -421,24 +548,20 @@ int main(int argc, char **argv) { srand(seed); signal(SIGINT, intHandler); - int tot; - for (K = oK; K < N*N; K++) { + int tot, otot; + for (K = oK; K < stopK; K++) { printf("try %d rooks\n", K); // starting position if we just started, otherwise add the rook to the // existing position if (first) { init_pos(); first = 0; + num_attacks(); + tot = num_violations(); } else { - spawn_rook(); + tot += position(K-1); } - // compute number of attacks and violations - num_attacks(); - tot = num_violations(); - //check_increment(); - //print(); - int useless = 0; int prev_rook = -1; @@ -462,6 +585,7 @@ int main(int argc, char **argv) { // remove the rook //printf("tot is %d will rm %d\n", tot, rook); G[z][x][y] = 0; + otot = tot; tot += upd_atk_rm(rook); //printf("tot is %d after rm\n", tot); R[rook][0] = MAXN; @@ -470,7 +594,7 @@ int main(int argc, char **argv) { if(debug) print_attacks(); //check_increment(); - + // recompute V without the rook //num_violations(); //printf("check_incr\n"); @@ -479,7 +603,6 @@ int main(int argc, char **argv) { // choose next position tot += position(rook); - int otot = tot; int pred = V[R[rook][0]][R[rook][1]][R[rook][2]]; if(debug) print(); if(debug) print_all_violations(); @@ -497,7 +620,7 @@ int main(int argc, char **argv) { if(debug) print_attacks(); if(debug) print(); //printf("total viol %d\n", tot); - + if (tot >= otot) { // no improvement at this round useless++; diff --git a/run.sh b/run.sh @@ -1,10 +1,9 @@ #!/bin/bash -FILE="tmp/main_${1}.c" +FILE="main.c" PROG="tmp/rooks$1" OUT="tmp/out$1" -cat main.c | sed 's/define MAXN.*/define MAXN '$1'/' > $FILE -gcc -std=c99 -O2 -o $PROG $FILE -$PROG $1 1 > $OUT & +gcc -std=c99 -O3 -march=native -o $PROG $FILE +$PROG $1 1 9999 > $OUT & PID="$!" sleep $2 kill $PID