ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/loops/Microscope.java
Revision: 1.14
Committed: Thu Jan 15 18:35:03 2015 UTC (9 years, 3 months ago) by jsr166
Branch: MAIN
Changes since 1.13: +1 -1 lines
Log Message:
typo

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