2 * fifteen.c: standard 15-puzzle.
14 #define PREFERRED_TILE_SIZE 48
15 #define TILE_SIZE (ds->tilesize)
16 #define BORDER (TILE_SIZE / 2)
17 #define HIGHLIGHT_WIDTH (TILE_SIZE / 20)
18 #define COORD(x) ( (x) * TILE_SIZE + BORDER )
19 #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
21 #define ANIM_TIME 0.13F
22 #define FLASH_FRAME 0.13F
24 #define X(state, i) ( (i) % (state)->w )
25 #define Y(state, i) ( (i) / (state)->w )
26 #define C(state, x, y) ( (y) * (state)->w + (x) )
28 #define PARITY_P(params, gap) (((X((params), (gap)) - ((params)->w - 1)) ^ \
29 (Y((params), (gap)) - ((params)->h - 1)) ^ \
30 (((params)->w * (params)->h) + 1)) & 1)
31 #define PARITY_S(state) PARITY_P((state), ((state)->gap_pos))
50 int used_solve
; /* used to suppress completion flash */
54 static game_params
*default_params(void)
56 game_params
*ret
= snew(game_params
);
63 static int game_fetch_preset(int i
, char **name
, game_params
**params
)
66 *params
= default_params();
67 *name
= dupstr("4x4");
73 static void free_params(game_params
*params
)
78 static game_params
*dup_params(const game_params
*params
)
80 game_params
*ret
= snew(game_params
);
81 *ret
= *params
; /* structure copy */
85 static void decode_params(game_params
*ret
, char const *string
)
87 ret
->w
= ret
->h
= atoi(string
);
88 while (*string
&& isdigit((unsigned char)*string
)) string
++;
91 ret
->h
= atoi(string
);
95 static char *encode_params(const game_params
*params
, int full
)
99 sprintf(data
, "%dx%d", params
->w
, params
->h
);
104 static config_item
*game_configure(const game_params
*params
)
109 ret
= snewn(3, config_item
);
111 ret
[0].name
= "Width";
112 ret
[0].type
= C_STRING
;
113 sprintf(buf
, "%d", params
->w
);
114 ret
[0].sval
= dupstr(buf
);
117 ret
[1].name
= "Height";
118 ret
[1].type
= C_STRING
;
119 sprintf(buf
, "%d", params
->h
);
120 ret
[1].sval
= dupstr(buf
);
131 static game_params
*custom_params(const config_item
*cfg
)
133 game_params
*ret
= snew(game_params
);
135 ret
->w
= atoi(cfg
[0].sval
);
136 ret
->h
= atoi(cfg
[1].sval
);
141 static char *validate_params(const game_params
*params
, int full
)
143 if (params
->w
< 2 || params
->h
< 2)
144 return "Width and height must both be at least two";
149 static int perm_parity(int *perm
, int n
)
155 for (i
= 0; i
< n
-1; i
++)
156 for (j
= i
+1; j
< n
; j
++)
157 if (perm
[i
] > perm
[j
])
163 static char *new_game_desc(const game_params
*params
, random_state
*rs
,
164 char **aux
, int interactive
)
167 int x1
, x2
, p1
, p2
, parity
;
172 n
= params
->w
* params
->h
;
174 tiles
= snewn(n
, int);
175 used
= snewn(n
, int);
177 for (i
= 0; i
< n
; i
++) {
182 gap
= random_upto(rs
, n
);
187 * Place everything else except the last two tiles.
189 for (x
= 0, i
= n
-1; i
> 2; i
--) {
190 int k
= random_upto(rs
, i
);
193 for (j
= 0; j
< n
; j
++)
194 if (!used
[j
] && (k
-- == 0))
197 assert(j
< n
&& !used
[j
]);
200 while (tiles
[x
] >= 0)
207 * Find the last two locations, and the last two pieces.
209 while (tiles
[x
] >= 0)
214 while (tiles
[x
] >= 0)
219 for (i
= 0; i
< n
; i
++)
223 for (i
= p1
+1; i
< n
; i
++)
229 * Determine the required parity of the overall permutation.
230 * This is the XOR of:
232 * - The chessboard parity ((x^y)&1) of the gap square. The
233 * bottom right counts as even.
235 * - The parity of n. (The target permutation is 1,...,n-1,0
236 * rather than 0,...,n-1; this is a cyclic permutation of
237 * the starting point and hence is odd iff n is even.)
239 parity
= PARITY_P(params
, gap
);
242 * Try the last two tiles one way round. If that fails, swap
247 if (perm_parity(tiles
, n
) != parity
) {
250 assert(perm_parity(tiles
, n
) == parity
);
254 * Now construct the game description, by describing the tile
255 * array as a simple sequence of comma-separated integers.
259 for (i
= 0; i
< n
; i
++) {
263 k
= sprintf(buf
, "%d,", tiles
[i
]);
265 ret
= sresize(ret
, retlen
+ k
+ 1, char);
266 strcpy(ret
+ retlen
, buf
);
269 ret
[retlen
-1] = '\0'; /* delete last comma */
277 static char *validate_desc(const game_params
*params
, const char *desc
)
284 area
= params
->w
* params
->h
;
288 used
= snewn(area
, int);
289 for (i
= 0; i
< area
; i
++)
292 for (i
= 0; i
< area
; i
++) {
296 if (*p
< '0' || *p
> '9') {
297 err
= "Not enough numbers in string";
300 while (*p
>= '0' && *p
<= '9')
302 if (i
< area
-1 && *p
!= ',') {
303 err
= "Expected comma after number";
306 else if (i
== area
-1 && *p
) {
307 err
= "Excess junk at end of string";
311 if (n
< 0 || n
>= area
) {
312 err
= "Number out of range";
316 err
= "Number used twice";
321 if (*p
) p
++; /* eat comma */
329 static game_state
*new_game(midend
*me
, const game_params
*params
,
332 game_state
*state
= snew(game_state
);
336 state
->w
= params
->w
;
337 state
->h
= params
->h
;
338 state
->n
= params
->w
* params
->h
;
339 state
->tiles
= snewn(state
->n
, int);
344 for (i
= 0; i
< state
->n
; i
++) {
346 state
->tiles
[i
] = atoi(p
);
347 if (state
->tiles
[i
] == 0)
349 while (*p
&& *p
!= ',')
351 if (*p
) p
++; /* eat comma */
354 assert(state
->tiles
[state
->gap_pos
] == 0);
356 state
->completed
= state
->movecount
= 0;
357 state
->used_solve
= FALSE
;
362 static game_state
*dup_game(const game_state
*state
)
364 game_state
*ret
= snew(game_state
);
369 ret
->tiles
= snewn(state
->w
* state
->h
, int);
370 memcpy(ret
->tiles
, state
->tiles
, state
->w
* state
->h
* sizeof(int));
371 ret
->gap_pos
= state
->gap_pos
;
372 ret
->completed
= state
->completed
;
373 ret
->movecount
= state
->movecount
;
374 ret
->used_solve
= state
->used_solve
;
379 static void free_game(game_state
*state
)
385 static char *solve_game(const game_state
*state
, const game_state
*currstate
,
386 const char *aux
, char **error
)
391 static int game_can_format_as_text_now(const game_params
*params
)
396 static char *game_text_format(const game_state
*state
)
398 char *ret
, *p
, buf
[80];
399 int x
, y
, col
, maxlen
;
402 * First work out how many characters we need to display each
405 col
= sprintf(buf
, "%d", state
->n
-1);
408 * Now we know the exact total size of the grid we're going to
409 * produce: it's got h rows, each containing w lots of col, w-1
410 * spaces and a trailing newline.
412 maxlen
= state
->h
* state
->w
* (col
+1);
414 ret
= snewn(maxlen
+1, char);
417 for (y
= 0; y
< state
->h
; y
++) {
418 for (x
= 0; x
< state
->w
; x
++) {
419 int v
= state
->tiles
[state
->w
*y
+x
];
421 sprintf(buf
, "%*s", col
, "");
423 sprintf(buf
, "%*d", col
, v
);
433 assert(p
- ret
== maxlen
);
438 static game_ui
*new_ui(const game_state
*state
)
443 static void free_ui(game_ui
*ui
)
447 static char *encode_ui(const game_ui
*ui
)
452 static void decode_ui(game_ui
*ui
, const char *encoding
)
456 static void game_changed_state(game_ui
*ui
, const game_state
*oldstate
,
457 const game_state
*newstate
)
461 struct game_drawstate
{
468 static int flip_cursor(int button
)
471 case CURSOR_UP
: return CURSOR_DOWN
;
472 case CURSOR_DOWN
: return CURSOR_UP
;
473 case CURSOR_LEFT
: return CURSOR_RIGHT
;
474 case CURSOR_RIGHT
: return CURSOR_LEFT
;
479 static void next_move_3x2(int ax
, int ay
, int bx
, int by
,
480 int gx
, int gy
, int *dx
, int *dy
)
482 /* When w = 3 and h = 2 and the tile going in the top left corner
483 * is at (ax, ay) and the tile going in the bottom left corner is
484 * at (bx, by) and the blank tile is at (gx, gy), how do you move? */
486 /* Hard-coded shortest solutions. Sorry. */
487 static const unsigned char move
[120] = {
512 static const struct { int dx
, dy
; } d
[4] = {{+1,0},{-1,0},{0,+1},{0,-1}};
514 int ea
= 3*ay
+ ax
, eb
= 3*by
+ bx
, eg
= 3*gy
+ gx
, v
;
518 v
= move
[ea
+ eb
*6 + eg
*5*6];
523 static void next_move(int nx
, int ny
, int ox
, int oy
, int gx
, int gy
,
524 int tx
, int ty
, int w
, int *dx
, int *dy
)
526 const int to_tile_x
= (gx
< nx
? +1 : -1);
527 const int to_goal_x
= (gx
< tx
? +1 : -1);
528 const int gap_x_on_goal_side
= ((nx
-tx
) * (nx
-gx
) > 0);
530 assert (nx
!= tx
|| ny
!= ty
); /* not already in place */
531 assert (nx
!= gx
|| ny
!= gy
); /* not placing the gap */
532 assert (ty
<= ny
); /* because we're greedy (and flipping) */
533 assert (ty
<= gy
); /* because we're greedy (and flipping) */
535 /* TODO: define a termination function. Idea: 0 if solved, or
536 * the number of moves to solve the next piece plus the number of
537 * further unsolved pieces times an upper bound on the number of
538 * moves required to solve any piece. If such a function can be
539 * found, we have (termination && (termination => correctness)).
540 * The catch is our temporary disturbance of 2x3 corners. */
542 /* handles end-of-row, when 3 and 4 are in the top right 2x3 box */
544 ny
<= ty
+ 2 && (nx
== tx
|| nx
== tx
+ 1) &&
545 oy
<= ty
+ 2 && (ox
== tx
|| ox
== tx
+ 1) &&
546 gy
<= ty
+ 2 && (gx
== tx
|| gx
== tx
+ 1))
548 next_move_3x2(oy
- ty
, tx
+ 1 - ox
,
549 ny
- ty
, tx
+ 1 - nx
,
550 gy
- ty
, tx
+ 1 - gx
, dy
, dx
);
556 if (ny
<= ty
+ 2 && (nx
== tx
|| nx
== tx
- 1) &&
557 gy
<= ty
+ 2 && (gx
== tx
|| gx
== tx
- 1)) {
558 next_move_3x2(ny
- ty
, tx
- nx
, 0, 1, gy
- ty
, tx
- gx
, dy
, dx
);
562 else if (nx
!= tx
|| ny
!= ty
+ 1) {
563 next_move((w
- 1) - nx
, ny
, -1, -1, (w
- 1) - gx
, gy
,
564 0, ty
+ 1, -1, dx
, dy
);
573 /* note that *dy = -1 is unsafe when gy = ty + 1 and gx < tx */
575 if (nx
== gx
|| (gy
== ty
&& gx
== tx
))
577 else if (!gap_x_on_goal_side
)
579 else if (ny
- ty
> abs(nx
- tx
))
584 if (nx
== tx
) /* then we know ny > ty */
585 if (gx
> nx
|| ny
> ty
+ 1)
586 *dy
= -1; /* ... so this is safe */
589 else if (gap_x_on_goal_side
)
591 else if (gy
== ty
|| (gy
== ty
+ 1 && gx
< tx
))
596 else if (nx
== tx
) /* gy > ny */
603 else if (gap_x_on_goal_side
)
604 if (gy
== ty
+ 1 && gx
< tx
)
609 else if (ny
- ty
> abs(nx
- tx
))
615 static int compute_hint(const game_state
*state
, int *out_x
, int *out_y
)
617 /* The overall solving process is this:
618 * 1. Find the next piece to be put in its place
619 * 2. Move it diagonally towards its place
620 * 3. Move it horizontally or vertically towards its place
621 * (Modulo the last two tiles at the end of each row/column)
624 int gx
= X(state
, state
->gap_pos
);
625 int gy
= Y(state
, state
->gap_pos
);
627 int tx
, ty
, nx
, ny
, ox
, oy
, /* {target,next,next2}_{x,y} */ i
;
630 /* 1. Find the next piece
631 * if (there are no more unfinished columns than rows) {
632 * fill the top-most row, left to right
633 * } else { fill the left-most column, top to bottom }
635 const int w
= state
->w
, h
= state
->h
, n
= w
*h
;
636 int next_piece
= 0, next_piece_2
= 0, solr
= 0, solc
= 0;
637 int unsolved_rows
= h
, unsolved_cols
= w
;
642 while (solr
< h
&& solc
< w
) {
643 int start
, step
, stop
;
644 if (unsolved_cols
<= unsolved_rows
)
645 start
= solr
*w
+ solc
, step
= 1, stop
= unsolved_cols
;
647 start
= solr
*w
+ solc
, step
= w
, stop
= unsolved_rows
;
648 for (i
= 0; i
< stop
; ++i
) {
649 const int j
= start
+ i
*step
;
650 if (state
->tiles
[j
] != j
+ 1) {
652 next_piece_2
= next_piece
+ step
;
658 (unsolved_cols
<= unsolved_rows
)
659 ? (++solr
, --unsolved_rows
)
660 : (++solc
, --unsolved_cols
);
666 /* 2, 3. Move the next piece towards its place */
668 /* gx, gy already set */
669 tx
= X(state
, next_piece
- 1); /* where we're going */
670 ty
= Y(state
, next_piece
- 1);
671 for (i
= 0; i
< n
&& state
->tiles
[i
] != next_piece
; ++i
);
672 nx
= X(state
, i
); /* where we're at */
674 for (i
= 0; i
< n
&& state
->tiles
[i
] != next_piece_2
; ++i
);
678 if (unsolved_cols
<= unsolved_rows
)
679 next_move(nx
, ny
, ox
, oy
, gx
, gy
, tx
, ty
, w
, &dx
, &dy
);
681 next_move(ny
, nx
, oy
, ox
, gy
, gx
, ty
, tx
, h
, &dy
, &dx
);
690 static char *interpret_move(const game_state
*state
, game_ui
*ui
,
691 const game_drawstate
*ds
,
692 int x
, int y
, int button
)
694 int cx
= X(state
, state
->gap_pos
), nx
= cx
;
695 int cy
= Y(state
, state
->gap_pos
), ny
= cy
;
700 if (button
== LEFT_BUTTON
) {
703 if (nx
< 0 || nx
>= state
->w
|| ny
< 0 || ny
>= state
->h
)
704 return NULL
; /* out of bounds */
705 } else if (IS_CURSOR_MOVE(button
)) {
706 static int invert_cursor
= -1;
707 if (invert_cursor
== -1) {
708 char *env
= getenv("FIFTEEN_INVERT_CURSOR");
709 invert_cursor
= (env
&& (env
[0] == 'y' || env
[0] == 'Y'));
711 button
= flip_cursor(button
); /* the default */
713 button
= flip_cursor(button
); /* undoes the first flip */
714 move_cursor(button
, &nx
, &ny
, state
->w
, state
->h
, FALSE
);
715 } else if ((button
== 'h' || button
== 'H') && !state
->completed
) {
716 if (!compute_hint(state
, &nx
, &ny
))
717 return NULL
; /* shouldn't happen, since ^^we^^checked^^ */
719 return NULL
; /* no move */
722 * Any click location should be equal to the gap location
723 * in _precisely_ one coordinate.
725 if ((cx
== nx
) ^ (cy
== ny
)) {
726 sprintf(buf
, "M%d,%d", nx
, ny
);
733 static game_state
*execute_move(const game_state
*from
, const char *move
)
735 int gx
, gy
, dx
, dy
, ux
, uy
, up
, p
;
738 if (!strcmp(move
, "S")) {
741 ret
= dup_game(from
);
744 * Simply replace the grid with a solved one. For this game,
745 * this isn't a useful operation for actually telling the user
746 * what they should have done, but it is useful for
747 * conveniently being able to get hold of a clean state from
748 * which to practise manoeuvres.
750 for (i
= 0; i
< ret
->n
; i
++)
751 ret
->tiles
[i
] = (i
+1) % ret
->n
;
752 ret
->gap_pos
= ret
->n
-1;
753 ret
->used_solve
= TRUE
;
754 ret
->completed
= ret
->movecount
= 1;
759 gx
= X(from
, from
->gap_pos
);
760 gy
= Y(from
, from
->gap_pos
);
762 if (move
[0] != 'M' ||
763 sscanf(move
+1, "%d,%d", &dx
, &dy
) != 2 ||
764 (dx
== gx
&& dy
== gy
) || (dx
!= gx
&& dy
!= gy
) ||
765 dx
< 0 || dx
>= from
->w
|| dy
< 0 || dy
>= from
->h
)
769 * Find the unit displacement from the original gap
770 * position towards this one.
772 ux
= (dx
< gx
? -1 : dx
> gx
? +1 : 0);
773 uy
= (dy
< gy
? -1 : dy
> gy
? +1 : 0);
774 up
= C(from
, ux
, uy
);
776 ret
= dup_game(from
);
778 ret
->gap_pos
= C(from
, dx
, dy
);
779 assert(ret
->gap_pos
>= 0 && ret
->gap_pos
< ret
->n
);
781 ret
->tiles
[ret
->gap_pos
] = 0;
783 for (p
= from
->gap_pos
; p
!= ret
->gap_pos
; p
+= up
) {
784 assert(p
>= 0 && p
< from
->n
);
785 ret
->tiles
[p
] = from
->tiles
[p
+ up
];
790 * See if the game has been completed.
792 if (!ret
->completed
) {
793 ret
->completed
= ret
->movecount
;
794 for (p
= 0; p
< ret
->n
; p
++)
795 if (ret
->tiles
[p
] != (p
< ret
->n
-1 ? p
+1 : 0))
802 /* ----------------------------------------------------------------------
806 static void game_compute_size(const game_params
*params
, int tilesize
,
809 /* Ick: fake up `ds->tilesize' for macro expansion purposes */
810 struct { int tilesize
; } ads
, *ds
= &ads
;
811 ads
.tilesize
= tilesize
;
813 *x
= TILE_SIZE
* params
->w
+ 2 * BORDER
;
814 *y
= TILE_SIZE
* params
->h
+ 2 * BORDER
;
817 static void game_set_size(drawing
*dr
, game_drawstate
*ds
,
818 const game_params
*params
, int tilesize
)
820 ds
->tilesize
= tilesize
;
823 static float *game_colours(frontend
*fe
, int *ncolours
)
825 float *ret
= snewn(3 * NCOLOURS
, float);
828 game_mkhighlight(fe
, ret
, COL_BACKGROUND
, COL_HIGHLIGHT
, COL_LOWLIGHT
);
830 for (i
= 0; i
< 3; i
++)
831 ret
[COL_TEXT
* 3 + i
] = 0.0;
833 *ncolours
= NCOLOURS
;
837 static game_drawstate
*game_new_drawstate(drawing
*dr
, const game_state
*state
)
839 struct game_drawstate
*ds
= snew(struct game_drawstate
);
845 ds
->bgcolour
= COL_BACKGROUND
;
846 ds
->tiles
= snewn(ds
->w
*ds
->h
, int);
847 ds
->tilesize
= 0; /* haven't decided yet */
848 for (i
= 0; i
< ds
->w
*ds
->h
; i
++)
854 static void game_free_drawstate(drawing
*dr
, game_drawstate
*ds
)
860 static void draw_tile(drawing
*dr
, game_drawstate
*ds
, const game_state
*state
,
861 int x
, int y
, int tile
, int flash_colour
)
864 draw_rect(dr
, x
, y
, TILE_SIZE
, TILE_SIZE
,
870 coords
[0] = x
+ TILE_SIZE
- 1;
871 coords
[1] = y
+ TILE_SIZE
- 1;
872 coords
[2] = x
+ TILE_SIZE
- 1;
875 coords
[5] = y
+ TILE_SIZE
- 1;
876 draw_polygon(dr
, coords
, 3, COL_LOWLIGHT
, COL_LOWLIGHT
);
880 draw_polygon(dr
, coords
, 3, COL_HIGHLIGHT
, COL_HIGHLIGHT
);
882 draw_rect(dr
, x
+ HIGHLIGHT_WIDTH
, y
+ HIGHLIGHT_WIDTH
,
883 TILE_SIZE
- 2*HIGHLIGHT_WIDTH
, TILE_SIZE
- 2*HIGHLIGHT_WIDTH
,
886 sprintf(str
, "%d", tile
);
887 draw_text(dr
, x
+ TILE_SIZE
/2, y
+ TILE_SIZE
/2,
888 FONT_VARIABLE
, TILE_SIZE
/3, ALIGN_VCENTRE
| ALIGN_HCENTRE
,
891 draw_update(dr
, x
, y
, TILE_SIZE
, TILE_SIZE
);
894 static void game_redraw(drawing
*dr
, game_drawstate
*ds
,
895 const game_state
*oldstate
, const game_state
*state
,
896 int dir
, const game_ui
*ui
,
897 float animtime
, float flashtime
)
899 int i
, pass
, bgcolour
;
902 int frame
= (int)(flashtime
/ FLASH_FRAME
);
903 bgcolour
= (frame
% 2 ? COL_LOWLIGHT
: COL_HIGHLIGHT
);
905 bgcolour
= COL_BACKGROUND
;
911 TILE_SIZE
* state
->w
+ 2 * BORDER
,
912 TILE_SIZE
* state
->h
+ 2 * BORDER
, COL_BACKGROUND
);
913 draw_update(dr
, 0, 0,
914 TILE_SIZE
* state
->w
+ 2 * BORDER
,
915 TILE_SIZE
* state
->h
+ 2 * BORDER
);
918 * Recessed area containing the whole puzzle.
920 coords
[0] = COORD(state
->w
) + HIGHLIGHT_WIDTH
- 1;
921 coords
[1] = COORD(state
->h
) + HIGHLIGHT_WIDTH
- 1;
922 coords
[2] = COORD(state
->w
) + HIGHLIGHT_WIDTH
- 1;
923 coords
[3] = COORD(0) - HIGHLIGHT_WIDTH
;
924 coords
[4] = coords
[2] - TILE_SIZE
;
925 coords
[5] = coords
[3] + TILE_SIZE
;
926 coords
[8] = COORD(0) - HIGHLIGHT_WIDTH
;
927 coords
[9] = COORD(state
->h
) + HIGHLIGHT_WIDTH
- 1;
928 coords
[6] = coords
[8] + TILE_SIZE
;
929 coords
[7] = coords
[9] - TILE_SIZE
;
930 draw_polygon(dr
, coords
, 5, COL_HIGHLIGHT
, COL_HIGHLIGHT
);
932 coords
[1] = COORD(0) - HIGHLIGHT_WIDTH
;
933 coords
[0] = COORD(0) - HIGHLIGHT_WIDTH
;
934 draw_polygon(dr
, coords
, 5, COL_LOWLIGHT
, COL_LOWLIGHT
);
940 * Now draw each tile. We do this in two passes to make
943 for (pass
= 0; pass
< 2; pass
++) {
944 for (i
= 0; i
< state
->n
; i
++) {
947 * Figure out what should be displayed at this
948 * location. It's either a simple tile, or it's a
949 * transition between two tiles (in which case we say
950 * -1 because it must always be drawn).
953 if (oldstate
&& oldstate
->tiles
[i
] != state
->tiles
[i
])
960 if (ds
->bgcolour
!= bgcolour
|| /* always redraw when flashing */
961 ds
->tiles
[i
] != t
|| ds
->tiles
[i
] == -1 || t
== -1) {
965 * Figure out what to _actually_ draw, and where to
973 * On the first pass, just blank the tile.
976 x
= COORD(X(state
, i
));
977 y
= COORD(Y(state
, i
));
985 * Don't bother moving the gap; just don't
992 * Find the coordinates of this tile in the old and
995 x1
= COORD(X(state
, i
));
996 y1
= COORD(Y(state
, i
));
997 for (j
= 0; j
< oldstate
->n
; j
++)
998 if (oldstate
->tiles
[j
] == state
->tiles
[i
])
1000 assert(j
< oldstate
->n
);
1001 x0
= COORD(X(state
, j
));
1002 y0
= COORD(Y(state
, j
));
1004 c
= (animtime
/ ANIM_TIME
);
1005 if (c
< 0.0F
) c
= 0.0F
;
1006 if (c
> 1.0F
) c
= 1.0F
;
1008 x
= x0
+ (int)(c
* (x1
- x0
));
1009 y
= y0
+ (int)(c
* (y1
- y0
));
1015 x
= COORD(X(state
, i
));
1016 y
= COORD(Y(state
, i
));
1019 draw_tile(dr
, ds
, state
, x
, y
, t
, bgcolour
);
1024 ds
->bgcolour
= bgcolour
;
1027 * Update the status bar.
1030 char statusbuf
[256];
1033 * Don't show the new status until we're also showing the
1034 * new _state_ - after the game animation is complete.
1039 if (state
->used_solve
)
1040 sprintf(statusbuf
, "Moves since auto-solve: %d",
1041 state
->movecount
- state
->completed
);
1043 sprintf(statusbuf
, "%sMoves: %d",
1044 (state
->completed
? "COMPLETED! " : ""),
1045 (state
->completed
? state
->completed
: state
->movecount
));
1047 status_bar(dr
, statusbuf
);
1051 static float game_anim_length(const game_state
*oldstate
,
1052 const game_state
*newstate
, int dir
, game_ui
*ui
)
1057 static float game_flash_length(const game_state
*oldstate
,
1058 const game_state
*newstate
, int dir
, game_ui
*ui
)
1060 if (!oldstate
->completed
&& newstate
->completed
&&
1061 !oldstate
->used_solve
&& !newstate
->used_solve
)
1062 return 2 * FLASH_FRAME
;
1067 static int game_status(const game_state
*state
)
1069 return state
->completed
? +1 : 0;
1072 static int game_timing_state(const game_state
*state
, game_ui
*ui
)
1077 static void game_print_size(const game_params
*params
, float *x
, float *y
)
1081 static void game_print(drawing
*dr
, const game_state
*state
, int tilesize
)
1086 #define thegame fifteen
1089 const struct game thegame
= {
1090 "Fifteen", "games.fifteen", "fifteen",
1092 game_fetch_preset
, NULL
,
1097 TRUE
, game_configure
, custom_params
,
1105 TRUE
, game_can_format_as_text_now
, game_text_format
,
1113 PREFERRED_TILE_SIZE
, game_compute_size
, game_set_size
,
1116 game_free_drawstate
,
1121 FALSE
, FALSE
, game_print_size
, game_print
,
1122 TRUE
, /* wants_statusbar */
1123 FALSE
, game_timing_state
,
1127 #ifdef STANDALONE_SOLVER
1129 int main(int argc
, char **argv
)
1131 game_params
*params
;
1133 char *id
= NULL
, *desc
, *err
;
1135 char *progname
= argv
[0];
1138 int limit
, x
, y
, solvable
;
1140 while (--argc
> 0) {
1142 if (!strcmp(p
, "-v")) {
1143 /* solver_show_working = TRUE; */
1144 } else if (!strcmp(p
, "-g")) {
1146 } else if (*p
== '-') {
1147 fprintf(stderr
, "%s: unrecognised option `%s'\n", progname
, p
);
1155 fprintf(stderr
, "usage: %s [-g | -v] <game_id>\n", argv
[0]);
1159 desc
= strchr(id
, ':');
1161 fprintf(stderr
, "%s: game id expects a colon in it\n", argv
[0]);
1166 params
= default_params();
1167 decode_params(params
, id
);
1168 err
= validate_desc(params
, desc
);
1170 free_params(params
);
1171 fprintf(stderr
, "%s: %s\n", argv
[0], err
);
1175 state
= new_game(NULL
, params
, desc
);
1176 free_params(params
);
1178 solvable
= (PARITY_S(state
) == perm_parity(state
->tiles
, state
->n
));
1179 if (grade
|| !solvable
) {
1181 fputs(solvable
? "Game is solvable" : "Game is unsolvable",
1182 grade
? stdout
: stderr
);
1186 for (limit
= 5 * state
->n
* state
->n
* state
->n
; limit
; --limit
) {
1187 game_state
*next_state
;
1188 if (!compute_hint(state
, &x
, &y
)) {
1189 fprintf(stderr
, "couldn't compute next move while solving %s:%s",
1193 printf("Move the space to (%d, %d), moving %d into the space\n",
1194 x
+ 1, y
+ 1, state
->tiles
[C(state
, x
, y
)]);
1195 sprintf(buf
, "M%d,%d", x
, y
);
1196 next_state
= execute_move(state
, buf
);
1200 fprintf(stderr
, "invalid move when solving %s:%s\n", id
, desc
);
1204 if (next_state
->completed
) {
1211 fprintf(stderr
, "ran out of moves for %s:%s\n", id
, desc
);