~ubuntu-branches/ubuntu/saucy/sgt-puzzles/saucy-proposed

« back to all changes in this revision

Viewing changes to loopy.c

  • Committer: Bazaar Package Importer
  • Author(s): Ben Hutchings
  • Date: 2011-07-11 03:56:55 UTC
  • mfrom: (1.2.10 upstream)
  • Revision ID: james.westby@ubuntu.com-20110711035655-qjakg0h7wv99tmu9
Tags: 9179-1
* New upstream version:
  - Remove unused-but-set variables - closes: #625425
  - Avoid infinite loop in Loopy at Easy level
  - Add Penrose tilings to Loopy
* Update German translation, thanks to Helge Kreutzmann
* Do not compile with -Werror

Show diffs side-by-side

added added

removed removed

Lines of Context:
107
107
};
108
108
 
109
109
struct game_state {
110
 
    grid *game_grid;
 
110
    grid *game_grid; /* ref-counted (internally) */
111
111
 
112
112
    /* Put -1 in a face that doesn't get a clue */
113
113
    signed char *clues;
201
201
SOLVERLIST(SOLVER_FN_DECL)
202
202
static int (*(solver_fns[]))(solver_state *) = { SOLVERLIST(SOLVER_FN) };
203
203
static int const solver_diffs[] = { SOLVERLIST(SOLVER_DIFF) };
204
 
const int NUM_SOLVERS = sizeof(solver_diffs)/sizeof(*solver_diffs);
 
204
static const int NUM_SOLVERS = sizeof(solver_diffs)/sizeof(*solver_diffs);
205
205
 
206
206
struct game_params {
207
207
    int w, h;
208
208
    int diff;
209
209
    int type;
210
 
 
211
 
    /* Grid generation is expensive, so keep a (ref-counted) reference to the
212
 
     * grid for these parameters, and only generate when required. */
213
 
    grid *game_grid;
214
210
};
215
211
 
216
212
/* line_drawstate is the same as line_state, but with the extra ERROR
228
224
    int started;
229
225
    int tilesize;
230
226
    int flashing;
 
227
    int *textx, *texty;
231
228
    char *lines;
232
229
    char *clue_error;
233
230
    char *clue_satisfied;
246
243
 
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)
260
259
 
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) \
265
264
    {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 {
273
272
    int amin, omin;
274
273
    char *aerr, *oerr;
276
275
 
277
276
/* Generates a (dynamically allocated) new grid, according to the
278
277
 * type and size requested in params.  Does nothing if the grid is already
279
 
 * generated.  The allocated grid is owned by the params object, and will be
280
 
 * freed in free_params(). */
281
 
static void params_generate_grid(game_params *params)
 
278
 * generated. */
 
279
static grid *loopy_generate_grid(game_params *params, char *grid_desc)
282
280
{
283
 
    if (!params->game_grid) {
284
 
        params->game_grid = grid_fns[params->type](params->w, params->h);
285
 
    }
 
281
    return grid_new(grid_types[params->type], params->w, params->h, grid_desc);
286
282
}
287
283
 
288
284
/* ----------------------------------------------------------------------
479
475
    ret->diff = DIFF_EASY;
480
476
    ret->type = 0;
481
477
 
482
 
    ret->game_grid = NULL;
483
 
 
484
478
    return ret;
485
479
}
486
480
 
489
483
    game_params *ret = snew(game_params);
490
484
 
491
485
    *ret = *params;                       /* structure copy */
492
 
    if (ret->game_grid) {
493
 
        ret->game_grid->refcount++;
494
 
    }
495
486
    return ret;
496
487
}
497
488
 
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 },
513
506
#else
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 }
530
525
#endif
531
526
};
532
527
 
550
545
 
551
546
static void free_params(game_params *params)
552
547
{
553
 
    if (params->game_grid) {
554
 
        grid_free(params->game_grid);
555
 
    }
556
548
    sfree(params);
557
549
}
558
550
 
