ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/loops/Microscope.java
Revision: 1.12
Committed: Wed Dec 31 17:00:58 2014 UTC (9 years, 4 months ago) by jsr166
Branch: MAIN
Changes since 1.11: +3 -3 lines
Log Message:
lexicographic import order

File Contents

# User Rev Content
1 dl 1.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 jsr166 1.6 * http://creativecommons.org/publicdomain/zero/1.0/
5 dl 1.1 */
6    
7     import java.awt.*;
8 jsr166 1.12 import java.awt.event.*;
9     import java.util.*;
10     import java.util.concurrent.*;
11 dl 1.1 import javax.swing.*;
12     import javax.swing.event.*;
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 jsr166 1.2 * The code has been mangled beyond recognition
22 jsr166 1.4 * as a test of ForkJoin.
23     */
24 dl 1.1 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 jsr166 1.2 static void onExit() {
75 dl 1.1 long now = System.currentTimeMillis();
76     System.out.println("time: " + (now - startTime) + "ms");
77     System.out.println(group.toString());
78     System.exit(0);
79 jsr166 1.2 }
80    
81 dl 1.1 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 jsr166 1.2
92 dl 1.1 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 jsr166 1.2 synchronized void setBoard(Board b) {
107     board = b;
108 dl 1.1 System.out.print(b.score(player) + " ");
109     if (boardPanel != null)
110 jsr166 1.2 boardPanel.repaint();
111 dl 1.1 }
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 jsr166 1.2 public Microscope() {
143 dl 1.1 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 jsr166 1.2 if (getDemoMode())
169 dl 1.1 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 jsr166 1.2
187 dl 1.1 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 jsr166 1.2
197 dl 1.1
198     topPanel.add(autoButton);
199     topPanel.add(modeButton);
200     topPanel.add(undoButton);
201     topPanel.add(scoreLabel);
202     add(topPanel);
203 jsr166 1.2
204 dl 1.1
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 jsr166 1.2
218 dl 1.1 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 jsr166 1.4 public void init() {
233 dl 1.1 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 jsr166 1.2
245 jsr166 1.7 public int level() { return Microscope.lookAheads; }
246 jsr166 1.2
247 dl 1.1
248     // process a move (called only from mover)
249    
250     public void move(Move m, Mover mvr) {
251 jsr166 1.2 if (mvr != mover ||
252 dl 1.1 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 jsr166 1.2 if (mvr == auto &&
268     getDemoMode() &&
269 dl 1.1 !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 jsr166 1.2
306 dl 1.1
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 jsr166 1.2
329 dl 1.1 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 jsr166 1.2
337 dl 1.1 scoreLabel.setForeground(displayColor(p));
338     scoreLabel.setText("Score: " + s);
339    
340 jsr166 1.2 if (getDemoMode())
341 dl 1.1 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 jsr166 1.2 static final int CELL_SIZE = 40; // size of a tile/cell
353    
354 dl 1.1 static final Color paleGreen = new Color(152, 251, 152);
355     static final Color darkGreen = new Color(60, 179, 113);
356 jsr166 1.2
357 dl 1.1 static final Color possibleMoveColor = Color.yellow;
358 jsr166 1.2
359 dl 1.1
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 jsr166 1.2
375     BoardPanel() {
376     setSize(new Dimension(Board.RANKS * CELL_SIZE + 5,
377 dl 1.1 Board.RANKS * CELL_SIZE + 5));
378     addMouseListener(BoardPanel.this);
379     }
380 jsr166 1.2
381 dl 1.1 public void paint(Graphics g) {
382 jsr166 1.2
383 dl 1.1 Board b = getBoard();
384     Player p = getPlayer();
385 jsr166 1.2
386 dl 1.1 // the cells
387     for (int row = 0; row < Board.RANKS; row++) {
388     for (int col = 0; col < Board.RANKS; col++) {
389 jsr166 1.2
390 dl 1.1 // Highlight selected tile and legal destinations
391     if (user.placing()) {
392 jsr166 1.2 if (user.hasMovedFrom(row, col))
393 dl 1.1 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 jsr166 1.2
400 dl 1.1 else
401     g.setColor(displayColor(b.occupant(row, col)));
402 jsr166 1.2
403 dl 1.1 // tiles are just filled rectangles
404     g.fillRect(row * CELL_SIZE, col * CELL_SIZE, CELL_SIZE, CELL_SIZE);
405     }
406     }
407 jsr166 1.2
408 dl 1.1 // 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 jsr166 1.2
415 dl 1.1 updateStatus();
416     }
417 jsr166 1.2
418 dl 1.1 public void mouseReleased(MouseEvent evt) {
419 jsr166 1.2
420 dl 1.1 int x = evt.getX();
421     int y = evt.getY();
422 jsr166 1.2
423 dl 1.1 int row = x / CELL_SIZE;
424     int col = y / CELL_SIZE;
425 jsr166 1.2
426 dl 1.1 if (Board.inBounds(row, col)) { // cell selection
427     userMove(row, col);
428     repaint();
429     }
430     }
431 jsr166 1.2
432 dl 1.1 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 jsr166 1.4 */
441 dl 1.1 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 jsr166 1.2
448 dl 1.1 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 jsr166 1.2
453 dl 1.1 /* private */ int code_;
454 jsr166 1.2
455 dl 1.1 public Player(int code) { code_ = code; }
456     public Player(Player p) { code_ = p.code_; }
457 jsr166 1.2
458 dl 1.1 public boolean same(Player p) { return code_ == p.code_; }
459 jsr166 1.2
460 dl 1.1 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 jsr166 1.2
465     public Player opponent() {
466 dl 1.1 if (code_ == GREEN) return Blue;
467     else if (code_ == BLUE) return Green;
468     else return Illegal;
469     }
470 jsr166 1.2
471 dl 1.1 }
472 jsr166 1.2
473 dl 1.1 /**
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 jsr166 1.4 */
482 jsr166 1.9 static final class Board {
483 dl 1.1
484 jsr166 1.2 /*
485 dl 1.1 First, some Constants and utilities that might as well be here
486     */
487 jsr166 1.2
488 dl 1.1 public static final int RANKS = 7;
489     public static final int CELLS = RANKS * RANKS;
490 jsr166 1.2
491 dl 1.1 static final long FULL = (1L << CELLS) - 1;
492 jsr166 1.2
493 dl 1.1 // The finder uses a spare bit to remember whose move it is.
494     static final long BLUEBIT = (1L << CELLS);
495 jsr166 1.2
496 dl 1.1 // Bits representing the adjacent cells for every position
497     static final long[] adjacentMasks = new long[CELLS];
498 jsr166 1.2
499 dl 1.1 // 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 jsr166 1.2
538    
539 dl 1.1 public static boolean inBounds(int row, int col) {
540     return (0 <= row) && (row < RANKS) && (0 <= col) && (col < RANKS);
541     }
542 jsr166 1.2
543 dl 1.1 // The representation
544 jsr166 1.2
545 dl 1.1 long blue_; // bit vector; true if occupied by blue
546     long green_; // same for green;
547 jsr166 1.2
548 dl 1.1 // constructors and intializers:
549 jsr166 1.2
550 dl 1.1 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 jsr166 1.2
554 dl 1.1 public void copyState(Board b) {
555 jsr166 1.2 blue_ = b.blue_;
556     green_ = b.green_;
557 dl 1.1 }
558    
559 jsr166 1.2 void reset() {
560     blue_ = 0L; green_ = 0L;
561 dl 1.1 }
562 jsr166 1.2
563 dl 1.1 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 jsr166 1.2
578    
579 dl 1.1 // place a tile without taking opponent tiles
580 jsr166 1.2
581 dl 1.1 public void occupy(Player player, int row, int col) {
582     long m = 1L << (row + col * RANKS);
583     long nm = ~m;
584 jsr166 1.4 if (player.code_ == Player.BLUE) {
585 dl 1.1 blue_ |= m;
586     green_ &= nm;
587     }
588 jsr166 1.2 else if (player.code_ == Player.GREEN) {
589 dl 1.1 blue_ &= nm;
590     green_ |= m;
591     }
592 jsr166 1.2 else {
593 dl 1.1 blue_ &= nm;
594     green_ &= nm;
595     }
596     }
597 jsr166 1.2
598 dl 1.1 public void unoccupy(int row, int col) {
599     long nm = ~(1L << (row + col * RANKS));
600     blue_ &= nm;
601     green_ &= nm;
602     }
603 jsr166 1.2
604    
605    
606 dl 1.1 // place a tile, taking all adjacent tiles of opponent
607 jsr166 1.2
608 dl 1.1 public void take(Player player, int row, int col) {
609 jsr166 1.8 int k = row + col * RANKS;
610 dl 1.1 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 jsr166 1.8 green_ = sourceGreen | dest | (sourceBlue & nbrMask);
621 dl 1.1 }
622     }
623 jsr166 1.2
624 dl 1.1 public boolean gameOver() {
625 jsr166 1.2 return
626 dl 1.1 (((blue_ | green_) & FULL) == FULL) ||
627     ((blue_ & ~BLUEBIT) == 0) ||
628     ((green_ & ~BLUEBIT) == 0);
629     }
630 jsr166 1.2
631    
632 dl 1.1 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 jsr166 1.2
641 dl 1.1 static int score(long b, long g) {
642 jsr166 1.2
643 dl 1.1 // much faster by splitting into ints
644     // and using clever shift-based bit counter
645 jsr166 1.2
646 dl 1.1 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 jsr166 1.2
681    
682    
683 dl 1.1 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 jsr166 1.2
694    
695 dl 1.1 }
696 jsr166 1.2
697 dl 1.1 /**
698     * Moves represent transitions across Board states
699 jsr166 1.4 */
700     static final class Move {
701 dl 1.1
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 jsr166 1.2
705 dl 1.1 // utilities for classifying moves
706 jsr166 1.2
707     public static boolean twoFrom(int a, int b) {
708     return (a - b == 2) || (b - a == 2);
709 dl 1.1 }
710 jsr166 1.2
711     public static boolean withinTwo(int a, int b) {
712 dl 1.1 int diff = a - b; return -2 <= diff && diff <= 2;
713     }
714 jsr166 1.2
715 dl 1.1 // representations
716 jsr166 1.2
717 dl 1.1 int fromRow;
718     int fromCol;
719 jsr166 1.2
720 dl 1.1 int toRow;
721     int toCol;
722 jsr166 1.2
723 dl 1.1 Player player_;
724     Board board_;
725 jsr166 1.2
726 dl 1.1 boolean committed = false; // true if board reflects move
727 jsr166 1.2
728 dl 1.1 // constructors and intializers
729 jsr166 1.2
730     public Move(Player turn, Board board) {
731 dl 1.1 fromRow = NO_VALUE; fromCol = NO_VALUE;
732     toRow = NO_VALUE; toCol = NO_VALUE;
733     player_ = turn;
734     board_ = board;
735     }
736 jsr166 1.2
737     public Move(Player turn, Board board, boolean isCommitted) {
738 dl 1.1 fromRow = NO_VALUE; fromCol = NO_VALUE;
739     toRow = NO_VALUE; toCol = NO_VALUE;
740     player_ = turn;
741     board_ = board;
742     committed = isCommitted;
743     }
744 jsr166 1.2
745 dl 1.1 synchronized void reset() {
746     fromRow = NO_VALUE;
747     fromCol = NO_VALUE;
748     toRow = NO_VALUE;
749     toCol = NO_VALUE;
750     }
751 jsr166 1.2
752 dl 1.1 // setters:
753 jsr166 1.2
754 jsr166 1.8 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 dl 1.1 synchronized void to(int dr, int dc) { toRow = dr; toCol = dc; }
758 jsr166 1.2
759 dl 1.1 // accessors:
760 jsr166 1.2
761     synchronized boolean isFrom(int r, int c) {
762     return fromRow== r && fromCol == c;
763 dl 1.1 }
764 jsr166 1.9 synchronized boolean isTo(int r, int c) {
765 jsr166 1.2 return toRow == r && toCol == c;
766 dl 1.1 }
767 jsr166 1.2 synchronized Board board() {
768     return board_;
769 dl 1.1 }
770 jsr166 1.2 synchronized Player player() {
771     return player_;
772 dl 1.1 }
773 jsr166 1.2
774 dl 1.1
775     // status checks:
776 jsr166 1.2
777 dl 1.1 synchronized boolean isPass() { // is this a `pass' move?
778     return (toRow == PASS_VALUE || fromRow == PASS_VALUE);
779     }
780 jsr166 1.2
781 dl 1.1 synchronized boolean isJump() {
782 jsr166 1.2 return
783 dl 1.1 (fromRow - toRow == 2) || (toRow - fromRow == 2) ||
784     (fromCol - toCol == 2) || (toCol - fromCol == 2);
785     }
786 jsr166 1.2
787 dl 1.1 synchronized boolean hasFrom() { // is from set?
788     return fromRow != NO_VALUE && fromCol != NO_VALUE;
789     }
790 jsr166 1.2
791 dl 1.1 synchronized boolean hasTo() { // is to set?
792     return toRow != NO_VALUE && toCol != NO_VALUE;
793     }
794 jsr166 1.2
795    
796 dl 1.1 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 jsr166 1.2
803 dl 1.1 synchronized boolean isLegal() {
804 jsr166 1.2 if (isPass())
805 dl 1.1 return true;
806 jsr166 1.2 else if (!board_.occupant(toRow, toCol).isEmpty())
807 dl 1.1 return false;
808 jsr166 1.2 else if (!board_.occupant(fromRow, fromCol).same(player_))
809 dl 1.1 return false;
810 jsr166 1.2 else if (!(withinTwo(fromRow, toRow) && withinTwo(fromCol, toCol)))
811 dl 1.1 return false;
812     else
813     return true;
814     }
815 jsr166 1.2
816 dl 1.1 synchronized void commit() { // update board to reflect move
817     if (!committed) {
818     committed = true;
819 jsr166 1.4 if (isLegal() && !isPass()) {
820 dl 1.1 if (isJump()) board_.occupy(Player.Empty, fromRow, fromCol);
821     board_.take(player_, toRow, toCol);
822     }
823     }
824     }
825 jsr166 1.2
826 dl 1.1 }
827 jsr166 1.2
828 dl 1.1 /**
829     * Mover is an abstract class to simplify code dealing with
830     * either user moves or auto moves.
831 jsr166 1.3 */
832     abstract static class Mover {
833 jsr166 1.2
834 dl 1.1 // caller for move callbacks
835     protected Microscope game;
836 jsr166 1.2
837 dl 1.1 protected Mover(Microscope ap) { game = ap; }
838 jsr166 1.2
839 dl 1.1 // start a turn as player on given board
840     public abstract void startTurn(Board b, Player p);
841 jsr166 1.2
842 dl 1.1 // cancel current partial move
843     public abstract void cancel();
844 jsr166 1.2
845 dl 1.1 // return true if move not yet ready
846     public abstract boolean placing();
847 jsr166 1.2
848 dl 1.1 }
849 jsr166 1.2
850 dl 1.1 /**
851 jsr166 1.4 * User builds moves via instructions/clicks by users
852     */
853 dl 1.1 static class User extends Mover {
854    
855     private Move current;
856 jsr166 1.2
857 dl 1.1 public User(Microscope ap) { super(ap); current = null; }
858 jsr166 1.2
859 dl 1.1 public synchronized void startTurn(Board b, Player p) {
860     current = new Move(p, b);
861     }
862 jsr166 1.2
863     public boolean placing() {
864     return current != null && current.hasFrom() && !current.hasTo();
865 dl 1.1 }
866 jsr166 1.2
867     public synchronized void cancel() {
868 dl 1.1 if (current != null) {
869 jsr166 1.2 current.reset();
870     current = null;
871 dl 1.1 }
872     }
873 jsr166 1.2
874 dl 1.1 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 jsr166 1.2
894 dl 1.1 public synchronized boolean canMoveTo(int row, int col) {
895     return placing() && current.possibleTo(row, col);
896     }
897 jsr166 1.2
898 dl 1.1 public synchronized boolean hasMovedFrom(int row, int col) {
899     return current != null && current.isFrom(row, col);
900     }
901 jsr166 1.2
902 dl 1.1 }
903    
904    
905     /**
906 jsr166 1.4 * AutoMover constructs Finders that compute actual moves
907     */
908 dl 1.1 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 jsr166 1.2
917    
918     public synchronized boolean placing() {
919     return currentFinder != null;
920 dl 1.1 }
921    
922 jsr166 1.2 synchronized void stopPlacing() {
923 dl 1.1 currentFinder = null;
924     }
925 jsr166 1.2
926    
927 dl 1.1 public synchronized void cancel() {
928 jsr166 1.4 if (placing()) {
929 dl 1.1 currentFinder.cancel(false);
930     stopPlacing();
931     }
932     }
933    
934     public synchronized void startTurn(Board board, Player player) {
935     if (!placing()) {
936 jsr166 1.2 currentFinder = new RootFinder(board, player,
937 dl 1.1 Microscope.lookAheads, this);
938     group.execute(currentFinder);
939     }
940     }
941 jsr166 1.2
942 dl 1.1 synchronized void relay(Move move) { // relay callback from finder
943     if (placing()) {
944     stopPlacing();
945     game.move(move, this);
946     }
947     }
948 jsr166 1.2
949 dl 1.1 }
950 jsr166 1.2
951 dl 1.1
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 jsr166 1.4 */
961 dl 1.1 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 jsr166 1.2
967 dl 1.1 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 jsr166 1.2 if ((ours & ~Board.BLUEBIT) == 0)
986 dl 1.1 bestScore = LOSE;
987    
988 jsr166 1.2 else if ((theirs & ~Board.BLUEBIT) == 0)
989 dl 1.1 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 jsr166 1.2 else
999 dl 1.1 search();
1000     }
1001 jsr166 1.2
1002 dl 1.1
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 jsr166 1.2
1007 dl 1.1 long open = ~(ours | theirs); // currently empty cells
1008 jsr166 1.10 long here = 1; // traverse through bits
1009 jsr166 1.2
1010 dl 1.1 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 jsr166 1.2
1021 dl 1.1 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 jsr166 1.2 if (level > 1)
1027     (forked =
1028 dl 1.1 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 jsr166 1.2 if (level > 1)
1048 dl 1.1 (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 jsr166 1.4 * Overridden in RootFinder.
1066     */
1067 dl 1.1 void collect(Finder forked) {
1068     int best = NOMOVE;
1069     while (forked != null) {
1070 jsr166 1.2 while (!forked.isDone()) {
1071     // interleave joins with status checks
1072 dl 1.1 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 jsr166 1.4 * Cancels all forked subtasks in list.
1098     */
1099 dl 1.1 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 jsr166 1.4 */
1111 dl 1.1 static class RootFinder extends Finder {
1112 jsr166 1.2 final AutoMover automover;
1113 dl 1.1 final Player player;
1114     RootFinder(Board board, Player p, int level, AutoMover automover) {
1115 jsr166 1.5 super( (p.isBlue() ? (board.getBlue()| Board.BLUEBIT) : board.getGreen()),
1116     (p.isBlue() ? board.getGreen() : (board.getBlue()| Board.BLUEBIT)),
1117 dl 1.1 level,
1118     null);
1119 jsr166 1.2
1120 dl 1.1 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 jsr166 1.4 */
1129 dl 1.1 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 jsr166 1.2
1154 dl 1.1 // Just for fun, introduce a little randomness via hashcodes
1155     else if (score == best &&
1156     !Microscope.DETERMINISTIC &&
1157 jsr166 1.2 (System.identityHashCode(forked) >
1158 dl 1.1 System.identityHashCode(bestFinder))) {
1159     bestFinder = forked;
1160     }
1161     forked = forked.next;
1162     }
1163 jsr166 1.2
1164 dl 1.1 Move move = null;
1165     if (bestFinder != null) {
1166 jsr166 1.2 /*
1167 dl 1.1 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 jsr166 1.2
1173 dl 1.1 long nextOurs = bestFinder.theirs;
1174     long nextTheirs = bestFinder.ours;
1175 jsr166 1.5 long blue = player.isBlue() ? nextOurs : nextTheirs;
1176 jsr166 1.11 long green = player.isBlue() ? nextTheirs : nextOurs;
1177 dl 1.1 move = new Move(player, new Board(blue, green), true);
1178     }
1179     automover.relay(move);
1180     }
1181     }
1182    
1183 jsr166 1.2
1184 dl 1.1 }