247
244
/* ------- List of grid generators ------- */
248
245
#define GRIDLIST(A) \
249
A(Squares,grid_new_square,3,3) \
250
A(Triangular,grid_new_triangular,3,3) \
251
A(Honeycomb,grid_new_honeycomb,3,3) \
252
A(Snub-Square,grid_new_snubsquare,3,3) \
253
A(Cairo,grid_new_cairo,3,4) \
254
A(Great-Hexagonal,grid_new_greathexagonal,3,3) \
255
A(Octagonal,grid_new_octagonal,3,3) \
256
A(Kites,grid_new_kites,3,3) \
257
A(Floret,grid_new_floret,1,2) \
258
A(Dodecagonal,grid_new_dodecagonal,2,2) \
259
A(Great-Dodecagonal,grid_new_greatdodecagonal,2,2)
246
A(Squares,GRID_SQUARE,3,3) \
247
A(Triangular,GRID_TRIANGULAR,3,3) \
248
A(Honeycomb,GRID_HONEYCOMB,3,3) \
249
A(Snub-Square,GRID_SNUBSQUARE,3,3) \
250
A(Cairo,GRID_CAIRO,3,4) \
251
A(Great-Hexagonal,GRID_GREATHEXAGONAL,3,3) \
252
A(Octagonal,GRID_OCTAGONAL,3,3) \
253
A(Kites,GRID_KITE,3,3) \
254
A(Floret,GRID_FLORET,1,2) \
255
A(Dodecagonal,GRID_DODECAGONAL,2,2) \
256
A(Great-Dodecagonal,GRID_GREATDODECAGONAL,2,2) \
257
A(Penrose (kite/dart),GRID_PENROSE_P2,3,3) \
258
A(Penrose (rhombs),GRID_PENROSE_P3,3,3)
261
#define GRID_NAME(title,fn,amin,omin) #title,
262
#define GRID_CONFIG(title,fn,amin,omin) ":" #title
263
#define GRID_FN(title,fn,amin,omin) &fn,
264
#define GRID_SIZES(title,fn,amin,omin) \
260
#define GRID_NAME(title,type,amin,omin) #title,
261
#define GRID_CONFIG(title,type,amin,omin) ":" #title
262
#define GRID_TYPE(title,type,amin,omin) type,
263
#define GRID_SIZES(title,type,amin,omin) \
266
265
"Width and height for this grid type must both be at least " #amin, \
267
266
"At least one of width and height for this grid type must be at least " #omin,},
268
267
static char const *const gridnames[] = { GRIDLIST(GRID_NAME) };
269
268
#define GRID_CONFIGS GRIDLIST(GRID_CONFIG)
270
static grid * (*(grid_fns[]))(int w, int h) = { GRIDLIST(GRID_FN) };
271
#define NUM_GRID_TYPES (sizeof(grid_fns) / sizeof(grid_fns[0]))
269
static grid_type grid_types[] = { GRIDLIST(GRID_TYPE) };
270
#define NUM_GRID_TYPES (sizeof(grid_types) / sizeof(grid_types[0]))
272
271
static const struct {
274
273
char *aerr, *oerr;
489
483
game_params *ret = snew(game_params);
491
485
*ret = *params; /* structure copy */
492
if (ret->game_grid) {
493
ret->game_grid->refcount++;
498
489
static const game_params presets[] = {
499
490
#ifdef SMALL_SCREEN
500
{ 7, 7, DIFF_EASY, 0, NULL },
501
{ 7, 7, DIFF_NORMAL, 0, NULL },
502
{ 7, 7, DIFF_HARD, 0, NULL },
503
{ 7, 7, DIFF_HARD, 1, NULL },
504
{ 7, 7, DIFF_HARD, 2, NULL },
505
{ 5, 5, DIFF_HARD, 3, NULL },
506
{ 7, 7, DIFF_HARD, 4, NULL },
507
{ 5, 4, DIFF_HARD, 5, NULL },
508
{ 5, 5, DIFF_HARD, 6, NULL },
509
{ 5, 5, DIFF_HARD, 7, NULL },
510
{ 3, 3, DIFF_HARD, 8, NULL },
511
{ 3, 3, DIFF_HARD, 9, NULL },
512
{ 3, 3, DIFF_HARD, 10, NULL },
491
{ 7, 7, DIFF_EASY, 0 },
492
{ 7, 7, DIFF_NORMAL, 0 },
493
{ 7, 7, DIFF_HARD, 0 },
494
{ 7, 7, DIFF_HARD, 1 },
495
{ 7, 7, DIFF_HARD, 2 },
496
{ 5, 5, DIFF_HARD, 3 },
497
{ 7, 7, DIFF_HARD, 4 },
498
{ 5, 4, DIFF_HARD, 5 },
499
{ 5, 5, DIFF_HARD, 6 },
500
{ 5, 5, DIFF_HARD, 7 },
501
{ 3, 3, DIFF_HARD, 8 },
502
{ 3, 3, DIFF_HARD, 9 },
503
{ 3, 3, DIFF_HARD, 10 },
504
{ 6, 6, DIFF_HARD, 11 },
505
{ 6, 6, DIFF_HARD, 12 },
514
{ 7, 7, DIFF_EASY, 0, NULL },
515
{ 10, 10, DIFF_EASY, 0, NULL },
516
{ 7, 7, DIFF_NORMAL, 0, NULL },
517
{ 10, 10, DIFF_NORMAL, 0, NULL },
518
{ 7, 7, DIFF_HARD, 0, NULL },
519
{ 10, 10, DIFF_HARD, 0, NULL },
520
{ 10, 10, DIFF_HARD, 1, NULL },
521
{ 12, 10, DIFF_HARD, 2, NULL },
522
{ 7, 7, DIFF_HARD, 3, NULL },
523
{ 9, 9, DIFF_HARD, 4, NULL },
524
{ 5, 4, DIFF_HARD, 5, NULL },
525
{ 7, 7, DIFF_HARD, 6, NULL },
526
{ 5, 5, DIFF_HARD, 7, NULL },
527
{ 5, 5, DIFF_HARD, 8, NULL },
528
{ 5, 4, DIFF_HARD, 9, NULL },
529
{ 5, 4, DIFF_HARD, 10, NULL },
507
{ 7, 7, DIFF_EASY, 0 },
508
{ 10, 10, DIFF_EASY, 0 },
509
{ 7, 7, DIFF_NORMAL, 0 },
510
{ 10, 10, DIFF_NORMAL, 0 },
511
{ 7, 7, DIFF_HARD, 0 },
512
{ 10, 10, DIFF_HARD, 0 },
513
{ 10, 10, DIFF_HARD, 1 },
514
{ 12, 10, DIFF_HARD, 2 },
515
{ 7, 7, DIFF_HARD, 3 },
516
{ 9, 9, DIFF_HARD, 4 },
517
{ 5, 4, DIFF_HARD, 5 },
518
{ 7, 7, DIFF_HARD, 6 },
519
{ 5, 5, DIFF_HARD, 7 },
520
{ 5, 5, DIFF_HARD, 8 },
521
{ 5, 4, DIFF_HARD, 9 },
522
{ 5, 4, DIFF_HARD, 10 },
523
{ 10, 10, DIFF_HARD, 11 },
524
{ 10, 10, DIFF_HARD, 12 }
691
#define GRID_DESC_SEP '_'
693
/* Splits up a (optional) grid_desc from the game desc. Returns the
694
* grid_desc (which needs freeing) and updates the desc pointer to
695
* start of real desc, or returns NULL if no desc. */
696
static char *extract_grid_desc(char **desc)
698
char *sep = strchr(*desc, GRID_DESC_SEP), *gd;
701
if (!sep) return NULL;
703
gd_len = sep - (*desc);
704
gd = snewn(gd_len+1, char);
705
memcpy(gd, *desc, gd_len);
704
713
/* We require that the params pass the test in validate_params and that the
705
714
* description fills the entire game area */
706
715
static char *validate_desc(game_params *params, char *desc)
710
params_generate_grid(params);
711
g = params->game_grid;
719
char *grid_desc, *ret;
721
/* It's pretty inefficient to do this just for validation. All we need to
722
* know is the precise number of faces. */
723
grid_desc = extract_grid_desc(&desc);
724
ret = grid_validate_desc(grid_types[params->type], params->w, params->h, grid_desc);
727
g = loopy_generate_grid(params, grid_desc);
728
if (grid_desc) sfree(grid_desc);
713
730
for (; *desc; ++desc) {
714
731
if ((*desc >= '0' && *desc <= '9') || (*desc >= 'A' && *desc <= 'Z')) {
871
895
struct game_drawstate *ds = snew(struct game_drawstate);
872
896
int num_faces = state->game_grid->num_faces;
873
897
int num_edges = state->game_grid->num_edges;
875
900
ds->tilesize = 0;
877
902
ds->lines = snewn(num_edges, char);
878
903
ds->clue_error = snewn(num_faces, char);
879
904
ds->clue_satisfied = snewn(num_faces, char);
905
ds->textx = snewn(num_faces, int);
906
ds->texty = snewn(num_faces, int);
880
907
ds->flashing = 0;
882
909
memset(ds->lines, LINE_UNKNOWN, num_edges);
883
910
memset(ds->clue_error, 0, num_faces);
884
911
memset(ds->clue_satisfied, 0, num_faces);
912
for (i = 0; i < num_faces; i++)
913
ds->textx[i] = ds->texty[i] = -1;
889
918
static void game_free_drawstate(drawing *dr, game_drawstate *ds)
891
922
sfree(ds->clue_error);
892
923
sfree(ds->clue_satisfied);
893
924
sfree(ds->lines);
2447
2497
sstate->face_solved[i] = TRUE;
2501
if (f->order - state->clues[i] == current_no + 1 &&
2502
f->order - current_yes - current_no > 2) {
2504
* One small refinement to the above: we also look for any
2505
* adjacent pair of LINE_UNKNOWNs around the face with
2506
* some LINE_YES incident on it from elsewhere. If we find
2507
* one, then we know that pair of LINE_UNKNOWNs can't
2508
* _both_ be LINE_YES, and hence that pushes us one line
2509
* closer to being able to determine all the rest.
2511
int j, k, e1, e2, e, d;
2513
for (j = 0; j < f->order; j++) {
2514
e1 = f->edges[j] - g->edges;
2515
e2 = f->edges[j+1 < f->order ? j+1 : 0] - g->edges;
2517
if (g->edges[e1].dot1 == g->edges[e2].dot1 ||
2518
g->edges[e1].dot1 == g->edges[e2].dot2) {
2519
d = g->edges[e1].dot1 - g->dots;
2521
assert(g->edges[e1].dot2 == g->edges[e2].dot1 ||
2522
g->edges[e1].dot2 == g->edges[e2].dot2);
2523
d = g->edges[e1].dot2 - g->dots;
2526
if (state->lines[e1] == LINE_UNKNOWN &&
2527
state->lines[e2] == LINE_UNKNOWN) {
2528
for (k = 0; k < g->dots[d].order; k++) {
2529
int e = g->dots[d].edges[k] - g->edges;
2530
if (state->lines[e] == LINE_YES)
2531
goto found; /* multi-level break */
2539
* If we get here, we've found such a pair of edges, and
2540
* they're e1 and e2.
2542
for (j = 0; j < f->order; j++) {
2543
e = f->edges[j] - g->edges;
2544
if (state->lines[e] == LINE_UNKNOWN && e != e1 && e != e2) {
2545
int r = solver_set_line(sstate, e, LINE_YES);
2547
diff = min(diff, DIFF_EASY);
2452
2553
check_caches(sstate);
3336
3437
/* Returns (into x,y) position of centre of face for rendering the text clue.
3338
3439
static void face_text_pos(const game_drawstate *ds, const grid *g,
3339
const grid_face *f, int *x, int *y)
3440
grid_face *f, int *xret, int *yret)
3343
/* Simplest solution is the centroid. Might not work in some cases. */
3345
/* Another algorithm to look into:
3346
* Find the midpoints of the sides, find the bounding-box,
3347
* then take the centre of that. */
3349
/* Best solution probably involves incentres (inscribed circles) */
3351
int sx = 0, sy = 0; /* sums */
3352
for (i = 0; i < f->order; i++) {
3353
grid_dot *d = f->dots[i];
3442
int faceindex = f - g->faces;
3445
* Return the cached position for this face, if we've already
3448
if (ds->textx[faceindex] >= 0) {
3449
*xret = ds->textx[faceindex];
3450
*yret = ds->texty[faceindex];
3360
/* convert to screen coordinates */
3361
grid_to_screen(ds, g, sx, sy, x, y);
3455
* Otherwise, use the incentre computed by grid.c and convert it
3456
* to screen coordinates.
3458
grid_find_incentre(f);
3459
grid_to_screen(ds, g, f->ix, f->iy,
3460
&ds->textx[faceindex], &ds->texty[faceindex]);
3462
*xret = ds->textx[faceindex];
3463
*yret = ds->texty[faceindex];
3466
static void face_text_bbox(game_drawstate *ds, grid *g, grid_face *f,
3467
int *x, int *y, int *w, int *h)
3470
face_text_pos(ds, g, f, &xx, &yy);
3472
/* There seems to be a certain amount of trial-and-error involved
3473
* in working out the correct bounding-box for the text. */
3475
*x = xx - ds->tilesize/4 - 1;
3476
*y = yy - ds->tilesize/4 - 3;
3477
*w = ds->tilesize/2 + 2;
3478
*h = ds->tilesize/2 + 5;
3364
3481
static void game_redraw_clue(drawing *dr, game_drawstate *ds,
3384
3501
ds->clue_satisfied[i] ? COL_SATISFIED : COL_FOREGROUND, c);
3504
static void edge_bbox(game_drawstate *ds, grid *g, grid_edge *e,
3505
int *x, int *y, int *w, int *h)
3507
int x1 = e->dot1->x;
3508
int y1 = e->dot1->y;
3509
int x2 = e->dot2->x;
3510
int y2 = e->dot2->y;
3511
int xmin, xmax, ymin, ymax;
3513
grid_to_screen(ds, g, x1, y1, &x1, &y1);
3514
grid_to_screen(ds, g, x2, y2, &x2, &y2);
3515
/* Allow extra margin for dots, and thickness of lines */
3516
xmin = min(x1, x2) - 2;
3517
xmax = max(x1, x2) + 2;
3518
ymin = min(y1, y2) - 2;
3519
ymax = max(y1, y2) + 2;
3523
*w = xmax - xmin + 1;
3524
*h = ymax - ymin + 1;
3527
static void dot_bbox(game_drawstate *ds, grid *g, grid_dot *d,
3528
int *x, int *y, int *w, int *h)
3532
grid_to_screen(ds, g, d->x, d->y, &x1, &y1);
3540
static const int loopy_line_redraw_phases[] = {
3541
COL_FAINT, COL_LINEUNKNOWN, COL_FOREGROUND, COL_HIGHLIGHT, COL_MISTAKE
3543
#define NPHASES lenof(loopy_line_redraw_phases)
3387
3545
static void game_redraw_line(drawing *dr, game_drawstate *ds,
3388
game_state *state, int i)
3546
game_state *state, int i, int phase)
3390
3548
grid *g = state->game_grid;
3391
3549
grid_edge *e = g->edges + i;
3392
3550
int x1, x2, y1, y2;
3393
int xmin, ymin, xmax, ymax;
3394
3551
int line_colour;
3396
3553
if (state->line_errors[i])
3441
3595
draw_circle(dr, x, y, 2, COL_FOREGROUND, COL_FOREGROUND);
3598
static int boxes_intersect(int x0, int y0, int w0, int h0,
3599
int x1, int y1, int w1, int h1)
3602
* Two intervals intersect iff neither is wholly on one side of
3603
* the other. Two boxes intersect iff their horizontal and
3604
* vertical intervals both intersect.
3606
return (x0 < x1+w1 && x1 < x0+w0 && y0 < y1+h1 && y1 < y0+h0);
3609
static void game_redraw_in_rect(drawing *dr, game_drawstate *ds,
3610
game_state *state, int x, int y, int w, int h)
3612
grid *g = state->game_grid;
3616
clip(dr, x, y, w, h);
3617
draw_rect(dr, x, y, w, h, COL_BACKGROUND);
3619
for (i = 0; i < g->num_faces; i++) {
3620
if (state->clues[i] >= 0) {
3621
face_text_bbox(ds, g, &g->faces[i], &bx, &by, &bw, &bh);
3622
if (boxes_intersect(x, y, w, h, bx, by, bw, bh))
3623
game_redraw_clue(dr, ds, state, i);
3626
for (phase = 0; phase < NPHASES; phase++) {
3627
for (i = 0; i < g->num_edges; i++) {
3628
edge_bbox(ds, g, &g->edges[i], &bx, &by, &bw, &bh);
3629
if (boxes_intersect(x, y, w, h, bx, by, bw, bh))
3630
game_redraw_line(dr, ds, state, i, phase);
3633
for (i = 0; i < g->num_dots; i++) {
3634
dot_bbox(ds, g, &g->dots[i], &bx, &by, &bw, &bh);
3635
if (boxes_intersect(x, y, w, h, bx, by, bw, bh))
3636
game_redraw_dot(dr, ds, state, i);
3640
draw_update(dr, x, y, w, h);
3444
3643
static void game_redraw(drawing *dr, game_drawstate *ds, game_state *oldstate,
3445
3644
game_state *state, int dir, game_ui *ui,
3446
3645
float animtime, float flashtime)
3540
3739
/* Pass one is now done. Now we do the actual drawing. */
3541
3740
if (redraw_everything) {
3543
/* This is the unsubtle version. */
3545
3741
int grid_width = g->highest_x - g->lowest_x;
3546
3742
int grid_height = g->highest_y - g->lowest_y;
3547
3743
int w = grid_width * ds->tilesize / g->tilesize;
3548
3744
int h = grid_height * ds->tilesize / g->tilesize;
3550
draw_rect(dr, 0, 0, w + 2*border + 1, h + 2*border + 1,
3553
for (i = 0; i < g->num_faces; i++)
3554
game_redraw_clue(dr, ds, state, i);
3555
for (i = 0; i < g->num_edges; i++)
3556
game_redraw_line(dr, ds, state, i);
3557
for (i = 0; i < g->num_dots; i++)
3558
game_redraw_dot(dr, ds, state, i);
3560
draw_update(dr, 0, 0, w + 2*border + 1, h + 2*border + 1);
3746
game_redraw_in_rect(dr, ds, state,
3747
0, 0, w + 2*border + 1, h + 2*border + 1);
3563
3750
/* Right. Now we roll up our sleeves. */
3565
3752
for (i = 0; i < nfaces; i++) {
3566
3753
grid_face *f = g->faces + faces[i];
3568
3754
int x, y, w, h;
3571
/* There seems to be a certain amount of trial-and-error
3572
* involved in working out the correct bounding-box for
3574
face_text_pos(ds, g, f, &xx, &yy);
3576
x = xx - ds->tilesize/4 - 1; w = ds->tilesize/2 + 2;
3577
y = yy - ds->tilesize/4 - 3; h = ds->tilesize/2 + 5;
3578
clip(dr, x, y, w, h);
3579
draw_rect(dr, x, y, w, h, COL_BACKGROUND);
3581
game_redraw_clue(dr, ds, state, faces[i]);
3582
for (j = 0; j < f->order; j++)
3583
game_redraw_line(dr, ds, state, f->edges[j] - g->edges);
3584
for (j = 0; j < f->order; j++)
3585
game_redraw_dot(dr, ds, state, f->dots[j] - g->dots);
3587
draw_update(dr, x, y, w, h);
3756
face_text_bbox(ds, g, f, &x, &y, &w, &h);
3757
game_redraw_in_rect(dr, ds, state, x, y, w, h);
3590
3760
for (i = 0; i < nedges; i++) {
3591
grid_edge *e = g->edges + edges[i], *ee;
3592
int x1 = e->dot1->x;
3593
int y1 = e->dot1->y;
3594
int x2 = e->dot2->x;
3595
int y2 = e->dot2->y;
3596
int xmin, xmax, ymin, ymax;
3599
grid_to_screen(ds, g, x1, y1, &x1, &y1);
3600
grid_to_screen(ds, g, x2, y2, &x2, &y2);
3601
/* Allow extra margin for dots, and thickness of lines */
3602
xmin = min(x1, x2) - 2;
3603
xmax = max(x1, x2) + 2;
3604
ymin = min(y1, y2) - 2;
3605
ymax = max(y1, y2) + 2;
3606
/* For testing, I find it helpful to change COL_BACKGROUND
3607
* to COL_SATISFIED here. */
3608
clip(dr, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
3609
draw_rect(dr, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1,
3613
game_redraw_clue(dr, ds, state, e->face1 - g->faces);
3615
game_redraw_clue(dr, ds, state, e->face2 - g->faces);
3617
game_redraw_line(dr, ds, state, edges[i]);
3618
for (j = 0; j < e->dot1->order; j++) {
3619
ee = e->dot1->edges[j];
3621
game_redraw_line(dr, ds, state, ee - g->edges);
3623
for (j = 0; j < e->dot2->order; j++) {
3624
ee = e->dot2->edges[j];
3626
game_redraw_line(dr, ds, state, ee - g->edges);
3628
game_redraw_dot(dr, ds, state, e->dot1 - g->dots);
3629
game_redraw_dot(dr, ds, state, e->dot2 - g->dots);
3632
draw_update(dr, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
3761
grid_edge *e = g->edges + edges[i];
3764
edge_bbox(ds, g, e, &x, &y, &w, &h);
3765
game_redraw_in_rect(dr, ds, state, x, y, w, h);