559
551
static void decode_params(game_params *params, char const *string)
560
552
{
561
 
    if (params->game_grid) {
562
 
        grid_free(params->game_grid);
563
 
        params->game_grid = NULL;
564
 
    }
565
553
    params->h = params->w = atoi(string);
566
554
    params->diff = DIFF_EASY;
567
555
    while (*string && isdigit((unsigned char)*string)) string++;
640
628
    ret->type = cfg[2].ival;
641
629
    ret->diff = cfg[3].ival;
642
630
 
643
 
    ret->game_grid = NULL;
644
631
    return ret;
645
632
}
646
633
 
701
688
    return retval;
702
689
}
703
690
 
 
691
#define GRID_DESC_SEP '_'
 
692
 
 
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)
 
697
{
 
698
    char *sep = strchr(*desc, GRID_DESC_SEP), *gd;
 
699
    int gd_len;
 
700
 
 
701
    if (!sep) return NULL;
 
702
 
 
703
    gd_len = sep - (*desc);
 
704
    gd = snewn(gd_len+1, char);
 
705
    memcpy(gd, *desc, gd_len);
 
706
    gd[gd_len] = '\0';
 
707
 
 
708
    *desc = sep+1;
 
709
 
 
710
    return gd;
 
711
}
 
712
 
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)
707
716
{
708
717
    int count = 0;
709
718
    grid *g;
710
 
    params_generate_grid(params);
711
 
    g = params->game_grid;
 
719
    char *grid_desc, *ret;
 
720
 
 
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);
 
725
    if (ret) return ret;
 
726
 
 
727
    g = loopy_generate_grid(params, grid_desc);
 
728
    if (grid_desc) sfree(grid_desc);
712
729
 
713
730
    for (; *desc; ++desc) {
714
731
        if ((*desc >= '0' && *desc <= '9') || (*desc >= 'A' && *desc <= 'Z')) {
727
744
    if (count > g->num_faces)
728
745
        return "Description too long for board size";
729
746
 
 
747
    grid_free(g);
 
748
 
730
749
    return NULL;
731
750
}
732
751
 
808
827
static void game_compute_size(game_params *params, int tilesize,
809
828
                              int *x, int *y)
810
829
{
811
 
    grid *g;
812
830
    int grid_width, grid_height, rendered_width, rendered_height;
813
 
 
814
 
    params_generate_grid(params);
815
 
    g = params->game_grid;
816
 
    grid_width = g->highest_x - g->lowest_x;
817
 
    grid_height = g->highest_y - g->lowest_y;
 
831
    int g_tilesize;
 
832
 
 
833
    grid_compute_size(grid_types[params->type], params->w, params->h,
 
834
                      &g_tilesize, &grid_width, &grid_height);
 
835
 
818
836
    /* multiply first to minimise rounding error on integer division */
819
 
    rendered_width = grid_width * tilesize / g->tilesize;
820
 
    rendered_height = grid_height * tilesize / g->tilesize;
 
837
    rendered_width = grid_width * tilesize / g_tilesize;
 
838
    rendered_height = grid_height * tilesize / g_tilesize;
821
839
    *x = rendered_width + 2 * BORDER(tilesize) + 1;
822
840
    *y = rendered_height + 2 * BORDER(tilesize) + 1;
823
841
}
838
856
    ret[COL_FOREGROUND * 3 + 1] = 0.0F;
839
857
    ret[COL_FOREGROUND * 3 + 2] = 0.0F;
840
858
 
841
 
    ret[COL_LINEUNKNOWN * 3 + 0] = 0.8F;
842
 
    ret[COL_LINEUNKNOWN * 3 + 1] = 0.8F;
 
859
    /*
 
860
     * We want COL_LINEUNKNOWN to be a yellow which is a bit darker
 
861
     * than the background. (I previously set it to 0.8,0.8,0, but
 
862
     * found that this went badly with the 0.8,0.8,0.8 favoured as a
 
863
     * background by the Java frontend.)
 
864
     */
 
865
    ret[COL_LINEUNKNOWN * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 0.9F;
 
866
    ret[COL_LINEUNKNOWN * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.9F;
843
867
    ret[COL_LINEUNKNOWN * 3 + 2] = 0.0F;
844
868
 
845
869
    ret[COL_HIGHLIGHT * 3 + 0] = 1.0F;
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;
 
898
    int i;
874
899
 
875
900
    ds->tilesize = 0;
876
901
    ds->started = 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;
881
908
 
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;
885
914
 
886
915
    return ds;
887
916
}
888
917
 
889
918
static void game_free_drawstate(drawing *dr, game_drawstate *ds)
890
919
{
 
920
    sfree(ds->textx);
 
921
    sfree(ds->texty);
891
922
    sfree(ds->clue_error);
892
923
    sfree(ds->clue_satisfied);
893
924
    sfree(ds->lines);
1824
1855
                           char **aux, int interactive)
1825
1856
{
1826
1857
    /* solution and description both use run-length encoding in obvious ways */
1827
 
    char *retval;
 
1858
    char *retval, *game_desc, *grid_desc;
1828
1859
    grid *g;
1829
1860
    game_state *state = snew(game_state);
1830
1861
    game_state *state_new;
1831
 
    int count = 0;
1832
 
    params_generate_grid(params);
1833
 
    state->game_grid = g = params->game_grid;
1834
 
    g->refcount++;
 
1862
 
 
1863
    grid_desc = grid_new_desc(grid_types[params->type], params->w, params->h, rs);
 
1864
    state->game_grid = g = loopy_generate_grid(params, grid_desc);
 
1865
 
1835
1866
    state->clues = snewn(g->num_faces, signed char);
1836
1867
    state->lines = snewn(g->num_edges, char);
1837
1868
    state->line_errors = snewn(g->num_edges, unsigned char);
1850
1881
     * preventing games smaller than 4x4 seems to stop this happening */
1851
1882
    do {
1852
1883
        add_full_clues(state, rs);
1853
 
        if (++count%100 == 0) printf("tried %d times to make a unique board\n", count);
1854
1884
    } while (!game_has_unique_soln(state, params->diff));
1855
1885
 
1856
1886
    state_new = remove_clues(state, rs, params->diff);
1865
1895
        goto newboard_please;
1866
1896
    }
1867
1897
 
1868
 
    retval = state_to_text(state);
 
1898
    game_desc = state_to_text(state);
1869
1899
 
1870
1900
    free_game(state);
1871
1901
 
 
1902
    if (grid_desc) {
 
1903
        retval = snewn(strlen(grid_desc) + 1 + strlen(game_desc) + 1, char);
 
1904
        sprintf(retval, "%s%c%s", grid_desc, (int)GRID_DESC_SEP, game_desc);
 
1905
        sfree(grid_desc);
 
1906
        sfree(game_desc);
 
1907
    } else {
 
1908
        retval = game_desc;
 
1909
    }
 
1910
 
1872
1911
    assert(!validate_desc(params, retval));
1873
1912
 
1874
1913
    return retval;
1880
1919
    game_state *state = snew(game_state);
1881
1920
    int empties_to_make = 0;
1882
1921
    int n,n2;
1883
 
    const char *dp = desc;
 
1922
    const char *dp;
 
1923
    char *grid_desc;
1884
1924
    grid *g;
1885
1925
    int num_faces, num_edges;
1886
1926
 
1887
 
    params_generate_grid(params);
1888
 
    state->game_grid = g = params->game_grid;
1889
 
    g->refcount++;
 
1927
    grid_desc = extract_grid_desc(&desc);
 
1928
    state->game_grid = g = loopy_generate_grid(params, grid_desc);
 
1929
    if (grid_desc) sfree(grid_desc);
 
1930
 
 
1931
    dp = desc;
 
1932
 
1890
1933
    num_faces = g->num_faces;
1891
1934
    num_edges = g->num_edges;
1892
1935
 
2426
2469
        if (state->clues[i] < 0)
2427
2470
            continue;
2428
2471
 
 
2472
        /*
 
2473
         * This code checks whether the numeric clue on a face is so
 
2474
         * large as to permit all its remaining LINE_UNKNOWNs to be
 
2475
         * filled in as LINE_YES, or alternatively so small as to
 
2476
         * permit them all to be filled in as LINE_NO.
 
2477
         */
 
2478
 
2429
2479
        if (state->clues[i] < current_yes) {
2430
2480
            sstate->solver_status = SOLVER_MISTAKE;
2431
2481
            return DIFF_EASY;
2447
2497
            sstate->face_solved[i] = TRUE;
2448
2498
            continue;
2449
2499
        }
 
2500
 
 
2501
        if (f->order - state->clues[i] == current_no + 1 &&
 
2502
            f->order - current_yes - current_no > 2) {
 
2503
            /*
 
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.
 
2510
             */
 
2511
            int j, k, e1, e2, e, d;
 
2512
 
 
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;
 
2516
 
 
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;
 
2520
                } else {
 
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;
 
2524
                }
 
2525
 
 
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 */
 
2532
                    }
 
2533
                }
 
