/* * Given a 3x3 netslide puzzle, which consists of a 3x3 grid in which * the left, right, top, and bottom columns and rows can slide * circularly: * * ^ ^ * +---+---+---+ * <- | | | | -> * +---+---+---+ * | | | | * +---+---+---+ * <- | | | | -> * +---+---+---+ * v v * * (the centre cell is fixed in place), look for God's algorithm: * given an arbitrary permutation of the outer eight cells, what is * the quickest way to reach the solved state? * * Our solved state looks like * * +---+---+---+ * | 0 | 1 | 2 | * +---+---+---+ * | 7 | | 3 | * +---+---+---+ * | 6 | 5 | 4 | * +---+---+---+ * * We represent a state as a 24-bit number, in which each three-bit * field represents the contents of one cell. (This is obviously * gross overkill, in a sense, in that there are 2^24 possible values * for that number but only 8!=40320 possible puzzle states. But it's * much easier to operate on, and, with a little help from precomputed * lists, it's not hard to map between them either way.) * * Unfortunately, if the puzzle includes multiple identical blocks, we * have no good way to disambiguate. For the moment, we have to just * manually enter multiple permutations of the input to get one that's * solvable, then (if there are multiple duplicates) swap to find the * quickest solution. For example, if the puzzle is * * +-------+-------+-------+ * | | | | | * | --* | --+-- | * | * | | | | | * +·······+-------+-------+ * | | | : | | * | --+ | --X : +-- | * | | | : | * +-------+-------+·······+ * | | : | | * | * : +-- | *-- | * | : | | | * +-------+-------+-------+ * * then we can specify this as 41350762 or as 41650732, because the * upper right and lower left cells are interchangeable. When there * is only one pair of identical cells, one form will be solvable and * the other not, because only even permutations are achievable. If * there's a triple, or multiple pairs, as in * * +-------+-------+-------+ * | | | | | * | --* | | | --* | * | | | | | * +-------+-------+·······+ * | | : | | | | * | +-- : X-- | * | * | : | | | * +-------+-------+·······+ * | : | | * | +-- : --* | --+-- | * | | : | | | * +-------+-------+-------+ * * (where three of them are identical), then there are multiple * solvable inputs; we just need to try them all to find the quickest. * In this second example, we can use *7*61*05 where the three *s * contain some permutation of 2, 3, and 4. This gives us six inputs, * of which three (37261405, 27461305, 47361205) are unsolvable and * the other three (27361405, 37461205, 47261305) are solvable. * 37461205 and 47261305 turn out to be five-move positions, with * 27361405 a four-move position, so there is a four-move solution to * this puzzle: rd tl ld br. */ #include #include #define NSTATE (1<<24) #define NPERM (8*7*6*5*4*3*2*1) #define NMOVE 8 typedef unsigned int STATE; typedef int PERM; typedef struct move MOVE; struct move { STATE (*fn)(STATE); const char *move; const char *inv; } ; static PERM state_to_perm[NSTATE]; // -1 if not a possible state static STATE perm_to_state[NPERM]; static unsigned int dist[NPERM]; static PERM livev[2][NPERM*NMOVE]; static PERM *olive; static int onlive; static PERM *nlive; static int nnlive; static STATE rot_lu(STATE s) { return( (s & 007777700) | ((s & 070000000) >> 18) | ((s & 000000070) >> 3) | ((s & 000000007) << 21) ); } static STATE rot_ld(STATE s) { return( (s & 007777700) | ((s & 070000000) >> 21) | ((s & 000000007) << 3) | ((s & 000000070) << 18) ); } static STATE rot_ru(STATE s) { return( (s & 077000777) | ((s & 000700000) >> 6) | ((s & 000077000) << 3) ); } static STATE rot_rd(STATE s) { return( (s & 077000777) | ((s & 000770000) >> 3) | ((s & 000007000) << 6) ); } static STATE rot_tr(STATE s) { return( (s & 000077777) | ((s & 077000000) >> 3) | ((s & 000700000) << 6) ); } static STATE rot_tl(STATE s) { return( (s & 000077777) | ((s & 070000000) >> 6) | ((s & 007700000) << 3) ); } static STATE rot_br(STATE s) { return( (s & 077770007) | ((s & 000007000) >> 6) | ((s & 000000770) << 3) ); } static STATE rot_bl(STATE s) { return( (s & 077770007) | ((s & 000007700) >> 3) | ((s & 000000070) << 6) ); } static const MOVE moves[NMOVE] = { { &rot_lu, "lu", "ld" }, { &rot_ld, "ld", "lu" }, { &rot_ru, "ru", "rd" }, { &rot_rd, "rd", "ru" }, { &rot_tr, "tr", "tl" }, { &rot_tl, "tl", "tr" }, { &rot_br, "br", "bl" }, { &rot_bl, "bl", "br" } }; static void setup_enumerated(const unsigned char *cv, PERM *nextp) { STATE s; s = (cv[0] << 21) | (cv[1] << 18) | (cv[2] << 15) | (cv[3] << 12) | (cv[4] << 9) | (cv[5] << 6) | (cv[6] << 3) | cv[7]; state_to_perm[s] = *nextp; perm_to_state[*nextp] = s; nextp[0] ++; } static void setup_enumerate(unsigned char *cv, unsigned char *cx, PERM *nextp, unsigned int taken) { int i; if (taken == 0xff) { setup_enumerated(cv,nextp); return; } for (i=8-1;i>=0;i--) { if ((taken >> i) & 1) continue; *cx = i; setup_enumerate(cv,cx+1,nextp,taken+(1<=0;i--) state_to_perm[i] = -1; px = 0; setup_enumerate(&count[0],&count[0],&px,0); if (px != NPERM) abort(); for (i=NPERM-1;i>=0;i--) { if (perm_to_state[i] >= NSTATE) abort(); if (state_to_perm[perm_to_state[i]] != i) abort(); } for (i=NSTATE-1;i>=0;i--) { if (state_to_perm[i] < 0) continue; if (state_to_perm[i] >= NPERM) abort(); if (perm_to_state[state_to_perm[i]] != i) abort(); } } static void search(void) { int x; PERM p; STATE s; int d; int fx; STATE s2; PERM p2; for (x=NPERM-1;x>=0;x--) dist[x] = NSTATE; olive = &livev[0][0]; nlive = &livev[1][0]; x = state_to_perm[001234567]; if (x < 0) abort(); nnlive = 1; nlive[0] = x; onlive = 0; x = 0; d = -1; while (1) { if (x >= onlive) { PERM *t; if (nnlive < 1) return; t = olive; olive = nlive; nlive = t; onlive = nnlive; nnlive = 0; x = 0; d ++; printf("live shift, n=%d, d=%d\n",onlive,d); continue; } p = olive[x++]; s = perm_to_state[p]; if (dist[p] <= d) continue; dist[p] = d; for (fx=NMOVE-1;fx>=0;fx--) { s2 = (*moves[fx].fn)(s); p2 = state_to_perm[s2]; if (p2 < 0) abort(); nlive[nnlive++] = p2; } } } static void solve(void) { STATE s; int n; int c; int fx; int bx; int bd; int d; STATE s2; PERM p2; STATE bs; while (1) { c = getchar(); if (c == '\n') continue; if (c == EOF) break; ungetc(c,stdin); n = -1; scanf("%o%n",&s,&n); //printf("s=%08o n=%d\n",s,n); if ((n < 0) || (s > NSTATE) || (state_to_perm[s] < 0)) { printf("Input must be an octal number, a permutation of 01234567.\n"); do c = getchar(); while ((c != '\n') && (c != EOF)); continue; } printf("%08o",s); if (dist[state_to_perm[s]] == NSTATE) { printf(" unreachable\n"); continue; } //printf("d=%d\n",dist[state_to_perm[s]]); while (dist[state_to_perm[s]] > 0) { bd = NSTATE; for (fx=NMOVE-1;fx>=0;fx--) { s2 = (*moves[fx].fn)(s); if (s2 >= NSTATE) abort(); p2 = state_to_perm[s2]; if ((p2 < 0) || (p2 >= NSTATE)) abort(); d = dist[p2]; //printf("move %s to %08o d=%d\n",moves[fx].move,s2,d); if (d == NSTATE) abort(); if (d < bd) { bd = d; bs = s2; bx = fx; //printf("new best\n"); } } if (bd == NSTATE) abort(); printf(" %s %08o",moves[bx].move,s2); s = bs; //printf("s now %08o\n",s); } printf("\n"); } } int main(void); int main(void) { setup(); search(); solve(); return(0); }