diff --git a/src/dev/kske/chess/board/Log.java b/src/dev/kske/chess/board/Log.java
index 95f9e43..f6352f5 100644
--- a/src/dev/kske/chess/board/Log.java
+++ b/src/dev/kske/chess/board/Log.java
@@ -1,8 +1,10 @@
package dev.kske.chess.board;
import java.util.ArrayList;
+import java.util.Iterator;
import java.util.List;
+import dev.kske.chess.board.Log.MoveNode;
import dev.kske.chess.board.Piece.Color;
/**
@@ -11,7 +13,7 @@ import dev.kske.chess.board.Piece.Color;
* Created: 09.07.2019
* Author: Kai S. K. Engelbart
*/
-public class Log {
+public class Log implements Iterable {
private MoveNode root, current;
@@ -46,6 +48,28 @@ public class Log {
}
}
+ @Override
+ public Iterator iterator() {
+ return new Iterator() {
+
+ private MoveNode current = root;
+ private boolean hasNext = true;
+
+ @Override
+ public boolean hasNext() {
+ return hasNext;
+ }
+
+ @Override
+ public MoveNode next() {
+ MoveNode result = current;
+ if (current.hasVariations()) current = current.variations.get(0);
+ else hasNext = false;
+ return result;
+ }
+ };
+ }
+
/**
* Adds a move to the move history and adjusts the log to the new position.
*
@@ -75,18 +99,19 @@ public class Log {
* move.
*/
public void removeLast() {
- if (!isEmpty() && current.parent != null) {
+ if (hasParent()) {
current.parent.variations.remove(current);
- current = current.parent;
- activeColor = current.activeColor;
- enPassant = current.enPassant;
- fullmoveCounter = current.fullmoveCounter;
- halfmoveClock = current.halfmoveClock;
+ current = current.parent;
+ update();
} else reset();
}
public boolean isEmpty() { return root == null; }
+ public boolean hasParent() {
+ return !isEmpty() && current.parent != null;
+ }
+
/**
* Reverts the log to its initial state corresponding to the default board
* position.
@@ -100,6 +125,44 @@ public class Log {
halfmoveClock = 0;
}
+ /**
+ *
+ * @param index
+ */
+ public void selectNextNode(int index) {
+ if (!isEmpty() && current.variations != null && index < current.variations.size()) {
+ current = current.variations.get(index);
+ update();
+ }
+ }
+
+ /**
+ * Selects the parent of the current {@link MoveNode} as the current node.
+ */
+ public void selectPreviousNode() {
+ if (hasParent()) {
+ current = current.parent;
+ update();
+ }
+ }
+
+ /**
+ * Selects the root {@link MoveNode} as the current node.
+ */
+ public void selectRootNode() {
+ if (!isEmpty()) {
+ current = root;
+ update();
+ }
+ }
+
+ private void update() {
+ activeColor = current.activeColor;
+ enPassant = current.enPassant;
+ fullmoveCounter = current.fullmoveCounter;
+ halfmoveClock = current.halfmoveClock;
+ }
+
/**
* @return The first logged move, or {@code null} if there is none
*/
@@ -196,5 +259,9 @@ public class Log {
* @return A list of all variations associated with this {@link MoveNode}
*/
public List getVariations() { return variations; }
+
+ public boolean hasVariations() {
+ return variations != null && variations.size() > 0;
+ }
}
}
\ No newline at end of file
diff --git a/src/dev/kske/chess/game/Game.java b/src/dev/kske/chess/game/Game.java
index 9e99779..4fe88c3 100644
--- a/src/dev/kske/chess/game/Game.java
+++ b/src/dev/kske/chess/game/Game.java
@@ -80,8 +80,6 @@ public class Game {
// Run garbage collection
System.gc();
- System.out.printf("%s: %s%n", player.color, move);
- System.out.println("FEN: " + board.toFEN());
EventBus.getInstance().dispatch(new MoveEvent(move));
GameState eventType = board.getGameEventType(board.getDest(move).getColor().opposite());
switch (eventType) {
diff --git a/src/dev/kske/chess/ui/GamePane.java b/src/dev/kske/chess/ui/GamePane.java
index 7a4c6cc..6887a5f 100644
--- a/src/dev/kske/chess/ui/GamePane.java
+++ b/src/dev/kske/chess/ui/GamePane.java
@@ -28,6 +28,10 @@ public class GamePane extends JComponent {
private LogPanel logPanel;
private Game game;
private Color activeColor;
+ private JPanel moveSelectionPanel;
+ private JButton btnNext;
+ private JButton btnFirst;
+ private JButton btnLast;
public GamePane() {
activeColor = Color.WHITE;
@@ -35,8 +39,8 @@ public class GamePane extends JComponent {
GridBagLayout gridBagLayout = new GridBagLayout();
gridBagLayout.columnWidths = new int[] { 450, 1, 0 };
gridBagLayout.rowHeights = new int[] { 33, 267, 1, 0 };
- gridBagLayout.columnWeights = new double[] { 0.0, 0.0, Double.MIN_VALUE };
- gridBagLayout.rowWeights = new double[] { 0.0, 0.0, 0.0, Double.MIN_VALUE };
+ gridBagLayout.columnWeights = new double[] { 0.0, 1.0, Double.MIN_VALUE };
+ gridBagLayout.rowWeights = new double[] { 1.0, 0.0, 0.0, Double.MIN_VALUE };
setLayout(gridBagLayout);
JPanel toolPanel = new JPanel();
@@ -58,7 +62,31 @@ public class GamePane extends JComponent {
gbc_toolPanel.fill = GridBagConstraints.HORIZONTAL;
gbc_toolPanel.gridx = 0;
gbc_toolPanel.gridy = 0;
+ gbc_toolPanel.gridwidth = 2;
add(toolPanel, gbc_toolPanel);
+
+ moveSelectionPanel = new JPanel();
+ GridBagConstraints gbc_moveSelectionPanel = new GridBagConstraints();
+ gbc_moveSelectionPanel.fill = GridBagConstraints.BOTH;
+ gbc_moveSelectionPanel.gridx = 2;
+ gbc_moveSelectionPanel.gridy = 0;
+ add(moveSelectionPanel, gbc_moveSelectionPanel);
+
+ btnFirst = new JButton("First");
+ btnFirst.setEnabled(false);
+ moveSelectionPanel.add(btnFirst);
+
+ JButton btnPreviousMove = new JButton("Previous");
+ btnPreviousMove.setEnabled(false);
+ moveSelectionPanel.add(btnPreviousMove);
+
+ btnNext = new JButton("Next");
+ btnNext.setEnabled(false);
+ moveSelectionPanel.add(btnNext);
+
+ btnLast = new JButton("Last");
+ btnLast.setEnabled(false);
+ moveSelectionPanel.add(btnLast);
boardPane = new BoardPane();
GridBagConstraints gbc_boardPane = new GridBagConstraints();
gbc_boardPane.fill = GridBagConstraints.BOTH;
@@ -93,10 +121,11 @@ public class GamePane extends JComponent {
// Initialize LogPanel
logPanel = new LogPanel();
GridBagConstraints gbc_logPanel = new GridBagConstraints();
- gbc_logPanel.anchor = GridBagConstraints.EAST;
- gbc_logPanel.fill = GridBagConstraints.VERTICAL;
- gbc_logPanel.gridx = 2;
- gbc_logPanel.gridy = 1;
+ gbc_logPanel.anchor = GridBagConstraints.EAST;
+ gbc_logPanel.fill = GridBagConstraints.VERTICAL;
+ gbc_logPanel.gridx = 2;
+ gbc_logPanel.gridy = 1;
+ gbc_logPanel.gridheight = 2;
add(logPanel, gbc_logPanel);
}
diff --git a/src/dev/kske/chess/ui/LogPanel.java b/src/dev/kske/chess/ui/LogPanel.java
index 7343fb2..25c4eb7 100644
--- a/src/dev/kske/chess/ui/LogPanel.java
+++ b/src/dev/kske/chess/ui/LogPanel.java
@@ -1,10 +1,9 @@
package dev.kske.chess.ui;
import java.awt.BorderLayout;
-import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
-import java.util.List;
+import java.util.Iterator;
import java.util.Set;
import javax.swing.JPanel;
@@ -30,7 +29,7 @@ public class LogPanel extends JPanel implements Subscribable {
private static final long serialVersionUID = 1932671698254197119L;
- private JTable mtable;
+ private JTable mtable;
private Log log;
@@ -53,18 +52,17 @@ public class LogPanel extends JPanel implements Subscribable {
return new HashSet<>(Arrays.asList(MoveEvent.class));
}
+ // TODO: Implement support for variation selection
@Override
public void handle(Event> event) {
- if (log == null) return;
-
- // TODO: Display log with variations
- final List moves = /* log.getLoggedMoves() */ new ArrayList<>();
- String[][] data = new String[moves.size() / 2 + moves.size() % 2][2];
- for (int i = 0; i < data.length; i++) {
- data[i][0] = moves.get(i * 2).move.toSAN();
- if (i * 2 + 1 < moves.size()) data[i][1] = moves.get(i * 2 + 1).move.toSAN();
+ if (log == null || log.isEmpty()) return;
+ final DefaultTableModel model = new DefaultTableModel(new String[] { "White", "Black" }, 0);
+ for (Iterator iter = log.iterator(); iter.hasNext();) {
+ String[] row = new String[] { iter.next().move.toSAN(), "" };
+ if (iter.hasNext()) row[1] = iter.next().move.toSAN();
+ model.addRow(row);
}
- mtable.setModel(new DefaultTableModel(data, new String[] { "White", "Black" }));
+ mtable.setModel(model);
}
public Log getLog() { return log; }