/* * $XConsortium: puzzle.c,v 1.14 94/03/28 18:34:10 gildea Exp $ */ /* Puzzle - (C) Copyright 1987, 1988 Don Bennett. * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, * provided that the above copyright notice appear in all copies and that * both that copyright notice and this permission notice appear in * supporting documentation. */ /** ** Puzzle ** ** Don Bennett, HP Labs ** ** this is the code that does the real work to solve the ** puzzle. (Commonly seen as a 4x4 grid of sliding pieces ** numbered 1-15 with one empty space.) ** ** The idea for the solution algorithm - solving the puzzle ** in layers working from the outside in - comes to me ** indirectly from John Nagle. **/ #include #include #include #define min(x,y) (((x)>(y))?(x):(y)) #define MAX_PLAN 1000 #define LEFT 0 #define RIGHT 1 #define UP 2 #define DOWN 3 int other_dir[4] = { RIGHT, LEFT, DOWN, UP }; /** layer info macros -> (innermost 4 tiles are layer zero, ordinal goes up ** as you move out) ** layer_depth - returns number of (rows down),(cols across) the layer starts; ** layer_width - number of blocks wide the layer is; **/ #define layer_depth(l) (layers-1-(l)) #define layer_width(l) (PuzzleSize - 2*layer_depth(l)) /** macros for finding the corners of each layer **/ #define UL(l) (layer_depth(l)*(PuzzleSize+1) + \ ExtraRows*PuzzleWidth + ExtraColumns*(layers-(l))) #define UR(l) (layer_depth(l)*(PuzzleSize+1) + layer_width(l) - 1 + \ ExtraRows*PuzzleWidth + ExtraColumns*(layers-(l))) #define LL(l) ((layer_depth(l)+layer_width(l)-1)*PuzzleSize+layer_depth(l)+ \ ExtraRows*PuzzleSize + ExtraColumns*(PuzzleHeight+1+(l)-layers)) #define LR(l) ((layer_depth(l)+layer_width(l)-1)*(PuzzleSize+1) + \ ExtraRows*PuzzleSize + ExtraColumns*(PuzzleHeight+1+(l)-layers)) /** get the x and y coordinates of a location in the matrix **/ #define get_x(loc) ((loc) % PuzzleWidth) #define get_y(loc) ((loc) / PuzzleWidth) #define indx(x,y) (((y)*PuzzleWidth) + (x)) #define next_left(loc) (loc - 1) #define next_right(loc) (loc + 1) #define next_up(loc) (loc - PuzzleWidth) #define next_down(loc) (loc + PuzzleWidth) #define sign(foo) (((foo)>0)?1:-1) int OutputLogging = 0; static int SolvingFlag = 0; static int AbortSolvingFlag = 0; static int ExtraRows = 0; static int ExtraColumns = 0; /** PuzzleSize MUST be a multiple of 2; **/ extern int PuzzleSize; extern int PuzzleWidth, PuzzleHeight; int layers; int *tmp_matrix; int *targetm; int *locked; int *loclist; int *position; int space_x, space_y; /** location of space in the position matrix **/ static jmp_buf solve_env; /** ** this piece of code needs to be fixed if you want to use it ** for non-square matrices; **/ print_matrix(mat) int (*mat)[]; { int i,j; printf("\n"); for (i=0; i0; i--) { (*path)[i] = loclist[next_loc]; switch(loclist[next_loc]) { case LEFT: next_loc = next_right(next_loc); break; case RIGHT: next_loc = next_left(next_loc); break; case UP: next_loc = next_down(next_loc); break; case DOWN: next_loc = next_up(next_loc); break; } } } move_space(dir,dist) int dir,dist; { int i, step, count; int first_x,first_y; int last_x,last_y, shift_dir; if (PuzzlePending()) ProcessEvents(); if (SolvingFlag && AbortSolvingFlag) longjmp(solve_env,1); if (dist == 0) return; if (dir == LEFT) { dir = RIGHT; dist = -dist; } if (dir == UP) { dir = DOWN; dist = -dist; } first_x = space_x; first_y = space_y; step = 1; count = dist; if (dist < 0) { step = -1; count = -count; } /** first_x,y are the location of the first piece to be shifted **/ if (dir == RIGHT) first_x += step; else first_y += step; /** shift_dir is the direction the pieces need to be shifted **/ if (dist < 0) shift_dir = dir; else switch (dir) { case LEFT: shift_dir = RIGHT; break; case RIGHT: shift_dir = LEFT; break; case UP: shift_dir = DOWN; break; case DOWN: shift_dir = UP; break; } for (i=0; itv_sec); tvp->tv_usec = 0L; /* ignore tzp for now since this file doesn't use it */ } #endif initialize() { /** Initialize the position and ** the targetm matrices; **/ int i; int sp_x, sp_y; struct timeval tv; X_GETTIMEOFDAY (&tv); srand ((int) tv.tv_usec); layers = PuzzleSize / 2; ExtraRows = PuzzleHeight - PuzzleSize; ExtraColumns = PuzzleWidth - PuzzleSize; tmp_matrix = (int *) malloc(PuzzleWidth*PuzzleHeight*sizeof(int)); targetm = (int *) malloc(PuzzleWidth*PuzzleHeight*sizeof(int)); locked = (int *) malloc(PuzzleWidth*PuzzleHeight*sizeof(int)); loclist = (int *) malloc(PuzzleWidth*PuzzleHeight*sizeof(int)); position = (int *) malloc(PuzzleWidth*PuzzleHeight*sizeof(int)); for (i=0; i> 3) % PuzzleWidth; new_y = (rand() >> 3) % PuzzleHeight; move_space(RIGHT,new_x-space_x); move_space(DOWN,new_y-space_y); } OutputLogging = old_output_state; } /** To solve this puzzle, work from the outside in; ** For each successive ring working your way in, ** ** (1) put the corners in place; ** (2) finish off the rest of the boundaries; ** (3) do the next layer in; **/ solve_layer_0() { move_piece(find_piece(targetm[UL(0)]),UL(0)); move_space_to(LR(0)); } do_last_two_on_edge(ntlast,last,tmp,emergency) int ntlast,last,tmp,emergency; { int last_piece, ntlast_piece; last_piece = targetm[last]; ntlast_piece = targetm[ntlast]; move_piece(find_piece(ntlast_piece),last); locked[last] = 1; /** if the last piece is stuck where the next to the last ** piece should go, do some magic to fix things up; **/ if (find_piece(0) == ntlast) move_space_to(tmp); if (find_piece(last_piece) == ntlast) { /** a rescue is necessary **/ locked[last] = 0; move_piece(find_piece(ntlast_piece),ntlast); locked[ntlast] = 1; move_piece(find_piece(last_piece),emergency); locked[emergency] = 1; locked[ntlast] = 0; move_piece(find_piece(ntlast_piece),last); locked[emergency] = 0; locked[last] = 1; } move_piece(find_piece(last_piece),tmp); locked[tmp] = 1; move_space_to(ntlast); locked[tmp] = 0; locked[last] = 0; move_space_to(last); move_space_to(tmp); locked[ntlast] = 1; locked[last] = 1; } solve_layer(layer) int layer; { int i, tmp, last, ntlast, emergency; int ul, ur, ll, lr; if (layer == 0) solve_layer_0(); else { /** find and put each of the corners into place **/ ul = UL(layer); ur = UR(layer); ll = LL(layer); lr = LR(layer); move_piece(find_piece(targetm[ul]),ul); locked[ul] = 1; move_piece(find_piece(targetm[ur]),ur); locked[ur] = 1; move_piece(find_piece(targetm[ll]),ll); locked[ll] = 1; move_piece(find_piece(targetm[lr]),lr); locked[lr] = 1; /** Strategy for doing the pieces between the corners: ** (1) put all but the last two edge pieces in place; ** (2) put the next to the last piece next to the corner; ** (3) put the last piece one move in from its final position; ** (4) move the space to the final position of the next ** to the last piece; ** (5) slide the next to the last piece over and the last ** piece into the edge where it goes. **/ /**************/ /** top edge **/ /**************/ for (i=ul+1; i=0; i--) solve_layer(i); /** move the space back out to the LR corner; **/ /** i is the layer the space is moving into **/ for (i=1; i