Advertisement
Guest User

Untitled

a guest
Sep 21st, 2019
449
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 24.92 KB | None | 0 0
  1. package signpost;
  2.  
  3. import java.util.ArrayList;
  4. import java.util.BitSet;
  5. import java.util.Formatter;
  6. import java.util.HashSet;
  7. import java.util.Iterator;
  8. import java.util.Arrays;
  9.  
  10. import static signpost.Place.pl;
  11. import static signpost.Place.PlaceList;
  12. import static signpost.Utils.*;
  13.  
  14. /** The state of a Signpost puzzle. Each cell has coordinates (x, y),
  15. * where 0 <= x < width(), 0 <= y < height(). The upper-left corner of
  16. * the puzzle has coordinates (0, height() - 1), and the lower-right corner
  17. * is at (width() - 1, 0).
  18. *
  19. * A constructor initializes the squares according to a particular
  20. * solution. A solution is an assignment of sequence numbers from 1 to
  21. * size() == width() * height() to square positions so that squares with
  22. * adjacent numbers are separated by queen moves. A queen move is a move from
  23. * one square to another horizontally, vertically, or diagonally. The effect
  24. * is to give each square whose number in the solution is less than
  25. * size() an <i>arrow direction</i>, 1 <= d <= 8, indicating the direction
  26. * of the next higher numbered square in the solution: d * 45 degrees clockwise
  27. * from straight up (i.e., toward higher y coordinates). The highest-numbered
  28. * square has direction 0. Certain squares can have their values fixed to
  29. * those in the solution. Initially, the only two squares with fixed values
  30. * are those with the lowest and highest sequence numbers in the solution.
  31. *
  32. * At any given time after initialization, a square whose value is not fixed
  33. * may have an unknown value, represented as 0, or a tentative number (not
  34. * necessarily that of the solution) between 1 and size(). Squares may be
  35. * connected together, indicating that their sequence numbers (unknown or not)
  36. * are consecutive.
  37. *
  38. * When square S0 is connected to S1, we say that S1 is the <i>successor</i> of
  39. * S0, and S0 is the <i>predecessor</i> of S1. Sequences of connected squares
  40. * with unknown (0) values form a <i>group</i>, identified by a unique
  41. * <i>group number</i>. Numbered cells (whether linked or not) are in group 0.
  42. * Unnumbered, unlinked cells are in group -1.
  43. *
  44. * Squares are represented as objects of the inner class Sq (Model.Sq). A
  45. * Model object is itself iterable, yielding its squares in unspecified order.
  46. *
  47. * The puzzle is solved when all cells are contained in a single sequence
  48. * of consecutively numbered cells (therefore all in group 0) and all cells
  49. * with fixed sequence numbers appear at the corresponding position
  50. * in that sequence.
  51. *
  52. * @author
  53. */
  54. class Model implements Iterable<Model.Sq> {
  55.  
  56. /** A Model whose solution is SOLUTION, initialized to its starting,
  57. * unsolved state (where only cells with fixed numbers currently
  58. * have sequence numbers and no unnumbered cells are connected).
  59. * SOLUTION must be a proper solution:
  60. * 1. It must have dimensions w x h such that w * h >= 2.
  61. * 2. There must be a sequence of chess-queen moves such that
  62. * the sequence of values in the cells reached is 1, 2, ... w * h.
  63. * The contents of SOLUTION are copied into this Model, so that subsequent
  64. * changes to it have no effect on the Model.
  65. */
  66. Model(int[][] solution) {
  67. if (solution.length == 0 || solution.length * solution[0].length < 2) {
  68. throw badArgs("must have at least 2 squares");
  69. }
  70. _width = solution.length; _height = solution[0].length;
  71. int last = _width * _height;
  72. BitSet allNums = new BitSet();
  73.  
  74. _allSuccessors = Place.successorCells(_width, _height);
  75. _solution = new int[_width][_height];
  76. deepCopy(solution, _solution);
  77.  
  78. // DUMMY SETUP
  79. // FIXME: Remove everything down "// END DUMMY SETUP".
  80. _board = new Sq[][] {
  81. { new Sq(0, 0, 0, false, 2, -1), new Sq(0, 1, 0, false, 2, -1),
  82. new Sq(0, 2, 0, false, 4, -1), new Sq(0, 3, 1, true, 2, 0) },
  83. { new Sq(1, 0, 0, false, 2, -1), new Sq(1, 1, 0, false, 2, -1),
  84. new Sq(1, 2, 0, false, 6, -1), new Sq(1, 3, 0, false, 2, -1) },
  85. { new Sq(2, 0, 0, false, 6, -1), new Sq(2, 1, 0, false, 2, -1),
  86. new Sq(2, 2, 0, false, 6, -1), new Sq(2, 3, 0, false, 2, -1) },
  87. { new Sq(3, 0, 16, true, 0, 0), new Sq(3, 1, 0, false, 5, -1),
  88. new Sq(3, 2, 0, false, 6, -1), new Sq(3, 3, 0, false, 4, -1) }
  89. };
  90. for (Sq[] col: _board) {
  91. for (Sq sq : col) {
  92. _allSquares.add(sq);
  93.  
  94. }
  95. }
  96. // END DUMMY SETUP
  97.  
  98. // FIXME: Initialize _board so that _board[x][y] contains the Sq object
  99. // representing the contents at cell (x, y), _allSquares
  100. // contains the list of all Sq objects on the board, and
  101. // _solnNumToPlace[k] contains the Place in _solution that
  102. // contains sequence number k. Check that all numbers from
  103. // 1 - last appear; else throw IllegalArgumentException (see
  104. // badArgs utility).
  105.  
  106.  
  107. }
  108.  
  109. // FIXME: For each Sq object on the board, set its _successors and
  110. // _predecessor lists to the lists of locations of all cells
  111. // that it might connect to (i.e., all cells that are a queen
  112. // move away in the direction of its arrow, and of all cells
  113. // that might connect to it.
  114.  
  115. _unconnected = last - 1;
  116. }
  117.  
  118. /** Initializes a copy of MODEL. */
  119. Model(Model model) {
  120. _width = model.width(); _height = model.height();
  121. _unconnected = model._unconnected;
  122. _solnNumToPlace = model._solnNumToPlace;
  123. _solution = model._solution;
  124. _usedGroups.addAll(model._usedGroups);
  125. _allSuccessors = model._allSuccessors;
  126.  
  127. // FIXME: Initialize _board and _allSquares to contain copies of the
  128. // the Sq objects in MODEL other than their _successor,
  129. // _predecessor, and _head fields (which can't necessarily be
  130. // set until all the necessary Sq objects are first created.)
  131.  
  132. // FIXME: Fill in the _successor, _predecessor, and _head fields of the
  133. // copied Sq objects.
  134. }
  135.  
  136. /** Returns the width (number of columns of cells) of the board. */
  137. final int width() {
  138. return _width;
  139. }
  140.  
  141. /** Returns the height (number of rows of cells) of the board. */
  142. final int height() {
  143. return _height;
  144. }
  145.  
  146. /** Returns the number of cells (and thus, the sequence number of the
  147. * final cell). */
  148. final int size() {
  149. return _width * _height;
  150. }
  151.  
  152. /** Returns true iff (X, Y) is a valid cell location. */
  153. final boolean isCell(int x, int y) {
  154. return 0 <= x && x < width() && 0 <= y && y < height();
  155. }
  156.  
  157. /** Returns true iff P is a valid cell location. */
  158. final boolean isCell(Place p) {
  159. return isCell(p.x, p.y);
  160. }
  161.  
  162. /** Returns all cell locations that are a queen move from (X, Y)
  163. * in direction DIR, or all queen moves in any direction if DIR = 0. */
  164. final PlaceList allSuccessors(int x, int y, int dir) {
  165. return _allSuccessors[x][y][dir];
  166. }
  167.  
  168. /** Returns all cell locations that are a queen move from P in direction
  169. * DIR, or all queen moves in any direction if DIR = 0. */
  170. final PlaceList allSuccessors(Place p, int dir) {
  171. return _allSuccessors[p.x][p.y][dir];
  172. }
  173.  
  174. /** Initialize MODEL to an empty WIDTH x HEIGHT board with a null solution.
  175. */
  176. void init(int width, int height) {
  177. if (width <= 0 || width * height < 2) {
  178. throw badArgs("must have at least 2 squares");
  179. }
  180. _width = width; _height = height;
  181. _unconnected = _width * _height - 1;
  182. _solution = null;
  183. _usedGroups.clear();
  184. // FIXME: Initialize _board to contain nulls and clear all objects from
  185. // _allSquares.
  186.  
  187. // FIXME: Initialize _allSuccSquares so that _allSuccSquares[x][y][dir]
  188. // is a list of all the Places on the board that are a queen
  189. // move in direction DIR from (x, y) and _allSuccSquares[x][y][0]
  190. // is a list of all Places that are one queen move from in
  191. // direction from (x, y).
  192. }
  193.  
  194. /** Remove all connections and non-fixed sequence numbers. */
  195. void restart() {
  196. for (Sq sq : this) {
  197. sq.disconnect();
  198. }
  199. assert _unconnected == _width * _height - 1;
  200. }
  201.  
  202. /** Return the number array that solves the current puzzle (the argument
  203. * the constructor. The result must not be subsequently modified. */
  204. final int[][] solution() {
  205. return _solution;
  206. }
  207.  
  208. /** Return the position of the cell with sequence number N in my
  209. * solution. */
  210. Place solnNumToPlace(int n) {
  211. return _solnNumToPlace[n];
  212. }
  213.  
  214. /** Return the current number of unconnected cells. */
  215. final int unconnected() {
  216. return _unconnected;
  217. }
  218.  
  219. /** Returns true iff the puzzle is solved. */
  220. final boolean solved() {
  221. return _unconnected == 0;
  222. }
  223.  
  224. /** Return the cell at (X, Y). */
  225. final Sq get(int x, int y) {
  226. return _board[x][y];
  227. }
  228.  
  229. /** Return the cell at P. */
  230. final Sq get(Place p) {
  231. return p == null ? null : _board[p.x][p.y];
  232. }
  233.  
  234. /** Return the cell at the same position as SQ (generally from another
  235. * board), or null if SQ is null. */
  236. final Sq get(Sq sq) {
  237. return sq == null ? null : _board[sq.x][sq.y];
  238. }
  239.  
  240. /** Connect all numbered cells with successive numbers that as yet are
  241. * unconnected and are separated by a queen move. Returns true iff
  242. * any changes were made. */
  243. boolean autoconnect() {
  244. return false; // FIXME
  245. }
  246.  
  247. /** Sets the numbers in my squares to the solution from which I was
  248. * last initialized by the constructor. */
  249. void solve() {
  250. // FIXME
  251. _unconnected = 0;
  252. }
  253.  
  254. /** Return the direction from cell (X, Y) in the solution to its
  255. * successor, or 0 if it has none. */
  256. private int arrowDirection(int x, int y) {
  257. int seq0 = _solution[x][y];
  258. // FIXME
  259. return 0;
  260. }
  261.  
  262. /** Return a new, currently unused group number > 0. Selects the
  263. * lowest not currently in used. */
  264. private int newGroup() {
  265. for (int i = 1; true; i += 1) {
  266. if (_usedGroups.add(i)) {
  267. return i;
  268. }
  269. }
  270. }
  271.  
  272. /** Indicate that group number GROUP is no longer in use. */
  273. private void releaseGroup(int group) {
  274. _usedGroups.remove(group);
  275. }
  276.  
  277. /** Combine the groups G1 and G2, returning the resulting group. Assumes
  278. * G1 != 0 != G2 and G1 != G2. */
  279. private int joinGroups(int g1, int g2) {
  280. assert (g1 != 0 && g2 != 0);
  281. if (g1 == -1 && g2 == -1) {
  282. return newGroup();
  283. } else if (g1 == -1) {
  284. return g2;
  285. } else if (g2 == -1) {
  286. return g1;
  287. } else if (g1 < g2) {
  288. releaseGroup(g2);
  289. return g1;
  290. } else {
  291. releaseGroup(g1);
  292. return g2;
  293. }
  294. }
  295.  
  296. @Override
  297. public Iterator<Sq> iterator() {
  298. return _allSquares.iterator();
  299. }
  300.  
  301. @Override
  302. public String toString() {
  303. String hline;
  304. hline = "+";
  305. for (int x = 0; x < _width; x += 1) {
  306. hline += "------+";
  307. }
  308.  
  309. Formatter out = new Formatter();
  310. for (int y = _height - 1; y >= 0; y -= 1) {
  311. out.format("%s%n", hline);
  312. out.format("|");
  313. for (int x = 0; x < _width; x += 1) {
  314. Sq sq = get(x, y);
  315. if (sq.hasFixedNum()) {
  316. out.format("+%-5s|", sq.seqText());
  317. } else {
  318. out.format("%-6s|", sq.seqText());
  319. }
  320. }
  321. out.format("%n|");
  322. for (int x = 0; x < _width; x += 1) {
  323. Sq sq = get(x, y);
  324. if (sq.predecessor() == null && sq.sequenceNum() != 1) {
  325. out.format(".");
  326. } else {
  327. out.format(" ");
  328. }
  329. if (sq.successor() == null
  330. && sq.sequenceNum() != size()) {
  331. out.format("o ");
  332. } else {
  333. out.format(" ");
  334. }
  335. out.format("%s |", ARROWS[sq.direction()]);
  336. }
  337. out.format("%n");
  338. }
  339. out.format(hline);
  340. return out.toString();
  341. }
  342.  
  343. @Override
  344. public boolean equals(Object obj) {
  345. Model model = (Model) obj;
  346. return (_unconnected == model._unconnected
  347. && _width == model._width && _height == model._height
  348. && Arrays.deepEquals(_solution, model._solution)
  349. && Arrays.deepEquals(_board, model._board));
  350. }
  351.  
  352. @Override
  353. public int hashCode() {
  354. return Arrays.deepHashCode(_solution) * Arrays.deepHashCode(_board);
  355. }
  356.  
  357. /** Represents a square on the board. */
  358. final class Sq {
  359. /** A square at (X0, Y0) with arrow in direction DIR (0 if not
  360. * set), group number GROUP, sequence number SEQUENCENUM (0
  361. * if none initially assigned), which is fixed iff FIXED. */
  362. Sq(int x0, int y0, int sequenceNum, boolean fixed, int dir, int group) {
  363. x = x0; y = y0;
  364. pl = pl(x, y);
  365. _hasFixedNum = fixed;
  366. _sequenceNum = sequenceNum;
  367. _dir = dir;
  368. _head = this;
  369. _group = group;
  370. }
  371.  
  372. /** A copy of OTHER, excluding head, successor, and predecessor. */
  373. Sq(Sq other) {
  374. this(other.x, other.y, other._sequenceNum, other._hasFixedNum,
  375. other._dir, other._group);
  376. _successor = _predecessor = null;
  377. _head = this;
  378. _successors = other._successors;
  379. _predecessors = other._predecessors;
  380. }
  381.  
  382. /** Return my current sequence number, or 0 if none assigned. */
  383. int sequenceNum() {
  384. return _sequenceNum;
  385. }
  386.  
  387. /** Fix my current sequence number at N>0. It is an error if my number
  388. * is not initially 0 or N. */
  389. void setFixedNum(int n) {
  390. if (n == 0 || (_sequenceNum != 0 && _sequenceNum != n)) {
  391. throw badArgs("sequence number may not be fixed");
  392. }
  393. _hasFixedNum = true;
  394. if (_sequenceNum == n) {
  395. return;
  396. } else {
  397. releaseGroup(_head._group);
  398. }
  399. _sequenceNum = n;
  400. for (Sq sq = this; sq._successor != null; sq = sq._successor) {
  401. sq._successor._sequenceNum = sq._sequenceNum + 1;
  402. }
  403. for (Sq sq = this; sq._predecessor != null; sq = sq._predecessor) {
  404. sq._predecessor._sequenceNum = sq._sequenceNum - 1;
  405. }
  406. }
  407.  
  408. /** Unfix my sequence number if it is currently fixed; otherwise do
  409. * nothing. */
  410. void unfixNum() {
  411. Sq next = _successor, pred = _predecessor;
  412. _hasFixedNum = false;
  413. disconnect();
  414. if (pred != null) {
  415. pred.disconnect();
  416. }
  417. _sequenceNum = 0;
  418. if (next != null) {
  419. connect(next);
  420. }
  421. if (pred != null) {
  422. pred.connect(this);
  423. }
  424. }
  425.  
  426. /** Return true iff my sequence number is fixed. */
  427. boolean hasFixedNum() {
  428. return _hasFixedNum;
  429. }
  430.  
  431. /** Returns direction of my arrow (0 if no arrow). */
  432. int direction() {
  433. return _dir;
  434. }
  435.  
  436. /** Return my current predecessor. */
  437. Sq predecessor() {
  438. return _predecessor;
  439. }
  440.  
  441. /** Return my current successor. */
  442. Sq successor() {
  443. return _successor;
  444. }
  445.  
  446. /** Return the head of the connected sequence I am currently in. */
  447. Sq head() {
  448. return _head;
  449. }
  450.  
  451. /** Return the group number of my group. It is 0 if I am numbered, and
  452. * -1 if I am alone in my group. */
  453. int group() {
  454. if (_sequenceNum != 0) {
  455. return 0;
  456. } else {
  457. return _head._group;
  458. }
  459. }
  460.  
  461. /** Size of alphabet. */
  462. static final int ALPHA_SIZE = 26;
  463.  
  464. /** Return a textual representation of my sequence number or
  465. * group/position. */
  466. String seqText() {
  467. if (_sequenceNum != 0) {
  468. return String.format("%d", _sequenceNum);
  469. }
  470. int g = group() - 1;
  471. if (g < 0) {
  472. return "";
  473. }
  474.  
  475. String groupName =
  476. String.format("%s%s",
  477. g < ALPHA_SIZE ? ""
  478. : Character.toString((char) (g / ALPHA_SIZE
  479. + 'a')),
  480. Character.toString((char) (g % ALPHA_SIZE
  481. + 'a')));
  482. if (this == _head) {
  483. return groupName;
  484. }
  485. int n;
  486. n = 0;
  487. for (Sq p = this; p != _head; p = p._predecessor) {
  488. n += 1;
  489. }
  490. return String.format("%s%+d", groupName, n);
  491. }
  492.  
  493. /** Return locations of my potential successors. */
  494. PlaceList successors() {
  495. return _successors;
  496. }
  497.  
  498. /** Return locations of my potential predecessors. */
  499. PlaceList predecessors() {
  500. return _predecessors;
  501. }
  502.  
  503. /** Returns true iff I may be connected to cell S1, that is:
  504. * + S1 is in the correct direction from me.
  505. * + S1 does not have a current predecessor, and I do not have a
  506. * current successor.
  507. * + If S1 and I both have sequence numbers, then mine is
  508. * sequenceNum() == S1.sequenceNum() - 1.
  509. * + If neither S1 nor I have sequence numbers, then we are not part
  510. * of the same connected sequence.
  511. */
  512. boolean connectable(Sq s1) {
  513. // FIXME
  514. return true;
  515. }
  516.  
  517. /** Connect me to S1, if we are connectable; otherwise do nothing.
  518. * Returns true iff we were connectable. Assumes S1 is in the proper
  519. * arrow direction from me. */
  520. boolean connect(Sq s1) {
  521. if (!connectable(s1)) {
  522. return false;
  523. }
  524. int sgroup = s1.group();
  525.  
  526. _unconnected -= 1;
  527.  
  528. // FIXME: Connect me to my successor:
  529. // + Set my _successor field and S1's _predecessor field.
  530. // + If I have a number, number all my successors
  531. // accordingly (if needed).
  532. // + If S1 is numbered, number me and my predecessors
  533. // accordingly (if needed).
  534. // + Set the _head fields of my successors to my _head.
  535. // + If either of this or S1 used to be unnumbered and is
  536. // now numbered, release its group of whichever was
  537. // unnumbered, so that it can be reused.
  538. // + If both this and S1 are unnumbered, set the group of
  539. // my head to the result of joining the two groups.
  540.  
  541. return true;
  542. }
  543.  
  544. /** Disconnect me from my current successor, if any. */
  545. void disconnect() {
  546. Sq next = _successor;
  547. if (next == null) {
  548. return;
  549. }
  550. _unconnected += 1;
  551. next._predecessor = _successor = null;
  552. if (_sequenceNum == 0) {
  553. // FIXME: If both this and next are now one-element groups,
  554. // release their former group and set both group
  555. // numbers to -1.
  556. // Otherwise, if either is now a one-element group, set
  557. // its group number to -1 without releasing the group
  558. // number.
  559. // Otherwise, the group has been split into two multi-
  560. // element groups. Create a new group for next.
  561. } else {
  562. // FIXME: If neither this nor any square in its group that
  563. // precedes it has a fixed sequence number, set all
  564. // their sequence numbers to 0 and create a new group
  565. // for them if this has a current predecessor (other
  566. // set group to -1).
  567. // FIXME: If neither next nor any square in its group that
  568. // follows it has a fixed sequence number, set all
  569. // their sequence numbers to 0 and create a new
  570. // group for them if next has a current successor
  571. // (otherwise set next's group to -1.)
  572. }
  573. // FIXME: Set the _head of next and all squares in its group to
  574. // next.
  575. }
  576.  
  577. @Override
  578. public boolean equals(Object obj) {
  579. Sq sq = (Sq) obj;
  580. return sq != null
  581. && pl == sq.pl
  582. && _hasFixedNum == sq._hasFixedNum
  583. && _sequenceNum == sq._sequenceNum
  584. && _dir == sq._dir
  585. && (_predecessor == null) == (sq._predecessor == null)
  586. && (_predecessor == null
  587. || _predecessor.pl == sq._predecessor.pl)
  588. && (_successor == null || _successor.pl == sq._successor.pl);
  589. }
  590.  
  591. @Override
  592. public int hashCode() {
  593. return (x + 1) * (y + 1) * (_dir + 1)
  594. * (_hasFixedNum ? 3 : 1) * (_sequenceNum + 1);
  595. }
  596.  
  597. /** The coordinates of this square in the board. */
  598. protected final int x, y;
  599. /** My coordinates as a Place. */
  600. protected final Place pl;
  601. /** The first in the currently connected sequence of cells ("group")
  602. * that includes this one. */
  603. private Sq _head;
  604. /** If _head == this, then the group number of the group of which this
  605. * is a member. Numbered sequences have a group number of 0,
  606. * regardless of the value of _group. Unnumbered one-member groups
  607. * have a group number of -1. */
  608. private int _group;
  609. /** True iff assigned a fixed sequence number. */
  610. private boolean _hasFixedNum;
  611. /** The current imputed or fixed sequence number,
  612. * numbering from 1, or 0 if there currently is none. */
  613. private int _sequenceNum;
  614. /** The arrow direction. The possible values are 0 (for unset),
  615. * 1 for northeast, 2 for east, 3 for southeast, 4 for south,
  616. * 5 for southwest, 6 for west, 7 for northwest, and 8 for north. */
  617. private int _dir;
  618. /** The current predecessor of this square, or null if there is
  619. * currently no predecessor. */
  620. private Sq _predecessor;
  621. /** The current successor of this square, or null if there is
  622. * currently no successor. */
  623. private Sq _successor;
  624. /** Locations of my possible predecessors. */
  625. private PlaceList _predecessors;
  626. /** Locations of my possible successor. */
  627. private PlaceList _successors;
  628. }
  629.  
  630. /** ASCII denotations of arrows, indexed by direction. */
  631. private static final String[] ARROWS = {
  632. " *", "NE", "E ", "SE", "S ", "SW", "W ", "NW", "N "
  633. };
  634.  
  635. /** Number of squares that haven't been connected. */
  636. private int _unconnected;
  637. /** Dimensions of board. */
  638. private int _width, _height;
  639. /** Contents of board, indexed by position. */
  640. private Sq[][] _board;
  641. /** Contents of board as a sequence of squares for convenient iteration. */
  642. private ArrayList<Sq> _allSquares = new ArrayList<>();
  643. /** _allSuccessors[x][y][dir] is a sequence of all queen moves possible
  644. * on the board of in direction dir from (x, y). If dir == 0,
  645. * this is all places that are a queen move from (x, y) in any
  646. * direction. */
  647. private PlaceList[][][] _allSuccessors;
  648. /** The solution from which this Model was built. */
  649. private int[][] _solution;
  650. /** Inverse mapping from sequence numbers to board positions. */
  651. private Place[] _solnNumToPlace;
  652. /** The set of positive group numbers currently in use. */
  653. private HashSet<Integer> _usedGroups = new HashSet<>();
  654.  
  655. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement