-
Notifications
You must be signed in to change notification settings - Fork 227
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
next: Support Read/Write Sgf with Variation, AW/AB & Comment #364
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
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; | ||
|
||
|
@@ -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(); | ||
|
@@ -79,12 +87,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; | ||
} | ||
// Suppoert unicode charactors (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 | ||
|
@@ -96,14 +101,27 @@ private static boolean parse(String value) { | |
case '(': | ||
if (!inTag) { | ||
subTreeDepth += 1; | ||
// Initialize the step count | ||
subTreeStepMap.put(Integer.valueOf(subTreeDepth), Integer.valueOf(0)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think in Java it's not required to explicitly convert between int and Integer, the java compiler will automatically box and unbox This comment applies for the entirety of this PR, it would be nice if you could go over the diff and remove all the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are right. |
||
} else { | ||
if (i > 0) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you explain the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. For skip first '(' that at the top of the Sgf file. |
||
// Allow the comment tag includes '(' | ||
tagContentBuilder.append(c); | ||
} | ||
} | ||
break; | ||
case ')': | ||
if (!inTag) { | ||
subTreeDepth -= 1; | ||
if (isMultiGo) { | ||
break PARSE_LOOP; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You can also remove the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Removed. |
||
// Restore to the variation node | ||
for (int s = 0; s < subTreeStepMap.get(Integer.valueOf(subTreeDepth)).intValue(); s++) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It's a bit suspicious to recompute There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||
Lizzie.board.previousMove(); | ||
} | ||
} | ||
subTreeDepth -= 1; | ||
} else { | ||
// Allow the comment tag includes '(' | ||
tagContentBuilder.append(c); | ||
} | ||
break; | ||
case '[': | ||
|
@@ -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(Integer.valueOf(subTreeDepth), Integer.valueOf(subTreeStepMap.get(Integer.valueOf(subTreeDepth)).intValue() + 1)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indentation is off here and a bit all over the PR... Could you configure your text editor to output spaces instead of tabs? The output should look nicer on GitHub and it would also be more consistent with the rest of the lizzie code base. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Sorry, I have ignored this problem because with multiple editors. |
||
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(Integer.valueOf(subTreeDepth), Integer.valueOf(subTreeStepMap.get(Integer.valueOf(subTreeDepth)).intValue() + 1)); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line seams to be copy pasted. Is a way to avoid the duplication? |
||
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) { | ||
|
@@ -173,6 +202,7 @@ private static boolean parse(String value) { | |
e.printStackTrace(); | ||
} | ||
} | ||
prevTag = tag; | ||
break; | ||
case ';': | ||
break; | ||
|
@@ -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; | ||
} | ||
|
||
|
@@ -250,65 +285,92 @@ private static void saveToStream(Board board, Writer writer) throws IOException | |
builder.append(String.format("[%c%c]", x, y)); | ||
} | ||
} | ||
} else { | ||
// Process the AW/AB stone | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't understand that else. Can you explain why this should be executed only when There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. When has handicap, there is Black stones only, the original code already is processed. |
||
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, writer, history.nextNode())); | ||
|
||
// close file | ||
builder.append(')'); | ||
writer.append(builder.toString()); | ||
} | ||
|
||
// Generate node | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Useless comment There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Fixed. |
||
private static String generateNode(Board board, Writer writer, BoardHistoryNode node) throws IOException { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you need the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Garbage code, deleted. |
||
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, writer, sub)); | ||
builder.append(")"); | ||
} | ||
} else if (node.numberOfChildren() == 1) { | ||
builder.append(generateNode(board, writer, node.next())); | ||
} else { | ||
return builder.toString(); | ||
} | ||
} | ||
} | ||
|
||
return builder.toString(); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo Suppoert
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
But I think you should remove that comment. After your changes the code supports unicode because it uses charAt instead of looking that bytes, so it will be hard to understand the comment out of context.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fixed.