From ea8d754a72479124b7e0bf2eee496359724083f3 Mon Sep 17 00:00:00 2001 From: CyB3RC0nN0R Date: Fri, 13 Sep 2019 18:13:34 +0200 Subject: [PATCH] Added variations in Log, added LogTest --- src/dev/kske/chess/board/Board.java | 10 +- src/dev/kske/chess/board/Log.java | 86 ++++++++----- src/dev/kske/chess/ui/LogPanel.java | 5 +- test/dev/kske/chess/board/LogTest.java | 159 +++++++++++++++++++++++++ 4 files changed, 225 insertions(+), 35 deletions(-) create mode 100644 test/dev/kske/chess/board/LogTest.java diff --git a/src/dev/kske/chess/board/Board.java b/src/dev/kske/chess/board/Board.java index c8f8e2b..4ae9622 100644 --- a/src/dev/kske/chess/board/Board.java +++ b/src/dev/kske/chess/board/Board.java @@ -5,7 +5,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; -import dev.kske.chess.board.Log.LoggedMove; +import dev.kske.chess.board.Log.MoveNode; import dev.kske.chess.board.Piece.Color; import dev.kske.chess.board.Piece.Type; @@ -163,9 +163,9 @@ public class Board implements Cloneable { * Reverts the last move. */ public void revert() { - LoggedMove loggedMove = log.getLast(); - Move move = loggedMove.move; - Piece capturedPiece = loggedMove.capturedPiece; + MoveNode moveNode = log.getLast(); + Move move = moveNode.move; + Piece capturedPiece = moveNode.capturedPiece; switch (move.type) { case PAWN_PROMOTION: @@ -509,7 +509,7 @@ public class Board implements Cloneable { if (castlingSb.length() == 0) sb.append("-"); sb.append(castlingSb); - final LoggedMove lastMove = log.getLast(); + final MoveNode lastMove = log.getLast(); // En passant availability sb.append(" " + (lastMove == null || lastMove.enPassant == null ? "-" : lastMove.enPassant.toSAN())); diff --git a/src/dev/kske/chess/board/Log.java b/src/dev/kske/chess/board/Log.java index 56d34dd..8bdb5e1 100644 --- a/src/dev/kske/chess/board/Log.java +++ b/src/dev/kske/chess/board/Log.java @@ -1,7 +1,7 @@ package dev.kske.chess.board; -import java.util.ArrayList; -import java.util.List; +import java.util.HashSet; +import java.util.Set; import dev.kske.chess.board.Piece.Color; @@ -13,27 +13,30 @@ import dev.kske.chess.board.Piece.Color; */ public class Log implements Cloneable { - private List moves; + private MoveNode root, current; private Position enPassant; private Color activeColor; private int fullmoveCounter, halfmoveClock; public Log() { - moves = new ArrayList<>(); reset(); } + // TODO: Adjust to variations @Override public Object clone() { Log log = null; try { log = (Log) super.clone(); + if (!isEmpty()) { + log.current = (MoveNode) current.clone(); + if (current == root) log.root = log.current; + else log.root = (MoveNode) root.clone(); + } } catch (CloneNotSupportedException e) { e.printStackTrace(); } - log.moves = new ArrayList<>(); - log.moves.addAll(this.moves); return log; } @@ -50,13 +53,16 @@ public class Log implements Cloneable { if (pawnMove || capturedPiece != null) halfmoveClock = 0; else++halfmoveClock; activeColor = activeColor.opposite(); - moves.add(new LoggedMove(move, capturedPiece, enPassant, activeColor, fullmoveCounter, halfmoveClock)); - } + final MoveNode leaf = new MoveNode(move, capturedPiece, enPassant, activeColor, fullmoveCounter, halfmoveClock); - /** - * @return the last logged move, or {@code null} if there is none - */ - public LoggedMove getLast() { return moves.isEmpty() ? null : moves.get(moves.size() - 1); } + if (isEmpty()) { + root = leaf; + current = leaf; + } else { + current.addVariation(leaf); + current = leaf; + } + } /** * Removed the last move from the log and adjusts its state to the previous @@ -64,36 +70,45 @@ public class Log implements Cloneable { */ public void removeLast() { if (!isEmpty()) { - moves.remove(moves.size() - 1); - if (!isEmpty()) { - LoggedMove last = moves.get(moves.size() - 1); - activeColor = last.activeColor; - enPassant = last.enPassant; - fullmoveCounter = last.fullmoveCounter; - halfmoveClock = last.halfmoveClock; - } else reset(); + if (current.parent == null) { + current = null; + root = null; + } else { + current = current.parent; + if (!isEmpty()) { + activeColor = current.activeColor; + enPassant = current.enPassant; + fullmoveCounter = current.fullmoveCounter; + halfmoveClock = current.halfmoveClock; + } else reset(); + } } else reset(); } - public boolean isEmpty() { return moves.isEmpty(); } + public boolean isEmpty() { return root == null; } /** * Reverts the log to its initial state corresponding to the default board * position. */ public void reset() { - moves.clear(); + root = null; + current = null; enPassant = null; activeColor = Color.WHITE; fullmoveCounter = 1; halfmoveClock = 0; } - public List getLoggedMoves() { return moves; } + /** + * @return The first logged move, or {@code null} if there is none + */ + public MoveNode getRoot() { return root; } - public List getMoves() { return moves; } - - public void setMoves(List moves) { this.moves = moves; } + /** + * @return the last logged move, or {@code null} if there is none + */ + public MoveNode getLast() { return current; } public Position getEnPassant() { return enPassant; } @@ -111,7 +126,7 @@ public class Log implements Cloneable { public void setHalfmoveClock(int halfmoveClock) { this.halfmoveClock = halfmoveClock; } - public static class LoggedMove { + public static class MoveNode { public final Move move; public final Piece capturedPiece; @@ -119,7 +134,10 @@ public class Log implements Cloneable { public final Color activeColor; public final int fullmoveCounter, halfmoveClock; - public LoggedMove(Move move, Piece capturedPiece, Position enPassant, Color activeColor, int fullmoveCounter, + private MoveNode parent; + private Set variations = new HashSet<>(); + + public MoveNode(Move move, Piece capturedPiece, Position enPassant, Color activeColor, int fullmoveCounter, int halfmoveClock) { this.move = move; this.capturedPiece = capturedPiece; @@ -128,5 +146,17 @@ public class Log implements Cloneable { this.fullmoveCounter = fullmoveCounter; this.halfmoveClock = halfmoveClock; } + + @Override + protected Object clone() throws CloneNotSupportedException { + return super.clone(); + } + + public void addVariation(MoveNode variation) { + if (!variations.contains(variation)) { + variations.add(variation); + variation.parent = this; + } + } } } diff --git a/src/dev/kske/chess/ui/LogPanel.java b/src/dev/kske/chess/ui/LogPanel.java index 96a7058..21488e2 100644 --- a/src/dev/kske/chess/ui/LogPanel.java +++ b/src/dev/kske/chess/ui/LogPanel.java @@ -1,6 +1,7 @@ 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; @@ -13,7 +14,7 @@ import javax.swing.border.EmptyBorder; import javax.swing.table.DefaultTableModel; import dev.kske.chess.board.Log; -import dev.kske.chess.board.Log.LoggedMove; +import dev.kske.chess.board.Log.MoveNode; import dev.kske.chess.event.Event; import dev.kske.chess.event.EventBus; import dev.kske.chess.event.MoveEvent; @@ -56,7 +57,7 @@ public class LogPanel extends JPanel implements Subscribable { public void handle(Event event) { if (log == null) return; - final List moves = log.getLoggedMoves(); + 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(); diff --git a/test/dev/kske/chess/board/LogTest.java b/test/dev/kske/chess/board/LogTest.java new file mode 100644 index 0000000..2c19a1c --- /dev/null +++ b/test/dev/kske/chess/board/LogTest.java @@ -0,0 +1,159 @@ +package dev.kske.chess.board; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.fail; + +import org.junit.jupiter.api.Test; + +import dev.kske.chess.board.Piece.Color; + +/** + * Project: Chess
+ * File: LogTest.java
+ * Created: 13 Sep 2019
+ * Author: Kai S. K. Engelbart + */ +class LogTest { + + Log log = new Log(); + + /** + * Test method for {@link dev.kske.chess.board.Log#Log()}. + */ + @Test + void testLog() { + assertTrue(log.isEmpty()); + assertNull(log.getLast()); + assertNull(log.getRoot()); + assertEquals(log.getActiveColor(), Color.WHITE); + assertNull(log.getEnPassant()); + assertEquals(log.getFullmoveCounter(), 1); + assertEquals(log.getHalfmoveClock(), 0); + } + + /** + * Test method for {@link dev.kske.chess.board.Log#clone()}. + */ + @Test + void testClone() { + Log other = (Log) log.clone(); + log.setActiveColor(Color.WHITE); + other.setActiveColor(Color.BLACK); + assertNotEquals(other.getActiveColor(), log.getActiveColor()); + } + + /** + * Test method for {@link dev.kske.chess.board.Log#add(dev.kske.chess.board.Move, dev.kske.chess.board.Piece, boolean)}. + */ + @Test + void testAdd() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link dev.kske.chess.board.Log#removeLast()}. + */ + @Test + void testRemoveLast() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link dev.kske.chess.board.Log#isEmpty()}. + */ + @Test + void testIsEmpty() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link dev.kske.chess.board.Log#reset()}. + */ + @Test + void testReset() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link dev.kske.chess.board.Log#getRoot()}. + */ + @Test + void testGetRoot() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link dev.kske.chess.board.Log#getLast()}. + */ + @Test + void testGetLast() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link dev.kske.chess.board.Log#getEnPassant()}. + */ + @Test + void testGetEnPassant() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link dev.kske.chess.board.Log#setEnPassant(dev.kske.chess.board.Position)}. + */ + @Test + void testSetEnPassant() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link dev.kske.chess.board.Log#getActiveColor()}. + */ + @Test + void testGetActiveColor() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link dev.kske.chess.board.Log#setActiveColor(dev.kske.chess.board.Piece.Color)}. + */ + @Test + void testSetActiveColor() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link dev.kske.chess.board.Log#getFullmoveCounter()}. + */ + @Test + void testGetFullmoveCounter() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link dev.kske.chess.board.Log#setFullmoveCounter(int)}. + */ + @Test + void testSetFullmoveCounter() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link dev.kske.chess.board.Log#getHalfmoveClock()}. + */ + @Test + void testGetHalfmoveClock() { + fail("Not yet implemented"); + } + + /** + * Test method for {@link dev.kske.chess.board.Log#setHalfmoveClock(int)}. + */ + @Test + void testSetHalfmoveClock() { + fail("Not yet implemented"); + } +} \ No newline at end of file