flood.c 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392
  1. /*
  2. * flood.c: puzzle in which you make a grid all the same colour by
  3. * repeatedly flood-filling the top left corner, and try to do so in
  4. * as few moves as possible.
  5. */
  6. /*
  7. * Possible further work:
  8. *
  9. * - UI: perhaps we should only permit clicking on regions that can
  10. * actually be reached by the next flood-fill - i.e. a click is
  11. * only interpreted as a move if it would cause the clicked-on
  12. * square to become part of the controlled area. This provides a
  13. * hint in cases where you mistakenly thought that would happen,
  14. * and protects you against typos in cases where you just
  15. * mis-aimed.
  16. *
  17. * - UI: perhaps mark the fill square in some way? Or even mark the
  18. * whole connected component _containing_ the fill square. Pro:
  19. * that would make it easier to tell apart cases where almost all
  20. * the yellow squares in the grid are part of the target component
  21. * (hence, yellow is _done_ and you never have to fill in that
  22. * colour again) from cases where there's still one yellow square
  23. * that's only diagonally adjacent and hence will need coming back
  24. * to. Con: but it would almost certainly be ugly.
  25. */
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <stdarg.h>
  29. #include <string.h>
  30. #include <assert.h>
  31. #include <ctype.h>
  32. #include <limits.h>
  33. #ifdef NO_TGMATH_H
  34. # include <math.h>
  35. #else
  36. # include <tgmath.h>
  37. #endif
  38. #include "puzzles.h"
  39. enum {
  40. COL_BACKGROUND, COL_SEPARATOR,
  41. COL_1, COL_2, COL_3, COL_4, COL_5, COL_6, COL_7, COL_8, COL_9, COL_10,
  42. COL_HIGHLIGHT, COL_LOWLIGHT,
  43. NCOLOURS
  44. };
  45. struct game_params {
  46. int w, h;
  47. int colours;
  48. int leniency;
  49. };
  50. /* Just in case I want to make this changeable later, I'll put the
  51. * coordinates of the flood-fill point here so that it'll be easy to
  52. * find everywhere later that has to change. */
  53. #define FILLX 0
  54. #define FILLY 0
  55. typedef struct soln {
  56. int refcount;
  57. int nmoves;
  58. char *moves;
  59. } soln;
  60. struct game_state {
  61. int w, h, colours;
  62. int moves, movelimit;
  63. bool complete;
  64. char *grid;
  65. bool cheated;
  66. int solnpos;
  67. soln *soln;
  68. };
  69. static game_params *default_params(void)
  70. {
  71. game_params *ret = snew(game_params);
  72. ret->w = ret->h = 12;
  73. ret->colours = 6;
  74. ret->leniency = 5;
  75. return ret;
  76. }
  77. static const struct {
  78. struct game_params preset;
  79. const char *name;
  80. } flood_presets[] = {
  81. /* Default 12x12 size, three difficulty levels. */
  82. {{12, 12, 6, 5}, "12x12 Easy"},
  83. {{12, 12, 6, 2}, "12x12 Medium"},
  84. {{12, 12, 6, 0}, "12x12 Hard"},
  85. /* Larger puzzles, leaving off Easy in the expectation that people
  86. * wanting a bigger grid will have played it enough to find Easy
  87. * easy. */
  88. {{16, 16, 6, 2}, "16x16 Medium"},
  89. {{16, 16, 6, 0}, "16x16 Hard"},
  90. /* A couple of different colour counts. It seems generally not too
  91. * hard with fewer colours (probably because fewer choices), so no
  92. * extra moves for these modes. */
  93. {{12, 12, 3, 0}, "12x12, 3 colours"},
  94. {{12, 12, 4, 0}, "12x12, 4 colours"},
  95. };
  96. static bool game_fetch_preset(int i, char **name, game_params **params)
  97. {
  98. game_params *ret;
  99. if (i < 0 || i >= lenof(flood_presets))
  100. return false;
  101. ret = snew(game_params);
  102. *ret = flood_presets[i].preset;
  103. *name = dupstr(flood_presets[i].name);
  104. *params = ret;
  105. return true;
  106. }
  107. static void free_params(game_params *params)
  108. {
  109. sfree(params);
  110. }
  111. static game_params *dup_params(const game_params *params)
  112. {
  113. game_params *ret = snew(game_params);
  114. *ret = *params; /* structure copy */
  115. return ret;
  116. }
  117. static void decode_params(game_params *ret, char const *string)
  118. {
  119. ret->w = ret->h = atoi(string);
  120. while (*string && isdigit((unsigned char)*string)) string++;
  121. if (*string == 'x') {
  122. string++;
  123. ret->h = atoi(string);
  124. while (*string && isdigit((unsigned char)*string)) string++;
  125. }
  126. while (*string) {
  127. if (*string == 'c') {
  128. string++;
  129. ret->colours = atoi(string);
  130. while (*string && isdigit((unsigned char)*string)) string++;
  131. } else if (*string == 'm') {
  132. string++;
  133. ret->leniency = atoi(string);
  134. while (*string && isdigit((unsigned char)*string)) string++;
  135. } else
  136. string++;
  137. }
  138. }
  139. static char *encode_params(const game_params *params, bool full)
  140. {
  141. char buf[256];
  142. sprintf(buf, "%dx%d", params->w, params->h);
  143. if (full)
  144. sprintf(buf + strlen(buf), "c%dm%d",
  145. params->colours, params->leniency);
  146. return dupstr(buf);
  147. }
  148. static config_item *game_configure(const game_params *params)
  149. {
  150. config_item *ret;
  151. char buf[80];
  152. ret = snewn(5, config_item);
  153. ret[0].name = "Width";
  154. ret[0].type = C_STRING;
  155. sprintf(buf, "%d", params->w);
  156. ret[0].u.string.sval = dupstr(buf);
  157. ret[1].name = "Height";
  158. ret[1].type = C_STRING;
  159. sprintf(buf, "%d", params->h);
  160. ret[1].u.string.sval = dupstr(buf);
  161. ret[2].name = "Colours";
  162. ret[2].type = C_STRING;
  163. sprintf(buf, "%d", params->colours);
  164. ret[2].u.string.sval = dupstr(buf);
  165. ret[3].name = "Extra moves permitted";
  166. ret[3].type = C_STRING;
  167. sprintf(buf, "%d", params->leniency);
  168. ret[3].u.string.sval = dupstr(buf);
  169. ret[4].name = NULL;
  170. ret[4].type = C_END;
  171. return ret;
  172. }
  173. static game_params *custom_params(const config_item *cfg)
  174. {
  175. game_params *ret = snew(game_params);
  176. ret->w = atoi(cfg[0].u.string.sval);
  177. ret->h = atoi(cfg[1].u.string.sval);
  178. ret->colours = atoi(cfg[2].u.string.sval);
  179. ret->leniency = atoi(cfg[3].u.string.sval);
  180. return ret;
  181. }
  182. static const char *validate_params(const game_params *params, bool full)
  183. {
  184. if (params->w < 2 && params->h < 2)
  185. return "Grid must contain at least two squares";
  186. if (params->w < 1 || params->h < 1)
  187. return "Width and height must be at least one";
  188. if (params->w > INT_MAX / params->h)
  189. return "Width times height must not be unreasonably large";
  190. if (params->colours < 3 || params->colours > 10)
  191. return "Must have between 3 and 10 colours";
  192. if (params->leniency < 0)
  193. return "Leniency must be non-negative";
  194. return NULL;
  195. }
  196. #if 0
  197. /*
  198. * Bodge to permit varying the recursion depth for testing purposes.
  199. To test two Floods against each other:
  200. paste <(./flood.1 --generate 100 12x12c6m0#12345 | cut -f2 -d,) <(./flood.2 --generate 100 12x12c6m0#12345 | cut -f2 -d,) | awk '{print $2-$1}' | sort -n | uniq -c | awk '{print $2,$1}' | tee z
  201. and then run gnuplot and plot "z".
  202. */
  203. static int rdepth = 0;
  204. #define RECURSION_DEPTH (rdepth)
  205. void check_recursion_depth(void)
  206. {
  207. if (!rdepth) {
  208. const char *depthstr = getenv("FLOOD_DEPTH");
  209. rdepth = depthstr ? atoi(depthstr) : 1;
  210. rdepth = rdepth > 0 ? rdepth : 1;
  211. }
  212. }
  213. #else
  214. /*
  215. * Last time I empirically checked this, depth 3 was a noticeable
  216. * improvement on 2, but 4 only negligibly better than 3.
  217. */
  218. #define RECURSION_DEPTH 3
  219. #define check_recursion_depth() (void)0
  220. #endif
  221. struct solver_scratch {
  222. int *queue[2];
  223. int *dist;
  224. char *grid, *grid2;
  225. char *rgrids;
  226. };
  227. static struct solver_scratch *new_scratch(int w, int h)
  228. {
  229. int wh = w*h;
  230. struct solver_scratch *scratch = snew(struct solver_scratch);
  231. check_recursion_depth();
  232. scratch->queue[0] = snewn(wh, int);
  233. scratch->queue[1] = snewn(wh, int);
  234. scratch->dist = snewn(wh, int);
  235. scratch->grid = snewn(wh, char);
  236. scratch->grid2 = snewn(wh, char);
  237. scratch->rgrids = snewn(wh * RECURSION_DEPTH, char);
  238. return scratch;
  239. }
  240. static void free_scratch(struct solver_scratch *scratch)
  241. {
  242. sfree(scratch->queue[0]);
  243. sfree(scratch->queue[1]);
  244. sfree(scratch->dist);
  245. sfree(scratch->grid);
  246. sfree(scratch->grid2);
  247. sfree(scratch->rgrids);
  248. sfree(scratch);
  249. }
  250. #if 0
  251. /* Diagnostic routines you can uncomment if you need them */
  252. void dump_grid(int w, int h, const char *grid, const char *titlefmt, ...)
  253. {
  254. int x, y;
  255. if (titlefmt) {
  256. va_list ap;
  257. va_start(ap, titlefmt);
  258. vprintf(titlefmt, ap);
  259. va_end(ap);
  260. printf(":\n");
  261. } else {
  262. printf("Grid:\n");
  263. }
  264. for (y = 0; y < h; y++) {
  265. printf(" ");
  266. for (x = 0; x < w; x++) {
  267. printf("%1x", grid[y*w+x]);
  268. }
  269. printf("\n");
  270. }
  271. }
  272. void dump_dist(int w, int h, const int *dists, const char *titlefmt, ...)
  273. {
  274. int x, y;
  275. if (titlefmt) {
  276. va_list ap;
  277. va_start(ap, titlefmt);
  278. vprintf(titlefmt, ap);
  279. va_end(ap);
  280. printf(":\n");
  281. } else {
  282. printf("Distances:\n");
  283. }
  284. for (y = 0; y < h; y++) {
  285. printf(" ");
  286. for (x = 0; x < w; x++) {
  287. printf("%3d", dists[y*w+x]);
  288. }
  289. printf("\n");
  290. }
  291. }
  292. #endif
  293. /*
  294. * Search a grid to find the most distant square(s). Return their
  295. * distance and the number of them, and also the number of squares in
  296. * the current controlled set (i.e. at distance zero).
  297. */
  298. static void search(int w, int h, char *grid, int x0, int y0,
  299. struct solver_scratch *scratch,
  300. int *rdist, int *rnumber, int *rcontrol)
  301. {
  302. int wh = w*h;
  303. int i, qcurr, qhead, qtail, qnext, currdist, remaining;
  304. for (i = 0; i < wh; i++)
  305. scratch->dist[i] = -1;
  306. scratch->queue[0][0] = y0*w+x0;
  307. scratch->queue[1][0] = y0*w+x0;
  308. scratch->dist[y0*w+x0] = 0;
  309. currdist = 0;
  310. qcurr = 0;
  311. qtail = 0;
  312. qhead = 1;
  313. qnext = 1;
  314. remaining = wh - 1;
  315. while (1) {
  316. if (qtail == qhead) {
  317. /* Switch queues. */
  318. if (currdist == 0)
  319. *rcontrol = qhead;
  320. currdist++;
  321. qcurr ^= 1; /* switch queues */
  322. qhead = qnext;
  323. qtail = 0;
  324. qnext = 0;
  325. #if 0
  326. printf("switch queue, new dist %d, queue %d\n", currdist, qhead);
  327. #endif
  328. } else if (remaining == 0 && qnext == 0) {
  329. break;
  330. } else {
  331. int pos = scratch->queue[qcurr][qtail++];
  332. int y = pos / w;
  333. int x = pos % w;
  334. #if 0
  335. printf("checking neighbours of %d,%d\n", x, y);
  336. #endif
  337. int dir;
  338. for (dir = 0; dir < 4; dir++) {
  339. int y1 = y + (dir == 1 ? 1 : dir == 3 ? -1 : 0);
  340. int x1 = x + (dir == 0 ? 1 : dir == 2 ? -1 : 0);
  341. if (0 <= x1 && x1 < w && 0 <= y1 && y1 < h) {
  342. int pos1 = y1*w+x1;
  343. #if 0
  344. printf("trying %d,%d: colours %d-%d dist %d\n", x1, y1,
  345. grid[pos], grid[pos1], scratch->dist[pos]);
  346. #endif
  347. if (scratch->dist[pos1] == -1 &&
  348. ((grid[pos1] == grid[pos] &&
  349. scratch->dist[pos] == currdist) ||
  350. (grid[pos1] != grid[pos] &&
  351. scratch->dist[pos] == currdist - 1))) {
  352. #if 0
  353. printf("marking %d,%d dist %d\n", x1, y1, currdist);
  354. #endif
  355. scratch->queue[qcurr][qhead++] = pos1;
  356. scratch->queue[qcurr^1][qnext++] = pos1;
  357. scratch->dist[pos1] = currdist;
  358. remaining--;
  359. }
  360. }
  361. }
  362. }
  363. }
  364. *rdist = currdist;
  365. *rnumber = qhead;
  366. if (currdist == 0)
  367. *rcontrol = qhead;
  368. }
  369. /*
  370. * Enact a flood-fill move on a grid.
  371. */
  372. static void fill(int w, int h, char *grid, int x0, int y0, char newcolour,
  373. int *queue)
  374. {
  375. char oldcolour;
  376. int qhead, qtail;
  377. oldcolour = grid[y0*w+x0];
  378. assert(oldcolour != newcolour);
  379. grid[y0*w+x0] = newcolour;
  380. queue[0] = y0*w+x0;
  381. qtail = 0;
  382. qhead = 1;
  383. while (qtail < qhead) {
  384. int pos = queue[qtail++];
  385. int y = pos / w;
  386. int x = pos % w;
  387. int dir;
  388. for (dir = 0; dir < 4; dir++) {
  389. int y1 = y + (dir == 1 ? 1 : dir == 3 ? -1 : 0);
  390. int x1 = x + (dir == 0 ? 1 : dir == 2 ? -1 : 0);
  391. if (0 <= x1 && x1 < w && 0 <= y1 && y1 < h) {
  392. int pos1 = y1*w+x1;
  393. if (grid[pos1] == oldcolour) {
  394. grid[pos1] = newcolour;
  395. queue[qhead++] = pos1;
  396. }
  397. }
  398. }
  399. }
  400. }
  401. /*
  402. * Detect a completed grid.
  403. */
  404. static bool completed(int w, int h, char *grid)
  405. {
  406. int wh = w*h;
  407. int i;
  408. for (i = 1; i < wh; i++)
  409. if (grid[i] != grid[0])
  410. return false;
  411. return true;
  412. }
  413. /*
  414. * Try out every possible move on a grid, and choose whichever one
  415. * reduced the result of search() by the most.
  416. */
  417. static char choosemove_recurse(int w, int h, char *grid, int x0, int y0,
  418. int maxmove, struct solver_scratch *scratch,
  419. int depth, int *rbestdist, int *rbestnumber, int *rbestcontrol)
  420. {
  421. int wh = w*h;
  422. char move, bestmove;
  423. int dist, number, control, bestdist, bestnumber, bestcontrol;
  424. char *tmpgrid;
  425. assert(0 <= depth && depth < RECURSION_DEPTH);
  426. tmpgrid = scratch->rgrids + depth*wh;
  427. bestdist = wh + 1;
  428. bestnumber = 0;
  429. bestcontrol = 0;
  430. bestmove = -1;
  431. #if 0
  432. dump_grid(w, h, grid, "before choosemove_recurse %d", depth);
  433. #endif
  434. for (move = 0; move < maxmove; move++) {
  435. if (grid[y0*w+x0] == move)
  436. continue;
  437. memcpy(tmpgrid, grid, wh * sizeof(*grid));
  438. fill(w, h, tmpgrid, x0, y0, move, scratch->queue[0]);
  439. if (completed(w, h, tmpgrid)) {
  440. /*
  441. * A move that wins is immediately the best, so stop
  442. * searching. Record what depth of recursion that happened
  443. * at, so that higher levels will choose a move that gets
  444. * to a winning position sooner.
  445. */
  446. *rbestdist = -1;
  447. *rbestnumber = depth;
  448. *rbestcontrol = wh;
  449. return move;
  450. }
  451. if (depth < RECURSION_DEPTH-1) {
  452. choosemove_recurse(w, h, tmpgrid, x0, y0, maxmove, scratch,
  453. depth+1, &dist, &number, &control);
  454. } else {
  455. #if 0
  456. dump_grid(w, h, tmpgrid, "after move %d at depth %d",
  457. move, depth);
  458. #endif
  459. search(w, h, tmpgrid, x0, y0, scratch, &dist, &number, &control);
  460. #if 0
  461. dump_dist(w, h, scratch->dist, "after move %d at depth %d",
  462. move, depth);
  463. printf("move %d at depth %d: %d at %d\n",
  464. depth, move, number, dist);
  465. #endif
  466. }
  467. if (dist < bestdist ||
  468. (dist == bestdist &&
  469. (number < bestnumber ||
  470. (number == bestnumber &&
  471. (control > bestcontrol))))) {
  472. bestdist = dist;
  473. bestnumber = number;
  474. bestcontrol = control;
  475. bestmove = move;
  476. }
  477. }
  478. #if 0
  479. printf("best at depth %d was %d (%d at %d, %d controlled)\n",
  480. depth, bestmove, bestnumber, bestdist, bestcontrol);
  481. #endif
  482. *rbestdist = bestdist;
  483. *rbestnumber = bestnumber;
  484. *rbestcontrol = bestcontrol;
  485. return bestmove;
  486. }
  487. static char choosemove(int w, int h, char *grid, int x0, int y0,
  488. int maxmove, struct solver_scratch *scratch)
  489. {
  490. int tmp0, tmp1, tmp2;
  491. return choosemove_recurse(w, h, grid, x0, y0, maxmove, scratch,
  492. 0, &tmp0, &tmp1, &tmp2);
  493. }
  494. static char *new_game_desc(const game_params *params, random_state *rs,
  495. char **aux, bool interactive)
  496. {
  497. int w = params->w, h = params->h, wh = w*h;
  498. int i, moves;
  499. char *desc;
  500. struct solver_scratch *scratch;
  501. scratch = new_scratch(w, h);
  502. /*
  503. * Invent a random grid.
  504. */
  505. do {
  506. for (i = 0; i < wh; i++)
  507. scratch->grid[i] = random_upto(rs, params->colours);
  508. } while (completed(w, h, scratch->grid));
  509. /*
  510. * Run the solver, and count how many moves it uses.
  511. */
  512. memcpy(scratch->grid2, scratch->grid, wh * sizeof(*scratch->grid2));
  513. moves = 0;
  514. check_recursion_depth();
  515. while (!completed(w, h, scratch->grid2)) {
  516. char move = choosemove(w, h, scratch->grid2, FILLX, FILLY,
  517. params->colours, scratch);
  518. fill(w, h, scratch->grid2, FILLX, FILLY, move, scratch->queue[0]);
  519. moves++;
  520. }
  521. /*
  522. * Adjust for difficulty.
  523. */
  524. moves += params->leniency;
  525. /*
  526. * Encode the game id.
  527. */
  528. desc = snewn(wh + 40, char);
  529. for (i = 0; i < wh; i++) {
  530. char colour = scratch->grid[i];
  531. char textcolour = (colour > 9 ? 'A' : '0') + colour;
  532. desc[i] = textcolour;
  533. }
  534. sprintf(desc+i, ",%d", moves);
  535. free_scratch(scratch);
  536. return desc;
  537. }
  538. static const char *validate_desc(const game_params *params, const char *desc)
  539. {
  540. int w = params->w, h = params->h, wh = w*h;
  541. int i;
  542. for (i = 0; i < wh; i++) {
  543. char c = *desc++;
  544. if (c == 0)
  545. return "Not enough data in grid description";
  546. if (c >= '0' && c <= '9')
  547. c -= '0';
  548. else if (c >= 'A' && c <= 'Z')
  549. c = 10 + (c - 'A');
  550. else
  551. return "Bad character in grid description";
  552. if ((unsigned)c >= params->colours)
  553. return "Colour out of range in grid description";
  554. }
  555. if (*desc != ',')
  556. return "Expected ',' after grid description";
  557. desc++;
  558. if (desc[strspn(desc, "0123456789")])
  559. return "Badly formatted move limit after grid description";
  560. return NULL;
  561. }
  562. static game_state *new_game(midend *me, const game_params *params,
  563. const char *desc)
  564. {
  565. int w = params->w, h = params->h, wh = w*h;
  566. game_state *state = snew(game_state);
  567. int i;
  568. state->w = w;
  569. state->h = h;
  570. state->colours = params->colours;
  571. state->moves = 0;
  572. state->grid = snewn(wh, char);
  573. for (i = 0; i < wh; i++) {
  574. char c = *desc++;
  575. assert(c);
  576. if (c >= '0' && c <= '9')
  577. c -= '0';
  578. else if (c >= 'A' && c <= 'Z')
  579. c = 10 + (c - 'A');
  580. else
  581. assert(!"bad colour");
  582. state->grid[i] = c;
  583. }
  584. assert(*desc == ',');
  585. desc++;
  586. state->movelimit = atoi(desc);
  587. state->complete = false;
  588. state->cheated = false;
  589. state->solnpos = 0;
  590. state->soln = NULL;
  591. return state;
  592. }
  593. static game_state *dup_game(const game_state *state)
  594. {
  595. game_state *ret = snew(game_state);
  596. ret->w = state->w;
  597. ret->h = state->h;
  598. ret->colours = state->colours;
  599. ret->moves = state->moves;
  600. ret->movelimit = state->movelimit;
  601. ret->complete = state->complete;
  602. ret->grid = snewn(state->w * state->h, char);
  603. memcpy(ret->grid, state->grid, state->w * state->h * sizeof(*ret->grid));
  604. ret->cheated = state->cheated;
  605. ret->soln = state->soln;
  606. if (ret->soln)
  607. ret->soln->refcount++;
  608. ret->solnpos = state->solnpos;
  609. return ret;
  610. }
  611. static void free_game(game_state *state)
  612. {
  613. if (state->soln && --state->soln->refcount == 0) {
  614. sfree(state->soln->moves);
  615. sfree(state->soln);
  616. }
  617. sfree(state->grid);
  618. sfree(state);
  619. }
  620. static char *solve_game(const game_state *state, const game_state *currstate,
  621. const char *aux, const char **error)
  622. {
  623. int w = state->w, h = state->h, wh = w*h;
  624. char *moves, *ret, *p;
  625. int i, len, nmoves;
  626. char buf[256];
  627. struct solver_scratch *scratch;
  628. if (currstate->complete) {
  629. *error = "Puzzle is already solved";
  630. return NULL;
  631. }
  632. /*
  633. * Find the best solution our solver can give.
  634. */
  635. moves = snewn(wh, char); /* sure to be enough */
  636. nmoves = 0;
  637. scratch = new_scratch(w, h);
  638. memcpy(scratch->grid2, currstate->grid, wh * sizeof(*scratch->grid2));
  639. check_recursion_depth();
  640. while (!completed(w, h, scratch->grid2)) {
  641. char move = choosemove(w, h, scratch->grid2, FILLX, FILLY,
  642. currstate->colours, scratch);
  643. fill(w, h, scratch->grid2, FILLX, FILLY, move, scratch->queue[0]);
  644. assert(nmoves < wh);
  645. moves[nmoves++] = move;
  646. }
  647. free_scratch(scratch);
  648. /*
  649. * Encode it as a move string.
  650. */
  651. len = 1; /* trailing NUL */
  652. for (i = 0; i < nmoves; i++)
  653. len += sprintf(buf, ",%d", moves[i]);
  654. ret = snewn(len, char);
  655. p = ret;
  656. for (i = 0; i < nmoves; i++)
  657. p += sprintf(p, "%c%d", (i==0 ? 'S' : ','), moves[i]);
  658. assert(p - ret == len - 1);
  659. sfree(moves);
  660. return ret;
  661. }
  662. static bool game_can_format_as_text_now(const game_params *params)
  663. {
  664. return true;
  665. }
  666. static char *game_text_format(const game_state *state)
  667. {
  668. int w = state->w, h = state->h;
  669. char *ret, *p;
  670. int x, y, len;
  671. len = h * (w+1); /* +1 for newline after each row */
  672. ret = snewn(len+1, char); /* and +1 for terminating \0 */
  673. p = ret;
  674. for (y = 0; y < h; y++) {
  675. for (x = 0; x < w; x++) {
  676. char colour = state->grid[y*w+x];
  677. char textcolour = (colour > 9 ? 'A' : '0') + colour;
  678. *p++ = textcolour;
  679. }
  680. *p++ = '\n';
  681. }
  682. assert(p - ret == len);
  683. *p = '\0';
  684. return ret;
  685. }
  686. struct game_ui {
  687. bool cursor_visible;
  688. int cx, cy;
  689. enum { VICTORY, DEFEAT } flash_type;
  690. };
  691. static game_ui *new_ui(const game_state *state)
  692. {
  693. struct game_ui *ui = snew(struct game_ui);
  694. ui->cursor_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
  695. ui->cx = FILLX;
  696. ui->cy = FILLY;
  697. return ui;
  698. }
  699. static void free_ui(game_ui *ui)
  700. {
  701. sfree(ui);
  702. }
  703. static void game_changed_state(game_ui *ui, const game_state *oldstate,
  704. const game_state *newstate)
  705. {
  706. }
  707. static const char *current_key_label(const game_ui *ui,
  708. const game_state *state, int button)
  709. {
  710. if (button == CURSOR_SELECT &&
  711. state->grid[0] != state->grid[ui->cy*state->w+ui->cx])
  712. return "Fill";
  713. if (button == CURSOR_SELECT2 &&
  714. state->soln && state->solnpos < state->soln->nmoves)
  715. return "Advance";
  716. return "";
  717. }
  718. struct game_drawstate {
  719. bool started;
  720. int tilesize;
  721. int *grid;
  722. };
  723. #define TILESIZE (ds->tilesize)
  724. #define PREFERRED_TILESIZE 32
  725. #define BORDER (TILESIZE / 2)
  726. #define SEP_WIDTH (TILESIZE / 32)
  727. #define CURSOR_INSET (TILESIZE / 8)
  728. #define HIGHLIGHT_WIDTH (TILESIZE / 10)
  729. #define COORD(x) ( (x) * TILESIZE + BORDER )
  730. #define FROMCOORD(x) ( ((x) - BORDER + TILESIZE) / TILESIZE - 1 )
  731. #define VICTORY_FLASH_FRAME 0.03F
  732. #define DEFEAT_FLASH_FRAME 0.10F
  733. static char *interpret_move(const game_state *state, game_ui *ui,
  734. const game_drawstate *ds,
  735. int x, int y, int button)
  736. {
  737. int w = state->w, h = state->h;
  738. int tx = -1, ty = -1, move = -1;
  739. if (button == LEFT_BUTTON) {
  740. tx = FROMCOORD(x);
  741. ty = FROMCOORD(y);
  742. ui->cursor_visible = false;
  743. } else if (button == CURSOR_LEFT && ui->cx > 0) {
  744. ui->cx--;
  745. ui->cursor_visible = true;
  746. return MOVE_UI_UPDATE;
  747. } else if (button == CURSOR_RIGHT && ui->cx+1 < w) {
  748. ui->cx++;
  749. ui->cursor_visible = true;
  750. return MOVE_UI_UPDATE;
  751. } else if (button == CURSOR_UP && ui->cy > 0) {
  752. ui->cy--;
  753. ui->cursor_visible = true;
  754. return MOVE_UI_UPDATE;
  755. } else if (button == CURSOR_DOWN && ui->cy+1 < h) {
  756. ui->cy++;
  757. ui->cursor_visible = true;
  758. return MOVE_UI_UPDATE;
  759. } else if (button == CURSOR_SELECT) {
  760. tx = ui->cx;
  761. ty = ui->cy;
  762. } else if (button == CURSOR_SELECT2 &&
  763. state->soln && state->solnpos < state->soln->nmoves) {
  764. move = state->soln->moves[state->solnpos];
  765. } else {
  766. return NULL;
  767. }
  768. if (tx >= 0 && tx < w && ty >= 0 && ty < h &&
  769. state->grid[0] != state->grid[ty*w+tx])
  770. move = state->grid[ty*w+tx];
  771. if (move >= 0 && !state->complete) {
  772. char buf[256];
  773. sprintf(buf, "M%d", move);
  774. return dupstr(buf);
  775. }
  776. return NULL;
  777. }
  778. static game_state *execute_move(const game_state *state, const char *move)
  779. {
  780. game_state *ret;
  781. int c;
  782. if (move[0] == 'M' &&
  783. sscanf(move+1, "%d", &c) == 1 &&
  784. c >= 0 && c < state->colours &&
  785. c != state->grid[FILLY * state->w + FILLX] &&
  786. !state->complete) {
  787. int *queue = snewn(state->w * state->h, int);
  788. ret = dup_game(state);
  789. fill(ret->w, ret->h, ret->grid, FILLX, FILLY, c, queue);
  790. ret->moves++;
  791. ret->complete = completed(ret->w, ret->h, ret->grid);
  792. if (ret->soln) {
  793. /*
  794. * If this move is the correct next one in the stored
  795. * solution path, advance solnpos.
  796. */
  797. if (c == ret->soln->moves[ret->solnpos] &&
  798. ret->solnpos+1 < ret->soln->nmoves) {
  799. ret->solnpos++;
  800. } else {
  801. /*
  802. * Otherwise, the user has strayed from the path or
  803. * else the path has come to an end; either way, the
  804. * path is no longer valid.
  805. */
  806. ret->soln->refcount--;
  807. assert(ret->soln->refcount > 0);/* `state' at least still exists */
  808. ret->soln = NULL;
  809. ret->solnpos = 0;
  810. }
  811. }
  812. sfree(queue);
  813. return ret;
  814. } else if (*move == 'S') {
  815. soln *sol;
  816. const char *p;
  817. int i;
  818. /*
  819. * This is a solve move, so we don't actually _change_ the
  820. * grid but merely set up a stored solution path.
  821. */
  822. move++;
  823. sol = snew(soln);
  824. sol->nmoves = 1;
  825. for (p = move; *p; p++) {
  826. if (*p == ',')
  827. sol->nmoves++;
  828. }
  829. sol->moves = snewn(sol->nmoves, char);
  830. for (i = 0, p = move; i < sol->nmoves; i++) {
  831. if (!*p) {
  832. badsolve:
  833. sfree(sol->moves);
  834. sfree(sol);
  835. return NULL;
  836. };
  837. sol->moves[i] = atoi(p);
  838. if (sol->moves[i] < 0 || sol->moves[i] >= state->colours ||
  839. (i == 0 ?
  840. sol->moves[i] == state->grid[FILLY * state->w + FILLX] :
  841. sol->moves[i] == sol->moves[i-1]))
  842. /* Solution contains a fill with an invalid colour or
  843. * the current colour. */
  844. goto badsolve;
  845. p += strspn(p, "0123456789");
  846. if (*p) {
  847. if (*p != ',') goto badsolve;
  848. p++;
  849. }
  850. }
  851. ret = dup_game(state);
  852. ret->cheated = true;
  853. if (ret->soln && --ret->soln->refcount == 0) {
  854. sfree(ret->soln->moves);
  855. sfree(ret->soln);
  856. }
  857. ret->soln = sol;
  858. ret->solnpos = 0;
  859. sol->refcount = 1;
  860. return ret;
  861. }
  862. return NULL;
  863. }
  864. /* ----------------------------------------------------------------------
  865. * Drawing routines.
  866. */
  867. static void game_compute_size(const game_params *params, int tilesize,
  868. const game_ui *ui, int *x, int *y)
  869. {
  870. /* Ick: fake up `ds->tilesize' for macro expansion purposes */
  871. struct { int tilesize; } ads, *ds = &ads;
  872. ads.tilesize = tilesize;
  873. *x = BORDER * 2 + TILESIZE * params->w;
  874. *y = BORDER * 2 + TILESIZE * params->h;
  875. }
  876. static void game_set_size(drawing *dr, game_drawstate *ds,
  877. const game_params *params, int tilesize)
  878. {
  879. ds->tilesize = tilesize;
  880. }
  881. static float *game_colours(frontend *fe, int *ncolours)
  882. {
  883. float *ret = snewn(3 * NCOLOURS, float);
  884. game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
  885. ret[COL_SEPARATOR * 3 + 0] = 0.0F;
  886. ret[COL_SEPARATOR * 3 + 1] = 0.0F;
  887. ret[COL_SEPARATOR * 3 + 2] = 0.0F;
  888. /* red */
  889. ret[COL_1 * 3 + 0] = 1.0F;
  890. ret[COL_1 * 3 + 1] = 0.0F;
  891. ret[COL_1 * 3 + 2] = 0.0F;
  892. /* yellow */
  893. ret[COL_2 * 3 + 0] = 1.0F;
  894. ret[COL_2 * 3 + 1] = 1.0F;
  895. ret[COL_2 * 3 + 2] = 0.0F;
  896. /* green */
  897. ret[COL_3 * 3 + 0] = 0.0F;
  898. ret[COL_3 * 3 + 1] = 1.0F;
  899. ret[COL_3 * 3 + 2] = 0.0F;
  900. /* blue */
  901. ret[COL_4 * 3 + 0] = 0.2F;
  902. ret[COL_4 * 3 + 1] = 0.3F;
  903. ret[COL_4 * 3 + 2] = 1.0F;
  904. /* orange */
  905. ret[COL_5 * 3 + 0] = 1.0F;
  906. ret[COL_5 * 3 + 1] = 0.5F;
  907. ret[COL_5 * 3 + 2] = 0.0F;
  908. /* purple */
  909. ret[COL_6 * 3 + 0] = 0.5F;
  910. ret[COL_6 * 3 + 1] = 0.0F;
  911. ret[COL_6 * 3 + 2] = 0.7F;
  912. /* brown */
  913. ret[COL_7 * 3 + 0] = 0.5F;
  914. ret[COL_7 * 3 + 1] = 0.3F;
  915. ret[COL_7 * 3 + 2] = 0.3F;
  916. /* light blue */
  917. ret[COL_8 * 3 + 0] = 0.4F;
  918. ret[COL_8 * 3 + 1] = 0.8F;
  919. ret[COL_8 * 3 + 2] = 1.0F;
  920. /* light green */
  921. ret[COL_9 * 3 + 0] = 0.7F;
  922. ret[COL_9 * 3 + 1] = 1.0F;
  923. ret[COL_9 * 3 + 2] = 0.7F;
  924. /* pink */
  925. ret[COL_10 * 3 + 0] = 1.0F;
  926. ret[COL_10 * 3 + 1] = 0.6F;
  927. ret[COL_10 * 3 + 2] = 1.0F;
  928. *ncolours = NCOLOURS;
  929. return ret;
  930. }
  931. static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
  932. {
  933. struct game_drawstate *ds = snew(struct game_drawstate);
  934. int w = state->w, h = state->h, wh = w*h;
  935. int i;
  936. ds->started = false;
  937. ds->tilesize = 0;
  938. ds->grid = snewn(wh, int);
  939. for (i = 0; i < wh; i++)
  940. ds->grid[i] = -1;
  941. return ds;
  942. }
  943. static void game_free_drawstate(drawing *dr, game_drawstate *ds)
  944. {
  945. sfree(ds->grid);
  946. sfree(ds);
  947. }
  948. #define BORDER_L 0x001
  949. #define BORDER_R 0x002
  950. #define BORDER_U 0x004
  951. #define BORDER_D 0x008
  952. #define CORNER_UL 0x010
  953. #define CORNER_UR 0x020
  954. #define CORNER_DL 0x040
  955. #define CORNER_DR 0x080
  956. #define CURSOR 0x100
  957. #define BADFLASH 0x200
  958. #define SOLNNEXT 0x400
  959. #define COLOUR_SHIFT 11
  960. static void draw_tile(drawing *dr, game_drawstate *ds,
  961. int x, int y, int tile)
  962. {
  963. int colour;
  964. int tx = COORD(x), ty = COORD(y);
  965. colour = tile >> COLOUR_SHIFT;
  966. if (tile & BADFLASH)
  967. colour = COL_SEPARATOR;
  968. else
  969. colour += COL_1;
  970. draw_rect(dr, tx, ty, TILESIZE, TILESIZE, colour);
  971. if (tile & BORDER_L)
  972. draw_rect(dr, tx, ty,
  973. SEP_WIDTH, TILESIZE, COL_SEPARATOR);
  974. if (tile & BORDER_R)
  975. draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty,
  976. SEP_WIDTH, TILESIZE, COL_SEPARATOR);
  977. if (tile & BORDER_U)
  978. draw_rect(dr, tx, ty,
  979. TILESIZE, SEP_WIDTH, COL_SEPARATOR);
  980. if (tile & BORDER_D)
  981. draw_rect(dr, tx, ty + TILESIZE - SEP_WIDTH,
  982. TILESIZE, SEP_WIDTH, COL_SEPARATOR);
  983. if (tile & CORNER_UL)
  984. draw_rect(dr, tx, ty,
  985. SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR);
  986. if (tile & CORNER_UR)
  987. draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty,
  988. SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR);
  989. if (tile & CORNER_DL)
  990. draw_rect(dr, tx, ty + TILESIZE - SEP_WIDTH,
  991. SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR);
  992. if (tile & CORNER_DR)
  993. draw_rect(dr, tx + TILESIZE - SEP_WIDTH, ty + TILESIZE - SEP_WIDTH,
  994. SEP_WIDTH, SEP_WIDTH, COL_SEPARATOR);
  995. if (tile & CURSOR)
  996. draw_rect_outline(dr, tx + CURSOR_INSET, ty + CURSOR_INSET,
  997. TILESIZE - 1 - CURSOR_INSET * 2,
  998. TILESIZE - 1 - CURSOR_INSET * 2,
  999. COL_SEPARATOR);
  1000. if (tile & SOLNNEXT) {
  1001. draw_circle(dr, tx + TILESIZE/2, ty + TILESIZE/2, TILESIZE/6,
  1002. COL_SEPARATOR, COL_SEPARATOR);
  1003. }
  1004. draw_update(dr, tx, ty, TILESIZE, TILESIZE);
  1005. }
  1006. static void game_redraw(drawing *dr, game_drawstate *ds,
  1007. const game_state *oldstate, const game_state *state,
  1008. int dir, const game_ui *ui,
  1009. float animtime, float flashtime)
  1010. {
  1011. int w = state->w, h = state->h, wh = w*h;
  1012. int x, y, flashframe, solnmove;
  1013. char *grid;
  1014. /* This was entirely cloned from fifteen.c; it should probably be
  1015. * moved into some generic 'draw-recessed-rectangle' utility fn. */
  1016. if (!ds->started) {
  1017. int coords[10];
  1018. /*
  1019. * Recessed area containing the whole puzzle.
  1020. */
  1021. coords[0] = COORD(w) + HIGHLIGHT_WIDTH - 1;
  1022. coords[1] = COORD(h) + HIGHLIGHT_WIDTH - 1;
  1023. coords[2] = COORD(w) + HIGHLIGHT_WIDTH - 1;
  1024. coords[3] = COORD(0) - HIGHLIGHT_WIDTH;
  1025. coords[4] = coords[2] - TILESIZE;
  1026. coords[5] = coords[3] + TILESIZE;
  1027. coords[8] = COORD(0) - HIGHLIGHT_WIDTH;
  1028. coords[9] = COORD(h) + HIGHLIGHT_WIDTH - 1;
  1029. coords[6] = coords[8] + TILESIZE;
  1030. coords[7] = coords[9] - TILESIZE;
  1031. draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT);
  1032. coords[1] = COORD(0) - HIGHLIGHT_WIDTH;
  1033. coords[0] = COORD(0) - HIGHLIGHT_WIDTH;
  1034. draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT);
  1035. draw_rect(dr, COORD(0) - SEP_WIDTH, COORD(0) - SEP_WIDTH,
  1036. TILESIZE * w + 2 * SEP_WIDTH, TILESIZE * h + 2 * SEP_WIDTH,
  1037. COL_SEPARATOR);
  1038. ds->started = true;
  1039. }
  1040. if (flashtime > 0) {
  1041. float frame = (ui->flash_type == VICTORY ?
  1042. VICTORY_FLASH_FRAME : DEFEAT_FLASH_FRAME);
  1043. flashframe = (int)(flashtime / frame);
  1044. } else {
  1045. flashframe = -1;
  1046. }
  1047. grid = snewn(wh, char);
  1048. memcpy(grid, state->grid, wh * sizeof(*grid));
  1049. if (state->soln && state->solnpos < state->soln->nmoves) {
  1050. int i, *queue;
  1051. /*
  1052. * Highlight as 'next auto-solver move' every square of the
  1053. * target colour which is adjacent to the currently controlled
  1054. * region. We do this by first enacting the actual move, then
  1055. * flood-filling again in a nonexistent colour, and finally
  1056. * reverting to the original grid anything in the new colour
  1057. * that was part of the original controlled region. Then
  1058. * regions coloured in the dummy colour should be displayed as
  1059. * soln_move with the SOLNNEXT flag.
  1060. */
  1061. solnmove = state->soln->moves[state->solnpos];
  1062. queue = snewn(wh, int);
  1063. fill(w, h, grid, FILLX, FILLY, solnmove, queue);
  1064. fill(w, h, grid, FILLX, FILLY, state->colours, queue);
  1065. sfree(queue);
  1066. for (i = 0; i < wh; i++)
  1067. if (grid[i] == state->colours && state->grid[i] != solnmove)
  1068. grid[i] = state->grid[i];
  1069. } else {
  1070. solnmove = 0; /* placate optimiser */
  1071. }
  1072. if (flashframe >= 0 && ui->flash_type == VICTORY) {
  1073. /*
  1074. * Modify the display grid by superimposing our rainbow flash
  1075. * on it.
  1076. */
  1077. for (x = 0; x < w; x++) {
  1078. for (y = 0; y < h; y++) {
  1079. int flashpos = flashframe - (abs(x - FILLX) + abs(y - FILLY));
  1080. if (flashpos >= 0 && flashpos < state->colours)
  1081. grid[y*w+x] = flashpos;
  1082. }
  1083. }
  1084. }
  1085. for (x = 0; x < w; x++) {
  1086. for (y = 0; y < h; y++) {
  1087. int pos = y*w+x;
  1088. int tile;
  1089. if (grid[pos] == state->colours) {
  1090. tile = (solnmove << COLOUR_SHIFT) | SOLNNEXT;
  1091. } else {
  1092. tile = (int)grid[pos] << COLOUR_SHIFT;
  1093. }
  1094. if (x == 0 || grid[pos-1] != grid[pos])
  1095. tile |= BORDER_L;
  1096. if (x==w-1 || grid[pos+1] != grid[pos])
  1097. tile |= BORDER_R;
  1098. if (y == 0 || grid[pos-w] != grid[pos])
  1099. tile |= BORDER_U;
  1100. if (y==h-1 || grid[pos+w] != grid[pos])
  1101. tile |= BORDER_D;
  1102. if (x == 0 || y == 0 || grid[pos-w-1] != grid[pos])
  1103. tile |= CORNER_UL;
  1104. if (x==w-1 || y == 0 || grid[pos-w+1] != grid[pos])
  1105. tile |= CORNER_UR;
  1106. if (x == 0 || y==h-1 || grid[pos+w-1] != grid[pos])
  1107. tile |= CORNER_DL;
  1108. if (x==w-1 || y==h-1 || grid[pos+w+1] != grid[pos])
  1109. tile |= CORNER_DR;
  1110. if (ui->cursor_visible && ui->cx == x && ui->cy == y)
  1111. tile |= CURSOR;
  1112. if (flashframe >= 0 && ui->flash_type == DEFEAT && flashframe != 1)
  1113. tile |= BADFLASH;
  1114. if (ds->grid[pos] != tile) {
  1115. draw_tile(dr, ds, x, y, tile);
  1116. ds->grid[pos] = tile;
  1117. }
  1118. }
  1119. }
  1120. sfree(grid);
  1121. {
  1122. char status[255];
  1123. sprintf(status, "%s%d / %d moves",
  1124. (state->complete && state->moves <= state->movelimit ?
  1125. (state->cheated ? "Auto-solved. " : "COMPLETED! ") :
  1126. state->moves >= state->movelimit ? "FAILED! " :
  1127. state->cheated ? "Auto-solver used. " :
  1128. ""),
  1129. state->moves,
  1130. state->movelimit);
  1131. status_bar(dr, status);
  1132. }
  1133. }
  1134. static float game_anim_length(const game_state *oldstate,
  1135. const game_state *newstate, int dir, game_ui *ui)
  1136. {
  1137. return 0.0F;
  1138. }
  1139. static void game_get_cursor_location(const game_ui *ui,
  1140. const game_drawstate *ds,
  1141. const game_state *state,
  1142. const game_params *params,
  1143. int *x, int *y, int *w, int *h)
  1144. {
  1145. if(ui->cursor_visible)
  1146. {
  1147. *x = COORD(ui->cx);
  1148. *y = COORD(ui->cy);
  1149. *w = *h = TILESIZE;
  1150. }
  1151. }
  1152. static int game_status(const game_state *state)
  1153. {
  1154. if (state->complete && state->moves <= state->movelimit) {
  1155. return +1; /* victory! */
  1156. } else if (state->moves >= state->movelimit) {
  1157. return -1; /* defeat */
  1158. } else {
  1159. return 0; /* still playing */
  1160. }
  1161. }
  1162. static float game_flash_length(const game_state *oldstate,
  1163. const game_state *newstate, int dir, game_ui *ui)
  1164. {
  1165. if (dir == +1) {
  1166. int old_status = game_status(oldstate);
  1167. int new_status = game_status(newstate);
  1168. if (old_status != new_status) {
  1169. assert(old_status == 0);
  1170. if (new_status == +1) {
  1171. int frames = newstate->w + newstate->h + newstate->colours - 2;
  1172. ui->flash_type = VICTORY;
  1173. return VICTORY_FLASH_FRAME * frames;
  1174. } else {
  1175. ui->flash_type = DEFEAT;
  1176. return DEFEAT_FLASH_FRAME * 3;
  1177. }
  1178. }
  1179. }
  1180. return 0.0F;
  1181. }
  1182. #ifdef COMBINED
  1183. #define thegame flood
  1184. #endif
  1185. const struct game thegame = {
  1186. "Flood", "games.flood", "flood",
  1187. default_params,
  1188. game_fetch_preset, NULL,
  1189. decode_params,
  1190. encode_params,
  1191. free_params,
  1192. dup_params,
  1193. true, game_configure, custom_params,
  1194. validate_params,
  1195. new_game_desc,
  1196. validate_desc,
  1197. new_game,
  1198. dup_game,
  1199. free_game,
  1200. true, solve_game,
  1201. true, game_can_format_as_text_now, game_text_format,
  1202. NULL, NULL, /* get_prefs, set_prefs */
  1203. new_ui,
  1204. free_ui,
  1205. NULL, /* encode_ui */
  1206. NULL, /* decode_ui */
  1207. NULL, /* game_request_keys */
  1208. game_changed_state,
  1209. current_key_label,
  1210. interpret_move,
  1211. execute_move,
  1212. PREFERRED_TILESIZE, game_compute_size, game_set_size,
  1213. game_colours,
  1214. game_new_drawstate,
  1215. game_free_drawstate,
  1216. game_redraw,
  1217. game_anim_length,
  1218. game_flash_length,
  1219. game_get_cursor_location,
  1220. game_status,
  1221. false, false, NULL, NULL, /* print_size, print */
  1222. true, /* wants_statusbar */
  1223. false, NULL, /* timing_state */
  1224. 0, /* flags */
  1225. };