sixteen.c 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220
  1. /*
  2. * sixteen.c: `16-puzzle', a sliding-tiles jigsaw which differs
  3. * from the 15-puzzle in that you toroidally rotate a row or column
  4. * at a time.
  5. */
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <assert.h>
  10. #include <ctype.h>
  11. #include <limits.h>
  12. #ifdef NO_TGMATH_H
  13. # include <math.h>
  14. #else
  15. # include <tgmath.h>
  16. #endif
  17. #include "puzzles.h"
  18. #define PREFERRED_TILE_SIZE 48
  19. #define TILE_SIZE (ds->tilesize)
  20. #define BORDER TILE_SIZE
  21. #define HIGHLIGHT_WIDTH (TILE_SIZE / 20)
  22. #define COORD(x) ( (x) * TILE_SIZE + BORDER )
  23. #define FROMCOORD(x) ( ((x) - BORDER + 2*TILE_SIZE) / TILE_SIZE - 2 )
  24. #define ANIM_TIME 0.13F
  25. #define FLASH_FRAME 0.13F
  26. #define X(state, i) ( (i) % (state)->w )
  27. #define Y(state, i) ( (i) / (state)->w )
  28. #define C(state, x, y) ( (y) * (state)->w + (x) )
  29. #define TILE_CURSOR(i, state, x, y) ((i) == C((state), (x), (y)) && \
  30. 0 <= (x) && (x) < (state)->w && \
  31. 0 <= (y) && (y) < (state)->h)
  32. enum {
  33. COL_BACKGROUND,
  34. COL_TEXT,
  35. COL_HIGHLIGHT,
  36. COL_LOWLIGHT,
  37. NCOLOURS
  38. };
  39. struct game_params {
  40. int w, h;
  41. int movetarget;
  42. };
  43. struct game_state {
  44. int w, h, n;
  45. int *tiles;
  46. int completed;
  47. bool used_solve; /* used to suppress completion flash */
  48. int movecount, movetarget;
  49. int last_movement_sense;
  50. };
  51. static game_params *default_params(void)
  52. {
  53. game_params *ret = snew(game_params);
  54. ret->w = ret->h = 4;
  55. ret->movetarget = 0;
  56. return ret;
  57. }
  58. static bool game_fetch_preset(int i, char **name, game_params **params)
  59. {
  60. game_params *ret;
  61. int w, h;
  62. char buf[80];
  63. switch (i) {
  64. case 0: w = 3, h = 3; break;
  65. case 1: w = 4, h = 3; break;
  66. case 2: w = 4, h = 4; break;
  67. case 3: w = 5, h = 4; break;
  68. case 4: w = 5, h = 5; break;
  69. default: return false;
  70. }
  71. sprintf(buf, "%dx%d", w, h);
  72. *name = dupstr(buf);
  73. *params = ret = snew(game_params);
  74. ret->w = w;
  75. ret->h = h;
  76. ret->movetarget = 0;
  77. return true;
  78. }
  79. static void free_params(game_params *params)
  80. {
  81. sfree(params);
  82. }
  83. static game_params *dup_params(const game_params *params)
  84. {
  85. game_params *ret = snew(game_params);
  86. *ret = *params; /* structure copy */
  87. return ret;
  88. }
  89. static void decode_params(game_params *ret, char const *string)
  90. {
  91. ret->w = ret->h = atoi(string);
  92. ret->movetarget = 0;
  93. while (*string && isdigit((unsigned char)*string)) string++;
  94. if (*string == 'x') {
  95. string++;
  96. ret->h = atoi(string);
  97. while (*string && isdigit((unsigned char)*string))
  98. string++;
  99. }
  100. if (*string == 'm') {
  101. string++;
  102. ret->movetarget = atoi(string);
  103. while (*string && isdigit((unsigned char)*string))
  104. string++;
  105. }
  106. }
  107. static char *encode_params(const game_params *params, bool full)
  108. {
  109. char data[256];
  110. sprintf(data, "%dx%d", params->w, params->h);
  111. /* Shuffle limit is part of the limited parameters, because we have to
  112. * supply the target move count. */
  113. if (params->movetarget)
  114. sprintf(data + strlen(data), "m%d", params->movetarget);
  115. return dupstr(data);
  116. }
  117. static config_item *game_configure(const game_params *params)
  118. {
  119. config_item *ret;
  120. char buf[80];
  121. ret = snewn(4, config_item);
  122. ret[0].name = "Width";
  123. ret[0].type = C_STRING;
  124. sprintf(buf, "%d", params->w);
  125. ret[0].u.string.sval = dupstr(buf);
  126. ret[1].name = "Height";
  127. ret[1].type = C_STRING;
  128. sprintf(buf, "%d", params->h);
  129. ret[1].u.string.sval = dupstr(buf);
  130. ret[2].name = "Number of shuffling moves";
  131. ret[2].type = C_STRING;
  132. sprintf(buf, "%d", params->movetarget);
  133. ret[2].u.string.sval = dupstr(buf);
  134. ret[3].name = NULL;
  135. ret[3].type = C_END;
  136. return ret;
  137. }
  138. static game_params *custom_params(const config_item *cfg)
  139. {
  140. game_params *ret = snew(game_params);
  141. ret->w = atoi(cfg[0].u.string.sval);
  142. ret->h = atoi(cfg[1].u.string.sval);
  143. ret->movetarget = atoi(cfg[2].u.string.sval);
  144. return ret;
  145. }
  146. static const char *validate_params(const game_params *params, bool full)
  147. {
  148. if (params->w < 2 || params->h < 2)
  149. return "Width and height must both be at least two";
  150. if (params->w > INT_MAX / params->h)
  151. return "Width times height must not be unreasonably large";
  152. return NULL;
  153. }
  154. static int perm_parity(int *perm, int n)
  155. {
  156. int i, j, ret;
  157. ret = 0;
  158. for (i = 0; i < n-1; i++)
  159. for (j = i+1; j < n; j++)
  160. if (perm[i] > perm[j])
  161. ret = !ret;
  162. return ret;
  163. }
  164. static char *new_game_desc(const game_params *params, random_state *rs,
  165. char **aux, bool interactive)
  166. {
  167. int stop, n, i, x;
  168. int x1, x2, p1, p2;
  169. int *tiles;
  170. bool *used;
  171. char *ret;
  172. int retlen;
  173. n = params->w * params->h;
  174. tiles = snewn(n, int);
  175. if (params->movetarget) {
  176. int prevoffset = -1;
  177. int max = (params->w > params->h ? params->w : params->h);
  178. int *prevmoves = snewn(max, int);
  179. /*
  180. * Shuffle the old-fashioned way, by making a series of
  181. * single moves on the grid.
  182. */
  183. for (i = 0; i < n; i++)
  184. tiles[i] = i;
  185. for (i = 0; i < params->movetarget; i++) {
  186. int start, offset, len, direction, index;
  187. int j, tmp;
  188. /*
  189. * Choose a move to make. We can choose from any row
  190. * or any column.
  191. */
  192. while (1) {
  193. j = random_upto(rs, params->w + params->h);
  194. if (j < params->w) {
  195. /* Column. */
  196. index = j;
  197. start = j;
  198. offset = params->w;
  199. len = params->h;
  200. } else {
  201. /* Row. */
  202. index = j - params->w;
  203. start = index * params->w;
  204. offset = 1;
  205. len = params->w;
  206. }
  207. direction = -1 + 2 * random_upto(rs, 2);
  208. /*
  209. * To at least _try_ to avoid boring cases, check
  210. * that this move doesn't directly undo a previous
  211. * one, or repeat it so many times as to turn it
  212. * into fewer moves in the opposite direction. (For
  213. * example, in a row of length 4, we're allowed to
  214. * move it the same way twice, but not three
  215. * times.)
  216. *
  217. * We track this for each individual row/column,
  218. * and clear all the counters as soon as a
  219. * perpendicular move is made. This isn't perfect
  220. * (it _can't_ guaranteeably be perfect - there
  221. * will always come a move count beyond which a
  222. * shorter solution will be possible than the one
  223. * which constructed the position) but it should
  224. * sort out all the obvious cases.
  225. */
  226. if (offset == prevoffset) {
  227. tmp = prevmoves[index] + direction;
  228. if (abs(2*tmp) > len || abs(tmp) < abs(prevmoves[index]))
  229. continue;
  230. }
  231. /* If we didn't `continue', we've found an OK move to make. */
  232. if (offset != prevoffset) {
  233. int i;
  234. for (i = 0; i < max; i++)
  235. prevmoves[i] = 0;
  236. prevoffset = offset;
  237. }
  238. prevmoves[index] += direction;
  239. break;
  240. }
  241. /*
  242. * Make the move.
  243. */
  244. if (direction < 0) {
  245. start += (len-1) * offset;
  246. offset = -offset;
  247. }
  248. tmp = tiles[start];
  249. for (j = 0; j+1 < len; j++)
  250. tiles[start + j*offset] = tiles[start + (j+1)*offset];
  251. tiles[start + (len-1) * offset] = tmp;
  252. }
  253. sfree(prevmoves);
  254. } else {
  255. used = snewn(n, bool);
  256. for (i = 0; i < n; i++) {
  257. tiles[i] = -1;
  258. used[i] = false;
  259. }
  260. /*
  261. * If both dimensions are odd, there is a parity
  262. * constraint.
  263. */
  264. if (params->w & params->h & 1)
  265. stop = 2;
  266. else
  267. stop = 0;
  268. /*
  269. * Place everything except (possibly) the last two tiles.
  270. */
  271. for (x = 0, i = n; i > stop; i--) {
  272. int k = i > 1 ? random_upto(rs, i) : 0;
  273. int j;
  274. for (j = 0; j < n; j++)
  275. if (!used[j] && (k-- == 0))
  276. break;
  277. assert(j < n && !used[j]);
  278. used[j] = true;
  279. while (tiles[x] >= 0)
  280. x++;
  281. assert(x < n);
  282. tiles[x] = j;
  283. }
  284. if (stop) {
  285. /*
  286. * Find the last two locations, and the last two
  287. * pieces.
  288. */
  289. while (tiles[x] >= 0)
  290. x++;
  291. assert(x < n);
  292. x1 = x;
  293. x++;
  294. while (tiles[x] >= 0)
  295. x++;
  296. assert(x < n);
  297. x2 = x;
  298. for (i = 0; i < n; i++)
  299. if (!used[i])
  300. break;
  301. p1 = i;
  302. for (i = p1+1; i < n; i++)
  303. if (!used[i])
  304. break;
  305. p2 = i;
  306. /*
  307. * Try the last two tiles one way round. If that fails,
  308. * swap them.
  309. */
  310. tiles[x1] = p1;
  311. tiles[x2] = p2;
  312. if (perm_parity(tiles, n) != 0) {
  313. tiles[x1] = p2;
  314. tiles[x2] = p1;
  315. assert(perm_parity(tiles, n) == 0);
  316. }
  317. }
  318. sfree(used);
  319. }
  320. /*
  321. * Now construct the game description, by describing the tile
  322. * array as a simple sequence of comma-separated integers.
  323. */
  324. ret = NULL;
  325. retlen = 0;
  326. for (i = 0; i < n; i++) {
  327. char buf[80];
  328. int k;
  329. k = sprintf(buf, "%d,", tiles[i]+1);
  330. ret = sresize(ret, retlen + k + 1, char);
  331. strcpy(ret + retlen, buf);
  332. retlen += k;
  333. }
  334. ret[retlen-1] = '\0'; /* delete last comma */
  335. sfree(tiles);
  336. return ret;
  337. }
  338. static const char *validate_desc(const game_params *params, const char *desc)
  339. {
  340. const char *p, *err;
  341. int i, area;
  342. bool *used;
  343. area = params->w * params->h;
  344. p = desc;
  345. err = NULL;
  346. used = snewn(area, bool);
  347. for (i = 0; i < area; i++)
  348. used[i] = false;
  349. for (i = 0; i < area; i++) {
  350. const char *q = p;
  351. int n;
  352. if (*p < '0' || *p > '9') {
  353. err = "Not enough numbers in string";
  354. goto leave;
  355. }
  356. while (*p >= '0' && *p <= '9')
  357. p++;
  358. if (i < area-1 && *p != ',') {
  359. err = "Expected comma after number";
  360. goto leave;
  361. }
  362. else if (i == area-1 && *p) {
  363. err = "Excess junk at end of string";
  364. goto leave;
  365. }
  366. n = atoi(q);
  367. if (n < 1 || n > area) {
  368. err = "Number out of range";
  369. goto leave;
  370. }
  371. if (used[n-1]) {
  372. err = "Number used twice";
  373. goto leave;
  374. }
  375. used[n-1] = true;
  376. if (*p) p++; /* eat comma */
  377. }
  378. leave:
  379. sfree(used);
  380. return err;
  381. }
  382. static game_state *new_game(midend *me, const game_params *params,
  383. const char *desc)
  384. {
  385. game_state *state = snew(game_state);
  386. int i;
  387. const char *p;
  388. state->w = params->w;
  389. state->h = params->h;
  390. state->n = params->w * params->h;
  391. state->tiles = snewn(state->n, int);
  392. p = desc;
  393. i = 0;
  394. for (i = 0; i < state->n; i++) {
  395. assert(*p);
  396. state->tiles[i] = atoi(p);
  397. while (*p && *p != ',')
  398. p++;
  399. if (*p) p++; /* eat comma */
  400. }
  401. assert(!*p);
  402. state->completed = state->movecount = 0;
  403. state->movetarget = params->movetarget;
  404. state->used_solve = false;
  405. state->last_movement_sense = 0;
  406. return state;
  407. }
  408. static game_state *dup_game(const game_state *state)
  409. {
  410. game_state *ret = snew(game_state);
  411. ret->w = state->w;
  412. ret->h = state->h;
  413. ret->n = state->n;
  414. ret->tiles = snewn(state->w * state->h, int);
  415. memcpy(ret->tiles, state->tiles, state->w * state->h * sizeof(int));
  416. ret->completed = state->completed;
  417. ret->movecount = state->movecount;
  418. ret->movetarget = state->movetarget;
  419. ret->used_solve = state->used_solve;
  420. ret->last_movement_sense = state->last_movement_sense;
  421. return ret;
  422. }
  423. static void free_game(game_state *state)
  424. {
  425. sfree(state->tiles);
  426. sfree(state);
  427. }
  428. static char *solve_game(const game_state *state, const game_state *currstate,
  429. const char *aux, const char **error)
  430. {
  431. return dupstr("S");
  432. }
  433. static bool game_can_format_as_text_now(const game_params *params)
  434. {
  435. return true;
  436. }
  437. static char *game_text_format(const game_state *state)
  438. {
  439. char *ret, *p, buf[80];
  440. int x, y, col, maxlen;
  441. /*
  442. * First work out how many characters we need to display each
  443. * number.
  444. */
  445. col = sprintf(buf, "%d", state->n);
  446. /*
  447. * Now we know the exact total size of the grid we're going to
  448. * produce: it's got h rows, each containing w lots of col, w-1
  449. * spaces and a trailing newline.
  450. */
  451. maxlen = state->h * state->w * (col+1);
  452. ret = snewn(maxlen+1, char);
  453. p = ret;
  454. for (y = 0; y < state->h; y++) {
  455. for (x = 0; x < state->w; x++) {
  456. int v = state->tiles[state->w*y+x];
  457. sprintf(buf, "%*d", col, v);
  458. memcpy(p, buf, col);
  459. p += col;
  460. if (x+1 == state->w)
  461. *p++ = '\n';
  462. else
  463. *p++ = ' ';
  464. }
  465. }
  466. assert(p - ret == maxlen);
  467. *p = '\0';
  468. return ret;
  469. }
  470. enum cursor_mode { unlocked, lock_tile, lock_position };
  471. struct game_ui {
  472. int cur_x, cur_y;
  473. bool cur_visible;
  474. enum cursor_mode cur_mode;
  475. };
  476. static game_ui *new_ui(const game_state *state)
  477. {
  478. game_ui *ui = snew(game_ui);
  479. ui->cur_x = 0;
  480. ui->cur_y = 0;
  481. ui->cur_visible = getenv_bool("PUZZLES_SHOW_CURSOR", false);
  482. ui->cur_mode = unlocked;
  483. return ui;
  484. }
  485. static void free_ui(game_ui *ui)
  486. {
  487. sfree(ui);
  488. }
  489. static void game_changed_state(game_ui *ui, const game_state *oldstate,
  490. const game_state *newstate)
  491. {
  492. }
  493. static const char *current_key_label(const game_ui *ui,
  494. const game_state *state, int button)
  495. {
  496. if (IS_CURSOR_SELECT(button) && ui->cur_visible) {
  497. if (ui->cur_x == -1 || ui->cur_x == state->w ||
  498. ui->cur_y == -1 || ui->cur_y == state->h)
  499. return button == CURSOR_SELECT2 ? "Back" : "Slide";
  500. if (button == CURSOR_SELECT)
  501. return ui->cur_mode == lock_tile ? "Unlock" : "Lock tile";
  502. if (button == CURSOR_SELECT2)
  503. return ui->cur_mode == lock_position ? "Unlock" : "Lock pos";
  504. }
  505. return "";
  506. }
  507. struct game_drawstate {
  508. bool started;
  509. int w, h, bgcolour;
  510. int *tiles;
  511. int tilesize;
  512. int cur_x, cur_y;
  513. };
  514. static char *interpret_move(const game_state *state, game_ui *ui,
  515. const game_drawstate *ds,
  516. int x, int y, int button)
  517. {
  518. int cx = -1, cy = -1, dx, dy;
  519. char buf[80];
  520. bool shift = button & MOD_SHFT, control = button & MOD_CTRL;
  521. int pad = button & MOD_NUM_KEYPAD;
  522. button &= ~MOD_MASK;
  523. if (IS_CURSOR_MOVE(button) || pad) {
  524. if (!ui->cur_visible) {
  525. ui->cur_visible = true;
  526. return MOVE_UI_UPDATE;
  527. }
  528. if (control || shift || ui->cur_mode) {
  529. int x = ui->cur_x, y = ui->cur_y, xwrap = x, ywrap = y;
  530. if (x < 0 || x >= state->w || y < 0 || y >= state->h)
  531. return NULL;
  532. move_cursor(button | pad, &x, &y,
  533. state->w, state->h, false);
  534. move_cursor(button | pad, &xwrap, &ywrap,
  535. state->w, state->h, true);
  536. if (x != xwrap) {
  537. sprintf(buf, "R%d,%c1", y, x ? '+' : '-');
  538. } else if (y != ywrap) {
  539. sprintf(buf, "C%d,%c1", x, y ? '+' : '-');
  540. } else if (x == ui->cur_x)
  541. sprintf(buf, "C%d,%d", x, y - ui->cur_y);
  542. else
  543. sprintf(buf, "R%d,%d", y, x - ui->cur_x);
  544. if (control || (!shift && ui->cur_mode == lock_tile)) {
  545. ui->cur_x = xwrap;
  546. ui->cur_y = ywrap;
  547. }
  548. return dupstr(buf);
  549. } else {
  550. int x = ui->cur_x + 1, y = ui->cur_y + 1;
  551. move_cursor(button | pad, &x, &y,
  552. state->w + 2, state->h + 2, false);
  553. if (x == 0 && y == 0) {
  554. int t = ui->cur_x;
  555. ui->cur_x = ui->cur_y;
  556. ui->cur_y = t;
  557. } else if (x == 0 && y == state->h + 1) {
  558. int t = ui->cur_x;
  559. ui->cur_x = (state->h - 1) - ui->cur_y;
  560. ui->cur_y = (state->h - 1) - t;
  561. } else if (x == state->w + 1 && y == 0) {
  562. int t = ui->cur_x;
  563. ui->cur_x = (state->w - 1) - ui->cur_y;
  564. ui->cur_y = (state->w - 1) - t;
  565. } else if (x == state->w + 1 && y == state->h + 1) {
  566. int t = ui->cur_x;
  567. ui->cur_x = state->w - state->h + ui->cur_y;
  568. ui->cur_y = state->h - state->w + t;
  569. } else {
  570. ui->cur_x = x - 1;
  571. ui->cur_y = y - 1;
  572. }
  573. ui->cur_visible = true;
  574. return MOVE_UI_UPDATE;
  575. }
  576. }
  577. if (button == LEFT_BUTTON || button == RIGHT_BUTTON) {
  578. cx = FROMCOORD(x);
  579. cy = FROMCOORD(y);
  580. ui->cur_visible = false;
  581. } else if (IS_CURSOR_SELECT(button)) {
  582. if (ui->cur_visible) {
  583. if (ui->cur_x == -1 || ui->cur_x == state->w ||
  584. ui->cur_y == -1 || ui->cur_y == state->h) {
  585. cx = ui->cur_x;
  586. cy = ui->cur_y;
  587. } else {
  588. const enum cursor_mode m = (button == CURSOR_SELECT2 ?
  589. lock_position : lock_tile);
  590. ui->cur_mode = (ui->cur_mode == m ? unlocked : m);
  591. return MOVE_UI_UPDATE;
  592. }
  593. } else {
  594. ui->cur_visible = true;
  595. return MOVE_UI_UPDATE;
  596. }
  597. } else {
  598. return NULL;
  599. }
  600. if (cx == -1 && cy >= 0 && cy < state->h)
  601. dx = -1, dy = 0;
  602. else if (cx == state->w && cy >= 0 && cy < state->h)
  603. dx = +1, dy = 0;
  604. else if (cy == -1 && cx >= 0 && cx < state->w)
  605. dy = -1, dx = 0;
  606. else if (cy == state->h && cx >= 0 && cx < state->w)
  607. dy = +1, dx = 0;
  608. else
  609. return MOVE_UI_UPDATE; /* invalid click location */
  610. /* reverse direction if right hand button is pressed */
  611. if (button == RIGHT_BUTTON || button == CURSOR_SELECT2) {
  612. dx = -dx;
  613. dy = -dy;
  614. }
  615. if (dx)
  616. sprintf(buf, "R%d,%d", cy, dx);
  617. else
  618. sprintf(buf, "C%d,%d", cx, dy);
  619. return dupstr(buf);
  620. }
  621. static game_state *execute_move(const game_state *from, const char *move)
  622. {
  623. int cx, cy, dx, dy;
  624. int tx, ty, n;
  625. game_state *ret;
  626. if (!strcmp(move, "S")) {
  627. int i;
  628. ret = dup_game(from);
  629. /*
  630. * Simply replace the grid with a solved one. For this game,
  631. * this isn't a useful operation for actually telling the user
  632. * what they should have done, but it is useful for
  633. * conveniently being able to get hold of a clean state from
  634. * which to practise manoeuvres.
  635. */
  636. for (i = 0; i < ret->n; i++)
  637. ret->tiles[i] = i+1;
  638. ret->used_solve = true;
  639. ret->completed = ret->movecount = 1;
  640. return ret;
  641. }
  642. if (move[0] == 'R' && sscanf(move+1, "%d,%d", &cy, &dx) == 2 &&
  643. cy >= 0 && cy < from->h && -from->h <= dx && dx <= from->w ) {
  644. cx = dy = 0;
  645. n = from->w;
  646. } else if (move[0] == 'C' && sscanf(move+1, "%d,%d", &cx, &dy) == 2 &&
  647. cx >= 0 && cx < from->w && -from->h <= dy && dy <= from->h) {
  648. cy = dx = 0;
  649. n = from->h;
  650. } else
  651. return NULL;
  652. ret = dup_game(from);
  653. do {
  654. tx = (cx - dx + from->w) % from->w;
  655. ty = (cy - dy + from->h) % from->h;
  656. ret->tiles[C(ret, cx, cy)] = from->tiles[C(from, tx, ty)];
  657. cx = tx;
  658. cy = ty;
  659. } while (--n > 0);
  660. ret->movecount++;
  661. ret->last_movement_sense = dx+dy;
  662. /*
  663. * See if the game has been completed.
  664. */
  665. if (!ret->completed) {
  666. ret->completed = ret->movecount;
  667. for (n = 0; n < ret->n; n++)
  668. if (ret->tiles[n] != n+1)
  669. ret->completed = 0;
  670. }
  671. return ret;
  672. }
  673. /* ----------------------------------------------------------------------
  674. * Drawing routines.
  675. */
  676. static void game_compute_size(const game_params *params, int tilesize,
  677. const game_ui *ui, int *x, int *y)
  678. {
  679. /* Ick: fake up `ds->tilesize' for macro expansion purposes */
  680. struct { int tilesize; } ads, *ds = &ads;
  681. ads.tilesize = tilesize;
  682. *x = TILE_SIZE * params->w + 2 * BORDER;
  683. *y = TILE_SIZE * params->h + 2 * BORDER;
  684. }
  685. static void game_set_size(drawing *dr, game_drawstate *ds,
  686. const game_params *params, int tilesize)
  687. {
  688. ds->tilesize = tilesize;
  689. }
  690. static float *game_colours(frontend *fe, int *ncolours)
  691. {
  692. float *ret = snewn(3 * NCOLOURS, float);
  693. int i;
  694. game_mkhighlight(fe, ret, COL_BACKGROUND, COL_HIGHLIGHT, COL_LOWLIGHT);
  695. for (i = 0; i < 3; i++)
  696. ret[COL_TEXT * 3 + i] = 0.0;
  697. *ncolours = NCOLOURS;
  698. return ret;
  699. }
  700. static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
  701. {
  702. struct game_drawstate *ds = snew(struct game_drawstate);
  703. int i;
  704. ds->started = false;
  705. ds->w = state->w;
  706. ds->h = state->h;
  707. ds->bgcolour = COL_BACKGROUND;
  708. ds->tiles = snewn(ds->w*ds->h, int);
  709. ds->tilesize = 0; /* haven't decided yet */
  710. for (i = 0; i < ds->w*ds->h; i++)
  711. ds->tiles[i] = -1;
  712. ds->cur_x = ds->cur_y = -1;
  713. return ds;
  714. }
  715. static void game_free_drawstate(drawing *dr, game_drawstate *ds)
  716. {
  717. sfree(ds->tiles);
  718. sfree(ds);
  719. }
  720. static void draw_tile(drawing *dr, game_drawstate *ds,
  721. const game_state *state, int x, int y,
  722. int tile, int flash_colour)
  723. {
  724. if (tile == 0) {
  725. draw_rect(dr, x, y, TILE_SIZE, TILE_SIZE,
  726. flash_colour);
  727. } else {
  728. int coords[6];
  729. char str[40];
  730. coords[0] = x + TILE_SIZE - 1;
  731. coords[1] = y + TILE_SIZE - 1;
  732. coords[2] = x + TILE_SIZE - 1;
  733. coords[3] = y;
  734. coords[4] = x;
  735. coords[5] = y + TILE_SIZE - 1;
  736. draw_polygon(dr, coords, 3, COL_LOWLIGHT, COL_LOWLIGHT);
  737. coords[0] = x;
  738. coords[1] = y;
  739. draw_polygon(dr, coords, 3, COL_HIGHLIGHT, COL_HIGHLIGHT);
  740. draw_rect(dr, x + HIGHLIGHT_WIDTH, y + HIGHLIGHT_WIDTH,
  741. TILE_SIZE - 2*HIGHLIGHT_WIDTH, TILE_SIZE - 2*HIGHLIGHT_WIDTH,
  742. flash_colour);
  743. sprintf(str, "%d", tile);
  744. draw_text(dr, x + TILE_SIZE/2, y + TILE_SIZE/2,
  745. FONT_VARIABLE, TILE_SIZE/3, ALIGN_VCENTRE | ALIGN_HCENTRE,
  746. COL_TEXT, str);
  747. }
  748. draw_update(dr, x, y, TILE_SIZE, TILE_SIZE);
  749. }
  750. static void draw_arrow(drawing *dr, game_drawstate *ds,
  751. int x, int y, int xdx, int xdy, bool cur)
  752. {
  753. int coords[14];
  754. int ydy = -xdx, ydx = xdy;
  755. #define POINT(n, xx, yy) ( \
  756. coords[2*(n)+0] = x + (xx)*xdx + (yy)*ydx, \
  757. coords[2*(n)+1] = y + (xx)*xdy + (yy)*ydy)
  758. POINT(0, TILE_SIZE / 2, 3 * TILE_SIZE / 4); /* top of arrow */
  759. POINT(1, 3 * TILE_SIZE / 4, TILE_SIZE / 2); /* right corner */
  760. POINT(2, 5 * TILE_SIZE / 8, TILE_SIZE / 2); /* right concave */
  761. POINT(3, 5 * TILE_SIZE / 8, TILE_SIZE / 4); /* bottom right */
  762. POINT(4, 3 * TILE_SIZE / 8, TILE_SIZE / 4); /* bottom left */
  763. POINT(5, 3 * TILE_SIZE / 8, TILE_SIZE / 2); /* left concave */
  764. POINT(6, TILE_SIZE / 4, TILE_SIZE / 2); /* left corner */
  765. draw_polygon(dr, coords, 7, cur ? COL_HIGHLIGHT : COL_LOWLIGHT, COL_TEXT);
  766. }
  767. static void draw_arrow_for_cursor(drawing *dr, game_drawstate *ds,
  768. int cur_x, int cur_y, bool cur)
  769. {
  770. if (cur_x == -1 && cur_y == -1)
  771. return; /* 'no cursur here */
  772. else if (cur_x == -1) /* LH column. */
  773. draw_arrow(dr, ds, COORD(0), COORD(cur_y+1), 0, -1, cur);
  774. else if (cur_x == ds->w) /* RH column */
  775. draw_arrow(dr, ds, COORD(ds->w), COORD(cur_y), 0, +1, cur);
  776. else if (cur_y == -1) /* Top row */
  777. draw_arrow(dr, ds, COORD(cur_x), COORD(0), +1, 0, cur);
  778. else if (cur_y == ds->h) /* Bottom row */
  779. draw_arrow(dr, ds, COORD(cur_x+1), COORD(ds->h), -1, 0, cur);
  780. else
  781. return;
  782. draw_update(dr, COORD(cur_x), COORD(cur_y),
  783. TILE_SIZE, TILE_SIZE);
  784. }
  785. static void game_redraw(drawing *dr, game_drawstate *ds,
  786. const game_state *oldstate, const game_state *state,
  787. int dir, const game_ui *ui,
  788. float animtime, float flashtime)
  789. {
  790. int i, bgcolour;
  791. int cur_x = -1, cur_y = -1;
  792. if (flashtime > 0) {
  793. int frame = (int)(flashtime / FLASH_FRAME);
  794. bgcolour = (frame % 2 ? COL_LOWLIGHT : COL_HIGHLIGHT);
  795. } else
  796. bgcolour = COL_BACKGROUND;
  797. if (!ds->started) {
  798. int coords[10];
  799. /*
  800. * Recessed area containing the whole puzzle.
  801. */
  802. coords[0] = COORD(state->w) + HIGHLIGHT_WIDTH - 1;
  803. coords[1] = COORD(state->h) + HIGHLIGHT_WIDTH - 1;
  804. coords[2] = COORD(state->w) + HIGHLIGHT_WIDTH - 1;
  805. coords[3] = COORD(0) - HIGHLIGHT_WIDTH;
  806. coords[4] = coords[2] - TILE_SIZE;
  807. coords[5] = coords[3] + TILE_SIZE;
  808. coords[8] = COORD(0) - HIGHLIGHT_WIDTH;
  809. coords[9] = COORD(state->h) + HIGHLIGHT_WIDTH - 1;
  810. coords[6] = coords[8] + TILE_SIZE;
  811. coords[7] = coords[9] - TILE_SIZE;
  812. draw_polygon(dr, coords, 5, COL_HIGHLIGHT, COL_HIGHLIGHT);
  813. coords[1] = COORD(0) - HIGHLIGHT_WIDTH;
  814. coords[0] = COORD(0) - HIGHLIGHT_WIDTH;
  815. draw_polygon(dr, coords, 5, COL_LOWLIGHT, COL_LOWLIGHT);
  816. /*
  817. * Arrows for making moves.
  818. */
  819. for (i = 0; i < state->w; i++) {
  820. draw_arrow(dr, ds, COORD(i), COORD(0), +1, 0, false);
  821. draw_arrow(dr, ds, COORD(i+1), COORD(state->h), -1, 0, false);
  822. }
  823. for (i = 0; i < state->h; i++) {
  824. draw_arrow(dr, ds, COORD(state->w), COORD(i), 0, +1, false);
  825. draw_arrow(dr, ds, COORD(0), COORD(i+1), 0, -1, false);
  826. }
  827. ds->started = true;
  828. }
  829. /*
  830. * Cursor (highlighted arrow around edge)
  831. */
  832. if (ui->cur_visible) {
  833. cur_x = ui->cur_x; cur_y = ui->cur_y;
  834. }
  835. if (cur_x != ds->cur_x || cur_y != ds->cur_y) {
  836. /* Cursor has changed; redraw two (prev and curr) arrows. */
  837. draw_arrow_for_cursor(dr, ds, cur_x, cur_y, true);
  838. draw_arrow_for_cursor(dr, ds, ds->cur_x, ds->cur_y, false);
  839. }
  840. /*
  841. * Now draw each tile.
  842. */
  843. clip(dr, COORD(0), COORD(0), TILE_SIZE*state->w, TILE_SIZE*state->h);
  844. for (i = 0; i < state->n; i++) {
  845. int t, t0;
  846. /*
  847. * Figure out what should be displayed at this
  848. * location. It's either a simple tile, or it's a
  849. * transition between two tiles (in which case we say
  850. * -1 because it must always be drawn).
  851. */
  852. if (oldstate && oldstate->tiles[i] != state->tiles[i])
  853. t = -1;
  854. else
  855. t = state->tiles[i];
  856. t0 = t;
  857. if (ds->bgcolour != bgcolour || /* always redraw when flashing */
  858. ds->tiles[i] != t || ds->tiles[i] == -1 || t == -1 ||
  859. ((ds->cur_x != cur_x || ds->cur_y != cur_y) && /* cursor moved */
  860. (TILE_CURSOR(i, state, ds->cur_x, ds->cur_y) ||
  861. TILE_CURSOR(i, state, cur_x, cur_y)))) {
  862. int x, y, x2, y2;
  863. /*
  864. * Figure out what to _actually_ draw, and where to
  865. * draw it.
  866. */
  867. if (t == -1) {
  868. int x0, y0, x1, y1, dx, dy;
  869. int j;
  870. float c;
  871. int sense;
  872. if (dir < 0) {
  873. assert(oldstate);
  874. sense = -oldstate->last_movement_sense;
  875. } else {
  876. sense = state->last_movement_sense;
  877. }
  878. t = state->tiles[i];
  879. /*
  880. * FIXME: must be prepared to draw a double
  881. * tile in some situations.
  882. */
  883. /*
  884. * Find the coordinates of this tile in the old and
  885. * new states.
  886. */
  887. x1 = COORD(X(state, i));
  888. y1 = COORD(Y(state, i));
  889. for (j = 0; j < oldstate->n; j++)
  890. if (oldstate->tiles[j] == state->tiles[i])
  891. break;
  892. assert(j < oldstate->n);
  893. x0 = COORD(X(state, j));
  894. y0 = COORD(Y(state, j));
  895. dx = (x1 - x0);
  896. if (dx != 0 &&
  897. dx != TILE_SIZE * sense) {
  898. dx = (dx < 0 ? dx + TILE_SIZE * state->w :
  899. dx - TILE_SIZE * state->w);
  900. assert(abs(dx) == TILE_SIZE);
  901. }
  902. dy = (y1 - y0);
  903. if (dy != 0 &&
  904. dy != TILE_SIZE * sense) {
  905. dy = (dy < 0 ? dy + TILE_SIZE * state->h :
  906. dy - TILE_SIZE * state->h);
  907. assert(abs(dy) == TILE_SIZE);
  908. }
  909. c = (animtime / ANIM_TIME);
  910. if (c < 0.0F) c = 0.0F;
  911. if (c > 1.0F) c = 1.0F;
  912. x = x0 + (int)(c * dx);
  913. y = y0 + (int)(c * dy);
  914. x2 = x1 - dx + (int)(c * dx);
  915. y2 = y1 - dy + (int)(c * dy);
  916. } else {
  917. x = COORD(X(state, i));
  918. y = COORD(Y(state, i));
  919. x2 = y2 = -1;
  920. }
  921. draw_tile(dr, ds, state, x, y, t,
  922. (x2 == -1 && TILE_CURSOR(i, state, cur_x, cur_y)) ?
  923. COL_LOWLIGHT : bgcolour);
  924. if (x2 != -1 || y2 != -1)
  925. draw_tile(dr, ds, state, x2, y2, t, bgcolour);
  926. }
  927. ds->tiles[i] = t0;
  928. }
  929. ds->cur_x = cur_x;
  930. ds->cur_y = cur_y;
  931. unclip(dr);
  932. ds->bgcolour = bgcolour;
  933. /*
  934. * Update the status bar.
  935. */
  936. {
  937. char statusbuf[256];
  938. /*
  939. * Don't show the new status until we're also showing the
  940. * new _state_ - after the game animation is complete.
  941. */
  942. if (oldstate)
  943. state = oldstate;
  944. if (state->used_solve)
  945. sprintf(statusbuf, "Moves since auto-solve: %d",
  946. state->movecount - state->completed);
  947. else {
  948. sprintf(statusbuf, "%sMoves: %d",
  949. (state->completed ? "COMPLETED! " : ""),
  950. (state->completed ? state->completed : state->movecount));
  951. if (state->movetarget)
  952. sprintf(statusbuf+strlen(statusbuf), " (target %d)",
  953. state->movetarget);
  954. }
  955. status_bar(dr, statusbuf);
  956. }
  957. }
  958. static float game_anim_length(const game_state *oldstate,
  959. const game_state *newstate, int dir, game_ui *ui)
  960. {
  961. return ANIM_TIME;
  962. }
  963. static float game_flash_length(const game_state *oldstate,
  964. const game_state *newstate, int dir, game_ui *ui)
  965. {
  966. if (!oldstate->completed && newstate->completed &&
  967. !oldstate->used_solve && !newstate->used_solve)
  968. return 2 * FLASH_FRAME;
  969. else
  970. return 0.0F;
  971. }
  972. static void game_get_cursor_location(const game_ui *ui,
  973. const game_drawstate *ds,
  974. const game_state *state,
  975. const game_params *params,
  976. int *x, int *y, int *w, int *h)
  977. {
  978. if(ui->cur_visible) {
  979. *x = COORD(ui->cur_x);
  980. *y = COORD(ui->cur_y);
  981. *w = *h = TILE_SIZE;
  982. }
  983. }
  984. static int game_status(const game_state *state)
  985. {
  986. return state->completed ? +1 : 0;
  987. }
  988. #ifdef COMBINED
  989. #define thegame sixteen
  990. #endif
  991. const struct game thegame = {
  992. "Sixteen", "games.sixteen", "sixteen",
  993. default_params,
  994. game_fetch_preset, NULL,
  995. decode_params,
  996. encode_params,
  997. free_params,
  998. dup_params,
  999. true, game_configure, custom_params,
  1000. validate_params,
  1001. new_game_desc,
  1002. validate_desc,
  1003. new_game,
  1004. dup_game,
  1005. free_game,
  1006. true, solve_game,
  1007. true, game_can_format_as_text_now, game_text_format,
  1008. NULL, NULL, /* get_prefs, set_prefs */
  1009. new_ui,
  1010. free_ui,
  1011. NULL, /* encode_ui */
  1012. NULL, /* decode_ui */
  1013. NULL, /* game_request_keys */
  1014. game_changed_state,
  1015. current_key_label,
  1016. interpret_move,
  1017. execute_move,
  1018. PREFERRED_TILE_SIZE, game_compute_size, game_set_size,
  1019. game_colours,
  1020. game_new_drawstate,
  1021. game_free_drawstate,
  1022. game_redraw,
  1023. game_anim_length,
  1024. game_flash_length,
  1025. game_get_cursor_location,
  1026. game_status,
  1027. false, false, NULL, NULL, /* print_size, print */
  1028. true, /* wants_statusbar */
  1029. false, NULL, /* timing_state */
  1030. 0, /* flags */
  1031. };
  1032. /* vim: set shiftwidth=4 tabstop=8: */