From cc7d3c78d1e19f12d438bddbc8be5a791c73af57 Mon Sep 17 00:00:00 2001 From: zsalch Date: Tue, 30 Oct 2018 16:24:52 +0800 Subject: [PATCH] Allow write winrate etc. to comment of sgf by append-winrate-to-comment --- src/main/java/featurecat/lizzie/Config.java | 2 + .../featurecat/lizzie/gui/BoardRenderer.java | 21 +----- .../featurecat/lizzie/gui/LizzieFrame.java | 20 ++++++ .../java/featurecat/lizzie/rules/Board.java | 38 ++++------- .../featurecat/lizzie/rules/BoardData.java | 3 + .../featurecat/lizzie/rules/SGFParser.java | 64 +++++++++++++++++++ 6 files changed, 102 insertions(+), 46 deletions(-) diff --git a/src/main/java/featurecat/lizzie/Config.java b/src/main/java/featurecat/lizzie/Config.java index bb72ce255..36e74536b 100644 --- a/src/main/java/featurecat/lizzie/Config.java +++ b/src/main/java/featurecat/lizzie/Config.java @@ -56,6 +56,7 @@ public class Config { public Color winrateMissLineColor = null; public Color blunderBarColor = null; public boolean solidStoneIndicator = false; + public boolean appendWinrateToComment = false; private JSONObject loadAndMergeConfig( JSONObject defaultCfg, String fileName, boolean needValidation) throws IOException { @@ -156,6 +157,7 @@ public Config() throws IOException { handicapInsteadOfWinrate = uiConfig.getBoolean("handicap-instead-of-winrate"); startMaximized = uiConfig.getBoolean("window-maximized"); showDynamicKomi = uiConfig.getBoolean("show-dynamic-komi"); + appendWinrateToComment = uiConfig.optBoolean("append-winrate-to-comment"); winrateStrokeWidth = theme.winrateStrokeWidth(); minimumBlunderBarWidth = theme.minimumBlunderBarWidth(); diff --git a/src/main/java/featurecat/lizzie/gui/BoardRenderer.java b/src/main/java/featurecat/lizzie/gui/BoardRenderer.java index 52153ee46..9d9b1add2 100644 --- a/src/main/java/featurecat/lizzie/gui/BoardRenderer.java +++ b/src/main/java/featurecat/lizzie/gui/BoardRenderer.java @@ -649,7 +649,7 @@ private void drawLeelazSuggestions(Graphics2D g) { suggestionX, suggestionY + stoneRadius * 2 / 5, LizzieFrame.uiFont, - getPlayoutsString(move.playouts), + Lizzie.frame.getPlayoutsString(move.playouts), (float) (stoneRadius * 0.8), stoneRadius * 1.4); } @@ -1072,25 +1072,6 @@ private Font makeFont(Font fontBase, int style) { return font.deriveFont(atts); } - /** - * @return a shorter, rounded string version of playouts. e.g. 345 -> 345, 1265 -> 1.3k, 44556 -> - * 45k, 133523 -> 134k, 1234567 -> 1.2m - */ - private String getPlayoutsString(int playouts) { - if (playouts >= 1_000_000) { - double playoutsDouble = (double) playouts / 100_000; // 1234567 -> 12.34567 - return round(playoutsDouble) / 10.0 + "m"; - } else if (playouts >= 10_000) { - double playoutsDouble = (double) playouts / 1_000; // 13265 -> 13.265 - return round(playoutsDouble) + "k"; - } else if (playouts >= 1_000) { - double playoutsDouble = (double) playouts / 100; // 1265 -> 12.65 - return round(playoutsDouble) / 10.0 + "k"; - } else { - return String.valueOf(playouts); - } - } - private int[] calculatePixelMargins() { return calculatePixelMargins(boardLength); } diff --git a/src/main/java/featurecat/lizzie/gui/LizzieFrame.java b/src/main/java/featurecat/lizzie/gui/LizzieFrame.java index c98e07203..4cc642532 100644 --- a/src/main/java/featurecat/lizzie/gui/LizzieFrame.java +++ b/src/main/java/featurecat/lizzie/gui/LizzieFrame.java @@ -4,6 +4,7 @@ import static java.awt.image.BufferedImage.TYPE_INT_RGB; import static java.lang.Math.max; import static java.lang.Math.min; +import static java.lang.Math.round; import com.jhlabs.image.GaussianFilter; import featurecat.lizzie.Lizzie; @@ -605,6 +606,25 @@ private void drawPonderingState(Graphics2D g, String text, int x, int y, double text, x + (width - stringWidth) / 2, y + stringHeight + (height - stringHeight) / 2); } + /** + * @return a shorter, rounded string version of playouts. e.g. 345 -> 345, 1265 -> 1.3k, 44556 -> + * 45k, 133523 -> 134k, 1234567 -> 1.2m + */ + public String getPlayoutsString(int playouts) { + if (playouts >= 1_000_000) { + double playoutsDouble = (double) playouts / 100_000; // 1234567 -> 12.34567 + return round(playoutsDouble) / 10.0 + "m"; + } else if (playouts >= 10_000) { + double playoutsDouble = (double) playouts / 1_000; // 13265 -> 13.265 + return round(playoutsDouble) + "k"; + } else if (playouts >= 1_000) { + double playoutsDouble = (double) playouts / 100; // 1265 -> 12.65 + return round(playoutsDouble) / 10.0 + "k"; + } else { + return String.valueOf(playouts); + } + } + /** * Truncate text that is too long for the given width * diff --git a/src/main/java/featurecat/lizzie/rules/Board.java b/src/main/java/featurecat/lizzie/rules/Board.java index b3ff4214a..c2e0fa5f3 100644 --- a/src/main/java/featurecat/lizzie/rules/Board.java +++ b/src/main/java/featurecat/lizzie/rules/Board.java @@ -323,13 +323,7 @@ public void place(int x, int y, Stone color, boolean newBranch) { if (!isValid(x, y) || (history.getStones()[getIndex(x, y)] != Stone.EMPTY && !newBranch)) return; - // Update winrate - Leelaz.WinrateStats stats = Lizzie.leelaz.getWinrateStats(); - - if (stats.maxWinrate >= 0 && stats.totalPlayouts > history.getData().playouts) { - history.getData().winrate = stats.maxWinrate; - history.getData().playouts = stats.totalPlayouts; - } + updateWinrate(); double nextWinrate = -100; if (history.getData().winrate >= 0) nextWinrate = 100 - history.getData().winrate; @@ -583,12 +577,7 @@ public int[] getMoveNumberList() { /** Goes to the next coordinate, thread safe */ public boolean nextMove() { synchronized (this) { - // Update win rate statistics - Leelaz.WinrateStats stats = Lizzie.leelaz.getWinrateStats(); - if (stats.totalPlayouts >= history.getData().playouts) { - history.getData().winrate = stats.maxWinrate; - history.getData().playouts = stats.totalPlayouts; - } + updateWinrate(); if (history.next().isPresent()) { // update leelaz board position, before updating to next node Optional lastMoveOpt = history.getData().lastMove; @@ -614,12 +603,7 @@ public boolean nextMove() { */ public boolean nextMove(int fromBackChildren) { synchronized (this) { - // Update win rate statistics - Leelaz.WinrateStats stats = Lizzie.leelaz.getWinrateStats(); - if (stats.totalPlayouts >= history.getData().playouts) { - history.getData().winrate = stats.maxWinrate; - history.getData().playouts = stats.totalPlayouts; - } + updateWinrate(); return nextVariation(fromBackChildren); } } @@ -944,13 +928,7 @@ public void clear() { public boolean previousMove() { synchronized (this) { if (inScoreMode()) setScoreMode(false); - // Update win rate statistics - Leelaz.WinrateStats stats = Lizzie.leelaz.getWinrateStats(); - - if (stats.totalPlayouts >= history.getData().playouts) { - history.getData().winrate = stats.maxWinrate; - history.getData().playouts = stats.totalPlayouts; - } + updateWinrate(); if (history.previous().isPresent()) { Lizzie.leelaz.undo(); Lizzie.frame.repaint(); @@ -1285,4 +1263,12 @@ public void resumePreviousGame() { } catch (JSONException err) { } } + + public void updateWinrate() { + Leelaz.WinrateStats stats = Lizzie.leelaz.getWinrateStats(); + if (stats.maxWinrate >= 0 && stats.totalPlayouts > history.getData().playouts) { + history.getData().winrate = stats.maxWinrate; + history.getData().playouts = stats.totalPlayouts; + } + } } diff --git a/src/main/java/featurecat/lizzie/rules/BoardData.java b/src/main/java/featurecat/lizzie/rules/BoardData.java index d874f42d6..8458b0f86 100644 --- a/src/main/java/featurecat/lizzie/rules/BoardData.java +++ b/src/main/java/featurecat/lizzie/rules/BoardData.java @@ -72,6 +72,9 @@ public static BoardData empty(int size) { */ public void addProperty(String key, String value) { SGFParser.addProperty(properties, key, value); + if ("N".equals(key) && comment.isEmpty()) { + comment = value; + } } /** diff --git a/src/main/java/featurecat/lizzie/rules/SGFParser.java b/src/main/java/featurecat/lizzie/rules/SGFParser.java index 5784dff9e..8043a2dda 100644 --- a/src/main/java/featurecat/lizzie/rules/SGFParser.java +++ b/src/main/java/featurecat/lizzie/rules/SGFParser.java @@ -4,6 +4,7 @@ import featurecat.lizzie.Lizzie; import featurecat.lizzie.analysis.GameInfo; +import featurecat.lizzie.analysis.Leelaz; import java.io.*; import java.text.SimpleDateFormat; import java.util.HashMap; @@ -307,6 +308,11 @@ private static void saveToStream(Board board, Writer writer) throws IOException "KM[%s]PW[%s]PB[%s]DT[%s]AP[Lizzie: %s]", komi, playerW, playerB, date, Lizzie.lizzieVersion)); + // To append the winrate to the comment of sgf we might need to update the Winrate + if (Lizzie.config.appendWinrateToComment) { + Lizzie.board.updateWinrate(); + } + // move to the first move history.toStart(); @@ -398,6 +404,11 @@ private static String generateNode(Board board, BoardHistoryNode node) throws IO // Node properties builder.append(data.propertiesString()); + if (Lizzie.config.appendWinrateToComment) { + // Append the winrate to the comment of sgf + data.comment = formatComment(node); + } + // Write the comment if (!data.comment.isEmpty()) { builder.append(String.format("C[%s]", data.comment)); @@ -421,6 +432,59 @@ private static String generateNode(Board board, BoardHistoryNode node) throws IO return builder.toString(); } + /** + * Format Comment with following format: Move () ( / ) + */ + private static String formatComment(BoardHistoryNode node) { + BoardData data = node.getData(); + String engine = Lizzie.leelaz.currentWeight(); + + // Playouts + String playouts = Lizzie.frame.getPlayoutsString(data.playouts); + + // Last winrate + BoardData lastNode = node.previous().get().getData(); + boolean validLastWinrate = (lastNode.playouts > 0); + double lastWR = validLastWinrate ? lastNode.winrate : 50; + + // Current winrate + boolean validWinrate = (data.playouts > 0); + double curWR = validWinrate ? data.winrate : 100 - lastWR; + String curWinrate = ""; + if (Lizzie.config.handicapInsteadOfWinrate) { + curWinrate = String.format("%.2f", Leelaz.winrateToHandicap(100 - curWR)); + } else { + curWinrate = String.format("%.1f%%", 100 - curWR); + } + + // Last move difference winrate + String lastMoveDiff = ""; + if (validLastWinrate && validWinrate) { + if (Lizzie.config.handicapInsteadOfWinrate) { + double currHandicapedWR = Leelaz.winrateToHandicap(100 - curWR); + double lastHandicapedWR = Leelaz.winrateToHandicap(lastWR); + lastMoveDiff = String.format(": %.2f", currHandicapedWR - lastHandicapedWR); + } else { + lastMoveDiff = String.format("(%.1f%%)", 100 - lastWR - curWR); + } + } + + String wf = "Move %d\n%s %s\n(%s / %s playouts)"; + String nc = String.format(wf, data.moveNumber, curWinrate, lastMoveDiff, engine, playouts); + + if (!data.comment.isEmpty()) { + String wp = + "Move [0-9]+\n[0-9\\.\\-]+%* \\(*[0-9\\.\\-]*%*\\)*\n\\([^\\(\\)/]* \\/ [0-9\\.]*[kmKM]* playouts\\)"; + if (data.comment.matches("(?s).*" + wp + "(?s).*")) { + nc = data.comment.replaceAll(wp, nc); + } else { + nc = String.format("%s\n\n%s", nc, data.comment); + } + } + return nc; + } + public static boolean isListProperty(String key) { return asList(listProps).contains(key); }