findloop.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  1. /*
  2. * Routine for finding loops in graphs, reusable across multiple
  3. * puzzles.
  4. *
  5. * The strategy is Tarjan's bridge-finding algorithm, which is
  6. * designed to list all edges whose removal would disconnect a
  7. * previously connected component of the graph. We're interested in
  8. * exactly the reverse - edges that are part of a loop in the graph
  9. * are precisely those which _wouldn't_ disconnect anything if removed
  10. * (individually) - but of course flipping the sense of the output is
  11. * easy.
  12. */
  13. #include "puzzles.h"
  14. struct findloopstate {
  15. int parent, child, sibling, component_root;
  16. bool visited;
  17. int index, minindex, maxindex;
  18. int minreachable, maxreachable;
  19. int bridge;
  20. };
  21. struct findloopstate *findloop_new_state(int nvertices)
  22. {
  23. /*
  24. * Allocate a findloopstate structure for each vertex, and one
  25. * extra one at the end which will be the overall root of a
  26. * 'super-tree', which links the whole graph together to make it
  27. * as easy as possible to iterate over all the connected
  28. * components.
  29. */
  30. return snewn(nvertices + 1, struct findloopstate);
  31. }
  32. void findloop_free_state(struct findloopstate *state)
  33. {
  34. sfree(state);
  35. }
  36. bool findloop_is_loop_edge(struct findloopstate *pv, int u, int v)
  37. {
  38. /*
  39. * Since the algorithm is intended for finding bridges, and a
  40. * bridge must be part of any spanning tree, it follows that there
  41. * is at most one bridge per vertex.
  42. *
  43. * Furthermore, by finding a _rooted_ spanning tree (so that each
  44. * bridge is a parent->child link), you can find an injection from
  45. * bridges to vertices (namely, map each bridge to the vertex at
  46. * its child end).
  47. *
  48. * So if the u-v edge is a bridge, then either v was u's parent
  49. * when the algorithm ran and we set pv[u].bridge = v, or vice
  50. * versa.
  51. */
  52. return !(pv[u].bridge == v || pv[v].bridge == u);
  53. }
  54. static bool findloop_is_bridge_oneway(
  55. struct findloopstate *pv, int u, int v, int *u_vertices, int *v_vertices)
  56. {
  57. int r, total, below;
  58. if (pv[u].bridge != v)
  59. return false;
  60. r = pv[u].component_root;
  61. total = pv[r].maxindex - pv[r].minindex + 1;
  62. below = pv[u].maxindex - pv[u].minindex + 1;
  63. if (u_vertices)
  64. *u_vertices = below;
  65. if (v_vertices)
  66. *v_vertices = total - below;
  67. return true;
  68. }
  69. bool findloop_is_bridge(
  70. struct findloopstate *pv, int u, int v, int *u_vertices, int *v_vertices)
  71. {
  72. return (findloop_is_bridge_oneway(pv, u, v, u_vertices, v_vertices) ||
  73. findloop_is_bridge_oneway(pv, v, u, v_vertices, u_vertices));
  74. }
  75. bool findloop_run(struct findloopstate *pv, int nvertices,
  76. neighbour_fn_t neighbour, void *ctx)
  77. {
  78. int u, v, w, root, index;
  79. int nbridges, nedges;
  80. root = nvertices;
  81. /*
  82. * First pass: organise the graph into a rooted spanning forest.
  83. * That is, a tree structure with a clear up/down orientation -
  84. * every node has exactly one parent (which may be 'root') and
  85. * zero or more children, and every parent-child link corresponds
  86. * to a graph edge.
  87. *
  88. * (A side effect of this is to find all the connected components,
  89. * which of course we could do less confusingly with a dsf - but
  90. * then we'd have to do that *and* build the tree, so it's less
  91. * effort to do it all at once.)
  92. */
  93. for (v = 0; v <= nvertices; v++) {
  94. pv[v].parent = root;
  95. pv[v].child = -2;
  96. pv[v].sibling = -1;
  97. pv[v].visited = false;
  98. }
  99. pv[root].child = -1;
  100. nedges = 0;
  101. debug(("------------- new find_loops, nvertices=%d\n", nvertices));
  102. for (v = 0; v < nvertices; v++) {
  103. if (pv[v].parent == root) {
  104. /*
  105. * Found a new connected component. Enumerate and treeify
  106. * it.
  107. */
  108. pv[v].sibling = pv[root].child;
  109. pv[root].child = v;
  110. pv[v].component_root = v;
  111. debug(("%d is new child of root\n", v));
  112. u = v;
  113. while (1) {
  114. if (!pv[u].visited) {
  115. pv[u].visited = true;
  116. /*
  117. * Enumerate the neighbours of u, and any that are
  118. * as yet not in the tree structure (indicated by
  119. * child==-2, and distinct from the 'visited'
  120. * flag) become children of u.
  121. */
  122. debug((" component pass: processing %d\n", u));
  123. for (w = neighbour(u, ctx); w >= 0;
  124. w = neighbour(-1, ctx)) {
  125. debug((" edge %d-%d\n", u, w));
  126. if (pv[w].child == -2) {
  127. debug((" -> new child\n"));
  128. pv[w].child = -1;
  129. pv[w].sibling = pv[u].child;
  130. pv[w].parent = u;
  131. pv[w].component_root = pv[u].component_root;
  132. pv[u].child = w;
  133. }
  134. /* While we're here, count the edges in the whole
  135. * graph, so that we can easily check at the end
  136. * whether all of them are bridges, i.e. whether
  137. * no loop exists at all. */
  138. if (w > u) /* count each edge only in one direction */
  139. nedges++;
  140. }
  141. /*
  142. * Now descend in depth-first search.
  143. */
  144. if (pv[u].child >= 0) {
  145. u = pv[u].child;
  146. debug((" descending to %d\n", u));
  147. continue;
  148. }
  149. }
  150. if (u == v) {
  151. debug((" back at %d, done this component\n", u));
  152. break;
  153. } else if (pv[u].sibling >= 0) {
  154. u = pv[u].sibling;
  155. debug((" sideways to %d\n", u));
  156. } else {
  157. u = pv[u].parent;
  158. debug((" ascending to %d\n", u));
  159. }
  160. }
  161. }
  162. }
  163. /*
  164. * Second pass: index all the vertices in such a way that every
  165. * subtree has a contiguous range of indices. (Easily enough done,
  166. * by iterating through the tree structure we just built and
  167. * numbering its elements as if they were those of a sorted list.)
  168. *
  169. * For each vertex, we compute the min and max index of the
  170. * subtree starting there.
  171. *
  172. * (We index the vertices in preorder, per Tarjan's original
  173. * description, so that each vertex's min subtree index is its own
  174. * index; but that doesn't actually matter; either way round would
  175. * do. The important thing is that we have a simple arithmetic
  176. * criterion that tells us whether a vertex is in a given subtree
  177. * or not.)
  178. */
  179. debug(("--- begin indexing pass\n"));
  180. index = 0;
  181. for (v = 0; v < nvertices; v++)
  182. pv[v].visited = false;
  183. pv[root].visited = true;
  184. u = pv[root].child;
  185. while (1) {
  186. if (!pv[u].visited) {
  187. pv[u].visited = true;
  188. /*
  189. * Index this node.
  190. */
  191. pv[u].minindex = pv[u].index = index;
  192. debug((" vertex %d <- index %d\n", u, index));
  193. index++;
  194. /*
  195. * Now descend in depth-first search.
  196. */
  197. if (pv[u].child >= 0) {
  198. u = pv[u].child;
  199. debug((" descending to %d\n", u));
  200. continue;
  201. }
  202. }
  203. if (u == root) {
  204. debug((" back at %d, done indexing\n", u));
  205. break;
  206. }
  207. /*
  208. * As we re-ascend to here from its children (or find that we
  209. * had no children to descend to in the first place), fill in
  210. * its maxindex field.
  211. */
  212. pv[u].maxindex = index-1;
  213. debug((" vertex %d <- maxindex %d\n", u, pv[u].maxindex));
  214. if (pv[u].sibling >= 0) {
  215. u = pv[u].sibling;
  216. debug((" sideways to %d\n", u));
  217. } else {
  218. u = pv[u].parent;
  219. debug((" ascending to %d\n", u));
  220. }
  221. }
  222. /*
  223. * We're ready to generate output now, so initialise the output
  224. * fields.
  225. */
  226. for (v = 0; v < nvertices; v++)
  227. pv[v].bridge = -1;
  228. /*
  229. * Final pass: determine the min and max index of the vertices
  230. * reachable from every subtree, not counting the link back to
  231. * each vertex's parent. Then our criterion is: given a vertex u,
  232. * defining a subtree consisting of u and all its descendants, we
  233. * compare the range of vertex indices _in_ that subtree (which is
  234. * just the minindex and maxindex of u) with the range of vertex
  235. * indices in the _neighbourhood_ of the subtree (computed in this
  236. * final pass, and not counting u's own edge to its parent), and
  237. * if the latter includes anything outside the former, then there
  238. * must be some path from u to outside its subtree which does not
  239. * go through the parent edge - i.e. the edge from u to its parent
  240. * is part of a loop.
  241. */
  242. debug(("--- begin min-max pass\n"));
  243. nbridges = 0;
  244. for (v = 0; v < nvertices; v++)
  245. pv[v].visited = false;
  246. u = pv[root].child;
  247. pv[root].visited = true;
  248. while (1) {
  249. if (!pv[u].visited) {
  250. pv[u].visited = true;
  251. /*
  252. * Look for vertices reachable directly from u, including
  253. * u itself.
  254. */
  255. debug((" processing vertex %d\n", u));
  256. pv[u].minreachable = pv[u].maxreachable = pv[u].minindex;
  257. for (w = neighbour(u, ctx); w >= 0; w = neighbour(-1, ctx)) {
  258. debug((" edge %d-%d\n", u, w));
  259. if (w != pv[u].parent) {
  260. int i = pv[w].index;
  261. if (pv[u].minreachable > i)
  262. pv[u].minreachable = i;
  263. if (pv[u].maxreachable < i)
  264. pv[u].maxreachable = i;
  265. }
  266. }
  267. debug((" initial min=%d max=%d\n",
  268. pv[u].minreachable, pv[u].maxreachable));
  269. /*
  270. * Now descend in depth-first search.
  271. */
  272. if (pv[u].child >= 0) {
  273. u = pv[u].child;
  274. debug((" descending to %d\n", u));
  275. continue;
  276. }
  277. }
  278. if (u == root) {
  279. debug((" back at %d, done min-maxing\n", u));
  280. break;
  281. }
  282. /*
  283. * As we re-ascend to this vertex, go back through its
  284. * immediate children and do a post-update of its min/max.
  285. */
  286. for (v = pv[u].child; v >= 0; v = pv[v].sibling) {
  287. if (pv[u].minreachable > pv[v].minreachable)
  288. pv[u].minreachable = pv[v].minreachable;
  289. if (pv[u].maxreachable < pv[v].maxreachable)
  290. pv[u].maxreachable = pv[v].maxreachable;
  291. }
  292. debug((" postorder update of %d: min=%d max=%d (indices %d-%d)\n", u,
  293. pv[u].minreachable, pv[u].maxreachable,
  294. pv[u].minindex, pv[u].maxindex));
  295. /*
  296. * And now we know whether each to our own parent is a bridge.
  297. */
  298. if ((v = pv[u].parent) != root) {
  299. if (pv[u].minreachable >= pv[u].minindex &&
  300. pv[u].maxreachable <= pv[u].maxindex) {
  301. /* Yes, it's a bridge. */
  302. pv[u].bridge = v;
  303. nbridges++;
  304. debug((" %d-%d is a bridge\n", v, u));
  305. } else {
  306. debug((" %d-%d is not a bridge\n", v, u));
  307. }
  308. }
  309. if (pv[u].sibling >= 0) {
  310. u = pv[u].sibling;
  311. debug((" sideways to %d\n", u));
  312. } else {
  313. u = pv[u].parent;
  314. debug((" ascending to %d\n", u));
  315. }
  316. }
  317. debug(("finished, nedges=%d nbridges=%d\n", nedges, nbridges));
  318. /*
  319. * Done.
  320. */
  321. return nbridges < nedges;
  322. }
  323. /*
  324. * Appendix: the long and painful history of loop detection in these puzzles
  325. * =========================================================================
  326. *
  327. * For interest, I thought I'd write up the five loop-finding methods
  328. * I've gone through before getting to this algorithm. It's a case
  329. * study in all the ways you can solve this particular problem
  330. * wrongly, and also how much effort you can waste by not managing to
  331. * find the existing solution in the literature :-(
  332. *
  333. * Vertex dsf
  334. * ----------
  335. *
  336. * Initially, in puzzles where you need to not have any loops in the
  337. * solution graph, I detected them by using a dsf to track connected
  338. * components of vertices. Iterate over each edge unifying the two
  339. * vertices it connects; but before that, check if the two vertices
  340. * are _already_ known to be connected. If so, then the new edge is
  341. * providing a second path between them, i.e. a loop exists.
  342. *
  343. * That's adequate for automated solvers, where you just need to know
  344. * _whether_ a loop exists, so as to rule out that move and do
  345. * something else. But during play, you want to do better than that:
  346. * you want to _point out_ the loops with error highlighting.
  347. *
  348. * Graph pruning
  349. * -------------
  350. *
  351. * So my second attempt worked by iteratively pruning the graph. Find
  352. * a vertex with degree 1; remove that edge; repeat until you can't
  353. * find such a vertex any more. This procedure will remove *every*
  354. * edge of the graph if and only if there were no loops; so if there
  355. * are any edges remaining, highlight them.
  356. *
  357. * This successfully highlights loops, but not _only_ loops. If the
  358. * graph contains a 'dumb-bell' shaped subgraph consisting of two
  359. * loops connected by a path, then we'll end up highlighting the
  360. * connecting path as well as the loops. That's not what we wanted.
  361. *
  362. * Vertex dsf with ad-hoc loop tracing
  363. * -----------------------------------
  364. *
  365. * So my third attempt was to go back to the dsf strategy, only this
  366. * time, when you detect that a particular edge connects two
  367. * already-connected vertices (and hence is part of a loop), you try
  368. * to trace round that loop to highlight it - before adding the new
  369. * edge, search for a path between its endpoints among the edges the
  370. * algorithm has already visited, and when you find one (which you
  371. * must), highlight the loop consisting of that path plus the new
  372. * edge.
  373. *
  374. * This solves the dumb-bell problem - we definitely now cannot
  375. * accidentally highlight any edge that is *not* part of a loop. But
  376. * it's far from clear that we'll highlight *every* edge that *is*
  377. * part of a loop - what if there were multiple paths between the two
  378. * vertices? It would be difficult to guarantee that we'd always catch
  379. * every single one.
  380. *
  381. * On the other hand, it is at least guaranteed that we'll highlight
  382. * _something_ if any loop exists, and in other error highlighting
  383. * situations (see in particular the Tents connected component
  384. * analysis) I've been known to consider that sufficient. So this
  385. * version hung around for quite a while, until I had a better idea.
  386. *
  387. * Face dsf
  388. * --------
  389. *
  390. * Round about the time Loopy was being revamped to include non-square
  391. * grids, I had a much cuter idea, making use of the fact that the
  392. * graph is planar, and hence has a concept of faces.
  393. *
  394. * In Loopy, there are really two graphs: the 'grid', consisting of
  395. * all the edges that the player *might* fill in, and the solution
  396. * graph of the edges the player actually *has* filled in. The
  397. * algorithm is: set up a dsf on the *faces* of the grid. Iterate over
  398. * each edge of the grid which is _not_ marked by the player as an
  399. * edge of the solution graph, unifying the faces on either side of
  400. * that edge. This groups the faces into connected components. Now,
  401. * there is more than one connected component iff a loop exists, and
  402. * moreover, an edge of the solution graph is part of a loop iff the
  403. * faces on either side of it are in different connected components!
  404. *
  405. * This is the first algorithm I came up with that I was confident
  406. * would successfully highlight exactly the correct set of edges in
  407. * all cases. It's also conceptually elegant, and very easy to
  408. * implement and to be confident you've got it right (since it just
  409. * consists of two very simple loops over the edge set, one building
  410. * the dsf and one reading it off). I was very pleased with it.
  411. *
  412. * Doing the same thing in Slant is slightly more difficult because
  413. * the set of edges the user can fill in do not form a planar graph
  414. * (the two potential edges in each square cross in the middle). But
  415. * you can still apply the same principle by considering the 'faces'
  416. * to be diamond-shaped regions of space around each horizontal or
  417. * vertical grid line. Equivalently, pretend each edge added by the
  418. * player is really divided into two edges, each from a square-centre
  419. * to one of the square's corners, and now the grid graph is planar
  420. * again.
  421. *
  422. * However, it fell down when - much later - I tried to implement the
  423. * same algorithm in Net.
  424. *
  425. * Net doesn't *absolutely need* loop detection, because of its system
  426. * of highlighting squares connected to the source square: an argument
  427. * involving counting vertex degrees shows that if any loop exists,
  428. * then it must be counterbalanced by some disconnected square, so
  429. * there will be _some_ error highlight in any invalid grid even
  430. * without loop detection. However, in large complicated cases, it's
  431. * still nice to highlight the loop itself, so that once the player is
  432. * clued in to its existence by a disconnected square elsewhere, they
  433. * don't have to spend forever trying to find it.
  434. *
  435. * The new wrinkle in Net, compared to other loop-disallowing puzzles,
  436. * is that it can be played with wrapping walls, or - topologically
  437. * speaking - on a torus. And a torus has a property that algebraic
  438. * topologists would know of as a 'non-trivial H_1 homology group',
  439. * which essentially means that there can exist a loop on a torus
  440. * which *doesn't* separate the surface into two regions disconnected
  441. * from each other.
  442. *
  443. * In other words, using this algorithm in Net will do fine at finding
  444. * _small_ localised loops, but a large-scale loop that goes (say) off
  445. * the top of the grid, back on at the bottom, and meets up in the
  446. * middle again will not be detected.
  447. *
  448. * Footpath dsf
  449. * ------------
  450. *
  451. * To solve this homology problem in Net, I hastily thought up another
  452. * dsf-based algorithm.
  453. *
  454. * This time, let's consider each edge of the graph to be a road, with
  455. * a separate pedestrian footpath down each side. We'll form a dsf on
  456. * those imaginary segments of footpath.
  457. *
  458. * At each vertex of the graph, we go round the edges leaving that
  459. * vertex, in order around the vertex. For each pair of edges adjacent
  460. * in this order, we unify their facing pair of footpaths (e.g. if
  461. * edge E appears anticlockwise of F, then we unify the anticlockwise
  462. * footpath of F with the clockwise one of E) . In particular, if a
  463. * vertex has degree 1, then the two footpaths on either side of its
  464. * single edge are unified.
  465. *
  466. * Then, an edge is part of a loop iff its two footpaths are not
  467. * reachable from one another.
  468. *
  469. * This algorithm is almost as simple to implement as the face dsf,
  470. * and it works on a wider class of graphs embedded in plane-like
  471. * surfaces; in particular, it fixes the torus bug in the face-dsf
  472. * approach. However, it still depends on the graph having _some_ sort
  473. * of embedding in a 2-manifold, because it relies on there being a
  474. * meaningful notion of 'order of edges around a vertex' in the first
  475. * place, so you couldn't use it on a wildly nonplanar graph like the
  476. * diamond lattice. Also, more subtly, it depends on the graph being
  477. * embedded in an _orientable_ surface - and that's a thing that might
  478. * much more plausibly change in future puzzles, because it's not at
  479. * all unlikely that at some point I might feel moved to implement a
  480. * puzzle that can be played on the surface of a Mobius strip or a
  481. * Klein bottle. And then even this algorithm won't work.
  482. *
  483. * Tarjan's bridge-finding algorithm
  484. * ---------------------------------
  485. *
  486. * And so, finally, we come to the algorithm above. This one is pure
  487. * graph theory: it doesn't depend on any concept of 'faces', or 'edge
  488. * ordering around a vertex', or any other trapping of a planar or
  489. * quasi-planar graph embedding. It should work on any graph
  490. * whatsoever, and reliably identify precisely the set of edges that
  491. * form part of some loop. So *hopefully* this long string of failures
  492. * has finally come to an end...
  493. */