fork download
  1. import java.awt.BorderLayout;
  2. import java.awt.Color;
  3. import java.awt.Dimension;
  4. import java.awt.Graphics;
  5. import java.awt.Graphics2D;
  6. import java.awt.RenderingHints;
  7. import java.awt.Toolkit;
  8. import java.awt.datatransfer.StringSelection;
  9. import java.awt.event.KeyAdapter;
  10. import java.awt.event.KeyEvent;
  11. import java.util.ArrayList;
  12. import java.util.Formatter;
  13. import java.util.List;
  14. import java.util.Random;
  15. import java.util.concurrent.atomic.AtomicBoolean;
  16. import java.util.concurrent.atomic.AtomicInteger;
  17.  
  18. import javax.swing.JFrame;
  19. import javax.swing.JLabel;
  20. import javax.swing.JPanel;
  21. import javax.swing.WindowConstants;
  22.  
  23. /**
  24.  * メインクラス
  25.  */
  26. public class Main {
  27.  
  28. /**
  29. * 1体の敵
  30. */
  31. static class Enemy {
  32.  
  33. /**
  34. * 敵の種別
  35. */
  36. enum Kind {
  37. /**
  38. * 敵V
  39. */
  40. V,
  41. /**
  42. * 敵H
  43. */
  44. H,
  45. /**
  46. * 敵L
  47. */
  48. L,
  49. /**
  50. * 敵R
  51. */
  52. R,
  53. /**
  54. * 敵Jが敵Lになっている状態
  55. */
  56. JL,
  57. /**
  58. * 敵Jが敵Rになっている状態
  59. */
  60. JR,
  61. }
  62.  
  63. /**
  64. * X座標
  65. */
  66. int x;
  67. /**
  68. * Y座標
  69. */
  70. int y;
  71. /**
  72. * 直前のX座標
  73. */
  74. int lastX;
  75. /**
  76. * 直前のY座標
  77. */
  78. int lastY;
  79. /**
  80. * 種類
  81. */
  82. Kind kind;
  83.  
  84. /**
  85. * 敵を初期化
  86. * @param x X座標
  87. * @param y Y座標
  88. * @param kind 種類
  89. */
  90. Enemy(final int x, final int y, final Kind kind) {
  91. this.x = x;
  92. this.y = y;
  93. this.lastX = x;
  94. this.lastY = y;
  95. this.kind = kind;
  96. }
  97.  
  98. /**
  99. * 移動
  100. * @param data データ
  101. * @param player プレイヤ
  102. * @param time 時刻
  103. */
  104. void move(final char[][] data, final Player player, final AtomicInteger time) {
  105. final char down = data[this.y + 1][this.x];
  106. final char left = data[this.y][this.x - 1];
  107. final char up = data[this.y - 1][this.x];
  108. final char right = data[this.y][this.x + 1];
  109. if (time.get() == 0) {
  110. /*
  111. * 時刻 t = 0 においては、初期位置の 下、左、上、右 の順で最初に進入可能なマスの方向に移動します。
  112. */
  113. if (down != '#') {
  114. this.y++;
  115. } else if (left != '#') {
  116. this.x--;
  117. } else if (up != '#') {
  118. this.y--;
  119. } else if (right != '#') {
  120. this.x++;
  121. } else {
  122. System.err.printf("動けません。t = %d, x = %d, y = %d, kind = %s\n", time.get(),
  123. this.x, this.y, this.kind);
  124. }
  125. } else {
  126. final int numOfWalls = (down == '#' ? 1 : 0) + (left == '#' ? 1 : 0)
  127. + (up == '#' ? 1 : 0) + (right == '#' ? 1 : 0);
  128. if (numOfWalls == 3) {
  129. /*
  130. * 行き止まりマスの場合、唯一進入可能な隣接マスに移動します。
  131. */
  132. if (down != '#') {
  133. this.y++;
  134. } else if (left != '#') {
  135. this.x--;
  136. } else if (up != '#') {
  137. this.y--;
  138. } else if (right != '#') {
  139. this.x++;
  140. } else {
  141. System.err.printf("動けません。t = %d, x = %d, y = %d, kind = %s\n", time.get(),
  142. this.x, this.y, this.kind);
  143. }
  144. } else if (numOfWalls == 2) {
  145. /*
  146. * 通路マスの場合、時刻 t-1 に居たマス以外の進入可能な隣接するマスに移動します。
  147. */
  148. if (down != '#' && this.lastY != this.y + 1) {
  149. this.y++;
  150. } else if (left != '#' && this.lastX != this.x - 1) {
  151. this.x--;
  152. } else if (up != '#' && this.lastY != this.y - 1) {
  153. this.y--;
  154. } else if (right != '#' && this.lastX != this.x + 1) {
  155. this.x++;
  156. } else {
  157. System.err.printf("動けません。t = %d, x = %d, y = %d, kind = %s\n", time.get(),
  158. this.x, this.y, this.kind);
  159. }
  160. } else if (numOfWalls == 1 || numOfWalls == 0) {
  161. /*
  162. * 交差点マスの場合は、敵の種別に応じたアルゴリズムに応じて移動方向を決定します。
  163. */
  164. final int dX = player.lastX - this.x;
  165. final int dY = player.lastY - this.y;
  166. final int signDX = (int) Math.signum(dX);
  167. final int signDY = (int) Math.signum(dY);
  168. switch (this.kind) {
  169. case V:
  170. /*
  171. * 敵から見た自機の相対位置を (dx, dy) と表すものとします。次のルールを上から順に適用し、最初に選ばれた方向に移動します。
  172. * 1. dy ≠ 0 でかつ dy の符号方向にあるマスが進入可能であれば、その方向に移動します。
  173. * 2. dx ≠ 0 でかつ dx の符号方向にあるマスが進入可能であれば、その方向に移動します。
  174. * 3. 現在位置の 下、左、上、右 の順で最初に進入可能なマスの方向に移動する。
  175. */
  176. if (dY != 0 && data[this.y + signDY][this.x] != '#') {
  177. this.y += signDY;
  178. } else if (dX != 0 && data[this.y][this.x + signDX] != '#') {
  179. this.x += signDX;
  180. } else {
  181. if (down != '#') {
  182. this.y++;
  183. } else if (left != '#') {
  184. this.x--;
  185. } else if (right != '#') {
  186. this.x++;
  187. } else if (up != '#') {
  188. this.y--;
  189. } else {
  190. System.err.printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
  191. time.get(), this.x, this.y, this.kind);
  192. }
  193. }
  194. break;
  195. case H:
  196. /*
  197. * 敵 V とほぼ同じです。唯一異なるのは 、進行方向を決めるルールのうち、
  198. * 最初の二つのルールの適用順序が入れ替わるところです。
  199. * すなわち、先に dx ≠ 0 のチェックを行ない、次に dy ≠ 0 のチェックを行います。
  200. */
  201. if (dX != 0 && data[this.y][this.x + signDX] != '#') {
  202. this.x += signDX;
  203. } else if (dY != 0 && data[this.y + signDY][this.x] != '#') {
  204. this.y += signDY;
  205. } else {
  206. if (down != '#') {
  207. this.y++;
  208. } else if (left != '#') {
  209. this.x--;
  210. } else if (right != '#') {
  211. this.x++;
  212. } else if (up != '#') {
  213. this.y--;
  214. } else {
  215. System.err.printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
  216. time.get(), this.x, this.y, this.kind);
  217. }
  218. }
  219. break;
  220. case L:
  221. case JL:
  222. /*
  223. * 現在位置への進入方向から見て相対的に 左、前、右 の順で最初に進入可能なマスの方向に移動します。
  224. */
  225. if (this.lastY + 1 == this.y) {
  226. if (right != '#') {
  227. this.x++;
  228. } else if (down != '#') {
  229. this.y++;
  230. } else if (left != '#') {
  231. this.x--;
  232. } else {
  233. System.err.printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
  234. time.get(), this.x, this.y, this.kind);
  235. }
  236. } else if (this.lastX - 1 == this.x) {
  237. if (down != '#') {
  238. this.y++;
  239. } else if (left != '#') {
  240. this.x--;
  241. } else if (up != '#') {
  242. this.y--;
  243. } else {
  244. System.err.printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
  245. time.get(), this.x, this.y, this.kind);
  246. }
  247. } else if (this.lastY - 1 == this.y) {
  248. if (left != '#') {
  249. this.x--;
  250. } else if (up != '#') {
  251. this.y--;
  252. } else if (right != '#') {
  253. this.x++;
  254. } else {
  255. System.err.printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
  256. time.get(), this.x, this.y, this.kind);
  257. }
  258. } else if (this.lastX + 1 == this.x) {
  259. if (up != '#') {
  260. this.y--;
  261. } else if (right != '#') {
  262. this.x++;
  263. } else if (down != '#') {
  264. this.y++;
  265. } else {
  266. System.err.printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
  267. time.get(), this.x, this.y, this.kind);
  268. }
  269. } else {
  270. System.err.printf("止まっていた?t = %d, x = %d, y = %d, kind = %s\n",
  271. time.get(), this.x, this.y, this.kind);
  272. }
  273. break;
  274. case R:
  275. case JR:
  276. /*
  277. * 現在位置への進入方向から見て相対的に 右、前、左 の順で最初に進入可能なマスの方向に移動します。
  278. */
  279. if (this.lastY + 1 == this.y) {
  280. if (left != '#') {
  281. this.x--;
  282. } else if (down != '#') {
  283. this.y++;
  284. } else if (right != '#') {
  285. this.x++;
  286. } else {
  287. System.err.printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
  288. time.get(), this.x, this.y, this.kind);
  289. }
  290. } else if (this.lastX - 1 == this.x) {
  291. if (up != '#') {
  292. this.y--;
  293. } else if (left != '#') {
  294. this.x--;
  295. } else if (down != '#') {
  296. this.y++;
  297. } else {
  298. System.err.printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
  299. time.get(), this.x, this.y, this.kind);
  300. }
  301. } else if (this.lastY - 1 == this.y) {
  302. if (right != '#') {
  303. this.x++;
  304. } else if (up != '#') {
  305. this.y--;
  306. } else if (left != '#') {
  307. this.x--;
  308. } else {
  309. System.err.printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
  310. time.get(), this.x, this.y, this.kind);
  311. }
  312. } else if (this.lastX + 1 == this.x) {
  313. if (down != '#') {
  314. this.y++;
  315. } else if (right != '#') {
  316. this.x++;
  317. } else if (up != '#') {
  318. this.y--;
  319. } else {
  320. System.err.printf("動けません。t = %d, x = %d, y = %d, kind = %s\n",
  321. time.get(), this.x, this.y, this.kind);
  322. }
  323. } else {
  324. System.err.printf("止まっていた?t = %d, x = %d, y = %d, kind = %s\n",
  325. time.get(), this.x, this.y, this.kind);
  326. }
  327. break;
  328. }
  329. /*
  330. * 交差点マスに入るたびに、最初は敵Lの行動、次回は敵Rの行動、さらに次回はまた敵Lの行動、と繰り返します。
  331. */
  332. this.kind = this.kind == Kind.JL ? Kind.JR : (this.kind == Kind.JR ? Kind.JL
  333. : this.kind);
  334. } else {
  335. System.err.printf("動けません。t = %d, x = %d, y = %d, kind = %s\n", time.get(),
  336. this.x, this.y, this.kind);
  337. }
  338. }
  339. }
  340.  
  341. /**
  342. * @param player プレイヤ
  343. * @return 殺したかどうか
  344. */
  345. boolean killed(final Player player) {
  346. if (this.x == player.x && this.y == player.y) {
  347. return true;
  348. } else if (this.x == player.lastX && this.y == player.lastY && this.lastX == player.x
  349. && this.lastY == player.y) {
  350. return true;
  351. } else {
  352. return false;
  353. }
  354. }
  355.  
  356. @Override
  357. public String toString() {
  358. return new Formatter().format("[x = %d, y = %d, kind = %s]", this.x, this.y, this.kind)
  359. .toString();
  360. }
  361. }
  362.  
  363. /**
  364. * プレイヤ
  365. */
  366. static class Player {
  367. /**
  368. * X座標
  369. */
  370. int x = -1;
  371. /**
  372. * Y座標
  373. */
  374. int y = -1;
  375. /**
  376. * 直前のX座標
  377. */
  378. int lastX = -1;
  379. /**
  380. * 直前のY座標
  381. */
  382. int lastY = -1;
  383.  
  384. /**
  385. * 上へ移動
  386. * @param data データ
  387. * @param log ログ
  388. */
  389. void moveDown(final char[][] data, final StringBuilder log) {
  390. this.lastX = this.x;
  391. this.lastY = this.y;
  392. if (data[this.y + 1][this.x] != '#') {
  393. this.y++;
  394. }
  395. log.append('j');
  396. }
  397.  
  398. /**
  399. * 左へ移動
  400. * @param data データ
  401. * @param log ログ
  402. */
  403. void moveLeft(final char[][] data, final StringBuilder log) {
  404. this.lastX = this.x;
  405. this.lastY = this.y;
  406. if (data[this.y][this.x - 1] != '#') {
  407. this.x--;
  408. }
  409. log.append('h');
  410. }
  411.  
  412. /**
  413. * 上へ移動
  414. * @param data データ
  415. * @param log ログ
  416. */
  417. void moveUp(final char[][] data, final StringBuilder log) {
  418. this.lastX = this.x;
  419. this.lastY = this.y;
  420. if (data[this.y - 1][this.x] != '#') {
  421. this.y--;
  422. }
  423. log.append('k');
  424. }
  425.  
  426. /**
  427. * 右へ移動
  428. * @param data データ
  429. * @param log ログ
  430. */
  431. void moveRight(final char[][] data, final StringBuilder log) {
  432. this.lastX = this.x;
  433. this.lastY = this.y;
  434. if (data[this.y][this.x + 1] != '#') {
  435. this.x++;
  436. }
  437. log.append('l');
  438. }
  439.  
  440. /**
  441. * 留まる
  442. * @param log ログ
  443. */
  444. void stay(final StringBuilder log) {
  445. this.lastX = this.x;
  446. this.lastY = this.y;
  447. log.append('.');
  448. }
  449.  
  450. @Override
  451. public String toString() {
  452. return new Formatter().format("[x = %d, y = %d, lX = %d, lY = %d]", this.x, this.y,
  453. this.lastX, this.lastY).toString();
  454. }
  455. }
  456.  
  457. /**
  458. * ステージ
  459. */
  460. enum Stage {
  461. /**
  462. * 入力1
  463. */
  464. LV1(50, 11, 7, "###########" + "#.V..#..H.#" + "#.##...##.#" + "#L#..#..R.#"
  465. + "#.#.###.#.#" + "#....@....#" + "###########"),
  466. /**
  467. * 入力2
  468. */
  469. LV2(300, 20, 17, "####################" + "###.....L..........#" + "###.##.##.##L##.##.#"
  470. + "###.##.##.##.##.##.#" + "#.L................#" + "#.##.##.##.##.##.###"
  471. + "#.##.##L##.##.##.###" + "#.................L#" + "#.#.#.#J####J#.#.#.#"
  472. + "#L.................#" + "###.##.##.##.##.##.#" + "###.##.##R##.##.##.#"
  473. + "#................R.#" + "#.##.##.##.##R##.###" + "#.##.##.##.##.##.###"
  474. + "#@....R..........###" + "####################"),
  475. /**
  476. * 入力3
  477. */
  478. LV3(700, 58, 17, "##########################################################"
  479. + "#........................................................#"
  480. + "#.###.#########.###############.########.###.#####.#####.#"
  481. + "#.###.#########.###############.########.###.#####.#####.#"
  482. + "#.....#########....J.............J.......###.............#"
  483. + "#####.###.......#######.#######.########.###.#######.#####"
  484. + "#####.###.#####J#######.#######.########.###.## ##.#####"
  485. + "#####.###L#####.## ##L## ##.## ##.###.## ##.#####"
  486. + "#####.###..H###.## ##.## ##.########.###.#######J#####"
  487. + "#####.#########.## ##L## ##.########.###.###V....#####"
  488. + "#####.#########.#######.#######..........###.#######.#####"
  489. + "#####.#########.#######.#######.########.###.#######.#####"
  490. + "#.....................L.........########..........R......#"
  491. + "#L####.##########.##.##########....##....#########.#####.#"
  492. + "#.####.##########.##.##########.##.##.##.#########.#####.#"
  493. + "#.................##............##..@.##...............R.#"
  494. + "##########################################################");
  495. /**
  496. * 制限時間
  497. */
  498. int timeLimit;
  499. /**
  500. * 幅
  501. */
  502. int width;
  503. /**
  504. * 高さ
  505. */
  506. int height;
  507. /**
  508. * データ
  509. */
  510. char[][] data;
  511. /**
  512. * 点
  513. */
  514. int dots;
  515.  
  516. /**
  517. * ステージを初期化
  518. * @param timeLimit 制限時間
  519. * @param width 幅
  520. * @param height 高さ
  521. * @param string 入力文字列
  522. */
  523. Stage(final int timeLimit, final int width, final int height, final String string) {
  524. this.timeLimit = timeLimit;
  525. this.width = width;
  526. this.height = height;
  527. this.data = new char[height][width];
  528. this.dots = 0;
  529. for (int i = 0; i < height; i++) {
  530. for (int j = 0; j < width; j++) {
  531. final char character = string.charAt(i * width + j);
  532. this.data[i][j] = character;
  533. if (character == '.') {
  534. this.dots++;
  535. }
  536. }
  537. }
  538. };
  539. }
  540.  
  541. /**
  542. * メインメソッド
  543. * @param args コマンドライン引数
  544. * @throws InterruptedException 割り込み例外
  545. */
  546. public static void main(final String[] args) throws InterruptedException {
  547. final Stage stage = Stage.LV1;
  548. final int timeLimit = stage.timeLimit;
  549. final int width = stage.width;
  550. final int height = stage.height;
  551. final char[][] data = new char[height][width];
  552. final Player player = new Player();
  553. final List<Enemy> enemies = new ArrayList<Enemy>();
  554. final AtomicInteger time = new AtomicInteger();
  555. final AtomicInteger dots = new AtomicInteger();
  556. final StringBuilder log = new StringBuilder();
  557. final AtomicBoolean isDead = new AtomicBoolean();
  558. final JFrame frame = new JFrame(stage.toString());
  559. final int size = 20;
  560. final JLabel statusLabel = new JLabel("ここに現在の状態が表示されます。");
  561. statusLabel.setOpaque(true);
  562. statusLabel.setBackground(Color.WHITE);
  563. frame.add(statusLabel, BorderLayout.NORTH);
  564. final JPanel playPanel = new JPanel() {
  565. @Override
  566. protected void paintComponent(final Graphics graphics) {
  567. super.paintComponent(graphics);
  568. final Graphics2D g = (Graphics2D) graphics;
  569. g.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
  570. RenderingHints.VALUE_ANTIALIAS_ON);
  571. g.setColor(Color.RED);
  572. g.fillOval(player.x * size, player.y * size, size, size);
  573. for (final Enemy enemy : enemies) {
  574. switch (enemy.kind) {
  575. case V:
  576. g.setColor(Color.GREEN);
  577. break;
  578. case H:
  579. g.setColor(Color.BLUE);
  580. break;
  581. case L:
  582. g.setColor(Color.ORANGE);
  583. break;
  584. case R:
  585. g.setColor(Color.PINK);
  586. break;
  587. case JL:
  588. case JR:
  589. g.setColor(Color.CYAN);
  590. break;
  591. default:
  592. g.setColor(Color.BLACK);
  593. }
  594. g.fillOval(enemy.x * size, enemy.y * size, size, size);
  595. }
  596. g.setColor(Color.BLACK);
  597. for (final Enemy enemy : enemies) {
  598. g.drawString(enemy.kind.toString(), enemy.x * size + size / 2
  599. - g.getFontMetrics().stringWidth(enemy.kind.toString()) / 2, enemy.y
  600. * size + g.getFontMetrics().getAscent());
  601. }
  602. g.drawString("P", player.x * size + size / 2 - g.getFontMetrics().stringWidth("P")
  603. / 2, player.y * size + g.getFontMetrics().getAscent());
  604. for (int i = 0; i < height; i++) {
  605. for (int j = 0; j < width; j++) {
  606. switch (data[i][j]) {
  607. case '#':
  608. g.setColor(Color.GRAY);
  609. g.fillRect(j * size, i * size, size, size);
  610. break;
  611. case '.':
  612. g.setColor(Color.BLACK);
  613. g.fillOval(j * size + size / 2 - 2, i * size + size / 2 - 2, 4, 4);
  614. break;
  615. }
  616. }
  617. }
  618. }
  619. };
  620. playPanel.setFocusable(true);
  621. playPanel.addKeyListener(new KeyAdapter() {
  622. @Override
  623. public void keyPressed(final KeyEvent e) {
  624. switch (e.getKeyCode()) {
  625. case KeyEvent.VK_ESCAPE:
  626. initialize(width, height, data, stage, player, enemies, time, dots, log,
  627. isDead, statusLabel, timeLimit);
  628. playPanel.repaint();
  629. break;
  630. case KeyEvent.VK_Z:
  631. playBack(log.toString().replaceFirst(".$", ""), stage, timeLimit, width,
  632. height, data, player, enemies, time, dots, log, isDead, statusLabel,
  633. playPanel);
  634. playPanel.repaint();
  635. updateStatusLabel(statusLabel, stage, timeLimit, time, dots, log);
  636. break;
  637. case KeyEvent.VK_1:
  638. break;
  639. case KeyEvent.VK_2:
  640. break;
  641. case KeyEvent.VK_3:
  642. }
  643. if (!isDead.get()) {
  644. switch (e.getKeyCode()) {
  645. case KeyEvent.VK_DOWN:
  646. case KeyEvent.VK_J:
  647. player.moveDown(data, log);
  648. break;
  649. case KeyEvent.VK_LEFT:
  650. case KeyEvent.VK_H:
  651. player.moveLeft(data, log);
  652. break;
  653. case KeyEvent.VK_UP:
  654. case KeyEvent.VK_K:
  655. player.moveUp(data, log);
  656. break;
  657. case KeyEvent.VK_RIGHT:
  658. case KeyEvent.VK_L:
  659. player.moveRight(data, log);
  660. break;
  661. case KeyEvent.VK_SPACE:
  662. case KeyEvent.VK_ENTER:
  663. case KeyEvent.VK_PERIOD:
  664. player.stay(log);
  665. break;
  666. default:
  667. return;
  668. }
  669. moveEnemies(data, player, enemies, time, isDead);
  670. getDot(data, player, dots);
  671. time.incrementAndGet();
  672. if (time.get() >= timeLimit) {
  673. isDead.set(true);
  674. }
  675. if (dots.get() == stage.dots) {
  676. System.out.printf("(cleared)time: %d/%d, score: %d/%d, log: %s\n",
  677. time.get(), timeLimit, dots.get(), stage.dots, log);
  678. isDead.set(true);
  679. }
  680. playPanel.repaint();
  681. updateStatusLabel(statusLabel, stage, timeLimit, time, dots, log);
  682. if (isDead.get()) {
  683. statusLabel.setText((dots.get() == stage.dots ? "(cleared)" : "(dead)")
  684. + statusLabel.getText());
  685. Toolkit.getDefaultToolkit().getSystemClipboard()
  686. .setContents(new StringSelection(log.toString()), null);
  687. }
  688. }
  689. }
  690. });
  691. playPanel.setPreferredSize(new Dimension(width * size, height * size));
  692. playPanel.setBackground(Color.WHITE);
  693. frame.add(playPanel);
  694. frame.pack();
  695. frame.setLocationByPlatform(true);
  696. frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
  697. initialize(width, height, data, stage, player, enemies, time, dots, log, isDead,
  698. statusLabel, timeLimit);
  699. final boolean isGUI = false;
  700. frame.setVisible(isGUI);
  701. if (isGUI) {
  702. Thread.sleep(1000);
  703. }
  704. final Random random = new Random();
  705. int maxDots = 0;
  706. int maxTime = 0;
  707. double resetRate = .001;
  708. final double stayRate = .2;
  709. final double forwardRate = .9;
  710. final double hungryRate = .8;
  711. String rollBackPoint = "";
  712. while (true) {
  713. if (random.nextDouble() < stayRate) {
  714. player.stay(log);
  715. } else {
  716. final char down = data[player.y + 1][player.x];
  717. final char left = data[player.y][player.x - 1];
  718. final char up = data[player.y - 1][player.x];
  719. final char right = data[player.y][player.x + 1];
  720. final int numOfWalls = (down == '#' ? 1 : 0) + (left == '#' ? 1 : 0)
  721. + (up == '#' ? 1 : 0) + (right == '#' ? 1 : 0);
  722. if (numOfWalls < 2) {
  723. rollBackPoint = log.toString();
  724. }
  725. final int numOfDots = (down == '.' ? 1 : 0) + (left == '.' ? 1 : 0)
  726. + (up == '.' ? 1 : 0) + (right == '.' ? 1 : 0);
  727. if (numOfDots > 0 && random.nextDouble() < hungryRate) {
  728. loop: while (true) {
  729. switch (random.nextInt(4)) {
  730. case 0:
  731. if (down == '.') {
  732. player.moveDown(data, log);
  733. break loop;
  734. }
  735. break;
  736. case 1:
  737. if (left == '.') {
  738. player.moveLeft(data, log);
  739. break loop;
  740. }
  741. break;
  742. case 2:
  743. if (up == '.') {
  744. player.moveUp(data, log);
  745. break loop;
  746. }
  747. break;
  748. case 3:
  749. if (right == '.') {
  750. player.moveRight(data, log);
  751. break loop;
  752. }
  753. break;
  754. }
  755. }
  756. } else {
  757. loop: while (true) {
  758. final boolean isForward = random.nextDouble() < forwardRate;
  759. switch (random.nextInt(4)) {
  760. case 0:
  761. if (down != '#') {
  762. if (isForward && player.lastY == player.y + 1) {
  763. break;
  764. }
  765. player.moveDown(data, log);
  766. break loop;
  767. }
  768. break;
  769. case 1:
  770. if (left != '#') {
  771. if (isForward && player.lastX == player.x - 1) {
  772. break;
  773. }
  774. player.moveLeft(data, log);
  775. break loop;
  776. }
  777. break;
  778. case 2:
  779. if (up != '#') {
  780. if (isForward && player.lastY == player.y - 1) {
  781. break;
  782. }
  783. player.moveUp(data, log);
  784. break loop;
  785. }
  786. break;
  787. case 3:
  788. if (right != '#') {
  789. if (isForward && player.lastX == player.x + 1) {
  790. break;
  791. }
  792. player.moveRight(data, log);
  793. break loop;
  794. }
  795. break;
  796. }
  797. }
  798. }
  799. }
  800. moveEnemies(data, player, enemies, time, isDead);
  801. getDot(data, player, dots);
  802. time.incrementAndGet();
  803. if (time.get() >= timeLimit) {
  804. isDead.set(true);
  805. }
  806. if (dots.get() == stage.dots) {
  807. isDead.set(true);
  808. }
  809. if (isDead.get()) {
  810. if (dots.get() > maxDots) {
  811. System.out.printf("time: %d/%d, score: %d/%d, log: %s\n", time.get(),
  812. timeLimit, dots.get(), stage.dots, log);
  813. maxDots = dots.get();
  814. }
  815. if (dots.get() == stage.dots && timeLimit - time.get() > maxTime) {
  816. System.out.printf("clear! time: %d, score: %d, log: %s\n",
  817. timeLimit - time.get(), dots.get(), log);
  818. maxTime = timeLimit - time.get();
  819. }
  820. if (random.nextDouble() < resetRate) {
  821. initialize(width, height, data, stage, player, enemies, time, dots, log,
  822. isDead, statusLabel, timeLimit);
  823. resetRate *= .9;
  824. } else {
  825. if (random.nextBoolean()) {
  826. playBack(log.subSequence(0, log.length() * 99 / 100).toString(), stage,
  827. timeLimit, width, height, data, player, enemies, time, dots, log,
  828. isDead, statusLabel, playPanel);
  829. } else {
  830. playBack(rollBackPoint, stage, timeLimit, width, height, data, player,
  831. enemies, time, dots, log, isDead, statusLabel, playPanel);
  832. }
  833. }
  834.  
  835. }
  836. if (isGUI) {
  837. playPanel.repaint();
  838. statusLabel.setText(new Formatter().format("time: %d, score: %d, log: %s",
  839. timeLimit - time.get(), dots.get(), log).toString());
  840. Thread.sleep(10);
  841. }
  842. }
  843. }
  844.  
  845. /**
  846. * 初期化
  847. * @param width 幅
  848. * @param height 高さ
  849. * @param data データ
  850. * @param stage ステージ
  851. * @param player プレイヤ
  852. * @param enemies 敵
  853. * @param time 時刻
  854. * @param dots 点
  855. * @param log ログ
  856. * @param isDead 死んでいるかどうか
  857. * @param statusLabel ラベル
  858. * @param timeLimit 制限時間
  859. */
  860. static void initialize(final int width, final int height, final char[][] data,
  861. final Stage stage, final Player player, final List<Enemy> enemies,
  862. final AtomicInteger time, final AtomicInteger dots, final StringBuilder log,
  863. final AtomicBoolean isDead, final JLabel statusLabel, final int timeLimit) {
  864. enemies.clear();
  865. for (int i = 0; i < height; i++) {
  866. for (int j = 0; j < width; j++) {
  867. final char kind = stage.data[i][j];
  868. switch (kind) {
  869. case 'V':
  870. data[i][j] = ' ';
  871. enemies.add(new Enemy(j, i, Enemy.Kind.V));
  872. break;
  873. case 'H':
  874. data[i][j] = ' ';
  875. enemies.add(new Enemy(j, i, Enemy.Kind.H));
  876. break;
  877. case 'L':
  878. data[i][j] = ' ';
  879. enemies.add(new Enemy(j, i, Enemy.Kind.L));
  880. break;
  881. case 'R':
  882. data[i][j] = ' ';
  883. enemies.add(new Enemy(j, i, Enemy.Kind.R));
  884. break;
  885. case 'J':
  886. data[i][j] = ' ';
  887. enemies.add(new Enemy(j, i, Enemy.Kind.JL));
  888. break;
  889. case '@':
  890. data[i][j] = ' ';
  891. player.x = j;
  892. player.y = i;
  893. player.lastX = player.x;
  894. player.lastY = player.y;
  895. break;
  896. case '#':
  897. case '.':
  898. case ' ':
  899. data[i][j] = stage.data[i][j];
  900. break;
  901. default:
  902. System.err.printf("unknown data: '%s', x = %d, y = %d\n", stage.data[i][j], j,
  903. i);
  904. }
  905. }
  906. }
  907. time.set(0);
  908. dots.set(0);
  909. log.setLength(0);
  910. isDead.set(false);
  911. updateStatusLabel(statusLabel, stage, timeLimit, time, dots, log);
  912. }
  913.  
  914. /**
  915. * リプレイ
  916. * @param string 動作を指示する文字列
  917. * @param stage ステージ
  918. * @param timeLimit 制限時間
  919. * @param width 幅
  920. * @param height 高さ
  921. * @param data データ
  922. * @param player プレイヤ
  923. * @param enemies 敵
  924. * @param time 時刻
  925. * @param dots 点
  926. * @param log ログ
  927. * @param isDead 死んでいるかどうか
  928. * @param statusLabel ラベル
  929. * @param playPanel パネル
  930. */
  931. static void playBack(final String string, final Stage stage, final int timeLimit,
  932. final int width, final int height, final char[][] data, final Player player,
  933. final List<Enemy> enemies, final AtomicInteger time, final AtomicInteger dots,
  934. final StringBuilder log, final AtomicBoolean isDead, final JLabel statusLabel,
  935. final JPanel playPanel) {
  936. initialize(width, height, data, stage, player, enemies, time, dots, log, isDead,
  937. statusLabel, timeLimit);
  938. for (int i = 0; i < string.length(); i++) {
  939. if (!isDead.get()) {
  940. switch (string.charAt(i)) {
  941. case 'j':
  942. player.moveDown(data, log);
  943. break;
  944. case 'h':
  945. player.moveLeft(data, log);
  946. break;
  947. case 'k':
  948. player.moveUp(data, log);
  949. break;
  950. case 'l':
  951. player.moveRight(data, log);
  952. break;
  953. case '.':
  954. player.stay(log);
  955. break;
  956. default:
  957. System.err.printf("unknown data: %s\n", string.charAt(i));
  958. }
  959. moveEnemies(data, player, enemies, time, isDead);
  960. getDot(data, player, dots);
  961. time.incrementAndGet();
  962. if (time.get() >= timeLimit) {
  963. isDead.set(true);
  964. }
  965. }
  966. }
  967. }
  968.  
  969. /**
  970. * 点を食べる
  971. * @param data データ
  972. * @param player プレイヤ
  973. * @param dots 点
  974. */
  975. static void getDot(final char[][] data, final Player player, final AtomicInteger dots) {
  976. if (data[player.y][player.x] == '.') {
  977. data[player.y][player.x] = ' ';
  978. dots.incrementAndGet();
  979. }
  980. }
  981.  
  982. /**
  983. * 敵を移動
  984. * @param data データ
  985. * @param player プレイヤ
  986. * @param enemies 敵
  987. * @param time 時刻
  988. * @param isDead 死んでいるかどうか
  989. */
  990. static void moveEnemies(final char[][] data, final Player player, final List<Enemy> enemies,
  991. final AtomicInteger time, final AtomicBoolean isDead) {
  992. for (final Enemy enemy : enemies) {
  993. final int x = enemy.x;
  994. final int y = enemy.y;
  995. enemy.move(data, player, time);
  996. enemy.lastX = x;
  997. enemy.lastY = y;
  998. if (enemy.killed(player)) {
  999. isDead.set(true);
  1000. }
  1001. }
  1002. }
  1003.  
  1004. /**
  1005. * 現在の状態を表示するラベルを更新
  1006. * @param statusLabel ラベル
  1007. * @param stage ステージ
  1008. * @param timeLimit 制限時間
  1009. * @param time 時刻
  1010. * @param dots 点
  1011. * @param log ログ
  1012. */
  1013. static void updateStatusLabel(final JLabel statusLabel, final Stage stage, final int timeLimit,
  1014. final AtomicInteger time, final AtomicInteger dots, final StringBuilder log) {
  1015. statusLabel.setText(new Formatter().format("time: %d/%d, score: %d/%d, log: %s",
  1016. time.get(), timeLimit, dots.get(), stage.dots, log).toString());
  1017. }
  1018. }
Not running #stdin #stdout 0s 0KB
stdin
Standard input is empty
stdout
Standard output is empty