ViewVC Help
View File | Revision Log | Show Annotations | Download File | Root Listing
root/jsr166/jsr166/src/test/loops/Microscope.java
Revision: 1.17
Committed: Thu Sep 15 06:45:56 2016 UTC (7 years, 7 months ago) by jsr166
Branch: MAIN
CVS Tags: HEAD
Changes since 1.16: +2 -2 lines
Log Message:
whitespace

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 jsr166 1.17 return (0 <= row) && (row < RANKS) && (0 <= col) && (col < RANKS);
528 dl 1.1 }
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 jsr166 1.17 if ((0 <= row) && (row < RANKS) && (0 <= col) && (col < RANKS)) {
555 dl 1.1 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 jsr166 1.16 /** Places a tile without taking opponent tiles. */
565 dl 1.1 public void occupy(Player player, int row, int col) {
566     long m = 1L << (row + col * RANKS);
567     long nm = ~m;
568 jsr166 1.4 if (player.code_ == Player.BLUE) {
569 dl 1.1 blue_ |= m;
570     green_ &= nm;
571     }
572 jsr166 1.2 else if (player.code_ == Player.GREEN) {
573 dl 1.1 blue_ &= nm;
574     green_ |= m;
575     }
576 jsr166 1.2 else {
577 dl 1.1 blue_ &= nm;
578     green_ &= nm;
579     }
580     }
581 jsr166 1.2
582 dl 1.1 public void unoccupy(int row, int col) {
583     long nm = ~(1L << (row + col * RANKS));
584     blue_ &= nm;
585     green_ &= nm;
586     }
587 jsr166 1.2
588 jsr166 1.16 /** Places a tile, taking all adjacent tiles of opponent. */
589 dl 1.1 public void take(Player player, int row, int col) {
590 jsr166 1.8 int k = row + col * RANKS;
591 dl 1.1 long dest = 1L << k;
592     long nbrMask = adjacentMasks[k];
593     long sourceBlue = blue_;
594     long sourceGreen = green_;
595     if (player.code_ == Player.BLUE) {
596     blue_ = sourceBlue | dest | (sourceGreen & nbrMask);
597     green_ = sourceGreen & ~(sourceGreen & nbrMask);
598     }
599     else {
600     blue_ = sourceBlue & ~(sourceBlue & nbrMask);
601 jsr166 1.8 green_ = sourceGreen | dest | (sourceBlue & nbrMask);
602 dl 1.1 }
603     }
604 jsr166 1.2
605 dl 1.1 public boolean gameOver() {
606 jsr166 1.2 return
607 dl 1.1 (((blue_ | green_) & FULL) == FULL) ||
608     ((blue_ & ~BLUEBIT) == 0) ||
609     ((green_ & ~BLUEBIT) == 0);
610     }
611 jsr166 1.2
612 dl 1.1 public int score(Player player) {
613     if (player.isBlue()) {
614     return score(blue_, green_);
615     }
616     else {
617     return score(green_, blue_);
618     }
619     }
620 jsr166 1.2
621 dl 1.1 static int score(long b, long g) {
622 jsr166 1.2
623 dl 1.1 // much faster by splitting into ints
624     // and using clever shift-based bit counter
625 jsr166 1.2
626 dl 1.1 int lb = (int)(b & ((1L << 32) - 1));
627     int hb = ((int)(b >>> 32)) & ((1 << (CELLS - 32)) - 1);
628    
629     lb -= (0xaaaaaaaa & lb) >>> 1;
630     lb = (lb & 0x33333333) + ((lb >>> 2) & 0x33333333);
631     lb = lb + (lb >>> 4) & 0x0f0f0f0f;
632     lb += lb >>> 8;
633     lb += lb >>> 16;
634    
635     hb -= (0xaaaaaaaa & hb) >>> 1;
636     hb = (hb & 0x33333333) + ((hb >>> 2) & 0x33333333);
637     hb = hb + (hb >>> 4) & 0x0f0f0f0f;
638     hb += hb >>> 8;
639     hb += hb >>> 16;
640    
641     hb = ((lb + hb) & 0xff);
642    
643     int lg = (int)(g & ((1L << 32) - 1));
644     int hg = ((int)(g >>> 32)) & ((1 << (CELLS - 32)) - 1);
645    
646     lg -= (0xaaaaaaaa & lg) >>> 1;
647     lg = (lg & 0x33333333) + ((lg >>> 2) & 0x33333333);
648     lg = lg + (lg >>> 4) & 0x0f0f0f0f;
649     lg += lg >>> 8;
650     lg += lg >>> 16;
651    
652     hg -= (0xaaaaaaaa & hg) >>> 1;
653     hg = (hg & 0x33333333) + ((hg >>> 2) & 0x33333333);
654     hg = hg + (hg >>> 4) & 0x0f0f0f0f;
655     hg += hg >>> 8;
656     hg += hg >>> 16;
657    
658     return hb - ((lg + hg) & 0xff);
659     }
660 jsr166 1.2
661 dl 1.1 static int slowscore(long b, long g) {
662     int score = 0;
663     for (int l = 0; l < CELLS; ++l) {
664     score += (int)(b & 1);
665     b >>>= 1;
666     score -= (int)(g & 1);
667     g >>>= 1;
668     }
669     return score;
670     }
671     }
672 jsr166 1.2
673 dl 1.1 /**
674     * Moves represent transitions across Board states
675 jsr166 1.4 */
676     static final class Move {
677 dl 1.1
678     static final int NO_VALUE = -1; // row/col value if not yet set
679     static final int PASS_VALUE = -2; // special value for pass moves
680 jsr166 1.2
681 dl 1.1 // utilities for classifying moves
682 jsr166 1.2
683     public static boolean twoFrom(int a, int b) {
684     return (a - b == 2) || (b - a == 2);
685 dl 1.1 }
686 jsr166 1.2
687     public static boolean withinTwo(int a, int b) {
688 dl 1.1 int diff = a - b; return -2 <= diff && diff <= 2;
689     }
690 jsr166 1.2
691 dl 1.1 // representations
692 jsr166 1.2
693 dl 1.1 int fromRow;
694     int fromCol;
695 jsr166 1.2
696 dl 1.1 int toRow;
697     int toCol;
698 jsr166 1.2
699 dl 1.1 Player player_;
700     Board board_;
701 jsr166 1.2
702 dl 1.1 boolean committed = false; // true if board reflects move
703 jsr166 1.2
704 dl 1.1 // constructors and intializers
705 jsr166 1.2
706     public Move(Player turn, Board board) {
707 dl 1.1 fromRow = NO_VALUE; fromCol = NO_VALUE;
708     toRow = NO_VALUE; toCol = NO_VALUE;
709     player_ = turn;
710     board_ = board;
711     }
712 jsr166 1.2
713     public Move(Player turn, Board board, boolean isCommitted) {
714 dl 1.1 fromRow = NO_VALUE; fromCol = NO_VALUE;
715     toRow = NO_VALUE; toCol = NO_VALUE;
716     player_ = turn;
717     board_ = board;
718     committed = isCommitted;
719     }
720 jsr166 1.2
721 dl 1.1 synchronized void reset() {
722     fromRow = NO_VALUE;
723     fromCol = NO_VALUE;
724     toRow = NO_VALUE;
725     toCol = NO_VALUE;
726     }
727 jsr166 1.2
728 dl 1.1 // setters:
729 jsr166 1.2
730 jsr166 1.8 synchronized void player(Player p) { player_ = p; }
731     synchronized void board(Board b) { board_ = b; }
732     synchronized void from(int sr, int sc) { fromRow = sr; fromCol = sc; }
733 dl 1.1 synchronized void to(int dr, int dc) { toRow = dr; toCol = dc; }
734 jsr166 1.2
735 dl 1.1 // accessors:
736 jsr166 1.2
737     synchronized boolean isFrom(int r, int c) {
738     return fromRow== r && fromCol == c;
739 dl 1.1 }
740 jsr166 1.9 synchronized boolean isTo(int r, int c) {
741 jsr166 1.2 return toRow == r && toCol == c;
742 dl 1.1 }
743 jsr166 1.2 synchronized Board board() {
744     return board_;
745 dl 1.1 }
746 jsr166 1.2 synchronized Player player() {
747     return player_;
748 dl 1.1 }
749 jsr166 1.2
750 dl 1.1
751     // status checks:
752 jsr166 1.2
753 dl 1.1 synchronized boolean isPass() { // is this a `pass' move?
754     return (toRow == PASS_VALUE || fromRow == PASS_VALUE);
755     }
756 jsr166 1.2
757 dl 1.1 synchronized boolean isJump() {
758 jsr166 1.2 return
759 dl 1.1 (fromRow - toRow == 2) || (toRow - fromRow == 2) ||
760     (fromCol - toCol == 2) || (toCol - fromCol == 2);
761     }
762 jsr166 1.2
763 dl 1.1 synchronized boolean hasFrom() { // is from set?
764     return fromRow != NO_VALUE && fromCol != NO_VALUE;
765     }
766 jsr166 1.2
767 dl 1.1 synchronized boolean hasTo() { // is to set?
768     return toRow != NO_VALUE && toCol != NO_VALUE;
769     }
770 jsr166 1.2
771 dl 1.1 synchronized boolean possibleTo(int r, int c) { // is (r, c) a legal `to'?
772     return hasFrom() &&
773     withinTwo(fromRow, r) &&
774     withinTwo(fromCol, c) &&
775     board_.occupant(r, c).isEmpty();
776     }
777 jsr166 1.2
778 dl 1.1 synchronized boolean isLegal() {
779 jsr166 1.2 if (isPass())
780 dl 1.1 return true;
781 jsr166 1.2 else if (!board_.occupant(toRow, toCol).isEmpty())
782 dl 1.1 return false;
783 jsr166 1.2 else if (!board_.occupant(fromRow, fromCol).same(player_))
784 dl 1.1 return false;
785 jsr166 1.2 else if (!(withinTwo(fromRow, toRow) && withinTwo(fromCol, toCol)))
786 dl 1.1 return false;
787     else
788     return true;
789     }
790 jsr166 1.2
791 dl 1.1 synchronized void commit() { // update board to reflect move
792     if (!committed) {
793     committed = true;
794 jsr166 1.4 if (isLegal() && !isPass()) {
795 dl 1.1 if (isJump()) board_.occupy(Player.Empty, fromRow, fromCol);
796     board_.take(player_, toRow, toCol);
797     }
798     }
799     }
800 jsr166 1.2
801 dl 1.1 }
802 jsr166 1.2
803 dl 1.1 /**
804     * Mover is an abstract class to simplify code dealing with
805     * either user moves or auto moves.
806 jsr166 1.3 */
807     abstract static class Mover {
808 jsr166 1.2
809 dl 1.1 // caller for move callbacks
810     protected Microscope game;
811 jsr166 1.2
812 dl 1.1 protected Mover(Microscope ap) { game = ap; }
813 jsr166 1.2
814 dl 1.1 // start a turn as player on given board
815     public abstract void startTurn(Board b, Player p);
816 jsr166 1.2
817 dl 1.1 // cancel current partial move
818     public abstract void cancel();
819 jsr166 1.2
820 dl 1.1 // return true if move not yet ready
821     public abstract boolean placing();
822 jsr166 1.2
823 dl 1.1 }
824 jsr166 1.2
825 dl 1.1 /**
826 jsr166 1.4 * User builds moves via instructions/clicks by users
827     */
828 dl 1.1 static class User extends Mover {
829    
830     private Move current;
831 jsr166 1.2
832 dl 1.1 public User(Microscope ap) { super(ap); current = null; }
833 jsr166 1.2
834 dl 1.1 public synchronized void startTurn(Board b, Player p) {
835     current = new Move(p, b);
836     }
837 jsr166 1.2
838     public boolean placing() {
839     return current != null && current.hasFrom() && !current.hasTo();
840 dl 1.1 }
841 jsr166 1.2
842     public synchronized void cancel() {
843 dl 1.1 if (current != null) {
844 jsr166 1.2 current.reset();
845     current = null;
846 dl 1.1 }
847     }
848 jsr166 1.2
849 dl 1.1 public synchronized void choose(int row, int col) {
850     if (current != null) {
851     if (row == Move.PASS_VALUE) {
852     current.from(row, col);
853     game.move(current, this);
854     current = null;
855     }
856     else if (!current.hasFrom()) {
857     if (current.board().occupant(row, col).same(current.player())) {
858     current.from(row, col);
859     }
860     }
861     else {
862     current.to(row, col);
863     game.move(current, this);
864     current = null;
865     }
866     }
867     }
868 jsr166 1.2
869 dl 1.1 public synchronized boolean canMoveTo(int row, int col) {
870     return placing() && current.possibleTo(row, col);
871     }
872 jsr166 1.2
873 dl 1.1 public synchronized boolean hasMovedFrom(int row, int col) {
874     return current != null && current.isFrom(row, col);
875     }
876 jsr166 1.2
877 dl 1.1 }
878    
879     /**
880 jsr166 1.4 * AutoMover constructs Finders that compute actual moves
881     */
882 dl 1.1 static class AutoMover extends Mover {
883    
884     boolean cancelled = false;
885     RootFinder currentFinder = null;
886    
887     public AutoMover(Microscope ap) {
888     super(ap);
889     }
890 jsr166 1.2
891     public synchronized boolean placing() {
892     return currentFinder != null;
893 dl 1.1 }
894    
895 jsr166 1.2 synchronized void stopPlacing() {
896 dl 1.1 currentFinder = null;
897     }
898 jsr166 1.2
899 dl 1.1 public synchronized void cancel() {
900 jsr166 1.4 if (placing()) {
901 dl 1.1 currentFinder.cancel(false);
902     stopPlacing();
903     }
904     }
905    
906     public synchronized void startTurn(Board board, Player player) {
907     if (!placing()) {
908 jsr166 1.2 currentFinder = new RootFinder(board, player,
909 dl 1.1 Microscope.lookAheads, this);
910     group.execute(currentFinder);
911     }
912     }
913 jsr166 1.2
914 dl 1.1 synchronized void relay(Move move) { // relay callback from finder
915     if (placing()) {
916     stopPlacing();
917     game.move(move, this);
918     }
919     }
920 jsr166 1.2
921 dl 1.1 }
922 jsr166 1.2
923 dl 1.1 /**
924 jsr166 1.14 * Implements a classic all-possible-move search algorithm using
925 dl 1.1 * ForkJoinTasks. The move finder is not all that smart. Among
926     * other possible improvements, it could keep a cache of explored
927     * moves and avoid repeating them. This would likely speed it up
928     * since most expansions are duplicates of others. It could also
929     * be changed to prune moves, although this is unlikely to work
930     * well without better partial evaluation functions.
931 jsr166 1.4 */
932 dl 1.1 static class Finder extends RecursiveAction {
933    
934     static final int NOMOVE = Integer.MIN_VALUE;
935     static final int LOSE = NOMOVE+1;
936     static final int WIN = -LOSE;
937 jsr166 1.2
938 dl 1.1 final long ours; // bits for our tiles
939     final long theirs; // bits for opponent tiles
940     final int level; // current number of lookAheads
941     final Finder next; // Each Finder is placed in a linked list by parent
942    
943     // Assigned once; must be volatile since accessed by parents
944     volatile int bestScore;
945    
946     Finder(long ours, long theirs, int level, Finder next) {
947     this.ours = ours;
948     this.theirs = theirs;
949     this.level = level;
950     this.next = next;
951     }
952    
953     protected final void compute() {
954    
955     // Handle sure wins and losses here
956 jsr166 1.2 if ((ours & ~Board.BLUEBIT) == 0)
957 dl 1.1 bestScore = LOSE;
958    
959 jsr166 1.2 else if ((theirs & ~Board.BLUEBIT) == 0)
960 dl 1.1 bestScore = WIN;
961    
962     else if (((ours | theirs) & Board.FULL) == Board.FULL) {
963     int score = Board.score(ours, theirs);
964     if (score > 0) bestScore = WIN;
965     else if (score < 0) bestScore = LOSE;
966     else bestScore = 0;
967     }
968    
969 jsr166 1.2 else
970 dl 1.1 search();
971     }
972 jsr166 1.2
973 dl 1.1 final void search() {
974     int best = NOMOVE; // For direct evaluation when level == 1
975     Finder forked = null; // list of forked subtasks when level > 1
976 jsr166 1.2
977 dl 1.1 long open = ~(ours | theirs); // currently empty cells
978 jsr166 1.10 long here = 1; // traverse through bits
979 jsr166 1.2
980 dl 1.1 for (int k = 0; k < Board.CELLS; ++k, here <<= 1) {
981     if ((here & ours) != 0) {
982     /*
983     * Step through possible destinations to find
984     * jumps for this tile
985     */
986     byte[] dests = Board.jumpDestinations[k];
987     for (int j = 0; j < dests.length; ++j) {
988     byte d = dests[j];
989     long dest = 1L << d;
990 jsr166 1.2
991 dl 1.1 if ( (dest & open) != 0) {
992     long adjacent = Board.adjacentMasks[d];
993     long nTheirs = theirs & ~adjacent;
994     long nOurs = (ours & ~here) | dest | (theirs & adjacent);
995    
996 jsr166 1.2 if (level > 1)
997     (forked =
998 dl 1.1 new Finder(nTheirs, nOurs, level-1, forked)).fork();
999     else {
1000     int sc = Board.score(nOurs, nTheirs);
1001     if (sc > best) best = sc;
1002     }
1003     }
1004     }
1005     }
1006     else if ((here & open) != 0) {
1007     /*
1008     * If this cell is open, and is within 1 of one of
1009     * our tiles, it can be taken in some copy move.
1010     * It doesn't matter which of the adjacent cells
1011     * is considered to be source of copy move
1012     */
1013     long adjacent = Board.adjacentMasks[k];
1014     if ((ours & adjacent) != 0) {
1015     long nTheirs = theirs & ~adjacent;
1016     long nOurs = ours | here | (theirs & adjacent);
1017 jsr166 1.2 if (level > 1)
1018 dl 1.1 (forked = new Finder(nTheirs, nOurs, level-1, forked)).fork();
1019     else {
1020     int sc = Board.score(nOurs, nTheirs);
1021     if (sc > best) best = sc;
1022     }
1023     }
1024     }
1025     }
1026    
1027     if (level > 1)
1028     collect(forked);
1029     else
1030     bestScore = best;
1031     }
1032    
1033     /**
1034     * Join all subtasks and evaluate moves. Default is sub-finder version.
1035 jsr166 1.4 * Overridden in RootFinder.
1036     */
1037 dl 1.1 void collect(Finder forked) {
1038     int best = NOMOVE;
1039     while (forked != null) {
1040 jsr166 1.2 while (!forked.isDone()) {
1041     // interleave joins with status checks
1042 dl 1.1 if (isDone()) {
1043     cancelAll(forked);
1044     return;
1045     }
1046     else {
1047     ForkJoinTask<?> t = pollTask();
1048     if (t != null)
1049     t.quietlyInvoke();
1050     }
1051     }
1052     int score = -forked.bestScore; // negate opponent score
1053     if (score > best) {
1054     best = score;
1055     if (score >= WIN) {
1056     cancelAll(forked.next);
1057     break;
1058     }
1059     }
1060     forked = forked.next;
1061     }
1062    
1063     bestScore = best;
1064     }
1065    
1066     /**
1067 jsr166 1.4 * Cancels all forked subtasks in list.
1068     */
1069 dl 1.1 void cancelAll(Finder forked) {
1070     while (forked != null) {
1071     forked.cancel(false);
1072     forked = forked.next;
1073     }
1074     }
1075    
1076     }
1077    
1078     /**
1079     * Root Finder class -- wait out other finders and issue callback to game.
1080 jsr166 1.4 */
1081 dl 1.1 static class RootFinder extends Finder {
1082 jsr166 1.2 final AutoMover automover;
1083 dl 1.1 final Player player;
1084     RootFinder(Board board, Player p, int level, AutoMover automover) {
1085 jsr166 1.5 super( (p.isBlue() ? (board.getBlue()| Board.BLUEBIT) : board.getGreen()),
1086     (p.isBlue() ? board.getGreen() : (board.getBlue()| Board.BLUEBIT)),
1087 dl 1.1 level,
1088     null);
1089 jsr166 1.2
1090 dl 1.1 this.player = p;
1091     this.automover = automover;
1092     }
1093    
1094     /**
1095     * This differs from default version by recording
1096     * and calling back with best move
1097 jsr166 1.4 */
1098 dl 1.1 void collect(Finder forked) {
1099     int best = NOMOVE;
1100     Finder bestFinder = null;
1101     while (forked != null) {
1102     while (!forked.isDone()) {
1103     if (isDone()) {
1104     cancelAll(forked);
1105     return;
1106     }
1107     else {
1108     ForkJoinTask<?> t = pollTask();
1109     if (t != null)
1110     t.quietlyInvoke();
1111     }
1112     }
1113     int score = -forked.bestScore; // negate opponent score
1114     if (bestFinder == null || score > best) {
1115     best = score;
1116     bestFinder = forked;
1117     if (score >= WIN) {
1118     cancelAll(forked.next);
1119     break;
1120     }
1121     }
1122 jsr166 1.2
1123 dl 1.1 // Just for fun, introduce a little randomness via hashcodes
1124     else if (score == best &&
1125     !Microscope.DETERMINISTIC &&
1126 jsr166 1.2 (System.identityHashCode(forked) >
1127 dl 1.1 System.identityHashCode(bestFinder))) {
1128     bestFinder = forked;
1129     }
1130     forked = forked.next;
1131     }
1132 jsr166 1.2
1133 dl 1.1 Move move = null;
1134     if (bestFinder != null) {
1135 jsr166 1.2 /*
1136 dl 1.1 Even though accessed here,
1137     the ours and theirs vars of Finders do not
1138     need to be volatile because they are immutably
1139     established in constructors.
1140     */
1141 jsr166 1.2
1142 dl 1.1 long nextOurs = bestFinder.theirs;
1143     long nextTheirs = bestFinder.ours;
1144 jsr166 1.5 long blue = player.isBlue() ? nextOurs : nextTheirs;
1145 jsr166 1.11 long green = player.isBlue() ? nextTheirs : nextOurs;
1146 dl 1.1 move = new Move(player, new Board(blue, green), true);
1147     }
1148     automover.relay(move);
1149     }
1150     }
1151     }