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; }