crooks

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

main.c (18368B)


      1 #include <signal.h>
      2 #include <stdio.h>
      3 #include <stdlib.h>
      4 #include <time.h>
      5 #include <inttypes.h>
      6 
      7 // compilation:
      8 //   gcc -std=c99 -O2 -Wall main.c
      9 // usage:
     10 //   ./a.out N K [SEED]
     11 //   SEED is used to seed the RNG, otherwise time is used;
     12 //   used seed is always printed first
     13 //   N is grid size, K is number of rooks (program will increase K when it
     14 //   succeeded)
     15 
     16 // general idea: always take one of the rooks with most violations and move to a
     17 // cell with least violations; when stuck for UBOUND steps, do a totally random
     18 // move
     19 // in all intermediate configurations there are never two rooks in direct
     20 // attack, the program tries to minimize the number of cells attacked by wrong
     21 // number of rooks
     22 // TODO: optimize
     23 // TODO: profile
     24 // TODO: plot asymptotics to make conjectures
     25 // TODO: optimize UBOUND
     26 
     27 // upper bound on grid size (N) for static allocation
     28 #define MAXN 64
     29 // number of useless steps to make before doing a random move
     30 #define UBOUND 10
     31 
     32 // C1/C2 is the probability of going on doing an additional random move
     33 #define C1 3
     34 #define C2 5
     35 
     36 const int debug = 0;
     37 
     38 // N: grid size
     39 // K: number of rooks
     40 int N, K;
     41 // stores if a cell is taken by a rook or not
     42 // coordinates are z, x, y
     43 // corresponding to height, line, col
     44 unsigned char G[MAXN][MAXN][MAXN];
     45 // stores a number indicating by which directions the cell is attacked
     46 // same coordinates
     47 // numbers are in [0, 8[:, LSB is z, 2nd LSB is x, 3rd LSB is y
     48 unsigned char A[MAXN][MAXN][MAXN];
     49 // stores the coordinates of each rook
     50 // 0 is z (height), 1 is x (line), 2 is y (col)
     51 unsigned char R[MAXN*MAXN][3];
     52 // stores the number of violations (number of attacked cells attacked by two
     53 // other rooks) for each cell (no matter whether it contains a rook or not)
     54 unsigned char V[MAXN][MAXN][MAXN];
     55 
     56 // proj_xy[i]&(1<<j) is true, iff there is a rook of coordinates
     57 // (x=i, y=j, z=anything)
     58 // In other words, it is the projection along the z axis giving a matrice of
     59 // booleans, with the booleans then grouped in bitfields along the y axis.
     60 // Similarily for the others.
     61 uint64_t proj_xy[MAXN];
     62 uint64_t proj_xz[MAXN];
     63 uint64_t proj_yx[MAXN];
     64 uint64_t proj_yz[MAXN];
     65 uint64_t proj_zx[MAXN];
     66 uint64_t proj_zy[MAXN];
     67 // v_zx[k][i] is the number of cases already attacked in both the z and the
     68 // x direction, in the line with coordinates (z=k, x=i).
     69 // Interesting invariants: v_zx[z][x] = popcount(proj_xy[x] & proj_zy[z])
     70 // V[z][x][y] = v_zx[z][x] + v_xy[x][y] + v_zy[z][y]
     71 uint8_t v_zx[MAXN][MAXN];
     72 uint8_t v_xy[MAXN][MAXN];
     73 uint8_t v_zy[MAXN][MAXN];
     74 
     75 void print();
     76 
     77 int popcount64(uint64_t bitfield) {
     78 	return __builtin_popcountll((unsigned long long) bitfield);
     79 }
     80 
     81 int getVEmpty(int z, int x, int y) {
     82 	int result;
     83 	result = (v_zx[z][x] + v_xy[x][y] + v_zy[z][y]);
     84 	return result;
     85 }
     86 
     87 // TODO: optimise in position where we know that !G[z][x][y]
     88 int getV(int z, int x, int y) {
     89 	int result;
     90 	result = getVEmpty(z, x, y);
     91 	if (G[z][x][y])
     92 		result -= 3; // Do not count a rook as being in violation with itself.
     93 	return result;
     94 }
     95 
     96 void intHandler() {
     97   // needed for profiling
     98   printf("SIGINT caught, exiting\n");
     99   exit(1);
    100 }
    101 
    102 void print() {
    103   // print the current grid and also rook positions when debug
    104   for (int x = 0; x < N; x++) {
    105     for (int y = 0; y < N; y++) {
    106       int pos = -1;
    107       for (int z = 0; z < N; z++) {
    108         if (G[z][x][y]) {
    109           pos = z;
    110           break;
    111         }
    112       }
    113       if (pos < 0)
    114         printf("* ");
    115       else {
    116         if (pos < 9)
    117           printf("%d ", pos + 1);
    118         else
    119           printf("%c ", 'A' + (pos - 9));
    120       }
    121     }
    122     printf("\n");
    123   }
    124   if (debug) {
    125     for (int i = 0; i < K; i++)
    126     printf("rook %d at %d %d %d\n", i, R[i][1], R[i][2], R[i][0]);
    127   }
    128 }
    129 
    130 void print_attacks() {
    131   // print the attack pattern in each cell
    132   for (int z = 0; z < N; z++) {
    133     printf("== %d ==\n", z);
    134     for (int x = 0; x < N; x++) {
    135       for (int y = 0; y < N; y++) {
    136         int val = A[z][x][y];
    137         printf("%d%d%d ", val & (1 << 0), (val & (1 << 1))>>1, (val& (1 << 2))>>2);
    138       }
    139       printf("\n");
    140     }
    141     printf("\n");
    142   }
    143 }
    144 
    145 void print_all_violations() {
    146   // print the number of violation in each cell
    147   for (int z = 0; z < N; z++) {
    148     printf("== %d ==\n", z);
    149     for (int x = 0; x < N; x++) {
    150       for (int y = 0; y < N; y++) {
    151         int val = V[z][x][y];
    152         printf("%d ", val);
    153       }
    154       printf("\n");
    155     }
    156     printf("\n");
    157   }
    158 }
    159 
    160 void print_violations() {
    161   // print the number of violations of the rooks
    162   int tot = 0;
    163   for (int i = 0; i < K; i++) {
    164     int val = V[R[i][0]][R[i][1]][R[i][2]];
    165     printf("rook %d has %d violations\n", i,
    166         val);
    167     tot += val;
    168   }
    169   printf("=> %d total violations\n", tot);
    170 }
    171 
    172 int upd_atk_rm(int i) {
    173   // update A to reflect the fact that rook i is about to be removed
    174   int z = R[i][0];
    175   int x = R[i][1];
    176   int y = R[i][2];
    177   uint64_t mask_z = 1ull << z;
    178   uint64_t mask_x = 1ull << x;
    179   uint64_t mask_y = 1ull << y;
    180   uint64_t old_vzx1, old_vzx2, old_vzy1, old_vzy2, old_vxy1, old_vxy2;
    181   proj_xy[x] &= ~mask_y;
    182   proj_xz[x] &= ~mask_z;
    183   proj_yx[y] &= ~mask_x;
    184   proj_yz[y] &= ~mask_z;
    185   proj_zx[z] &= ~mask_x;
    186   proj_zy[z] &= ~mask_y;
    187 
    188 //printf("rmv rook at %d %d %d\n", z, x, y);
    189   int new_delta = 0;
    190   for (int p = 0; p < N; p++) {
    191     A[p][x][y] &= ~(1 << 0); // p x y attacked by z x y along direction z
    192     A[z][p][y] &= ~(1 << 1); // z p y attacked by z x y along direction x
    193     A[z][x][p] &= ~(1 << 2); // z x p attacked by z x y along direction y
    194 
    195 // Interesting invariants: v_zx[z][x] = popcount(proj_xy[x] & proj_zy[z])
    196 // 			   v_zy[z][y] = popcount(proj_yx[y] & proj_zx[z])
    197 // 			   v_xy[x][y] = popcount(proj_yz[y] & proj_xz[x]);
    198     // TODO: refactor
    199     old_vzx1 = v_zx[z][p];
    200     old_vzx2 = v_zx[p][x];
    201     old_vzy1 = v_zy[z][p];
    202     old_vzy2 = v_zy[p][y];
    203     old_vxy1 = v_xy[x][p];
    204     old_vxy2 = v_xy[p][y];
    205     v_zx[z][p] = popcount64(proj_xy[p] & proj_zy[z]);
    206     v_zx[p][x] = popcount64(proj_xy[x] & proj_zy[p]);
    207     v_zy[z][p] = popcount64(proj_yx[p] & proj_zx[z]);
    208     v_zy[p][y] = popcount64(proj_yx[y] & proj_zx[p]);
    209     v_xy[x][p] = popcount64(proj_yz[p] & proj_xz[x]);
    210     v_xy[p][y] = popcount64(proj_yz[y] & proj_xz[p]);
    211     if (proj_xz[p] & mask_z)
    212 	    new_delta += v_zx[z][p] - old_vzx1;
    213     if (proj_zx[p] & mask_x)
    214 	    new_delta += v_zx[p][x] - old_vzx2;
    215     if (proj_yz[p] & mask_z)
    216 	    new_delta += v_zy[z][p] - old_vzy1;
    217     if (proj_zy[p] & mask_y)
    218 	    new_delta += v_zy[p][y] - old_vzy2;
    219     if (proj_yx[p] & mask_x)
    220 	    new_delta += v_xy[x][p] - old_vxy1;
    221     if (proj_xy[p] & mask_y)
    222 	    new_delta += v_xy[p][y] - old_vxy2;
    223   }
    224   new_delta -= getV(z, x, y);
    225   return new_delta;
    226 }
    227 
    228 int upd_atk_add(int i) {
    229   // update A to reflect the fact that rook i was just added
    230   int z = R[i][0];
    231   int x = R[i][1];
    232   int y = R[i][2];
    233   uint64_t mask_z = 1ull << z;
    234   uint64_t mask_x = 1ull << x;
    235   uint64_t mask_y = 1ull << y;
    236   uint64_t old_vzx1, old_vzx2, old_vzy1, old_vzy2, old_vxy1, old_vxy2;
    237   proj_xy[x] |= mask_y;
    238   proj_xz[x] |= mask_z;
    239   proj_yx[y] |= mask_x;
    240   proj_yz[y] |= mask_z;
    241   proj_zx[z] |= mask_x;
    242   proj_zy[z] |= mask_y;
    243   //printf("added rook at %d %d %d\n", z, x, y);
    244   int new_delta = 0;
    245   for (int p = 0; p < N; p++) {
    246     A[p][x][y] |= 1 << 0;
    247     A[z][p][y] |= 1 << 1;
    248     A[z][x][p] |= 1 << 2;
    249 
    250 // Interesting invariants: v_zx[z][x] = popcount(proj_xy[x] & proj_zy[z])
    251 // 			   v_zy[z][y] = popcount(proj_yx[y] & proj_zx[z])
    252 // 			   v_xy[x][y] = popcount(proj_yz[y] & proj_xz[x]);
    253     // TODO: refactor
    254     old_vzx1 = v_zx[z][p];
    255     old_vzx2 = v_zx[p][x];
    256     old_vzy1 = v_zy[z][p];
    257     old_vzy2 = v_zy[p][y];
    258     old_vxy1 = v_xy[x][p];
    259     old_vxy2 = v_xy[p][y];
    260     v_zx[z][p] = popcount64(proj_xy[p] & proj_zy[z]);
    261     v_zx[p][x] = popcount64(proj_xy[x] & proj_zy[p]);
    262     v_zy[z][p] = popcount64(proj_yx[p] & proj_zx[z]);
    263     v_zy[p][y] = popcount64(proj_yx[y] & proj_zx[p]);
    264     v_xy[x][p] = popcount64(proj_yz[p] & proj_xz[x]);
    265     v_xy[p][y] = popcount64(proj_yz[y] & proj_xz[p]);
    266     if (p != z) {
    267 	    if (proj_zx[p] & mask_x)
    268 		    new_delta += v_zx[p][x] - old_vzx2;
    269 	    if (proj_zy[p] & mask_y)
    270 		    new_delta += v_zy[p][y] - old_vzy2;
    271     }
    272     if (p != x) {
    273 	    if (proj_xz[p] & mask_z)
    274 		    new_delta += v_zx[z][p] - old_vzx1;
    275 	    if (proj_xy[p] & mask_y)
    276 		    new_delta += v_xy[p][y] - old_vxy2;
    277     }
    278     if (p != y) {
    279 	    if (proj_yz[p] & mask_z)
    280 		    new_delta += v_zy[z][p] - old_vzy1;
    281 	    if (proj_yx[p] & mask_x)
    282 		    new_delta += v_xy[x][p] - old_vxy1;
    283     }
    284   }
    285   new_delta += getV(z, x, y);
    286   return new_delta;
    287 }
    288 
    289 void num_attacks() {
    290   // generate A from scratch
    291   for (int i = 0; i < N; i++)
    292     for (int j = 0; j < N; j++)
    293       for (int k = 0; k < N; k++)
    294         A[i][j][k] = 0;
    295   for (int i = 0; i < K; i++) {
    296     if (R[i][0] < MAXN)
    297       upd_atk_add(i);
    298   }
    299 }
    300 
    301 void check_attacks() {
    302   // debug: regenerate A from scratch
    303   // and check that the previous version was already correct
    304   int backup[MAXN][MAXN][MAXN];
    305   for (int i = 0; i < N; i++)
    306     for (int j = 0; j < N; j++)
    307       for (int k = 0; k < N; k++)
    308         backup[i][j][k] = A[i][j][k];
    309   num_attacks();
    310   for (int i = 0; i < N; i++)
    311     for (int j = 0; j < N; j++)
    312       for (int k = 0; k < N; k++)
    313         if (backup[i][j][k] != A[i][j][k]) {
    314           print_attacks();
    315           printf("ERROR at %d %d %d\n", i, j, k);
    316           exit(1);
    317         }
    318 }
    319 
    320 int num_violations() {
    321   // regenerate V from scratch
    322   // returns total number of violations
    323   // TODO: find a way to update this rather than recompute
    324   int tot = 0;
    325   for (int i = 0; i < N; i++)
    326     for (int j = 0; j < N; j++)
    327       for (int k = 0; k < N; k++)
    328         V[i][j][k] = 0; // clear
    329   for (int z = 0; z < N; z++)
    330     for (int x = 0; x < N; x++)
    331       for (int y = 0; y < N; y++) {
    332         for (int p = 0; p < N; p++) {
    333           // must count cells that have already two attack directions and are
    334           // missing the attack direction from the current cell
    335           if (p != z && (A[p][x][y] | (1 << 0)) == 7)
    336           {V[z][x][y]++;} // putting rook in z x y would attack p x y thrice
    337           if (p != x && (A[z][p][y] | (1 << 1)) == 7)
    338           {V[z][x][y]++;} // putting rook in z x y would attack z p y thrice
    339           if (p != y && (A[z][x][p] | (1 << 2)) == 7)
    340           {V[z][x][y]++;} // putting rook in z x y would attack z x p thrice
    341         }
    342         if (G[z][x][y]) {
    343           tot += V[z][x][y];
    344           //printf("add %d to num_violations for %d %d %d\n", V[z][x][y], x, y, z);
    345         }
    346       }
    347   return tot;
    348 }
    349 
    350 void check_increment(int ntot) {
    351   //printf("CHECK: before\n");
    352   //print_all_violations();
    353   // debug: regenerate A, V from scratch
    354   // and check that the previous version was already correct
    355   int backupA[MAXN][MAXN][MAXN];
    356   int backupV[MAXN][MAXN][MAXN];
    357   for (int i = 0; i < N; i++)
    358     for (int j = 0; j < N; j++)
    359       for (int k = 0; k < N; k++) {
    360         backupA[i][j][k] = A[i][j][k];
    361         backupV[i][j][k] = V[i][j][k];
    362       }
    363   num_attacks();
    364   int rntot = num_violations();
    365   //printf("CHECK: after\n");
    366   //print_all_violations();
    367   for (int i = 0; i < N; i++)
    368     for (int j = 0; j < N; j++)
    369       for (int k = 0; k < N; k++) {
    370         if (backupA[i][j][k] != A[i][j][k]) {
    371           print_attacks();
    372           printf("ERROR attacks at %d %d %d\n", i, j, k);
    373           exit(1);
    374         }
    375         if (backupV[i][j][k] != V[i][j][k]) {
    376           print();
    377           print_attacks();
    378           print_all_violations();
    379           printf("ERROR violations at z=%d x=%d y=%d, had %d computed %d\n", i, j, k, backupV[i][j][k], V[i][j][k]);
    380           exit(1);
    381         }
    382       }
    383   if (ntot != rntot) {
    384     print_all_violations();
    385     print_violations();
    386     printf("total violation miscount, had %d computed %d\n", ntot, rntot);
    387     exit(1);
    388   }
    389 }
    390 
    391 
    392 void init_pos() {
    393   // create initial position
    394   for (int i = 0; i < N; i++)
    395     for (int j = 0; j < N; j++)
    396       for (int k = 0; k < N; k++)
    397         G[i][j][k] = 0;
    398   int remaining = K;
    399   int height = 0, pos = 0;
    400   while (remaining > 0) {
    401     int z = height;
    402     int x = pos;
    403     int y = (pos + height) % N;
    404     G[z][x][y] = 1;
    405     remaining--;
    406     R[remaining][0] = z;
    407     R[remaining][1] = x;
    408     R[remaining][2] = y;
    409     pos++;
    410     if (pos >= N) {
    411       pos = 0;
    412       height++;
    413     }
    414   }
    415 }
    416 
    417 int choose_rook(int prev) {
    418   // choose the next rook to move: a random one among the ones with maximal
    419   // violations, forbiding prev (the previously moved rook)
    420   // TODO: better notion including "age" of rook to favor moving old rooks (?)
    421   // and small probability to move rooks with not exactly maximal num of viols
    422   // TODO: make it faster (priority queue?)
    423   int best = -1;
    424   int nvs[MAXN*MAXN];
    425   int nnvs = 0;
    426   int nv;
    427   for (int i = 0; i < K; i++) {
    428     if (i == prev)
    429       continue;
    430     if ((nv = getV(R[i][0], R[i][1], R[i][2])) > best) {
    431       best = nv;
    432       nvs[0] = i;
    433       nnvs = 1;
    434     } else if (nv == best) {
    435       nvs[nnvs++] = i;
    436     }
    437   }
    438   // choose random rook among the optimal ones
    439   return nvs[rand() % nnvs];
    440 }
    441 
    442 int random_move() {
    443   // make a totally random move (when we are stuck)
    444   int rook = (rand() % K);
    445   //printf("move %d at random\n", rook);
    446   int z = R[rook][0];
    447   int x = R[rook][1];
    448   int y = R[rook][2];
    449   int delta = 0;
    450   G[z][x][y] = 0;
    451   delta += upd_atk_rm(rook);
    452   int nx, ny, nz;
    453   uint64_t mask_x, mask_y;
    454   do {
    455     nx = (rand() % N);
    456     ny = (rand() % N);
    457     nz = (rand() % N);
    458     mask_x = 1ull << x;
    459     mask_y = 1ull << y;
    460     if (!A[nz][nx][ny] && !G[nz][nx][ny]) {
    461 #if 0
    462 	    printf("proj_zy[nz]:%lx, mask_y: %lx:%lx -- ", proj_zy[nz], mask_y, proj_zy[nz] & mask_y);
    463 	    printf("proj_xy[nx]:%lx, mask_y: %lx:%lx -- ", proj_xy[nx], mask_y, proj_xy[nx] & mask_y);
    464 	    printf("proj_zx[nz]:%lx, mask_x: %lx:%lx\n", proj_zx[nz], mask_x, proj_zx[nz] & mask_x);
    465 #endif
    466       break; // found a spot that's both empty and not in direct attack
    467     }
    468     // TODO: nothing tests if this loop stands a chance of terminating, it would
    469     // be neater to exit the program then
    470   } while (1); // ((proj_zy[nz] & mask_y) || (proj_xy[nx] & mask_y) || (proj_zx[nz] & mask_x));
    471   G[nz][nx][ny] = 1;
    472   R[rook][0] = nz;
    473   R[rook][1] = nx;
    474   R[rook][2] = ny;
    475   delta += upd_atk_add(rook);
    476   return delta;
    477 }
    478 
    479 int position(int i) {
    480 	// position rook i that was just removed
    481 	// TODO: more efficient (pqueue?)
    482 	// TODO more clever (chance to select cell with non-optimal number of
    483 	// violations)
    484 	int best = N*N*N+1;
    485 	int bs[MAXN*MAXN*MAXN][3];
    486 	int nbs = 0;
    487 	int v_zxy;
    488 	uint64_t mask_x, mask_y;
    489 	for (int z = 0; z < N; z++) {
    490 		for (int x = 0; x < N; x++) {
    491 			mask_x = 1ull << x;
    492 			if((proj_zx[z] & mask_x) || (v_zx[z][x] > best))
    493 				{continue;}
    494 			for (int y = 0; y < N; y++) {
    495 				mask_y = 1ull << y;
    496 				if ((proj_zy[z] | proj_xy[x]) & mask_y)
    497 					{continue;}
    498 				// getVEmpty, because we know that the cell is
    499 				// not attacked, thus empty.
    500 				v_zxy = getVEmpty(z, x, y);
    501 				if (v_zxy < best) {
    502 				    	best = v_zxy;
    503 					bs[0][0] = z;
    504 					bs[0][1] = x;
    505 					bs[0][2] = y;
    506 				    	nbs = 1;
    507 					if (debug)
    508 						printf("%d %d %d is a fitting position\n", z, x, y);
    509 				}
    510 				else if (v_zxy == best) {
    511 				    	bs[nbs][0] = z;
    512 				    	bs[nbs][1] = x;
    513 				    	bs[nbs][2] = y;
    514 				    	nbs++;
    515 				    	if (debug)
    516 						printf("%d %d %d is a fitting position\n", z, x, y);
    517 				}
    518 			}
    519 		}
    520 	}
    521 	// choose random cell among optimal ones
    522 	int npos = rand() % nbs;
    523 	int bz = bs[npos][0];
    524 	int bx = bs[npos][1];
    525 	int by = bs[npos][2];
    526 	if(debug) printf("position %d to %d %d %d with viol %d\n", i, bx, by, bz, getV(bz, by, bx));
    527 	G[bz][bx][by] = 1;
    528 	R[i][0] = bz;
    529 	R[i][1] = bx;
    530 	R[i][2] = by;
    531 	return upd_atk_add(i);
    532 }
    533 
    534 int main(int argc, char **argv) {
    535   int oK;
    536   int stopK;
    537   int first = 1;
    538   int seed;
    539   N = atoi(argv[1]);
    540   oK = atoi(argv[2]);
    541   stopK = atoi(argv[3]);
    542   if (argc == 5)
    543     seed = atoi(argv[4]);
    544   else {
    545     seed = time(NULL);
    546   }
    547   printf("using random seed %d\n", seed);
    548   srand(seed);
    549   signal(SIGINT, intHandler);
    550 
    551   int tot, otot;
    552   for (K = oK; K < stopK; K++) {
    553     printf("try %d rooks\n", K);
    554     // starting position if we just started, otherwise add the rook to the
    555     // existing position
    556     if (first) {
    557       init_pos();
    558       first = 0;
    559       num_attacks();
    560       tot = num_violations();
    561     } else {
    562       tot += position(K-1);
    563     }
    564 
    565     int useless = 0;
    566     int prev_rook = -1;
    567 
    568     // improve while violations remain
    569     while (tot > 0) {
    570       if (debug) print_violations();
    571 
    572       // choose a rook
    573       int rook = choose_rook(prev_rook);
    574       prev_rook = rook;
    575 
    576       // when doing a useless step, offset the choice
    577       //rook = (rook + useless1) % K;
    578       if(debug) printf("chosen rook %d\n", rook);
    579       if(debug) print();
    580       if(debug) print_attacks();
    581       int z = R[rook][0];
    582       int x = R[rook][1];
    583       int y = R[rook][2];
    584 
    585       // remove the rook
    586       //printf("tot is %d will rm %d\n", tot, rook);
    587       G[z][x][y] = 0;
    588       otot = tot;
    589       tot += upd_atk_rm(rook);
    590       //printf("tot is %d after rm\n", tot);
    591       R[rook][0] = MAXN;
    592       R[rook][1] = MAXN;
    593       R[rook][2] = MAXN;
    594 
    595       if(debug) print_attacks();
    596       //check_increment();
    597 
    598       // recompute V without the rook
    599       //num_violations();
    600       //printf("check_incr\n");
    601       //check_increment(tot);
    602 
    603       // choose next position
    604       tot += position(rook);
    605 
    606       int pred = V[R[rook][0]][R[rook][1]][R[rook][2]];
    607       if(debug) print();
    608       if(debug) print_all_violations();
    609 
    610       // recompute number of violations
    611       //tot = num_violations();
    612 
    613       if(debug) print_all_violations();
    614       int npred = V[R[rook][0]][R[rook][1]][R[rook][2]];
    615       if (npred != pred) {
    616         printf("violation mispredict: thought %d and had %d!\n", pred, npred);
    617         exit(1);
    618       }
    619       if(debug) print_violations();
    620       if(debug) print_attacks();
    621       if(debug) print();
    622       //printf("total viol %d\n", tot);
    623 
    624       if (tot >= otot) {
    625         // no improvement at this round
    626         useless++;
    627       } else {
    628         useless = 0;
    629       }
    630       if (useless > UBOUND) {
    631         // too many useless steps, do a random move
    632         useless = 0;
    633         do {
    634           tot += random_move();
    635         } while ((rand() % C2) < C1);
    636       }
    637     }
    638     // we have found a configuration for K rooks; display it
    639     printf("!!! DONE %d rooks\n", K);
    640     print();
    641     fflush(stdout);
    642   }
    643   return 0;
    644 }
    645 
    646