2534
            }
 
2535
            continue;
 
2536
 
 
2537
          found:
 
2538
            /*
 
2539
             * If we get here, we've found such a pair of edges, and
 
2540
             * they're e1 and e2.
 
2541
             */
 
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);
 
2546
                    assert(r);
 
2547
                    diff = min(diff, DIFF_EASY);
 
2548
                }
 
2549
            }
 
2550
        }
2450
2551
    }
2451
2552
 
2452
2553
    check_caches(sstate);
3336
3437
/* Returns (into x,y) position of centre of face for rendering the text clue.
3337
3438
 */
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)
3340
3441
{
3341
 
    int i;
3342
 
 
3343
 
    /* Simplest solution is the centroid. Might not work in some cases. */
3344
 
 
3345
 
    /* Another algorithm to look into:
3346
 
     * Find the midpoints of the sides, find the bounding-box,
3347
 
     * then take the centre of that. */
3348
 
 
3349
 
    /* Best solution probably involves incentres (inscribed circles) */
3350
 
 
3351
 
    int sx = 0, sy = 0; /* sums */
3352
 
    for (i = 0; i < f->order; i++) {
3353
 
        grid_dot *d = f->dots[i];
3354
 
        sx += d->x;
3355
 
        sy += d->y;
 
3442
    int faceindex = f - g->faces;
 
3443
 
 
3444
    /*
 
3445
     * Return the cached position for this face, if we've already
 
3446
     * worked it out.
 
3447
     */
 
3448
    if (ds->textx[faceindex] >= 0) {
 
3449
        *xret = ds->textx[faceindex];
 
3450
        *yret = ds->texty[faceindex];
 
3451
        return;
3356
3452
    }
3357
 
    sx /= f->order;
3358
 
    sy /= f->order;
3359
 
 
3360
 
    /* convert to screen coordinates */
3361
 
    grid_to_screen(ds, g, sx, sy, x, y);
 
3453
 
 
3454
    /*
 
3455
     * Otherwise, use the incentre computed by grid.c and convert it
 
3456
     * to screen coordinates.
 
3457
     */
 
3458
    grid_find_incentre(f);
 
3459
    grid_to_screen(ds, g, f->ix, f->iy,
 
3460
                   &ds->textx[faceindex], &ds->texty[faceindex]);
 
3461
 
 
3462
    *xret = ds->textx[faceindex];
 
3463
    *yret = ds->texty[faceindex];
 
3464
}
 
3465
 
 
3466
static void face_text_bbox(game_drawstate *ds, grid *g, grid_face *f,
 
3467
                           int *x, int *y, int *w, int *h)
 
3468
{
 
3469
    int xx, yy;
 
3470
    face_text_pos(ds, g, f, &xx, &yy);
 
3471
 
 
3472
    /* There seems to be a certain amount of trial-and-error involved
 
3473
     * in working out the correct bounding-box for the text. */
 
3474
 
 
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;
3362
3479
}
3363
3480
 
3364
3481
static void game_redraw_clue(drawing *dr, game_drawstate *ds,
3384
3501
              ds->clue_satisfied[i] ? COL_SATISFIED : COL_FOREGROUND, c);
3385
3502
}
3386
3503
 
 
3504
static void edge_bbox(game_drawstate *ds, grid *g, grid_edge *e,
 
3505
                      int *x, int *y, int *w, int *h)
 
