Added variations in Log, added LogTest

This commit is contained in:
Kai S. K. Engelbart 2019-09-13 18:13:34 +02:00
parent 1d2cf364bd
commit ea8d754a72
4 changed files with 225 additions and 35 deletions

View File

@ -5,7 +5,7 @@ import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; 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.Color;
import dev.kske.chess.board.Piece.Type; import dev.kske.chess.board.Piece.Type;
@ -163,9 +163,9 @@ public class Board implements Cloneable {
* Reverts the last move. * Reverts the last move.
*/ */
public void revert() { public void revert() {
LoggedMove loggedMove = log.getLast(); MoveNode moveNode = log.getLast();
Move move = loggedMove.move; Move move = moveNode.move;
Piece capturedPiece = loggedMove.capturedPiece; Piece capturedPiece = moveNode.capturedPiece;
switch (move.type) { switch (move.type) {
case PAWN_PROMOTION: case PAWN_PROMOTION:
@ -509,7 +509,7 @@ public class Board implements Cloneable {
if (castlingSb.length() == 0) sb.append("-"); if (castlingSb.length() == 0) sb.append("-");
sb.append(castlingSb); sb.append(castlingSb);
final LoggedMove lastMove = log.getLast(); final MoveNode lastMove = log.getLast();
// En passant availability // En passant availability
sb.append(" " + (lastMove == null || lastMove.enPassant == null ? "-" : lastMove.enPassant.toSAN())); sb.append(" " + (lastMove == null || lastMove.enPassant == null ? "-" : lastMove.enPassant.toSAN()));

View File

@ -1,7 +1,7 @@
package dev.kske.chess.board; package dev.kske.chess.board;
import java.util.ArrayList; import java.util.HashSet;
import java.util.List; import java.util.Set;
import dev.kske.chess.board.Piece.Color; import dev.kske.chess.board.Piece.Color;
@ -13,27 +13,30 @@ import dev.kske.chess.board.Piece.Color;
*/ */
public class Log implements Cloneable { public class Log implements Cloneable {
private List<LoggedMove> moves; private MoveNode root, current;
private Position enPassant; private Position enPassant;
private Color activeColor; private Color activeColor;
private int fullmoveCounter, halfmoveClock; private int fullmoveCounter, halfmoveClock;
public Log() { public Log() {
moves = new ArrayList<>();
reset(); reset();
} }
// TODO: Adjust to variations
@Override @Override
public Object clone() { public Object clone() {
Log log = null; Log log = null;
try { try {
log = (Log) super.clone(); 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) { } catch (CloneNotSupportedException e) {
e.printStackTrace(); e.printStackTrace();
} }
log.moves = new ArrayList<>();
log.moves.addAll(this.moves);
return log; return log;
} }
@ -50,13 +53,16 @@ public class Log implements Cloneable {
if (pawnMove || capturedPiece != null) halfmoveClock = 0; if (pawnMove || capturedPiece != null) halfmoveClock = 0;
else++halfmoveClock; else++halfmoveClock;
activeColor = activeColor.opposite(); activeColor = activeColor.opposite();
moves.add(new LoggedMove(move, capturedPiece, enPassant, activeColor, fullmoveCounter, halfmoveClock)); final MoveNode leaf = new MoveNode(move, capturedPiece, enPassant, activeColor, fullmoveCounter, halfmoveClock);
}
/** if (isEmpty()) {
* @return the last logged move, or {@code null} if there is none root = leaf;
*/ current = leaf;
public LoggedMove getLast() { return moves.isEmpty() ? null : moves.get(moves.size() - 1); } } else {
current.addVariation(leaf);
current = leaf;
}
}
/** /**
* Removed the last move from the log and adjusts its state to the previous * 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() { public void removeLast() {
if (!isEmpty()) { if (!isEmpty()) {
moves.remove(moves.size() - 1); if (current.parent == null) {
current = null;
root = null;
} else {
current = current.parent;
if (!isEmpty()) { if (!isEmpty()) {
LoggedMove last = moves.get(moves.size() - 1); activeColor = current.activeColor;
activeColor = last.activeColor; enPassant = current.enPassant;
enPassant = last.enPassant; fullmoveCounter = current.fullmoveCounter;
fullmoveCounter = last.fullmoveCounter; halfmoveClock = current.halfmoveClock;
halfmoveClock = last.halfmoveClock;
} else reset(); } else reset();
}
} 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 * Reverts the log to its initial state corresponding to the default board
* position. * position.
*/ */
public void reset() { public void reset() {
moves.clear(); root = null;
current = null;
enPassant = null; enPassant = null;
activeColor = Color.WHITE; activeColor = Color.WHITE;
fullmoveCounter = 1; fullmoveCounter = 1;
halfmoveClock = 0; halfmoveClock = 0;
} }
public List<LoggedMove> getLoggedMoves() { return moves; } /**
* @return The first logged move, or {@code null} if there is none
*/
public MoveNode getRoot() { return root; }
public List<LoggedMove> getMoves() { return moves; } /**
* @return the last logged move, or {@code null} if there is none
public void setMoves(List<LoggedMove> moves) { this.moves = moves; } */
public MoveNode getLast() { return current; }
public Position getEnPassant() { return enPassant; } public Position getEnPassant() { return enPassant; }
@ -111,7 +126,7 @@ public class Log implements Cloneable {
public void setHalfmoveClock(int halfmoveClock) { this.halfmoveClock = halfmoveClock; } public void setHalfmoveClock(int halfmoveClock) { this.halfmoveClock = halfmoveClock; }
public static class LoggedMove { public static class MoveNode {
public final Move move; public final Move move;
public final Piece capturedPiece; public final Piece capturedPiece;
@ -119,7 +134,10 @@ public class Log implements Cloneable {
public final Color activeColor; public final Color activeColor;
public final int fullmoveCounter, halfmoveClock; public final int fullmoveCounter, halfmoveClock;
public LoggedMove(Move move, Piece capturedPiece, Position enPassant, Color activeColor, int fullmoveCounter, private MoveNode parent;
private Set<MoveNode> variations = new HashSet<>();
public MoveNode(Move move, Piece capturedPiece, Position enPassant, Color activeColor, int fullmoveCounter,
int halfmoveClock) { int halfmoveClock) {
this.move = move; this.move = move;
this.capturedPiece = capturedPiece; this.capturedPiece = capturedPiece;
@ -128,5 +146,17 @@ public class Log implements Cloneable {
this.fullmoveCounter = fullmoveCounter; this.fullmoveCounter = fullmoveCounter;
this.halfmoveClock = halfmoveClock; 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;
}
}
} }
} }

View File

@ -1,6 +1,7 @@
package dev.kske.chess.ui; package dev.kske.chess.ui;
import java.awt.BorderLayout; import java.awt.BorderLayout;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
@ -13,7 +14,7 @@ import javax.swing.border.EmptyBorder;
import javax.swing.table.DefaultTableModel; import javax.swing.table.DefaultTableModel;
import dev.kske.chess.board.Log; 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.Event;
import dev.kske.chess.event.EventBus; import dev.kske.chess.event.EventBus;
import dev.kske.chess.event.MoveEvent; import dev.kske.chess.event.MoveEvent;
@ -56,7 +57,7 @@ public class LogPanel extends JPanel implements Subscribable {
public void handle(Event<?> event) { public void handle(Event<?> event) {
if (log == null) return; if (log == null) return;
final List<LoggedMove> moves = log.getLoggedMoves(); final List<MoveNode> moves = /* log.getLoggedMoves() */ new ArrayList<>();
String[][] data = new String[moves.size() / 2 + moves.size() % 2][2]; String[][] data = new String[moves.size() / 2 + moves.size() % 2][2];
for (int i = 0; i < data.length; i++) { for (int i = 0; i < data.length; i++) {
data[i][0] = moves.get(i * 2).move.toSAN(); data[i][0] = moves.get(i * 2).move.toSAN();

View File

@ -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: <strong>Chess</strong><br>
* File: <strong>LogTest.java</strong><br>
* Created: <strong>13 Sep 2019</strong><br>
* Author: <strong>Kai S. K. Engelbart</strong>
*/
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");
}
}