samegame.c 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687
  1. /*
  2. * 'same game' -- try to remove all the coloured squares by
  3. * selecting regions of contiguous colours.
  4. */
  5. /*
  6. * TODO on grid generation:
  7. *
  8. * - Generation speed could still be improved.
  9. * * 15x10c3 is the only really difficult one of the existing
  10. * presets. The others are all either small enough, or have
  11. * the great flexibility given by four colours, that they
  12. * don't take long at all.
  13. * * I still suspect many problems arise from separate
  14. * subareas. I wonder if we can also somehow prioritise left-
  15. * or rightmost insertions so as to avoid area splitting at
  16. * all where feasible? It's not easy, though, because the
  17. * current shuffle-then-try-all-options approach to move
  18. * choice doesn't leave room for `soft' probabilistic
  19. * prioritisation: we either try all class A moves before any
  20. * class B ones, or we don't.
  21. *
  22. * - The current generation algorithm inserts exactly two squares
  23. * at a time, with a single exception at the beginning of
  24. * generation for grids of odd overall size. An obvious
  25. * extension would be to permit larger inverse moves during
  26. * generation.
  27. * * this might reduce the number of failed generations by
  28. * making the insertion algorithm more flexible
  29. * * on the other hand, it would be significantly more complex
  30. * * if I do this I'll need to take out the odd-subarea
  31. * avoidance
  32. * * a nice feature of the current algorithm is that the
  33. * computer's `intended' solution always receives the minimum
  34. * possible score, so that pretty much the player's entire
  35. * score represents how much better they did than the
  36. * computer.
  37. *
  38. * - Is it possible we can _temporarily_ tolerate neighbouring
  39. * squares of the same colour, until we've finished setting up
  40. * our inverse move?
  41. * * or perhaps even not choose the colour of our inserted
  42. * region until we have finished placing it, and _then_ look
  43. * at what colours border on it?
  44. * * I don't think this is currently meaningful unless we're
  45. * placing more than a domino at a time.
  46. *
  47. * - possibly write out a full solution so that Solve can somehow
  48. * show it step by step?
  49. * * aux_info would have to encode the click points
  50. * * solve_game() would have to encode not only those click
  51. * points but also give a move string which reconstructed the
  52. * initial state
  53. * * the game_state would include a pointer to a solution move
  54. * list, plus an index into that list
  55. * * game_changed_state would auto-select the next move if
  56. * handed a new state which had a solution move list active
  57. * * execute_move, if passed such a state as input, would check
  58. * to see whether the move being made was the same as the one
  59. * stated by the solution, and if so would advance the move
  60. * index. Failing that it would return a game_state without a
  61. * solution move list active at all.
  62. */
  63. #include <stdio.h>
  64. #include <stdlib.h>
  65. #include <string.h>
  66. #include <assert.h>
  67. #include <ctype.h>
  68. #include <limits.h>
  69. #ifdef NO_TGMATH_H
  70. # include <math.h>
  71. #else
  72. # include <tgmath.h>
  73. #endif
  74. #include "puzzles.h"
  75. #define TILE_INNER (ds->tileinner)
  76. #define TILE_GAP (ds->tilegap)
  77. #define TILE_SIZE (TILE_INNER + TILE_GAP)
  78. #define PREFERRED_TILE_SIZE 32
  79. #define BORDER (TILE_SIZE / 2)
  80. #define HIGHLIGHT_WIDTH 2
  81. #define FLASH_FRAME 0.13F
  82. #define COORD(x) ( (x) * TILE_SIZE + BORDER )
  83. #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
  84. #define X(state, i) ( (i) % (state)->params.w )
  85. #define Y(state, i) ( (i) / (state)->params.w )
  86. #define C(state, x, y) ( (y) * (state)->w + (x) )
  87. enum {
  88. COL_BACKGROUND,
  89. COL_1, COL_2, COL_3, COL_4, COL_5, COL_6, COL_7, COL_8, COL_9,
  90. COL_IMPOSSIBLE, COL_SEL, COL_HIGHLIGHT, COL_LOWLIGHT,
  91. NCOLOURS
  92. };
  93. /* scoresub is 1 or 2 (for (n-1)^2 or (n-2)^2) */
  94. struct game_params {
  95. int w, h, ncols, scoresub;
  96. bool soluble; /* choose generation algorithm */
  97. };
  98. /* These flags must be unique across all uses; in the game_state,
  99. * the game_ui, and the drawstate (as they all get combined in the
  100. * drawstate). */
  101. #define TILE_COLMASK 0x00ff
  102. #define TILE_SELECTED 0x0100 /* used in ui and drawstate */
  103. #define TILE_JOINRIGHT 0x0200 /* used in drawstate */
  104. #define TILE_JOINDOWN 0x0400 /* used in drawstate */
  105. #define TILE_JOINDIAG 0x0800 /* used in drawstate */
  106. #define TILE_HASSEL 0x1000 /* used in drawstate */
  107. #define TILE_IMPOSSIBLE 0x2000 /* used in drawstate */
  108. #define TILE(gs,x,y) ((gs)->tiles[(gs)->params.w*(y)+(x)])
  109. #define COL(gs,x,y) (TILE(gs,x,y) & TILE_COLMASK)
  110. #define ISSEL(gs,x,y) (TILE(gs,x,y) & TILE_SELECTED)
  111. #define SWAPTILE(gs,x1,y1,x2,y2) do { \
  112. int t = TILE(gs,x1,y1); \
  113. TILE(gs,x1,y1) = TILE(gs,x2,y2); \
  114. TILE(gs,x2,y2) = t; \
  115. } while (0)
  116. static int npoints(const game_params *params, int nsel)
  117. {
  118. int sdiff = nsel - params->scoresub;
  119. return (sdiff > 0) ? sdiff * sdiff : 0;
  120. }
  121. struct game_state {
  122. struct game_params params;
  123. int n;
  124. int *tiles; /* colour only */
  125. int score;
  126. bool complete, impossible;
  127. };
  128. static game_params *default_params(void)
  129. {
  130. game_params *ret = snew(game_params);
  131. ret->w = 5;
  132. ret->h = 5;
  133. ret->ncols = 3;
  134. ret->scoresub = 2;
  135. ret->soluble = true;
  136. return ret;
  137. }
  138. static const struct game_params samegame_presets[] = {
  139. { 5, 5, 3, 2, true },
  140. { 10, 5, 3, 2, true },
  141. #ifdef SLOW_SYSTEM
  142. { 10, 10, 3, 2, true },
  143. #else
  144. { 15, 10, 3, 2, true },
  145. #endif
  146. { 15, 10, 4, 2, true },
  147. { 20, 15, 4, 2, true }
  148. };
  149. static bool game_fetch_preset(int i, char **name, game_params **params)
  150. {
  151. game_params *ret;
  152. char str[80];
  153. if (i < 0 || i >= lenof(samegame_presets))
  154. return false;
  155. ret = snew(game_params);
  156. *ret = samegame_presets[i];
  157. sprintf(str, "%dx%d, %d colours", ret->w, ret->h, ret->ncols);
  158. *name = dupstr(str);
  159. *params = ret;
  160. return true;
  161. }
  162. static void free_params(game_params *params)
  163. {
  164. sfree(params);
  165. }
  166. static game_params *dup_params(const game_params *params)
  167. {
  168. game_params *ret = snew(game_params);
  169. *ret = *params; /* structure copy */
  170. return ret;
  171. }
  172. static void decode_params(game_params *params, char const *string)
  173. {
  174. char const *p = string;
  175. params->w = atoi(p);
  176. while (*p && isdigit((unsigned char)*p)) p++;
  177. if (*p == 'x') {
  178. p++;
  179. params->h = atoi(p);
  180. while (*p && isdigit((unsigned char)*p)) p++;
  181. } else {
  182. params->h = params->w;
  183. }
  184. if (*p == 'c') {
  185. p++;
  186. params->ncols = atoi(p);
  187. while (*p && isdigit((unsigned char)*p)) p++;
  188. } else {
  189. params->ncols = 3;
  190. }
  191. if (*p == 's') {
  192. p++;
  193. params->scoresub = atoi(p);
  194. while (*p && isdigit((unsigned char)*p)) p++;
  195. } else {
  196. params->scoresub = 2;
  197. }
  198. if (*p == 'r') {
  199. p++;
  200. params->soluble = false;
  201. }
  202. }
  203. static char *encode_params(const game_params *params, bool full)
  204. {
  205. char ret[80];
  206. sprintf(ret, "%dx%dc%ds%d%s",
  207. params->w, params->h, params->ncols, params->scoresub,
  208. full && !params->soluble ? "r" : "");
  209. return dupstr(ret);
  210. }
  211. static config_item *game_configure(const game_params *params)
  212. {
  213. config_item *ret;
  214. char buf[80];
  215. ret = snewn(6, config_item);
  216. ret[0].name = "Width";
  217. ret[0].type = C_STRING;
  218. sprintf(buf, "%d", params->w);
  219. ret[0].u.string.sval = dupstr(buf);
  220. ret[1].name = "Height";
  221. ret[1].type = C_STRING;
  222. sprintf(buf, "%d", params->h);
  223. ret[1].u.string.sval = dupstr(buf);
  224. ret[2].name = "No. of colours";
  225. ret[2].type = C_STRING;
  226. sprintf(buf, "%d", params->ncols);
  227. ret[2].u.string.sval = dupstr(buf);
  228. ret[3].name = "Scoring system";
  229. ret[3].type = C_CHOICES;
  230. ret[3].u.choices.choicenames = ":(n-1)^2:(n-2)^2";
  231. ret[3].u.choices.selected = params->scoresub-1;
  232. ret[4].name = "Ensure solubility";
  233. ret[4].type = C_BOOLEAN;
  234. ret[4].u.boolean.bval = params->soluble;
  235. ret[5].name = NULL;
  236. ret[5].type = C_END;
  237. return ret;
  238. }
  239. static game_params *custom_params(const config_item *cfg)
  240. {
  241. game_params *ret = snew(game_params);
  242. ret->w = atoi(cfg[0].u.string.sval);
  243. ret->h = atoi(cfg[1].u.string.sval);
  244. ret->ncols = atoi(cfg[2].u.string.sval);
  245. ret->scoresub = cfg[3].u.choices.selected + 1;
  246. ret->soluble = cfg[4].u.boolean.bval;
  247. return ret;
  248. }
  249. static const char *validate_params(const game_params *params, bool full)
  250. {
  251. if (params->w < 1 || params->h < 1)
  252. return "Width and height must both be positive";
  253. if (params->w > INT_MAX / params->h)
  254. return "Width times height must not be unreasonably large";
  255. if (params->ncols > 9)
  256. return "Maximum of 9 colours";
  257. if (params->soluble) {
  258. if (params->ncols < 3)
  259. return "Number of colours must be at least three";
  260. if (params->w * params->h <= 1)
  261. return "Grid area must be greater than 1";
  262. } else {
  263. if (params->ncols < 2)
  264. return "Number of colours must be at least three";
  265. /* ...and we must make sure we can generate at least 2 squares
  266. * of each colour so it's theoretically soluble. */
  267. if ((params->w * params->h) < (params->ncols * 2))
  268. return "Too many colours makes given grid size impossible";
  269. }
  270. if ((params->scoresub < 1) || (params->scoresub > 2))
  271. return "Scoring system not recognised";
  272. return NULL;
  273. }
  274. /*
  275. * Guaranteed-soluble grid generator.
  276. */
  277. static void gen_grid(int w, int h, int nc, int *grid, random_state *rs)
  278. {
  279. int wh = w*h, tc = nc+1;
  280. int i, j, k, c, x, y, pos, n;
  281. int *list, *grid2;
  282. bool ok;
  283. int failures = 0;
  284. /*
  285. * We'll use `list' to track the possible places to put our
  286. * next insertion. There are up to h places to insert in each
  287. * column: in a column of height n there are n+1 places because
  288. * we can insert at the very bottom or the very top, but a
  289. * column of height h can't have anything at all inserted in it
  290. * so we have up to h in each column. Likewise, with n columns
  291. * present there are n+1 places to fit a new one in between but
  292. * we can't insert a column if there are already w; so there
  293. * are a maximum of w new columns too. Total is wh + w.
  294. */
  295. list = snewn(wh + w, int);
  296. grid2 = snewn(wh, int);
  297. do {
  298. /*
  299. * Start with two or three squares - depending on parity of w*h
  300. * - of a random colour.
  301. */
  302. for (i = 0; i < wh; i++)
  303. grid[i] = 0;
  304. j = 2 + (wh % 2);
  305. c = 1 + random_upto(rs, nc);
  306. if (j <= w) {
  307. for (i = 0; i < j; i++)
  308. grid[(h-1)*w+i] = c;
  309. } else {
  310. assert(j <= h);
  311. for (i = 0; i < j; i++)
  312. grid[(h-1-i)*w] = c;
  313. }
  314. /*
  315. * Now repeatedly insert a two-square blob in the grid, of
  316. * whatever colour will go at the position we chose.
  317. */
  318. while (1) {
  319. n = 0;
  320. /*
  321. * Build up a list of insertion points. Each point is
  322. * encoded as y*w+x; insertion points between columns are
  323. * encoded as h*w+x.
  324. */
  325. if (grid[wh - 1] == 0) {
  326. /*
  327. * The final column is empty, so we can insert new
  328. * columns.
  329. */
  330. for (i = 0; i < w; i++) {
  331. list[n++] = wh + i;
  332. if (grid[(h-1)*w + i] == 0)
  333. break;
  334. }
  335. }
  336. /*
  337. * Now look for places to insert within columns.
  338. */
  339. for (i = 0; i < w; i++) {
  340. if (grid[(h-1)*w+i] == 0)
  341. break; /* no more columns */
  342. if (grid[i] != 0)
  343. continue; /* this column is full */
  344. for (j = h; j-- > 0 ;) {
  345. list[n++] = j*w+i;
  346. if (grid[j*w+i] == 0)
  347. break; /* this column is exhausted */
  348. }
  349. }
  350. if (n == 0)
  351. break; /* we're done */
  352. #ifdef GENERATION_DIAGNOSTICS
  353. printf("initial grid:\n");
  354. {
  355. int x,y;
  356. for (y = 0; y < h; y++) {
  357. for (x = 0; x < w; x++) {
  358. if (grid[y*w+x] == 0)
  359. printf("-");
  360. else
  361. printf("%d", grid[y*w+x]);
  362. }
  363. printf("\n");
  364. }
  365. }
  366. #endif
  367. /*
  368. * Now go through the list one element at a time in
  369. * random order, and actually attempt to insert
  370. * something there.
  371. */
  372. while (n-- > 0) {
  373. int dirs[4], ndirs, dir;
  374. i = random_upto(rs, n+1);
  375. pos = list[i];
  376. list[i] = list[n];
  377. x = pos % w;
  378. y = pos / w;
  379. memcpy(grid2, grid, wh * sizeof(int));
  380. if (y == h) {
  381. /*
  382. * Insert a column at position x.
  383. */
  384. for (i = w-1; i > x; i--)
  385. for (j = 0; j < h; j++)
  386. grid2[j*w+i] = grid2[j*w+(i-1)];
  387. /*
  388. * Clear the new column.
  389. */
  390. for (j = 0; j < h; j++)
  391. grid2[j*w+x] = 0;
  392. /*
  393. * Decrement y so that our first square is actually
  394. * inserted _in_ the grid rather than just below it.
  395. */
  396. y--;
  397. }
  398. /*
  399. * Insert a square within column x at position y.
  400. */
  401. for (i = 0; i+1 <= y; i++)
  402. grid2[i*w+x] = grid2[(i+1)*w+x];
  403. #ifdef GENERATION_DIAGNOSTICS
  404. printf("trying at n=%d (%d,%d)\n", n, x, y);
  405. grid2[y*w+x] = tc;
  406. {
  407. int x,y;
  408. for (y = 0; y < h; y++) {
  409. for (x = 0; x < w; x++) {
  410. if (grid2[y*w+x] == 0)
  411. printf("-");
  412. else if (grid2[y*w+x] <= nc)
  413. printf("%d", grid2[y*w+x]);
  414. else
  415. printf("*");
  416. }
  417. printf("\n");
  418. }
  419. }
  420. #endif
  421. /*
  422. * Pick our square colour so that it doesn't match any
  423. * of its neighbours.
  424. */
  425. {
  426. int wrongcol[4], nwrong = 0;
  427. /*
  428. * List the neighbouring colours.
  429. */
  430. if (x > 0)
  431. wrongcol[nwrong++] = grid2[y*w+(x-1)];
  432. if (x+1 < w)
  433. wrongcol[nwrong++] = grid2[y*w+(x+1)];
  434. if (y > 0)
  435. wrongcol[nwrong++] = grid2[(y-1)*w+x];
  436. if (y+1 < h)
  437. wrongcol[nwrong++] = grid2[(y+1)*w+x];
  438. /*
  439. * Eliminate duplicates. We can afford a shoddy
  440. * algorithm here because the problem size is
  441. * bounded.
  442. */
  443. for (i = j = 0 ;; i++) {
  444. int pos = -1, min = 0;
  445. if (j > 0)
  446. min = wrongcol[j-1];
  447. for (k = i; k < nwrong; k++)
  448. if (wrongcol[k] > min &&
  449. (pos == -1 || wrongcol[k] < wrongcol[pos]))
  450. pos = k;
  451. if (pos >= 0) {
  452. int v = wrongcol[pos];
  453. wrongcol[pos] = wrongcol[j];
  454. wrongcol[j++] = v;
  455. } else
  456. break;
  457. }
  458. nwrong = j;
  459. /*
  460. * If no colour will go here, stop trying.
  461. */
  462. if (nwrong == nc)
  463. continue;
  464. /*
  465. * Otherwise, pick a colour from the remaining
  466. * ones.
  467. */
  468. c = 1 + random_upto(rs, nc - nwrong);
  469. for (i = 0; i < nwrong; i++) {
  470. if (c >= wrongcol[i])
  471. c++;
  472. else
  473. break;
  474. }
  475. }
  476. /*
  477. * Place the new square.
  478. *
  479. * Although I've _chosen_ the new region's colour
  480. * (so that we can check adjacency), I'm going to
  481. * actually place it as an invalid colour (tc)
  482. * until I'm sure it's viable. This is so that I
  483. * can conveniently check that I really have made a
  484. * _valid_ inverse move later on.
  485. */
  486. #ifdef GENERATION_DIAGNOSTICS
  487. printf("picked colour %d\n", c);
  488. #endif
  489. grid2[y*w+x] = tc;
  490. /*
  491. * Now attempt to extend it in one of three ways: left,
  492. * right or up.
  493. */
  494. ndirs = 0;
  495. if (x > 0 &&
  496. grid2[y*w+(x-1)] != c &&
  497. grid2[x-1] == 0 &&
  498. (y+1 >= h || grid2[(y+1)*w+(x-1)] != c) &&
  499. (y+1 >= h || grid2[(y+1)*w+(x-1)] != 0) &&
  500. (x <= 1 || grid2[y*w+(x-2)] != c))
  501. dirs[ndirs++] = -1; /* left */
  502. if (x+1 < w &&
  503. grid2[y*w+(x+1)] != c &&
  504. grid2[x+1] == 0 &&
  505. (y+1 >= h || grid2[(y+1)*w+(x+1)] != c) &&
  506. (y+1 >= h || grid2[(y+1)*w+(x+1)] != 0) &&
  507. (x+2 >= w || grid2[y*w+(x+2)] != c))
  508. dirs[ndirs++] = +1; /* right */
  509. if (y > 0 &&
  510. grid2[x] == 0 &&
  511. (x <= 0 || grid2[(y-1)*w+(x-1)] != c) &&
  512. (x+1 >= w || grid2[(y-1)*w+(x+1)] != c)) {
  513. /*
  514. * We add this possibility _twice_, so that the
  515. * probability of placing a vertical domino is
  516. * about the same as that of a horizontal. This
  517. * should yield less bias in the generated
  518. * grids.
  519. */
  520. dirs[ndirs++] = 0; /* up */
  521. dirs[ndirs++] = 0; /* up */
  522. }
  523. if (ndirs == 0)
  524. continue;
  525. dir = dirs[random_upto(rs, ndirs)];
  526. #ifdef GENERATION_DIAGNOSTICS
  527. printf("picked dir %d\n", dir);
  528. #endif
  529. /*
  530. * Insert a square within column (x+dir) at position y.
  531. */
  532. for (i = 0; i+1 <= y; i++)
  533. grid2[i*w+x+dir] = grid2[(i+1)*w+x+dir];
  534. grid2[y*w+x+dir] = tc;
  535. /*
  536. * See if we've divided the remaining grid squares
  537. * into sub-areas. If so, we need every sub-area to
  538. * have an even area or we won't be able to
  539. * complete generation.
  540. *
  541. * If the height is odd and not all columns are
  542. * present, we can increase the area of a subarea
  543. * by adding a new column in it, so in that
  544. * situation we don't mind having as many odd
  545. * subareas as there are spare columns.
  546. *
  547. * If the height is even, we can't fix it at all.
  548. */
  549. {
  550. int nerrs = 0, nfix = 0;
  551. k = 0; /* current subarea size */
  552. for (i = 0; i < w; i++) {
  553. if (grid2[(h-1)*w+i] == 0) {
  554. if (h % 2)
  555. nfix++;
  556. continue;
  557. }
  558. for (j = 0; j < h && grid2[j*w+i] == 0; j++);
  559. assert(j < h);
  560. if (j == 0) {
  561. /*
  562. * End of previous subarea.
  563. */
  564. if (k % 2)
  565. nerrs++;
  566. k = 0;
  567. } else {
  568. k += j;
  569. }
  570. }
  571. if (k % 2)
  572. nerrs++;
  573. if (nerrs > nfix)
  574. continue; /* try a different placement */
  575. }
  576. /*
  577. * We've made a move. Verify that it is a valid
  578. * move and that if made it would indeed yield the
  579. * previous grid state. The criteria are:
  580. *
  581. * (a) removing all the squares of colour tc (and
  582. * shuffling the columns up etc) from grid2
  583. * would yield grid
  584. * (b) no square of colour tc is adjacent to one
  585. * of colour c
  586. * (c) all the squares of colour tc form a single
  587. * connected component
  588. *
  589. * We verify the latter property at the same time
  590. * as checking that removing all the tc squares
  591. * would yield the previous grid. Then we colour
  592. * the tc squares in colour c by breadth-first
  593. * search, which conveniently permits us to test
  594. * that they're all connected.
  595. */
  596. {
  597. int x1, x2, y1, y2;
  598. bool ok = true;
  599. int fillstart = -1, ntc = 0;
  600. #ifdef GENERATION_DIAGNOSTICS
  601. {
  602. int x,y;
  603. printf("testing move (new, old):\n");
  604. for (y = 0; y < h; y++) {
  605. for (x = 0; x < w; x++) {
  606. if (grid2[y*w+x] == 0)
  607. printf("-");
  608. else if (grid2[y*w+x] <= nc)
  609. printf("%d", grid2[y*w+x]);
  610. else
  611. printf("*");
  612. }
  613. printf(" ");
  614. for (x = 0; x < w; x++) {
  615. if (grid[y*w+x] == 0)
  616. printf("-");
  617. else
  618. printf("%d", grid[y*w+x]);
  619. }
  620. printf("\n");
  621. }
  622. }
  623. #endif
  624. for (x1 = x2 = 0; x2 < w; x2++) {
  625. bool usedcol = false;
  626. for (y1 = y2 = h-1; y2 >= 0; y2--) {
  627. if (grid2[y2*w+x2] == tc) {
  628. ntc++;
  629. if (fillstart == -1)
  630. fillstart = y2*w+x2;
  631. if ((y2+1 < h && grid2[(y2+1)*w+x2] == c) ||
  632. (y2-1 >= 0 && grid2[(y2-1)*w+x2] == c) ||
  633. (x2+1 < w && grid2[y2*w+x2+1] == c) ||
  634. (x2-1 >= 0 && grid2[y2*w+x2-1] == c)) {
  635. #ifdef GENERATION_DIAGNOSTICS
  636. printf("adjacency failure at %d,%d\n",
  637. x2, y2);
  638. #endif
  639. ok = false;
  640. }
  641. continue;
  642. }
  643. if (grid2[y2*w+x2] == 0)
  644. break;
  645. usedcol = true;
  646. if (grid2[y2*w+x2] != grid[y1*w+x1]) {
  647. #ifdef GENERATION_DIAGNOSTICS
  648. printf("matching failure at %d,%d vs %d,%d\n",
  649. x2, y2, x1, y1);
  650. #endif
  651. ok = false;
  652. }
  653. y1--;
  654. }
  655. /*
  656. * If we've reached the top of the column
  657. * in grid2, verify that we've also reached
  658. * the top of the column in `grid'.
  659. */
  660. if (usedcol) {
  661. while (y1 >= 0) {
  662. if (grid[y1*w+x1] != 0) {
  663. #ifdef GENERATION_DIAGNOSTICS
  664. printf("junk at column top (%d,%d)\n",
  665. x1, y1);
  666. #endif
  667. ok = false;
  668. }
  669. y1--;
  670. }
  671. }
  672. if (!ok)
  673. break;
  674. if (usedcol)
  675. x1++;
  676. }
  677. if (!ok) {
  678. assert(!"This should never happen");
  679. /*
  680. * If this game is compiled NDEBUG so that
  681. * the assertion doesn't bring it to a
  682. * crashing halt, the only thing we can do
  683. * is to give up, loop round again, and
  684. * hope to randomly avoid making whatever
  685. * type of move just caused this failure.
  686. */
  687. continue;
  688. }
  689. /*
  690. * Now use bfs to fill in the tc section as
  691. * colour c. We use `list' to store the set of
  692. * squares we have to process.
  693. */
  694. i = j = 0;
  695. assert(fillstart >= 0);
  696. list[i++] = fillstart;
  697. #ifdef OUTPUT_SOLUTION
  698. printf("M");
  699. #endif
  700. while (j < i) {
  701. k = list[j];
  702. x = k % w;
  703. y = k / w;
  704. #ifdef OUTPUT_SOLUTION
  705. printf("%s%d", j ? "," : "", k);
  706. #endif
  707. j++;
  708. assert(grid2[k] == tc);
  709. grid2[k] = c;
  710. if (x > 0 && grid2[k-1] == tc)
  711. list[i++] = k-1;
  712. if (x+1 < w && grid2[k+1] == tc)
  713. list[i++] = k+1;
  714. if (y > 0 && grid2[k-w] == tc)
  715. list[i++] = k-w;
  716. if (y+1 < h && grid2[k+w] == tc)
  717. list[i++] = k+w;
  718. }
  719. #ifdef OUTPUT_SOLUTION
  720. printf("\n");
  721. #endif
  722. /*
  723. * Check that we've filled the same number of
  724. * tc squares as we originally found.
  725. */
  726. assert(j == ntc);
  727. }
  728. memcpy(grid, grid2, wh * sizeof(int));
  729. break; /* done it! */
  730. }
  731. #ifdef GENERATION_DIAGNOSTICS
  732. {
  733. int x,y;
  734. printf("n=%d\n", n);
  735. for (y = 0; y < h; y++) {
  736. for (x = 0; x < w; x++) {
  737. if (grid[y*w+x] == 0)
  738. printf("-");
  739. else
  740. printf("%d", grid[y*w+x]);
  741. }
  742. printf("\n");
  743. }
  744. }
  745. #endif
  746. if (n < 0)
  747. break;
  748. }
  749. ok = true;
  750. for (i = 0; i < wh; i++)
  751. if (grid[i] == 0) {
  752. ok = false;
  753. failures++;
  754. #if defined GENERATION_DIAGNOSTICS || defined SHOW_INCOMPLETE
  755. {
  756. int x,y;
  757. printf("incomplete grid:\n");
  758. for (y = 0; y < h; y++) {
  759. for (x = 0; x < w; x++) {
  760. if (grid[y*w+x] == 0)
  761. printf("-");
  762. else
  763. printf("%d", grid[y*w+x]);
  764. }
  765. printf("\n");
  766. }
  767. }
  768. #endif
  769. break;
  770. }
  771. } while (!ok);
  772. #if defined GENERATION_DIAGNOSTICS || defined COUNT_FAILURES
  773. printf("%d failures\n", failures);
  774. #else
  775. (void)failures;
  776. #endif
  777. #ifdef GENERATION_DIAGNOSTICS
  778. {
  779. int x,y;
  780. printf("final grid:\n");
  781. for (y = 0; y < h; y++) {
  782. for (x = 0; x < w; x++) {
  783. printf("%d", grid[y*w+x]);
  784. }
  785. printf("\n");
  786. }
  787. }
  788. #endif
  789. sfree(grid2);
  790. sfree(list);
  791. }
  792. /*
  793. * Not-guaranteed-soluble grid generator; kept as a legacy, and in
  794. * case someone finds the slightly odd quality of the guaranteed-
  795. * soluble grids to be aesthetically displeasing or finds its CPU
  796. * utilisation to be excessive.
  797. */
  798. static void gen_grid_random(int w, int h, int nc, int *grid, random_state *rs)
  799. {
  800. int i, j, c;
  801. int n = w * h;
  802. for (i = 0; i < n; i++)
  803. grid[i] = 0;
  804. /*
  805. * Our sole concession to not gratuitously generating insoluble
  806. * grids is to ensure we have at least two of every colour.
  807. */
  808. for (c = 1; c <= nc; c++) {
  809. for (j = 0; j < 2; j++) {
  810. do {
  811. i = (int)random_upto(rs, n);
  812. } while (grid[i] != 0);
  813. grid[i] = c;
  814. }
  815. }
  816. /*
  817. * Fill in the rest of the grid at random.
  818. */
  819. for (i = 0; i < n; i++) {
  820. if (grid[i] == 0)
  821. grid[i] = (int)random_upto(rs, nc)+1;
  822. }
  823. }
  824. static char *new_game_desc(const game_params *params, random_state *rs,
  825. char **aux, bool interactive)
  826. {
  827. char *ret;
  828. int n, i, retlen, *tiles;
  829. n = params->w * params->h;
  830. tiles = snewn(n, int);
  831. if (params->soluble)
  832. gen_grid(params->w, params->h, params->ncols, tiles, rs);
  833. else
  834. gen_grid_random(params->w, params->h, params->ncols, tiles, rs);
  835. ret = NULL;
  836. retlen = 0;
  837. for (i = 0; i < n; i++) {
  838. char buf[80];
  839. int k;
  840. k = sprintf(buf, "%d,", tiles[i]);
  841. ret = sresize(ret, retlen + k + 1, char);
  842. strcpy(ret + retlen, buf);
  843. retlen += k;
  844. }
  845. ret[retlen-1] = '\0'; /* delete last comma */
  846. sfree(tiles);
  847. return ret;
  848. }
  849. static const char *validate_desc(const game_params *params, const char *desc)
  850. {
  851. int area = params->w * params->h, i;
  852. const char *p = desc;
  853. for (i = 0; i < area; i++) {
  854. const char *q = p;
  855. int n;
  856. if (!isdigit((unsigned char)*p))
  857. return "Not enough numbers in string";
  858. while (isdigit((unsigned char)*p)) p++;
  859. if (i < area-1 && *p != ',')
  860. return "Expected comma after number";
  861. else if (i == area-1 && *p)
  862. return "Excess junk at end of string";
  863. n = atoi(q);
  864. if (n < 0 || n > params->ncols)
  865. return "Colour out of range";
  866. if (*p) p++; /* eat comma */
  867. }
  868. return NULL;
  869. }
  870. static game_state *new_game(midend *me, const game_params *params,
  871. const char *desc)
  872. {
  873. game_state *state = snew(game_state);
  874. const char *p = desc;
  875. int i;
  876. state->params = *params; /* struct copy */
  877. state->n = state->params.w * state->params.h;
  878. state->tiles = snewn(state->n, int);
  879. for (i = 0; i < state->n; i++) {
  880. assert(*p);
  881. state->tiles[i] = atoi(p);
  882. while (*p && *p != ',')
  883. p++;
  884. if (*p) p++; /* eat comma */
  885. }
  886. state->complete = false;
  887. state->impossible = false;
  888. state->score = 0;
  889. return state;
  890. }
  891. static game_state *dup_game(const game_state *state)
  892. {
  893. game_state *ret = snew(game_state);
  894. *ret = *state; /* structure copy, except... */
  895. ret->tiles = snewn(state->n, int);
  896. memcpy(ret->tiles, state->tiles, state->n * sizeof(int));
  897. return ret;
  898. }
  899. static void free_game(game_state *state)
  900. {
  901. sfree(state->tiles);
  902. sfree(state);
  903. }
  904. static bool game_can_format_as_text_now(const game_params *params)
  905. {
  906. return true;
  907. }
  908. static char *game_text_format(const game_state *state)
  909. {
  910. char *ret, *p;
  911. int x, y, maxlen;
  912. maxlen = state->params.h * (state->params.w + 1);
  913. ret = snewn(maxlen+1, char);
  914. p = ret;
  915. for (y = 0; y < state->params.h; y++) {
  916. for (x = 0; x < state->params.w; x++) {
  917. int t = TILE(state,x,y);
  918. if (t <= 0) *p++ = ' ';
  919. else if (t < 10) *p++ = '0'+t;
  920. else *p++ = 'a'+(t-10);
  921. }
  922. *p++ = '\n';
  923. }
  924. assert(p - ret == maxlen);
  925. *p = '\0';
  926. return ret;
  927. }
  928. struct game_ui {
  929. struct game_params params;
  930. int *tiles; /* selected-ness only */
  931. int nselected;
  932. int xsel, ysel;
  933. bool displaysel;
  934. };
  935. static game_ui *new_ui(const game_state *state)
  936. {
  937. game_ui *ui = snew(game_ui);
  938. ui->params = state->params; /* structure copy */
  939. ui->tiles = snewn(state->n, int);
  940. memset(ui->tiles, 0, state->n*sizeof(int));
  941. ui->nselected = 0;
  942. ui->xsel = ui->ysel = 0;
  943. ui->displaysel = getenv_bool("PUZZLES_SHOW_CURSOR", false);
  944. return ui;
  945. }
  946. static void free_ui(game_ui *ui)
  947. {
  948. sfree(ui->tiles);
  949. sfree(ui);
  950. }
  951. static void sel_clear(game_ui *ui, const game_state *state)
  952. {
  953. int i;
  954. for (i = 0; i < state->n; i++)
  955. ui->tiles[i] &= ~TILE_SELECTED;
  956. ui->nselected = 0;
  957. }
  958. static void game_changed_state(game_ui *ui, const game_state *oldstate,
  959. const game_state *newstate)
  960. {
  961. sel_clear(ui, newstate);
  962. /*
  963. * If the game state has just changed into an unplayable one
  964. * (either completed or impossible), we vanish the keyboard-
  965. * control cursor.
  966. */
  967. if (newstate->complete || newstate->impossible)
  968. ui->displaysel = false;
  969. }
  970. static const char *current_key_label(const game_ui *ui,
  971. const game_state *state, int button)
  972. {
  973. if (IS_CURSOR_SELECT(button)) {
  974. int x = ui->xsel, y = ui->ysel, c = COL(state,x,y);
  975. if (c == 0) return "";
  976. if (ISSEL(ui, x, y))
  977. return button == CURSOR_SELECT2 ? "Unselect" : "Remove";
  978. if ((x > 0 && COL(state,x-1,y) == c) ||
  979. (x+1 < state->params.w && COL(state,x+1,y) == c) ||
  980. (y > 0 && COL(state,x,y-1) == c) ||
  981. (y+1 < state->params.h && COL(state,x,y+1) == c))
  982. return "Select";
  983. /* Cursor is over a lone square, so we can't select it. */
  984. if (ui->nselected) return "Unselect";
  985. }
  986. return "";
  987. }
  988. static char *sel_movedesc(game_ui *ui, const game_state *state)
  989. {
  990. int i;
  991. char *ret, buf[80];
  992. const char *sep;
  993. int retlen, retsize;
  994. retsize = 256;
  995. ret = snewn(retsize, char);
  996. retlen = 0;
  997. ret[retlen++] = 'M';
  998. sep = "";
  999. for (i = 0; i < state->n; i++) {
  1000. if (ui->tiles[i] & TILE_SELECTED) {
  1001. sprintf(buf, "%s%d", sep, i);
  1002. sep = ",";
  1003. if (retlen + (int)strlen(buf) >= retsize) {
  1004. retsize = retlen + strlen(buf) + 256;
  1005. ret = sresize(ret, retsize, char);
  1006. }
  1007. strcpy(ret + retlen, buf);
  1008. retlen += strlen(buf);
  1009. ui->tiles[i] &= ~TILE_SELECTED;
  1010. }
  1011. }
  1012. ui->nselected = 0;
  1013. assert(retlen < retsize);
  1014. ret[retlen++] = '\0';
  1015. return sresize(ret, retlen, char);
  1016. }
  1017. static void sel_expand(game_ui *ui, const game_state *state, int tx, int ty)
  1018. {
  1019. int ns = 1, nadded, x, y, c;
  1020. TILE(ui,tx,ty) |= TILE_SELECTED;
  1021. do {
  1022. nadded = 0;
  1023. for (x = 0; x < state->params.w; x++) {
  1024. for (y = 0; y < state->params.h; y++) {
  1025. if (x == tx && y == ty) continue;
  1026. if (ISSEL(ui,x,y)) continue;
  1027. c = COL(state,x,y);
  1028. if ((x > 0) &&
  1029. ISSEL(ui,x-1,y) && COL(state,x-1,y) == c) {
  1030. TILE(ui,x,y) |= TILE_SELECTED;
  1031. nadded++;
  1032. continue;
  1033. }
  1034. if ((x+1 < state->params.w) &&
  1035. ISSEL(ui,x+1,y) && COL(state,x+1,y) == c) {
  1036. TILE(ui,x,y) |= TILE_SELECTED;
  1037. nadded++;
  1038. continue;
  1039. }
  1040. if ((y > 0) &&
  1041. ISSEL(ui,x,y-1) && COL(state,x,y-1) == c) {
  1042. TILE(ui,x,y) |= TILE_SELECTED;
  1043. nadded++;
  1044. continue;
  1045. }
  1046. if ((y+1 < state->params.h) &&
  1047. ISSEL(ui,x,y+1) && COL(state,x,y+1) == c) {
  1048. TILE(ui,x,y) |= TILE_SELECTED;
  1049. nadded++;
  1050. continue;
  1051. }
  1052. }
  1053. }
  1054. ns += nadded;
  1055. } while (nadded > 0);
  1056. if (ns > 1) {
  1057. ui->nselected = ns;
  1058. } else {
  1059. sel_clear(ui, state);
  1060. }
  1061. }
  1062. static bool sg_emptycol(game_state *ret, int x)
  1063. {
  1064. int y;
  1065. for (y = 0; y < ret->params.h; y++) {
  1066. if (COL(ret,x,y)) return false;
  1067. }
  1068. return true;
  1069. }
  1070. static void sg_snuggle(game_state *ret)
  1071. {
  1072. int x,y, ndone;
  1073. /* make all unsupported tiles fall down. */
  1074. do {
  1075. ndone = 0;
  1076. for (x = 0; x < ret->params.w; x++) {
  1077. for (y = ret->params.h-1; y > 0; y--) {
  1078. if (COL(ret,x,y) != 0) continue;
  1079. if (COL(ret,x,y-1) != 0) {
  1080. SWAPTILE(ret,x,y,x,y-1);
  1081. ndone++;
  1082. }
  1083. }
  1084. }
  1085. } while (ndone);
  1086. /* shuffle all columns as far left as they can go. */
  1087. do {
  1088. ndone = 0;
  1089. for (x = 0; x < ret->params.w-1; x++) {
  1090. if (sg_emptycol(ret,x) && !sg_emptycol(ret,x+1)) {
  1091. ndone++;
  1092. for (y = 0; y < ret->params.h; y++) {
  1093. SWAPTILE(ret,x,y,x+1,y);
  1094. }
  1095. }
  1096. }
  1097. } while (ndone);
  1098. }
  1099. static void sg_check(game_state *ret)
  1100. {
  1101. int x,y;
  1102. bool complete = true, impossible = true;
  1103. for (x = 0; x < ret->params.w; x++) {
  1104. for (y = 0; y < ret->params.h; y++) {
  1105. if (COL(ret,x,y) == 0)
  1106. continue;
  1107. complete = false;
  1108. if (x+1 < ret->params.w) {
  1109. if (COL(ret,x,y) == COL(ret,x+1,y))
  1110. impossible = false;
  1111. }
  1112. if (y+1 < ret->params.h) {
  1113. if (COL(ret,x,y) == COL(ret,x,y+1))
  1114. impossible = false;
  1115. }
  1116. }
  1117. }
  1118. ret->complete = complete;
  1119. ret->impossible = impossible;
  1120. }
  1121. struct game_drawstate {
  1122. bool started;
  1123. int bgcolour;
  1124. int tileinner, tilegap;
  1125. int *tiles; /* contains colour and SELECTED. */
  1126. };
  1127. static char *interpret_move(const game_state *state, game_ui *ui,
  1128. const game_drawstate *ds,
  1129. int x, int y, int button)
  1130. {
  1131. int tx, ty;
  1132. char *ret = MOVE_UI_UPDATE;
  1133. ui->displaysel = false;
  1134. if (button == RIGHT_BUTTON || button == LEFT_BUTTON) {
  1135. tx = FROMCOORD(x); ty= FROMCOORD(y);
  1136. } else if (IS_CURSOR_MOVE(button)) {
  1137. int dx = 0, dy = 0;
  1138. ui->displaysel = true;
  1139. dx = (button == CURSOR_LEFT) ? -1 : ((button == CURSOR_RIGHT) ? +1 : 0);
  1140. dy = (button == CURSOR_DOWN) ? +1 : ((button == CURSOR_UP) ? -1 : 0);
  1141. ui->xsel = (ui->xsel + state->params.w + dx) % state->params.w;
  1142. ui->ysel = (ui->ysel + state->params.h + dy) % state->params.h;
  1143. return ret;
  1144. } else if (IS_CURSOR_SELECT(button)) {
  1145. ui->displaysel = true;
  1146. tx = ui->xsel;
  1147. ty = ui->ysel;
  1148. } else
  1149. return NULL;
  1150. if (tx < 0 || tx >= state->params.w || ty < 0 || ty >= state->params.h)
  1151. return NULL;
  1152. if (COL(state, tx, ty) == 0) return NULL;
  1153. if (ISSEL(ui,tx,ty)) {
  1154. if (button == RIGHT_BUTTON || button == CURSOR_SELECT2)
  1155. sel_clear(ui, state);
  1156. else
  1157. ret = sel_movedesc(ui, state);
  1158. } else {
  1159. sel_clear(ui, state); /* might be no-op */
  1160. sel_expand(ui, state, tx, ty);
  1161. }
  1162. return ret;
  1163. }
  1164. static game_state *execute_move(const game_state *from, const char *move)
  1165. {
  1166. int i, n;
  1167. game_state *ret;
  1168. if (move[0] == 'M') {
  1169. ret = dup_game(from);
  1170. n = 0;
  1171. move++;
  1172. while (*move) {
  1173. if (!isdigit((unsigned char)*move)) {
  1174. free_game(ret);
  1175. return NULL;
  1176. }
  1177. i = atoi(move);
  1178. if (i < 0 || i >= ret->n) {
  1179. free_game(ret);
  1180. return NULL;
  1181. }
  1182. n++;
  1183. ret->tiles[i] = 0;
  1184. while (*move && isdigit((unsigned char)*move)) move++;
  1185. if (*move == ',') move++;
  1186. }
  1187. ret->score += npoints(&ret->params, n);
  1188. sg_snuggle(ret); /* shifts blanks down and to the left */
  1189. sg_check(ret); /* checks for completeness or impossibility */
  1190. return ret;
  1191. } else
  1192. return NULL; /* couldn't parse move string */
  1193. }
  1194. /* ----------------------------------------------------------------------
  1195. * Drawing routines.
  1196. */
  1197. static void game_set_size(drawing *dr, game_drawstate *ds,
  1198. const game_params *params, int tilesize)
  1199. {
  1200. ds->tilegap = 2;
  1201. ds->tileinner = tilesize - ds->tilegap;
  1202. }
  1203. static void game_compute_size(const game_params *params, int tilesize,
  1204. const game_ui *ui, int *x, int *y)
  1205. {
  1206. /* Ick: fake up tile size variables for macro expansion purposes */
  1207. game_drawstate ads, *ds = &ads;
  1208. game_set_size(NULL, ds, params, tilesize);
  1209. *x = TILE_SIZE * params->w + 2 * BORDER - TILE_GAP;
  1210. *y = TILE_SIZE * params->h + 2 * BORDER - TILE_GAP;
  1211. }
  1212. static float *game_colours(frontend *fe, int *ncolours)
  1213. {
  1214. float *ret = snewn(3 * NCOLOURS, float);
  1215. game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
  1216. ret[COL_1 * 3 + 0] = 0.0F;
  1217. ret[COL_1 * 3 + 1] = 0.0F;
  1218. ret[COL_1 * 3 + 2] = 1.0F;
  1219. ret[COL_2 * 3 + 0] = 0.0F;
  1220. ret[COL_2 * 3 + 1] = 0.5F;
  1221. ret[COL_2 * 3 + 2] = 0.0F;
  1222. ret[COL_3 * 3 + 0] = 1.0F;
  1223. ret[COL_3 * 3 + 1] = 0.0F;
  1224. ret[COL_3 * 3 + 2] = 0.0F;
  1225. ret[COL_4 * 3 + 0] = 0.7F;
  1226. ret[COL_4 * 3 + 1] = 0.7F;
  1227. ret[COL_4 * 3 + 2] = 0.0F;
  1228. ret[COL_5 * 3 + 0] = 1.0F;
  1229. ret[COL_5 * 3 + 1] = 0.0F;
  1230. ret[COL_5 * 3 + 2] = 1.0F;
  1231. ret[COL_6 * 3 + 0] = 0.0F;
  1232. ret[COL_6 * 3 + 1] = 0.8F;
  1233. ret[COL_6 * 3 + 2] = 0.8F;
  1234. ret[COL_7 * 3 + 0] = 0.5F;
  1235. ret[COL_7 * 3 + 1] = 0.5F;
  1236. ret[COL_7 * 3 + 2] = 1.0F;
  1237. ret[COL_8 * 3 + 0] = 0.2F;
  1238. ret[COL_8 * 3 + 1] = 0.8F;
  1239. ret[COL_8 * 3 + 2] = 0.2F;
  1240. ret[COL_9 * 3 + 0] = 1.0F;
  1241. ret[COL_9 * 3 + 1] = 0.5F;
  1242. ret[COL_9 * 3 + 2] = 0.5F;
  1243. ret[COL_IMPOSSIBLE * 3 + 0] = 0.0F;
  1244. ret[COL_IMPOSSIBLE * 3 + 1] = 0.0F;
  1245. ret[COL_IMPOSSIBLE * 3 + 2] = 0.0F;
  1246. ret[COL_SEL * 3 + 0] = 1.0F;
  1247. ret[COL_SEL * 3 + 1] = 1.0F;
  1248. ret[COL_SEL * 3 + 2] = 1.0F;
  1249. *ncolours = NCOLOURS;
  1250. return ret;
  1251. }
  1252. static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
  1253. {
  1254. struct game_drawstate *ds = snew(struct game_drawstate);
  1255. int i;
  1256. ds->started = false;
  1257. ds->tileinner = ds->tilegap = 0; /* not decided yet */
  1258. ds->tiles = snewn(state->n, int);
  1259. ds->bgcolour = -1;
  1260. for (i = 0; i < state->n; i++)
  1261. ds->tiles[i] = -1;
  1262. return ds;
  1263. }
  1264. static void game_free_drawstate(drawing *dr, game_drawstate *ds)
  1265. {
  1266. sfree(ds->tiles);
  1267. sfree(ds);
  1268. }
  1269. /* Drawing routing for the tile at (x,y) is responsible for drawing
  1270. * itself and the gaps to its right and below. If we're the same colour
  1271. * as the tile to our right, then we fill in the gap; ditto below, and if
  1272. * both then we fill the teeny tiny square in the corner as well.
  1273. */
  1274. static void tile_redraw(drawing *dr, game_drawstate *ds,
  1275. int x, int y, bool dright, bool dbelow,
  1276. int tile, int bgcolour)
  1277. {
  1278. int outer = bgcolour, inner = outer, col = tile & TILE_COLMASK;
  1279. if (col) {
  1280. if (tile & TILE_IMPOSSIBLE) {
  1281. outer = col;
  1282. inner = COL_IMPOSSIBLE;
  1283. } else if (tile & TILE_SELECTED) {
  1284. outer = COL_SEL;
  1285. inner = col;
  1286. } else {
  1287. outer = inner = col;
  1288. }
  1289. }
  1290. draw_rect(dr, COORD(x), COORD(y), TILE_INNER, TILE_INNER, outer);
  1291. draw_rect(dr, COORD(x)+TILE_INNER/4, COORD(y)+TILE_INNER/4,
  1292. TILE_INNER/2, TILE_INNER/2, inner);
  1293. if (dright)
  1294. draw_rect(dr, COORD(x)+TILE_INNER, COORD(y), TILE_GAP, TILE_INNER,
  1295. (tile & TILE_JOINRIGHT) ? outer : bgcolour);
  1296. if (dbelow)
  1297. draw_rect(dr, COORD(x), COORD(y)+TILE_INNER, TILE_INNER, TILE_GAP,
  1298. (tile & TILE_JOINDOWN) ? outer : bgcolour);
  1299. if (dright && dbelow)
  1300. draw_rect(dr, COORD(x)+TILE_INNER, COORD(y)+TILE_INNER, TILE_GAP, TILE_GAP,
  1301. (tile & TILE_JOINDIAG) ? outer : bgcolour);
  1302. if (tile & TILE_HASSEL) {
  1303. int sx = COORD(x)+2, sy = COORD(y)+2, ssz = TILE_INNER-5;
  1304. int scol = (outer == COL_SEL) ? COL_LOWLIGHT : COL_HIGHLIGHT;
  1305. draw_line(dr, sx, sy, sx+ssz, sy, scol);
  1306. draw_line(dr, sx+ssz, sy, sx+ssz, sy+ssz, scol);
  1307. draw_line(dr, sx+ssz, sy+ssz, sx, sy+ssz, scol);
  1308. draw_line(dr, sx, sy+ssz, sx, sy, scol);
  1309. }
  1310. draw_update(dr, COORD(x), COORD(y), TILE_SIZE, TILE_SIZE);
  1311. }
  1312. static void game_redraw(drawing *dr, game_drawstate *ds,
  1313. const game_state *oldstate, const game_state *state,
  1314. int dir, const game_ui *ui,
  1315. float animtime, float flashtime)
  1316. {
  1317. int bgcolour, x, y;
  1318. /* This was entirely cloned from fifteen.c; it should probably be
  1319. * moved into some generic 'draw-recessed-rectangle' utility fn. */
  1320. if (!ds->started) {
  1321. int coords[10];
  1322. /*
  1323. * Recessed area containing the whole puzzle.
  1324. */
  1325. coords[0] = COORD(state->params.w) + HIGHLIGHT_WIDTH - 1 - TILE_GAP;
  1326. coords[1] = COORD(state->params.h) + HIGHLIGHT_WIDTH - 1 - TILE_GAP;
  1327. coords[2] = COORD(state->params.w) + HIGHLIGHT_WIDTH - 1 - TILE_GAP;
  1328. coords[3] = COORD(0) - HIGHLIGHT_WIDTH;
  1329. coords[4] = coords[2] - TILE_SIZE;
  1330. coords[5] = coords[3] + TILE_SIZE;
  1331. coords[8] = COORD(0) - HIGHLIGHT_WIDTH;
  1332. coords[9] = COORD(state->params.h) + HIGHLIGHT_WIDTH - 1 - TILE_GAP;
  1333. coords[6] = coords[8] + TILE_SIZE;
  1334. coords[7] = coords[9] - TILE_SIZE;
  1335. draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT);
  1336. coords[1] = COORD(0) - HIGHLIGHT_WIDTH;
  1337. coords[0] = COORD(0) - HIGHLIGHT_WIDTH;
  1338. draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT);
  1339. ds->started = true;
  1340. }
  1341. if (flashtime > 0.0F) {
  1342. int frame = (int)(flashtime / FLASH_FRAME);
  1343. bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT);
  1344. } else
  1345. bgcolour = COL_BACKGROUND;
  1346. for (x = 0; x < state->params.w; x++) {
  1347. for (y = 0; y < state->params.h; y++) {
  1348. int i = (state->params.w * y) + x;
  1349. int col = COL(state,x,y), tile = col;
  1350. bool dright = (x+1 < state->params.w);
  1351. bool dbelow = (y+1 < state->params.h);
  1352. tile |= ISSEL(ui,x,y);
  1353. if (state->impossible)
  1354. tile |= TILE_IMPOSSIBLE;
  1355. if (dright && COL(state,x+1,y) == col)
  1356. tile |= TILE_JOINRIGHT;
  1357. if (dbelow && COL(state,x,y+1) == col)
  1358. tile |= TILE_JOINDOWN;
  1359. if ((tile & TILE_JOINRIGHT) && (tile & TILE_JOINDOWN) &&
  1360. COL(state,x+1,y+1) == col)
  1361. tile |= TILE_JOINDIAG;
  1362. if (ui->displaysel && ui->xsel == x && ui->ysel == y)
  1363. tile |= TILE_HASSEL;
  1364. /* For now we're never expecting oldstate at all (because we have
  1365. * no animation); when we do we might well want to be looking
  1366. * at the tile colours from oldstate, not state. */
  1367. if ((oldstate && COL(oldstate,x,y) != col) ||
  1368. (ds->bgcolour != bgcolour) ||
  1369. (tile != ds->tiles[i])) {
  1370. tile_redraw(dr, ds, x, y, dright, dbelow, tile, bgcolour);
  1371. ds->tiles[i] = tile;
  1372. }
  1373. }
  1374. }
  1375. ds->bgcolour = bgcolour;
  1376. {
  1377. char status[255], score[80];
  1378. sprintf(score, "Score: %d", state->score);
  1379. if (state->complete)
  1380. sprintf(status, "COMPLETE! %s", score);
  1381. else if (state->impossible)
  1382. sprintf(status, "Cannot move! %s", score);
  1383. else if (ui->nselected)
  1384. sprintf(status, "%s Selected: %d (%d)",
  1385. score, ui->nselected, npoints(&state->params, ui->nselected));
  1386. else
  1387. sprintf(status, "%s", score);
  1388. status_bar(dr, status);
  1389. }
  1390. }
  1391. static float game_anim_length(const game_state *oldstate,
  1392. const game_state *newstate, int dir, game_ui *ui)
  1393. {
  1394. return 0.0F;
  1395. }
  1396. static float game_flash_length(const game_state *oldstate,
  1397. const game_state *newstate, int dir, game_ui *ui)
  1398. {
  1399. if ((!oldstate->complete && newstate->complete) ||
  1400. (!oldstate->impossible && newstate->impossible))
  1401. return 2 * FLASH_FRAME;
  1402. else
  1403. return 0.0F;
  1404. }
  1405. static void game_get_cursor_location(const game_ui *ui,
  1406. const game_drawstate *ds,
  1407. const game_state *state,
  1408. const game_params *params,
  1409. int *x, int *y, int *w, int *h)
  1410. {
  1411. if(ui->displaysel) {
  1412. *x = COORD(ui->xsel);
  1413. *y = COORD(ui->ysel);
  1414. *w = *h = TILE_SIZE;
  1415. }
  1416. }
  1417. static int game_status(const game_state *state)
  1418. {
  1419. /*
  1420. * Dead-end situations are assumed to be rescuable by Undo, so we
  1421. * don't bother to identify them and return -1.
  1422. */
  1423. return state->complete ? +1 : 0;
  1424. }
  1425. #ifdef COMBINED
  1426. #define thegame samegame
  1427. #endif
  1428. const struct game thegame = {
  1429. "Same Game", "games.samegame", "samegame",
  1430. default_params,
  1431. game_fetch_preset, NULL,
  1432. decode_params,
  1433. encode_params,
  1434. free_params,
  1435. dup_params,
  1436. true, game_configure, custom_params,
  1437. validate_params,
  1438. new_game_desc,
  1439. validate_desc,
  1440. new_game,
  1441. dup_game,
  1442. free_game,
  1443. false, NULL, /* solve */
  1444. true, game_can_format_as_text_now, game_text_format,
  1445. NULL, NULL, /* get_prefs, set_prefs */
  1446. new_ui,
  1447. free_ui,
  1448. NULL, /* encode_ui */
  1449. NULL, /* decode_ui */
  1450. NULL, /* game_request_keys */
  1451. game_changed_state,
  1452. current_key_label,
  1453. interpret_move,
  1454. execute_move,
  1455. PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
  1456. game_colours,
  1457. game_new_drawstate,
  1458. game_free_drawstate,
  1459. game_redraw,
  1460. game_anim_length,
  1461. game_flash_length,
  1462. game_get_cursor_location,
  1463. game_status,
  1464. false, false, NULL, NULL, /* print_size, print */
  1465. true, /* wants_statusbar */
  1466. false, NULL, /* timing_state */
  1467. 0, /* flags */
  1468. };