3506
{
 
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;
 
3512
 
 
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;
 
3520
 
 
3521
    *x = xmin;
 
3522
    *y = ymin;
 
3523
    *w = xmax - xmin + 1;
 
3524
    *h = ymax - ymin + 1;
 
3525
}
 
3526
 
 
3527
static void dot_bbox(game_drawstate *ds, grid *g, grid_dot *d,
 
3528
                     int *x, int *y, int *w, int *h)
 
3529
{
 
3530
    int x1, y1;
 
3531
 
 
3532
    grid_to_screen(ds, g, d->x, d->y, &x1, &y1);
 
3533
 
 
3534
    *x = x1 - 2;
 
3535
    *y = y1 - 2;
 
3536
    *w = 5;
 
3537
    *h = 5;
 
3538
}
 
3539
 
 
3540
static const int loopy_line_redraw_phases[] = {
 
3541
    COL_FAINT, COL_LINEUNKNOWN, COL_FOREGROUND, COL_HIGHLIGHT, COL_MISTAKE
 
3542
};
 
3543
#define NPHASES lenof(loopy_line_redraw_phases)
 
3544
 
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)
3389
3547
{
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;
3395
3552
 
3396
3553
    if (state->line_errors[i])
3403
3560
        line_colour = COL_HIGHLIGHT;
3404
3561
    else
3405
3562
        line_colour = COL_FOREGROUND;
 
3563
    if (line_colour != loopy_line_redraw_phases[phase])
 
3564
        return;
3406
3565
 
3407
3566
    /* Convert from grid to screen coordinates */
3408
3567
    grid_to_screen(ds, g, e->dot1->x, e->dot1->y, &x1, &y1);
3409
3568
    grid_to_screen(ds, g, e->dot2->x, e->dot2->y, &x2, &y2);
3410
3569
 
3411
 
    xmin = min(x1, x2);
3412
 
    xmax = max(x1, x2);
3413
 
    ymin = min(y1, y2);
3414
 
    ymax = max(y1, y2);
3415
 
 
3416
3570
    if (line_colour == COL_FAINT) {
3417
3571
        static int draw_faint_lines = -1;
3418
3572
        if (draw_faint_lines < 0) {
3441
3595
    draw_circle(dr, x, y, 2, COL_FOREGROUND, COL_FOREGROUND);
3442
3596
}
3443
3597
 
 
3598
static int boxes_intersect(int x0, int y0, int w0, int h0,
 
3599
                           int x1, int y1, int w1, int h1)
 
3600
{
 
3601
    /*
 
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.
 
3605
     */
 
3606
    return (x0 < x1+w1 && x1 < x0+w0 && y0 < y1+h1 && y1 < y0+h0);
 
3607
}
 
3608
 
 
3609
static void game_redraw_in_rect(drawing *dr, game_drawstate *ds,
 
3610
                                game_state *state, int x, int y, int w, int h)
 
3611
{
 
3612
    grid *g = state->game_grid;
 
3613
    int i, phase;
 
3614
    int bx, by, bw, bh;
 
3615
 
 
3616
    clip(dr, x, y, w, h);
 
3617
    draw_rect(dr, x, y, w, h, COL_BACKGROUND);
 
3618
 
 
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);
 
3624
        }
 
3625
    }
 
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);
 
3631
        }
 
3632
    }
 
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);
 
3637
    }
 
3638
 
 
3639
    unclip(dr);
 
3640
    draw_update(dr, x, y, w, h);
 
3641
}
 
3642
 
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)
3539
3738
 
3540
3739
    /* Pass one is now done.  Now we do the actual drawing. */
3541
3740
    if (redraw_everything) {
3542
 
 
3543
 
        /* This is the unsubtle version. */
3544
 
 
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;
3549
3745
 
3550
 
        draw_rect(dr, 0, 0, w + 2*border + 1, h + 2*border + 1,
3551
 
                  COL_BACKGROUND);
3552
 
 
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);
3559
 
 
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);
3561
3748
    } else {
3562
3749
 
3563
3750
        /* Right.  Now we roll up our sleeves. */
3564
3751
 
3565
3752
        for (i = 0; i < nfaces; i++) {
3566
3753
            grid_face *f = g->faces + faces[i];
3567
 
            int xx, yy;
3568
3754
            int x, y, w, h;
3569
 
            int j;
3570
 
 
3571
 
            /* There seems to be a certain amount of trial-and-error
3572
 
             * involved in working out the correct bounding-box for
3573
 
             * the text. */
3574
 
            face_text_pos(ds, g, f, &xx, &yy);
3575
 
 
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);
3580
 
 
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);
3586
 
            unclip(dr);
3587
 
            draw_update(dr, x, y, w, h);
 
3755
 
 
3756
            face_text_bbox(ds, g, f, &x, &y, &w, &h);
 
3757
            game_redraw_in_rect(dr, ds, state, x, y, w, h);
3588
3758
        }
3589
3759
 
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;
3597
 
            int j;
3598
 
 
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,
3610
 
                      COL_BACKGROUND);
3611
 
 
3612
 
            if (e->face1)
3613
 
                game_redraw_clue(dr, ds, state, e->face1 - g->faces);
3614
 
            if (e->face2)
3615
 
                game_redraw_clue(dr, ds, state, e->face2 - g->faces);
3616
 
 
3617
 
            game_redraw_line(dr, ds, state, edges[i]);
3618
 
            for (j = 0; j < e->dot1->order; j++) {
3619
 
                ee = e->dot1->edges[j];
3620
 
                if (ee != e)
3621
 
                    game_redraw_line(dr, ds, state, ee - g->edges);
3622
 
            }
3623
 
            for (j = 0; j < e->dot2->order; j++) {
3624
 
                ee = e->dot2->edges[j];
3625
 
                if (ee != e)
3626
 
                    game_redraw_line(dr, ds, state, ee - g->edges);
3627
 
            }
3628
 
            game_redraw_dot(dr, ds, state, e->dot1 - g->dots);
3629
 
            game_redraw_dot(dr, ds, state, e->dot2 - g->dots);
3630
 
 
3631
 
            unclip(dr);
3632
 
            draw_update(dr, xmin, ymin, xmax - xmin + 1, ymax - ymin + 1);
 
3761
            grid_edge *e = g->edges + edges[i];
 
3762
            int x, y, w, h;
 
3763
 
 
3764
            edge_bbox(ds, g, e, &x, &y, &w, &h);
 
3765
            game_redraw_in_rect(dr, ds, state, x, y, w, h);
3633
3766
        }
3634
3767
    }
3635
3768
 
3647
3780
    return 0.0F;
3648
3781
}
3649
3782
 
 
3783
static int game_status(game_state *state)
 
3784
{
 
3785
    return state->solved ? +1 : 0;
 
3786
}
 
3787
 
3650
3788
static void game_print_size(game_params *params, float *x, float *y)
3651
3789
{
3652
3790
    int pw, ph;
3667
3805
    grid *g = state->game_grid;
3668
3806
 
3669
3807
    ds->tilesize = tilesize;
 
3808
    ds->textx = snewn(g->num_faces, int);
 
3809
    ds->texty = snewn(g->num_faces, int);
 
3810
    for (i = 0; i < g->num_faces; i++)
 
3811
        ds->textx[i] = ds->texty[i] = -1;
3670
3812
 
3671
3813
    for (i = 0; i < g->num_dots; i++) {
3672
3814
        int x, y;
3736
3878
            }
3737
3879
        }
3738
3880
    }
 
3881
 
 
3882
    sfree(ds->textx);
 
3883
    sfree(ds->texty);
3739
3884
}
3740
3885
 
3741
3886
#ifdef COMBINED
3773
3918
    game_redraw,
3774
3919
    game_anim_length,
3775
3920
    game_flash_length,
 
3921
    game_status,
3776
3922
    TRUE, FALSE, game_print_size, game_print,
3777
3923
    FALSE /* wants_statusbar */,
3778
3924
    FALSE, game_timing_state,
3905
4051
}
3906
4052
 
3907
4053
#endif
 
4054
 
 
4055
/* vim: set shiftwidth=4 tabstop=8: */