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