ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/loops/Microscope.java
Revision: 1.15
Committed: Mon Aug 10 03:13:33 2015 UTC (8 years, 9 months ago) by jsr166
Branch: MAIN
Changes since 1.14: +0 -1 lines
Log Message:
delete unwanted blank lines

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