Skip to content

Commit

Permalink
Merge pull request #364 from zsalch/next_var
Browse files Browse the repository at this point in the history
next: Support Read/Write Sgf with Variation, AW/AB & Comment
  • Loading branch information
featurecat authored Oct 1, 2018
2 parents f274469 + 339feef commit f804902
Show file tree
Hide file tree
Showing 7 changed files with 446 additions and 61 deletions.
19 changes: 19 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
<configuration>
<source>1.8</source>
<target>1.8</target>
<compilerArgs>
<arg>-Xlint:all</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
Expand Down Expand Up @@ -52,6 +55,15 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.9</version>
<configuration>
<!-- Required to get the test failure reports in stdout -->
<useFile>false</useFile>
</configuration>
</plugin>
</plugins>
</build>

Expand All @@ -63,5 +75,12 @@
<version>20180130</version>
</dependency>

<!-- https://mvnrepository.com/artifact/junit/junit -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
13 changes: 13 additions & 0 deletions src/main/java/featurecat/lizzie/rules/Board.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,19 @@ public static boolean isValid(int x, int y) {
return x >= 0 && x < BOARD_SIZE && y >= 0 && y < BOARD_SIZE;
}

/**
* The comment. Thread safe
* @param comment the comment of stone
*/
public void comment(String comment) {
synchronized (this) {

if (history.getData() != null) {
history.getData().comment = comment;
}
}
}

/**
* The pass. Thread safe
*
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/featurecat/lizzie/rules/BoardData.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,9 @@ public class BoardData {
public int blackCaptures;
public int whiteCaptures;

// Comment in the Sgf move
public String comment;

public BoardData(Stone[] stones, int[] lastMove, Stone lastMoveColor, boolean blackToPlay, Zobrist zobrist, int moveNumber, int[] moveNumberList, int blackCaptures, int whiteCaptures, double winrate, int playouts) {
this.moveNumber = moveNumber;
this.lastMove = lastMove;
Expand Down
16 changes: 15 additions & 1 deletion src/main/java/featurecat/lizzie/rules/BoardHistoryList.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,20 @@ public BoardData next() {
return head.getData();
}

/**
* moves the pointer to the right, returns the node stored there
*
* @return the next node, null if there is no next node
*/
public BoardHistoryNode nextNode() {
if (head.next() == null)
return null;
else
head = head.next();

return head;
}

