#include #include #include #include #include #include extern const char *__progname; #ifdef NO_PROGNAME const char *__progname; int main(int, char **); int main_(int, char **); int main(int ac, char **av){__progname=av[0];return(main_(ac,av));} #define main main_ #endif #define CARD_SUIT(c) (((unsigned int)(c))%4) #define CARD_VALUE(c) (((unsigned int)(c))/4) #define CARD_MAKE(s,v) ((s)+((v)*4)) /* Must be kept in sync with serialize_state_data() */ #define MAX_SERIAL (5+11 /* constant */ + 44 /* 52 cards at 4 bytes per 5 */) #define MIN(a,b) (((a)<(b)) ? (a) : (b)) typedef struct state STATE; struct state { unsigned char svalid; unsigned char hh[4]; unsigned char freework; signed char work[4]; unsigned char ph[10]; unsigned char pc[10][20]; unsigned char loc[52]; #define L_HOME() 0 #define L_WORK(i) ((i)+1) #define L_PILE(p,cwp) (((p)*20)+(cwp)+5) #define L_ISHOME(x) ((x)==0) #define L_ISWORK(x) ((unsigned int)((x)-1)<4) #define L_ISPILE(x) ((x)>4) #define L_WORK_PILE(x) ((x)-1) #define L_PILE_PILE(x) (((x)-5)/20) #define L_PILE_CWP(x) (((x)-5)%20) #define L_BADLOC(x) (((unsigned int)(x))>=220) #define L_SAMELOC(x,y) (((x)==(y))||(L_ISWORK(x)&&L_ISWORK(y))) unsigned char serial[MAX_SERIAL]; } ; typedef struct node NODE; struct node { unsigned char type; #define T_LEAF 1 #define T_SPLIT 2 union { unsigned char *leaf; NODE **split; } u; } ; typedef struct move MOVE; struct move { MOVE *link; unsigned char card; unsigned char to; } ; #define SPLITSIZE 16 #define SPLITINX(vec,inx) (((vec)[((inx)>>1)]>>(((inx)&1)?4:0))&0xf) static NODE *tree = 0; static STATE startstate; static unsigned long int crctable[256] = { 0, 0 }; #define CRCPOLY 0xedb88320 static void initcrc(void) { int i; unsigned long int crc; int j; if (crctable[1] != 0) return; for (i=0;i<256;i++) { crc = i; for (j=0;j<8;j++) crc = (crc >> 1) ^ ((crc & 1) ? CRCPOLY : 0); crctable[i] = crc; } } /* This function must: - Put the total number of valid characters in buf[SS_LEN]; - Try to ensure that different serialized states differ early. We do the latter by storing a crc of the serialized state in buf[0..3]. */ #define SS_LEN 4 /* MAX_SERIAL must be kept in sync with this */ static void serialize_state_data(const STATE *s, unsigned char *buf) { unsigned char *bp; unsigned long int v; int w[4]; int pperm[10]; int i; int j; int p; int x; int any; static void card(int c) { v = (v * 52) + c; i ++; if (i >= 5) { *bp++ = (v ) & 0xff; *bp++ = (v >> 8) & 0xff; *bp++ = (v >> 16) & 0xff; *bp++ = (v >> 24) & 0xff; v = 0; i = 0; } } initcrc(); bp = buf + 5; *bp++ = (s->hh[0] << 4) | s->hh[1]; *bp++ = (s->hh[2] << 4) | s->hh[3]; for (i=0;i<4;i++) w[i] = s->work[i]; do { any = 0; for (i=0;i<3;i++) { if (w[i] > w[i+1]) { any = 1; x = w[i+1]; w[i+1] = w[i]; w[i] = x; } } } while (any); for (i=0;i<10;i++) pperm[i] = i; do { any = 0; for (p=0;p<9;p++) { j = s->ph[pperm[p]] - s->ph[pperm[p+1]]; if (j < 0) continue; if (j == 0) { for (i=s->ph[pperm[p]]-1;i>=0;i--) { j = s->pc[pperm[p]][i] - s->pc[pperm[p+1]][i]; if (j != 0) break; } if (j <= 0) continue; } any = 1; x = pperm[p]; pperm[p] = pperm[p+1]; pperm[p+1] = x; } } while (any); v = 0; for (i=0;i<4;i++) { v *= 53; if (w[i] >= 0) v += w[i] + 1; } *bp++ = (v ) & 0xff; *bp++ = (v >> 8) & 0xff; *bp++ = (v >> 16) & 0xff; v = 0; for (i=0;i<5;i++) v = (v * 20) + s->ph[pperm[i]]; *bp++ = (v ) & 0xff; *bp++ = (v >> 8) & 0xff; *bp++ = (v >> 16) & 0xff; v = 0; for (i=0;i<5;i++) v = (v * 20) + s->ph[pperm[i+5]]; *bp++ = (v ) & 0xff; *bp++ = (v >> 8) & 0xff; *bp++ = (v >> 16) & 0xff; v = 0; i = 0; for (p=0;p<10;p++) for (x=s->ph[pperm[p]]-1;x>=0;x--) card(s->pc[pperm[p]][x]); while (i > 0) card(i); buf[4] = bp - buf; v = 0; for (i=buf[4]-4;i>0;i--) v = (v >> 8) ^ crctable[(v^*--bp)&0xff]; buf[0] = (v ) & 0xff; buf[1] = (v >> 8) & 0xff; buf[2] = (v >> 16) & 0xff; buf[3] = (v >> 24) & 0xff; } static void serialize_state(STATE *s) { serialize_state_data(s,&s->serial[0]); s->svalid = 1; } static void check_serialize(STATE *s) { if (! s->svalid) serialize_state(s); } static unsigned long int rnd(void) { #define N 63 static unsigned long int state[N]; static int h1 = -1; static int h2; if (h1 < 0) { struct timeval tv; struct stat stb; int i; stat(".",&stb); gettimeofday(&tv,0); bcopy(&stb,&state[0],MIN(sizeof(state),sizeof(stb))); for (i=0;i0;i--) { if (++h1 >= N) h1 = 0; if (++h2 >= N) h2 = 0; state[h2] += state[h1]; } } if (++h1 >= N) h1 = 0; if (++h2 >= N) h2 = 0; return(state[h2]+=state[h1]); #undef N } /* These must stay in sync with scancards() */ static char suitchars[4] = "cdhs"; static char valchars[13] = "A23456789TJQK"; static const char *cardstring(int c) { #define NBUFS 32 static char bufs[NBUFS][10]; static int hand = 0; if (--hand < 0) hand = NBUFS - 1; if (c == -1) { strcpy(&bufs[hand][0],"--"); } else if ((c < 0) || (c >= 52)) { sprintf(&bufs[hand][0],"?%d?",c); } else { bufs[hand][0] = valchars[CARD_VALUE(c)]; bufs[hand][1] = suitchars[CARD_SUIT(c)]; bufs[hand][2] = '\0'; } return(&bufs[hand][0]); #undef NBUFS } static const char *locstring(int l, int full) { #define NBUFS 32 static char bufs[NBUFS][10]; static int hand = 0; if (--hand < 0) hand = NBUFS - 1; if (L_ISHOME(l)) { strcpy(&bufs[hand][0],"Home"); } else if (L_ISWORK(l)) { if (full) { sprintf(&bufs[hand][0],"Work%d",L_WORK_PILE(l)); } else { sprintf(&bufs[hand][0],"Work"); } } else { sprintf(&bufs[hand][0],"Pile%dc%d",L_PILE_PILE(l),L_PILE_CWP(l)); } return(&bufs[hand][0]); #undef NBUFS } static const char *statestring(STATE *s) { #define NBUFS 32 static char bufs[NBUFS][2048]; static int hand = 0; char *bp; int i; int j; if (--hand < 0) hand = NBUFS - 1; bp = &bufs[hand][0]; if (s->svalid) { sprintf(bp,"%02x%02x%02x%02x/%d ",s->serial[0],s->serial[1],s->serial[2],s->serial[3],s->serial[4]); } else { strcpy(bp,"(noser) "); } bp += strlen(bp); for (i=0;i<4;i++) { sprintf(bp,"%s",cardstring((s->hh[i]<1)?-1:CARD_MAKE(i,s->hh[i]-1))); bp += strlen(bp); } *bp++ = ' '; for (i=0;i<4;i++) { sprintf(bp,"%s",cardstring(s->work[i])); bp += strlen(bp); } for (i=0;i<10;i++) { *bp++ = i ? '/' : ' '; for (j=0;jph[i];j++) { sprintf(bp,"%s",cardstring(s->pc[i][j])); bp += strlen(bp); } } for (i=0;i<52;i++) { if (L_ISHOME(s->loc[i])) { j = (s->hh[CARD_SUIT(i)] <= CARD_VALUE(i)); } else if (L_ISWORK(s->loc[i])) { j = (s->work[L_WORK_PILE(s->loc[i])] != i); } else { j = (s->pc[L_PILE_PILE(s->loc[i])][L_PILE_CWP(s->loc[i])] != i); } if (j) { sprintf(bp,"<%s:%s>",cardstring(i),locstring(s->loc[i],1)); bp += strlen(bp); } } *bp = '\0'; return(&bufs[hand][0]); #undef NBUFS } /* This must stay in sync with suitchars[] and valchars[] */ static int scancards(const char *s, int *cvp, int ncv) { int i; int suit; int value; i = 0; while (1) { switch (*s++) { case 'A': case 'a': value = 0; break; case '2': value = 1; break; case '3': value = 2; break; case '4': value = 3; break; case '5': value = 4; break; case '6': value = 5; break; case '7': value = 6; break; case '8': value = 7; break; case '9': value = 8; break; case 'T': case 't': value = 9; break; case 'J': case 'j': value = 10; break; case 'Q': case 'q': value = 11; break; case 'K': case 'k': value = 12; break; default: value = -1; break; } if (value < 0) return(i); switch (*s++) { case 'C': case 'c': suit = 0; break; case 'D': case 'd': suit = 1; break; case 'H': case 'h': suit = 2; break; case 'S': case 's': suit = 3; break; default: suit = -1; break; } if (suit < 0) return(i); if (i < ncv) cvp[i] = CARD_MAKE(suit,value); i ++; } } static void setup_game(STATE *s, int ac, char **av) { int i; int j; int c; const char *badstr; for (i=0;i<4;i++) s->hh[i] = 0; s->work[2] = -1; s->work[3] = -1; s->freework = 12; if (ac < 2) { int sptr; char shuf[52]; for (i=0;i<52;i++) shuf[i] = i; for (i=52-1;i>0;i--) { j = rnd() % (i+1); if (j != i) { c = shuf[i]; shuf[i] = shuf[j]; shuf[j] = c; } } sptr = 51; #define GETCARD() (shuf[sptr--]) #define PEEKCARD() (shuf[sptr]) for (i=0;i<2;i++) { c = GETCARD(); s->work[i] = c; s->loc[c] = L_WORK(i); } for (i=0;i<10;i++) { s->ph[i] = 5; for (j=0;j<5;j++) { c = GETCARD(); s->pc[i][j] = c; s->loc[c] = L_PILE(i,j); } } if (sptr != -1) abort(); #undef GETCARD #undef PEEKCARD } else if (ac == 12) { int cards[5]; char seen[52]; bzero(&seen[0],52); if (scancards(av[1],&cards[0],2) != 2) { badstr = av[1]; goto usage; } s->work[0] = cards[0]; s->loc[cards[0]] = L_WORK(0); s->work[1] = cards[1]; s->loc[cards[1]] = L_WORK(1); s->work[2] = -1; s->work[3] = -1; seen[cards[0]] ++; seen[cards[1]] ++; for (i=0;i<10;i++) { s->ph[i] = 5; if (scancards(av[2+i],&cards[0],5) != 5) { badstr = av[2+i]; goto usage; } for (j=0;j<5;j++) { s->pc[i][j] = cards[j]; s->loc[cards[j]] = L_PILE(i,j); seen[cards[j]] ++; } } j = 0; for (i=0;i<52;i++) { switch (seen[i]) { case 0: fprintf(stderr,"%s: card %s doesn't appear\n",__progname,cardstring(i)); j ++; break; case 1: break; default: fprintf(stderr,"%s: card %s appears more than once\n",__progname,cardstring(i)); j ++; break; } } if (j) exit(1); } else { if (0) { usage:; fprintf(stderr,"%s: invalid argument `%s'\n",__progname,badstr); } fprintf(stderr,"Usage: %s work pile1 ... pile10\n",__progname); fprintf(stderr," or: %s\n",__progname); fprintf(stderr,"(work = 2 cards, each pile = 5 cards)\n"); exit(1); } s->svalid = 0; } static NODE *getnode(void) { static NODE *have; static int nhave = -1; if (nhave < 1) { have = (void *) sbrk(65536); nhave = 65536 / sizeof(NODE); } nhave --; return(have++); } static NODE **getsplit(void) { static NODE **have; static int nhave = -1; NODE **rv; int i; if (nhave < 1) { have = (void *) sbrk(65536); nhave = 65536 / (SPLITSIZE * sizeof(NODE *)); } nhave --; rv = have; for (i=0;itype) { case T_LEAF: if ( (n->u.leaf[SS_LEN] == s->serial[SS_LEN]) && !bcmp(n->u.leaf,&s->serial[0],s->serial[SS_LEN]) ) { reg_seen ++; return(1); } else { NODE *newleaf; newleaf = getnode(); newleaf->type = T_LEAF; newleaf->u.leaf = n->u.leaf; n->type = T_SPLIT; n->u.split = getsplit(); n->u.split[SPLITINX(newleaf->u.leaf,sx)] = newleaf; } /* fall through */ case T_SPLIT: tptr = &n->u.split[SPLITINX(s->serial,sx)]; break; default: abort(); break; } sx ++; } n = getnode(); n->type = T_LEAF; n->u.leaf = getnvec(s->serial[SS_LEN]); bcopy(&s->serial[0],n->u.leaf,s->serial[SS_LEN]); *tptr = n; reg_new ++; return(0); } static int move_card(STATE *s, int c, int to, int check) { int l; if (L_BADLOC(to)) abort(); l = s->loc[c]; if (L_BADLOC(l)) abort(); if (L_ISHOME(l)) { if (CARD_VALUE(c)+1 != s->hh[CARD_SUIT(c)]) { if (check) return(1); else abort(); } s->hh[CARD_SUIT(c)] --; } else if (L_ISWORK(l)) { if (c != s->work[L_WORK_PILE(l)]) { if (check) return(1); else abort(); } s->work[L_WORK_PILE(l)] = -1; s->freework |= 1 << L_WORK_PILE(l); } else { if ( (s->ph[L_PILE_PILE(l)] < 1) || (L_PILE_CWP(l)+1 != s->ph[L_PILE_PILE(l)]) || (s->pc[L_PILE_PILE(l)][L_PILE_CWP(l)] != c) ) { if (check) return(1); else abort(); } s->ph[L_PILE_PILE(l)] --; } if (L_ISHOME(to)) { if (CARD_VALUE(c) != s->hh[CARD_SUIT(c)]) { if (check) return(1); else abort(); } s->hh[CARD_SUIT(c)] ++; } else if (L_ISWORK(to)) { int i; switch (s->freework) { default: if (check) return(1); else abort(); break; case 1: case 3: case 5: case 7: case 9: case 11: case 13: case 15: i = 0; break; case 2: case 6: case 10: case 14: i = 1; break; case 4: case 12: i = 2; break; case 8: i = 3; break; } if (s->work[i] >= 0) abort(); s->work[i] = c; s->freework &= ~ (1U << i); to = L_WORK(i); } else { if (s->ph[L_PILE_PILE(to)] != L_PILE_CWP(to)) { if (check) return(1); else abort(); } if ( (check > 1) && ( (s->ph[L_PILE_PILE(to)] == 0) ? (CARD_VALUE(c) != 12) : ( (CARD_VALUE(c) == 12) || (CARD_VALUE(c)+1 != CARD_VALUE(s->pc[L_PILE_PILE(to)][L_PILE_CWP(to)-1])) ) ) ) { return(1); } s->pc[L_PILE_PILE(to)][L_PILE_CWP(to)] = c; s->ph[L_PILE_PILE(to)] ++; } s->loc[c] = to; s->svalid = 0; return(0); } static int same_state(STATE *s1, STATE *s2) { check_serialize(s1); check_serialize(s2); return( (s1->serial[SS_LEN] == s2->serial[SS_LEN]) && !bcmp(&s1->serial[0],&s2->serial[0],s1->serial[SS_LEN]) ); } static void peephole(STATE *s, MOVE **v, int *np) { int i; int j; int k; STATE t1; STATE t2; int n; n = *np; i = 0; while (i < n) { if ((i+1 < n) && (v[i+1]->card == v[i]->card)) { printf("opt: delete %d (a)\n",i); bcopy(&v[i+1],&v[i],(n-(i+1))*sizeof(*v)); n --; continue; } for (j=i+1;jcard == v[i]->card) break; } if (j >= n) { noopt:; move_card(s,v[i]->card,v[i]->to,0); i ++; continue; } t1 = *s; for (k=i;k<=j;k++) move_card(&t1,v[k]->card,v[k]->to,0); /* Possible optimizations: * Move card from A to B, later from B to A - delete both moves * Move card from A to B, later from B to C - change first move to be from A to C - change second move to be from A to C In each case, if the result ends up being the same, keep it. */ if (L_SAMELOC(s->loc[v[i]->card],v[j]->to)) { t2 = *s; for (k=i+1;kcard,v[k]->to,2)) goto noopt; if (! same_state(&t1,&t2)) goto noopt; printf("opt: delete %d and %d\n",i,j); bcopy(&v[i+1],&v[i],(j-(i+1))*sizeof(*v)); bcopy(&v[j+1],&v[j-1],(n-(j+1))*sizeof(*v)); n -= 2; continue; } else { t2 = *s; if (move_card(&t2,v[j]->card,v[j]->to,2)) goto noopt1; for (k=i+1;kcard,v[k]->to,2)) goto noopt1; if (! same_state(&t1,&t2)) goto noopt1; printf("opt: replace %d with moved %d\n",i,j); v[i] = v[j]; bcopy(&v[j+1],&v[j],(n-(j+1))*sizeof(*v)); n --; continue; noopt1:; t2 = *s; for (k=i+1;k<=j;k++) if (move_card(&t2,v[k]->card,v[k]->to,2)) goto noopt; if (! same_state(&t1,&t2)) goto noopt; printf("opt: delete %d (b)\n",i); bcopy(&v[i+1],&v[i],(n-(i+1))*sizeof(*v)); n --; continue; } } *np = n; } static void havewin(MOVE *trail) { int n; MOVE *m; MOVE **v; int i; STATE s; printf("win found\n"); for (n=0,m=trail;m;n++,m=m->link) ; v = malloc(n*sizeof(MOVE *)); for (i=n-1,m=trail;m;i--,m=m->link) v[i] = m; if (i != -1) abort(); for (i=0;icard),locstring(m->to,0)); } s = startstate; peephole(&s,v,&n); printf("--------\n"); for (i=0;icard),locstring(m->to,0)); } } static void searchfrom(STATE *s, MOVE *mp) { int i; int c; int l; MOVE m; if (register_state(s)) return; m.link = mp; for (i=0;i<4;i++) { if (s->hh[i] < 13) { c = CARD_MAKE(i,s->hh[i]); if (L_ISWORK(s->loc[c])) { int oldl; oldl = s->loc[c]; move_card(s,c,L_HOME(),0); m.card = c; m.to = L_HOME(); searchfrom(s,&m); move_card(s,c,oldl,0); return; } else { if (s->ph[L_PILE_PILE(s->loc[c])] == L_PILE_CWP(s->loc[c])+1) { int oldl; oldl = s->loc[c]; move_card(s,c,L_HOME(),0); m.card = c; m.to = L_HOME(); searchfrom(s,&m); move_card(s,c,oldl,0); return; } } } } for (i=0;i<4;i++) if (s->hh[i] < 13) break; if (i >= 4) { havewin(mp); exit(0); } for (i=0;i<4+10;i++) { if (i < 10) { if (s->ph[i] < 1) continue; c = s->pc[i][s->ph[i]-1]; } else { if (s->work[i-10] < 0) continue; c = s->work[i-10]; } if (CARD_VALUE(c) == 12) { if ((i < 10) && (s->ph[i] == 1)) continue; for (l=0;l<10;l++) if (s->ph[l] < 1) break; if (l >= 10) continue; l = L_PILE(l,0); } else { l = s->loc[CARD_MAKE(CARD_SUIT(c),CARD_VALUE(c)+1)]; if (!L_ISPILE(l) || (L_PILE_CWP(l) != s->ph[L_PILE_PILE(l)]-1)) continue; l = L_PILE(L_PILE_PILE(l),L_PILE_CWP(l)+1); } { int oldl; oldl = s->loc[c]; move_card(s,c,l,0); m.card = c; m.to = l; searchfrom(s,&m); move_card(s,c,oldl,0); } } if (s->freework) { int oldl; for (i=0;i<10;i++) { if (s->ph[i] < 1) continue; c = s->pc[i][s->ph[i]-1]; oldl = s->loc[c]; move_card(s,c,L_WORK(0),0); m.card = c; m.to = s->loc[c]; searchfrom(s,&m); move_card(s,c,oldl,0); } } } int main(int, char **); int main(int ac, char **av) { STATE start; setup_game(&start,ac,av); startstate = start; printf("Start: %s\n",statestring(&start)); searchfrom(&start,0); printf("no win found\n"); exit(0); }