diff --git a/CuckooChess/.classpath b/CuckooChess/.classpath
new file mode 100644
index 0000000..48158d7
--- /dev/null
+++ b/CuckooChess/.classpath
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/CuckooChess/.externalToolBuilders/jar_builder.launch b/CuckooChess/.externalToolBuilders/jar_builder.launch
new file mode 100644
index 0000000..a646598
--- /dev/null
+++ b/CuckooChess/.externalToolBuilders/jar_builder.launch
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CuckooChess/.project b/CuckooChess/.project
new file mode 100644
index 0000000..e39bad1
--- /dev/null
+++ b/CuckooChess/.project
@@ -0,0 +1,28 @@
+
+
+ CuckooChess
+
+
+ CuckooChessEngine
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.ui.externaltools.ExternalToolBuilder
+ auto,full,incremental,
+
+
+ LaunchConfigHandle
+ <project>/.externalToolBuilders/jar_builder.launch
+
+
+
+
+
+ org.eclipse.jdt.core.javanature
+
+
diff --git a/CuckooChess/.settings/org.eclipse.jdt.core.prefs b/CuckooChess/.settings/org.eclipse.jdt.core.prefs
new file mode 100644
index 0000000..44f8e5d
--- /dev/null
+++ b/CuckooChess/.settings/org.eclipse.jdt.core.prefs
@@ -0,0 +1,12 @@
+#Sat May 08 15:39:56 CEST 2010
+eclipse.preferences.version=1
+org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
+org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.6
+org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
+org.eclipse.jdt.core.compiler.compliance=1.6
+org.eclipse.jdt.core.compiler.debug.lineNumber=generate
+org.eclipse.jdt.core.compiler.debug.localVariable=generate
+org.eclipse.jdt.core.compiler.debug.sourceFile=generate
+org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
+org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
+org.eclipse.jdt.core.compiler.source=1.6
diff --git a/CuckooChess/.settings/org.eclipse.ltk.core.refactoring.prefs b/CuckooChess/.settings/org.eclipse.ltk.core.refactoring.prefs
new file mode 100644
index 0000000..3c96de9
--- /dev/null
+++ b/CuckooChess/.settings/org.eclipse.ltk.core.refactoring.prefs
@@ -0,0 +1,3 @@
+#Sun May 09 18:44:06 CEST 2010
+eclipse.preferences.version=1
+org.eclipse.ltk.core.refactoring.enable.project.refactoring.history=false
diff --git a/CuckooChess/build_jar.xml b/CuckooChess/build_jar.xml
new file mode 100644
index 0000000..4cf251c
--- /dev/null
+++ b/CuckooChess/build_jar.xml
@@ -0,0 +1,33 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/CuckooChess/src/gui/AppletGUI.form b/CuckooChess/src/gui/AppletGUI.form
new file mode 100644
index 0000000..22a7dc0
--- /dev/null
+++ b/CuckooChess/src/gui/AppletGUI.form
@@ -0,0 +1,309 @@
+
+
+
diff --git a/CuckooChess/src/gui/AppletGUI.java b/CuckooChess/src/gui/AppletGUI.java
new file mode 100644
index 0000000..afec545
--- /dev/null
+++ b/CuckooChess/src/gui/AppletGUI.java
@@ -0,0 +1,440 @@
+/*
+ CuckooChess - A java chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package gui;
+
+import guibase.ChessController;
+import guibase.GUIInterface;
+
+import javax.swing.JOptionPane;
+import javax.swing.SwingUtilities;
+
+import chess.ComputerPlayer;
+import chess.Move;
+import chess.Position;
+
+/**
+ * The main class for the chess GUI.
+ * @author petero
+ */
+public class AppletGUI extends javax.swing.JApplet implements GUIInterface {
+ private static final long serialVersionUID = 7357610346389734323L;
+ ChessBoardPainter cbp;
+ ChessController ctrl;
+ final static int ttLogSize = 19; // Use 2^19 hash entries.
+ String moveListStr = "";
+ String thinkingStr = "";
+
+ /** Initializes the applet AppletGUI */
+ @Override
+ public void init() {
+ ctrl = new ChessController(this);
+ try {
+ java.awt.EventQueue.invokeAndWait(new Runnable() {
+ public void run() {
+ initComponents();
+ cbp = (ChessBoardPainter)ChessBoard;
+ ctrl.newGame(PlayerWhite.isSelected(), ttLogSize, true);
+ ctrl.startGame();
+ }
+ });
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ }
+
+ /**
+ * Entry point for the GUI version of the chess program.
+ */
+ public static void main(String[] args) {
+ javax.swing.JApplet theApplet = new AppletGUI();
+ theApplet.init();
+ javax.swing.JFrame window = new javax.swing.JFrame(ComputerPlayer.engineName);
+ window.setContentPane(theApplet);
+ window.setDefaultCloseOperation(javax.swing.JFrame.EXIT_ON_CLOSE);
+ window.pack();
+ window.setVisible(true);
+ }
+
+ /** This method is called from within the init() method to
+ * initialize the form.
+ * WARNING: Do NOT modify this code. The content of this method is
+ * always regenerated by the Form Editor.
+ */
+ private void initComponents() {
+
+ PlayerColor = new javax.swing.ButtonGroup();
+ MainPanel = new javax.swing.JPanel();
+ ChessBoardPanel = new javax.swing.JPanel();
+ ChessBoard = new ChessBoardPainter();
+ jPanel1 = new javax.swing.JPanel();
+ NewGame = new javax.swing.JButton();
+ SettingsPanel = new javax.swing.JPanel();
+ PlayerWhite = new javax.swing.JRadioButton();
+ PlayerBlack = new javax.swing.JRadioButton();
+ TimeLabel = new javax.swing.JLabel();
+ TimeSlider = new javax.swing.JSlider();
+ ShowThinking = new javax.swing.JCheckBox();
+ FlipBoard = new javax.swing.JCheckBox();
+ jScrollPane1 = new javax.swing.JScrollPane();
+ LogTextArea = new javax.swing.JTextPane();
+ StatusLine = new javax.swing.JTextField();
+ Forward = new javax.swing.JButton();
+ Backward = new javax.swing.JButton();
+
+ ChessBoardPanel.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0)));
+ ChessBoardPanel.setPreferredSize(new java.awt.Dimension(500, 500));
+
+ ChessBoard.addMouseListener(new java.awt.event.MouseAdapter() {
+ public void mousePressed(java.awt.event.MouseEvent evt) {
+ ChessBoardMousePressed(evt);
+ }
+ public void mouseReleased(java.awt.event.MouseEvent evt) {
+ ChessBoardMouseReleased(evt);
+ }
+ });
+ ChessBoard.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
+ public void mouseDragged(java.awt.event.MouseEvent evt) {
+ ChessBoardMouseDragged(evt);
+ }
+ });
+
+ javax.swing.GroupLayout ChessBoardPanelLayout = new javax.swing.GroupLayout(ChessBoardPanel);
+ ChessBoardPanel.setLayout(ChessBoardPanelLayout);
+ ChessBoardPanelLayout.setHorizontalGroup(
+ ChessBoardPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(ChessBoard, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE)
+ );
+ ChessBoardPanelLayout.setVerticalGroup(
+ ChessBoardPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(ChessBoard, javax.swing.GroupLayout.DEFAULT_SIZE, 500, Short.MAX_VALUE)
+ );
+
+ jPanel1.setFocusable(false);
+
+ NewGame.setText("New Game");
+ NewGame.setFocusable(false);
+ NewGame.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ NewGameActionPerformed(evt);
+ }
+ });
+
+ SettingsPanel.setBorder(javax.swing.BorderFactory.createTitledBorder("Settings"));
+ SettingsPanel.setFocusable(false);
+
+ PlayerColor.add(PlayerWhite);
+ PlayerWhite.setSelected(true);
+ PlayerWhite.setText("Play White");
+ PlayerWhite.setFocusable(false);
+
+ PlayerColor.add(PlayerBlack);
+ PlayerBlack.setText("Play Black");
+ PlayerBlack.setFocusable(false);
+
+ TimeLabel.setText("Thinking Time");
+
+ TimeSlider.setMajorTickSpacing(15);
+ TimeSlider.setMaximum(60);
+ TimeSlider.setMinorTickSpacing(5);
+ TimeSlider.setPaintLabels(true);
+ TimeSlider.setPaintTicks(true);
+ TimeSlider.setValue(5);
+ TimeSlider.setFocusable(false);
+ TimeSlider.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ TimeSliderStateChanged(evt);
+ }
+ });
+
+ ShowThinking.setText("Show Thinking");
+ ShowThinking.setFocusable(false);
+ ShowThinking.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ ShowThinkingStateChanged(evt);
+ }
+ });
+
+ FlipBoard.setText("Flip Board");
+ FlipBoard.setFocusable(false);
+ FlipBoard.addChangeListener(new javax.swing.event.ChangeListener() {
+ public void stateChanged(javax.swing.event.ChangeEvent evt) {
+ FlipBoardStateChanged(evt);
+ }
+ });
+
+ javax.swing.GroupLayout SettingsPanelLayout = new javax.swing.GroupLayout(SettingsPanel);
+ SettingsPanel.setLayout(SettingsPanelLayout);
+ SettingsPanelLayout.setHorizontalGroup(
+ SettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(ShowThinking, javax.swing.GroupLayout.PREFERRED_SIZE, 157, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addGroup(SettingsPanelLayout.createSequentialGroup()
+ .addComponent(PlayerWhite)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 104, Short.MAX_VALUE)
+ .addComponent(FlipBoard)
+ .addContainerGap())
+ .addGroup(SettingsPanelLayout.createSequentialGroup()
+ .addComponent(TimeLabel)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(TimeSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addComponent(PlayerBlack)
+ );
+ SettingsPanelLayout.setVerticalGroup(
+ SettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(SettingsPanelLayout.createSequentialGroup()
+ .addGroup(SettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(PlayerWhite)
+ .addComponent(FlipBoard))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(PlayerBlack)
+ .addGap(18, 18, 18)
+ .addGroup(SettingsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(TimeLabel)
+ .addComponent(TimeSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(ShowThinking)
+ .addContainerGap())
+ );
+
+ LogTextArea.setEditable(false);
+ LogTextArea.setVerifyInputWhenFocusTarget(false);
+ jScrollPane1.setViewportView(LogTextArea);
+
+ StatusLine.setEditable(false);
+ StatusLine.setFocusable(false);
+
+ Forward.setText("->");
+ Forward.setDefaultCapable(false);
+ Forward.setFocusPainted(false);
+ Forward.setFocusable(false);
+ Forward.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ ForwardActionPerformed(evt);
+ }
+ });
+
+ Backward.setText("<-");
+ Backward.setDefaultCapable(false);
+ Backward.setFocusPainted(false);
+ Backward.setFocusable(false);
+ Backward.addActionListener(new java.awt.event.ActionListener() {
+ public void actionPerformed(java.awt.event.ActionEvent evt) {
+ BackwardActionPerformed(evt);
+ }
+ });
+
+ javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
+ jPanel1.setLayout(jPanel1Layout);
+ jPanel1Layout.setHorizontalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(jScrollPane1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 319, Short.MAX_VALUE)
+ .addComponent(StatusLine, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 319, Short.MAX_VALUE)
+ .addGroup(javax.swing.GroupLayout.Alignment.LEADING, jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addComponent(NewGame)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(Backward)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(Forward))
+ .addComponent(SettingsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
+ .addContainerGap())
+ );
+ jPanel1Layout.setVerticalGroup(
+ jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(jPanel1Layout.createSequentialGroup()
+ .addComponent(SettingsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addGroup(jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
+ .addComponent(NewGame)
+ .addComponent(Forward)
+ .addComponent(Backward))
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jScrollPane1, javax.swing.GroupLayout.DEFAULT_SIZE, 283, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(StatusLine, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
+ );
+
+ javax.swing.GroupLayout MainPanelLayout = new javax.swing.GroupLayout(MainPanel);
+ MainPanel.setLayout(MainPanelLayout);
+ MainPanelLayout.setHorizontalGroup(
+ MainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(MainPanelLayout.createSequentialGroup()
+ .addContainerGap()
+ .addComponent(ChessBoardPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 502, Short.MAX_VALUE)
+ .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
+ .addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
+ .addContainerGap())
+ );
+ MainPanelLayout.setVerticalGroup(
+ MainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addGroup(javax.swing.GroupLayout.Alignment.TRAILING, MainPanelLayout.createSequentialGroup()
+ .addContainerGap()
+ .addGroup(MainPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
+ .addComponent(jPanel1, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ .addComponent(ChessBoardPanel, javax.swing.GroupLayout.Alignment.LEADING, javax.swing.GroupLayout.DEFAULT_SIZE, 502, Short.MAX_VALUE))
+ .addContainerGap())
+ );
+
+ javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
+ getContentPane().setLayout(layout);
+ layout.setHorizontalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(MainPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ );
+ layout.setVerticalGroup(
+ layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
+ .addComponent(MainPanel, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
+ );
+ }// //GEN-END:initComponents
+
+ private void ChessBoardMousePressed(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_ChessBoardMousePressed
+ if (ctrl.humansTurn()) {
+ int sq = cbp.eventToSquare(evt);
+ Move m = cbp.mousePressed(sq);
+ if (m != null) {
+ ctrl.humanMove(m);
+ }
+ }
+ }//GEN-LAST:event_ChessBoardMousePressed
+
+ private void FlipBoardStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_FlipBoardStateChanged
+ cbp.setFlipped(FlipBoard.isSelected());
+ }//GEN-LAST:event_FlipBoardStateChanged
+
+ private void NewGameActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_NewGameActionPerformed
+ ctrl.newGame(PlayerWhite.isSelected(), ttLogSize, true);
+ ctrl.startGame();
+ }//GEN-LAST:event_NewGameActionPerformed
+
+ private void ShowThinkingStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_ShowThinkingStateChanged
+ ctrl.setMoveList();
+ }//GEN-LAST:event_ShowThinkingStateChanged
+
+ private void BackwardActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_BackwardActionPerformed
+ ctrl.takeBackMove();
+ }//GEN-LAST:event_BackwardActionPerformed
+
+ private void ForwardActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ForwardActionPerformed
+ ctrl.redoMove();
+ }//GEN-LAST:event_ForwardActionPerformed
+
+ private void TimeSliderStateChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_TimeSliderStateChanged
+ ctrl.setTimeLimit();
+ }//GEN-LAST:event_TimeSliderStateChanged
+
+ private void ChessBoardMouseDragged(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_ChessBoardMouseDragged
+ if (ctrl.humansTurn()) {
+ cbp.mouseDragged(evt);
+ }
+ }//GEN-LAST:event_ChessBoardMouseDragged
+
+ private void ChessBoardMouseReleased(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_ChessBoardMouseReleased
+ if (ctrl.humansTurn()) {
+ int sq = cbp.eventToSquare(evt);
+ Move m = cbp.mouseReleased(sq);
+ if (m != null) {
+ ctrl.humanMove(m);
+ }
+ }
+ }//GEN-LAST:event_ChessBoardMouseReleased
+
+ // Variables declaration - do not modify//GEN-BEGIN:variables
+ private javax.swing.JButton Backward;
+ private javax.swing.JLabel ChessBoard;
+ private javax.swing.JPanel ChessBoardPanel;
+ private javax.swing.JCheckBox FlipBoard;
+ private javax.swing.JButton Forward;
+ private javax.swing.JTextPane LogTextArea;
+ private javax.swing.JPanel MainPanel;
+ private javax.swing.JButton NewGame;
+ private javax.swing.JRadioButton PlayerBlack;
+ private javax.swing.ButtonGroup PlayerColor;
+ private javax.swing.JRadioButton PlayerWhite;
+ private javax.swing.JPanel SettingsPanel;
+ private javax.swing.JCheckBox ShowThinking;
+ private javax.swing.JTextField StatusLine;
+ private javax.swing.JLabel TimeLabel;
+ private javax.swing.JSlider TimeSlider;
+ private javax.swing.JPanel jPanel1;
+ private javax.swing.JScrollPane jScrollPane1;
+ // End of variables declaration//GEN-END:variables
+
+ public void setPosition(Position pos) {
+ cbp.setPosition(pos);
+ }
+
+ public void setSelection(int sq) {
+ cbp.setSelection(sq);
+ }
+
+ public void setStatusString(String str) {
+ StatusLine.setText(str);
+ }
+
+ public void setMoveListString(String str) {
+ moveListStr = str;
+ str = moveListStr + "\n" + thinkingStr;
+ if (!str.equals(LogTextArea.getText())) {
+ LogTextArea.setText(str);
+ }
+ }
+
+ public void setThinkingString(String str) {
+ thinkingStr = str;
+ str = moveListStr + "\n" + thinkingStr;
+ if (!str.equals(LogTextArea.getText())) {
+ LogTextArea.setText(str);
+ }
+ }
+
+
+ public final int timeLimit() {
+ return Math.max(25, TimeSlider.getValue() * 1000);
+ }
+
+ public final boolean showThinking() {
+ return ShowThinking.isSelected();
+ }
+
+ public void requestPromotePiece() {
+ runOnUIThread(new Runnable() {
+ public void run() {
+ Object[] options = { "Queen", "Rook", "Bishop", "Knight" };
+ int choice = JOptionPane.showOptionDialog(
+ cbp, "Promote pawn to?", "Pawn Promotion",
+ 0, JOptionPane.QUESTION_MESSAGE, null, options, options[0]);
+ ctrl.reportPromotePiece(choice);
+ }
+ });
+ }
+
+ public void runOnUIThread(Runnable runnable) {
+ SwingUtilities.invokeLater(runnable);
+ }
+
+ @Override
+ public boolean randomMode() {
+ return false;
+ }
+
+ @Override
+ public void reportInvalidMove(Move m) {
+ }
+}
diff --git a/CuckooChess/src/gui/ChessBoardPainter.java b/CuckooChess/src/gui/ChessBoardPainter.java
new file mode 100644
index 0000000..4cc784d
--- /dev/null
+++ b/CuckooChess/src/gui/ChessBoardPainter.java
@@ -0,0 +1,292 @@
+/*
+ CuckooChess - A java chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package gui;
+
+import chess.Move;
+import chess.Piece;
+import chess.Position;
+import java.awt.BasicStroke;
+import java.awt.Color;
+import java.awt.Dimension;
+import java.awt.Font;
+import java.awt.FontFormatException;
+import java.awt.Graphics;
+import java.awt.Graphics2D;
+import java.awt.event.MouseEvent;
+import java.awt.font.FontRenderContext;
+import java.awt.geom.Rectangle2D;
+import java.io.IOException;
+import java.io.InputStream;
+import javax.swing.JLabel;
+
+/**
+ * Draws a graphical chess board. Also handles user interaction.
+ * @author petero
+ */
+public class ChessBoardPainter extends JLabel {
+ private static final long serialVersionUID = -1319250011487017825L;
+ private Position pos;
+ private int selectedSquare;
+ private int x0, y0, sqSize;
+ private boolean flipped;
+ private Font chessFont;
+
+ // For piece animation during dragging
+ private int activeSquare;
+ private boolean dragging;
+ private int dragX;
+ private int dragY;
+ private boolean cancelSelection;
+
+ ChessBoardPainter() {
+ pos = new Position();
+ selectedSquare = -1;
+ x0 = y0 = sqSize = 0;
+ flipped = false;
+ activeSquare = -1;
+ }
+
+ /**
+ * Set the board to a given state.
+ * @param pos
+ */
+ final public void setPosition(Position pos) {
+ this.pos = pos;
+ repaint();
+ }
+
+ /**
+ * Set/clear the board flipped status.
+ * @param flipped
+ */
+ final public void setFlipped(boolean flipped) {
+ this.flipped = flipped;
+ repaint();
+ }
+
+ /**
+ * Set/clear the selected square.
+ * @param square The square to select, or -1 to clear selection.
+ */
+ final public void setSelection(int square) {
+ if (square != this.selectedSquare) {
+ this.selectedSquare = square;
+ repaint();
+ }
+ }
+
+ @Override
+ public void paint(Graphics g0) {
+ Graphics2D g = (Graphics2D)g0;
+ Dimension size = getSize();
+ sqSize = (Math.min(size.width, size.height) - 4) / 8;
+ x0 = (size.width - sqSize * 8) / 2;
+ y0 = (size.height - sqSize * 8) / 2;
+
+ boolean doDrag = (activeSquare >= 0) && dragging;
+
+ for (int x = 0; x < 8; x++) {
+ for (int y = 0; y < 8; y++) {
+ final int xCrd = getXCrd(x);
+ final int yCrd = getYCrd(y);
+ g.setColor(Position.darkSquare(x, y) ? Color.GRAY : new Color(190, 190, 90));
+ g.fillRect(xCrd, yCrd, sqSize, sqSize);
+
+ int sq = Position.getSquare(x, y);
+ int p = pos.getPiece(sq);
+ if (doDrag && (sq == activeSquare)) {
+ // Skip this piece. It will be drawn later at (dragX,dragY)
+ } else {
+ drawPiece(g, xCrd + sqSize / 2, yCrd + sqSize / 2, p);
+ }
+ }
+ }
+ if (selectedSquare >= 0) {
+ int selX = Position.getX(selectedSquare);
+ int selY = Position.getY(selectedSquare);
+ g.setColor(Color.RED);
+ g.setStroke(new BasicStroke(3));
+ g.drawRect(getXCrd(selX), getYCrd(selY), sqSize, sqSize);
+ }
+ if (doDrag) {
+ int p = pos.getPiece(activeSquare);
+ drawPiece(g, dragX, dragY, p);
+ }
+ }
+
+ private final void drawPiece(Graphics2D g, int xCrd, int yCrd, int p) {
+ g.setColor(Piece.isWhite(p) ? Color.WHITE : Color.BLACK);
+ String ps;
+ switch (p) {
+ case Piece.EMPTY:
+ ps = "";
+ break;
+ case Piece.WKING:
+ ps = "k";
+ break;
+ case Piece.WQUEEN:
+ ps = "q";
+ break;
+ case Piece.WROOK:
+ ps = "r";
+ break;
+ case Piece.WBISHOP:
+ ps = "b";
+ break;
+ case Piece.WKNIGHT:
+ ps = "n";
+ break;
+ case Piece.WPAWN:
+ ps = "p";
+ break;
+ case Piece.BKING:
+ ps = "l";
+ break;
+ case Piece.BQUEEN:
+ ps = "w";
+ break;
+ case Piece.BROOK:
+ ps = "t";
+ break;
+ case Piece.BBISHOP:
+ ps = "v";
+ break;
+ case Piece.BKNIGHT:
+ ps = "m";
+ break;
+ case Piece.BPAWN:
+ ps = "o";
+ break;
+ default:
+ ps = "?";
+ break;
+ }
+ if (ps.length() > 0) {
+ FontRenderContext frc = g.getFontRenderContext();
+ if ((chessFont == null) || (chessFont.getSize() != sqSize)) {
+ InputStream inStream = getClass().getResourceAsStream("/gui/casefont.ttf");
+ try {
+ Font font = Font.createFont(Font.TRUETYPE_FONT, inStream);
+ chessFont = font.deriveFont((float)sqSize);
+ } catch (FontFormatException ex) {
+ throw new RuntimeException();
+ } catch (IOException ex) {
+ throw new RuntimeException();
+ }
+ }
+ g.setFont(chessFont);
+ Rectangle2D textRect = g.getFont().getStringBounds(ps, frc);
+ int xCent = (int)textRect.getCenterX();
+ int yCent = (int)textRect.getCenterY();
+ g.drawString(ps, xCrd - xCent, yCrd - yCent);
+ }
+ }
+
+ private final int getXCrd(int x) {
+ return x0 + sqSize * (flipped ? 7 - x : x);
+ }
+ private final int getYCrd(int y) {
+ return y0 + sqSize * (flipped ? y : (7 - y));
+ }
+
+ /**
+ * Compute the square corresponding to the coordinates of a mouse event.
+ * @param evt Details about the mouse event.
+ * @return The square corresponding to the mouse event, or -1 if outside board.
+ */
+ final int eventToSquare(MouseEvent evt) {
+ int xCrd = evt.getX();
+ int yCrd = evt.getY();
+
+ int sq = -1;
+ if ((xCrd >= x0) && (yCrd >= y0) && (sqSize > 0)) {
+ int x = (xCrd - x0) / sqSize;
+ int y = 7 - (yCrd - y0) / sqSize;
+ if ((x >= 0) && (x < 8) && (y >= 0) && (y < 8)) {
+ if (flipped) {
+ x = 7 - x;
+ y = 7 - y;
+ }
+ sq = Position.getSquare(x, y);
+ }
+ }
+ return sq;
+ }
+
+ final Move mousePressed(int sq) {
+ Move m = null;
+ cancelSelection = false;
+ int p = pos.getPiece(sq);
+ if ((selectedSquare >= 0) && (sq == selectedSquare)) {
+ int fromPiece = pos.getPiece(selectedSquare);
+ if ((fromPiece == Piece.EMPTY) || (Piece.isWhite(fromPiece) != pos.whiteMove)) {
+ return m; // Can't move the piece the oppenent just moved.
+ }
+ }
+ if ((selectedSquare < 0) &&
+ ((p == Piece.EMPTY) || (Piece.isWhite(p) != pos.whiteMove))) {
+ return m; // You must click on one of your own pieces.
+ }
+ activeSquare = sq;
+ dragging = false;
+ dragX = dragY = -1;
+
+ if (selectedSquare >= 0) {
+ if (sq == selectedSquare) {
+ cancelSelection = true;
+ } else {
+ if ((p == Piece.EMPTY) || (Piece.isWhite(p) != pos.whiteMove)) {
+ m = new Move(selectedSquare, sq, Piece.EMPTY);
+ activeSquare = -1;
+ setSelection(sq);
+ }
+ }
+ }
+ if (m == null) {
+ setSelection(-1);
+ }
+ return m;
+ }
+
+ final void mouseDragged(MouseEvent evt) {
+ final int xCrd = evt.getX();
+ final int yCrd = evt.getY();
+ if (!dragging || (dragX != xCrd) || (dragY != yCrd)) {
+ dragging = true;
+ dragX = xCrd;
+ dragY = yCrd;
+ repaint();
+ }
+ }
+
+ final Move mouseReleased(int sq) {
+ Move m = null;
+ if (activeSquare >= 0) {
+ if (sq != activeSquare) {
+ m = new Move(activeSquare, sq, Piece.EMPTY);
+ setSelection(sq);
+ } else if (!cancelSelection) {
+ setSelection(sq);
+ }
+ activeSquare = -1;
+ repaint();
+ }
+ return m;
+ }
+}
diff --git a/CuckooChess/src/gui/casefont.ttf b/CuckooChess/src/gui/casefont.ttf
new file mode 100644
index 0000000..61c12d6
Binary files /dev/null and b/CuckooChess/src/gui/casefont.ttf differ
diff --git a/CuckooChess/src/tui/Main.java b/CuckooChess/src/tui/Main.java
new file mode 100644
index 0000000..4d01b9a
--- /dev/null
+++ b/CuckooChess/src/tui/Main.java
@@ -0,0 +1,52 @@
+/*
+ CuckooChess - A java chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package tui;
+
+import java.io.IOException;
+
+import chess.ComputerPlayer;
+import chess.HumanPlayer;
+import chess.Player;
+import chess.TreeLogger;
+
+/**
+ *
+ * @author petero
+ */
+public class Main {
+
+ /**
+ * @param args the command line arguments
+ */
+ public static void main(String[] args) throws IOException {
+ if ((args.length == 1) && args[0].equals("gui")) {
+ gui.AppletGUI.main(args);
+ } else if ((args.length == 1) && args[0].equals("txt")) {
+ Player whitePlayer = new HumanPlayer();
+ ComputerPlayer blackPlayer = new ComputerPlayer();
+ blackPlayer.setTTLogSize(21);
+ TUIGame game = new TUIGame(whitePlayer, blackPlayer);
+ game.play();
+ } else if ((args.length == 2) && args[0].equals("tree")) {
+ TreeLogger.main(new String[]{args[1]});
+ } else {
+ uci.UCIProtocol.main(false);
+ }
+ }
+}
diff --git a/CuckooChess/src/tui/TUIGame.java b/CuckooChess/src/tui/TUIGame.java
new file mode 100644
index 0000000..ae304b1
--- /dev/null
+++ b/CuckooChess/src/tui/TUIGame.java
@@ -0,0 +1,216 @@
+/*
+ CuckooChess - A java chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package tui;
+
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.LineNumberReader;
+
+import uci.UCIProtocol;
+import chess.ChessParseError;
+import chess.ComputerPlayer;
+import chess.Evaluate;
+import chess.Game;
+import chess.Move;
+import chess.Player;
+import chess.Position;
+import chess.TextIO;
+import chess.TwoReturnValues;
+
+public class TUIGame extends Game {
+
+ public TUIGame(Player whitePlayer, Player blackPlayer) {
+ super(whitePlayer, blackPlayer);
+ }
+
+ protected boolean handleCommand(String moveStr) {
+ if (super.handleCommand(moveStr))
+ return true;
+ if (moveStr.startsWith("testsuite ")) {
+ String testSuiteCmd = moveStr.substring(moveStr.indexOf(" ") + 1);
+ return handleTestSuite(testSuiteCmd);
+ } else if (moveStr.equals("uci")) {
+ whitePlayer = null;
+ blackPlayer = null;
+ UCIProtocol.main(true);
+ System.exit(0);
+ return false;
+ } else if (moveStr.equals("help")) {
+ showHelp();
+ return true;
+ }
+
+ return false;
+ }
+
+ private void showHelp() {
+ System.out.println("Enter a move, or one of the following special commands:");
+ System.out.println(" new - Start a new game");
+ System.out.println(" undo - Undo last half-move");
+ System.out.println(" redo - Redo next half-move");
+ System.out.println(" swap - Swap sides");
+ System.out.println(" go - Same as swap");
+ System.out.println(" list - List all moves in current game");
+ System.out.println(" setpos FEN - Set a position using a FEN string");
+ System.out.println(" getpos - Print current position in FEN notation");
+ System.out.println(" draw rep [move] - Claim draw by repetition");
+ System.out.println(" draw 50 [move] - Claim draw by 50-move rule");
+ System.out.println(" draw offer move - Play move and offer draw");
+ System.out.println(" draw accept - Accept a draw offer");
+ System.out.println(" resign - Resign the current game");
+ System.out.println(" testsuite filename maxtime");
+ System.out.println(" book on|off - Turn opening book on/off");
+ System.out.println(" time t - Set computer thinking time, ms");
+ System.out.println(" perft d - Run perft test to depth d");
+ System.out.println(" uci - Switch to uci protocol.");
+ System.out.println(" help - Show this help");
+ System.out.println(" quit - Terminate program");
+ }
+
+ private boolean handleTestSuite(String cmd) {
+ LineNumberReader fr = null;
+ try {
+ int idx = cmd.indexOf(" ");
+ String filename = cmd.substring(0, idx);
+ String timeStr = cmd.substring(idx + 1, cmd.length());
+ int timeLimit = Integer.parseInt(timeStr);
+ // System.out.printf("file:%s time:%s (%d)\n", filename, timeStr, timeLimit);
+ fr = new LineNumberReader(new FileReader(filename));
+ String line;
+ Player pl = whitePlayer.isHumanPlayer() ? blackPlayer : whitePlayer;
+ if (pl.isHumanPlayer()) {
+ System.out.printf("No computer player available");
+ return false;
+ }
+ ComputerPlayer cp = (ComputerPlayer)pl;
+ int numRight = 0;
+ int numTotal = 0;
+ while ((line = fr.readLine()) != null) {
+ if (line.startsWith("#") || (line.length() == 0)) {
+ continue;
+ }
+ int idx1 = line.indexOf(" bm ");
+ String fen = line.substring(0, idx1);
+ int idx2 = line.indexOf(";", idx1);
+ String bm = line.substring(idx1 + 4, idx2);
+ // System.out.printf("Line %3d: fen:%s bm:%s\n", fr.getLineNumber(), fen, bm);
+ Position testPos = TextIO.readFEN(fen);
+ cp.clearTT();
+ TwoReturnValues ret = cp.searchPosition(testPos, timeLimit);
+ Move sm = ret.first;
+ String PV = ret.second;
+ Move m = new Move(sm);
+ String[] answers = bm.split(" ");
+ boolean correct = false;
+ for (String a : answers) {
+ Move am = TextIO.stringToMove(testPos, a);
+ if (am == null) {
+ throw new ChessParseError("Invalid move " + a);
+ }
+ if (am.equals(m)) {
+ correct = true;
+ break;
+ }
+ }
+ if (correct) {
+ numRight++;
+ }
+ numTotal++;
+ System.out.printf("%3d : %6s %6d %d %03d/%03d %s : %s\n", fr.getLineNumber(),
+ TextIO.moveToString(testPos, sm, false), sm.score, correct ? 1 : 0,
+ numRight, numTotal, bm, PV);
+ }
+ fr.close();
+ } catch (NumberFormatException nfe) {
+ System.out.printf("Number format exception: %s\n", nfe.getMessage());
+ return false;
+ } catch (FileNotFoundException fnfe) {
+ System.out.printf("File not found: %s\n", fnfe.getMessage());
+ return false;
+ } catch (IOException ex) {
+ System.out.printf("IO error: %s\n", ex.getMessage());
+ } catch (ChessParseError cpe) {
+ int lineNo = (fr == null) ? -1 : fr.getLineNumber();
+ System.out.printf("Parse error, line %d: %s\n", lineNo, cpe.getMessage());
+ } catch (StringIndexOutOfBoundsException e) {
+ int lineNo = (fr == null) ? -1 : fr.getLineNumber();
+ System.out.printf("Parse error, line %d: %s\n", lineNo, e.getMessage());
+ } finally {
+ if (fr != null) {
+ try {
+ fr.close();
+ } catch (IOException ex) {
+ // Stupid FileReader class forces me to catch this meaningless exception
+ }
+ }
+ }
+ return true;
+ }
+
+ /**
+ * Administrate a game between two players, human or computer.
+ */
+ public void play() throws IOException {
+ handleCommand("new");
+ while (true) {
+ // Print last move
+ if (currentMove > 0) {
+ Position prevPos = new Position(pos);
+ prevPos.unMakeMove(moveList.get(currentMove - 1), uiInfoList.get(currentMove - 1));
+ String moveStr= TextIO.moveToString(prevPos, moveList.get(currentMove - 1), false);
+ if (haveDrawOffer()) {
+ moveStr += " (offer draw)";
+ }
+ String msg = String.format("Last move: %d%s %s",
+ prevPos.fullMoveCounter, prevPos.whiteMove ? "." : "...",
+ moveStr);
+ System.out.println(msg);
+ }
+// System.out.printf("Hash: %016x\n", pos.zobristHash());
+ {
+ Evaluate eval = new Evaluate();
+ int evScore = eval.evalPos(pos) * (pos.whiteMove ? 1 : -1);
+ System.out.printf("Eval: %.2f%n", evScore / 100.0);
+ }
+
+ // Check game state
+ System.out.print(TextIO.asciiBoard(pos));
+ String stateStr = getGameStateString();
+ if (stateStr.length() > 0) {
+ System.out.printf("%s%n", stateStr);
+ }
+ if (getGameState() != GameState.ALIVE) {
+ activateHumanPlayer();
+ }
+
+ // Get command from current player and act on it
+ Player pl = pos.whiteMove ? whitePlayer : blackPlayer;
+ String moveStr = pl.getCommand(new Position(pos), haveDrawOffer(), getHistory());
+ if (moveStr.equals("quit")) {
+ return;
+ } else {
+ boolean ok = processString(moveStr);
+ if (!ok) {
+ System.out.printf("Invalid move: %s\n", moveStr);
+ }
+ }
+ }
+ }
+}
diff --git a/CuckooChess/src/uci/EngineControl.java b/CuckooChess/src/uci/EngineControl.java
new file mode 100644
index 0000000..b38461e
--- /dev/null
+++ b/CuckooChess/src/uci/EngineControl.java
@@ -0,0 +1,435 @@
+/*
+ CuckooChess - A java chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package uci;
+
+import chess.Book;
+import chess.ComputerPlayer;
+import chess.Move;
+import chess.MoveGen;
+import chess.Parameters;
+import chess.Piece;
+import chess.Position;
+import chess.Search;
+import chess.TextIO;
+import chess.TranspositionTable;
+import chess.Parameters.CheckParam;
+import chess.Parameters.ComboParam;
+import chess.Parameters.ParamBase;
+import chess.Parameters.SpinParam;
+import chess.Parameters.StringParam;
+import chess.TranspositionTable.TTEntry;
+import chess.UndoInfo;
+import java.io.PrintStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.List;
+import java.util.Random;
+
+/**
+ * Control the search thread.
+ * @author petero
+ */
+public class EngineControl {
+ PrintStream os;
+
+ Thread engineThread;
+ private final Object threadMutex;
+ Search sc;
+ TranspositionTable tt;
+ MoveGen moveGen;
+
+ Position pos;
+ long[] posHashList;
+ int posHashListSize;
+ boolean ponder; // True if currently doing pondering
+ boolean onePossibleMove;
+ boolean infinite;
+
+ int minTimeLimit;
+ int maxTimeLimit;
+ int maxDepth;
+ int maxNodes;
+ List searchMoves;
+
+ // Options
+ int hashSizeMB = 16;
+ boolean ownBook = false;
+ boolean analyseMode = false;
+ boolean ponderMode = true;
+
+ // Reduced strength variables
+ int strength = 1000;
+ long randomSeed = 0;
+
+ /**
+ * This class is responsible for sending "info" strings during search.
+ */
+ private static class SearchListener implements Search.Listener {
+ PrintStream os;
+
+ SearchListener(PrintStream os) {
+ this.os = os;
+ }
+
+ public void notifyDepth(int depth) {
+ os.printf("info depth %d%n", depth);
+ }
+
+ public void notifyCurrMove(Move m, int moveNr) {
+ os.printf("info currmove %s currmovenumber %d%n", moveToString(m), moveNr);
+ }
+
+ public void notifyPV(int depth, int score, int time, int nodes, int nps, boolean isMate,
+ boolean upperBound, boolean lowerBound, ArrayList pv) {
+ StringBuilder pvBuf = new StringBuilder();
+ for (Move m : pv) {
+ pvBuf.append(" ");
+ pvBuf.append(moveToString(m));
+ }
+ String bound = "";
+ if (upperBound) {
+ bound = " upperbound";
+ } else if (lowerBound) {
+ bound = " lowerbound";
+ }
+ os.printf("info depth %d score %s %d%s time %d nodes %d nps %d pv%s%n",
+ depth, isMate ? "mate" : "cp", score, bound, time, nodes, nps, pvBuf.toString());
+ }
+
+ public void notifyStats(int nodes, int nps, int time) {
+ os.printf("info nodes %d nps %d time %d%n", nodes, nps, time);
+ }
+ }
+
+ public EngineControl(PrintStream os) {
+ this.os = os;
+ threadMutex = new Object();
+ setupTT();
+ moveGen = new MoveGen();
+ }
+
+ final public void startSearch(Position pos, ArrayList moves, SearchParams sPar) {
+ setupPosition(new Position(pos), moves);
+ computeTimeLimit(sPar);
+ ponder = false;
+ infinite = (maxTimeLimit < 0) && (maxDepth < 0) && (maxNodes < 0);
+ startThread(minTimeLimit, maxTimeLimit, maxDepth, maxNodes);
+ searchMoves = sPar.searchMoves;
+ }
+
+ final public void startPonder(Position pos, List moves, SearchParams sPar) {
+ setupPosition(new Position(pos), moves);
+ computeTimeLimit(sPar);
+ ponder = true;
+ infinite = false;
+ startThread(-1, -1, -1, -1);
+ }
+
+ final public void ponderHit() {
+ Search mySearch;
+ synchronized (threadMutex) {
+ mySearch = sc;
+ }
+ if (mySearch != null) {
+ if (onePossibleMove) {
+ if (minTimeLimit > 1) minTimeLimit = 1;
+ if (maxTimeLimit > 1) maxTimeLimit = 1;
+ }
+ mySearch.timeLimit(minTimeLimit, maxTimeLimit);
+ }
+ infinite = (maxTimeLimit < 0) && (maxDepth < 0) && (maxNodes < 0);
+ ponder = false;
+ }
+
+ final public void stopSearch() {
+ stopThread();
+ }
+
+ final public void newGame() {
+ randomSeed = new Random().nextLong();
+ tt.clear();
+ }
+
+ /**
+ * Compute thinking time for current search.
+ */
+ final public void computeTimeLimit(SearchParams sPar) {
+ minTimeLimit = -1;
+ maxTimeLimit = -1;
+ maxDepth = -1;
+ maxNodes = -1;
+ if (sPar.infinite) {
+ minTimeLimit = -1;
+ maxTimeLimit = -1;
+ maxDepth = -1;
+ } else if (sPar.depth > 0) {
+ maxDepth = sPar.depth;
+ } else if (sPar.mate > 0) {
+ maxDepth = sPar.mate * 2 - 1;
+ } else if (sPar.moveTime > 0) {
+ minTimeLimit = maxTimeLimit = sPar.moveTime;
+ } else if (sPar.nodes > 0) {
+ maxNodes = sPar.nodes;
+ } else {
+ int moves = sPar.movesToGo;
+ if (moves == 0) {
+ moves = 999;
+ }
+ moves = Math.min(moves, 45); // Assume 45 more moves until end of game
+ if (ponderMode) {
+ final double ponderHitRate = 0.35;
+ moves = (int)Math.ceil(moves * (1 - ponderHitRate));
+ }
+ boolean white = pos.whiteMove;
+ int time = white ? sPar.wTime : sPar.bTime;
+ int inc = white ? sPar.wInc : sPar.bInc;
+ final int margin = Math.min(1000, time * 9 / 10);
+ int timeLimit = (time + inc * (moves - 1) - margin) / moves;
+ minTimeLimit = (int)(timeLimit * 0.85);
+ maxTimeLimit = (int)(minTimeLimit * (Math.max(2.5, Math.min(4.0, moves / 2))));
+
+ // Leave at least 1s on the clock, but can't use negative time
+ minTimeLimit = clamp(minTimeLimit, 1, time - margin);
+ maxTimeLimit = clamp(maxTimeLimit, 1, time - margin);
+ }
+ }
+
+ private static final int clamp(int val, int min, int max) {
+ if (val < min) {
+ return min;
+ } else if (val > max) {
+ return max;
+ } else {
+ return val;
+ }
+ }
+
+ final private void startThread(final int minTimeLimit, final int maxTimeLimit,
+ int maxDepth, final int maxNodes) {
+ synchronized (threadMutex) {} // Must not start new search until old search is finished
+ sc = new Search(pos, posHashList, posHashListSize, tt);
+ sc.timeLimit(minTimeLimit, maxTimeLimit);
+ sc.setListener(new SearchListener(os));
+ sc.setStrength(strength, randomSeed);
+ MoveGen.MoveList moves = moveGen.pseudoLegalMoves(pos);
+ MoveGen.removeIllegal(pos, moves);
+ if ((searchMoves != null) && (searchMoves.size() > 0)) {
+ Arrays.asList(moves.m).retainAll(searchMoves);
+ }
+ final MoveGen.MoveList srchMoves = moves;
+ onePossibleMove = false;
+ if ((srchMoves.size < 2) && !infinite) {
+ onePossibleMove = true;
+ if (!ponder) {
+ if ((maxDepth < 0) || (maxDepth > 2)) maxDepth = 2;
+ }
+ }
+ tt.nextGeneration();
+ final int srchmaxDepth = maxDepth;
+ engineThread = new Thread(new Runnable() {
+ public void run() {
+ Move m = null;
+ if (ownBook && !analyseMode) {
+ Book book = new Book(false);
+ m = book.getBookMove(pos);
+ }
+ if (m == null) {
+ m = sc.iterativeDeepening(srchMoves, srchmaxDepth, maxNodes, false);
+ }
+ while (ponder || infinite) {
+ // We should not respond until told to do so. Just wait until
+ // we are allowed to respond.
+ try {
+ Thread.sleep(10);
+ } catch (InterruptedException ex) {
+ break;
+ }
+ }
+ Move ponderMove = getPonderMove(pos, m);
+ synchronized (threadMutex) {
+ if (ponderMove != null) {
+ os.printf("bestmove %s ponder %s%n", moveToString(m), moveToString(ponderMove));
+ } else {
+ os.printf("bestmove %s%n", moveToString(m));
+ }
+ engineThread = null;
+ sc = null;
+ }
+ }
+ });
+ engineThread.start();
+ }
+
+ private final void stopThread() {
+ Thread myThread;
+ Search mySearch;
+ synchronized (threadMutex) {
+ myThread = engineThread;
+ mySearch = sc;
+ }
+ if (myThread != null) {
+ mySearch.timeLimit(0, 0);
+ infinite = false;
+ ponder = false;
+ try {
+ myThread.join();
+ } catch (InterruptedException ex) {
+ throw new RuntimeException();
+ }
+ }
+ }
+
+
+ private final void setupTT() {
+ int nEntries = hashSizeMB > 0 ? hashSizeMB * (1 << 20) / 24 : 1024;
+ int logSize = (int) Math.floor(Math.log(nEntries) / Math.log(2));
+ tt = new TranspositionTable(logSize);
+ }
+
+ private final void setupPosition(Position pos, List moves) {
+ UndoInfo ui = new UndoInfo();
+ posHashList = new long[200 + moves.size()];
+ posHashListSize = 0;
+ for (Move m : moves) {
+ posHashList[posHashListSize++] = pos.zobristHash();
+ pos.makeMove(m, ui);
+ }
+ this.pos = pos;
+ }
+
+ /**
+ * Try to find a move to ponder from the transposition table.
+ */
+ private final Move getPonderMove(Position pos, Move m) {
+ if (m == null) return null;
+ Move ret = null;
+ UndoInfo ui = new UndoInfo();
+ pos.makeMove(m, ui);
+ TTEntry ent = tt.probe(pos.historyHash());
+ if (ent.type != TTEntry.T_EMPTY) {
+ ret = new Move(0, 0, 0);
+ ent.getMove(ret);
+ MoveGen.MoveList moves = moveGen.pseudoLegalMoves(pos);
+ MoveGen.removeIllegal(pos, moves);
+ boolean contains = false;
+ for (int mi = 0; mi < moves.size; mi++)
+ if (moves.m[mi].equals(ret)) {
+ contains = true;
+ break;
+ }
+ if (!contains)
+ ret = null;
+ }
+ pos.unMakeMove(m, ui);
+ return ret;
+ }
+
+ private static final String moveToString(Move m) {
+ if (m == null)
+ return "0000";
+ String ret = TextIO.squareToString(m.from);
+ ret += TextIO.squareToString(m.to);
+ switch (m.promoteTo) {
+ case Piece.WQUEEN:
+ case Piece.BQUEEN:
+ ret += "q";
+ break;
+ case Piece.WROOK:
+ case Piece.BROOK:
+ ret += "r";
+ break;
+ case Piece.WBISHOP:
+ case Piece.BBISHOP:
+ ret += "b";
+ break;
+ case Piece.WKNIGHT:
+ case Piece.BKNIGHT:
+ ret += "n";
+ break;
+ default:
+ break;
+ }
+ return ret;
+ }
+
+ static void printOptions(PrintStream os) {
+ os.printf("option name Hash type spin default 16 min 1 max 2048%n");
+ os.printf("option name OwnBook type check default false%n");
+ os.printf("option name Ponder type check default true%n");
+ os.printf("option name UCI_AnalyseMode type check default false%n");
+ os.printf("option name UCI_EngineAbout type string default %s by Peter Osterlund, see http://web.comhem.se/petero2home/javachess/index.html%n",
+ ComputerPlayer.engineName);
+ os.printf("option name Strength type spin default 1000 min 0 max 1000\n");
+
+ for (String pName : Parameters.instance().getParamNames()) {
+ ParamBase p = Parameters.instance().getParam(pName);
+ switch (p.type) {
+ case CHECK: {
+ CheckParam cp = (CheckParam)p;
+ os.printf("optionn name %s type check default %s\n",
+ p.name, cp.defaultValue?"true":"false");
+ break;
+ }
+ case SPIN: {
+ SpinParam sp = (SpinParam)p;
+ os.printf("option name %s type spin default %d min %d max %d\n",
+ p.name, sp.defaultValue, sp.minValue, sp.maxValue);
+ break;
+ }
+ case COMBO: {
+ ComboParam cp = (ComboParam)p;
+ os.printf("option name %s type combo default %s ", cp.name, cp.defaultValue);
+ for (String s : cp.allowedValues)
+ os.printf(" var %s", s);
+ os.printf("\n");
+ break;
+ }
+ case BUTTON:
+ os.printf("option name %s type button\n", p.name);
+ break;
+ case STRING: {
+ StringParam sp = (StringParam)p;
+ os.printf("option name %s type string default %s\n",
+ p.name, sp.defaultValue);
+ break;
+ }
+ }
+ }
+ }
+
+ final void setOption(String optionName, String optionValue) {
+ try {
+ if (optionName.equals("hash")) {
+ hashSizeMB = Integer.parseInt(optionValue);
+ setupTT();
+ } else if (optionName.equals("ownbook")) {
+ ownBook = Boolean.parseBoolean(optionValue);
+ } else if (optionName.equals("ponder")) {
+ ponderMode = Boolean.parseBoolean(optionValue);
+ } else if (optionName.equals("uci_analysemode")) {
+ analyseMode = Boolean.parseBoolean(optionValue);
+ } else if (optionName.equals("strength")) {
+ strength = Integer.parseInt(optionValue);
+ } else {
+ Parameters.instance().set(optionName, optionValue);
+ }
+ } catch (NumberFormatException nfe) {
+ }
+ }
+}
diff --git a/CuckooChess/src/uci/SearchParams.java b/CuckooChess/src/uci/SearchParams.java
new file mode 100644
index 0000000..6e5866f
--- /dev/null
+++ b/CuckooChess/src/uci/SearchParams.java
@@ -0,0 +1,45 @@
+/*
+ CuckooChess - A java chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package uci;
+
+import chess.Move;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Store search parameters (times, increments, max depth, etc).
+ * @author petero
+ */
+public class SearchParams {
+ List searchMoves; // If non-empty, search only these moves
+ int wTime; // White remaining time, ms
+ int bTime; // Black remaining time, ms
+ int wInc; // White increment per move, ms
+ int bInc; // Black increment per move, ms
+ int movesToGo; // Moves to next time control
+ int depth; // If >0, don't search deeper than this
+ int nodes; // If >0, don't search more nodes than this
+ int mate; // If >0, search for mate-in-x
+ int moveTime; // If >0, search for exactly this amount of time, ms
+ boolean infinite;
+
+ public SearchParams() {
+ searchMoves = new ArrayList();
+ }
+}
diff --git a/CuckooChess/src/uci/UCIProtocol.java b/CuckooChess/src/uci/UCIProtocol.java
new file mode 100644
index 0000000..4c8a5f7
--- /dev/null
+++ b/CuckooChess/src/uci/UCIProtocol.java
@@ -0,0 +1,222 @@
+/*
+ CuckooChess - A java chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package uci;
+
+import chess.ChessParseError;
+import chess.ComputerPlayer;
+import chess.Move;
+import chess.Position;
+import chess.TextIO;
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.util.ArrayList;
+
+/**
+ * Handle the UCI protocol mode.
+ * @author petero
+ */
+public class UCIProtocol {
+ // Data set by the "position" command.
+ Position pos;
+ ArrayList moves;
+
+ // Engine data
+ EngineControl engine;
+
+ // Set to true to break out of main loop
+ boolean quit;
+
+
+ public static void main(boolean autoStart) {
+ UCIProtocol uciProt = new UCIProtocol();
+ uciProt.mainLoop(System.in, System.out, autoStart);
+ }
+
+ public UCIProtocol() {
+ pos = null;
+ moves = new ArrayList();
+ quit = false;
+ }
+
+ final public void mainLoop(InputStream is, PrintStream os, boolean autoStart) {
+ try {
+ if (autoStart) {
+ handleCommand("uci", os);
+ }
+ InputStreamReader inStrRead = new InputStreamReader(is);
+ BufferedReader inBuf = new BufferedReader(inStrRead);
+ String line;
+ while ((line = inBuf.readLine()) != null) {
+ handleCommand(line, os);
+ if (quit) {
+ break;
+ }
+ }
+ } catch (IOException ex) {
+ // If stream closed or other I/O error, terminate program
+ }
+ }
+
+ final void handleCommand(String cmdLine, PrintStream os) {
+ String[] tokens = tokenize(cmdLine);
+ try {
+ String cmd = tokens[0];
+ if (cmd.equals("uci")) {
+ os.printf("id name %s%n", ComputerPlayer.engineName);
+ os.printf("id author Peter Osterlund%n");
+ EngineControl.printOptions(os);
+ os.printf("uciok%n");
+ } else if (cmd.equals("isready")) {
+ initEngine(os);
+ os.printf("readyok%n");
+ } else if (cmd.equals("setoption")) {
+ initEngine(os);
+ StringBuilder optionName = new StringBuilder();
+ StringBuilder optionValue = new StringBuilder();
+ if (tokens[1].endsWith("name")) {
+ int idx = 2;
+ while ((idx < tokens.length) && !tokens[idx].equals("value")) {
+ optionName.append(tokens[idx++].toLowerCase());
+ optionName.append(' ');
+ }
+ if ((idx < tokens.length) && tokens[idx++].equals("value")) {
+ while ((idx < tokens.length)) {
+ optionValue.append(tokens[idx++].toLowerCase());
+ optionValue.append(' ');
+ }
+ }
+ engine.setOption(optionName.toString().trim(), optionValue.toString().trim());
+ }
+ } else if (cmd.equals("ucinewgame")) {
+ if (engine != null) {
+ engine.newGame();
+ }
+ } else if (cmd.equals("position")) {
+ String fen = null;
+ int idx = 1;
+ if (tokens[idx].equals("startpos")) {
+ idx++;
+ fen = TextIO.startPosFEN;
+ } else if (tokens[idx].equals("fen")) {
+ idx++;
+ StringBuilder sb = new StringBuilder();
+ while ((idx < tokens.length) && !tokens[idx].equals("moves")) {
+ sb.append(tokens[idx++]);
+ sb.append(' ');
+ }
+ fen = sb.toString().trim();
+ }
+ if (fen != null) {
+ pos = TextIO.readFEN(fen);
+ moves.clear();
+ if ((idx < tokens.length) && tokens[idx++].equals("moves")) {
+ for (int i = idx; i < tokens.length; i++) {
+ Move m = TextIO.uciStringToMove(tokens[i]);
+ if (m != null) {
+ moves.add(m);
+ } else {
+ break;
+ }
+ }
+ }
+ }
+ } else if (cmd.equals("go")) {
+ if (pos == null) {
+ try {
+ pos = TextIO.readFEN(TextIO.startPosFEN);
+ } catch (ChessParseError ex) {
+ throw new RuntimeException();
+ }
+ }
+ initEngine(os);
+ int idx = 1;
+ SearchParams sPar = new SearchParams();
+ boolean ponder = false;
+ while (idx < tokens.length) {
+ String subCmd = tokens[idx++];
+ if (subCmd.equals("searchmoves")) {
+ while (idx < tokens.length) {
+ Move m = TextIO.uciStringToMove(tokens[idx]);
+ if (m != null) {
+ sPar.searchMoves.add(m);
+ idx++;
+ } else {
+ break;
+ }
+ }
+ } else if (subCmd.equals("ponder")) {
+ ponder = true;
+ } else if (subCmd.equals("wtime")) {
+ sPar.wTime = Integer.parseInt(tokens[idx++]);
+ } else if (subCmd.equals("btime")) {
+ sPar.bTime = Integer.parseInt(tokens[idx++]);
+ } else if (subCmd.equals("winc")) {
+ sPar.wInc = Integer.parseInt(tokens[idx++]);
+ } else if (subCmd.equals("binc")) {
+ sPar.bInc = Integer.parseInt(tokens[idx++]);
+ } else if (subCmd.equals("movestogo")) {
+ sPar.movesToGo = Integer.parseInt(tokens[idx++]);
+ } else if (subCmd.equals("depth")) {
+ sPar.depth = Integer.parseInt(tokens[idx++]);
+ } else if (subCmd.equals("nodes")) {
+ sPar.nodes = Integer.parseInt(tokens[idx++]);
+ } else if (subCmd.equals("mate")) {
+ sPar.mate = Integer.parseInt(tokens[idx++]);
+ } else if (subCmd.equals("movetime")) {
+ sPar.moveTime = Integer.parseInt(tokens[idx++]);
+ } else if (subCmd.equals("infinite")) {
+ sPar.infinite = true;
+ }
+ }
+ if (ponder) {
+ engine.startPonder(pos, moves, sPar);
+ } else {
+ engine.startSearch(pos, moves, sPar);
+ }
+ } else if (cmd.equals("stop")) {
+ engine.stopSearch();
+ } else if (cmd.equals("ponderhit")) {
+ engine.ponderHit();
+ } else if (cmd.equals("quit")) {
+ if (engine != null) {
+ engine.stopSearch();
+ }
+ quit = true;
+ }
+ } catch (ChessParseError ex) {
+ } catch (ArrayIndexOutOfBoundsException e) {
+ } catch (NumberFormatException nfe) {
+ }
+ }
+
+ final private void initEngine(PrintStream os) {
+ if (engine == null) {
+ engine = new EngineControl(os);
+ }
+ }
+
+ /** Convert a string to tokens by splitting at whitespace characters. */
+ final String[] tokenize(String cmdLine) {
+ cmdLine = cmdLine.trim();
+ return cmdLine.split("\\s+");
+ }
+}
diff --git a/CuckooChess/test/uci/UCIProtocolTest.java b/CuckooChess/test/uci/UCIProtocolTest.java
new file mode 100644
index 0000000..b29a4b4
--- /dev/null
+++ b/CuckooChess/test/uci/UCIProtocolTest.java
@@ -0,0 +1,68 @@
+/*
+ CuckooChess - A java chess program.
+ Copyright (C) 2011 Peter Österlund, peterosterlund2@gmail.com
+
+ This program 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 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ 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.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see .
+*/
+
+package uci;
+
+import org.junit.After;
+import org.junit.AfterClass;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author petero
+ */
+public class UCIProtocolTest {
+
+ public UCIProtocolTest() {
+ }
+
+ @BeforeClass
+ public static void setUpClass() throws Exception {
+ }
+
+ @AfterClass
+ public static void tearDownClass() throws Exception {
+ }
+
+ @Before
+ public void setUp() {
+ }
+
+ @After
+ public void tearDown() {
+ }
+
+ /**
+ * Test of tokenize method, of class UCIProtocol.
+ */
+ @Test
+ public void testTokenize() {
+ System.out.println("tokenize");
+ UCIProtocol uci = new UCIProtocol();
+ String[] result = uci.tokenize(" a b c de \t \t fgh");
+ assertEquals(5, result.length);
+ assertEquals("a", result[0]);
+ assertEquals("b", result[1]);
+ assertEquals("c", result[2]);
+ assertEquals("de", result[3]);
+ assertEquals("fgh", result[4]);
+ }
+}