Skip to content

Instantly share code, notes, and snippets.

@erantapaa
Last active September 11, 2017 06:09
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save erantapaa/cb275219ebe931318614e532bdb8e5e7 to your computer and use it in GitHub Desktop.
Save erantapaa/cb275219ebe931318614e532bdb8e5e7 to your computer and use it in GitHub Desktop.
minichess board positions
/*
* "3x3c.c" - source file of the 3x3 chess solver, version 1.0.0
*
* Copyright (c) 2004-2011 Kirill Kryukov
*
* This software is provided 'as-is', without any express or implied warranty.
* In no event will the authors be held liable for any damages arising from
* the use of this software.
*
* Permission is granted to anyone to use this software for any purpose,
* including commercial applications, and to alter it and redistribute it
* freely, subject to the following restrictions:
*
* 1. The origin of this software must not be misrepresented; you must
* not claim that you wrote the original software. If you use this
* software in a product, an acknowledgment in the product
* documentation would be appreciated but is not required.
*
* 2. Altered source versions must be plainly marked as such, and must not
* be misrepresented as being the original software.
*
* 3. This notice may not be removed or altered from any source distribution.
*/
/*
* 3x3 chess: pawns are allowed on the first rank. No castling, no double pawn
* step.
*
* In drawn positions stalemate is used as a goal (Assuming it's better to
* stalemate than to be stalemated).
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include "move.c"
const int kingw[17] = {0,0,0,0,0,1,1,3,3,3,6,6,6,6,6,7,7};
const int kingb[17] = {2,5,6,7,8,6,7,2,5,8,0,1,2,5,8,0,1};
const int pagenum[81] =
{
0, 0, 1, 0, 0, 2, 3, 4, 5,
0, 0, 0, 0, 0, 0, 6, 7, -6,
-1, 0, 0, -2, 0, 0, -5, -4, -3,
0, 0, 8, 0, 0, 9, 0, 0, 10,
0, 0, 0, 0, 0, 0, 0, 0, 0,
-8, 0, 0, -9, 0, 0,-10, 0, 0,
11, 12, 13, 0, 0, 14, 0, 0, 15,
16, 17,-16, 0, 0, 0, 0, 0, 0,
-13,-12,-11,-14, 0, 0,-15, 0, 0
};
const int location[17][7] =
{
{ 1, 3,4,5,6,7,8},
{ 1,2,3,4, 6,7,8},
{ 1,2,3,4,5, 7,8},
{ 1,2,3,4,5,6, 8},
{ 1,2,3,4,5,6,7 },
{0, 2,3,4,5, 7,8},
{0, 2,3,4,5,6, 8},
{0,1, 4,5,6,7,8},
{0,1,2, 4, 6,7,8},
{0,1,2, 4,5,6,7 },
{ 1,2,3,4,5, 7,8},
{0, 2,3,4,5, 7,8},
{0,1, 3,4,5, 7,8},
{0,1,2,3,4, 7,8},
{0,1,2,3,4,5, 7 },
{ 1,2,3,4,5,6, 8},
{0, 2,3,4,5,6, 8}
};
const int vflip[9] = {6,7,8,3,4,5,0,1,2};
const int hflip[9] = {2,1,0,5,4,3,8,7,6};
const int pagebytes = 19487171;
const int dbsize = 17*19487171;
const char piece_to_index[16] = {0,0,1,2,3,4,5,0,0,0,6,7,8,9,10,0};
const char index_to_piece[11] = {0,2,3,4,5,6,10,11,12,13,14};
const char filechar[9]={'a','b','c','a','b','c','a','b','c'};
const char rankchar[9]={'3','3','3','2','2','2','1','1','1'};
const char piece_to_char[16] = {'.','K','Q','R','B','N','P','?','?','k','q','r','b','n','p','?'};
const char char_to_piece[256] =
{
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15, 0,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15, 4,15,15,15,15,15,15,15,15, 1,15,15, 5,15,
6, 2, 3,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,12,15,15,15,15,15,15,15,15, 9,15,15,13,15,
14,10,11,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,
15,15,15,15,15,15,15,15,15,15,15,15,15,15,15,15
};
char* pages[17] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
int wk,bk;
int page;
char* pagebase;
int pieces[7] = {0,0,0,0,0,0,0};
int board[9] = {0,0,0,0,0,0,0,0,0};
int ofs = 0;
int wk1,bk1;
int board1[9];
int found = 0;
int illegal_count = 0;
int checkmate_count = 0;
int stalemate_count = 0;
char* dbname = "db.dat";
FILE *db = 0;
int sideswap = 0;
#define swap(a,b) { int c=a; a=b; b=c; }
static void print_time() { int sec = clock()/CLOCKS_PER_SEC; printf(" (%02d:%02d:%02d)\n",sec/(60*60),(sec%(60*60))/60,sec%60); }
static inline void b2w()
{
swap(wk1,bk1);
for (int i=0; i<9; i++) if (board1[i]) board1[i]^=8;
wk1 = vflip[wk1];
bk1 = vflip[bk1];
swap(board1[0],board1[6]);
swap(board1[1],board1[7]);
swap(board1[2],board1[8]);
}
static inline int fixpage()
{
int p = pagenum[wk1*9+bk1];
if (p<0)
{
swap(board1[0],board1[2]);
swap(board1[3],board1[5]);
swap(board1[6],board1[8]);
wk1 = hflip[wk1];
bk1 = hflip[bk1];
return -p-1;
}
else return p-1;
}
static inline int getofs()
{
int ofs1 = 0;
for (int j=0; j<9; j++) if ((board1[j]&7)!=1) ofs1 = ofs1*11 + piece_to_index[board1[j]];
return ofs1;
}
static inline char lookup() { b2w(); int p = fixpage(); return pages[p][getofs()]; }
static inline void push() { wk1 = wk; bk1 = bk; memcpy(board1,board,36); }
static inline void pop() { wk = wk1; bk = bk1; memcpy(board,board1,36); }
static inline void mark_squares_attacked_by_black(int *a)
{
for (int i=0; i<9; i++)
if (board[i]>=9)
{
const int *am = move[board[i]][i];
for (int t=0; t<9; t++)
if (am[t]&2) { int x = am[t]>>2; if (!x || !board[x]) a[t] = 1; }
}
}
static inline void mark_squares_attacked_by_white(int *a)
{
for (int i=0; i<9; i++)
if (board[i]>=1 && board[i]<=6)
{
const int *am = move[board[i]][i];
for (int t=0; t<9; t++)
if (am[t]&2) { int x = am[t]>>2; if (!x || !board[x]) a[t] = 1; }
}
}
static int white_safe()
{
if (board[0]==6 || board[1]==6 || board[2]==6 || board[6]==14 || board[7]==14 || board[8]==14) return 0;
int a[9] = {0,0,0,0,0,0,0,0,0};
mark_squares_attacked_by_black(a);
if (a[wk]) {
return 0;
}
return 1;
}
static int black_safe()
{
if (board[0]==6 || board[1]==6 || board[2]==6 || board[6]==14 || board[7]==14 || board[8]==14) return 0;
int a[9] = {0,0,0,0,0,0,0,0,0};
mark_squares_attacked_by_white(a);
if (a[bk]) {
return 0;
}
return 1;
}
static int illegal()
{
int a[9] = {0,0,0,0,0,0,0,0,0};
mark_squares_attacked_by_white(a);
if (a[bk] || board[0]==6 || board[1]==6 || board[2]==6 || board[6]==14 || board[7]==14 || board[8]==14) return -47;
return 0;
}
static int checkmate_or_stalemate()
{
int a[9] = {0,0,0,0,0,0,0,0,0};
int attackers = 0;
int attacker_location = 0;
int pinned[9] = {0,0,0,0,0,0,0,0,0};
int who_is_pinning[9] = {0,0,0,0,0,0,0,0,0};
for (int i=0; i<9; i++)
if (board[i]>=9)
{
const int *am = move[board[i]][i];
for (int t=0; t<9; t++)
{
int m = am[t];
if (!(m&2)) continue;
int x = m>>2;
if (x && board[x]) { if (t==wk && !(board[x]>>3)) { pinned[x] = 1; who_is_pinning[x] = i; } continue; }
a[t] = 1;
if (t==wk) { attackers++; attacker_location = i; }
}
}
for (int k=0; k<9; k++)
if (move[1][wk][k] && !a[k] && (!board[k] || board[k]>=10))
{
push();
board1[wk1] = 0;
wk1 = k;
board1[wk1] = 1;
char d = lookup();
if (d>=-46) return 0;
}
if (attackers==1)
{
int d = move[board[attacker_location]][attacker_location][wk]>>2;
for (int j=0; j<9; j++)
if (board[j]>=2 && board[j]<=6 && !pinned[j])
{
int m = move[board[j]][j][attacker_location];
if (m&2) { int x = m>>2; if (!x || !board[x]) return 0; }
if (d) { m = move[board[j]][j][d]; if (m&1) { int x = m>>2; if (!x || !board[x]) return 0; } }
}
}
if (attackers==0)
for (int j=0; j<9; j++)
if (board[j]>=2 && board[j]<=6)
{
const int *bm = move[board[j]][j];
if (pinned[j]) { if (bm[who_is_pinning[j]]&2) return 0; }
else for (int t=0; t<9; t++)
{
int tp = board[t];
int m = bm[t];
if (!m || (tp>=1 && tp<=6)) continue;
if (!tp && !(m&1)) continue;
if (tp && !(m&2)) continue;
int x = m>>2;
if (x && board[x]) continue;
return 0;
}
}
return a[wk]?-46:-23;
}
static int analyze()
{
int mn = 47, attacked[9] = {0,0,0,0,0,0,0,0,0};
mark_squares_attacked_by_black(attacked);
const int *mm = move[1][wk];
for (int t=0; t<9; t++)
if (mm[t] && !attacked[t] && (board[t]<1 || board[t]>6))
{
push(); board1[wk1] = 0; wk1 = t; board1[wk1] = 1;
char d = lookup(); if (d>-47 && d<mn) mn = d;
}
for (int s=0; s<9; s++)
{
int sp = board[s];
if (sp>=2 && sp<=6)
{
mm = move[sp][s];
for (int t=0; t<9; t++)
{
int tp = board[t];
int m = mm[t];
if (!m || (tp>=1 && tp<=6)) continue;
if (!tp && !(m&1)) continue;
if (tp && !(m&2)) continue;
int x = m>>2;
if (x && board[x]) continue;
if (sp==6 && t<=2)
for (int pp=2; pp<6; pp++)
{
push(); board1[t] = pp; board1[s] = 0;
char d = lookup(); if (d>-47 && d<mn) mn = d;
}
else
{
push(); board1[t] = board[s]; board1[s] = 0;
char d = lookup(); if (d>-47 && d<mn) mn = d;
}
}
}
}
int res = (mn==47)?47:((mn>0)?1-mn:-mn);
return (res==found)?res:0;
}
static void cycle_init()
{
wk = kingw[page]; bk = kingb[page]; pagebase = pages[page];
memset(pieces,0,28); memset(board,0,36);
board[wk] = 1; board[bk] = 9;
ofs = 0;
}
int iterate()
{
ofs++;
if (ofs>=pagebytes) return 0;
int i = 6;
while (i>=0 && pieces[i]==10) i--;
if (i==-1) return 0;
pieces[i]++;
board[location[page][i]] = index_to_piece[pieces[i]];
while (i<6)
{
i++;
pieces[i] = 0;
board[location[page][i]] = 0;
}
return 1;
}
void one_phase(int start)
{
int iteration = 1, changed = 1, analyzed;
found = start;
while (changed)
{
changed = 0;
analyzed = 0;
printf(" %2d ",iteration);
for (page=0; page<17; page++)
{
printf("*"); cycle_init();
do {
if (!pagebase[ofs])
{
analyzed++;
char r = analyze();
if (r) { changed++; pagebase[ofs] = r; }
}
} while (iterate());
}
printf(" %9d %9d",changed,analyzed); print_time();
found = found>0?1-found:-found;
iteration++;
}
}
void count_valid()
{
for (int a=0; a<17 && (pages[a]=(char*)malloc(pagebytes)); a++) {}
if (!pages[16]) { printf("fail\n"); return; }
printf("resetting ");
for (int s=0; s<17; s++) { printf("."); memset(pages[s],0,pagebytes); }
printf(" ok"); print_time();
int total_count = 0;
int total_black_safe = 0;
int total_both_safe = 0;
for (int wk0 = 0; wk0 < 9; ++wk0) {
for (int bk0 = 0; bk0 < 9; ++bk0) {
int p = pagenum[9*wk0+bk0];
if (p == 0) continue;
page = p < 0 ? -p-1 : p-1;
int page_total = 0;
int page_both_safe = 0;
int page_black_safe = 0;
cycle_init();
do {
page_total++;
if (black_safe()) {
page_black_safe++;
if (white_safe()) {
page_both_safe++;
}
}
} while (iterate());
printf("%d %d page: %2d total: %10d black_safe: %10d both_safe: %10d\n",
wk0, bk0, page, page_total, page_black_safe, page_both_safe);
total_count += page_total;
total_black_safe += page_black_safe;
total_both_safe += page_both_safe;
}
}
printf(" ok"); print_time();
printf("total : %11d\n", total_count);
printf("both : %11d\n", total_both_safe);
printf("black : %11d\n", total_black_safe);
}
void build()
{
printf("constructing database\n");
printf("allocating %d bytes .. ",17*pagebytes);
for (int a=0; a<17 && (pages[a]=(char*)malloc(pagebytes)); a++) {}
if (!pages[16]) { printf("fail\n"); return; }
printf("resetting ");
for (int s=0; s<17; s++) { printf("."); memset(pages[s],0,pagebytes); }
printf(" ok"); print_time();
printf("marking illegal positions ");
int total_count = 0;
for (page=0; page<17; page++)
{
printf("."); cycle_init();
do { total_count++; if (illegal()) { illegal_count++; pagebase[ofs] = -47; } } while (iterate());
}
printf(" ok"); print_time();
printf("total: %d, illegal: %d, legal: %d\n", total_count, illegal_count, total_count - illegal_count);
return;
printf("marking checkmates ");
for (page=0; page<17; page++)
{
printf("."); cycle_init();
do { if (!pagebase[ofs] && checkmate_or_stalemate()==-46) { checkmate_count++; pagebase[ofs] = -46; } } while (iterate());
}
printf(" ok"); print_time();
printf("%d illegal, %d checkmates, %d normal positions\n",
illegal_count,checkmate_count,dbsize-illegal_count-checkmate_count);
printf(" _________________ changed analyzed\n");
one_phase(46);
printf(" ");
for (page=0; page<17; page++)
{
printf("."); cycle_init();
do if (!pagebase[ofs] && checkmate_or_stalemate()==-23) { stalemate_count++; pagebase[ofs] = -23; }
while (iterate());
}
printf(" %9d stalemates",stalemate_count); print_time();
one_phase(23);
printf("writing the database file \"%s\" (330 MB)",dbname);
if (!(db=fopen(dbname,"wb"))) { printf(" - can't create file\n"); }
else
{
for (int y=0; y<17; y++)
for (int z=0; z<pagebytes; z++)
pages[y][z] += 79;
int w=0;
for (int t=0; t<17; t++) { printf("."); w += fwrite(pages[t],1,pagebytes,db); }
fclose(db); db = 0;
printf(w==dbsize?"ok":"fail"); print_time();
}
}
char lookup_file(int side)
{
if (!db) { db = fopen(dbname,"rb"); if (!db) return 100; }
if (side) b2w();
fixpage();
int offset = (pagenum[wk1*9+bk1]-1)*pagebytes + getofs();
char a;
fseek(db,offset,SEEK_SET);
fread(&a,1,1,db);
return a-79;
}
int load_position(char* s)
{
int hexnumber = -1;
if (strlen(s)>2 && s[0]=='0' && s[1]=='x')
{
hexnumber = strtol(s+2,0,16);
if (hexnumber<0 || hexnumber>331281907) { printf(" position index must be in range 0x0..0x13BEF5F3\n"); return 0; }
sideswap = 0;
int pgnum = hexnumber/pagebytes;
wk1 = kingw[pgnum];
bk1 = kingb[pgnum];
printf("%d -> page %d (wk at %d, bk at %d)\n",hexnumber,pgnum,wk1,bk1);
board1[wk1] = 1;
board1[bk1] = 9;
int a = hexnumber - pgnum*pagebytes;
for (int i=0; i<7; i++)
{
int b = a%11;
printf("%d -> %d at %d\n",a,b,location[pgnum][6-i]);
board1[location[pgnum][6-i]] = index_to_piece[b];
a = a/11;
}
}
if (hexnumber<0)
{
if (strlen(s)!=10 ) { printf(" position description must be 10 characters long\n"); return 0; }
if (*s!='w' && *s!='b') { printf(" first character must be 'w' or 'b'\n"); return 0; }
sideswap = (*s=='b');
for (int i=0; i<9; i++) board1[i] = char_to_piece[(unsigned char)s[i+1]];
}
{ printf(" %c ",sideswap?'b':' '); for (int i=0; i<3; i++) printf(" %c",piece_to_char[board1[i]]); printf("\n"); }
{ printf(" "); for (int i=3; i<6; i++) printf(" %c",piece_to_char[board1[i]]); printf("\n"); }
{ printf(" %c ",sideswap?' ':'w'); for (int i=6; i<9; i++) printf(" %c",piece_to_char[board1[i]]); printf("\n"); }
int wn=0,bn=0,in=0;
for (int c=0; c<9; c++)
{ if (board1[c]==15) in++; if (board1[c]==1) { wn++; wk1 = c; } if (board1[c]==9) { bn++; bk1 = c; } }
if (in) { printf(" illegal characters found (other than .KQRBNPkqrbnp)\n"); return 0; }
if (wn!=1 || bn!=1) { printf(" one white and one black king must be present\n"); return 0; }
if (!pagenum[wk1*9+bk1]) { printf(" illegal kings placement\n"); return 0; }
if (sideswap) b2w();
pop();
int res = lookup_file(0);
if (res==100) printf(" can't read file\n");
else if (res==-47) printf(" illegal position\n");
else if (res==0) printf(" draw\n");
else if (res>=1 && res<=23) printf(" draw (stalemate in %d)\n",24-res);
else if (res>=24 && res<=46) printf(" win in %d\n",47-res);
else if (res>=-22 && res<=-1) printf(" draw (get stalemated in %d)\n",res+23);
else if (res==-23) printf(" stalemate\n");
else if (res>=-45 && res<=-24) printf(" lose in %d\n",res+46);
else if (res==-46) printf(" checkmate\n");
else printf(" unknown\n");
return 1;
}
void print_move(int from,int to,char promote,int res)
{
printf(" %c",piece_to_char[board[from]]);
char mc = board[to]?'x':'-';
if (sideswap) { from = vflip[from]; to = vflip[to]; }
printf("%c%c%c%c%c",filechar[from],rankchar[from],mc,filechar[to],rankchar[to]);
if (promote) printf("%c",promote);
printf(" -> ");
if (res==-47) printf("illegal\n");
else if (res==0) printf("draw\n");
else if (res==-46) printf("checkmate\n");
else if (res==-23) printf("stalemate\n");
else if (res>=1 && res<=23) printf("draw (stalemated in %d)\n",24-res);
else if (res>=-22 && res<=-1) printf("draw (stalemate in %d)\n",res+24);
else if (res>=24 && res<=46) printf("loss in %d\n",47-res);
else if (res>=-45 && res<=-24) printf("win in %d\n",res+47);
}
void tell()
{
int attacked[9]={0,0,0,0,0,0,0,0,0};
mark_squares_attacked_by_black(attacked);
const int *mm = move[1][wk];
for (int t=0; t<9; t++)
if (mm[t] && !attacked[t] && !(board[t]>=2 && board[t]<=6))
{
push(); board1[wk1] = 0; wk1 = t; board1[wk1] = 1;
print_move(wk,t,0,lookup_file(1));
}
for (int s=0; s<9; s++)
{
int sp = board[s];
if (sp>=2 && sp<=6)
{
mm = move[sp][s];
for (int t=0; t<9; t++)
{
int m = mm[t];
if (!m) continue;
int tp = board[t];
if (tp && !(tp>>3)) continue;
if (!tp && !(m&1)) continue;
if (tp && !(m&2)) continue;
int x = m>>2;
if (x && board[x]) continue;
if (sp==6 && t<=2)
for (int pp=2; pp<6; pp++)
{
push(); board1[t] = pp; board1[s] = 0;
print_move(s,t,piece_to_char[pp],lookup_file(1));
}
else
{
push(); board1[t] = board[s]; board1[s] = 0;
print_move(s,t,0,lookup_file(1));
}
}
}
}
}
void done()
{
for (int d=0; d<17; d++) if (pages[d]) free(pages[d]);
if (db) fclose(db);
}
static char* helpstr =
"3x3 chess solver by Kirill Kryukov\n"
"\n"
" To build the database: 3x3c -build\n"
" To query a position: 3x3c <position>\n"
" To enter query mode: 3x3c\n"
"\n"
"You need 330 MB of RAM to construct the database\n"
"You need same amount of disk space to save it\n"
"\n"
"Position format = 10 characters:\n"
" First character: 'w' or 'b' (side to move).\n"
" The other nine characters describe the board.\n"
" Order of squares is: 1 2 3\n"
" 4 5 6\n"
" 7 8 9\n"
" Empty square is '.', piece are K,Q,R,B,N,P.\n"
" Capital letters for white pieces, small - for black.\n"
" Example position: wK.k..NqRn\n"
"\n"
"Without parameters solver enters the query mode, if the database is already built.\n"
"In the query mode it just waits for you to type positions.\n"
"Type 'quit' to finish.";
int main(int argc,char **argv)
{
clock();
atexit(done);
setbuf(stdout,0);
if (argc==2 && (!strcmp(argv[1],"-h") || !strcmp(argv[1],"-help"))) { puts(helpstr); return 0; }
if (argc==2 && !strcmp(argv[1],"-count")) { count_valid(); return 0; }
if (argc==2 && !strcmp(argv[1],"-build")) { build(); return 0; }
if (argc>2) { puts(helpstr); return 1; }
if (!(db=fopen(dbname,"rb"))) { printf("database is not constructed yet\ntype \"3x3c -h\" for help\n"); return 1; }
if (argc==2) { if (load_position(argv[1])) { tell(); return 0; } else return 1; }
if (argc>1) { printf("type \"3x3c -h\" for help\n"); return 1; }
printf("input a position, or type 'help' or 'exit'\n");
char s[150]; printf("> ");
while (gets(s))
{
if (!strcmp(s,"quit")) break;
if (!strcmp(s,"help")) puts(helpstr);
else if (*s && load_position(s)) tell();
printf("> ");
}
return 0;
}
resetting ................. ok (00:00:00)
0 2 page: 0 total: 19487171 black_safe: 4942080 both_safe: 1644672
0 5 page: 1 total: 19487171 black_safe: 3128832 both_safe: 1147608
0 6 page: 2 total: 19487171 black_safe: 5559840 both_safe: 1931580
0 7 page: 3 total: 19487171 black_safe: 4105728 both_safe: 1586304
0 8 page: 4 total: 19487171 black_safe: 5559840 both_safe: 1966300
1 6 page: 5 total: 19487171 black_safe: 6040320 both_safe: 1586304
1 7 page: 6 total: 19487171 black_safe: 3779136 both_safe: 823543
1 8 page: 5 total: 19487171 black_safe: 6040320 both_safe: 1586304
2 0 page: 0 total: 19487171 black_safe: 4942080 both_safe: 1644672
2 3 page: 1 total: 19487171 black_safe: 3128832 both_safe: 1147608
2 6 page: 4 total: 19487171 black_safe: 5559840 both_safe: 1966300
2 7 page: 3 total: 19487171 black_safe: 4105728 both_safe: 1586304
2 8 page: 2 total: 19487171 black_safe: 5559840 both_safe: 1931580
3 2 page: 7 total: 19487171 black_safe: 4818528 both_safe: 937916
3 5 page: 8 total: 19487171 black_safe: 2612736 both_safe: 420175
3 8 page: 9 total: 19487171 black_safe: 5436288 both_safe: 1147608
5 0 page: 7 total: 19487171 black_safe: 4818528 both_safe: 937916
5 3 page: 8 total: 19487171 black_safe: 2612736 both_safe: 420175
5 6 page: 9 total: 19487171 black_safe: 5436288 both_safe: 1147608
6 0 page: 10 total: 19487171 black_safe: 4928040 both_safe: 1348956
6 1 page: 11 total: 19487171 black_safe: 3244032 both_safe: 1002064
6 2 page: 12 total: 19487171 black_safe: 4942080 both_safe: 1404500
6 5 page: 13 total: 19487171 black_safe: 3128832 both_safe: 937916
6 8 page: 14 total: 19487171 black_safe: 5575680 both_safe: 1644672
7 0 page: 15 total: 19487171 black_safe: 5353920 both_safe: 1002064
7 1 page: 16 total: 19487171 black_safe: 2985984 both_safe: 420175
7 2 page: 15 total: 19487171 black_safe: 5353920 both_safe: 1002064
8 0 page: 12 total: 19487171 black_safe: 4942080 both_safe: 1404500
8 1 page: 11 total: 19487171 black_safe: 3244032 both_safe: 1002064
8 2 page: 10 total: 19487171 black_safe: 4928040 both_safe: 1348956
8 3 page: 13 total: 19487171 black_safe: 3128832 both_safe: 937916
8 6 page: 14 total: 19487171 black_safe: 5575680 both_safe: 1644672
ok (00:02:45)
total : 623589472
both : 40660996
black : 145518672
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment