package org.myrobotlab.chess; // // ChessApp.java // A simple Java Swing applet to play chess. // Copyright (c) 2001 Peter Hunter. GUIService was originally based on code // by David Dagon (c) 1997 David Dagon and is used with his permission. // The search code is heavily based on Tom Kerrigan's tscp, for which he // owns the copyright, and is used with his permission. All rights are // reserved by the owners of the respective copyrights. import java.awt.BorderLayout; import java.awt.Container; import java.awt.FlowLayout; import java.awt.GridLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyVetoException; import java.beans.VetoableChangeListener; import java.util.Collection; import java.util.Iterator; import javax.swing.JApplet; import javax.swing.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import org.myrobotlab.logging.LoggerFactory; import org.op.chess.ChessBoard; import org.slf4j.Logger; public final class ChessApp extends JApplet implements Constants, VetoableChangeListener, PropertyChangeListener { final class Thinker extends Thread { @Override public void run() { think(); if (searcher.isStopped()) return; final HMove best = searcher.getBest(); if (best == null) { System.out.println("(no legal moves)"); computerSide = EMPTY; return; } board.makeMove(best); searcher.board.makeMove(best); // showStatus("Computer move: " + best.toString()); makeMove(best); chessView.switchMoveMarkers(board.side == LIGHT); isResult(); chessView.setMoving(false); guessedMove = searcher.getBestNext(); if (guessedMove != null) { searcher.board.makeMove(guessedMove); searcher.setStopTime(Long.MAX_VALUE); searcher.shiftPV(); thinkThread = new Thinker(); thinkThread.start(); } } } /** * */ private static final long serialVersionUID = 1L; public final static Logger log = LoggerFactory.getLogger(ChessApp.class.getCanonicalName()); private Board board = new Board(); private Search searcher = new Search(); private ChessBoard chessView; private int computerSide = DARK; private final int[] playTime = { 3000, 5000, 10000, 20000, 30000, 60000 }; private JLabel principalVariation; // private int moves = 0; private int maxTime = 10000; private Thread thinkThread = null; private HMove guessedMove = null; private void computerMove() { searcher.stopThinking(); try { if (thinkThread != null) thinkThread.join(); } catch (InterruptedException ignore) { } searcher.restartThinking(); searcher.clearPV(); thinkThread = new Thinker(); thinkThread.start(); searcher.setStopTime(System.currentTimeMillis() + maxTime); showStatus("Thinking..."); } @Override public void init() { super.init(); // showStatus ("Please Wait; Program Loading"); chessView = new ChessBoard(); chessView.addPropertyChangeListener(this); chessView.addVetoableChangeListener(this); JPanel p1 = new JPanel(); p1.setLayout(new FlowLayout(FlowLayout.LEFT)); // add (flipBox = new Checkbox("Flip Board")); JButton resetButton = new JButton("New Game"); resetButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { playNewGame(); } }); p1.add(resetButton); JButton switchSidesButton = new JButton("Switch Sides"); switchSidesButton.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { computerSide = board.side; guessedMove = null; computerMove(); } }); p1.add(switchSidesButton); String[] timeStrings = { "3 seconds", "5 seconds", "10 seconds", "20 seconds", "30 seconds", "1 minute" }; JComboBox<String> timeBox = new JComboBox<String>(timeStrings); timeBox.setSelectedIndex(2); timeBox.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JComboBox<Object> cb = (JComboBox<Object>) e.getSource(); int selection = cb.getSelectedIndex(); setMaxTime(playTime[selection]); } }); p1.add(timeBox); p1.add(new JLabel("per computer move")); JCheckBox showPV = new JCheckBox("Show thinking"); showPV.setSelected(true); showPV.addActionListener(new ActionListener() { @Override public void actionPerformed(ActionEvent e) { JCheckBox cb = (JCheckBox) e.getSource(); if (cb.isSelected()) principalVariation.setVisible(true); else principalVariation.setVisible(false); } }); p1.add(showPV); JPanel p2 = new JPanel(); p2.add(chessView); principalVariation = new JLabel(); JPanel p3 = new JPanel(); p3.setLayout(new GridLayout(2, 1)); p3.add(principalVariation); p3.add(new JLabel("Copyright 2002 Peter Hunter. All rights reserved.")); Container cp = getContentPane(); cp.setLayout(new BorderLayout()); cp.add(p1, "North"); cp.add(p2, "Center"); cp.add(p3, "South"); } private boolean isResult() { Collection<HMove> validMoves = board.gen(); Iterator<HMove> i = validMoves.iterator(); boolean found = false; while (i.hasNext()) { if (board.makeMove((HMove) i.next())) { board.takeBack(); found = true; break; } } String message = null; if (!found) { if (board.inCheck(board.side)) { if (board.side == LIGHT) message = "0 - 1 Black mates"; else message = "1 - 0 White mates"; } else message = "0 - 0 Stalemate"; } else if (board.reps() == 3) message = "1/2 - 1/2 Draw by repetition"; else if (board.fifty >= 100) message = "1/2 - 1/2 Draw by fifty move rule"; if (message != null) { int choice = JOptionPane.showConfirmDialog(this, message + "\nPlay again?", "Play Again?", JOptionPane.YES_NO_OPTION); if (choice == JOptionPane.YES_OPTION) { searcher.stopThinking(); playNewGame(); } return true; } if (board.inCheck(board.side)) showStatus("Check!"); return false; } private void makeMove(HMove m) { int from = m.getFrom(); int to = m.getTo(); log.info("computer " + from + " " + to); if (m.promote != 0) { chessView.makeMoveWithPromote(m, m.promote, board.side != LIGHT); } else { if ((m.bits & 2) != 0) { if (from == E1 && to == G1) chessView.makeMove(new org.op.chess.Move(H1, F1)); else if (from == E1 && to == C1) chessView.makeMove(new org.op.chess.Move(A1, D1)); else if (from == E8 && to == G8) chessView.makeMove(new org.op.chess.Move(H8, F8)); else if (from == E8 && to == C8) chessView.makeMove(new org.op.chess.Move(A8, D8)); } else if ((m.bits & 4) != 0) { if (board.xside == LIGHT) chessView.clear(m.getToRow() + 1, m.getToCol()); else chessView.clear(m.getToRow() - 1, m.getToCol()); } chessView.makeMove(m); } } public void playNewGame() { computerSide = DARK; guessedMove = null; searcher = new Search(); board = new Board(); chessView.setupBoard(); } @Override public void propertyChange(PropertyChangeEvent pce) { } public void setMaxTime(int millis) { maxTime = millis; } public void setPrincipalVariation(String s) { principalVariation.setText("Thinking: " + s); } @Override public void showStatus(String msg) { log.info(msg); } @Override public void start() { playNewGame(); } @Override public void stop() { searcher.stopThinking(); } private void think() { searcher.think(this); } @Override public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException { org.op.chess.Move move = (org.op.chess.Move) pce.getNewValue(); if (move == null) return; log.info("user move from " + move.getFrom() + " " + move.getToRow() + "," + move.getToCol()); log.info("user move to " + move.getTo() + " " + move.getToRow() + "," + move.getToCol()); int promote = 0; int to = move.getTo(); int from = move.getFrom(); if ((((to < 8) && (board.side == LIGHT)) || ((to > 55) && (board.side == DARK))) && (board.getPiece(from) == PAWN)) { promote = chessView.promotionDialog(board.side == LIGHT); } boolean found = false; Collection<HMove> validMoves = board.gen(); Iterator<HMove> i = validMoves.iterator(); HMove m = null; while (i.hasNext()) { m = (HMove) i.next(); if (m.getFrom() == from && m.getTo() == to && m.promote == promote) { found = true; break; } } if (!found || !board.makeMove(m)) { showStatus("Illegal move"); throw new PropertyVetoException("Illegal move", pce); } else { makeMove(m); chessView.switchMoveMarkers(board.side == LIGHT); if (isResult()) return; if (board.side == computerSide) { if (guessedMove != null) { if (!m.equals(guessedMove)) { searcher.stopThinking(); try { thinkThread.join(); } catch (InterruptedException ignore) { } searcher.restartThinking(); searcher.board.takeBack(); searcher.board.makeMove(m); searcher.clearPV(); thinkThread = new Thinker(); thinkThread.start(); } } else { searcher.clearPV(); searcher.board.makeMove(m); thinkThread = new Thinker(); thinkThread.start(); } searcher.setStopTime(System.currentTimeMillis() + maxTime); showStatus("Thinking..."); } } } }