|
DataMuseum.dkPresents historical artifacts from the history of: DKUUG/EUUG Conference tapes |
This is an automatic "excavation" of a thematic subset of
See our Wiki for more about DKUUG/EUUG Conference tapes Excavated with: AutoArchaeologist - Free & Open Source Software. |
top - metrics - downloadIndex: T p
Length: 19826 (0x4d72) Types: TextFile Names: »puzzle.c«
└─⟦276d19d6e⟧ Bits:30007243 EUUGD5_I: X11R5 └─⟦af7d3f39a⟧ »./mit-2/mit-2.00« └─⟦0abaffd9e⟧ └─⟦this⟧ »mit/demos/puzzle/puzzle.c«
/* * $XConsortium: puzzle.c,v 1.11 91/07/25 13:48:17 rws 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 <X11/Xos.h> #include <stdio.h> #include <setjmp.h> #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; i<PuzzleHeight; i++) { for (j=0; j<PuzzleWidth; j++) printf(" %2d ",(*mat)[indx(j,i)]); printf("\n"); } printf("\n"); } find_piece(piece) int piece; { int i; for (i=0; i<PuzzleWidth*PuzzleHeight; i++) if (position[i] == piece) return(i); printf("piece %d not found!\n",piece); exit(1); } move_space_to(loc) int loc; { int i,current_dir,dist; int plan[MAX_PLAN]; plan_move(indx(space_x,space_y),loc,plan); current_dir = plan[1]; dist = 0; for (i=1; i<=plan[0]; i++) if (plan[i] == current_dir) dist++; else if (plan[i] == other_dir[current_dir]) dist--; else { move_space(current_dir,dist); current_dir = plan[i]; dist = 1; } move_space(current_dir,dist); } move_piece(loc,targetm) int loc,targetm; { int i; int plan[MAX_PLAN]; plan_move(loc,targetm,plan); for (i=1; i<=plan[0]; i++) switch(plan[i]) { case LEFT: locked[loc] = 1; move_space_to(next_left(loc)); locked[loc] = 0; move_space_to(loc); loc = next_left(loc); break; case RIGHT: locked[loc] = 1; move_space_to(next_right(loc)); locked[loc] = 0; move_space_to(loc); loc = next_right(loc); break; case UP: locked[loc] = 1; move_space_to(next_up(loc)); locked[loc] = 0; move_space_to(loc); loc = next_up(loc); break; case DOWN: locked[loc] = 1; move_space_to(next_down(loc)); locked[loc] = 0; move_space_to(loc); loc = next_down(loc); break; } } plan_move(start_loc,end_loc,path) int start_loc, end_loc; int (*path)[]; { #define QUEUE_SIZE 1000 #define DIST(loc1,loc2) (abs(get_x(loc1) - get_x(loc2)) + abs(get_y(loc1) - get_y(loc2))) int i, next_loc, next_dist, chosen, found_path, move_num; int loc_x, loc_y; int loc_queue[QUEUE_SIZE]; int loc_dist[QUEUE_SIZE]; int loc_queue_used[QUEUE_SIZE]; int queue_head, queue_tail; int candidate[4]; found_path = 0; for (i=0; i<PuzzleWidth*PuzzleHeight; i++) { tmp_matrix[i] = -locked[i]; loclist[i] = -1; } for (i=0; i<QUEUE_SIZE; i++) loc_queue_used[i] = 0; queue_head = 0; queue_tail = 0; loc_queue[0] = start_loc; loc_dist[0] = DIST(end_loc,start_loc); tmp_matrix[start_loc] = 1; queue_tail++; /** if the selected element has a distance of zero, we've found it; ** (This really isn't a queue, but rather a range of elements ** to be searched for an element of the desired properties; **/ /** as we search for a path, ** LINK array is used to indicate the direction from which ** we moved into a location; ** TMP_MATRIX array is used to keep track of the move number; **/ while(queue_head < queue_tail && !found_path) { /** find the entry that ** (1) has the smallest distance and ** (2) has the smallest move number; **/ next_loc = loc_queue[queue_head]; next_dist = loc_dist[queue_head]; chosen = queue_head; for (i=queue_head+1; i<queue_tail; i++) if (!loc_queue_used[i] && ( loc_dist[i] < next_dist) || ( (loc_dist[i] == next_dist) && (tmp_matrix[loc_queue[i]] < tmp_matrix[next_loc]) )) { next_loc = loc_queue[i]; next_dist = loc_dist[i]; chosen = i; } if (next_dist == 0) { found_path = 1; break; } loc_queue_used[chosen] = 1; /********************************/ /** permute the chosen element **/ /********************************/ candidate[0] = next_left(next_loc); candidate[1] = next_right(next_loc); candidate[2] = next_up(next_loc); candidate[3] = next_down(next_loc); loc_x = get_x(next_loc); loc_y = get_y(next_loc); if (loc_x == 0) candidate[0] = -1; if (loc_x == PuzzleWidth-1) candidate[1] = -1; if (loc_y == 0) candidate[2] = -1; if (loc_y == PuzzleHeight-1) candidate[3] = -1; move_num = tmp_matrix[next_loc] + 1; for (i=0; i<4; i++) if (candidate[i] != -1 && tmp_matrix[candidate[i]] == 0) { tmp_matrix[candidate[i]] = move_num; /** the next line works because the candidate index is ** same as the direction moved to reach the candidate; **/ loclist[candidate[i]] = i; loc_queue[queue_tail] = candidate[i]; loc_dist[queue_tail] = DIST(end_loc,candidate[i]); queue_tail++; if (queue_tail == QUEUE_SIZE) goto broke; } /***************************************************/ /** delete used items from the front of the queue **/ /***************************************************/ while(loc_queue_used[queue_head] && queue_head < queue_tail) queue_head++; } if (!found_path) { printf("couldn't find a way to move (%d,%d) to (%d,%d).\n", get_x(start_loc),get_y(start_loc), get_x(end_loc),get_y(end_loc)); #ifdef UNDEFINED print_matrix(position); printf("\n"); print_matrix(locked); printf("\n"); #endif /* UNDEFINED */ return(0); } broke: if (queue_tail == QUEUE_SIZE) { printf("it didn't work.\n"); return(0); } /** copy the path we found into the path array; ** element 0 will contain the number of moves in the path; **/ /** by the time we get there, next_loc is in the final location **/ (*path)[0] = tmp_matrix[next_loc] - 1; for (i=(*path)[0]; i>0; 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; i<count; i++) if (dir == RIGHT) { position[indx(space_x,space_y)] = position[indx(space_x+step,space_y)]; position[indx(space_x+step,space_y)] = 0; space_x += step; } /** dir == DOWN **/ else { position[indx(space_x,space_y)] = position[indx(space_x,space_y+step)]; position[indx(space_x,space_y+step)] = 0; space_y += step; } last_x = space_x; last_y = space_y; /** the blocks first_x,y through last_x,y need to be shifted ** one block in the shift_dir direction; **/ if (OutputLogging) LogMoveSpace(first_x,first_y,last_x,last_y,shift_dir); } /* SYSV386 gets this from libBerk.a */ #if defined(USG) && !defined(CRAY) && !defined(SYSV386) int gettimeofday (tvp, tzp) struct timeval *tvp; struct timezone *tzp; { time (&tvp->tv_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; gettimeofday (&tv, NULL); 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<PuzzleWidth*PuzzleHeight; i++) locked[i] = 0; if (!tmp_matrix || !targetm || !locked || !loclist || !position) { printf("matrix allocation failed.\n"); exit(1); } for (i=0; i<PuzzleWidth*PuzzleHeight-1; i++) { targetm[i] = i+1; position[i] = i+1; } /** assert i == PuzzleWidth * PuzzleHeight - 1; **/ position[i] = 0; targetm[i] = 0; space_x = PuzzleWidth - 1; space_y = PuzzleHeight - 1; /** Move the space into the LR corner of the ** innermost layer; ** For each of the outer layers, move the space ** left one and up one; **/ sp_x = space_x; sp_y = space_y; for (i=0; i<layers-1; i++) { /** move the space left one; **/ targetm[indx(sp_x,sp_y)] = targetm[indx(sp_x-1,sp_y)]; targetm[indx(sp_x-1,sp_y)] = 0; sp_x -= 1; /** move the space up one; **/ targetm[indx(sp_x,sp_y)] = targetm[indx(sp_x,sp_y-1)]; targetm[indx(sp_x,sp_y-1)] = 0; sp_y -= 1; } } Scramble() { int i; int new_x, new_y; int old_output_state; struct timeval tv; old_output_state = OutputLogging; OutputLogging = 0; gettimeofday (&tv, NULL); srand ((int) tv.tv_usec); for (i=0; i<10*PuzzleWidth*PuzzleHeight; i++) { new_x = (rand() >> 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<ur-2; i++) { move_piece(find_piece(targetm[i]),i); locked[i] = 1; } ntlast = i; last = i+1; tmp = UR(layer-1); emergency = next_down(tmp); do_last_two_on_edge(ntlast,last,tmp,emergency); /*****************/ /** bottom edge **/ /*****************/ for (i=ll+1; i<lr-2; i++) { move_piece(find_piece(targetm[i]),i); locked[i] = 1; } ntlast = i; last = i+1; tmp = LR(layer-1); emergency = next_up(tmp); do_last_two_on_edge(ntlast,last,tmp,emergency); /***************/ /** left side **/ /***************/ for (i=ul+PuzzleWidth; i<ll-2*PuzzleWidth; i+=PuzzleWidth) { move_piece(find_piece(targetm[i]),i); locked[i] = 1; } ntlast = i; last = i + PuzzleWidth; tmp = LL(layer-1); emergency = next_right(tmp); do_last_two_on_edge(ntlast,last,tmp,emergency); /****************/ /** right side **/ /****************/ for (i=ur+PuzzleWidth; i<lr-2*PuzzleWidth; i+=PuzzleWidth) { move_piece(find_piece(targetm[i]),i); locked[i] = 1; } ntlast = i; last = i + PuzzleWidth; tmp = LR(layer-1); emergency = next_left(tmp); do_last_two_on_edge(ntlast,last,tmp,emergency); } } solve_row(row) int row; { int i, loc, last, ntlast, tmp, emergency; for (i=0; i<PuzzleWidth-2; i++) { loc = indx(i,row); move_piece(find_piece(targetm[loc]),loc); locked[loc] = 1; } ntlast = indx(PuzzleWidth-2,row); last = indx(PuzzleWidth-1,row); tmp = last + PuzzleWidth; emergency = tmp + PuzzleWidth; do_last_two_on_edge(ntlast,last,tmp,emergency); } solve_col(col) int col; { int i, loc, last, ntlast, tmp, emergency; for (i=0; i<PuzzleHeight-2; i++) { loc = indx(col,i); move_piece(find_piece(targetm[loc]),loc); locked[loc] = 1; } ntlast = indx(col,PuzzleHeight-2); last = indx(col,PuzzleHeight-1); tmp = last + 1; emergency = tmp + 1; do_last_two_on_edge(ntlast,last,tmp,emergency); } AbortSolving() { if (SolvingFlag) AbortSolvingFlag = 1; } SolvingStatus() { return(SolvingFlag); } Solve() { /** determine the position we want to be in when ** we are done; This position will have the space in ** the center; Then, we'll move the space back to ** the outside. **/ int i; if (SolvingFlag) return; if (!setjmp(solve_env)) { SolvingFlag = 1; for (i=0; i<PuzzleWidth*PuzzleHeight; i++) locked[i] = 0; /** solve the extra rows and cols **/ for (i=0; i<ExtraRows; i++) solve_row(i); for (i=0; i<ExtraColumns; i++) solve_col(i); /** solve each layer **/ for (i=layers-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<layers; i++) { move_space(DOWN,1); move_space(RIGHT,1); } flushLogging(); } else { flushLogging(); RepaintTiles(); } for (i=0; i<PuzzleWidth*PuzzleHeight; i++) locked[i] = 0; AbortSolvingFlag = 0; SolvingFlag = 0; } #ifdef UNDEFINED main() { #ifdef DEBUG int plan[1000]; int i; #endif /* DEBUG */ initialize(); #ifdef DEBUG print_matrix(position); #endif /* DEBUG */ scramble(); #ifdef DEBUG print_matrix(position); #ifdef UDEFINED locked[indx(4,3)] = 1; locked[indx(4,2)] = 1; locked[indx(4,1)] = 1; locked[indx(5,2)] = 1; plan_move(indx(space_x,space_y),indx(5,1),plan); print_matrix(tmp_matrix); printf("\nplan has %d moves.\n",plan[0]); for (i=0; i<plan[0]; i++) { switch(plan[i+1]) { case UP: printf("up\n"); break; case DOWN: printf("down\n"); break; case LEFT: printf("left\n"); break; case RIGHT: printf("right\n"); break; } } #endif /* UDEFINED */ #endif /* DEBUG */ solve(); } #endif /* UNDEFINED */