Advertisement
markvigliotti

Untitled

Jan 26th, 2020
235
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 20.00 KB | None | 0 0
  1. import java.awt.Color;
  2. import javalib.funworld.World;
  3. import javalib.funworld.WorldScene;
  4. import javalib.worldimages.CircleImage;
  5. import javalib.worldimages.OutlineMode;
  6. import javalib.worldimages.Posn;
  7. import javalib.worldimages.RectangleImage;
  8. import javalib.worldimages.WorldImage;
  9. import tester.Tester;
  10.  
  11. class Utility {
  12.  
  13. // pins the given Posn to the nearest tile center
  14. Posn pin(Posn p) {
  15. int dx = p.x % 10;
  16. int x = p.x - dx + 5;
  17. int dy = p.y % 10;
  18. int y = p.y - dy + 5;
  19. return new Posn(x, y);
  20. }
  21.  
  22. }
  23.  
  24. class CentipedeWorld extends World {
  25.  
  26. /* TEMPLATE
  27. * Fields:
  28. * ... this.width ... -- int
  29. * ... this.height ... -- int
  30. * ... this.listItems ... -- ILoItem
  31. * Methods:
  32. * ... this.makeScene() ... -- WorldScene
  33. * ... this.onTick() ... -- CentipedeWorld
  34. * ... this.onMouseClicked(Posn p, String buttonName) ... -- CentipedeWorld
  35. * Methods of Fields:
  36. * ... this.listItems.drawItems() ... -- WorldScene
  37. * ... this.listItems.isOccupied(Posn p) ... -- boolean
  38. * ... this.listItems.remove(Posn p) ... -- ILoItem
  39. */
  40.  
  41. // the number of pixels in the x- (width) and y- (height) directions
  42. int width, height;
  43.  
  44. // the list of IItem in the world
  45. ILoItem listItems;
  46.  
  47. // constructor
  48. CentipedeWorld(int width, int height, ILoItem listItems) {
  49. // throws an exception if the scene bounds aren't within 50 and 800 for width,
  50. // and 100 and 800 for height
  51. if (width <= 50 || width >= 800 || height <= 100 || height >= 800) {
  52. throw new IllegalArgumentException("The game scene size is either too small or too large. "
  53. + "Please keep game scene limits between 50 and 800 for width, "
  54. + "and 100 and 800 for height.");
  55. }
  56. // initializes the fields
  57. this.width = width;
  58. this.height = height;
  59. this.listItems = listItems;
  60. }
  61.  
  62. // convenience constructor that just takes in the scene dimensions and uses an MtLoItem
  63. CentipedeWorld(int width, int height) {
  64. this(width, height, new MtLoItem());
  65. }
  66.  
  67. // displays the current state of the CentipedeWorld
  68. public WorldScene makeScene() {
  69. return this.listItems.drawItems(this.getEmptyScene());
  70. }
  71.  
  72. // updates the CentipedeWorld
  73. public CentipedeWorld onTick() {
  74. return this;
  75. }
  76.  
  77. // operations for when the left mouse button is clicked
  78. public CentipedeWorld onMouseClicked(Posn p, String buttonName) {
  79. Utility util = new Utility();
  80. Posn pinned = util.pin(p);
  81.  
  82. // removes the Item from the current ILoItem if the left mouse is clicked and there is an
  83. // IItem on the current tile
  84. if (buttonName.equals("LeftButton") && this.listItems.isOccupied(pinned)) {
  85. return new CentipedeWorld(this.width, this.height, this.listItems.remove(pinned));
  86.  
  87. // adds a new Dandelion if the left mouse is clicked and there is no IItem on the current
  88. // tile
  89. } else if (buttonName.equals("LeftButton") && !this.listItems.isOccupied(pinned)) {
  90. return new CentipedeWorld(this.width, this.height,
  91. new ConsLoItem(new Dandelion(pinned), this.listItems));
  92.  
  93. // adds a new Rock if the right mouse is clicked and there is no IItem on the current tile
  94. } else if (buttonName.equals("RightButton") && !this.listItems.isOccupied(pinned)) {
  95. return new CentipedeWorld(this.width, this.height,
  96. new ConsLoItem(new Rock(util.pin(pinned)), this.listItems));
  97.  
  98. // returns the same list if nothing is clicked
  99. } else {
  100. return this;
  101. }
  102.  
  103. }
  104.  
  105. }
  106.  
  107. interface ILoItem {
  108. // draws this list ILoItem onto the scene
  109. WorldScene drawItems(WorldScene bgScene);
  110.  
  111. // is the given Posn occupied by an IItem in this ILoItem?
  112. boolean isOccupied(Posn p);
  113.  
  114. // removes the IItem with the given Posn from this ILoItem
  115. ILoItem remove(Posn p);
  116. }
  117.  
  118. class ConsLoItem implements ILoItem {
  119.  
  120. /* TEMPLATE
  121. * Fields:
  122. * ... this.first ... -- IItem
  123. * ... this.rest ... -- ILoItem
  124. * Methods:
  125. * ... this.drawItems(WorldScene bgScene) ... -- WorldScene
  126. * ... this.isOccupied(Posn p) ... -- boolean
  127. * ... this.remove(Posn p) ... -- ILoItem
  128. * Methods of Fields:
  129. * ... this.first.drawItem(WorldScene bgScene) ... -- WorldScene
  130. * ... this.first.isOccupied(Posn p) ... -- boolean
  131. * ... this.rest.drawItems(WorldScene bgScene) ... -- WorldScene
  132. * ... this.rest.isOccupied(Posn p) ... -- boolean
  133. * ... this.rest.remove(Posn p) ... -- ILoItem
  134. */
  135.  
  136. IItem first;
  137. ILoItem rest;
  138.  
  139. // constructor
  140. ConsLoItem(IItem first, ILoItem rest) {
  141. this.first = first;
  142. this.rest = rest;
  143. }
  144.  
  145. // draws this ILoItem onto the given scene
  146. public WorldScene drawItems(WorldScene bgScene) {
  147.  
  148. /* TEMPLATE
  149. * Everything in ConsLoItem template, including:
  150. * Parameters:
  151. * ... bgScene ... -- WorldScene
  152. * Fields of Parameters:
  153. * ... bgScene.width ... -- int
  154. * ... bgScene.height ... -- int
  155. * Methods of Parameters:
  156. * ... bgScene.placeImageXY(WorldImage image, int x, int y) ... -- WorldScene
  157. */
  158.  
  159. return this.first.drawItem(this.rest.drawItems(bgScene));
  160. }
  161.  
  162. public boolean isOccupied(Posn p) {
  163.  
  164. /* TEMPLATE
  165. * Everything in ConsLoItem template, including:
  166. * Parameters:
  167. * ... p ... -- Posn
  168. * Fields of Parameters:
  169. * ... p.x ... -- int
  170. * ... p.y ... -- int
  171. * Methods of Parameters:
  172. */
  173.  
  174. return this.first.isOccupied(p) || this.rest.isOccupied(p);
  175. }
  176.  
  177. public ILoItem remove(Posn p) {
  178.  
  179. /* TEMPLATE
  180. * Everything in ConsLoItem template, including:
  181. * Parameters:
  182. * ... p ... -- Posn
  183. * Fields of Parameters:
  184. * ... p.x ... -- int
  185. * ... p.y ... -- int
  186. * Methods of Parameters:
  187. */
  188.  
  189. if (this.first.isOccupied(p)) {
  190. return this.rest;
  191. } else {
  192. return new ConsLoItem(this.first, this.rest.remove(p));
  193. }
  194. }
  195.  
  196. }
  197.  
  198. class MtLoItem implements ILoItem {
  199.  
  200. /* TEMPLATE
  201. * Fields:
  202. * Methods:
  203. * ... this.drawItems(WorldScene bgScene) ... -- WorldScene
  204. * ... this.isOccupied(Posn p) ... -- boolean
  205. * Methods of Fields:
  206. */
  207.  
  208. // draws the background image onto the given scene
  209. public WorldScene drawItems(WorldScene bgScene) {
  210.  
  211. /* TEMPLATE
  212. * Everything in MtLoItem template, including:
  213. * Parameters:
  214. * ... bgScene ... -- WorldScene
  215. * Fields of Parameters:
  216. * ... bgScene.width ... -- int
  217. * ... bgScene.height ... -- int
  218. * Methods of Parameters:
  219. * ... bgScene.placeImageXY(WorldImage image, int x, int y) ... -- WorldScene
  220. */
  221.  
  222. WorldImage bgImage =
  223. new RectangleImage(bgScene.width, bgScene.height, OutlineMode.SOLID, Color.GREEN);
  224. return bgScene.placeImageXY(bgImage, bgScene.width / 2, bgScene.height / 2);
  225. }
  226.  
  227. // return false since this list does not have an item
  228. public boolean isOccupied(Posn p) {
  229.  
  230. /* TEMPLATE
  231. * Everything in MtLoItem template, including:
  232. * Parameters:
  233. * ... p ... -- Posn
  234. * Fields of Parameters:
  235. * ... p.x ... -- int
  236. * ... p.y ... -- int
  237. * Methods of Parameters:
  238. */
  239.  
  240. return false;
  241. }
  242.  
  243. // return this since empty can't be removed
  244. public ILoItem remove(Posn p) {
  245.  
  246. /* TEMPLATE
  247. * Everything in ConsLoItem template, including:
  248. * Parameters:
  249. * ... p ... -- Posn
  250. * Fields of Parameters:
  251. * ... p.x ... -- int
  252. * ... p.y ... -- int
  253. * Methods of Parameters:
  254. */
  255.  
  256. return this;
  257. }
  258.  
  259. }
  260.  
  261. interface IItem {
  262. // draws this IItem onto the given scene
  263. WorldScene drawItem(WorldScene bgScene);
  264.  
  265. // is the given Posn equal to this IItem's Posn?
  266. boolean isOccupied(Posn p);
  267. }
  268.  
  269. class Rock implements IItem {
  270.  
  271. /* TEMPLATE
  272. * Fields:
  273. * ... this.p ... -- Posn
  274. * ... this.p.x ... -- int
  275. * ... this.p.y ... -- int
  276. * Methods:
  277. * ... this.drawItem(WorldScene bgScene) ... -- WorldScene
  278. * ... this.isOccupied(Posn p) ... -- boolean
  279. * Methods on Fields:
  280. */
  281.  
  282. Posn p;
  283.  
  284. // constructor
  285. Rock(Posn p) {
  286. this.p = p;
  287. }
  288.  
  289. // returns a scene with this Rock placed at its position on top of the given scene
  290. public WorldScene drawItem(WorldScene bgScene) {
  291.  
  292. /* TEMPLATE
  293. * Everything in Rock template, including:
  294. * Parameters:
  295. * ... bgScene ... -- WorldScene
  296. * Fields of Parameters:
  297. * ... bgScene.width ... -- int
  298. * ... bgScene.height ... -- int
  299. * Methods of Parameters:
  300. * ... bgScene.placeImageXY(WorldImage image, int x, int y) ... -- WorldScene
  301. */
  302.  
  303. WorldImage rockImage = new CircleImage(5, OutlineMode.SOLID, Color.GRAY);
  304. return bgScene.placeImageXY(rockImage, this.p.x, this.p.y);
  305. }
  306.  
  307. public boolean isOccupied(Posn p) {
  308.  
  309. /* TEMPLATE
  310. * Everything in Rock template, including:
  311. * Parameters:
  312. * ... p ... -- Posn
  313. * Fields of Parameters:
  314. * ... p.x ... -- int
  315. * ... p.y ... -- int
  316. * Methods of Parameters:
  317. */
  318.  
  319. return this.p.x == p.x && this.p.y == p.y;
  320. }
  321.  
  322. }
  323.  
  324. class Dandelion implements IItem {
  325.  
  326. /* TEMPLATE
  327. * Fields:
  328. * ... this.p ... -- Posn
  329. * ... this.p.x ... -- int
  330. * ... this.p.y ... -- int
  331. * Methods:
  332. * ... this.drawItem(WorldScene bgScene) ... -- WorldScene
  333. * ... this.isOccupied(Posn p) ... -- boolean
  334. * Methods on Fields:
  335. */
  336.  
  337. Posn p;
  338.  
  339. // constructor
  340. Dandelion(Posn p) {
  341. this.p = p;
  342. }
  343.  
  344. //returns a scene with this Dandelion placed at its position on top of the given scene
  345. public WorldScene drawItem(WorldScene bgScene) {
  346.  
  347. /* TEMPLATE
  348. * Everything in Dandelion template, including:
  349. * Parameters:
  350. * ... bgScene ... -- WorldScene
  351. * Fields of Parameters:
  352. * ... bgScene.width ... -- int
  353. * ... bgScene.height ... -- int
  354. * Methods of Parameters:
  355. * ... bgScene.placeImageXY(WorldImage image, int x, int y) ... -- WorldScene
  356. */
  357.  
  358. WorldImage dandelionImage = new CircleImage(5, OutlineMode.SOLID, Color.YELLOW);
  359. return bgScene.placeImageXY(dandelionImage, this.p.x, this.p.y);
  360. }
  361.  
  362. public boolean isOccupied(Posn p) {
  363.  
  364. /* TEMPLATE
  365. * Everything in Rock template, including:
  366. * Parameters:
  367. * ... p ... -- Posn
  368. * Fields of Parameters:
  369. * ... p.x ... -- int
  370. * ... p.y ... -- int
  371. * Methods of Parameters:
  372. */
  373.  
  374. return this.p.x == p.x && this.p.y == p.y;
  375. }
  376.  
  377. }
  378.  
  379. class ExamplesGameWorld {
  380.  
  381. // pixel dimensions of a CentipedeWorld
  382. int cWidth = 300;
  383. int cHeight = 500;
  384.  
  385. // a CentipedeWorld
  386. CentipedeWorld c = new CentipedeWorld(this.cWidth, this.cHeight);
  387. // the tick rate for the CentipedeWorld
  388. double TICK_RATE = 1;
  389.  
  390. //the empty background scene
  391. WorldScene bgScene = c.getEmptyScene();
  392. // the green grass background image
  393. WorldImage bgImage =
  394. new RectangleImage(bgScene.width, bgScene.height, OutlineMode.SOLID, Color.GREEN);
  395. // the background scene with green grass
  396. WorldScene bgGrassScene = this.bgScene.placeImageXY(
  397. this.bgImage, this.bgScene.width / 2, this.bgScene.height / 2);
  398.  
  399. // image of a Dandelion
  400. WorldImage dandelionImage = new CircleImage(5, OutlineMode.SOLID, Color.YELLOW);
  401. // image of a Rock
  402. WorldImage rockImage = new CircleImage(5, OutlineMode.SOLID, Color.GRAY);
  403.  
  404. // Dandelions at (5, 5) and (15, 15)
  405. IItem dand55 = new Dandelion(new Posn(5, 5));
  406. IItem dand1515 = new Dandelion(new Posn(15, 15));
  407. // Rocks at (5, 15) and (15, 5)
  408. IItem rock515 = new Rock(new Posn(5, 15));
  409. IItem rock155 = new Rock(new Posn(15, 5));
  410.  
  411. //a list of one Dandelion
  412. ILoItem oneDand = new ConsLoItem(this.dand55, new MtLoItem());
  413. // a list of two Dandelions
  414. ILoItem twoDand = new ConsLoItem(this.dand1515, this.oneDand);
  415. // a list of one Rock
  416. ILoItem oneRock = new ConsLoItem(this.rock515, new MtLoItem());
  417. // a list of two Rocks
  418. ILoItem twoRock = new ConsLoItem(this.rock155, this.oneRock);
  419. // a list of one Dandelion and one Rock
  420. ILoItem dandRock = new ConsLoItem(
  421. this.dand55, new ConsLoItem(this.rock515, new MtLoItem()));
  422.  
  423. // Centipede worlds
  424. CentipedeWorld cDandelion = new CentipedeWorld(this.cWidth, this.cHeight, this.oneDand);
  425. CentipedeWorld cRock = new CentipedeWorld(this.cWidth, this.cHeight, this.oneRock);
  426. CentipedeWorld cDandRock = new CentipedeWorld(this.cWidth, this.cHeight, this.dandRock);
  427.  
  428. boolean testCentipede(Tester t) {
  429. return
  430. // tests the game
  431. c.bigBang(this.c.width, this.c.height, this.TICK_RATE);
  432. }
  433.  
  434. // tests for the CentipedeWorldConstructor
  435. boolean testCentipedeWorldConstructor(Tester t) {
  436. Exception invalidRange = new IllegalArgumentException("The game scene size is either too "
  437. + "small or too large. Please keep game scene limits between 50 and 800 for width, and "
  438. + "100 and 800 for height.");
  439.  
  440. return
  441. // tests valid inputs for the CentipedeWorldConstructor
  442. t.checkExpect(this.c.width == this.cWidth, true)
  443. && t.checkExpect(this.c.height == this.cHeight, true)
  444.  
  445. // tests invalid inputs for the CentipedeWorldConstructor
  446. // tests if the width is too small
  447. && t.checkConstructorException(invalidRange, "CentipedeWorld", 2, 200)
  448. // tests if the width is too large
  449. && t.checkConstructorException(invalidRange, "CentipedeWorld", 1000, 200)
  450. // tests if the height is too small
  451. && t.checkConstructorException(invalidRange, "CentipedeWorld", 200, 2)
  452. // tests if the height is too large
  453. && t.checkConstructorException(invalidRange, "CentipedeWorld", 200, 1000);
  454. }
  455.  
  456. // tests for the makeScene method which draws the current ILoItem on the screen
  457. boolean testMakeScene(Tester t) {
  458. return
  459. // for a world with Dandelions and Rocks
  460. t.checkExpect(this.cDandRock.makeScene(), this.bgGrassScene.placeImageXY(
  461. this.dandelionImage, 5, 5).placeImageXY(this.rockImage, 5, 15));
  462. }
  463.  
  464. // tests for the onTick method which updates the world every tick
  465.  
  466. // tests for mouse click updates
  467. boolean testOnMouseClicked(Tester t) {
  468. return
  469. // tests for adding IItems at the correct position
  470. // tests if a new Dandelion is added on a left mouse click while the mouse is centered on
  471. // a tile
  472. t.checkExpect(c.onMouseClicked(new Posn(5, 5), "LeftButton"), this.cDandelion)
  473. // tests if a new Dandelion is added on a left mouse click to the nearest-by tile since the
  474. // mouse is not centered on a tile
  475. && t.checkExpect(c.onMouseClicked(new Posn(9, 9), "LeftButton"), this.cDandelion)
  476. // tests if a new Rock is added on a left mouse click while the mouse is centered on
  477. // a tile
  478. && t.checkExpect(c.onMouseClicked(new Posn(5, 15), "RightButton"), this.cRock)
  479. // tests if a new Rock is added on a left mouse click to the nearest-by tile since the
  480. // mouse is not centered on a tile
  481. && t.checkExpect(c.onMouseClicked(new Posn(9, 10), "RightButton"), this.cRock);
  482.  
  483. // TODO: tests for removing items
  484. }
  485.  
  486. // tests for drawItems
  487. boolean testDrawItems(Tester t) {
  488. return
  489. // tests if the background is drawn with an MtLoItem
  490. t.checkExpect(new MtLoItem().drawItems(this.bgScene), this.bgGrassScene)
  491. // tests if a ConsLoItem with one Dandelion is drawn onto the background
  492. && t.checkExpect(this.oneDand.drawItems(this.bgScene), this.bgGrassScene.placeImageXY(
  493. this.dandelionImage, 5, 5))
  494. // tests if a ConsLoItem with two Dandelions is drawn onto the background
  495. && t.checkExpect(this.twoDand.drawItems(this.bgScene), this.bgGrassScene.placeImageXY(
  496. this.dandelionImage, 15, 15).placeImageXY(this.dandelionImage, 5, 5))
  497. // tests if a ConsLoItem with one Rock is drawn onto the background
  498. && t.checkExpect(this.oneRock.drawItems(this.bgScene), this.bgGrassScene.placeImageXY(
  499. this.rockImage, 5, 15))
  500. // tests if a ConsLoItem with two Rocks is drawn onto the background
  501. && t.checkExpect(this.twoRock.drawItems(this.bgScene), this.bgGrassScene.placeImageXY(
  502. this.rockImage, 15, 5).placeImageXY(this.rockImage, 5, 15))
  503. // tests if a ConsLoItem with one Dandelion and one Rock is drawn onto the background
  504. && t.checkExpect(this.dandRock.drawItems(this.bgScene), this.bgGrassScene.placeImageXY(
  505. this.dandelionImage, 5, 5).placeImageXY(this.rockImage, 5, 15))
  506. // tests if the given scene is not the background
  507. && t.checkExpect(this.oneRock.drawItems(
  508. this.bgGrassScene.placeImageXY(this.rockImage, 15, 5)), this.bgGrassScene.placeImageXY(
  509. this.rockImage, 15, 5).placeImageXY(this.rockImage, 5, 15));
  510. }
  511.  
  512. // tests for isOccupied
  513. boolean testIsOccupied(Tester t) {
  514. return
  515. // for ILoItem
  516. // for an MtLoItem
  517. t.checkExpect(new MtLoItem().isOccupied(new Posn(5, 5)), false)
  518. // for a ConsLoItem where the given Posn is not occupied
  519. && t.checkExpect(this.dandRock.isOccupied(new Posn(45, 45)), false)
  520. // for a ConsLoItem where the given Posn is occupied by a Dandelion
  521. && t.checkExpect(this.dandRock.isOccupied(new Posn(5, 5)), true)
  522. // for a ConsLoItem where the given Posn is occupied by a Rock
  523. && t.checkExpect(this.dandRock.isOccupied(new Posn(5, 15)), true)
  524.  
  525. // for IItem
  526. // for a Dandelion at (5, 5) given a Posn at (15, 15) [x and y don't match]
  527. && t.checkExpect(this.dand55.isOccupied(new Posn(15, 15)), false)
  528. // for a Dandelion at (5, 5) given a Posn at (5, 15) [x matches, y doesn't]
  529. && t.checkExpect(this.dand55.isOccupied(new Posn(5, 15)), false)
  530. // for a Dandelion at (5, 5) given a Posn at (5, 15) [y matches, x doesn't]
  531. && t.checkExpect(this.dand55.isOccupied(new Posn(15, 5)), false)
  532. // for a Dandelion at (5, 5) given a Posn at (5, 5) [x and y both match]
  533. && t.checkExpect(this.dand55.isOccupied(new Posn(5, 5)), true)
  534.  
  535. // for a Rock at (15, 5) given a Posn at (5, 15) [x and y don't match]
  536. && t.checkExpect(this.rock155.isOccupied(new Posn(5, 15)), false)
  537. // for a Rock at (15, 5) given a Posn at (15, 5) [x matches, y doesn't]
  538. && t.checkExpect(this.rock155.isOccupied(new Posn(15, 5)), true)
  539. // for a Rock at (15, 5) given a Posn at (5, 5) [y matches, x doesn't]
  540. && t.checkExpect(this.rock155.isOccupied(new Posn(5, 15)), false)
  541. // for a Rock at (15, 5) given a Posn at (15, 5) [x and y both match]
  542. && t.checkExpect(this.rock155.isOccupied(new Posn(15, 55)), false);
  543. }
  544.  
  545. // tests for remove
  546. boolean testRemove(Tester t) {
  547. return
  548. // for an MtLoItem
  549. t.checkExpect(new MtLoItem().remove(new Posn(5, 5)), new MtLoItem())
  550.  
  551. // for ILoItems
  552. // for an ILoItem with one item
  553. && t.checkExpect(this.oneDand.remove(new Posn(5, 5)), new MtLoItem())
  554. // for an ILoItem with multiple items
  555. && t.checkExpect(this.dandRock.remove(new Posn(5, 15)), this.oneDand);
  556. }
  557.  
  558. // tests for drawItem
  559. boolean testDrawItem(Tester t) {
  560. return
  561. // tests if a Dandelion is drawn onto the background
  562. t.checkExpect(this.dand55.drawItem(this.bgScene), this.bgGrassScene.placeImageXY(
  563. this.dandelionImage, 5, 5))
  564. // tests if a Rock is drawn onto the background
  565. && t.checkExpect(this.rock515.drawItem(this.bgScene), this.bgGrassScene.placeImageXY(
  566. this.rockImage, 5, 15));
  567. }
  568.  
  569. // TESTS FOR THE Utility CLASS
  570.  
  571. // tests for the pin method
  572. boolean testPin(Tester t) {
  573. return
  574. // tests if the mouse location is currently on the center of a tile
  575. t.checkExpect(new Utility().pin(new Posn(5, 5)), new Posn(5, 5))
  576. // tests if the mouse location is not currently on the center of a tile
  577. && t.checkExpect(new Utility().pin(new Posn(9, 9)), new Posn(5, 5))
  578. && t.checkExpect(new Utility().pin(new Posn(19, 19)), new Posn(15, 15));
  579. }
  580. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement