twiddle.c 37 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334
  1. /*
  2. * twiddle.c: Puzzle involving rearranging a grid of squares by
  3. * rotating subsquares. Adapted and generalised from a
  4. * door-unlocking puzzle in Metroid Prime 2 (the one in the Main
  5. * Gyro Chamber).
  6. */
  7. #include <stdio.h>
  8. #include <stdlib.h>
  9. #include <string.h>
  10. #include <assert.h>
  11. #include <ctype.h>
  12. #include <limits.h>
  13. #ifdef NO_TGMATH_H
  14. # include <math.h>
  15. #else
  16. # include <tgmath.h>
  17. #endif
  18. #include "puzzles.h"
  19. #define PREFERRED_TILE_SIZE 48
  20. #define TILE_SIZE (ds->tilesize)
  21. #define BORDER (TILE_SIZE / 2)
  22. #define HIGHLIGHT_WIDTH (TILE_SIZE / 20)
  23. #define COORD(x) ( (x) * TILE_SIZE + BORDER )
  24. #define FROMCOORD(x) ( ((x) - BORDER + TILE_SIZE) / TILE_SIZE - 1 )
  25. #define ANIM_PER_BLKSIZE_UNIT 0.13F
  26. #define FLASH_FRAME 0.13F
  27. enum {
  28. COL_BACKGROUND,
  29. COL_TEXT,
  30. COL_HIGHLIGHT,
  31. COL_HIGHLIGHT_GENTLE,
  32. COL_LOWLIGHT,
  33. COL_LOWLIGHT_GENTLE,
  34. COL_HIGHCURSOR, COL_LOWCURSOR,
  35. NCOLOURS
  36. };
  37. struct game_params {
  38. int w, h, n;
  39. bool rowsonly;
  40. bool orientable;
  41. int movetarget;
  42. };
  43. struct game_state {
  44. int w, h, n;
  45. bool orientable;
  46. int *grid;
  47. int completed;
  48. bool used_solve; /* used to suppress completion flash */
  49. int movecount, movetarget;
  50. int lastx, lasty, lastr; /* coordinates of last rotation */
  51. };
  52. static game_params *default_params(void)
  53. {
  54. game_params *ret = snew(game_params);
  55. ret->w = ret->h = 3;
  56. ret->n = 2;
  57. ret->rowsonly = ret->orientable = false;
  58. ret->movetarget = 0;
  59. return ret;
  60. }
  61. static void free_params(game_params *params)
  62. {
  63. sfree(params);
  64. }
  65. static game_params *dup_params(const game_params *params)
  66. {
  67. game_params *ret = snew(game_params);
  68. *ret = *params; /* structure copy */
  69. return ret;
  70. }
  71. static bool game_fetch_preset(int i, char **name, game_params **params)
  72. {
  73. static struct {
  74. const char *title;
  75. game_params params;
  76. } const presets[] = {
  77. { "3x3 rows only", { 3, 3, 2, true, false } },
  78. { "3x3 normal", { 3, 3, 2, false, false } },
  79. { "3x3 orientable", { 3, 3, 2, false, true } },
  80. { "4x4 normal", { 4, 4, 2, false } },
  81. { "4x4 orientable", { 4, 4, 2, false, true } },
  82. { "4x4, rotating 3x3 blocks", { 4, 4, 3, false } },
  83. { "5x5, rotating 3x3 blocks", { 5, 5, 3, false } },
  84. { "6x6, rotating 4x4 blocks", { 6, 6, 4, false } },
  85. };
  86. if (i < 0 || i >= lenof(presets))
  87. return false;
  88. *name = dupstr(presets[i].title);
  89. *params = dup_params(&presets[i].params);
  90. return true;
  91. }
  92. static void decode_params(game_params *ret, char const *string)
  93. {
  94. ret->w = ret->h = atoi(string);
  95. ret->n = 2;
  96. ret->rowsonly = false;
  97. ret->orientable = false;
  98. ret->movetarget = 0;
  99. while (*string && isdigit((unsigned char)*string)) string++;
  100. if (*string == 'x') {
  101. string++;
  102. ret->h = atoi(string);
  103. while (*string && isdigit((unsigned char)*string)) string++;
  104. }
  105. if (*string == 'n') {
  106. string++;
  107. ret->n = atoi(string);
  108. while (*string && isdigit((unsigned char)*string)) string++;
  109. }
  110. while (*string) {
  111. if (*string == 'r') {
  112. ret->rowsonly = true;
  113. string++;
  114. } else if (*string == 'o') {
  115. ret->orientable = true;
  116. string++;
  117. } else if (*string == 'm') {
  118. string++;
  119. ret->movetarget = atoi(string);
  120. while (*string && isdigit((unsigned char)*string)) string++;
  121. } else
  122. string++;
  123. }
  124. }
  125. static char *encode_params(const game_params *params, bool full)
  126. {
  127. char buf[256];
  128. sprintf(buf, "%dx%dn%d%s%s", params->w, params->h, params->n,
  129. params->rowsonly ? "r" : "",
  130. params->orientable ? "o" : "");
  131. /* Shuffle limit is part of the limited parameters, because we have to
  132. * supply the target move count. */
  133. if (params->movetarget)
  134. sprintf(buf + strlen(buf), "m%d", params->movetarget);
  135. return dupstr(buf);
  136. }
  137. static config_item *game_configure(const game_params *params)
  138. {
  139. config_item *ret;
  140. char buf[80];
  141. ret = snewn(7, config_item);
  142. ret[0].name = "Width";
  143. ret[0].type = C_STRING;
  144. sprintf(buf, "%d", params->w);
  145. ret[0].u.string.sval = dupstr(buf);
  146. ret[1].name = "Height";
  147. ret[1].type = C_STRING;
  148. sprintf(buf, "%d", params->h);
  149. ret[1].u.string.sval = dupstr(buf);
  150. ret[2].name = "Rotating block size";
  151. ret[2].type = C_STRING;
  152. sprintf(buf, "%d", params->n);
  153. ret[2].u.string.sval = dupstr(buf);
  154. ret[3].name = "One number per row";
  155. ret[3].type = C_BOOLEAN;
  156. ret[3].u.boolean.bval = params->rowsonly;
  157. ret[4].name = "Orientation matters";
  158. ret[4].type = C_BOOLEAN;
  159. ret[4].u.boolean.bval = params->orientable;
  160. ret[5].name = "Number of shuffling moves";
  161. ret[5].type = C_STRING;
  162. sprintf(buf, "%d", params->movetarget);
  163. ret[5].u.string.sval = dupstr(buf);
  164. ret[6].name = NULL;
  165. ret[6].type = C_END;
  166. return ret;
  167. }
  168. static game_params *custom_params(const config_item *cfg)
  169. {
  170. game_params *ret = snew(game_params);
  171. ret->w = atoi(cfg[0].u.string.sval);
  172. ret->h = atoi(cfg[1].u.string.sval);
  173. ret->n = atoi(cfg[2].u.string.sval);
  174. ret->rowsonly = cfg[3].u.boolean.bval;
  175. ret->orientable = cfg[4].u.boolean.bval;
  176. ret->movetarget = atoi(cfg[5].u.string.sval);
  177. return ret;
  178. }
  179. static const char *validate_params(const game_params *params, bool full)
  180. {
  181. if (params->n < 2)
  182. return "Rotating block size must be at least two";
  183. if (params->w < params->n)
  184. return "Width must be at least the rotating block size";
  185. if (params->h < params->n)
  186. return "Height must be at least the rotating block size";
  187. if (params->w > INT_MAX / params->h)
  188. return "Width times height must not be unreasonably large";
  189. return NULL;
  190. }
  191. /*
  192. * This function actually performs a rotation on a grid. The `x'
  193. * and `y' coordinates passed in are the coordinates of the _top
  194. * left corner_ of the rotated region. (Using the centre would have
  195. * involved half-integers and been annoyingly fiddly. Clicking in
  196. * the centre is good for a user interface, but too inconvenient to
  197. * use internally.)
  198. */
  199. static void do_rotate(int *grid, int w, int h, int n, bool orientable,
  200. int x, int y, int dir)
  201. {
  202. int i, j;
  203. assert(x >= 0 && x+n <= w);
  204. assert(y >= 0 && y+n <= h);
  205. dir &= 3;
  206. if (dir == 0)
  207. return; /* nothing to do */
  208. grid += y*w+x; /* translate region to top corner */
  209. /*
  210. * If we were leaving the result of the rotation in a separate
  211. * grid, the simple thing to do would be to loop over each
  212. * square within the rotated region and assign it from its
  213. * source square. However, to do it in place without taking
  214. * O(n^2) memory, we need to be marginally more clever. What
  215. * I'm going to do is loop over about one _quarter_ of the
  216. * rotated region and permute each element within that quarter
  217. * with its rotational coset.
  218. *
  219. * The size of the region I need to loop over is (n+1)/2 by
  220. * n/2, which is an obvious exact quarter for even n and is a
  221. * rectangle for odd n. (For odd n, this technique leaves out
  222. * one element of the square, which is of course the central
  223. * one that never moves anyway.)
  224. */
  225. for (i = 0; i < (n+1)/2; i++) {
  226. for (j = 0; j < n/2; j++) {
  227. int k;
  228. int g[4];
  229. int p[4];
  230. p[0] = j*w+i;
  231. p[1] = i*w+(n-j-1);
  232. p[2] = (n-j-1)*w+(n-i-1);
  233. p[3] = (n-i-1)*w+j;
  234. for (k = 0; k < 4; k++)
  235. g[k] = grid[p[k]];
  236. for (k = 0; k < 4; k++) {
  237. int v = g[(k+dir) & 3];
  238. if (orientable)
  239. v ^= ((v+dir) ^ v) & 3; /* alter orientation */
  240. grid[p[k]] = v;
  241. }
  242. }
  243. }
  244. /*
  245. * Don't forget the orientation on the centre square, if n is
  246. * odd.
  247. */
  248. if (orientable && (n & 1)) {
  249. int v = grid[n/2*(w+1)];
  250. v ^= ((v+dir) ^ v) & 3; /* alter orientation */
  251. grid[n/2*(w+1)] = v;
  252. }
  253. }
  254. static bool grid_complete(int *grid, int wh, bool orientable)
  255. {
  256. bool ok = true;
  257. int i;
  258. for (i = 1; i < wh; i++)
  259. if (grid[i] < grid[i-1])
  260. ok = false;
  261. if (orientable) {
  262. for (i = 0; i < wh; i++)
  263. if (grid[i] & 3)
  264. ok = false;
  265. }
  266. return ok;
  267. }
  268. static char *new_game_desc(const game_params *params, random_state *rs,
  269. char **aux, bool interactive)
  270. {
  271. int *grid;
  272. int w = params->w, h = params->h, n = params->n, wh = w*h;
  273. int i;
  274. char *ret;
  275. int retlen;
  276. int total_moves;
  277. /*
  278. * Set up a solved grid.
  279. */
  280. grid = snewn(wh, int);
  281. for (i = 0; i < wh; i++)
  282. grid[i] = ((params->rowsonly ? i/w : i) + 1) * 4;
  283. /*
  284. * Shuffle it. This game is complex enough that I don't feel up
  285. * to analysing its full symmetry properties (particularly at
  286. * n=4 and above!), so I'm going to do it the pedestrian way
  287. * and simply shuffle the grid by making a long sequence of
  288. * randomly chosen moves.
  289. */
  290. total_moves = params->movetarget;
  291. if (!total_moves)
  292. /* Add a random move to avoid parity issues. */
  293. total_moves = w*h*n*n*2 + random_upto(rs, 2);
  294. do {
  295. int *prevmoves;
  296. int rw, rh; /* w/h of rotation centre space */
  297. rw = w - n + 1;
  298. rh = h - n + 1;
  299. prevmoves = snewn(rw * rh, int);
  300. for (i = 0; i < rw * rh; i++)
  301. prevmoves[i] = 0;
  302. for (i = 0; i < total_moves; i++) {
  303. int x, y, r, oldtotal, newtotal, dx, dy;
  304. do {
  305. x = random_upto(rs, w - n + 1);
  306. y = random_upto(rs, h - n + 1);
  307. r = 2 * random_upto(rs, 2) - 1;
  308. /*
  309. * See if any previous rotations has happened at
  310. * this point which nothing has overlapped since.
  311. * If so, ensure we haven't either undone a
  312. * previous move or repeated one so many times that
  313. * it turns into fewer moves in the inverse
  314. * direction (i.e. three identical rotations).
  315. */
  316. oldtotal = prevmoves[y*rw+x];
  317. newtotal = oldtotal + r;
  318. /*
  319. * Special case here for w==h==n, in which case
  320. * there is actually no way to _avoid_ all moves
  321. * repeating or undoing previous ones.
  322. */
  323. } while ((w != n || h != n) &&
  324. (abs(newtotal) < abs(oldtotal) || abs(newtotal) > 2));
  325. do_rotate(grid, w, h, n, params->orientable, x, y, r);
  326. /*
  327. * Log the rotation we've just performed at this point,
  328. * for inversion detection in the next move.
  329. *
  330. * Also zero a section of the prevmoves array, because
  331. * any rotation area which _overlaps_ this one is now
  332. * entirely safe to perform further moves in.
  333. *
  334. * Two rotation areas overlap if their top left
  335. * coordinates differ by strictly less than n in both
  336. * directions
  337. */
  338. prevmoves[y*rw+x] += r;
  339. for (dy = -n+1; dy <= n-1; dy++) {
  340. if (y + dy < 0 || y + dy >= rh)
  341. continue;
  342. for (dx = -n+1; dx <= n-1; dx++) {
  343. if (x + dx < 0 || x + dx >= rw)
  344. continue;
  345. if (dx == 0 && dy == 0)
  346. continue;
  347. prevmoves[(y+dy)*rw+(x+dx)] = 0;
  348. }
  349. }
  350. }
  351. sfree(prevmoves);
  352. } while (grid_complete(grid, wh, params->orientable));
  353. /*
  354. * Now construct the game description, by describing the grid
  355. * as a simple sequence of integers. They're comma-separated,
  356. * unless the puzzle is orientable in which case they're
  357. * separated by orientation letters `u', `d', `l' and `r'.
  358. */
  359. ret = NULL;
  360. retlen = 0;
  361. for (i = 0; i < wh; i++) {
  362. char buf[80];
  363. int k;
  364. k = sprintf(buf, "%d%c", grid[i] / 4,
  365. (char)(params->orientable ? "uldr"[grid[i] & 3] : ','));
  366. ret = sresize(ret, retlen + k + 1, char);
  367. strcpy(ret + retlen, buf);
  368. retlen += k;
  369. }
  370. if (!params->orientable)
  371. ret[retlen-1] = '\0'; /* delete last comma */
  372. sfree(grid);
  373. return ret;
  374. }
  375. static const char *validate_desc(const game_params *params, const char *desc)
  376. {
  377. const char *p;
  378. int w = params->w, h = params->h, wh = w*h;
  379. int i;
  380. p = desc;
  381. for (i = 0; i < wh; i++) {
  382. if (*p < '0' || *p > '9')
  383. return "Not enough numbers in string";
  384. while (*p >= '0' && *p <= '9')
  385. p++;
  386. if (!params->orientable && i < wh-1) {
  387. if (*p != ',')
  388. return "Expected comma after number";
  389. } else if (params->orientable && i < wh) {
  390. if (*p != 'l' && *p != 'r' && *p != 'u' && *p != 'd')
  391. return "Expected orientation letter after number";
  392. } else if (i == wh-1 && *p) {
  393. return "Excess junk at end of string";
  394. }
  395. if (*p) p++; /* eat comma */
  396. }
  397. return NULL;
  398. }
  399. static game_state *new_game(midend *me, const game_params *params,
  400. const char *desc)
  401. {
  402. game_state *state = snew(game_state);
  403. int w = params->w, h = params->h, n = params->n, wh = w*h;
  404. int i;
  405. const char *p;
  406. state->w = w;
  407. state->h = h;
  408. state->n = n;
  409. state->orientable = params->orientable;
  410. state->completed = 0;
  411. state->used_solve = false;
  412. state->movecount = 0;
  413. state->movetarget = params->movetarget;
  414. state->lastx = state->lasty = state->lastr = -1;
  415. state->grid = snewn(wh, int);
  416. p = desc;
  417. for (i = 0; i < wh; i++) {
  418. state->grid[i] = 4 * atoi(p);
  419. while (*p >= '0' && *p <= '9')
  420. p++;
  421. if (*p) {
  422. if (params->orientable) {
  423. switch (*p) {
  424. case 'l': state->grid[i] |= 1; break;
  425. case 'd': state->grid[i] |= 2; break;
  426. case 'r': state->grid[i] |= 3; break;
  427. }
  428. }
  429. p++;
  430. }
  431. }
  432. return state;
  433. }
  434. static game_state *dup_game(const game_state *state)
  435. {
  436. game_state *ret = snew(game_state);
  437. ret->w = state->w;
  438. ret->h = state->h;
  439. ret->n = state->n;
  440. ret->orientable = state->orientable;
  441. ret->completed = state->completed;
  442. ret->movecount = state->movecount;
  443. ret->movetarget = state->movetarget;
  444. ret->lastx = state->lastx;
  445. ret->lasty = state->lasty;
  446. ret->lastr = state->lastr;
  447. ret->used_solve = state->used_solve;
  448. ret->grid = snewn(ret->w * ret->h, int);
  449. memcpy(ret->grid, state->grid, ret->w * ret->h * sizeof(int));
  450. return ret;
  451. }
  452. static void free_game(game_state *state)
  453. {
  454. sfree(state->grid);
  455. sfree(state);
  456. }
  457. static int compare_int(const void *av, const void *bv)
  458. {
  459. const int *a = (const int *)av;
  460. const int *b = (const int *)bv;
  461. if (*a < *b)
  462. return -1;
  463. else if (*a > *b)
  464. return +1;
  465. else
  466. return 0;
  467. }
  468. static char *solve_game(const game_state *state, const game_state *currstate,
  469. const char *aux, const char **error)
  470. {
  471. return dupstr("S");
  472. }
  473. static bool game_can_format_as_text_now(const game_params *params)
  474. {
  475. return true;
  476. }
  477. static char *game_text_format(const game_state *state)
  478. {
  479. char *ret, *p, buf[80];
  480. int i, x, y, col, maxlen;
  481. bool o = state->orientable;
  482. /* Pedantic check: ensure buf is large enough to format an int in
  483. * decimal, using the bound log10(2) < 1/3. (Obviously in practice
  484. * int is not going to be larger than even 32 bits any time soon,
  485. * but.) */
  486. assert(sizeof(buf) >= 1 + sizeof(int) * CHAR_BIT/3);
  487. /*
  488. * First work out how many characters we need to display each
  489. * number. We're pretty flexible on grid contents here, so we
  490. * have to scan the entire grid.
  491. */
  492. col = 0;
  493. for (i = 0; i < state->w * state->h; i++) {
  494. x = sprintf(buf, "%d", state->grid[i] / 4);
  495. if (col < x) col = x;
  496. }
  497. /* Reassure sprintf-checking compilers like gcc that the field
  498. * width we've just computed is not now excessive */
  499. if (col >= sizeof(buf))
  500. col = sizeof(buf)-1;
  501. /*
  502. * Now we know the exact total size of the grid we're going to
  503. * produce: it's got h rows, each containing w lots of col+o,
  504. * w-1 spaces and a trailing newline.
  505. */
  506. maxlen = state->h * state->w * (col+o+1);
  507. ret = snewn(maxlen+1, char);
  508. p = ret;
  509. for (y = 0; y < state->h; y++) {
  510. for (x = 0; x < state->w; x++) {
  511. int v = state->grid[state->w*y+x];
  512. sprintf(buf, "%*d", col, v/4);
  513. memcpy(p, buf, col);
  514. p += col;
  515. if (o)
  516. *p++ = "^<v>"[v & 3];
  517. if (x+1 == state->w)
  518. *p++ = '\n';
  519. else
  520. *p++ = ' ';
  521. }
  522. }
  523. assert(p - ret == maxlen);
  524. *p = '\0';
  525. return ret;
  526. }
  527. struct game_ui {
  528. int cur_x, cur_y;
  529. bool cur_visible;
  530. };
  531. static game_ui *new_ui(const game_state *state)
  532. {
  533. game_ui *ui = snew(game_ui);
  534. ui->cur_x = 0;
  535. ui->cur_y = 0;
  536. ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
  537. return ui;
  538. }
  539. static void free_ui(game_ui *ui)
  540. {
  541. sfree(ui);
  542. }
  543. static void game_changed_state(game_ui *ui, const game_state *oldstate,
  544. const game_state *newstate)
  545. {
  546. }
  547. static const char *current_key_label(const game_ui *ui,
  548. const game_state *state, int button)
  549. {
  550. if (!ui->cur_visible) return "";
  551. switch (button) {
  552. case CURSOR_SELECT: return "Turn left";
  553. case CURSOR_SELECT2: return "Turn right";
  554. }
  555. return "";
  556. }
  557. struct game_drawstate {
  558. bool started;
  559. int w, h, bgcolour;
  560. int *grid;
  561. int tilesize;
  562. int cur_x, cur_y;
  563. };
  564. static char *interpret_move(const game_state *state, game_ui *ui,
  565. const game_drawstate *ds,
  566. int x, int y, int button)
  567. {
  568. int w = state->w, h = state->h, n = state->n /* , wh = w*h */;
  569. char buf[80];
  570. int dir;
  571. button = button & (~MOD_MASK | MOD_NUM_KEYPAD);
  572. if (IS_CURSOR_MOVE(button)) {
  573. if (button == CURSOR_LEFT && ui->cur_x > 0)
  574. ui->cur_x--;
  575. if (button == CURSOR_RIGHT && (ui->cur_x+n) < (w))
  576. ui->cur_x++;
  577. if (button == CURSOR_UP && ui->cur_y > 0)
  578. ui->cur_y--;
  579. if (button == CURSOR_DOWN && (ui->cur_y+n) < (h))
  580. ui->cur_y++;
  581. ui->cur_visible = true;
  582. return MOVE_UI_UPDATE;
  583. }
  584. if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
  585. /*
  586. * Determine the coordinates of the click. We offset by n-1
  587. * half-blocks so that the user must click at the centre of
  588. * a rotation region rather than at the corner.
  589. */
  590. x -= (n-1) * TILE_SIZE / 2;
  591. y -= (n-1) * TILE_SIZE / 2;
  592. x = FROMCOORD(x);
  593. y = FROMCOORD(y);
  594. dir = (button == LEFT_BUTTON ? 1 : -1);
  595. if (x < 0 || x > w-n || y < 0 || y > h-n)
  596. return NULL;
  597. ui->cur_visible = false;
  598. } else if (IS_CURSOR_SELECT(button)) {
  599. if (ui->cur_visible) {
  600. x = ui->cur_x;
  601. y = ui->cur_y;
  602. dir = (button == CURSOR_SELECT2) ? -1 : +1;
  603. } else {
  604. ui->cur_visible = true;
  605. return MOVE_UI_UPDATE;
  606. }
  607. } else if (button == 'a' || button == 'A' || button==MOD_NUM_KEYPAD+'7') {
  608. x = y = 0;
  609. dir = (button == 'A' ? -1 : +1);
  610. } else if (button == 'b' || button == 'B' || button==MOD_NUM_KEYPAD+'9') {
  611. x = w-n;
  612. y = 0;
  613. dir = (button == 'B' ? -1 : +1);
  614. } else if (button == 'c' || button == 'C' || button==MOD_NUM_KEYPAD+'1') {
  615. x = 0;
  616. y = h-n;
  617. dir = (button == 'C' ? -1 : +1);
  618. } else if (button == 'd' || button == 'D' || button==MOD_NUM_KEYPAD+'3') {
  619. x = w-n;
  620. y = h-n;
  621. dir = (button == 'D' ? -1 : +1);
  622. } else if (button==MOD_NUM_KEYPAD+'8' && (w-n) % 2 == 0) {
  623. x = (w-n) / 2;
  624. y = 0;
  625. dir = +1;
  626. } else if (button==MOD_NUM_KEYPAD+'2' && (w-n) % 2 == 0) {
  627. x = (w-n) / 2;
  628. y = h-n;
  629. dir = +1;
  630. } else if (button==MOD_NUM_KEYPAD+'4' && (h-n) % 2 == 0) {
  631. x = 0;
  632. y = (h-n) / 2;
  633. dir = +1;
  634. } else if (button==MOD_NUM_KEYPAD+'6' && (h-n) % 2 == 0) {
  635. x = w-n;
  636. y = (h-n) / 2;
  637. dir = +1;
  638. } else if (button==MOD_NUM_KEYPAD+'5' && (w-n) % 2 == 0 && (h-n) % 2 == 0){
  639. x = (w-n) / 2;
  640. y = (h-n) / 2;
  641. dir = +1;
  642. } else {
  643. return NULL; /* no move to be made */
  644. }
  645. /*
  646. * If we reach here, we have a valid move.
  647. */
  648. sprintf(buf, "M%d,%d,%d", x, y, dir);
  649. return dupstr(buf);
  650. }
  651. static game_state *execute_move(const game_state *from, const char *move)
  652. {
  653. game_state *ret;
  654. int w = from->w, h = from->h, n = from->n, wh = w*h;
  655. int x, y, dir;
  656. if (!strcmp(move, "S")) {
  657. int i;
  658. ret = dup_game(from);
  659. /*
  660. * Simply replace the grid with a solved one. For this game,
  661. * this isn't a useful operation for actually telling the user
  662. * what they should have done, but it is useful for
  663. * conveniently being able to get hold of a clean state from
  664. * which to practise manoeuvres.
  665. */
  666. qsort(ret->grid, ret->w*ret->h, sizeof(int), compare_int);
  667. for (i = 0; i < ret->w*ret->h; i++)
  668. ret->grid[i] &= ~3;
  669. ret->used_solve = true;
  670. ret->completed = ret->movecount = 1;
  671. return ret;
  672. }
  673. if (move[0] != 'M' ||
  674. sscanf(move+1, "%d,%d,%d", &x, &y, &dir) != 3 ||
  675. x < 0 || y < 0 || x > from->w - n || y > from->h - n)
  676. return NULL; /* can't parse this move string */
  677. ret = dup_game(from);
  678. ret->movecount++;
  679. do_rotate(ret->grid, w, h, n, ret->orientable, x, y, dir);
  680. ret->lastx = x;
  681. ret->lasty = y;
  682. ret->lastr = dir;
  683. /*
  684. * See if the game has been completed. To do this we simply
  685. * test that the grid contents are in increasing order.
  686. */
  687. if (!ret->completed && grid_complete(ret->grid, wh, ret->orientable))
  688. ret->completed = ret->movecount;
  689. return ret;
  690. }
  691. /* ----------------------------------------------------------------------
  692. * Drawing routines.
  693. */
  694. static void game_compute_size(const game_params *params, int tilesize,
  695. const game_ui *ui, int *x, int *y)
  696. {
  697. /* Ick: fake up `ds->tilesize' for macro expansion purposes */
  698. struct { int tilesize; } ads, *ds = &ads;
  699. ads.tilesize = tilesize;
  700. *x = TILE_SIZE * params->w + 2 * BORDER;
  701. *y = TILE_SIZE * params->h + 2 * BORDER;
  702. }
  703. static void game_set_size(drawing *dr, game_drawstate *ds,
  704. const game_params *params, int tilesize)
  705. {
  706. ds->tilesize = tilesize;
  707. }
  708. static float *game_colours(frontend *fe, int *ncolours)
  709. {
  710. float *ret = snewn(3 * NCOLOURS, float);
  711. int i;
  712. game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
  713. /* cursor is light-background with a red tinge. */
  714. ret[COL_HIGHCURSOR * 3 + 0] = ret[COL_BACKGROUND * 3 + 0] * 1.0F;
  715. ret[COL_HIGHCURSOR * 3 + 1] = ret[COL_BACKGROUND * 3 + 1] * 0.5F;
  716. ret[COL_HIGHCURSOR * 3 + 2] = ret[COL_BACKGROUND * 3 + 2] * 0.5F;
  717. for (i = 0; i < 3; i++) {
  718. ret[COL_HIGHLIGHT_GENTLE * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 1.1F;
  719. ret[COL_LOWLIGHT_GENTLE * 3 + i] = ret[COL_BACKGROUND * 3 + i] * 0.9F;
  720. ret[COL_TEXT * 3 + i] = 0.0;
  721. ret[COL_LOWCURSOR * 3 + i] = ret[COL_HIGHCURSOR * 3 + i] * 0.6F;
  722. }
  723. *ncolours = NCOLOURS;
  724. return ret;
  725. }
  726. static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
  727. {
  728. struct game_drawstate *ds = snew(struct game_drawstate);
  729. int i;
  730. ds->started = false;
  731. ds->w = state->w;
  732. ds->h = state->h;
  733. ds->bgcolour = COL_BACKGROUND;
  734. ds->grid = snewn(ds->w*ds->h, int);
  735. ds->tilesize = 0; /* haven't decided yet */
  736. for (i = 0; i < ds->w*ds->h; i++)
  737. ds->grid[i] = -1;
  738. ds->cur_x = ds->cur_y = -state->n;
  739. return ds;
  740. }
  741. static void game_free_drawstate(drawing *dr, game_drawstate *ds)
  742. {
  743. sfree(ds->grid);
  744. sfree(ds);
  745. }
  746. struct rotation {
  747. int cx, cy, cw, ch; /* clip region */
  748. int ox, oy; /* rotation origin */
  749. float c, s; /* cos and sin of rotation angle */
  750. int lc, rc, tc, bc; /* colours of tile edges */
  751. };
  752. static void rotate(int *xy, struct rotation *rot)
  753. {
  754. if (rot) {
  755. float xf = (float)xy[0] - rot->ox, yf = (float)xy[1] - rot->oy;
  756. float xf2, yf2;
  757. xf2 = rot->c * xf + rot->s * yf;
  758. yf2 = - rot->s * xf + rot->c * yf;
  759. xy[0] = (int)(xf2 + rot->ox + 0.5F); /* round to nearest */
  760. xy[1] = (int)(yf2 + rot->oy + 0.5F); /* round to nearest */
  761. }
  762. }
  763. #define CUR_TOP 1
  764. #define CUR_RIGHT 2
  765. #define CUR_BOTTOM 4
  766. #define CUR_LEFT 8
  767. static void draw_tile(drawing *dr, game_drawstate *ds, const game_state *state,
  768. int x, int y, int tile, int flash_colour,
  769. struct rotation *rot, unsigned cedges)
  770. {
  771. int coords[8];
  772. char str[40];
  773. /*
  774. * If we've been passed a rotation region but we're drawing a
  775. * tile which is outside it, we must draw it normally. This can
  776. * occur if we're cleaning up after a completion flash while a
  777. * new move is also being made.
  778. */
  779. if (rot && (x < rot->cx || y < rot->cy ||
  780. x >= rot->cx+rot->cw || y >= rot->cy+rot->ch))
  781. rot = NULL;
  782. if (rot)
  783. clip(dr, rot->cx, rot->cy, rot->cw, rot->ch);
  784. /*
  785. * We must draw each side of the tile's highlight separately,
  786. * because in some cases (during rotation) they will all need
  787. * to be different colours.
  788. */
  789. /* The centre point is common to all sides. */
  790. coords[4] = x + TILE_SIZE / 2;
  791. coords[5] = y + TILE_SIZE / 2;
  792. rotate(coords+4, rot);
  793. /* Right side. */
  794. coords[0] = x + TILE_SIZE - 1;
  795. coords[1] = y + TILE_SIZE - 1;
  796. rotate(coords+0, rot);
  797. coords[2] = x + TILE_SIZE - 1;
  798. coords[3] = y;
  799. rotate(coords+2, rot);
  800. draw_polygon(dr, coords, 3, rot ? rot->rc : COL_LOWLIGHT,
  801. rot ? rot->rc : (cedges & CUR_RIGHT) ? COL_LOWCURSOR : COL_LOWLIGHT);
  802. /* Bottom side. */
  803. coords[2] = x;
  804. coords[3] = y + TILE_SIZE - 1;
  805. rotate(coords+2, rot);
  806. draw_polygon(dr, coords, 3, rot ? rot->bc : COL_LOWLIGHT,
  807. rot ? rot->bc : (cedges & CUR_BOTTOM) ? COL_LOWCURSOR : COL_LOWLIGHT);
  808. /* Left side. */
  809. coords[0] = x;
  810. coords[1] = y;
  811. rotate(coords+0, rot);
  812. draw_polygon(dr, coords, 3, rot ? rot->lc : COL_HIGHLIGHT,
  813. rot ? rot->lc : (cedges & CUR_LEFT) ? COL_HIGHCURSOR : COL_HIGHLIGHT);
  814. /* Top side. */
  815. coords[2] = x + TILE_SIZE - 1;
  816. coords[3] = y;
  817. rotate(coords+2, rot);
  818. draw_polygon(dr, coords, 3, rot ? rot->tc : COL_HIGHLIGHT,
  819. rot ? rot->tc : (cedges & CUR_TOP) ? COL_HIGHCURSOR : COL_HIGHLIGHT);
  820. /*
  821. * Now the main blank area in the centre of the tile.
  822. */
  823. if (rot) {
  824. coords[0] = x + HIGHLIGHT_WIDTH;
  825. coords[1] = y + HIGHLIGHT_WIDTH;
  826. rotate(coords+0, rot);
  827. coords[2] = x + HIGHLIGHT_WIDTH;
  828. coords[3] = y + TILE_SIZE - 1 - HIGHLIGHT_WIDTH;
  829. rotate(coords+2, rot);
  830. coords[4] = x + TILE_SIZE - 1 - HIGHLIGHT_WIDTH;
  831. coords[5] = y + TILE_SIZE - 1 - HIGHLIGHT_WIDTH;
  832. rotate(coords+4, rot);
  833. coords[6] = x + TILE_SIZE - 1 - HIGHLIGHT_WIDTH;
  834. coords[7] = y + HIGHLIGHT_WIDTH;
  835. rotate(coords+6, rot);
  836. draw_polygon(dr, coords, 4, flash_colour, flash_colour);
  837. } else {
  838. draw_rect(dr, x + HIGHLIGHT_WIDTH, y + HIGHLIGHT_WIDTH,
  839. TILE_SIZE - 2*HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH,
  840. flash_colour);
  841. }
  842. /*
  843. * Next, the triangles for orientation.
  844. */
  845. if (state->orientable) {
  846. int xdx, xdy, ydx, ydy;
  847. int cx, cy, displ, displ2;
  848. switch (tile & 3) {
  849. case 0:
  850. xdx = 1, xdy = 0;
  851. ydx = 0, ydy = 1;
  852. break;
  853. case 1:
  854. xdx = 0, xdy = -1;
  855. ydx = 1, ydy = 0;
  856. break;
  857. case 2:
  858. xdx = -1, xdy = 0;
  859. ydx = 0, ydy = -1;
  860. break;
  861. default /* case 3 */:
  862. xdx = 0, xdy = 1;
  863. ydx = -1, ydy = 0;
  864. break;
  865. }
  866. cx = x + TILE_SIZE / 2;
  867. cy = y + TILE_SIZE / 2;
  868. displ = TILE_SIZE / 2 - HIGHLIGHT_WIDTH - 2;
  869. displ2 = TILE_SIZE / 3 - HIGHLIGHT_WIDTH;
  870. coords[0] = cx - displ * xdx + displ2 * ydx;
  871. coords[1] = cy - displ * xdy + displ2 * ydy;
  872. rotate(coords+0, rot);
  873. coords[2] = cx + displ * xdx + displ2 * ydx;
  874. coords[3] = cy + displ * xdy + displ2 * ydy;
  875. rotate(coords+2, rot);
  876. coords[4] = cx - displ * ydx;
  877. coords[5] = cy - displ * ydy;
  878. rotate(coords+4, rot);
  879. draw_polygon(dr, coords, 3, COL_LOWLIGHT_GENTLE, COL_LOWLIGHT_GENTLE);
  880. }
  881. coords[0] = x + TILE_SIZE/2;
  882. coords[1] = y + TILE_SIZE/2;
  883. rotate(coords+0, rot);
  884. sprintf(str, "%d", tile / 4);
  885. draw_text(dr, coords[0], coords[1],
  886. FONT_VARIABLE, TILE_SIZE/3, ALIGN_VCENTRE | ALIGN_HCENTRE,
  887. COL_TEXT, str);
  888. if (rot)
  889. unclip(dr);
  890. draw_update(dr, x, y, TILE_SIZE, TILE_SIZE);
  891. }
  892. static int highlight_colour(float angle)
  893. {
  894. int colours[32] = {
  895. COL_LOWLIGHT,
  896. COL_LOWLIGHT_GENTLE,
  897. COL_LOWLIGHT_GENTLE,
  898. COL_LOWLIGHT_GENTLE,
  899. COL_HIGHLIGHT_GENTLE,
  900. COL_HIGHLIGHT_GENTLE,
  901. COL_HIGHLIGHT_GENTLE,
  902. COL_HIGHLIGHT,
  903. COL_HIGHLIGHT,
  904. COL_HIGHLIGHT,
  905. COL_HIGHLIGHT,
  906. COL_HIGHLIGHT,
  907. COL_HIGHLIGHT,
  908. COL_HIGHLIGHT,
  909. COL_HIGHLIGHT,
  910. COL_HIGHLIGHT,
  911. COL_HIGHLIGHT,
  912. COL_HIGHLIGHT_GENTLE,
  913. COL_HIGHLIGHT_GENTLE,
  914. COL_HIGHLIGHT_GENTLE,
  915. COL_LOWLIGHT_GENTLE,
  916. COL_LOWLIGHT_GENTLE,
  917. COL_LOWLIGHT_GENTLE,
  918. COL_LOWLIGHT,
  919. COL_LOWLIGHT,
  920. COL_LOWLIGHT,
  921. COL_LOWLIGHT,
  922. COL_LOWLIGHT,
  923. COL_LOWLIGHT,
  924. COL_LOWLIGHT,
  925. COL_LOWLIGHT,
  926. COL_LOWLIGHT,
  927. };
  928. return colours[(int)((angle + 2*(float)PI) / ((float)PI/16)) & 31];
  929. }
  930. static float game_anim_length_real(const game_state *oldstate,
  931. const game_state *newstate, int dir,
  932. const game_ui *ui)
  933. {
  934. /*
  935. * Our game_anim_length doesn't need to modify its game_ui, so
  936. * this is the real function which declares ui as const. We must
  937. * wrap this for the backend structure with a version that has ui
  938. * non-const, but we still need this version to call from within
  939. * game_redraw which only has a const ui available.
  940. */
  941. return (float)(ANIM_PER_BLKSIZE_UNIT * sqrt(newstate->n-1));
  942. }
  943. static float game_anim_length(const game_state *oldstate,
  944. const game_state *newstate, int dir, game_ui *ui)
  945. {
  946. return game_anim_length_real(oldstate, newstate, dir, ui);
  947. }
  948. static float game_flash_length(const game_state *oldstate,
  949. const game_state *newstate, int dir, game_ui *ui)
  950. {
  951. if (!oldstate->completed && newstate->completed &&
  952. !oldstate->used_solve && !newstate->used_solve)
  953. return 2 * FLASH_FRAME;
  954. else
  955. return 0.0F;
  956. }
  957. static void game_get_cursor_location(const game_ui *ui,
  958. const game_drawstate *ds,
  959. const game_state *state,
  960. const game_params *params,
  961. int *x, int *y, int *w, int *h)
  962. {
  963. if(ui->cur_visible) {
  964. *x = COORD(ui->cur_x);
  965. *y = COORD(ui->cur_y);
  966. *w = *h = state->n * TILE_SIZE;
  967. }
  968. }
  969. static int game_status(const game_state *state)
  970. {
  971. return state->completed ? +1 : 0;
  972. }
  973. static void game_redraw(drawing *dr, game_drawstate *ds,
  974. const game_state *oldstate, const game_state *state,
  975. int dir, const game_ui *ui,
  976. float animtime, float flashtime)
  977. {
  978. int i, bgcolour;
  979. struct rotation srot, *rot;
  980. int lastx = -1, lasty = -1, lastr = -1;
  981. int cx, cy, n = state->n;
  982. bool cmoved = false;
  983. cx = ui->cur_visible ? ui->cur_x : -state->n;
  984. cy = ui->cur_visible ? ui->cur_y : -state->n;
  985. if (cx != ds->cur_x || cy != ds->cur_y)
  986. cmoved = true;
  987. if (flashtime > 0) {
  988. int frame = (int)(flashtime / FLASH_FRAME);
  989. bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT);
  990. } else
  991. bgcolour = COL_BACKGROUND;
  992. if (!ds->started) {
  993. int coords[10];
  994. /*
  995. * Recessed area containing the whole puzzle.
  996. */
  997. coords[0] = COORD(state->w) + HIGHLIGHT_WIDTH - 1;
  998. coords[1] = COORD(state->h) + HIGHLIGHT_WIDTH - 1;
  999. coords[2] = COORD(state->w) + HIGHLIGHT_WIDTH - 1;
  1000. coords[3] = COORD(0) - HIGHLIGHT_WIDTH;
  1001. coords[4] = coords[2] - TILE_SIZE;
  1002. coords[5] = coords[3] + TILE_SIZE;
  1003. coords[8] = COORD(0) - HIGHLIGHT_WIDTH;
  1004. coords[9] = COORD(state->h) + HIGHLIGHT_WIDTH - 1;
  1005. coords[6] = coords[8] + TILE_SIZE;
  1006. coords[7] = coords[9] - TILE_SIZE;
  1007. draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT);
  1008. coords[1] = COORD(0) - HIGHLIGHT_WIDTH;
  1009. coords[0] = COORD(0) - HIGHLIGHT_WIDTH;
  1010. draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT);
  1011. ds->started = true;
  1012. }
  1013. /*
  1014. * If we're drawing any rotated tiles, sort out the rotation
  1015. * parameters, and also zap the rotation region to the
  1016. * background colour before doing anything else.
  1017. */
  1018. if (oldstate) {
  1019. float angle;
  1020. float anim_max = game_anim_length_real(oldstate, state, dir, ui);
  1021. if (dir > 0) {
  1022. lastx = state->lastx;
  1023. lasty = state->lasty;
  1024. lastr = state->lastr;
  1025. } else {
  1026. lastx = oldstate->lastx;
  1027. lasty = oldstate->lasty;
  1028. lastr = -oldstate->lastr;
  1029. }
  1030. rot = &srot;
  1031. rot->cx = COORD(lastx);
  1032. rot->cy = COORD(lasty);
  1033. rot->cw = rot->ch = TILE_SIZE * state->n;
  1034. rot->ox = rot->cx + rot->cw/2;
  1035. rot->oy = rot->cy + rot->ch/2;
  1036. angle = ((-(float)PI/2 * lastr) * (1.0F - animtime / anim_max));
  1037. rot->c = (float)cos(angle);
  1038. rot->s = (float)sin(angle);
  1039. /*
  1040. * Sort out the colours of the various sides of the tile.
  1041. */
  1042. rot->lc = highlight_colour((float)PI + angle);
  1043. rot->rc = highlight_colour(angle);
  1044. rot->tc = highlight_colour((float)(PI/2.0) + angle);
  1045. rot->bc = highlight_colour((float)(-PI/2.0) + angle);
  1046. draw_rect(dr, rot->cx, rot->cy, rot->cw, rot->ch, bgcolour);
  1047. } else
  1048. rot = NULL;
  1049. /*
  1050. * Now draw each tile.
  1051. */
  1052. for (i = 0; i < state->w * state->h; i++) {
  1053. int t;
  1054. bool cc = false;
  1055. int tx = i % state->w, ty = i / state->w;
  1056. /*
  1057. * Figure out what should be displayed at this location.
  1058. * Usually it will be state->grid[i], unless we're in the
  1059. * middle of animating an actual rotation and this cell is
  1060. * within the rotation region, in which case we set -1
  1061. * (always display).
  1062. */
  1063. if (oldstate && lastx >= 0 && lasty >= 0 &&
  1064. tx >= lastx && tx < lastx + state->n &&
  1065. ty >= lasty && ty < lasty + state->n)
  1066. t = -1;
  1067. else
  1068. t = state->grid[i];
  1069. if (cmoved) {
  1070. /* cursor has moved (or changed visibility)... */
  1071. if (tx == cx || tx == cx+n-1 || ty == cy || ty == cy+n-1)
  1072. cc = true; /* ...we're on new cursor, redraw */
  1073. if (tx == ds->cur_x || tx == ds->cur_x+n-1 ||
  1074. ty == ds->cur_y || ty == ds->cur_y+n-1)
  1075. cc = true; /* ...we were on old cursor, redraw */
  1076. }
  1077. if (ds->bgcolour != bgcolour || /* always redraw when flashing */
  1078. ds->grid[i] != t || ds->grid[i] == -1 || t == -1 || cc) {
  1079. int x = COORD(tx), y = COORD(ty);
  1080. unsigned cedges = 0;
  1081. if (tx == cx && ty >= cy && ty <= cy+n-1) cedges |= CUR_LEFT;
  1082. if (ty == cy && tx >= cx && tx <= cx+n-1) cedges |= CUR_TOP;
  1083. if (tx == cx+n-1 && ty >= cy && ty <= cy+n-1) cedges |= CUR_RIGHT;
  1084. if (ty == cy+n-1 && tx >= cx && tx <= cx+n-1) cedges |= CUR_BOTTOM;
  1085. draw_tile(dr, ds, state, x, y, state->grid[i], bgcolour, rot, cedges);
  1086. ds->grid[i] = t;
  1087. }
  1088. }
  1089. ds->bgcolour = bgcolour;
  1090. ds->cur_x = cx; ds->cur_y = cy;
  1091. /*
  1092. * Update the status bar.
  1093. */
  1094. {
  1095. char statusbuf[256];
  1096. /*
  1097. * Don't show the new status until we're also showing the
  1098. * new _state_ - after the game animation is complete.
  1099. */
  1100. if (oldstate)
  1101. state = oldstate;
  1102. if (state->used_solve)
  1103. sprintf(statusbuf, "Moves since auto-solve: %d",
  1104. state->movecount - state->completed);
  1105. else {
  1106. sprintf(statusbuf, "%sMoves: %d",
  1107. (state->completed ? "COMPLETED! " : ""),
  1108. (state->completed ? state->completed : state->movecount));
  1109. if (state->movetarget)
  1110. sprintf(statusbuf+strlen(statusbuf), " (target %d)",
  1111. state->movetarget);
  1112. }
  1113. status_bar(dr, statusbuf);
  1114. }
  1115. }
  1116. #ifdef COMBINED
  1117. #define thegame twiddle
  1118. #endif
  1119. const struct game thegame = {
  1120. "Twiddle", "games.twiddle", "twiddle",
  1121. default_params,
  1122. game_fetch_preset, NULL,
  1123. decode_params,
  1124. encode_params,
  1125. free_params,
  1126. dup_params,
  1127. true, game_configure, custom_params,
  1128. validate_params,
  1129. new_game_desc,
  1130. validate_desc,
  1131. new_game,
  1132. dup_game,
  1133. free_game,
  1134. true, solve_game,
  1135. true, game_can_format_as_text_now, game_text_format,
  1136. NULL, NULL, /* get_prefs, set_prefs */
  1137. new_ui,
  1138. free_ui,
  1139. NULL, /* encode_ui */
  1140. NULL, /* decode_ui */
  1141. NULL, /* game_request_keys */
  1142. game_changed_state,
  1143. current_key_label,
  1144. interpret_move,
  1145. execute_move,
  1146. PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
  1147. game_colours,
  1148. game_new_drawstate,
  1149. game_free_drawstate,
  1150. game_redraw,
  1151. game_anim_length,
  1152. game_flash_length,
  1153. game_get_cursor_location,
  1154. game_status,
  1155. false, false, NULL, NULL, /* print_size, print */
  1156. true, /* wants_statusbar */
  1157. false, NULL, /* timing_state */
  1158. 0, /* flags */
  1159. };
  1160. /* vim: set shiftwidth=4 tabstop=8: */