/** * * @author greg (at) myrobotlab.org * * This file is part of MyRobotLab (http://myrobotlab.org). * * MyRobotLab is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 2 of the License, or * (at your option) any later version (subject to the "Classpath" exception * as provided in the LICENSE.txt file that accompanied this code). * * MyRobotLab is distributed in the hope that it will be useful or fun, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * All libraries in thirdParty bundle are subject to their own license * requirements - please refer to http://myrobotlab.org/libraries for * details. * * Enjoy ! * * */ package org.myrobotlab.control; import java.awt.BorderLayout; 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.JButton; import javax.swing.JCheckBox; import javax.swing.JComboBox; import javax.swing.JLabel; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JTabbedPane; import org.myrobotlab.chess.Board; import org.myrobotlab.chess.Constants; import org.myrobotlab.chess.HMove; import org.myrobotlab.chess.Search; import org.myrobotlab.logging.Logging; import org.myrobotlab.service.GUIService; import org.op.chess.ChessBoard; public class ChessGameGUI extends ServiceGUI implements Constants, VetoableChangeListener, PropertyChangeListener { final class Thinker extends Thread { private ChessGameGUI gui; public Thinker(ChessGameGUI gui) { this.gui = gui; } @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()); gui.myService.send(boundServiceName, "computerMoved", best.toString()); makeMove(best, true); 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(gui); thinkThread.start(); } } } static final long serialVersionUID = 1L; 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; public static String cleanMove(String t) { log.info("cleanMove " + t); // remove piece descriptor if (t.length() > 5) { char check = t.charAt(t.length() - 1); if (Character.isDigit(check)) { t = t.substring(1); } else { t = t.substring(0, t.length() - 1); } } // remove - if (t.contains("-")) { t = (t.substring(0, 2) + t.substring(3)); } t = t.toLowerCase(); log.info("cleanedMove " + t); return t; } public ChessGameGUI(final String boundServiceName, final GUIService myService, final JTabbedPane tabs) { super(boundServiceName, myService, tabs); } @Override public void attachGUI() { subscribe("inputMove", "inputMove", String.class); subscribe("inputHMove", "inputHMove", HMove.class); } private void computerMove() { searcher.stopThinking(); try { if (thinkThread != null) thinkThread.join(); } catch (InterruptedException ignore) { } searcher.restartThinking(); searcher.clearPV(); thinkThread = new Thinker(this); thinkThread.start(); searcher.setStopTime(System.currentTimeMillis() + maxTime); showStatus("Thinking..."); } @Override public void detachGUI() { unsubscribe("inputMove", "inputMove", String.class); unsubscribe("inputHMove", "inputHMove", HMove.class); } private int getPos(String s) { log.info("getPos " + s); String temp1 = ""; temp1 += s.charAt(1); int num = Integer.parseInt(temp1); int pos = s.charAt(0) - 97 + ((8 - num) * 8); return pos; } @Override public void 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(); display.setLayout(new BorderLayout()); display.add(p1, "North"); display.add(p2, "Center"); display.add(p3, "South"); display.setSize(800, 600); } public HMove inputHMove(HMove m2) { try { youGotToMoveItMoveIt(null, m2, false); } catch (PropertyVetoException e) { // TODO Auto-generated catch block Logging.logError(e); } return m2; } public String inputMove(String m) { log.info(m); String s = cleanMove(m); log.info(s); log.info(m + " pfrom " + getPos(s) + " pto " + getPos(s.substring(2))); org.op.chess.Move m2 = new org.op.chess.Move(getPos(s), getPos(s.substring(2))); try { youGotToMoveItMoveIt(null, m2, true); // last param is to publish or // not } catch (PropertyVetoException e) { // TODO Auto-generated catch block Logging.logError(e); } return s; } private boolean isResult() { Collection<?> validMoves = board.gen(); Iterator<?> 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); // int choice = JOptionPane.showInternalConfirmDialog(this, message // + "\nPlay again?", "Play Again?", JOptionPane.YES_NO_OPTION); int choice = 1; if (choice == JOptionPane.YES_OPTION) { searcher.stopThinking(); playNewGame(); } return true; } if (board.inCheck(board.side)) showStatus("Check!"); return false; } private void makeMove(HMove m, boolean publishEvent) { int from = m.getFrom(); int to = m.getTo(); String s = cleanMove(m.toString()); log.info(m + " from " + from + " to " + to); log.info(m + " pfrom " + getPos(s) + " pto " + getPos(s.substring(2))); // log.info((int)s.charAt(0)); // log.info(testFrom); // log.info(m + " from " + testFrom + " to " + to); if (publishEvent) { myService.send(boundServiceName, "makeMove", m, "n"); myService.send(boundServiceName, "makeHMove", m); } 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); } public void showStatus(String msg) { log.info(msg); } public void start() { playNewGame(); } public void stop() { searcher.stopThinking(); } private void think() { // searcher.think(this); searcher.think(); } @Override public void vetoableChange(PropertyChangeEvent pce) throws PropertyVetoException { youGotToMoveItMoveIt(pce, null, true); } private void youGotToMoveItMoveIt(PropertyChangeEvent pce, org.op.chess.Move m2, boolean publishEvent) throws PropertyVetoException { org.op.chess.Move move = null; if (pce != null) { move = (org.op.chess.Move) pce.getNewValue(); } else { move = m2; } if (move == null) return; // HMove h1 = new HMove(5,3,2,4,5); /* * Move m1 = new Move(5,8); log.info(m1); 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<?> validMoves = board.gen(); Iterator<?> 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"); HMove illegal = new HMove(move.getFrom(), move.getTo(), 0, 0, 'p'); myService.send(boundServiceName, "makeMove", illegal, "i"); myService.send(boundServiceName, "makeHMove", illegal); if (pce != null) { throw new PropertyVetoException("Illegal move", pce); } else { log.error("ILLEGAL MOVE"); } } else { makeMove(m, publishEvent); 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(this); thinkThread.start(); } } else { searcher.clearPV(); searcher.board.makeMove(m); thinkThread = new Thinker(this); thinkThread.start(); } searcher.setStopTime(System.currentTimeMillis() + maxTime); showStatus("Thinking..."); } } } }