untangle.c 40 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581
  1. /*
  2. * untangle.c: Game about planar graphs. You are given a graph
  3. * represented by points and straight lines, with some lines
  4. * crossing; your task is to drag the points into a configuration
  5. * where none of the lines cross.
  6. *
  7. * Cloned from a Flash game called `Planarity', by John Tantalo.
  8. * <http://home.cwru.edu/~jnt5/Planarity> at the time of writing
  9. * this. The Flash game had a fixed set of levels; my added value,
  10. * as usual, is automatic generation of random games to order.
  11. */
  12. /*
  13. * TODO:
  14. *
  15. * - This puzzle, perhaps uniquely among the collection, could use
  16. * support for non-aspect-ratio-preserving resizes. This would
  17. * require some sort of fairly large redesign, unfortunately (since
  18. * it would invalidate the basic assumption that puzzles' size
  19. * requirements are adequately expressed by a single scalar tile
  20. * size), and probably complicate the rest of the puzzles' API as a
  21. * result. So I'm not sure I really want to do it.
  22. */
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <assert.h>
  27. #include <ctype.h>
  28. #include <limits.h>
  29. #ifdef NO_TGMATH_H
  30. # include <math.h>
  31. #else
  32. # include <tgmath.h>
  33. #endif
  34. #if HAVE_STDINT_H
  35. # include <stdint.h>
  36. #endif
  37. #include "puzzles.h"
  38. #include "tree234.h"
  39. #define CIRCLE_RADIUS 6
  40. #define DRAG_THRESHOLD (CIRCLE_RADIUS * 2)
  41. #define PREFERRED_TILESIZE 64
  42. #define FLASH_TIME 0.30F
  43. #define ANIM_TIME 0.13F
  44. #define SOLVEANIM_TIME 0.50F
  45. enum {
  46. COL_SYSBACKGROUND,
  47. COL_BACKGROUND,
  48. COL_LINE,
  49. #ifdef SHOW_CROSSINGS
  50. COL_CROSSEDLINE,
  51. #endif
  52. COL_OUTLINE,
  53. COL_POINT,
  54. COL_DRAGPOINT,
  55. COL_NEIGHBOUR,
  56. COL_FLASH1,
  57. COL_FLASH2,
  58. NCOLOURS
  59. };
  60. typedef struct point {
  61. /*
  62. * Points are stored using rational coordinates, with the same
  63. * denominator for both coordinates.
  64. */
  65. long x, y, d;
  66. } point;
  67. typedef struct edge {
  68. /*
  69. * This structure is implicitly associated with a particular
  70. * point set, so all it has to do is to store two point
  71. * indices. It is required to store them in the order (lower,
  72. * higher), i.e. a < b always.
  73. */
  74. int a, b;
  75. } edge;
  76. struct game_params {
  77. int n; /* number of points */
  78. };
  79. struct graph {
  80. int refcount; /* for deallocation */
  81. tree234 *edges; /* stores `edge' structures */
  82. };
  83. struct game_state {
  84. game_params params;
  85. int w, h; /* extent of coordinate system only */
  86. point *pts;
  87. #ifdef SHOW_CROSSINGS
  88. int *crosses; /* mark edges which are crossed */
  89. #endif
  90. struct graph *graph;
  91. bool completed, cheated, just_solved;
  92. };
  93. static int edgecmpC(const void *av, const void *bv)
  94. {
  95. const edge *a = (const edge *)av;
  96. const edge *b = (const edge *)bv;
  97. if (a->a < b->a)
  98. return -1;
  99. else if (a->a > b->a)
  100. return +1;
  101. else if (a->b < b->b)
  102. return -1;
  103. else if (a->b > b->b)
  104. return +1;
  105. return 0;
  106. }
  107. static int edgecmp(void *av, void *bv) { return edgecmpC(av, bv); }
  108. static game_params *default_params(void)
  109. {
  110. game_params *ret = snew(game_params);
  111. ret->n = 10;
  112. return ret;
  113. }
  114. static bool game_fetch_preset(int i, char **name, game_params **params)
  115. {
  116. game_params *ret;
  117. int n;
  118. char buf[80];
  119. switch (i) {
  120. case 0: n = 6; break;
  121. case 1: n = 10; break;
  122. case 2: n = 15; break;
  123. case 3: n = 20; break;
  124. case 4: n = 25; break;
  125. default: return false;
  126. }
  127. sprintf(buf, "%d points", n);
  128. *name = dupstr(buf);
  129. *params = ret = snew(game_params);
  130. ret->n = n;
  131. return true;
  132. }
  133. static void free_params(game_params *params)
  134. {
  135. sfree(params);
  136. }
  137. static game_params *dup_params(const game_params *params)
  138. {
  139. game_params *ret = snew(game_params);
  140. *ret = *params; /* structure copy */
  141. return ret;
  142. }
  143. static void decode_params(game_params *params, char const *string)
  144. {
  145. params->n = atoi(string);
  146. }
  147. static char *encode_params(const game_params *params, bool full)
  148. {
  149. char buf[80];
  150. sprintf(buf, "%d", params->n);
  151. return dupstr(buf);
  152. }
  153. static config_item *game_configure(const game_params *params)
  154. {
  155. config_item *ret;
  156. char buf[80];
  157. ret = snewn(3, config_item);
  158. ret[0].name = "Number of points";
  159. ret[0].type = C_STRING;
  160. sprintf(buf, "%d", params->n);
  161. ret[0].u.string.sval = dupstr(buf);
  162. ret[1].name = NULL;
  163. ret[1].type = C_END;
  164. return ret;
  165. }
  166. static game_params *custom_params(const config_item *cfg)
  167. {
  168. game_params *ret = snew(game_params);
  169. ret->n = atoi(cfg[0].u.string.sval);
  170. return ret;
  171. }
  172. static const char *validate_params(const game_params *params, bool full)
  173. {
  174. if (params->n < 4)
  175. return "Number of points must be at least four";
  176. if (params->n > INT_MAX / 3)
  177. return "Number of points must not be unreasonably large";
  178. return NULL;
  179. }
  180. /* ----------------------------------------------------------------------
  181. * Small number of 64-bit integer arithmetic operations, to prevent
  182. * integer overflow at the very core of cross().
  183. */
  184. #if !HAVE_STDINT_H
  185. /* For prehistoric C implementations, do this the hard way */
  186. typedef struct {
  187. long hi;
  188. unsigned long lo;
  189. } int64;
  190. #define greater64(i,j) ( (i).hi>(j).hi || ((i).hi==(j).hi && (i).lo>(j).lo))
  191. #define sign64(i) ((i).hi < 0 ? -1 : (i).hi==0 && (i).lo==0 ? 0 : +1)
  192. static int64 mulu32to64(unsigned long x, unsigned long y)
  193. {
  194. unsigned long a, b, c, d, t;
  195. int64 ret;
  196. a = (x & 0xFFFF) * (y & 0xFFFF);
  197. b = (x & 0xFFFF) * (y >> 16);
  198. c = (x >> 16) * (y & 0xFFFF);
  199. d = (x >> 16) * (y >> 16);
  200. ret.lo = a;
  201. ret.hi = d + (b >> 16) + (c >> 16);
  202. t = (b & 0xFFFF) << 16;
  203. ret.lo += t;
  204. if (ret.lo < t)
  205. ret.hi++;
  206. t = (c & 0xFFFF) << 16;
  207. ret.lo += t;
  208. if (ret.lo < t)
  209. ret.hi++;
  210. #ifdef DIAGNOSTIC_VIA_LONGLONG
  211. assert(((unsigned long long)ret.hi << 32) + ret.lo ==
  212. (unsigned long long)x * y);
  213. #endif
  214. return ret;
  215. }
  216. static int64 mul32to64(long x, long y)
  217. {
  218. int sign = +1;
  219. int64 ret;
  220. #ifdef DIAGNOSTIC_VIA_LONGLONG
  221. long long realret = (long long)x * y;
  222. #endif
  223. if (x < 0)
  224. x = -x, sign = -sign;
  225. if (y < 0)
  226. y = -y, sign = -sign;
  227. ret = mulu32to64(x, y);
  228. if (sign < 0) {
  229. ret.hi = -ret.hi;
  230. ret.lo = -ret.lo;
  231. if (ret.lo)
  232. ret.hi--;
  233. }
  234. #ifdef DIAGNOSTIC_VIA_LONGLONG
  235. assert(((unsigned long long)ret.hi << 32) + ret.lo == realret);
  236. #endif
  237. return ret;
  238. }
  239. static int64 dotprod64(long a, long b, long p, long q)
  240. {
  241. int64 ab, pq;
  242. ab = mul32to64(a, b);
  243. pq = mul32to64(p, q);
  244. ab.hi += pq.hi;
  245. ab.lo += pq.lo;
  246. if (ab.lo < pq.lo)
  247. ab.hi++;
  248. return ab;
  249. }
  250. #else /* HAVE_STDINT_H */
  251. typedef int64_t int64;
  252. #define greater64(i,j) ((i) > (j))
  253. #define sign64(i) ((i) < 0 ? -1 : (i)==0 ? 0 : +1)
  254. #define mulu32to64(x,y) ((int64_t)(unsigned long)(x) * (unsigned long)(y))
  255. #define mul32to64(x,y) ((int64_t)(long)(x) * (long)(y))
  256. static int64 dotprod64(long a, long b, long p, long q)
  257. {
  258. return (int64)a * b + (int64)p * q;
  259. }
  260. #endif /* HAVE_STDINT_H */
  261. /*
  262. * Determine whether the line segments between a1 and a2, and
  263. * between b1 and b2, intersect. We count it as an intersection if
  264. * any of the endpoints lies _on_ the other line.
  265. */
  266. static bool cross(point a1, point a2, point b1, point b2)
  267. {
  268. long b1x, b1y, b2x, b2y, px, py;
  269. int64 d1, d2, d3;
  270. /*
  271. * The condition for crossing is that b1 and b2 are on opposite
  272. * sides of the line a1-a2, and vice versa. We determine this
  273. * by taking the dot product of b1-a1 with a vector
  274. * perpendicular to a2-a1, and similarly with b2-a1, and seeing
  275. * if they have different signs.
  276. */
  277. /*
  278. * Construct the vector b1-a1. We don't have to worry too much
  279. * about the denominator, because we're only going to check the
  280. * sign of this vector; we just need to get the numerator
  281. * right.
  282. */
  283. b1x = b1.x * a1.d - a1.x * b1.d;
  284. b1y = b1.y * a1.d - a1.y * b1.d;
  285. /* Now construct b2-a1, and a vector perpendicular to a2-a1,
  286. * in the same way. */
  287. b2x = b2.x * a1.d - a1.x * b2.d;
  288. b2y = b2.y * a1.d - a1.y * b2.d;
  289. px = a1.y * a2.d - a2.y * a1.d;
  290. py = a2.x * a1.d - a1.x * a2.d;
  291. /* Take the dot products. Here we resort to 64-bit arithmetic. */
  292. d1 = dotprod64(b1x, px, b1y, py);
  293. d2 = dotprod64(b2x, px, b2y, py);
  294. /* If they have the same non-zero sign, the lines do not cross. */
  295. if ((sign64(d1) > 0 && sign64(d2) > 0) ||
  296. (sign64(d1) < 0 && sign64(d2) < 0))
  297. return false;
  298. /*
  299. * If the dot products are both exactly zero, then the two line
  300. * segments are collinear. At this point the intersection
  301. * condition becomes whether or not they overlap within their
  302. * line.
  303. */
  304. if (sign64(d1) == 0 && sign64(d2) == 0) {
  305. /* Construct the vector a2-a1. */
  306. px = a2.x * a1.d - a1.x * a2.d;
  307. py = a2.y * a1.d - a1.y * a2.d;
  308. /* Determine the dot products of b1-a1 and b2-a1 with this. */
  309. d1 = dotprod64(b1x, px, b1y, py);
  310. d2 = dotprod64(b2x, px, b2y, py);
  311. /* If they're both strictly negative, the lines do not cross. */
  312. if (sign64(d1) < 0 && sign64(d2) < 0)
  313. return false;
  314. /* Otherwise, take the dot product of a2-a1 with itself. If
  315. * the other two dot products both exceed this, the lines do
  316. * not cross. */
  317. d3 = dotprod64(px, px, py, py);
  318. if (greater64(d1, d3) && greater64(d2, d3))
  319. return false;
  320. }
  321. /*
  322. * We've eliminated the only important special case, and we
  323. * have determined that b1 and b2 are on opposite sides of the
  324. * line a1-a2. Now do the same thing the other way round and
  325. * we're done.
  326. */
  327. b1x = a1.x * b1.d - b1.x * a1.d;
  328. b1y = a1.y * b1.d - b1.y * a1.d;
  329. b2x = a2.x * b1.d - b1.x * a2.d;
  330. b2y = a2.y * b1.d - b1.y * a2.d;
  331. px = b1.y * b2.d - b2.y * b1.d;
  332. py = b2.x * b1.d - b1.x * b2.d;
  333. d1 = dotprod64(b1x, px, b1y, py);
  334. d2 = dotprod64(b2x, px, b2y, py);
  335. if ((sign64(d1) > 0 && sign64(d2) > 0) ||
  336. (sign64(d1) < 0 && sign64(d2) < 0))
  337. return false;
  338. /*
  339. * The lines must cross.
  340. */
  341. return true;
  342. }
  343. static unsigned long squarert(unsigned long n) {
  344. unsigned long d, a, b, di;
  345. d = n;
  346. a = 0;
  347. b = 1L << 30; /* largest available power of 4 */
  348. do {
  349. a >>= 1;
  350. di = 2*a + b;
  351. if (di <= d) {
  352. d -= di;
  353. a += b;
  354. }
  355. b >>= 2;
  356. } while (b);
  357. return a;
  358. }
  359. /*
  360. * Our solutions are arranged on a square grid big enough that n
  361. * points occupy about 1/POINTDENSITY of the grid.
  362. */
  363. #define POINTDENSITY 3
  364. #define MAXDEGREE 4
  365. #define COORDLIMIT(n) squarert((n) * POINTDENSITY)
  366. static void addedge(tree234 *edges, int a, int b)
  367. {
  368. edge *e = snew(edge);
  369. assert(a != b);
  370. e->a = min(a, b);
  371. e->b = max(a, b);
  372. if (add234(edges, e) != e)
  373. /* Duplicate edge. */
  374. sfree(e);
  375. }
  376. static bool isedge(tree234 *edges, int a, int b)
  377. {
  378. edge e;
  379. assert(a != b);
  380. e.a = min(a, b);
  381. e.b = max(a, b);
  382. return find234(edges, &e, NULL) != NULL;
  383. }
  384. typedef struct vertex {
  385. int param;
  386. int vindex;
  387. } vertex;
  388. static int vertcmpC(const void *av, const void *bv)
  389. {
  390. const vertex *a = (const vertex *)av;
  391. const vertex *b = (const vertex *)bv;
  392. if (a->param < b->param)
  393. return -1;
  394. else if (a->param > b->param)
  395. return +1;
  396. else if (a->vindex < b->vindex)
  397. return -1;
  398. else if (a->vindex > b->vindex)
  399. return +1;
  400. return 0;
  401. }
  402. static int vertcmp(void *av, void *bv) { return vertcmpC(av, bv); }
  403. /*
  404. * Construct point coordinates for n points arranged in a circle,
  405. * within the bounding box (0,0) to (w,w).
  406. */
  407. static void make_circle(point *pts, int n, int w)
  408. {
  409. long d, r, c, i;
  410. /*
  411. * First, decide on a denominator. Although in principle it
  412. * would be nice to set this really high so as to finely
  413. * distinguish all the points on the circle, I'm going to set
  414. * it at a fixed size to prevent integer overflow problems.
  415. */
  416. d = PREFERRED_TILESIZE;
  417. /*
  418. * Leave a little space outside the circle.
  419. */
  420. c = d * w / 2;
  421. r = d * w * 3 / 7;
  422. /*
  423. * Place the points.
  424. */
  425. for (i = 0; i < n; i++) {
  426. double angle = i * 2 * PI / n;
  427. double x = r * sin(angle), y = - r * cos(angle);
  428. pts[i].x = (long)(c + x + 0.5);
  429. pts[i].y = (long)(c + y + 0.5);
  430. pts[i].d = d;
  431. }
  432. }
  433. static char *new_game_desc(const game_params *params, random_state *rs,
  434. char **aux, bool interactive)
  435. {
  436. int n = params->n, i;
  437. long w, h, j, k, m;
  438. point *pts, *pts2;
  439. long *tmp;
  440. tree234 *edges, *vertices;
  441. edge *e, *e2;
  442. vertex *v, *vs, *vlist;
  443. char *ret;
  444. w = h = COORDLIMIT(n);
  445. /*
  446. * Choose n points from this grid.
  447. */
  448. pts = snewn(n, point);
  449. tmp = snewn(w*h, long);
  450. for (i = 0; i < w*h; i++)
  451. tmp[i] = i;
  452. shuffle(tmp, w*h, sizeof(*tmp), rs);
  453. for (i = 0; i < n; i++) {
  454. pts[i].x = tmp[i] % w;
  455. pts[i].y = tmp[i] / w;
  456. pts[i].d = 1;
  457. }
  458. sfree(tmp);
  459. /*
  460. * Now start adding edges between the points.
  461. *
  462. * At all times, we attempt to add an edge to the lowest-degree
  463. * vertex we currently have, and we try the other vertices as
  464. * candidate second endpoints in order of distance from this
  465. * one. We stop as soon as we find an edge which
  466. *
  467. * (a) does not increase any vertex's degree beyond MAXDEGREE
  468. * (b) does not cross any existing edges
  469. * (c) does not intersect any actual point.
  470. */
  471. vs = snewn(n, vertex);
  472. vertices = newtree234(vertcmp);
  473. for (i = 0; i < n; i++) {
  474. v = vs + i;
  475. v->param = 0; /* in this tree, param is the degree */
  476. v->vindex = i;
  477. add234(vertices, v);
  478. }
  479. edges = newtree234(edgecmp);
  480. vlist = snewn(n, vertex);
  481. while (1) {
  482. bool added = false;
  483. for (i = 0; i < n; i++) {
  484. v = index234(vertices, i);
  485. j = v->vindex;
  486. if (v->param >= MAXDEGREE)
  487. break; /* nothing left to add! */
  488. /*
  489. * Sort the other vertices into order of their distance
  490. * from this one. Don't bother looking below i, because
  491. * we've already tried those edges the other way round.
  492. * Also here we rule out target vertices with too high
  493. * a degree, and (of course) ones to which we already
  494. * have an edge.
  495. */
  496. m = 0;
  497. for (k = i+1; k < n; k++) {
  498. vertex *kv = index234(vertices, k);
  499. int ki = kv->vindex;
  500. int dx, dy;
  501. if (kv->param >= MAXDEGREE || isedge(edges, ki, j))
  502. continue;
  503. vlist[m].vindex = ki;
  504. dx = pts[ki].x - pts[j].x;
  505. dy = pts[ki].y - pts[j].y;
  506. vlist[m].param = dx*dx + dy*dy;
  507. m++;
  508. }
  509. qsort(vlist, m, sizeof(*vlist), vertcmpC);
  510. for (k = 0; k < m; k++) {
  511. int p;
  512. int ki = vlist[k].vindex;
  513. /*
  514. * Check to see whether this edge intersects any
  515. * existing edge or point.
  516. */
  517. for (p = 0; p < n; p++)
  518. if (p != ki && p != j && cross(pts[ki], pts[j],
  519. pts[p], pts[p]))
  520. break;
  521. if (p < n)
  522. continue;
  523. for (p = 0; (e = index234(edges, p)) != NULL; p++)
  524. if (e->a != ki && e->a != j &&
  525. e->b != ki && e->b != j &&
  526. cross(pts[ki], pts[j], pts[e->a], pts[e->b]))
  527. break;
  528. if (e)
  529. continue;
  530. /*
  531. * We're done! Add this edge, modify the degrees of
  532. * the two vertices involved, and break.
  533. */
  534. addedge(edges, j, ki);
  535. added = true;
  536. del234(vertices, vs+j);
  537. vs[j].param++;
  538. add234(vertices, vs+j);
  539. del234(vertices, vs+ki);
  540. vs[ki].param++;
  541. add234(vertices, vs+ki);
  542. break;
  543. }
  544. if (k < m)
  545. break;
  546. }
  547. if (!added)
  548. break; /* we're done. */
  549. }
  550. /*
  551. * That's our graph. Now shuffle the points, making sure that
  552. * they come out with at least one crossed line when arranged
  553. * in a circle (so that the puzzle isn't immediately solved!).
  554. */
  555. tmp = snewn(n, long);
  556. for (i = 0; i < n; i++)
  557. tmp[i] = i;
  558. pts2 = snewn(n, point);
  559. make_circle(pts2, n, w);
  560. while (1) {
  561. shuffle(tmp, n, sizeof(*tmp), rs);
  562. for (i = 0; (e = index234(edges, i)) != NULL; i++) {
  563. for (j = i+1; (e2 = index234(edges, j)) != NULL; j++) {
  564. if (e2->a == e->a || e2->a == e->b ||
  565. e2->b == e->a || e2->b == e->b)
  566. continue;
  567. if (cross(pts2[tmp[e2->a]], pts2[tmp[e2->b]],
  568. pts2[tmp[e->a]], pts2[tmp[e->b]]))
  569. break;
  570. }
  571. if (e2)
  572. break;
  573. }
  574. if (e)
  575. break; /* we've found a crossing */
  576. }
  577. /*
  578. * We're done. Now encode the graph in a string format. Let's
  579. * use a comma-separated list of dash-separated vertex number
  580. * pairs, numbered from zero. We'll sort the list to prevent
  581. * side channels.
  582. */
  583. ret = NULL;
  584. {
  585. const char *sep;
  586. char buf[80];
  587. int retlen;
  588. edge *ea;
  589. retlen = 0;
  590. m = count234(edges);
  591. ea = snewn(m, edge);
  592. for (i = 0; (e = index234(edges, i)) != NULL; i++) {
  593. assert(i < m);
  594. ea[i].a = min(tmp[e->a], tmp[e->b]);
  595. ea[i].b = max(tmp[e->a], tmp[e->b]);
  596. retlen += 1 + sprintf(buf, "%d-%d", ea[i].a, ea[i].b);
  597. }
  598. assert(i == m);
  599. qsort(ea, m, sizeof(*ea), edgecmpC);
  600. ret = snewn(retlen, char);
  601. sep = "";
  602. k = 0;
  603. for (i = 0; i < m; i++) {
  604. k += sprintf(ret + k, "%s%d-%d", sep, ea[i].a, ea[i].b);
  605. sep = ",";
  606. }
  607. assert(k < retlen);
  608. sfree(ea);
  609. }
  610. /*
  611. * Encode the solution we started with as an aux_info string.
  612. */
  613. {
  614. char buf[80];
  615. char *auxstr;
  616. int auxlen;
  617. auxlen = 2; /* leading 'S' and trailing '\0' */
  618. for (i = 0; i < n; i++) {
  619. j = tmp[i];
  620. pts2[j] = pts[i];
  621. if (pts2[j].d & 1) {
  622. pts2[j].x *= 2;
  623. pts2[j].y *= 2;
  624. pts2[j].d *= 2;
  625. }
  626. pts2[j].x += pts2[j].d / 2;
  627. pts2[j].y += pts2[j].d / 2;
  628. auxlen += sprintf(buf, ";P%d:%ld,%ld/%ld", i,
  629. pts2[j].x, pts2[j].y, pts2[j].d);
  630. }
  631. k = 0;
  632. auxstr = snewn(auxlen, char);
  633. auxstr[k++] = 'S';
  634. for (i = 0; i < n; i++)
  635. k += sprintf(auxstr+k, ";P%d:%ld,%ld/%ld", i,
  636. pts2[i].x, pts2[i].y, pts2[i].d);
  637. assert(k < auxlen);
  638. *aux = auxstr;
  639. }
  640. sfree(pts2);
  641. sfree(tmp);
  642. sfree(vlist);
  643. freetree234(vertices);
  644. sfree(vs);
  645. while ((e = delpos234(edges, 0)) != NULL)
  646. sfree(e);
  647. freetree234(edges);
  648. sfree(pts);
  649. return ret;
  650. }
  651. static const char *validate_desc(const game_params *params, const char *desc)
  652. {
  653. int a, b;
  654. while (*desc) {
  655. a = atoi(desc);
  656. if (a < 0 || a >= params->n)
  657. return "Number out of range in game description";
  658. while (*desc && isdigit((unsigned char)*desc)) desc++;
  659. if (*desc != '-')
  660. return "Expected '-' after number in game description";
  661. desc++; /* eat dash */
  662. b = atoi(desc);
  663. if (b < 0 || b >= params->n)
  664. return "Number out of range in game description";
  665. while (*desc && isdigit((unsigned char)*desc)) desc++;
  666. if (*desc) {
  667. if (*desc != ',')
  668. return "Expected ',' after number in game description";
  669. desc++; /* eat comma */
  670. }
  671. if (a == b)
  672. return "Node linked to itself in game description";
  673. }
  674. return NULL;
  675. }
  676. static void mark_crossings(game_state *state)
  677. {
  678. bool ok = true;
  679. int i, j;
  680. edge *e, *e2;
  681. #ifdef SHOW_CROSSINGS
  682. for (i = 0; (e = index234(state->graph->edges, i)) != NULL; i++)
  683. state->crosses[i] = false;
  684. #endif
  685. /*
  686. * Check correctness: for every pair of edges, see whether they
  687. * cross.
  688. */
  689. for (i = 0; (e = index234(state->graph->edges, i)) != NULL; i++) {
  690. for (j = i+1; (e2 = index234(state->graph->edges, j)) != NULL; j++) {
  691. if (e2->a == e->a || e2->a == e->b ||
  692. e2->b == e->a || e2->b == e->b)
  693. continue;
  694. if (cross(state->pts[e2->a], state->pts[e2->b],
  695. state->pts[e->a], state->pts[e->b])) {
  696. ok = false;
  697. #ifdef SHOW_CROSSINGS
  698. state->crosses[i] = state->crosses[j] = true;
  699. #else
  700. goto done; /* multi-level break - sorry */
  701. #endif
  702. }
  703. }
  704. }
  705. /*
  706. * e == NULL if we've gone through all the edge pairs
  707. * without finding a crossing.
  708. */
  709. #ifndef SHOW_CROSSINGS
  710. done:
  711. #endif
  712. if (ok)
  713. state->completed = true;
  714. }
  715. static game_state *new_game(midend *me, const game_params *params,
  716. const char *desc)
  717. {
  718. int n = params->n;
  719. game_state *state = snew(game_state);
  720. int a, b;
  721. state->params = *params;
  722. state->w = state->h = COORDLIMIT(n);
  723. state->pts = snewn(n, point);
  724. make_circle(state->pts, n, state->w);
  725. state->graph = snew(struct graph);
  726. state->graph->refcount = 1;
  727. state->graph->edges = newtree234(edgecmp);
  728. state->completed = state->cheated = state->just_solved = false;
  729. while (*desc) {
  730. a = atoi(desc);
  731. assert(a >= 0 && a < params->n);
  732. while (*desc && isdigit((unsigned char)*desc)) desc++;
  733. assert(*desc == '-');
  734. desc++; /* eat dash */
  735. b = atoi(desc);
  736. assert(b >= 0 && b < params->n);
  737. while (*desc && isdigit((unsigned char)*desc)) desc++;
  738. if (*desc) {
  739. assert(*desc == ',');
  740. desc++; /* eat comma */
  741. }
  742. addedge(state->graph->edges, a, b);
  743. }
  744. #ifdef SHOW_CROSSINGS
  745. state->crosses = snewn(count234(state->graph->edges), int);
  746. mark_crossings(state); /* sets up `crosses' and `completed' */
  747. #endif
  748. return state;
  749. }
  750. static game_state *dup_game(const game_state *state)
  751. {
  752. int n = state->params.n;
  753. game_state *ret = snew(game_state);
  754. ret->params = state->params;
  755. ret->w = state->w;
  756. ret->h = state->h;
  757. ret->pts = snewn(n, point);
  758. memcpy(ret->pts, state->pts, n * sizeof(point));
  759. ret->graph = state->graph;
  760. ret->graph->refcount++;
  761. ret->completed = state->completed;
  762. ret->cheated = state->cheated;
  763. ret->just_solved = state->just_solved;
  764. #ifdef SHOW_CROSSINGS
  765. ret->crosses = snewn(count234(ret->graph->edges), int);
  766. memcpy(ret->crosses, state->crosses,
  767. count234(ret->graph->edges) * sizeof(int));
  768. #endif
  769. return ret;
  770. }
  771. static void free_game(game_state *state)
  772. {
  773. if (--state->graph->refcount <= 0) {
  774. edge *e;
  775. while ((e = delpos234(state->graph->edges, 0)) != NULL)
  776. sfree(e);
  777. freetree234(state->graph->edges);
  778. sfree(state->graph);
  779. }
  780. sfree(state->pts);
  781. sfree(state);
  782. }
  783. static char *solve_game(const game_state *state, const game_state *currstate,
  784. const char *aux, const char **error)
  785. {
  786. int n = state->params.n;
  787. int matrix[4];
  788. point *pts;
  789. int i, j, besti;
  790. float bestd;
  791. char buf[80], *ret;
  792. int retlen, retsize;
  793. if (!aux) {
  794. *error = "Solution not known for this puzzle";
  795. return NULL;
  796. }
  797. /*
  798. * Decode the aux_info to get the original point positions.
  799. */
  800. pts = snewn(n, point);
  801. aux++; /* eat 'S' */
  802. for (i = 0; i < n; i++) {
  803. int p, k;
  804. long x, y, d;
  805. int ret = sscanf(aux, ";P%d:%ld,%ld/%ld%n", &p, &x, &y, &d, &k);
  806. if (ret != 4 || p != i) {
  807. *error = "Internal error: aux_info badly formatted";
  808. sfree(pts);
  809. return NULL;
  810. }
  811. pts[i].x = x;
  812. pts[i].y = y;
  813. pts[i].d = d;
  814. aux += k;
  815. }
  816. /*
  817. * Now go through eight possible symmetries of the point set.
  818. * For each one, work out the sum of the Euclidean distances
  819. * between the points' current positions and their new ones.
  820. *
  821. * We're squaring distances here, which means we're at risk of
  822. * integer overflow. Fortunately, there's no real need to be
  823. * massively careful about rounding errors, since this is a
  824. * non-essential bit of the code; so I'll just work in floats
  825. * internally.
  826. */
  827. besti = -1;
  828. bestd = 0.0F;
  829. for (i = 0; i < 8; i++) {
  830. float d;
  831. matrix[0] = matrix[1] = matrix[2] = matrix[3] = 0;
  832. matrix[i & 1] = (i & 2) ? +1 : -1;
  833. matrix[3-(i&1)] = (i & 4) ? +1 : -1;
  834. d = 0.0F;
  835. for (j = 0; j < n; j++) {
  836. float px = (float)pts[j].x / pts[j].d;
  837. float py = (float)pts[j].y / pts[j].d;
  838. float sx = (float)currstate->pts[j].x / currstate->pts[j].d;
  839. float sy = (float)currstate->pts[j].y / currstate->pts[j].d;
  840. float cx = (float)currstate->w / 2;
  841. float cy = (float)currstate->h / 2;
  842. float ox, oy, dx, dy;
  843. px -= cx;
  844. py -= cy;
  845. ox = matrix[0] * px + matrix[1] * py;
  846. oy = matrix[2] * px + matrix[3] * py;
  847. ox += cx;
  848. oy += cy;
  849. dx = ox - sx;
  850. dy = oy - sy;
  851. d += dx*dx + dy*dy;
  852. }
  853. if (besti < 0 || bestd > d) {
  854. besti = i;
  855. bestd = d;
  856. }
  857. }
  858. assert(besti >= 0);
  859. /*
  860. * Now we know which symmetry is closest to the points' current
  861. * positions. Use it.
  862. */
  863. matrix[0] = matrix[1] = matrix[2] = matrix[3] = 0;
  864. matrix[besti & 1] = (besti & 2) ? +1 : -1;
  865. matrix[3-(besti&1)] = (besti & 4) ? +1 : -1;
  866. retsize = 256;
  867. ret = snewn(retsize, char);
  868. retlen = 0;
  869. ret[retlen++] = 'S';
  870. ret[retlen] = '\0';
  871. for (i = 0; i < n; i++) {
  872. float px = (float)pts[i].x / pts[i].d;
  873. float py = (float)pts[i].y / pts[i].d;
  874. float cx = (float)currstate->w / 2;
  875. float cy = (float)currstate->h / 2;
  876. float ox, oy;
  877. int extra;
  878. px -= cx;
  879. py -= cy;
  880. ox = matrix[0] * px + matrix[1] * py;
  881. oy = matrix[2] * px + matrix[3] * py;
  882. ox += cx;
  883. oy += cy;
  884. /*
  885. * Use a fixed denominator of 2, because we know the
  886. * original points were on an integer grid offset by 1/2.
  887. */
  888. pts[i].d = 2;
  889. ox *= pts[i].d;
  890. oy *= pts[i].d;
  891. pts[i].x = (long)(ox + 0.5F);
  892. pts[i].y = (long)(oy + 0.5F);
  893. extra = sprintf(buf, ";P%d:%ld,%ld/%ld", i,
  894. pts[i].x, pts[i].y, pts[i].d);
  895. if (retlen + extra >= retsize) {
  896. retsize = retlen + extra + 256;
  897. ret = sresize(ret, retsize, char);
  898. }
  899. strcpy(ret + retlen, buf);
  900. retlen += extra;
  901. }
  902. sfree(pts);
  903. return ret;
  904. }
  905. struct game_ui {
  906. int dragpoint; /* point being dragged; -1 if none */
  907. point newpoint; /* where it's been dragged to so far */
  908. bool just_dragged; /* reset in game_changed_state */
  909. bool just_moved; /* _set_ in game_changed_state */
  910. float anim_length;
  911. /*
  912. * User preference option to snap dragged points to a coarse-ish
  913. * grid. Requested by a user who otherwise found themself spending
  914. * too much time struggling to get lines nicely horizontal or
  915. * vertical.
  916. */
  917. bool snap_to_grid;
  918. };
  919. static game_ui *new_ui(const game_state *state)
  920. {
  921. game_ui *ui = snew(game_ui);
  922. ui->dragpoint = -1;
  923. ui->just_moved = ui->just_dragged = false;
  924. ui->snap_to_grid = false;
  925. return ui;
  926. }
  927. static config_item *get_prefs(game_ui *ui)
  928. {
  929. config_item *cfg;
  930. cfg = snewn(2, config_item);
  931. cfg[0].name = "Snap points to a grid";
  932. cfg[0].kw = "snap-to-grid";
  933. cfg[0].type = C_BOOLEAN;
  934. cfg[0].u.boolean.bval = ui->snap_to_grid;
  935. cfg[1].name = NULL;
  936. cfg[1].type = C_END;
  937. return cfg;
  938. }
  939. static void set_prefs(game_ui *ui, const config_item *cfg)
  940. {
  941. ui->snap_to_grid = cfg[0].u.boolean.bval;
  942. }
  943. static void free_ui(game_ui *ui)
  944. {
  945. sfree(ui);
  946. }
  947. static void game_changed_state(game_ui *ui, const game_state *oldstate,
  948. const game_state *newstate)
  949. {
  950. ui->dragpoint = -1;
  951. ui->just_moved = ui->just_dragged;
  952. ui->just_dragged = false;
  953. }
  954. struct game_drawstate {
  955. long tilesize;
  956. int bg, dragpoint;
  957. long *x, *y;
  958. };
  959. static void place_dragged_point(const game_state *state, game_ui *ui,
  960. const game_drawstate *ds, int x, int y)
  961. {
  962. if (ui->snap_to_grid) {
  963. /*
  964. * We snap points to a grid that has n-1 vertices on each
  965. * side. This should be large enough to permit a straight-
  966. * line drawing of any n-vertex planar graph, and moreover,
  967. * any specific planar embedding of that graph.
  968. *
  969. * Source: David Eppstein's book 'Forbidden Configurations in
  970. * Discrete Geometry' mentions (section 16.3, page 182) that
  971. * the point configuration he describes as GRID(n-1,n-1) -
  972. * that is, the vertices of a square grid with n-1 vertices on
  973. * each side - is universal for n-vertex planar graphs. In
  974. * other words (from definitions earlier in the chapter), if a
  975. * graph G admits any drawing in the plane at all, then it can
  976. * be drawn with straight lines, and with all vertices being
  977. * vertices of that grid.
  978. *
  979. * That fact by itself only says that _some_ planar embedding
  980. * of G can be drawn in this grid. We'd prefer that _all_
  981. * embeddings of G can be so drawn, because 'snap to grid' is
  982. * supposed to be a UI affordance, not an extra puzzle
  983. * challenge, so we don't want to constrain the player's
  984. * choice of planar embedding.
  985. *
  986. * But it doesn't make a difference. Proof: given a specific
  987. * planar embedding of G, triangulate it, by adding extra
  988. * edges to every face of degree > 3. When this process
  989. * terminates with every face a triangle, we have a new graph
  990. * G' such that no edge can be added without it ceasing to be
  991. * planar. Standard theorems say that a maximal planar graph
  992. * is 3-connected, and that a 3-connected planar graph has a
  993. * _unique_ embedding. So any drawing of G' in the plane can
  994. * be converted into a drawing of G in the desired embedding,
  995. * by simply removing all the extra edges that we added to
  996. * turn G into G'. And G' is still an n-vertex planar graph,
  997. * hence it can be drawn in GRID(n-1,n-1). []
  998. */
  999. int d = state->params.n - 1;
  1000. x = d * x / (state->w * ds->tilesize);
  1001. x *= (state->w * ds->tilesize) / d;
  1002. x += (state->w * ds->tilesize) / (2*d);
  1003. y = d * y / (state->h * ds->tilesize);
  1004. y *= (state->h * ds->tilesize) / d;
  1005. y += (state->h * ds->tilesize) / (2*d);
  1006. }
  1007. ui->newpoint.x = x;
  1008. ui->newpoint.y = y;
  1009. ui->newpoint.d = ds->tilesize;
  1010. }
  1011. static char *interpret_move(const game_state *state, game_ui *ui,
  1012. const game_drawstate *ds,
  1013. int x, int y, int button)
  1014. {
  1015. int n = state->params.n;
  1016. if (IS_MOUSE_DOWN(button)) {
  1017. int i, best;
  1018. long bestd;
  1019. /*
  1020. * Begin drag. We drag the vertex _nearest_ to the pointer,
  1021. * just in case one is nearly on top of another and we want
  1022. * to drag the latter. However, we drag nothing at all if
  1023. * the nearest vertex is outside DRAG_THRESHOLD.
  1024. */
  1025. best = -1;
  1026. bestd = 0;
  1027. for (i = 0; i < n; i++) {
  1028. long px = state->pts[i].x * ds->tilesize / state->pts[i].d;
  1029. long py = state->pts[i].y * ds->tilesize / state->pts[i].d;
  1030. long dx = px - x;
  1031. long dy = py - y;
  1032. long d = dx*dx + dy*dy;
  1033. if (best == -1 || bestd > d) {
  1034. best = i;
  1035. bestd = d;
  1036. }
  1037. }
  1038. if (bestd <= DRAG_THRESHOLD * DRAG_THRESHOLD) {
  1039. ui->dragpoint = best;
  1040. place_dragged_point(state, ui, ds, x, y);
  1041. return MOVE_UI_UPDATE;
  1042. }
  1043. } else if (IS_MOUSE_DRAG(button) && ui->dragpoint >= 0) {
  1044. place_dragged_point(state, ui, ds, x, y);
  1045. return MOVE_UI_UPDATE;
  1046. } else if (IS_MOUSE_RELEASE(button) && ui->dragpoint >= 0) {
  1047. int p = ui->dragpoint;
  1048. char buf[80];
  1049. ui->dragpoint = -1; /* terminate drag, no matter what */
  1050. /*
  1051. * First, see if we're within range. The user can cancel a
  1052. * drag by dragging the point right off the window.
  1053. */
  1054. if (ui->newpoint.x < 0 ||
  1055. ui->newpoint.x >= (long)state->w*ui->newpoint.d ||
  1056. ui->newpoint.y < 0 ||
  1057. ui->newpoint.y >= (long)state->h*ui->newpoint.d)
  1058. return MOVE_UI_UPDATE;
  1059. /*
  1060. * We aren't cancelling the drag. Construct a move string
  1061. * indicating where this point is going to.
  1062. */
  1063. sprintf(buf, "P%d:%ld,%ld/%ld", p,
  1064. ui->newpoint.x, ui->newpoint.y, ui->newpoint.d);
  1065. ui->just_dragged = true;
  1066. return dupstr(buf);
  1067. }
  1068. return NULL;
  1069. }
  1070. static game_state *execute_move(const game_state *state, const char *move)
  1071. {
  1072. int n = state->params.n;
  1073. int p, k;
  1074. long x, y, d;
  1075. game_state *ret = dup_game(state);
  1076. ret->just_solved = false;
  1077. while (*move) {
  1078. if (*move == 'S') {
  1079. move++;
  1080. if (*move == ';') move++;
  1081. ret->cheated = ret->just_solved = true;
  1082. }
  1083. if (*move == 'P' &&
  1084. sscanf(move+1, "%d:%ld,%ld/%ld%n", &p, &x, &y, &d, &k) == 4 &&
  1085. p >= 0 && p < n && d > 0) {
  1086. ret->pts[p].x = x;
  1087. ret->pts[p].y = y;
  1088. ret->pts[p].d = d;
  1089. move += k+1;
  1090. if (*move == ';') move++;
  1091. } else {
  1092. free_game(ret);
  1093. return NULL;
  1094. }
  1095. }
  1096. mark_crossings(ret);
  1097. return ret;
  1098. }
  1099. /* ----------------------------------------------------------------------
  1100. * Drawing routines.
  1101. */
  1102. static void game_compute_size(const game_params *params, int tilesize,
  1103. const game_ui *ui, int *x, int *y)
  1104. {
  1105. *x = *y = COORDLIMIT(params->n) * tilesize;
  1106. }
  1107. static void game_set_size(drawing *dr, game_drawstate *ds,
  1108. const game_params *params, int tilesize)
  1109. {
  1110. ds->tilesize = tilesize;
  1111. }
  1112. static float *game_colours(frontend *fe, int *ncolours)
  1113. {
  1114. float *ret = snewn(3 * NCOLOURS, float);
  1115. /*
  1116. * COL_BACKGROUND is what we use as the normal background colour.
  1117. * Unusually, though, it isn't colour #0: COL_SYSBACKGROUND, a bit
  1118. * darker, takes that place. This means that if the user resizes
  1119. * an Untangle window so as to change its aspect ratio, the
  1120. * still-square playable area will be distinguished from the dead
  1121. * space around it.
  1122. */
  1123. game_mkhighlight(fe, ret, COL_BACKGROUND, -1, COL_SYSBACKGROUND);
  1124. ret[COL_LINE * 3 + 0] = 0.0F;
  1125. ret[COL_LINE * 3 + 1] = 0.0F;
  1126. ret[COL_LINE * 3 + 2] = 0.0F;
  1127. #ifdef SHOW_CROSSINGS
  1128. ret[COL_CROSSEDLINE * 3 + 0] = 1.0F;
  1129. ret[COL_CROSSEDLINE * 3 + 1] = 0.0F;
  1130. ret[COL_CROSSEDLINE * 3 + 2] = 0.0F;
  1131. #endif
  1132. ret[COL_OUTLINE * 3 + 0] = 0.0F;
  1133. ret[COL_OUTLINE * 3 + 1] = 0.0F;
  1134. ret[COL_OUTLINE * 3 + 2] = 0.0F;
  1135. ret[COL_POINT * 3 + 0] = 0.0F;
  1136. ret[COL_POINT * 3 + 1] = 0.0F;
  1137. ret[COL_POINT * 3 + 2] = 1.0F;
  1138. ret[COL_DRAGPOINT * 3 + 0] = 1.0F;
  1139. ret[COL_DRAGPOINT * 3 + 1] = 1.0F;
  1140. ret[COL_DRAGPOINT * 3 + 2] = 1.0F;
  1141. ret[COL_NEIGHBOUR * 3 + 0] = 1.0F;
  1142. ret[COL_NEIGHBOUR * 3 + 1] = 0.0F;
  1143. ret[COL_NEIGHBOUR * 3 + 2] = 0.0F;
  1144. ret[COL_FLASH1 * 3 + 0] = 0.5F;
  1145. ret[COL_FLASH1 * 3 + 1] = 0.5F;
  1146. ret[COL_FLASH1 * 3 + 2] = 0.5F;
  1147. ret[COL_FLASH2 * 3 + 0] = 1.0F;
  1148. ret[COL_FLASH2 * 3 + 1] = 1.0F;
  1149. ret[COL_FLASH2 * 3 + 2] = 1.0F;
  1150. *ncolours = NCOLOURS;
  1151. return ret;
  1152. }
  1153. static game_drawstate *game_new_drawstate(drawing *dr, const game_state *state)
  1154. {
  1155. struct game_drawstate *ds = snew(struct game_drawstate);
  1156. int i;
  1157. ds->tilesize = 0;
  1158. ds->x = snewn(state->params.n, long);
  1159. ds->y = snewn(state->params.n, long);
  1160. for (i = 0; i < state->params.n; i++)
  1161. ds->x[i] = ds->y[i] = -1;
  1162. ds->bg = -1;
  1163. ds->dragpoint = -1;
  1164. return ds;
  1165. }
  1166. static void game_free_drawstate(drawing *dr, game_drawstate *ds)
  1167. {
  1168. sfree(ds->y);
  1169. sfree(ds->x);
  1170. sfree(ds);
  1171. }
  1172. static point mix(point a, point b, float distance)
  1173. {
  1174. point ret;
  1175. ret.d = a.d * b.d;
  1176. ret.x = (long)(a.x * b.d + distance * (b.x * a.d - a.x * b.d));
  1177. ret.y = (long)(a.y * b.d + distance * (b.y * a.d - a.y * b.d));
  1178. return ret;
  1179. }
  1180. static void game_redraw(drawing *dr, game_drawstate *ds,
  1181. const game_state *oldstate, const game_state *state,
  1182. int dir, const game_ui *ui,
  1183. float animtime, float flashtime)
  1184. {
  1185. int w, h;
  1186. edge *e;
  1187. int i, j;
  1188. int bg;
  1189. bool points_moved;
  1190. /*
  1191. * There's no terribly sensible way to do partial redraws of
  1192. * this game, so I'm going to have to resort to redrawing the
  1193. * whole thing every time.
  1194. */
  1195. if (flashtime == 0)
  1196. bg = COL_BACKGROUND;
  1197. else if ((int)(flashtime * 4 / FLASH_TIME) % 2 == 0)
  1198. bg = COL_FLASH1;
  1199. else
  1200. bg = COL_FLASH2;
  1201. /*
  1202. * To prevent excessive spinning on redraw during a completion
  1203. * flash, we first check to see if _either_ the flash
  1204. * background colour has changed _or_ at least one point has
  1205. * moved _or_ a drag has begun or ended, and abandon the redraw
  1206. * if neither is the case.
  1207. *
  1208. * Also in this loop we work out the coordinates of all the
  1209. * points for this redraw.
  1210. */
  1211. points_moved = false;
  1212. for (i = 0; i < state->params.n; i++) {
  1213. point p = state->pts[i];
  1214. long x, y;
  1215. if (ui->dragpoint == i)
  1216. p = ui->newpoint;
  1217. if (oldstate)
  1218. p = mix(oldstate->pts[i], p, animtime / ui->anim_length);
  1219. x = p.x * ds->tilesize / p.d;
  1220. y = p.y * ds->tilesize / p.d;
  1221. if (ds->x[i] != x || ds->y[i] != y)
  1222. points_moved = true;
  1223. ds->x[i] = x;
  1224. ds->y[i] = y;
  1225. }
  1226. if (ds->bg == bg && ds->dragpoint == ui->dragpoint && !points_moved)
  1227. return; /* nothing to do */
  1228. ds->dragpoint = ui->dragpoint;
  1229. ds->bg = bg;
  1230. game_compute_size(&state->params, ds->tilesize, ui, &w, &h);
  1231. draw_rect(dr, 0, 0, w, h, bg);
  1232. /*
  1233. * Draw the edges.
  1234. */
  1235. for (i = 0; (e = index234(state->graph->edges, i)) != NULL; i++) {
  1236. draw_line(dr, ds->x[e->a], ds->y[e->a], ds->x[e->b], ds->y[e->b],
  1237. #ifdef SHOW_CROSSINGS
  1238. (oldstate?oldstate:state)->crosses[i] ?
  1239. COL_CROSSEDLINE :
  1240. #endif
  1241. COL_LINE);
  1242. }
  1243. /*
  1244. * Draw the points.
  1245. *
  1246. * When dragging, we should not only vary the colours, but
  1247. * leave the point being dragged until last.
  1248. */
  1249. for (j = 0; j < 3; j++) {
  1250. int thisc = (j == 0 ? COL_POINT :
  1251. j == 1 ? COL_NEIGHBOUR : COL_DRAGPOINT);
  1252. for (i = 0; i < state->params.n; i++) {
  1253. int c;
  1254. if (ui->dragpoint == i) {
  1255. c = COL_DRAGPOINT;
  1256. } else if (ui->dragpoint >= 0 &&
  1257. isedge(state->graph->edges, ui->dragpoint, i)) {
  1258. c = COL_NEIGHBOUR;
  1259. } else {
  1260. c = COL_POINT;
  1261. }
  1262. if (c == thisc) {
  1263. #ifdef VERTEX_NUMBERS
  1264. draw_circle(dr, ds->x[i], ds->y[i], DRAG_THRESHOLD, bg, bg);
  1265. {
  1266. char buf[80];
  1267. sprintf(buf, "%d", i);
  1268. draw_text(dr, ds->x[i], ds->y[i], FONT_VARIABLE,
  1269. DRAG_THRESHOLD*3/2,
  1270. ALIGN_VCENTRE|ALIGN_HCENTRE, c, buf);
  1271. }
  1272. #else
  1273. draw_circle(dr, ds->x[i], ds->y[i], CIRCLE_RADIUS,
  1274. c, COL_OUTLINE);
  1275. #endif
  1276. }
  1277. }
  1278. }
  1279. draw_update(dr, 0, 0, w, h);
  1280. }
  1281. static float game_anim_length(const game_state *oldstate,
  1282. const game_state *newstate, int dir, game_ui *ui)
  1283. {
  1284. if (ui->just_moved)
  1285. return 0.0F;
  1286. if ((dir < 0 ? oldstate : newstate)->just_solved)
  1287. ui->anim_length = SOLVEANIM_TIME;
  1288. else
  1289. ui->anim_length = ANIM_TIME;
  1290. return ui->anim_length;
  1291. }
  1292. static float game_flash_length(const game_state *oldstate,
  1293. const game_state *newstate, int dir, game_ui *ui)
  1294. {
  1295. if (!oldstate->completed && newstate->completed &&
  1296. !oldstate->cheated && !newstate->cheated)
  1297. return FLASH_TIME;
  1298. return 0.0F;
  1299. }
  1300. static void game_get_cursor_location(const game_ui *ui,
  1301. const game_drawstate *ds,
  1302. const game_state *state,
  1303. const game_params *params,
  1304. int *x, int *y, int *w, int *h)
  1305. {
  1306. }
  1307. static int game_status(const game_state *state)
  1308. {
  1309. return state->completed ? +1 : 0;
  1310. }
  1311. #ifdef COMBINED
  1312. #define thegame untangle
  1313. #endif
  1314. const struct game thegame = {
  1315. "Untangle", "games.untangle", "untangle",
  1316. default_params,
  1317. game_fetch_preset, NULL,
  1318. decode_params,
  1319. encode_params,
  1320. free_params,
  1321. dup_params,
  1322. true, game_configure, custom_params,
  1323. validate_params,
  1324. new_game_desc,
  1325. validate_desc,
  1326. new_game,
  1327. dup_game,
  1328. free_game,
  1329. true, solve_game,
  1330. false, NULL, NULL, /* can_format_as_text_now, text_format */
  1331. get_prefs, set_prefs,
  1332. new_ui,
  1333. free_ui,
  1334. NULL, /* encode_ui */
  1335. NULL, /* decode_ui */
  1336. NULL, /* game_request_keys */
  1337. game_changed_state,
  1338. NULL, /* current_key_label */
  1339. interpret_move,
  1340. execute_move,
  1341. PREFERRED_TILESIZE, game_compute_size, game_set_size,
  1342. game_colours,
  1343. game_new_drawstate,
  1344. game_free_drawstate,
  1345. game_redraw,
  1346. game_anim_length,
  1347. game_flash_length,
  1348. game_get_cursor_location,
  1349. game_status,
  1350. false, false, NULL, NULL, /* print_size, print */
  1351. false, /* wants_statusbar */
  1352. false, NULL, /* timing_state */
  1353. SOLVE_ANIMATES, /* flags */
  1354. };