/**
* moves the pointer to the variation number idx, returns the data stored there
*
Expand Down Expand Up @@ -120,7 +134,7 @@ public BoardData getNext() {
public List<BoardHistoryNode> getNexts() {
return head.getNexts();
}

/**
* Does not change the pointer position
*
Expand Down
183 changes: 123 additions & 60 deletions src/main/java/featurecat/lizzie/rules/SGFParser.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package featurecat.lizzie.rules;

import java.util.HashMap;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

Expand Down Expand Up @@ -66,6 +68,12 @@ private static boolean parse(String value) {
return false;
}
int subTreeDepth = 0;
// Save the variation step count
Map<Integer, Integer> subTreeStepMap = new HashMap<Integer, Integer>();
// Comment of the AW/AB (Add White/Add Black) stone
String awabComment = null;
// Previous Tag
String prevTag = null;
boolean inTag = false, isMultiGo = false, escaping = false;
String tag = null;
StringBuilder tagBuilder = new StringBuilder();
Expand All @@ -78,13 +86,9 @@ private static boolean parse(String value) {

String blackPlayer = "", whitePlayer = "";

PARSE_LOOP:
for (byte b : value.getBytes()) {
// Check unicode charactors (UTF-8)
char c = (char) b;
if (((int) b & 0x80) != 0) {
continue;
}
// Support unicode characters (UTF-8)
for (int i = 0; i < value.length(); i++) {
char c = value.charAt(i);
if (escaping) {
// Any char following "\" is inserted verbatim
// (ref) "3.2. Text" in https://www.red-bean.com/sgf/sgf4.html
Expand All @@ -96,14 +100,28 @@ private static boolean parse(String value) {
case '(':
if (!inTag) {
subTreeDepth += 1;
// Initialize the step count
subTreeStepMap.put(subTreeDepth, 0);
} else {
if (i > 0) {
// Allow the comment tag includes '('
tagContentBuilder.append(c);
}
}
break;
case ')':
if (!inTag) {
subTreeDepth -= 1;
if (isMultiGo) {
break PARSE_LOOP;
// Restore to the variation node
int varStep = subTreeStepMap.get(subTreeDepth);
for (int s = 0; s < varStep; s++) {
Lizzie.board.previousMove();
}
}
subTreeDepth -= 1;
} else {
// Allow the comment tag includes '('
tagContentBuilder.append(c);
}
break;
case '[':
Expand Down Expand Up @@ -134,15 +152,26 @@ private static boolean parse(String value) {
if (move == null) {
Lizzie.board.pass(Stone.BLACK);
} else {
// Save the step count
subTreeStepMap.put(subTreeDepth, subTreeStepMap.get(subTreeDepth) + 1);
Lizzie.board.place(move[0], move[1], Stone.BLACK);
}
} else if (tag.equals("W")) {
int[] move = convertSgfPosToCoord(tagContent);
if (move == null) {
Lizzie.board.pass(Stone.WHITE);
} else {
// Save the step count
subTreeStepMap.put(subTreeDepth, subTreeStepMap.get(subTreeDepth) + 1);
Lizzie.board.place(move[0], move[1], Stone.WHITE);
}
} else if (tag.equals("C")) {
// Support comment
if ("AW".equals(prevTag) || "AB".equals(prevTag)) {
awabComment = tagContent;
} else {
Lizzie.board.comment(tagContent);
}
} else if (tag.equals("AB")) {
int[] move = convertSgfPosToCoord(tagContent);
if (move == null) {
Expand Down Expand Up @@ -173,6 +202,7 @@ private static boolean parse(String value) {
e.printStackTrace();
}
}
prevTag = tag;
break;
case ';':
break;
Expand All @@ -199,6 +229,11 @@ private static boolean parse(String value) {
// Rewind to game start
while (Lizzie.board.previousMove()) ;

// Set AW/AB Comment
if (awabComment != null) {
Lizzie.board.comment(awabComment);
}

return true;
}

Expand Down Expand Up @@ -250,65 +285,93 @@ private static void saveToStream(Board board, Writer writer) throws IOException
builder.append(String.format("[%c%c]", x, y));
}
}
} else {
// Process the AW/AB stone
Stone[] stones = history.getStones();
StringBuilder abStone = new StringBuilder();
StringBuilder awStone = new StringBuilder();
for (int i = 0; i < stones.length; i++) {
Stone stone = stones[i];
if (stone.isBlack() || stone.isWhite()) {
// i = x * Board.BOARD_SIZE + y;
int corY = i % Board.BOARD_SIZE;
int corX = (i - corY) / Board.BOARD_SIZE;

char x = (char) (corX + 'a');
char y = (char) (corY + 'a');

if (stone.isBlack()) {
abStone.append(String.format("[%c%c]", x, y));
} else {
awStone.append(String.format("[%c%c]", x, y));
}
}
}
if (abStone.length() > 0) {
builder.append("AB").append(abStone);
}
if (awStone.length() > 0) {
builder.append("AW").append(awStone);
}
}

// The AW/AB Comment
if (history.getData().comment != null) {
builder.append(String.format("C[%s]", history.getData().comment));
}

// replay moves, and convert them to tags.
// * format: ";B[xy]" or ";W[xy]"
// * with 'xy' = coordinates ; or 'tt' for pass.
BoardData data;

// TODO: this code comes from cngoodboy's plugin PR #65. It looks like it might be useful for handling
// AB/AW commands for sgfs in general -- we can extend it beyond just handicap. TODO integrate it
// data = history.getData();
//
// // For handicap
// ArrayList<int[]> abList = new ArrayList<int[]>();
// ArrayList<int[]> awList = new ArrayList<int[]>();
//
// for (int i = 0; i < Board.BOARD_SIZE; i++) {
// for (int j = 0; j < Board.BOARD_SIZE; j++) {
// switch (data.stones[Board.getIndex(i, j)]) {
// case BLACK:
// abList.add(new int[]{i, j});
// break;
// case WHITE:
// awList.add(new int[]{i, j});
// break;
// default:
// break;
// }
// }
// }
//
// if (!abList.isEmpty()) {
// builder.append(";AB");
// for (int i = 0; i < abList.size(); i++) {
// builder.append(String.format("[%s]", convertCoordToSgfPos(abList.get(i))));
// }
// }
//
// if (!awList.isEmpty()) {
// builder.append(";AW");
// for (int i = 0; i < awList.size(); i++) {
// builder.append(String.format("[%s]", convertCoordToSgfPos(awList.get(i))));
// }
// }

while ((data = history.next()) != null) {

String stone;
if (Stone.BLACK.equals(data.lastMoveColor)) stone = "B";
else if (Stone.WHITE.equals(data.lastMoveColor)) stone = "W";
else continue;

char x = data.lastMove == null ? 't' : (char) (data.lastMove[0] + 'a');
char y = data.lastMove == null ? 't' : (char) (data.lastMove[1] + 'a');

builder.append(String.format(";%s[%c%c]", stone, x, y));
}

// Write variation tree
builder.append(generateNode(board, history.getCurrentHistoryNode()));

// close file
builder.append(')');
writer.append(builder.toString());
}

/**
* Generate node with variations
*/
private static String generateNode(Board board, BoardHistoryNode node) throws IOException {
StringBuilder builder = new StringBuilder("");

if (node != null) {

BoardData data = node.getData();
String stone = "";
if (Stone.BLACK.equals(data.lastMoveColor) || Stone.WHITE.equals(data.lastMoveColor)) {

if (Stone.BLACK.equals(data.lastMoveColor)) stone = "B";
else if (Stone.WHITE.equals(data.lastMoveColor)) stone = "W";

char x = data.lastMove == null ? 't' : (char) (data.lastMove[0] + 'a');
char y = data.lastMove == null ? 't' : (char) (data.lastMove[1] + 'a');

builder.append(String.format(";%s[%c%c]", stone, x, y));

// Write the comment
if (data.comment != null) {
builder.append(String.format("C[%s]", data.comment));
}
}

if (node.numberOfChildren() > 1) {
// Variation
for (BoardHistoryNode sub : node.getNexts()) {
builder.append("(");
builder.append(generateNode(board, sub));
builder.append(")");
}
} else if (node.numberOfChildren() == 1) {
builder.append(generateNode(board, node.next()));
} else {
return builder.toString();
}
}

return builder.toString();
}
}
Loading

0 comments on commit f804902

Please sign in to comment.