ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/loops/Microscope.java
Revision: 1.5
Committed: Fri Oct 22 05:18:31 2010 UTC (13 years, 6 months ago) by jsr166
Branch: MAIN
Changes since 1.4: +4 -4 lines
Log Message:
whitespace

File Contents

# Content
1 /*
2 * Written by Doug Lea with assistance from members of JCP JSR-166
3 * Expert Group and released to the public domain, as explained at
4 * http://creativecommons.org/licenses/publicdomain
5 */
6
7 import java.awt.*;
8 import javax.swing.*;
9 import java.util.*;
10 import java.awt.event.*;
11 import javax.swing.event.*;
12 import java.util.concurrent.*;
13
14
15 /**
16 * Microscope implements a version of the 7th Guest
17 * game found looking in the Microscope in the laboratory.
18 * See <a href="http://gee.cs.oswego.edu/dl/applets/micro.html">
19 * Microscope</a> version for instructions.
20 * <p>
21 * The code has been mangled beyond recognition
22 * as a test of ForkJoin.
23 */
24 public class Microscope extends JPanel {
25
26 static final CountDownLatch cd = new CountDownLatch(1);
27 /*
28 * If true, the move finder uses a repeatable evaluation
29 * strategy, so all self-play games at same level have same outcome.
30 * This is useful for testing purposes, but much less fun to watch.
31 */
32
33 static boolean DETERMINISTIC = false;
34
35 static boolean headless = false;
36
37 // Command-line parameters
38
39 static int nprocs;
40 static int lookAheads = 4;
41 static boolean autostart = true;
42 static ForkJoinPool group;
43 static long startTime;
44
45 public static void main(String[] args) throws Exception {
46 nprocs = 0;
47 try {
48 if (args.length > 0) {
49 nprocs = Integer.parseInt(args[0]);
50 autostart = false;
51 if (args.length > 1) {
52 autostart = true;
53 lookAheads = Integer.parseInt(args[1]);
54 DETERMINISTIC = true;
55 }
56 }
57 }
58 catch (Exception e) {
59 System.out.println("Usage: java Microscope <threads> [<level>]");
60 return;
61 }
62 group = nprocs == 0 ? new ForkJoinPool() : new ForkJoinPool(nprocs);
63 startTime = System.currentTimeMillis();
64 System.out.print("Score: ");
65 oneRun();
66 cd.await();
67 long now = System.currentTimeMillis();
68 System.out.println();
69 System.out.println("time: " + (now - startTime) + "ms");
70 System.out.println(group.toString());
71 System.exit(0);
72 }
73
74 static void onExit() {
75 long now = System.currentTimeMillis();
76 System.out.println("time: " + (now - startTime) + "ms");
77 System.out.println(group.toString());
78 System.exit(0);
79 }
80
81 static void oneRun() {
82 if (java.awt.GraphicsEnvironment.isHeadless()) {
83 headless = true;
84 Microscope t = new Microscope();
85 t.init();
86 }
87 else {
88 JFrame frame = new JFrame();
89 frame.addWindowListener(new WindowAdapter() {
90 public void windowClosing(WindowEvent e) {onExit();}});
91
92 Microscope t = new Microscope();
93 frame.setSize(new Dimension(400, 400));
94 frame.getContentPane().add(t);
95 // frame.pack();
96 frame.setVisible(true);
97 t.init();
98 }
99 }
100
101 // representations:
102
103 Board board = new Board(); // The current board representation
104
105 synchronized Board getBoard() { return board; }
106 synchronized void setBoard(Board b) {
107 board = b;
108 System.out.print(b.score(player) + " ");
109 if (boardPanel != null)
110 boardPanel.repaint();
111 }
112
113 Player player = Player.Blue; // current player (BLUE, GREEN)
114
115 synchronized Player getPlayer() { return player; }
116 synchronized void setPlayer(Player p) { player = p; }
117
118
119 final AutoMover auto; // The move finder.
120 final User user; // Mover for user moves
121 Mover mover = null; // the current Mover (always == auto or user or null)
122
123 synchronized Mover getMover() { return mover; }
124 synchronized void setMover(Mover m) { mover = m; }
125 synchronized boolean isMoving() { return mover != null; }
126
127 Vector<Move> history = new Vector<Move>(); // List of completed moves;
128
129 boolean demoMode = true;
130 synchronized boolean getDemoMode() { return demoMode; }
131 synchronized void setDemoMode(boolean b) { demoMode = b; }
132 synchronized boolean toggleDemoMode() { return demoMode = !demoMode; }
133
134 final BoardPanel boardPanel;
135
136 JLabel scoreLabel;
137 JButton autoButton;
138 JButton undoButton;
139 JButton modeButton;
140 JSlider levelSlider;
141
142 public Microscope() {
143 if (headless) {
144 boardPanel = null;
145 auto = new AutoMover(this);
146 user = new User(this);
147 return;
148 }
149 boardPanel = new BoardPanel();
150 scoreLabel = new JLabel("Score: 0");
151 autoButton = new JButton(" Start ");
152 undoButton = new JButton("Undo");
153 modeButton = new JButton("Demo mode");
154 levelSlider = new JSlider(JSlider.VERTICAL, 2, 6, lookAheads);
155
156 auto = new AutoMover(this);
157 user = new User(this);
158
159 JPanel topPanel = new JPanel();
160 autoButton.addActionListener(new ActionListener() {
161 public void actionPerformed(ActionEvent e) {
162 if (!isMoving()) {
163 startMover(auto);
164 autoButton.setText("Cancel");
165 }
166 else {
167 stopMover();
168 if (getDemoMode())
169 autoButton.setText(" Start ");
170 else
171 autoButton.setText(" Find ");
172 }
173 }});
174
175 modeButton.addActionListener(new ActionListener() {
176 public synchronized void actionPerformed(ActionEvent e) {
177 toggleDemoMode();
178 updateStatus();
179
180 }});
181
182 undoButton.addActionListener(new ActionListener() {
183 public void actionPerformed(ActionEvent e) {
184 undo();
185 }});
186
187 levelSlider.addChangeListener(new ChangeListener() {
188 public void stateChanged(ChangeEvent e) {
189 setLevel(((JSlider)(e.getSource())).getValue());
190 }});
191
192 // Dimension labDim = new Dimension(40, 16);
193 // Dimension labDim = new Dimension(72, 24);
194 // scoreLabel.setMinimumSize(labDim);
195 // scoreLabel.setPreferredSize(labDim);
196
197
198 topPanel.add(autoButton);
199 topPanel.add(modeButton);
200 topPanel.add(undoButton);
201 topPanel.add(scoreLabel);
202 add(topPanel);
203
204
205 levelSlider.setLabelTable(levelSlider.createStandardLabels(1));
206 levelSlider.setPaintLabels(true);
207
208 JPanel botPanel = new JPanel();
209
210 botPanel.add(boardPanel);
211 JPanel sliderPanel = new JPanel();
212 sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.Y_AXIS));
213 sliderPanel.add(levelSlider);
214 sliderPanel.add(new JLabel("Level"));
215
216 botPanel.add(sliderPanel);
217
218 add(botPanel);
219 }
220
221 void initializeBoard() {
222 board.reset();
223 board.occupy(Player.Blue, 0, 0);
224 board.occupy(Player.Blue, Board.RANKS-1, Board.RANKS-1);
225 board.occupy(Player.Green, 0, Board.RANKS-1);
226 board.occupy(Player.Green, Board.RANKS-1, 0);
227 setPlayer(Player.Blue);
228 if (boardPanel != null)
229 boardPanel.repaint();
230 }
231
232 public void init() {
233 initializeBoard();
234 if (autostart) {
235 startMover(auto);
236 }
237 }
238
239
240 synchronized void setLevel(int l) {
241 lookAheads = l;
242 if (lookAheads <= 1) lookAheads = 2;
243 }
244
245 public int level () { return Microscope.lookAheads; }
246
247
248 // process a move (called only from mover)
249
250 public void move(Move m, Mover mvr) {
251 if (mvr != mover ||
252 m == null ||
253 (mvr == user && !m.isLegal())) {
254 setMover(null);
255 if (mvr == auto && autostart) {
256 cd.countDown();
257 return; // System.exit(0);
258 }
259 }
260 else {
261 m.commit();
262 setBoard(m.board());
263 setPlayer(m.player().opponent());
264
265 history.addElement(m);
266
267 if (mvr == auto &&
268 getDemoMode() &&
269 !m.isPass()) {
270 if (getBoard().gameOver()) {
271 if (autostart) {
272 cd.countDown();
273 return; // System.exit(0);
274 }
275 else
276 setMover(null);
277 }
278 else {
279 auto.startTurn(new Board(getBoard()), getPlayer());
280 }
281 }
282 else {
283 setMover(null);
284 }
285 }
286 }
287
288 // start up a Mover
289 void startMover(Mover m) {
290 Mover mvr = getMover();
291 if (mvr == null) {
292 setMover(m);
293 m.startTurn(new Board(getBoard()), player);
294 }
295 }
296
297 // stop current Mover
298 void stopMover() {
299 Mover mvr = getMover();
300 if (mvr != null) {
301 setMover(null);
302 mvr.cancel();
303 }
304 }
305
306
307 // handle Undo button
308 synchronized void undo() {
309 if (mover == null) {
310 if (history.size() > 1) {
311 history.removeElementAt(history.size()-1);
312 Move m = history.lastElement();
313 setPlayer(m.player().opponent());
314 setBoard(m.board());
315 }
316 else if (history.size() == 1) {
317 history.removeAllElements();
318 initializeBoard();
319 }
320 }
321 }
322
323 // handle click on tile
324 void userMove(int row, int col) {
325 startMover(user);
326 user.choose(row, col);
327 }
328
329 void updateStatus() { // normally called from board update
330 Player p = getPlayer();
331 int s = getBoard().score(p);
332 if (scoreLabel == null) {
333 System.out.print(s + " ");
334 return;
335 }
336
337 scoreLabel.setForeground(displayColor(p));
338 scoreLabel.setText("Score: " + s);
339
340 if (getDemoMode())
341 modeButton.setText("Demo mode");
342 else {
343 if (getPlayer().isBlue())
344 modeButton.setText("Blue turn");
345 else
346 modeButton.setText("Green turn");
347 }
348
349 }
350
351
352 static final int CELL_SIZE = 40; // size of a tile/cell
353
354 static final Color paleGreen = new Color(152, 251, 152);
355 static final Color darkGreen = new Color(60, 179, 113);
356
357 static final Color possibleMoveColor = Color.yellow;
358
359
360 public static Color displayColor(Player pl) {
361 if (pl.isBlue()) return Color.blue;
362 else if (pl.isGreen()) return darkGreen;
363 else return Color.white;
364 }
365
366 public static Color lightDisplayColor(Player pl) {
367 if (pl.isBlue()) return Color.cyan;
368 else if (pl.isGreen()) return paleGreen;
369 else return Color.gray;
370 }
371
372
373 class BoardPanel extends Canvas implements MouseListener {
374
375 BoardPanel() {
376 setSize(new Dimension(Board.RANKS * CELL_SIZE + 5,
377 Board.RANKS * CELL_SIZE + 5));
378 addMouseListener(BoardPanel.this);
379 }
380
381 public void paint(Graphics g) {
382
383 Board b = getBoard();
384 Player p = getPlayer();
385
386 // the cells
387 for (int row = 0; row < Board.RANKS; row++) {
388 for (int col = 0; col < Board.RANKS; col++) {
389
390 // Highlight selected tile and legal destinations
391 if (user.placing()) {
392 if (user.hasMovedFrom(row, col))
393 g.setColor(lightDisplayColor(p));
394 else if (user.canMoveTo(row, col))
395 g.setColor(possibleMoveColor);
396 else
397 g.setColor(displayColor(b.occupant(row, col)));
398 }
399
400 else
401 g.setColor(displayColor(b.occupant(row, col)));
402
403 // tiles are just filled rectangles
404 g.fillRect(row * CELL_SIZE, col * CELL_SIZE, CELL_SIZE, CELL_SIZE);
405 }
406 }
407
408 // the grid over the cells
409 g.setColor(Color.black);
410 for ( int i = 0; i <= Board.RANKS; i++) {
411 g.drawLine(0, i * CELL_SIZE, Board.RANKS * CELL_SIZE, i * CELL_SIZE);
412 g.drawLine(i * CELL_SIZE, 0, i * CELL_SIZE, Board.RANKS * CELL_SIZE);
413 }
414
415 updateStatus();
416 }
417
418 public void mouseReleased(MouseEvent evt) {
419
420 int x = evt.getX();
421 int y = evt.getY();
422
423 int row = x / CELL_SIZE;
424 int col = y / CELL_SIZE;
425
426 if (Board.inBounds(row, col)) { // cell selection
427 userMove(row, col);
428 repaint();
429 }
430 }
431
432 public void mouseClicked(MouseEvent e) {}
433 public void mousePressed(MouseEvent e) {}
434 public void mouseEntered(MouseEvent e) {}
435 public void mouseExited(MouseEvent e) {}
436 }
437
438 /**
439 * Player is just a glorified enumeration
440 */
441 static final class Player {
442
443 public static final int EMPTY = 0;
444 public static final int BLUE = 1;
445 public static final int GREEN = 2;
446 public static final int ILLEGAL_PLAYER_VALUE = 3;
447
448 public static final Player Empty = new Player(EMPTY);
449 public static final Player Blue = new Player(BLUE);
450 public static final Player Green = new Player(GREEN);
451 public static final Player Illegal = new Player(ILLEGAL_PLAYER_VALUE);
452
453 /* private */ int code_;
454
455 public Player(int code) { code_ = code; }
456 public Player(Player p) { code_ = p.code_; }
457
458 public boolean same(Player p) { return code_ == p.code_; }
459
460 public boolean isEmpty() { return code_ == EMPTY; }
461 public boolean isBlue() { return code_ == BLUE; }
462 public boolean isGreen() { return code_ == GREEN; }
463 public boolean isLegal() { return code_ <= GREEN; }
464
465 public Player opponent() {
466 if (code_ == GREEN) return Blue;
467 else if (code_ == BLUE) return Green;
468 else return Illegal;
469 }
470
471 }
472
473 /**
474 * Board configurations are represented by bit vectors.
475 * Since there are only 49 cells, the bits can be held in `longs',
476 * one for each player.
477 * <p>
478 * Boards are not immutable, but are never passed around across
479 * threads (instead new ones are constructed), so don't
480 * need any synch.
481 */
482 static final class Board {
483
484 /*
485 First, some Constants and utilities that might as well be here
486 */
487
488 public static final int RANKS = 7;
489 public static final int CELLS = RANKS * RANKS;
490
491 static final long FULL = (1L << CELLS) - 1;
492
493 // The finder uses a spare bit to remember whose move it is.
494 static final long BLUEBIT = (1L << CELLS);
495
496 // Bits representing the adjacent cells for every position
497 static final long[] adjacentMasks = new long[CELLS];
498
499 // bit pattern associated with each tile
500 static final long[] cellBits = new long[CELLS];
501
502 // locations of all cells reachable by a jump for every position
503 static final byte[][] jumpDestinations = new byte[CELLS][];
504
505 // initialize tables
506 static {
507 byte[] dests = new byte[CELLS];
508 for (int j = 0; j < RANKS; ++j) {
509 for (int i = 0; i < RANKS; ++i) {
510 int k = i + j * RANKS;
511 long nmask = 0;
512 int jumpCount = 0;
513 for (int c = j-2; c <= j+2; ++c) {
514 for (int r = i-2; r <= i+2; ++r) {
515 if (c >= 0 && c < RANKS &&
516 r >= 0 && r < RANKS) {
517 int cellIndex = r + c * RANKS;
518 if (r == i-2 || r == i+2 || c == j-2 || c == j+2) {
519 dests[jumpCount++] = (byte)cellIndex;
520 }
521 else if (!(r == i && c == j)) {
522 nmask |= 1L << cellIndex;
523 }
524 }
525 }
526 }
527 adjacentMasks[k] = nmask;
528 cellBits[k] = 1L << k;
529 jumpDestinations[k] = new byte[jumpCount];
530 for (int l = 0; l < jumpCount; ++l)
531 jumpDestinations[k][l] = dests[l];
532
533 }
534 }
535
536 }
537
538
539 public static boolean inBounds(int row, int col) {
540 return (0 <= row) && (row < RANKS) && (0 <= col) && (col < RANKS);
541 }
542
543 // The representation
544
545 long blue_; // bit vector; true if occupied by blue
546 long green_; // same for green;
547
548 // constructors and intializers:
549
550 public Board() { blue_ = 0L; green_ = 0L; }
551 public Board(Board b) { blue_ = b.blue_; green_ = b.green_; }
552 public Board(long b, long g) { blue_ = b; green_ = g; }
553
554 public void copyState(Board b) {
555 blue_ = b.blue_;
556 green_ = b.green_;
557 }
558
559 void reset() {
560 blue_ = 0L; green_ = 0L;
561 }
562
563 long getBlue() { return blue_; }
564 long getGreen() { return green_; }
565
566
567 public Player occupant(int row, int col) {
568 if ((0 <= row) && (row < RANKS) && (0 <= col) && (col < RANKS)) {
569 long m = 1L << (row + col * RANKS);
570 if ((blue_ & m) != 0L) return Player.Blue;
571 else if ((green_ &m) != 0L) return Player.Green;
572 else return Player.Empty;
573 }
574 else
575 return Player.Illegal;
576 }
577
578
579 // place a tile without taking opponent tiles
580
581 public void occupy(Player player, int row, int col) {
582 long m = 1L << (row + col * RANKS);
583 long nm = ~m;
584 if (player.code_ == Player.BLUE) {
585 blue_ |= m;
586 green_ &= nm;
587 }
588 else if (player.code_ == Player.GREEN) {
589 blue_ &= nm;
590 green_ |= m;
591 }
592 else {
593 blue_ &= nm;
594 green_ &= nm;
595 }
596 }
597
598 public void unoccupy(int row, int col) {
599 long nm = ~(1L << (row + col * RANKS));
600 blue_ &= nm;
601 green_ &= nm;
602 }
603
604
605
606 // place a tile, taking all adjacent tiles of opponent
607
608 public void take(Player player, int row, int col) {
609 int k = (row + col * RANKS);
610 long dest = 1L << k;
611 long nbrMask = adjacentMasks[k];
612 long sourceBlue = blue_;
613 long sourceGreen = green_;
614 if (player.code_ == Player.BLUE) {
615 blue_ = sourceBlue | dest | (sourceGreen & nbrMask);
616 green_ = sourceGreen & ~(sourceGreen & nbrMask);
617 }
618 else {
619 blue_ = sourceBlue & ~(sourceBlue & nbrMask);
620 green_ = sourceGreen | dest | (sourceBlue & nbrMask);
621 }
622 }
623
624 public boolean gameOver() {
625 return
626 (((blue_ | green_) & FULL) == FULL) ||
627 ((blue_ & ~BLUEBIT) == 0) ||
628 ((green_ & ~BLUEBIT) == 0);
629 }
630
631
632 public int score(Player player) {
633 if (player.isBlue()) {
634 return score(blue_, green_);
635 }
636 else {
637 return score(green_, blue_);
638 }
639 }
640
641 static int score(long b, long g) {
642
643 // much faster by splitting into ints
644 // and using clever shift-based bit counter
645
646 int lb = (int)(b & ((1L << 32) - 1));
647 int hb = ((int)(b >>> 32)) & ((1 << (CELLS - 32)) - 1);
648
649 lb -= (0xaaaaaaaa & lb) >>> 1;
650 lb = (lb & 0x33333333) + ((lb >>> 2) & 0x33333333);
651 lb = lb + (lb >>> 4) & 0x0f0f0f0f;
652 lb += lb >>> 8;
653 lb += lb >>> 16;
654
655 hb -= (0xaaaaaaaa & hb) >>> 1;
656 hb = (hb & 0x33333333) + ((hb >>> 2) & 0x33333333);
657 hb = hb + (hb >>> 4) & 0x0f0f0f0f;
658 hb += hb >>> 8;
659 hb += hb >>> 16;
660
661 hb = ((lb + hb) & 0xff);
662
663 int lg = (int)(g & ((1L << 32) - 1));
664 int hg = ((int)(g >>> 32)) & ((1 << (CELLS - 32)) - 1);
665
666 lg -= (0xaaaaaaaa & lg) >>> 1;
667 lg = (lg & 0x33333333) + ((lg >>> 2) & 0x33333333);
668 lg = lg + (lg >>> 4) & 0x0f0f0f0f;
669 lg += lg >>> 8;
670 lg += lg >>> 16;
671
672 hg -= (0xaaaaaaaa & hg) >>> 1;
673 hg = (hg & 0x33333333) + ((hg >>> 2) & 0x33333333);
674 hg = hg + (hg >>> 4) & 0x0f0f0f0f;
675 hg += hg >>> 8;
676 hg += hg >>> 16;
677
678 return hb - ((lg + hg) & 0xff);
679 }
680
681
682
683 static int slowscore(long b, long g) {
684 int score = 0;
685 for (int l = 0; l < CELLS; ++l) {
686 score += (int)(b & 1);
687 b >>>= 1;
688 score -= (int)(g & 1);
689 g >>>= 1;
690 }
691 return score;
692 }
693
694
695 }
696
697 /**
698 * Moves represent transitions across Board states
699 */
700 static final class Move {
701
702 static final int NO_VALUE = -1; // row/col value if not yet set
703 static final int PASS_VALUE = -2; // special value for pass moves
704
705 // utilities for classifying moves
706
707 public static boolean twoFrom(int a, int b) {
708 return (a - b == 2) || (b - a == 2);
709 }
710
711 public static boolean withinTwo(int a, int b) {
712 int diff = a - b; return -2 <= diff && diff <= 2;
713 }
714
715 // representations
716
717 int fromRow;
718 int fromCol;
719
720 int toRow;
721 int toCol;
722
723 Player player_;
724 Board board_;
725
726 boolean committed = false; // true if board reflects move
727
728 // constructors and intializers
729
730 public Move(Player turn, Board board) {
731 fromRow = NO_VALUE; fromCol = NO_VALUE;
732 toRow = NO_VALUE; toCol = NO_VALUE;
733 player_ = turn;
734 board_ = board;
735 }
736
737 public Move(Player turn, Board board, boolean isCommitted) {
738 fromRow = NO_VALUE; fromCol = NO_VALUE;
739 toRow = NO_VALUE; toCol = NO_VALUE;
740 player_ = turn;
741 board_ = board;
742 committed = isCommitted;
743 }
744
745 synchronized void reset() {
746 fromRow = NO_VALUE;
747 fromCol = NO_VALUE;
748 toRow = NO_VALUE;
749 toCol = NO_VALUE;
750 }
751
752 // setters:
753
754 synchronized void player(Player p) { player_ = p; }
755 synchronized void board(Board b) { board_ = b; }
756 synchronized void from(int sr, int sc) { fromRow = sr; fromCol = sc; }
757 synchronized void to(int dr, int dc) { toRow = dr; toCol = dc; }
758
759 // accessors:
760
761 synchronized boolean isFrom(int r, int c) {
762 return fromRow== r && fromCol == c;
763 }
764 synchronized boolean isTo(int r, int c) {
765 return toRow == r && toCol == c;
766 }
767 synchronized Board board() {
768 return board_;
769 }
770 synchronized Player player() {
771 return player_;
772 }
773
774
775 // status checks:
776
777 synchronized boolean isPass() { // is this a `pass' move?
778 return (toRow == PASS_VALUE || fromRow == PASS_VALUE);
779 }
780
781 synchronized boolean isJump() {
782 return
783 (fromRow - toRow == 2) || (toRow - fromRow == 2) ||
784 (fromCol - toCol == 2) || (toCol - fromCol == 2);
785 }
786
787 synchronized boolean hasFrom() { // is from set?
788 return fromRow != NO_VALUE && fromCol != NO_VALUE;
789 }
790
791 synchronized boolean hasTo() { // is to set?
792 return toRow != NO_VALUE && toCol != NO_VALUE;
793 }
794
795
796 synchronized boolean possibleTo(int r, int c) { // is (r, c) a legal `to'?
797 return hasFrom() &&
798 withinTwo(fromRow, r) &&
799 withinTwo(fromCol, c) &&
800 board_.occupant(r, c).isEmpty();
801 }
802
803 synchronized boolean isLegal() {
804 if (isPass())
805 return true;
806 else if (!board_.occupant(toRow, toCol).isEmpty())
807 return false;
808 else if (!board_.occupant(fromRow, fromCol).same(player_))
809 return false;
810 else if (!(withinTwo(fromRow, toRow) && withinTwo(fromCol, toCol)))
811 return false;
812 else
813 return true;
814 }
815
816 synchronized void commit() { // update board to reflect move
817 if (!committed) {
818 committed = true;
819 if (isLegal() && !isPass()) {
820 if (isJump()) board_.occupy(Player.Empty, fromRow, fromCol);
821 board_.take(player_, toRow, toCol);
822 }
823 }
824 }
825
826 }
827
828 /**
829 * Mover is an abstract class to simplify code dealing with
830 * either user moves or auto moves.
831 */
832 abstract static class Mover {
833
834 // caller for move callbacks
835 protected Microscope game;
836
837 protected Mover(Microscope ap) { game = ap; }
838
839 // start a turn as player on given board
840 public abstract void startTurn(Board b, Player p);
841
842 // cancel current partial move
843 public abstract void cancel();
844
845 // return true if move not yet ready
846 public abstract boolean placing();
847
848 }
849
850 /**
851 * User builds moves via instructions/clicks by users
852 */
853 static class User extends Mover {
854
855 private Move current;
856
857 public User(Microscope ap) { super(ap); current = null; }
858
859 public synchronized void startTurn(Board b, Player p) {
860 current = new Move(p, b);
861 }
862
863 public boolean placing() {
864 return current != null && current.hasFrom() && !current.hasTo();
865 }
866
867 public synchronized void cancel() {
868 if (current != null) {
869 current.reset();
870 current = null;
871 }
872 }
873
874 public synchronized void choose(int row, int col) {
875 if (current != null) {
876 if (row == Move.PASS_VALUE) {
877 current.from(row, col);
878 game.move(current, this);
879 current = null;
880 }
881 else if (!current.hasFrom()) {
882 if (current.board().occupant(row, col).same(current.player())) {
883 current.from(row, col);
884 }
885 }
886 else {
887 current.to(row, col);
888 game.move(current, this);
889 current = null;
890 }
891 }
892 }
893
894 public synchronized boolean canMoveTo(int row, int col) {
895 return placing() && current.possibleTo(row, col);
896 }
897
898 public synchronized boolean hasMovedFrom(int row, int col) {
899 return current != null && current.isFrom(row, col);
900 }
901
902 }
903
904
905 /**
906 * AutoMover constructs Finders that compute actual moves
907 */
908 static class AutoMover extends Mover {
909
910 boolean cancelled = false;
911 RootFinder currentFinder = null;
912
913 public AutoMover(Microscope ap) {
914 super(ap);
915 }
916
917
918 public synchronized boolean placing() {
919 return currentFinder != null;
920 }
921
922 synchronized void stopPlacing() {
923 currentFinder = null;
924 }
925
926
927 public synchronized void cancel() {
928 if (placing()) {
929 currentFinder.cancel(false);
930 stopPlacing();
931 }
932 }
933
934 public synchronized void startTurn(Board board, Player player) {
935 if (!placing()) {
936 currentFinder = new RootFinder(board, player,
937 Microscope.lookAheads, this);
938 group.execute(currentFinder);
939 }
940 }
941
942 synchronized void relay(Move move) { // relay callback from finder
943 if (placing()) {
944 stopPlacing();
945 game.move(move, this);
946 }
947 }
948
949 }
950
951
952 /**
953 * Implements a classic all-possible-move search algorith using
954 * ForkJoinTasks. The move finder is not all that smart. Among
955 * other possible improvements, it could keep a cache of explored
956 * moves and avoid repeating them. This would likely speed it up
957 * since most expansions are duplicates of others. It could also
958 * be changed to prune moves, although this is unlikely to work
959 * well without better partial evaluation functions.
960 */
961 static class Finder extends RecursiveAction {
962
963 static final int NOMOVE = Integer.MIN_VALUE;
964 static final int LOSE = NOMOVE+1;
965 static final int WIN = -LOSE;
966
967 final long ours; // bits for our tiles
968 final long theirs; // bits for opponent tiles
969 final int level; // current number of lookAheads
970 final Finder next; // Each Finder is placed in a linked list by parent
971
972 // Assigned once; must be volatile since accessed by parents
973 volatile int bestScore;
974
975 Finder(long ours, long theirs, int level, Finder next) {
976 this.ours = ours;
977 this.theirs = theirs;
978 this.level = level;
979 this.next = next;
980 }
981
982 protected final void compute() {
983
984 // Handle sure wins and losses here
985 if ((ours & ~Board.BLUEBIT) == 0)
986 bestScore = LOSE;
987
988 else if ((theirs & ~Board.BLUEBIT) == 0)
989 bestScore = WIN;
990
991 else if (((ours | theirs) & Board.FULL) == Board.FULL) {
992 int score = Board.score(ours, theirs);
993 if (score > 0) bestScore = WIN;
994 else if (score < 0) bestScore = LOSE;
995 else bestScore = 0;
996 }
997
998 else
999 search();
1000 }
1001
1002
1003 final void search() {
1004 int best = NOMOVE; // For direct evaluation when level == 1
1005 Finder forked = null; // list of forked subtasks when level > 1
1006
1007 long open = ~(ours | theirs); // currently empty cells
1008 long here = 1; // travserse through bits
1009
1010 for (int k = 0; k < Board.CELLS; ++k, here <<= 1) {
1011 if ((here & ours) != 0) {
1012 /*
1013 * Step through possible destinations to find
1014 * jumps for this tile
1015 */
1016 byte[] dests = Board.jumpDestinations[k];
1017 for (int j = 0; j < dests.length; ++j) {
1018 byte d = dests[j];
1019 long dest = 1L << d;
1020
1021 if ( (dest & open) != 0) {
1022 long adjacent = Board.adjacentMasks[d];
1023 long nTheirs = theirs & ~adjacent;
1024 long nOurs = (ours & ~here) | dest | (theirs & adjacent);
1025
1026 if (level > 1)
1027 (forked =
1028 new Finder(nTheirs, nOurs, level-1, forked)).fork();
1029 else {
1030 int sc = Board.score(nOurs, nTheirs);
1031 if (sc > best) best = sc;
1032 }
1033 }
1034 }
1035 }
1036 else if ((here & open) != 0) {
1037 /*
1038 * If this cell is open, and is within 1 of one of
1039 * our tiles, it can be taken in some copy move.
1040 * It doesn't matter which of the adjacent cells
1041 * is considered to be source of copy move
1042 */
1043 long adjacent = Board.adjacentMasks[k];
1044 if ((ours & adjacent) != 0) {
1045 long nTheirs = theirs & ~adjacent;
1046 long nOurs = ours | here | (theirs & adjacent);
1047 if (level > 1)
1048 (forked = new Finder(nTheirs, nOurs, level-1, forked)).fork();
1049 else {
1050 int sc = Board.score(nOurs, nTheirs);
1051 if (sc > best) best = sc;
1052 }
1053 }
1054 }
1055 }
1056
1057 if (level > 1)
1058 collect(forked);
1059 else
1060 bestScore = best;
1061 }
1062
1063 /**
1064 * Join all subtasks and evaluate moves. Default is sub-finder version.
1065 * Overridden in RootFinder.
1066 */
1067 void collect(Finder forked) {
1068 int best = NOMOVE;
1069 while (forked != null) {
1070 while (!forked.isDone()) {
1071 // interleave joins with status checks
1072 if (isDone()) {
1073 cancelAll(forked);
1074 return;
1075 }
1076 else {
1077 ForkJoinTask<?> t = pollTask();
1078 if (t != null)
1079 t.quietlyInvoke();
1080 }
1081 }
1082 int score = -forked.bestScore; // negate opponent score
1083 if (score > best) {
1084 best = score;
1085 if (score >= WIN) {
1086 cancelAll(forked.next);
1087 break;
1088 }
1089 }
1090 forked = forked.next;
1091 }
1092
1093 bestScore = best;
1094 }
1095
1096 /**
1097 * Cancels all forked subtasks in list.
1098 */
1099 void cancelAll(Finder forked) {
1100 while (forked != null) {
1101 forked.cancel(false);
1102 forked = forked.next;
1103 }
1104 }
1105
1106 }
1107
1108 /**
1109 * Root Finder class -- wait out other finders and issue callback to game.
1110 */
1111 static class RootFinder extends Finder {
1112 final AutoMover automover;
1113 final Player player;
1114 RootFinder(Board board, Player p, int level, AutoMover automover) {
1115 super( (p.isBlue() ? (board.getBlue()| Board.BLUEBIT) : board.getGreen()),
1116 (p.isBlue() ? board.getGreen() : (board.getBlue()| Board.BLUEBIT)),
1117 level,
1118 null);
1119
1120 this.player = p;
1121 this.automover = automover;
1122 }
1123
1124
1125 /**
1126 * This differs from default version by recording
1127 * and calling back with best move
1128 */
1129 void collect(Finder forked) {
1130 int best = NOMOVE;
1131 Finder bestFinder = null;
1132 while (forked != null) {
1133 while (!forked.isDone()) {
1134 if (isDone()) {
1135 cancelAll(forked);
1136 return;
1137 }
1138 else {
1139 ForkJoinTask<?> t = pollTask();
1140 if (t != null)
1141 t.quietlyInvoke();
1142 }
1143 }
1144 int score = -forked.bestScore; // negate opponent score
1145 if (bestFinder == null || score > best) {
1146 best = score;
1147 bestFinder = forked;
1148 if (score >= WIN) {
1149 cancelAll(forked.next);
1150 break;
1151 }
1152 }
1153
1154 // Just for fun, introduce a little randomness via hashcodes
1155 else if (score == best &&
1156 !Microscope.DETERMINISTIC &&
1157 (System.identityHashCode(forked) >
1158 System.identityHashCode(bestFinder))) {
1159 bestFinder = forked;
1160 }
1161 forked = forked.next;
1162 }
1163
1164 Move move = null;
1165 if (bestFinder != null) {
1166 /*
1167 Even though accessed here,
1168 the ours and theirs vars of Finders do not
1169 need to be volatile because they are immutably
1170 established in constructors.
1171 */
1172
1173 long nextOurs = bestFinder.theirs;
1174 long nextTheirs = bestFinder.ours;
1175 long blue = player.isBlue() ? nextOurs : nextTheirs;
1176 long green = player.isBlue() ? nextTheirs: nextOurs;
1177 move = new Move(player, new Board(blue, green), true);
1178 }
1179 automover.relay(move);
1180 }
1181 }
1182
1183
1184 }