diff --git a/doc/plugin.md b/doc/plugin.md
deleted file mode 100644
index e5053390c..000000000
--- a/doc/plugin.md
+++ /dev/null
@@ -1,115 +0,0 @@
-# Developing plug-ins for Lizzie
-
-
-The Jar file for a plugin should look like this:
-
-./plugin
-./plugin/Plugin.class
-./META-INF
-./META-INF/MANIFEST.MF
-./...Your other classes
-
-### Start first step
-
-Create a "HelloWorld" directory and create a "plugin" directory in it, and create a new "Plugin.java" file in the "plugin" directory.
-
-And write the following
-
-```java
-package plugin;
-
-import java.io.IOException;
-
-import IPlugin;
-
-/**
- * Plugin
- */
-public class Plugin implements IPlugin {
- public static String name = "Hello World";
- public static String version = "0.0.0";
-
- @Override
- public String getName() {
- return name;
- }
-
- @Override
- public String getVersion() {
- return version;
- }
-}
-```
-
-This is the most basic framework of a plugin where ``` name ``` and ``` version ``` are used to generate a hash of this plugin.
-
-Let's edit Plugin.java to have a message box pop up when we press 'H'.
-
-```java
- @Override
- public void onKeyPressed(KeyEvent e) {
- if (e.getKeyCode() == KeyEvent.VK_H) {
- JOptionPane.showConfirmDialog(null, "Hello World!");
- }
- }
-```
-
-And add the import statement at the beginning of the file
-
-```java
-import java.swt.KeyEvent;
-import javax.swing.JOptionPane;
-```
-
-Copy lizzie.jar to "HelloWorld" directory, and execute the command:
-
-```
-javac -classpath lizzie.jar ./plugin/Plugin.java
-jar -cvf HelloWorld.jar ./plugin/Plugin.class
-```
-
-Copy the generated "HelloWorld.jar" to the "plugin" directory under the Lizzie directory.
-
-Start the lizzie and press 'H'!
-
-### More
-
-The plugin can freely call Lizzie's own classes and interfaces, so it can do a lot of functions.
-
-In the future, plugins may be able to enable and disable certain functions, so it is possible to do very complete functions with plug-ins.
-
-### Methods for plug-ins
-
-In ``` Lizzie.config ```:
-
-``` public boolean showBranch ```
-
-``` public boolean showBestMoves ```
-
-``` public void toggleShowBranch() ```
-
-``` public void toggleShowBestMoves() ```
-
-### Callback functions
-
-Basic callback function
-
-``` public abstract String getName() ``` Should be override, return plug-in 's name.
-``` public abstract String getVersion() ``` Should be override, return plug-in 's version.
-
-``` public void onInit() throws IOException ``` Called when the plugin is loaded, the plugin is loaded after loading the configuration file.
-
-``` public void onMousePressed(MouseEvent e) ```
-``` public void onMouseReleased(MouseEvent e) ```
-``` public void onMouseMoved(MouseEvent e) ```
-``` public void onKeyPressed(KeyEvent e) ```
-``` public void onKeyReleased(KeyEvent e) ``` Like its name, it is called in the corresponding event.
-
-``` public boolean onDraw(Graphics2D g) ``` Called when drawing, the return value represents whether to submit the drawing result.
-
-``` public void onShutdown() throws IOException ``` Called when the program is shut down.
-
-
-Special callback function
-
-``` public void onSgfLoaded() ```
diff --git a/doc/theme.md b/doc/theme.md
deleted file mode 100644
index 20f006348..000000000
--- a/doc/theme.md
+++ /dev/null
@@ -1,19 +0,0 @@
-# Theme
-The theme's function is to make Lizzie's display richer, so simply change the assets file can not do things.
-
-The way to load a theme is to copy the theme's Jar file to the theme directory, and then set the theme's class name in the configuration file.
-
-Any class that implements the ITheme interface can be used as a theme class.
-
-Use ```javac -classpath ...``` to ensure that the theme's source files can be imported into the ITheme class.
-
-A theme class needs to implement the following methods:
-```java
-public Image getBlackStone(int[] position) throws IOException;
-
-public Image getWhiteStone(int[] position) throws IOException;
-
- public Image getBoard() throws IOException;
-
- public Image getBackground() throws IOException;
-```
diff --git a/pom.xml b/pom.xml
index be1ec6c0d..699e4d2f2 100644
--- a/pom.xml
+++ b/pom.xml
@@ -64,18 +64,6 @@
false
-
- com.coveo
- fmt-maven-plugin
- 2.5.1
-
-
-
- format
-
-
-
-
@@ -94,5 +82,12 @@
4.11
test
+
+
+
+ com.jhlabs
+ filters
+ 2.0.235
+
diff --git a/src/main/java/featurecat/lizzie/Config.java b/src/main/java/featurecat/lizzie/Config.java
index ee5c70b50..169880605 100644
--- a/src/main/java/featurecat/lizzie/Config.java
+++ b/src/main/java/featurecat/lizzie/Config.java
@@ -14,6 +14,8 @@ public class Config {
public boolean showMoveNumber = false;
public boolean showWinrate = true;
public boolean showVariationGraph = true;
+ public boolean showComment = false;
+ public int commentFontSize = 0;
public boolean showRawBoard = false;
public boolean showCaptured = true;
public boolean handicapInsteadOfWinrate = false;
@@ -130,6 +132,8 @@ public Config() throws IOException {
showBranch = uiConfig.getBoolean("show-leelaz-variation");
showWinrate = uiConfig.getBoolean("show-winrate");
showVariationGraph = uiConfig.getBoolean("show-variation-graph");
+ showComment = uiConfig.optBoolean("show-comment", false);
+ commentFontSize = uiConfig.optInt("comment-font-size", 0);
showCaptured = uiConfig.getBoolean("show-captured");
showBestMoves = uiConfig.getBoolean("show-best-moves");
showNextMoves = uiConfig.getBoolean("show-next-moves");
@@ -181,6 +185,10 @@ public void toggleShowVariationGraph() {
this.showVariationGraph = !this.showVariationGraph;
}
+ public void toggleShowComment() {
+ this.showComment = !this.showComment;
+ }
+
public void toggleShowBestMoves() {
this.showBestMoves = !this.showBestMoves;
}
@@ -249,7 +257,6 @@ private JSONObject createDefaultConfig() {
JSONObject ui = new JSONObject();
ui.put("board-color", new JSONArray("[217, 152, 77]"));
- ui.put("theme", "DefaultTheme");
ui.put("shadows-enabled", true);
ui.put("fancy-stones", true);
ui.put("fancy-board", true);
diff --git a/src/main/java/featurecat/lizzie/Lizzie.java b/src/main/java/featurecat/lizzie/Lizzie.java
index 08db4991f..15a3b6de2 100644
--- a/src/main/java/featurecat/lizzie/Lizzie.java
+++ b/src/main/java/featurecat/lizzie/Lizzie.java
@@ -2,11 +2,11 @@
import featurecat.lizzie.analysis.Leelaz;
import featurecat.lizzie.gui.LizzieFrame;
-import featurecat.lizzie.plugin.PluginManager;
import featurecat.lizzie.rules.Board;
import java.io.File;
import java.io.IOException;
import javax.swing.*;
+import org.json.JSONArray;
import org.json.JSONException;
/** Main class. */
@@ -16,40 +16,51 @@ public class Lizzie {
public static Board board;
public static Config config;
public static String lizzieVersion = "0.5";
+ private static String[] args;
/** Launches the game window, and runs the game. */
- public static void main(String[] args)
- throws IOException, JSONException, ClassNotFoundException, UnsupportedLookAndFeelException,
- InstantiationException, IllegalAccessException, InterruptedException {
- UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ public static void main(String[] args) throws IOException {
+ setLookAndFeel();
+ args = args;
config = new Config();
- PluginManager.loadPlugins();
board = new Board();
frame = new LizzieFrame();
+ new Thread(Lizzie::run).start();
+ }
- new Thread(
- () -> {
- try {
- leelaz = new Leelaz();
- if (config.handicapInsteadOfWinrate) {
- leelaz.estimatePassWinrate();
- }
- if (args.length == 1) {
- frame.loadFile(new File(args[0]));
- } else if (config.config.getJSONObject("ui").getBoolean("resume-previous-game")) {
- board.resumePreviousGame();
- }
- leelaz.togglePonder();
- } catch (IOException e) {
- e.printStackTrace();
- System.exit(-1);
- }
- })
- .start();
+ public static void run() {
+ try {
+ leelaz = new Leelaz();
+ if (config.handicapInsteadOfWinrate) {
+ leelaz.estimatePassWinrate();
+ }
+ if (args.length == 1) {
+ frame.loadFile(new File(args[0]));
+ } else if (config.config.getJSONObject("ui").getBoolean("resume-previous-game")) {
+ board.resumePreviousGame();
+ }
+ leelaz.togglePonder();
+ } catch (IOException e) {
+ e.printStackTrace();
+ System.exit(-1);
+ }
+ }
+
+ public static void setLookAndFeel() {
+ try {
+ UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
+ } catch (IllegalAccessException e) {
+ e.printStackTrace();
+ } catch (ClassNotFoundException e) {
+ e.printStackTrace();
+ } catch (InstantiationException e) {
+ e.printStackTrace();
+ } catch (UnsupportedLookAndFeelException e) {
+ e.printStackTrace();
+ }
}
public static void shutdown() {
- PluginManager.onShutdown();
if (board != null && config.config.getJSONObject("ui").getBoolean("confirm-exit")) {
int ret =
JOptionPane.showConfirmDialog(
@@ -64,11 +75,58 @@ public static void shutdown() {
try {
config.persist();
- } catch (IOException err) {
- // Failed to save config
+ } catch (IOException e) {
+ e.printStackTrace(); // Failed to save config
}
if (leelaz != null) leelaz.shutdown();
System.exit(0);
}
+
+ /**
+ * Switch the Engine by index number
+ *
+ * @param index engine index
+ */
+ public static void switchEngine(int index) {
+
+ String commandLine = null;
+ if (index == 0) {
+ commandLine = Lizzie.config.leelazConfig.getString("engine-command");
+ commandLine =
+ commandLine.replaceAll(
+ "%network-file", Lizzie.config.leelazConfig.getString("network-file"));
+ } else {
+ JSONArray commandList = Lizzie.config.leelazConfig.getJSONArray("engine-command-list");
+ if (commandList != null && commandList.length() >= index) {
+ commandLine = commandList.getString(index - 1);
+ } else {
+ index = -1;
+ }
+ }
+ if (index < 0
+ || commandLine == null
+ || commandLine.trim().isEmpty()
+ || index == Lizzie.leelaz.currentEngineN()) {
+ return;
+ }
+
+ // Workaround for leelaz cannot exit when restarting
+ if (leelaz.isThinking) {
+ if (Lizzie.frame.isPlayingAgainstLeelaz) {
+ Lizzie.frame.isPlayingAgainstLeelaz = false;
+ Lizzie.leelaz.togglePonder(); // we must toggle twice for it to restart pondering
+ Lizzie.leelaz.isThinking = false;
+ }
+ Lizzie.leelaz.togglePonder();
+ }
+
+ board.saveMoveNumber();
+ try {
+ leelaz.restartEngine(commandLine, index);
+ board.restoreMoveNumber();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
}
diff --git a/src/main/java/featurecat/lizzie/Util.java b/src/main/java/featurecat/lizzie/Util.java
index 432ebb1f5..94def40a9 100644
--- a/src/main/java/featurecat/lizzie/Util.java
+++ b/src/main/java/featurecat/lizzie/Util.java
@@ -1,5 +1,6 @@
package featurecat.lizzie;
+import java.awt.FontMetrics;
import java.io.*;
import java.net.URL;
import java.nio.channels.Channels;
@@ -74,4 +75,36 @@ public static void saveAsFile(URL url, File file) {
e.printStackTrace();
}
}
+
+ /**
+ * Truncate text that is too long for the given width
+ *
+ * @param line
+ * @param fm
+ * @param fitWidth
+ * @return fitted
+ */
+ public static String truncateStringByWidth(String line, FontMetrics fm, int fitWidth) {
+ if (line == null || line.length() == 0) {
+ return "";
+ }
+ int width = fm.stringWidth(line);
+ if (width > fitWidth) {
+ int guess = line.length() * fitWidth / width;
+ String before = line.substring(0, guess).trim();
+ width = fm.stringWidth(before);
+ if (width > fitWidth) {
+ int diff = width - fitWidth;
+ int i = 0;
+ for (; (diff > 0 && i < 5); i++) {
+ diff = diff - fm.stringWidth(line.substring(guess - i - 1, guess - i));
+ }
+ return line.substring(0, guess - i).trim();
+ } else {
+ return before;
+ }
+ } else {
+ return line;
+ }
+ }
}
diff --git a/src/main/java/featurecat/lizzie/analysis/Leelaz.java b/src/main/java/featurecat/lizzie/analysis/Leelaz.java
index 5c2774437..f9224919b 100644
--- a/src/main/java/featurecat/lizzie/analysis/Leelaz.java
+++ b/src/main/java/featurecat/lizzie/analysis/Leelaz.java
@@ -10,6 +10,11 @@
import java.net.URL;
import java.util.*;
import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.Executors;
+import java.util.concurrent.ScheduledExecutorService;
+import java.util.concurrent.TimeUnit;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
import javax.swing.*;
import org.json.JSONException;
import org.json.JSONObject;
@@ -55,6 +60,15 @@ public class Leelaz {
private boolean isLoaded = false;
private boolean isCheckingVersion;
+ // for Multiple Engine
+ private String engineCommand = null;
+ private List commands = null;
+ private JSONObject config = null;
+ private String currentWeight = null;
+ private boolean switching = false;
+ private int currentEngineN = -1;
+ private ScheduledExecutorService executor = null;
+
// dynamic komi and opponent komi as reported by dynamic-komi version of leelaz
private float dynamicKomi = Float.NaN, dynamicOppKomi = Float.NaN;
/**
@@ -73,7 +87,8 @@ public Leelaz() throws IOException, JSONException {
currentCmdNum = 0;
cmdQueue = new ArrayDeque<>();
- JSONObject config = Lizzie.config.config.getJSONObject("leelaz");
+ // Move config to member for other method call
+ config = Lizzie.config.config.getJSONObject("leelaz");
printCommunication = config.getBoolean("print-comms");
maxAnalyzeTimeMillis = MINUTE * config.getInt("max-analyze-time-minutes");
@@ -82,13 +97,44 @@ public Leelaz() throws IOException, JSONException {
updateToLatestNetwork();
}
+ // command string for starting the engine
+ engineCommand = config.getString("engine-command");
+ // substitute in the weights file
+ engineCommand = engineCommand.replaceAll("%network-file", config.getString("network-file"));
+
+ // Initialize current engine number and start engine
+ currentEngineN = 0;
+ startEngine(engineCommand);
+ Lizzie.frame.refreshBackground();
+ }
+
+ public void startEngine(String engineCommand) throws IOException {
+ // Check engine command
+ if (engineCommand == null || engineCommand.trim().isEmpty()) {
+ return;
+ }
+
File startfolder = new File(config.optString("engine-start-location", "."));
- String engineCommand = config.getString("engine-command");
String networkFile = config.getString("network-file");
// substitute in the weights file
engineCommand = engineCommand.replaceAll("%network-file", networkFile);
// create this as a list which gets passed into the processbuilder
- List commands = Arrays.asList(engineCommand.split(" "));
+ commands = Arrays.asList(engineCommand.split(" "));
+
+ // get weight name
+ if (engineCommand != null) {
+ Pattern wPattern = Pattern.compile("(?s).*?(--weights |-w )([^ ]+)(?s).*");
+ Matcher wMatcher = wPattern.matcher(engineCommand);
+ if (wMatcher.matches()) {
+ currentWeight = wMatcher.group(2);
+ if (currentWeight != null) {
+ String[] names = currentWeight.split("[\\\\|/]");
+ if (names != null && names.length > 1) {
+ currentWeight = names[names.length - 1];
+ }
+ }
+ }
+ }
// Check if engine is present
File lef = startfolder.toPath().resolve(new File(commands.get(0)).toPath()).toFile();
@@ -123,8 +169,42 @@ public Leelaz() throws IOException, JSONException {
sendCommand("version");
// start a thread to continuously read Leelaz output
- new Thread(this::read).start();
- Lizzie.frame.refreshBackground();
+ // new Thread(this::read).start();
+ // can stop engine for switching weights
+ executor = Executors.newSingleThreadScheduledExecutor();
+ executor.execute(this::read);
+ }
+
+ public void restartEngine(String engineCommand, int index) throws IOException {
+ if (engineCommand == null || engineCommand.trim().isEmpty()) {
+ return;
+ }
+ switching = true;
+ this.engineCommand = engineCommand;
+ // stop the ponder
+ if (Lizzie.leelaz.isPondering()) {
+ Lizzie.leelaz.togglePonder();
+ }
+ normalQuit();
+ startEngine(engineCommand);
+ currentEngineN = index;
+ togglePonder();
+ }
+
+ public void normalQuit() {
+ sendCommand("quit");
+ executor.shutdown();
+ try {
+ while (!executor.awaitTermination(1, TimeUnit.SECONDS)) {
+ executor.shutdownNow();
+ }
+ if (executor.awaitTermination(1, TimeUnit.SECONDS)) {
+ shutdown();
+ }
+ } catch (InterruptedException e) {
+ executor.shutdownNow();
+ Thread.currentThread().interrupt();
+ }
}
private void updateToLatestNetwork() {
@@ -201,6 +281,10 @@ private void parseLine(String line) {
// End of response
} else if (line.startsWith("info")) {
isLoaded = true;
+ // Clear switching prompt
+ switching = false;
+ // Display engine command in the title
+ if (Lizzie.frame != null) Lizzie.frame.updateTitle();
if (isResponseUpToDate()) {
// This should not be stale data when the command number match
parseInfo(line.substring(5));
@@ -301,7 +385,8 @@ private void read() {
System.out.println("Leelaz process ended.");
shutdown();
- System.exit(-1);
+ // Do no exit for switching weights
+ // System.exit(-1);
} catch (IOException e) {
e.printStackTrace();
System.exit(-1);
@@ -424,9 +509,8 @@ private void ponder() {
+ Lizzie.config
.config
.getJSONObject("leelaz")
- .getInt(
- "analyze-update-interval-centisec")); // until it responds to this, incoming
- // ponder results are obsolete
+ .getInt("analyze-update-interval-centisec")); // until it responds to this, incoming
+ // ponder results are obsolete
}
public void togglePonder() {
@@ -484,23 +568,12 @@ public WinrateStats getWinrateStats() {
final List moves = new ArrayList(bestMoves);
// get the total number of playouts in moves
- stats.totalPlayouts =
- moves
- .stream()
- .reduce(
- 0,
- (Integer result, MoveData move) -> result + move.playouts,
- (Integer a, Integer b) -> a + b);
+ int totalPlayouts = moves.stream().mapToInt(move -> move.playouts).sum();
+ stats.totalPlayouts = totalPlayouts;
// set maxWinrate to the weighted average winrate of moves
stats.maxWinrate =
- moves
- .stream()
- .reduce(
- 0d,
- (Double result, MoveData move) ->
- result + move.winrate * move.playouts / stats.totalPlayouts,
- (Double a, Double b) -> a + b);
+ moves.stream().mapToDouble(move -> move.winrate * move.playouts / totalPlayouts).sum();
}
return stats;
@@ -580,4 +653,20 @@ private synchronized void notifyBestMoveListeners() {
public boolean isLoaded() {
return isLoaded;
}
+
+ public String currentWeight() {
+ return currentWeight;
+ }
+
+ public boolean switching() {
+ return switching;
+ }
+
+ public int currentEngineN() {
+ return currentEngineN;
+ }
+
+ public String engineCommand() {
+ return this.engineCommand;
+ }
}
diff --git a/src/main/java/featurecat/lizzie/gui/BoardRenderer.java b/src/main/java/featurecat/lizzie/gui/BoardRenderer.java
index e3e7e18d4..a30f1cfcf 100644
--- a/src/main/java/featurecat/lizzie/gui/BoardRenderer.java
+++ b/src/main/java/featurecat/lizzie/gui/BoardRenderer.java
@@ -1,32 +1,38 @@
package featurecat.lizzie.gui;
+import static java.awt.RenderingHints.*;
+import static java.awt.image.BufferedImage.TYPE_INT_ARGB;
+import static java.lang.Math.log;
+import static java.lang.Math.max;
+import static java.lang.Math.min;
+import static java.lang.Math.round;
+
import featurecat.lizzie.Lizzie;
import featurecat.lizzie.analysis.Branch;
import featurecat.lizzie.analysis.MoveData;
-import featurecat.lizzie.plugin.PluginManager;
import featurecat.lizzie.rules.Board;
import featurecat.lizzie.rules.BoardData;
import featurecat.lizzie.rules.BoardHistoryNode;
import featurecat.lizzie.rules.SGFParser;
import featurecat.lizzie.rules.Stone;
import featurecat.lizzie.rules.Zobrist;
-import featurecat.lizzie.theme.DefaultTheme;
-import featurecat.lizzie.theme.ITheme;
import java.awt.*;
import java.awt.font.TextAttribute;
import java.awt.geom.Point2D;
import java.awt.image.BufferedImage;
+import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import javax.imageio.ImageIO;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
public class BoardRenderer {
- private static final double MARGIN =
- 0.03; // percentage of the boardLength to offset before drawing black lines
+ // Percentage of the boardLength to offset before drawing black lines
+ private static final double MARGIN = 0.03;
private static final double MARGIN_WITH_COORDINATES = 0.06;
private static final double STARPOINT_DIAMETER = 0.015;
@@ -43,6 +49,8 @@ public class BoardRenderer {
private int cachedX, cachedY;
private BufferedImage cachedStonesImage = null;
+ private BufferedImage cachedBoardImage = null;
+ private BufferedImage cachedWallpaperImage = null;
private BufferedImage cachedStonesShadowImage = null;
private Zobrist cachedZhash = new Zobrist(); // defaults to an empty board
@@ -54,7 +62,6 @@ public class BoardRenderer {
private boolean lastInScoreMode = false;
- public ITheme theme;
public List variation;
// special values of displayedBranchLength
@@ -70,10 +77,6 @@ public class BoardRenderer {
public BoardRenderer(boolean isMainBoard) {
uiConfig = Lizzie.config.config.getJSONObject("ui");
- theme = ITheme.loadTheme(uiConfig.getString("theme"));
- if (theme == null) {
- theme = new DefaultTheme();
- }
uiPersist = Lizzie.config.persisted.getJSONObject("ui-persist");
try {
maxAlpha = uiPersist.getInt("max-alpha");
@@ -89,7 +92,7 @@ public void draw(Graphics2D g) {
setupSizeParameters();
// Stopwatch timer = new Stopwatch();
- drawBackground(g);
+ drawGoban(g);
// timer.lap("background");
drawStones();
// timer.lap("stones");
@@ -121,7 +124,6 @@ public void draw(Graphics2D g) {
drawStoneMarkup(g);
}
- PluginManager.onDraw(g);
// timer.lap("leelaz");
// timer.print();
@@ -142,7 +144,7 @@ public String bestMoveCoordinateName() {
/** Calculate good values for boardLength, scaledMargin, availableLength, and squareLength */
private void setupSizeParameters() {
- int originalBoardLength = boardLength;
+ int boardLength0 = boardLength;
int[] calculatedPixelMargins = calculatePixelMargins();
boardLength = calculatedPixelMargins[0];
@@ -153,18 +155,20 @@ private void setupSizeParameters() {
stoneRadius = squareLength / 2 - 1;
// re-center board
- setLocation(
- x + (originalBoardLength - boardLength) / 2, y + (originalBoardLength - boardLength) / 2);
+ setLocation(x + (boardLength0 - boardLength) / 2, y + (boardLength0 - boardLength) / 2);
}
/**
* Draw the green background and go board with lines. We cache the image for a performance boost.
*/
- private void drawBackground(Graphics2D g0) {
+ private void drawGoban(Graphics2D g0) {
+ int width = Lizzie.frame.getWidth();
+ int height = Lizzie.frame.getHeight();
+
// draw the cached background image if frame size changes
if (cachedBackgroundImage == null
- || cachedBackgroundImage.getWidth() != Lizzie.frame.getWidth()
- || cachedBackgroundImage.getHeight() != Lizzie.frame.getHeight()
+ || cachedBackgroundImage.getWidth() != width
+ || cachedBackgroundImage.getHeight() != height
|| cachedX != x
|| cachedY != y
|| cachedBackgroundImageHasCoordinatesEnabled != showCoordinates()
@@ -172,9 +176,7 @@ private void drawBackground(Graphics2D g0) {
Lizzie.board.setForceRefresh(false);
- cachedBackgroundImage =
- new BufferedImage(
- Lizzie.frame.getWidth(), Lizzie.frame.getHeight(), BufferedImage.TYPE_INT_ARGB);
+ cachedBackgroundImage = new BufferedImage(width, height, TYPE_INT_ARGB);
Graphics2D g = cachedBackgroundImage.createGraphics();
// draw the wooden background
@@ -245,7 +247,7 @@ private void drawBackground(Graphics2D g0) {
g.dispose();
}
- g0.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
+ g0.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_OFF);
g0.drawImage(cachedBackgroundImage, 0, 0, null);
cachedX = x;
cachedY = y;
@@ -258,73 +260,31 @@ private void drawBackground(Graphics2D g0) {
*/
private void drawStarPoints(Graphics2D g) {
if (Board.BOARD_SIZE == 9) {
- drawStarPoints9x9(g);
+ drawStarPoints0(2, 2, 4, true, g);
} else if (Board.BOARD_SIZE == 13) {
- drawStarPoints13x13(g);
+ drawStarPoints0(2, 3, 6, true, g);
} else {
- drawStarPoints19x19(g);
+ drawStarPoints0(3, 3, 6, false, g);
}
}
- private void drawStarPoints19x19(Graphics2D g) {
- g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ private void drawStarPoints0(
+ int nStarpoints, int edgeOffset, int gridDistance, boolean center, Graphics2D g) {
+ g.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
int starPointRadius = (int) (STARPOINT_DIAMETER * boardLength) / 2;
- final int NUM_STARPOINTS = 3;
- final int STARPOINT_EDGE_OFFSET = 3;
- final int STARPOINT_GRID_DISTANCE = 6;
- for (int i = 0; i < NUM_STARPOINTS; i++) {
- for (int j = 0; j < NUM_STARPOINTS; j++) {
- int centerX =
- x + scaledMargin + squareLength * (STARPOINT_EDGE_OFFSET + STARPOINT_GRID_DISTANCE * i);
- int centerY =
- y + scaledMargin + squareLength * (STARPOINT_EDGE_OFFSET + STARPOINT_GRID_DISTANCE * j);
+ for (int i = 0; i < nStarpoints; i++) {
+ for (int j = 0; j < nStarpoints; j++) {
+ int centerX = x + scaledMargin + squareLength * (edgeOffset + gridDistance * i);
+ int centerY = y + scaledMargin + squareLength * (edgeOffset + gridDistance * j);
fillCircle(g, centerX, centerY, starPointRadius);
}
}
- }
- private void drawStarPoints13x13(Graphics2D g) {
- g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- int starPointRadius = (int) (STARPOINT_DIAMETER * boardLength) / 2;
- final int NUM_STARPOINTS = 2;
- final int STARPOINT_EDGE_OFFSET = 3;
- final int STARPOINT_GRID_DISTANCE = 6;
- for (int i = 0; i < NUM_STARPOINTS; i++) {
- for (int j = 0; j < NUM_STARPOINTS; j++) {
- int centerX =
- x + scaledMargin + squareLength * (STARPOINT_EDGE_OFFSET + STARPOINT_GRID_DISTANCE * i);
- int centerY =
- y + scaledMargin + squareLength * (STARPOINT_EDGE_OFFSET + STARPOINT_GRID_DISTANCE * j);
- fillCircle(g, centerX, centerY, starPointRadius);
- }
+ if (center) {
+ int centerX = x + scaledMargin + squareLength * gridDistance;
+ int centerY = y + scaledMargin + squareLength * gridDistance;
+ fillCircle(g, centerX, centerY, starPointRadius);
}
-
- // Draw center
- int centerX = x + scaledMargin + squareLength * STARPOINT_GRID_DISTANCE;
- int centerY = y + scaledMargin + squareLength * STARPOINT_GRID_DISTANCE;
- fillCircle(g, centerX, centerY, starPointRadius);
- }
-
- private void drawStarPoints9x9(Graphics2D g) {
- g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- int starPointRadius = (int) (STARPOINT_DIAMETER * boardLength) / 2;
- final int NUM_STARPOINTS = 2;
- final int STARPOINT_EDGE_OFFSET = 2;
- final int STARPOINT_GRID_DISTANCE = 4;
- for (int i = 0; i < NUM_STARPOINTS; i++) {
- for (int j = 0; j < NUM_STARPOINTS; j++) {
- int centerX =
- x + scaledMargin + squareLength * (STARPOINT_EDGE_OFFSET + STARPOINT_GRID_DISTANCE * i);
- int centerY =
- y + scaledMargin + squareLength * (STARPOINT_EDGE_OFFSET + STARPOINT_GRID_DISTANCE * j);
- fillCircle(g, centerX, centerY, starPointRadius);
- }
- }
-
- // Draw center
- int centerX = x + scaledMargin + squareLength * STARPOINT_GRID_DISTANCE;
- int centerY = y + scaledMargin + squareLength * STARPOINT_GRID_DISTANCE;
- fillCircle(g, centerX, centerY, starPointRadius);
}
/** Draw the stones. We cache the image for a performance boost. */
@@ -338,16 +298,15 @@ private void drawStones() {
|| Lizzie.board.inScoreMode()
|| lastInScoreMode) {
- cachedStonesImage = new BufferedImage(boardLength, boardLength, BufferedImage.TYPE_INT_ARGB);
- cachedStonesShadowImage =
- new BufferedImage(boardLength, boardLength, BufferedImage.TYPE_INT_ARGB);
+ cachedStonesImage = new BufferedImage(boardLength, boardLength, TYPE_INT_ARGB);
+ cachedStonesShadowImage = new BufferedImage(boardLength, boardLength, TYPE_INT_ARGB);
Graphics2D g = cachedStonesImage.createGraphics();
Graphics2D gShadow = cachedStonesShadowImage.createGraphics();
// we need antialiasing to make the stones pretty. Java is a bit slow at antialiasing; that's
// why we want the cache
- g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
- gShadow.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
+ gShadow.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
for (int i = 0; i < Board.BOARD_SIZE; i++) {
for (int j = 0; j < Board.BOARD_SIZE; j++) {
@@ -402,9 +361,8 @@ private void drawScore(Graphics2D go) {
/** Draw the 'ghost stones' which show a variation Leelaz is thinking about */
private void drawBranch() {
showingBranch = false;
- branchStonesImage = new BufferedImage(boardLength, boardLength, BufferedImage.TYPE_INT_ARGB);
- branchStonesShadowImage =
- new BufferedImage(boardLength, boardLength, BufferedImage.TYPE_INT_ARGB);
+ branchStonesImage = new BufferedImage(boardLength, boardLength, TYPE_INT_ARGB);
+ branchStonesShadowImage = new BufferedImage(boardLength, boardLength, TYPE_INT_ARGB);
branch = null;
if (Lizzie.frame.isPlayingAgainstLeelaz || Lizzie.leelaz == null) {
@@ -422,7 +380,7 @@ private void drawBranch() {
Graphics2D g = (Graphics2D) branchStonesImage.getGraphics();
Graphics2D gShadow = (Graphics2D) branchStonesShadowImage.getGraphics();
- MoveData suggestedMove = (isMainBoard ? mouseHoveredMove() : getBestMove());
+ MoveData suggestedMove = (isMainBoard ? mouseOveredMove() : getBestMove());
if (suggestedMove == null) return;
variation = suggestedMove.variation;
branch = new Branch(Lizzie.board, variation);
@@ -430,7 +388,7 @@ private void drawBranch() {
if (branch == null) return;
showingBranch = true;
- g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ g.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
for (int i = 0; i < Board.BOARD_SIZE; i++) {
for (int j = 0; j < Board.BOARD_SIZE; j++) {
@@ -449,8 +407,8 @@ private void drawBranch() {
gShadow.dispose();
}
- private MoveData mouseHoveredMove() {
- if (Lizzie.frame.mouseHoverCoordinate != null) {
+ private MoveData mouseOveredMove() {
+ if (Lizzie.frame.mouseOverCoordinate != null) {
for (int i = 0; i < bestMoves.size(); i++) {
MoveData move = bestMoves.get(i);
int[] coord = Board.convertNameToCoordinates(move.coordinate);
@@ -458,8 +416,7 @@ private MoveData mouseHoveredMove() {
continue;
}
- if (coord[0] == Lizzie.frame.mouseHoverCoordinate[0]
- && coord[1] == Lizzie.frame.mouseHoverCoordinate[1]) {
+ if (Lizzie.frame.isMouseOver(coord[0], coord[1])) {
return move;
}
}
@@ -473,7 +430,7 @@ private MoveData getBestMove() {
/** render the shadows and stones in correct background-foreground order */
private void renderImages(Graphics2D g) {
- g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
+ g.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_OFF);
g.drawImage(cachedStonesShadowImage, x, y, null);
if (Lizzie.config.showBranch) {
g.drawImage(branchStonesShadowImage, x, y, null);
@@ -486,9 +443,9 @@ private void renderImages(Graphics2D g) {
/** Draw move numbers and/or mark the last played move */
private void drawMoveNumbers(Graphics2D g) {
- g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
-
- int[] lastMove = branch == null ? Lizzie.board.getLastMove() : branch.data.lastMove;
+ g.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
+ Board board = Lizzie.board;
+ int[] lastMove = branch == null ? board.getLastMove() : branch.data.lastMove;
if (!Lizzie.config.showMoveNumber && branch == null) {
if (lastMove != null) {
// mark the last coordinate
@@ -497,27 +454,20 @@ private void drawMoveNumbers(Graphics2D g) {
int stoneY = y + scaledMargin + squareLength * lastMove[1];
// set color to the opposite color of whatever is on the board
- g.setColor(
- Lizzie.board.getStones()[Board.getIndex(lastMove[0], lastMove[1])].isWhite()
- ? Color.BLACK
- : Color.WHITE);
+ boolean isWhite = board.getStones()[Board.getIndex(lastMove[0], lastMove[1])].isWhite();
+ g.setColor(isWhite ? Color.BLACK : Color.WHITE);
+
drawCircle(g, stoneX, stoneY, lastMoveMarkerRadius);
- } else if (lastMove == null
- && Lizzie.board.getData().moveNumber != 0
- && !Lizzie.board.inScoreMode()) {
+ } else if (lastMove == null && board.getData().moveNumber != 0 && !board.inScoreMode()) {
g.setColor(
- Lizzie.board.getData().blackToPlay
- ? new Color(255, 255, 255, 150)
- : new Color(0, 0, 0, 150));
+ board.getData().blackToPlay ? new Color(255, 255, 255, 150) : new Color(0, 0, 0, 150));
g.fillOval(
x + boardLength / 2 - 4 * stoneRadius,
y + boardLength / 2 - 4 * stoneRadius,
stoneRadius * 8,
stoneRadius * 8);
g.setColor(
- Lizzie.board.getData().blackToPlay
- ? new Color(0, 0, 0, 255)
- : new Color(255, 255, 255, 255));
+ board.getData().blackToPlay ? new Color(0, 0, 0, 255) : new Color(255, 255, 255, 255));
drawString(
g,
x + boardLength / 2,
@@ -531,12 +481,10 @@ private void drawMoveNumbers(Graphics2D g) {
return;
}
- int[] moveNumberList =
- branch == null ? Lizzie.board.getMoveNumberList() : branch.data.moveNumberList;
+ int[] moveNumberList = branch == null ? board.getMoveNumberList() : branch.data.moveNumberList;
// Allow to display only last move number
- int lastMoveNumber =
- branch == null ? Lizzie.board.getData().moveNumber : branch.data.moveNumber;
+ int lastMoveNumber = branch == null ? board.getData().moveNumber : branch.data.moveNumber;
int onlyLastMoveNumber = Lizzie.config.uiConfig.optInt("only-last-move-number", 9999);
for (int i = 0; i < Board.BOARD_SIZE; i++) {
@@ -549,28 +497,21 @@ private void drawMoveNumbers(Graphics2D g) {
continue;
}
- Stone stoneAtThisPoint =
- branch == null
- ? Lizzie.board.getStones()[Board.getIndex(i, j)]
- : branch.data.stones[Board.getIndex(i, j)];
+ int here = Board.getIndex(i, j);
+ Stone stoneHere = branch == null ? board.getStones()[here] : branch.data.stones[here];
// don't write the move number if either: the move number is 0, or there will already be
// playout information written
if (moveNumberList[Board.getIndex(i, j)] > 0
- && !(branch != null
- && Lizzie.frame.mouseHoverCoordinate != null
- && i == Lizzie.frame.mouseHoverCoordinate[0]
- && j == Lizzie.frame.mouseHoverCoordinate[1])) {
+ && !(branch != null && Lizzie.frame.isMouseOver(i, j))) {
if (lastMove != null && i == lastMove[0] && j == lastMove[1])
- g.setColor(
- Color.RED
- .brighter()); // stoneAtThisPoint.isBlack() ? Color.RED.brighter() :
- // Color.BLUE.brighter());
+ g.setColor(Color.RED.brighter()); // stoneHere.isBlack() ? Color.RED.brighter() :
+ // Color.BLUE.brighter());
else {
// Draw white letters on black stones nomally.
// But use black letters for showing black moves without stones.
boolean reverse = (moveNumberList[Board.getIndex(i, j)] > maxBranchMoves());
- g.setColor(stoneAtThisPoint.isBlack() ^ reverse ? Color.WHITE : Color.BLACK);
+ g.setColor(stoneHere.isBlack() ^ reverse ? Color.WHITE : Color.BLACK);
}
String moveNumberString = moveNumberList[Board.getIndex(i, j)] + "";
@@ -593,11 +534,11 @@ private void drawMoveNumbers(Graphics2D g) {
private void drawLeelazSuggestions(Graphics2D g) {
if (Lizzie.leelaz == null) return;
- final int MIN_ALPHA = 32;
- final double HUE_SCALING_FACTOR = 3.0;
- final double ALPHA_SCALING_FACTOR = 5.0;
- final float GREEN_HUE = Color.RGBtoHSB(0, 1, 0, null)[0];
- final float CYAN_HUE = Color.RGBtoHSB(0, 1, 1, null)[0];
+ int minAlpha = 32;
+ float hueFactor = 3.0f;
+ float alphaFactor = 5.0f;
+ float greenHue = Color.RGBtoHSB(0, 1, 0, null)[0];
+ float cyanHue = Color.RGBtoHSB(0, 1, 1, null)[0];
if (!bestMoves.isEmpty()) {
@@ -633,44 +574,32 @@ private void drawLeelazSuggestions(Graphics2D g) {
if (move.playouts == 0) // this actually can happen
continue;
- double percentPlayouts = (double) move.playouts / maxPlayouts;
+ float percentPlayouts = (float) move.playouts / maxPlayouts;
int[] coordinates = Board.convertNameToCoordinates(move.coordinate);
int suggestionX = x + scaledMargin + squareLength * coordinates[0];
int suggestionY = y + scaledMargin + squareLength * coordinates[1];
// 0 = Reddest hue
- float hue =
- isBestMove
- ? CYAN_HUE
- : (float)
- (-GREEN_HUE
- * Math.max(0, Math.log(percentPlayouts) / HUE_SCALING_FACTOR + 1));
- float saturation = 0.75f; // saturation
- float brightness = 0.85f; // brightness
- int alpha =
- (int)
- (MIN_ALPHA
- + (maxAlpha - MIN_ALPHA)
- * Math.max(0, Math.log(percentPlayouts) / ALPHA_SCALING_FACTOR + 1));
- // if (uiConfig.getBoolean("shadows-enabled"))
- // alpha = 255;
+ float logPlayouts = (float) log(percentPlayouts);
+ float otherHue = -greenHue * max(0, logPlayouts / hueFactor + 1);
+ float hue = isBestMove ? cyanHue : otherHue;
+ float saturation = 0.75f;
+ float brightness = 0.85f;
+ float alpha = (minAlpha + (maxAlpha - minAlpha) * max(0, logPlayouts / alphaFactor + 1));
Color hsbColor = Color.getHSBColor(hue, saturation, brightness);
Color color =
- new Color(hsbColor.getRed(), hsbColor.getBlue(), hsbColor.getGreen(), alpha);
+ new Color(hsbColor.getRed(), hsbColor.getBlue(), hsbColor.getGreen(), (int) alpha);
+ boolean isMouseOver = Lizzie.frame.isMouseOver(coordinates[0], coordinates[1]);
if (branch == null) {
- drawShadow(g, suggestionX, suggestionY, true, (float) alpha / 255);
+ drawShadow(g, suggestionX, suggestionY, true, alpha / 255.0f);
g.setColor(color);
fillCircle(g, suggestionX, suggestionY, stoneRadius);
}
- if (branch == null
- || (isBestMove
- && Lizzie.frame.mouseHoverCoordinate != null
- && coordinates[0] == Lizzie.frame.mouseHoverCoordinate[0]
- && coordinates[1] == Lizzie.frame.mouseHoverCoordinate[1])) {
+ if (branch == null || isBestMove && isMouseOver) {
int strokeWidth = 1;
if (isBestMove != hasMaxWinrate) {
strokeWidth = 2;
@@ -683,13 +612,11 @@ private void drawLeelazSuggestions(Graphics2D g) {
g.setStroke(new BasicStroke(1));
}
- if ((branch == null
+ if (branch == null
&& (hasMaxWinrate
- || percentPlayouts >= uiConfig.getDouble("min-playout-ratio-for-stats")))
- || (Lizzie.frame.mouseHoverCoordinate != null
- && coordinates[0] == Lizzie.frame.mouseHoverCoordinate[0]
- && coordinates[1] == Lizzie.frame.mouseHoverCoordinate[1])) {
- double roundedWinrate = Math.round(move.winrate * 10) / 10.0;
+ || percentPlayouts >= uiConfig.getDouble("min-playout-ratio-for-stats"))
+ || isMouseOver) {
+ double roundedWinrate = round(move.winrate * 10) / 10.0;
if (uiConfig.getBoolean("win-rate-always-black")
&& !Lizzie.board.getData().blackToPlay) {
roundedWinrate = 100.0 - roundedWinrate;
@@ -714,6 +641,7 @@ private void drawLeelazSuggestions(Graphics2D g) {
stoneRadius,
stoneRadius * 1.5,
1);
+
drawString(
g,
suggestionX,
@@ -754,19 +682,25 @@ private void drawNextMoves(Graphics2D g) {
private void drawWoodenBoard(Graphics2D g) {
if (uiConfig.getBoolean("fancy-board")) {
- // fancy version
+ if (cachedBoardImage == null) {
+ try {
+ cachedBoardImage = ImageIO.read(getClass().getResourceAsStream("/assets/board.png"));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
int shadowRadius = (int) (boardLength * MARGIN / 6);
- BufferedImage boardImage = theme.getBoard();
- // Support seamless texture
drawTextureImage(
g,
- boardImage == null ? theme.getBoard() : boardImage,
+ cachedBoardImage,
x - 2 * shadowRadius,
y - 2 * shadowRadius,
boardLength + 4 * shadowRadius,
boardLength + 4 * shadowRadius);
g.setStroke(new BasicStroke(shadowRadius * 2));
+
// draw border
g.setColor(new Color(0, 0, 0, 50));
g.drawRect(
@@ -777,9 +711,8 @@ private void drawWoodenBoard(Graphics2D g) {
g.setStroke(new BasicStroke(1));
} else {
- // simple version
JSONArray boardColor = uiConfig.getJSONArray("board-color");
- g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF);
+ g.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_OFF);
g.setColor(new Color(boardColor.getInt(0), boardColor.getInt(1), boardColor.getInt(2)));
g.fillRect(x, y, boardLength, boardLength);
}
@@ -879,11 +812,10 @@ private void drawShadow(
/** Draws a stone centered at (centerX, centerY) */
private void drawStone(
Graphics2D g, Graphics2D gShadow, int centerX, int centerY, Stone color, int x, int y) {
- // g.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION,
- // RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY);
- g.setRenderingHint(
- RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR);
- g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
+ // g.setRenderingHint(KEY_ALPHA_INTERPOLATION,
+ // VALUE_ALPHA_INTERPOLATION_QUALITY);
+ g.setRenderingHint(KEY_INTERPOLATION, VALUE_INTERPOLATION_BILINEAR);
+ g.setRenderingHint(KEY_ANTIALIASING, VALUE_ANTIALIAS_ON);
// if no shadow graphics is supplied, just draw onto the same graphics
if (gShadow == null) gShadow = g;
@@ -893,11 +825,9 @@ private void drawStone(
boolean isGhost = (color == Stone.BLACK_GHOST || color == Stone.WHITE_GHOST);
if (uiConfig.getBoolean("fancy-stones")) {
drawShadow(gShadow, centerX, centerY, isGhost);
- Image stone =
- isBlack ? theme.getBlackStone(new int[] {x, y}) : theme.getWhiteStone(new int[] {x, y});
int size = stoneRadius * 2 + 1;
g.drawImage(
- getScaleStone(stone, isBlack, size, size),
+ getScaleStone(isBlack, size),
centerX - stoneRadius,
centerY - stoneRadius,
size,
@@ -905,13 +835,12 @@ private void drawStone(
null);
} else {
drawShadow(gShadow, centerX, centerY, true);
- g.setColor(
- isBlack
- ? (isGhost ? new Color(0, 0, 0) : Color.BLACK)
- : (isGhost ? new Color(255, 255, 255) : Color.WHITE));
+ Color blackColor = isGhost ? new Color(0, 0, 0) : Color.BLACK;
+ Color whiteColor = isGhost ? new Color(255, 255, 255) : Color.WHITE;
+ g.setColor(isBlack ? blackColor : whiteColor);
fillCircle(g, centerX, centerY, stoneRadius);
if (!isBlack) {
- g.setColor(isGhost ? new Color(0, 0, 0) : Color.BLACK);
+ g.setColor(blackColor);
drawCircle(g, centerX, centerY, stoneRadius);
}
}
@@ -919,12 +848,19 @@ private void drawStone(
}
/** Get scaled stone, if cached then return cached */
- public BufferedImage getScaleStone(Image img, boolean isBlack, int width, int height) {
+ private BufferedImage getScaleStone(boolean isBlack, int size) {
BufferedImage stone = isBlack ? cachedBlackStoneImage : cachedWhiteStoneImage;
- if (stone == null || stone.getWidth() != width || stone.getHeight() != height) {
- stone = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ if (stone == null) {
+ stone = new BufferedImage(size, size, TYPE_INT_ARGB);
+ String imgPath = isBlack ? "/assets/black0.png" : "/assets/white0.png";
+ Image img = null;
+ try {
+ img = ImageIO.read(getClass().getResourceAsStream(imgPath));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
Graphics2D g2 = stone.createGraphics();
- g2.drawImage(img.getScaledInstance(width, height, java.awt.Image.SCALE_SMOOTH), 0, 0, null);
+ g2.drawImage(img.getScaledInstance(size, size, java.awt.Image.SCALE_SMOOTH), 0, 0, null);
g2.dispose();
if (isBlack) {
cachedBlackStoneImage = stone;
@@ -935,6 +871,18 @@ public BufferedImage getScaleStone(Image img, boolean isBlack, int width, int he
return stone;
}
+ public BufferedImage getWallpaper() {
+ if (cachedWallpaperImage == null) {
+ try {
+ String wallpaperPath = "/assets/background.jpg";
+ cachedWallpaperImage = ImageIO.read(getClass().getResourceAsStream(wallpaperPath));
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return cachedWallpaperImage;
+ }
+
/**
* Draw scale smooth image, enhanced display quality (Not use, for future) This function use the
* traditional Image.getScaledInstance() method to provide the nice quality, but the performance
@@ -942,7 +890,7 @@ public BufferedImage getScaleStone(Image img, boolean isBlack, int width, int he
*/
// public void drawScaleSmoothImage(Graphics2D g, BufferedImage img, int x, int y, int width,
// int height, ImageObserver observer) {
- // BufferedImage newstone = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
+ // BufferedImage newstone = new BufferedImage(width, height, TYPE_INT_ARGB);
// Graphics2D g2 = newstone.createGraphics();
// g2.drawImage(img.getScaledInstance(width, height, java.awt.Image.SCALE_SMOOTH), 0, 0,
// observer);
@@ -973,10 +921,10 @@ public BufferedImage getScaleStone(Image img, boolean isBlack, int width, int he
// h = height;
// }
// }
- // BufferedImage tmp = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
+ // BufferedImage tmp = new BufferedImage(w, h, TYPE_INT_ARGB);
// Graphics2D g2 = tmp.createGraphics();
- // g2.setRenderingHint(RenderingHints.KEY_INTERPOLATION,
- // RenderingHints.VALUE_INTERPOLATION_BICUBIC);
+ // g2.setRenderingHint(KEY_INTERPOLATION,
+ // VALUE_INTERPOLATION_BICUBIC);
// g2.drawImage(newstone, 0, 0, w, h, null);
// g2.dispose();
// newstone = tmp;
@@ -1103,33 +1051,25 @@ private void drawString(
Font font = makeFont(fontBase, style);
// set maximum size of font
- font =
- font.deriveFont(
- (float)
- (font.getSize2D() * maximumFontWidth / g.getFontMetrics(font).stringWidth(string)));
- font = font.deriveFont(Math.min(maximumFontHeight, font.getSize()));
+ FontMetrics fm = g.getFontMetrics(font);
+ font = font.deriveFont((float) (font.getSize2D() * maximumFontWidth / fm.stringWidth(string)));
+ font = font.deriveFont(min(maximumFontHeight, font.getSize()));
g.setFont(font);
- FontMetrics metrics = g.getFontMetrics(font);
-
- int height = metrics.getAscent() - metrics.getDescent();
+ int height = fm.getAscent() - fm.getDescent();
int verticalOffset;
- switch (aboveOrBelow) {
- case -1:
- verticalOffset = height / 2;
- break;
-
- case 1:
- verticalOffset = -height / 2;
- break;
-
- default:
- verticalOffset = 0;
+ if (aboveOrBelow == -1) {
+ verticalOffset = height / 2;
+ } else if (aboveOrBelow == 1) {
+ verticalOffset = -height / 2;
+ } else {
+ verticalOffset = 0;
}
+
// bounding box for debugging
// g.drawRect(x-(int)maximumFontWidth/2, y - height/2 + verticalOffset, (int)maximumFontWidth,
// height+verticalOffset );
- g.drawString(string, x - metrics.stringWidth(string) / 2, y + height / 2 + verticalOffset);
+ g.drawString(string, x - fm.stringWidth(string) / 2, y + height / 2 + verticalOffset);
}
private void drawString(
@@ -1158,13 +1098,13 @@ private Font makeFont(Font fontBase, int style) {
private String getPlayoutsString(int playouts) {
if (playouts >= 1_000_000) {
double playoutsDouble = (double) playouts / 100_000; // 1234567 -> 12.34567
- return Math.round(playoutsDouble) / 10.0 + "m";
+ return round(playoutsDouble) / 10.0 + "m";
} else if (playouts >= 10_000) {
double playoutsDouble = (double) playouts / 1_000; // 13265 -> 13.265
- return Math.round(playoutsDouble) + "k";
+ return round(playoutsDouble) + "k";
} else if (playouts >= 1_000) {
double playoutsDouble = (double) playouts / 100; // 1265 -> 12.65
- return Math.round(playoutsDouble) / 10.0 + "k";
+ return round(playoutsDouble) / 10.0 + "k";
} else {
return String.valueOf(playouts);
}
@@ -1274,13 +1214,13 @@ public boolean incrementDisplayedBranchLength(int n) {
return false;
default:
// force nonnegative
- displayedBranchLength = Math.max(0, displayedBranchLength + n);
+ displayedBranchLength = max(0, displayedBranchLength + n);
return true;
}
}
public boolean isInside(int x1, int y1) {
- return (x <= x1 && x1 < x + boardLength && y <= y1 && y1 < y + boardLength);
+ return x <= x1 && x1 < x + boardLength && y <= y1 && y1 < y + boardLength;
}
private boolean showCoordinates() {
@@ -1288,7 +1228,7 @@ private boolean showCoordinates() {
}
public void increaseMaxAlpha(int k) {
- maxAlpha = Math.min(maxAlpha + k, 255);
+ maxAlpha = min(maxAlpha + k, 255);
uiPersist.put("max-alpha", maxAlpha);
}
}
diff --git a/src/main/java/featurecat/lizzie/gui/Input.java b/src/main/java/featurecat/lizzie/gui/Input.java
index e576bb8bb..fc36096ad 100644
--- a/src/main/java/featurecat/lizzie/gui/Input.java
+++ b/src/main/java/featurecat/lizzie/gui/Input.java
@@ -3,7 +3,6 @@
import static java.awt.event.KeyEvent.*;
import featurecat.lizzie.Lizzie;
-import featurecat.lizzie.plugin.PluginManager;
import java.awt.event.*;
import javax.swing.*;
@@ -13,20 +12,14 @@ public void mouseClicked(MouseEvent e) {}
@Override
public void mousePressed(MouseEvent e) {
- PluginManager.onMousePressed(e);
- int x = e.getX();
- int y = e.getY();
-
- if (e.getButton() == MouseEvent.BUTTON1) // left mouse click
- Lizzie.frame.onClicked(x, y);
- else if (e.getButton() == MouseEvent.BUTTON3) // right mouse click
+ if (e.getButton() == MouseEvent.BUTTON1) // left click
+ Lizzie.frame.onClicked(e.getX(), e.getY());
+ else if (e.getButton() == MouseEvent.BUTTON3) // right click
undo();
}
@Override
- public void mouseReleased(MouseEvent e) {
- PluginManager.onMouseReleased(e);
- }
+ public void mouseReleased(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {}
@@ -36,19 +29,12 @@ public void mouseExited(MouseEvent e) {}
@Override
public void mouseDragged(MouseEvent e) {
- int x = e.getX();
- int y = e.getY();
-
- Lizzie.frame.onMouseDragged(x, y);
+ Lizzie.frame.onMouseDragged(e.getX(), e.getY());
}
@Override
public void mouseMoved(MouseEvent e) {
- PluginManager.onMouseMoved(e);
- int x = e.getX();
- int y = e.getY();
-
- Lizzie.frame.onMouseMoved(x, y);
+ Lizzie.frame.onMouseMoved(e.getX(), e.getY());
}
@Override
@@ -155,9 +141,6 @@ private void toggleShowDynamicKomi() {
@Override
public void keyPressed(KeyEvent e) {
-
- PluginManager.onKeyPressed(e);
-
// If any controls key is pressed, let's disable analysis mode.
// This is probably the user attempting to exit analysis mode.
boolean shouldDisableAnalysis = true;
@@ -300,6 +283,10 @@ public void keyPressed(KeyEvent e) {
Lizzie.config.toggleShowVariationGraph();
break;
+ case VK_T:
+ Lizzie.config.toggleShowComment();
+ break;
+
case VK_C:
if (controlIsPressed(e)) {
Lizzie.frame.copySgf();
@@ -355,6 +342,21 @@ public void keyPressed(KeyEvent e) {
toggleShowDynamicKomi();
break;
+ // Use Ctrl+Num to switching multiple engine
+ case VK_0:
+ case VK_1:
+ case VK_2:
+ case VK_3:
+ case VK_4:
+ case VK_5:
+ case VK_6:
+ case VK_7:
+ case VK_8:
+ case VK_9:
+ if (controlIsPressed(e)) {
+ Lizzie.switchEngine(e.getKeyCode() - VK_0);
+ }
+ break;
default:
shouldDisableAnalysis = false;
}
@@ -368,7 +370,6 @@ public void keyPressed(KeyEvent e) {
@Override
public void keyReleased(KeyEvent e) {
- PluginManager.onKeyReleased(e);
switch (e.getKeyCode()) {
case VK_X:
if (wasPonderingWhenControlsShown) Lizzie.leelaz.togglePonder();
@@ -387,6 +388,9 @@ public void keyReleased(KeyEvent e) {
@Override
public void mouseWheelMoved(MouseWheelEvent e) {
+ if (Lizzie.frame.processCommentMouseWheelMoved(e)) {
+ return;
+ }
if (Lizzie.board.inAnalysisMode()) Lizzie.board.toggleAnalysis();
if (e.getWheelRotation() > 0) {
redo();
diff --git a/src/main/java/featurecat/lizzie/gui/LizzieFrame.java b/src/main/java/featurecat/lizzie/gui/LizzieFrame.java
index e2f44b8fe..1893a6414 100644
--- a/src/main/java/featurecat/lizzie/gui/LizzieFrame.java
+++ b/src/main/java/featurecat/lizzie/gui/LizzieFrame.java
@@ -23,6 +23,7 @@
import java.awt.datatransfer.DataFlavor;
import java.awt.datatransfer.StringSelection;
import java.awt.datatransfer.Transferable;
+import java.awt.event.MouseWheelEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
@@ -64,6 +65,7 @@ public class LizzieFrame extends JFrame {
resourceBundle.getString("LizzieFrame.commands.keyV"),
resourceBundle.getString("LizzieFrame.commands.keyW"),
resourceBundle.getString("LizzieFrame.commands.keyG"),
+ resourceBundle.getString("LizzieFrame.commands.keyT"),
resourceBundle.getString("LizzieFrame.commands.keyHome"),
resourceBundle.getString("LizzieFrame.commands.keyEnd"),
resourceBundle.getString("LizzieFrame.commands.keyControl"),
@@ -79,7 +81,7 @@ public class LizzieFrame extends JFrame {
private final BufferStrategy bs;
- public int[] mouseHoverCoordinate;
+ public int[] mouseOverCoordinate;
public boolean showControls = false;
public boolean showCoordinates = false;
public boolean isPlayingAgainstLeelaz = false;
@@ -91,6 +93,16 @@ public class LizzieFrame extends JFrame {
private long lastAutosaveTime = System.currentTimeMillis();
+ // Save the player title
+ private String playerTitle = null;
+
+ // Display Comment
+ private JScrollPane scrollPane = null;
+ private JTextPane commentPane = null;
+ private BufferedImage commentImage = null;
+ private String cachedComment = null;
+ private Rectangle commentRect = null;
+
static {
// load fonts
try {
@@ -129,6 +141,18 @@ public LizzieFrame() {
setExtendedState(Frame.MAXIMIZED_BOTH); // start maximized
}
+ // Comment Pane
+ commentPane = new JTextPane();
+ commentPane.setEditable(false);
+ commentPane.setMargin(new Insets(5, 5, 5, 5));
+ commentPane.setBackground(new Color(0, 0, 0, 200));
+ commentPane.setForeground(Color.WHITE);
+ scrollPane = new JScrollPane();
+ scrollPane.setViewportView(commentPane);
+ scrollPane.setBorder(null);
+ scrollPane.setVerticalScrollBarPolicy(
+ javax.swing.ScrollPaneConstants.VERTICAL_SCROLLBAR_AS_NEEDED);
+
setVisible(true);
createBufferStrategy(2);
@@ -415,26 +439,19 @@ public void paint(Graphics g0) {
if (Lizzie.leelaz != null && Lizzie.leelaz.isLoaded()) {
if (Lizzie.config.showStatus) {
- drawPonderingState(
- g,
- resourceBundle.getString("LizzieFrame.display.pondering")
- + (Lizzie.leelaz.isPondering()
- ? resourceBundle.getString("LizzieFrame.display.on")
- : resourceBundle.getString("LizzieFrame.display.off")),
- ponderingX,
- ponderingY,
- ponderingSize);
+ String pondKey = "LizzieFrame.display." + (Lizzie.leelaz.isPondering() ? "on" : "off");
+ String pondText = resourceBundle.getString(pondKey);
+ String switchText = resourceBundle.getString("LizzieFrame.prompt.switching");
+ String weightText = Lizzie.leelaz.currentWeight().toString();
+ String text = pondText + " " + weightText + (Lizzie.leelaz.switching() ? switchText : "");
+ drawPonderingState(g, text, ponderingX, ponderingY, ponderingSize);
}
- if (Lizzie.config.showDynamicKomi && Lizzie.leelaz.getDynamicKomi() != null) {
- drawPonderingState(
- g,
- resourceBundle.getString("LizzieFrame.display.dynamic-komi"),
- dynamicKomiLabelX,
- dynamicKomiLabelY,
- dynamicKomiSize);
- drawPonderingState(
- g, Lizzie.leelaz.getDynamicKomi(), dynamicKomiX, dynamicKomiY, dynamicKomiSize);
+ String dynamicKomi = Lizzie.leelaz.getDynamicKomi();
+ if (Lizzie.config.showDynamicKomi && dynamicKomi != null) {
+ String text = resourceBundle.getString("LizzieFrame.display.dynamic-komi");
+ drawPonderingState(g, text, dynamicKomiLabelX, dynamicKomiLabelY, dynamicKomiSize);
+ drawPonderingState(g, dynamicKomi, dynamicKomiX, dynamicKomiY, dynamicKomiSize);
}
// Todo: Make board move over when there is no space beside the board
@@ -446,8 +463,19 @@ public void paint(Graphics g0) {
if (Lizzie.config.showVariationGraph) {
drawVariationTreeContainer(backgroundG, vx, vy, vw, vh);
- variationTree.draw(g, treex, treey, treew, treeh);
+ int cHeight = 0;
+ if (Lizzie.config.showComment) {
+ // Draw the Comment of the Sgf
+ cHeight = drawComment(g, vx, vy, vw, vh, false);
+ }
+ variationTree.draw(g, treex, treey, treew, treeh - cHeight);
+ } else {
+ if (Lizzie.config.showComment) {
+ // Draw the Comment of the Sgf
+ drawComment(g, vx, topInset, vw, vh - topInset + vy, true);
+ }
}
+
if (Lizzie.config.showSubBoard) {
try {
subBoardRenderer.setLocation(subBoardX, subBoardY);
@@ -458,12 +486,8 @@ public void paint(Graphics g0) {
}
}
} else if (Lizzie.config.showStatus) {
- drawPonderingState(
- g,
- resourceBundle.getString("LizzieFrame.display.loading"),
- loadingX,
- loadingY,
- loadingSize);
+ String loadingText = resourceBundle.getString("LizzieFrame.display.loading");
+ drawPonderingState(g, loadingText, loadingX, loadingY, loadingSize);
}
if (Lizzie.config.showCaptured) drawCaptured(g, capx, capy, capw, caph);
@@ -503,11 +527,11 @@ private Graphics2D createBackground() {
Graphics2D g = cachedBackground.createGraphics();
- BufferedImage background = boardRenderer.theme.getBackground();
- int drawWidth = Math.max(background.getWidth(), getWidth());
- int drawHeight = Math.max(background.getHeight(), getHeight());
+ BufferedImage wallpaper = boardRenderer.getWallpaper();
+ int drawWidth = Math.max(wallpaper.getWidth(), getWidth());
+ int drawHeight = Math.max(wallpaper.getHeight(), getHeight());
// Support seamless texture
- boardRenderer.drawTextureImage(g, background, 0, 0, drawWidth, drawHeight);
+ boardRenderer.drawTextureImage(g, wallpaper, 0, 0, drawWidth, drawHeight);
return g;
}
@@ -523,11 +547,21 @@ private void drawVariationTreeContainer(Graphics2D g, int vx, int vy, int vw, in
}
private void drawPonderingState(Graphics2D g, String text, int x, int y, double size) {
- Font font =
- new Font(
- systemDefaultFontName, Font.PLAIN, (int) (Math.max(getWidth(), getHeight()) * size));
+ int fontSize = (int) (Math.max(getWidth(), getHeight()) * size);
+ Font font = new Font(systemDefaultFontName, Font.PLAIN, fontSize);
FontMetrics fm = g.getFontMetrics(font);
int stringWidth = fm.stringWidth(text);
+ // Truncate too long text when display switching prompt
+ if (Lizzie.leelaz.isLoaded()) {
+ int mainBoardX =
+ (boardRenderer != null && boardRenderer.getLocation() != null)
+ ? boardRenderer.getLocation().x
+ : 0;
+ if ((mainBoardX > x) && stringWidth > (mainBoardX - x)) {
+ text = Util.truncateStringByWidth(text, fm, mainBoardX - x);
+ stringWidth = fm.stringWidth(text);
+ }
+ }
int stringHeight = fm.getAscent() - fm.getDescent();
int width = stringWidth;
int height = (int) (stringHeight * 1.2);
@@ -580,17 +614,12 @@ void drawControls() {
int maxSize = Math.min(getWidth(), getHeight());
Font font = new Font(systemDefaultFontName, Font.PLAIN, (int) (maxSize * 0.034));
g.setFont(font);
+
FontMetrics metrics = g.getFontMetrics(font);
- int maxCommandWidth =
- commandsToShow
- .stream()
- .reduce(
- 0,
- (Integer i, String command) -> Math.max(i, metrics.stringWidth(command)),
- (Integer a, Integer b) -> Math.max(a, b));
+ int maxCmdWidth = commandsToShow.stream().mapToInt(c -> metrics.stringWidth(c)).max().orElse(0);
int lineHeight = (int) (font.getSize() * 1.15);
- int boxWidth = Util.clamp((int) (maxCommandWidth * 1.4), 0, getWidth());
+ int boxWidth = Util.clamp((int) (maxCmdWidth * 1.4), 0, getWidth());
int boxHeight = Util.clamp(commandsToShow.size() * lineHeight, 0, getHeight());
int commandsX = Util.clamp(getWidth() / 2 - boxWidth / 2, 0, getWidth());
@@ -733,11 +762,9 @@ private void drawMoveStatistics(Graphics2D g, int posX, int posY, int width, int
if (validLastWinrate && validWinrate) {
String text;
if (Lizzie.config.handicapInsteadOfWinrate) {
- text =
- String.format(
- ": %.2f",
- Lizzie.leelaz.winrateToHandicap(100 - curWR)
- - Lizzie.leelaz.winrateToHandicap(lastWR));
+ double currHandicapedWR = Lizzie.leelaz.winrateToHandicap(100 - curWR);
+ double lastHandicapedWR = Lizzie.leelaz.winrateToHandicap(lastWR);
+ text = String.format(": %.2f", currHandicapedWR - lastHandicapedWR);
} else {
text = String.format(": %.1f%%", 100 - lastWR - curWR);
}
@@ -918,16 +945,17 @@ public void playBestMove() {
}
public void onMouseMoved(int x, int y) {
- int[] newMouseHoverCoordinate = boardRenderer.convertScreenToCoordinates(x, y);
- if (mouseHoverCoordinate != null
- && newMouseHoverCoordinate != null
- && (mouseHoverCoordinate[0] != newMouseHoverCoordinate[0]
- || mouseHoverCoordinate[1] != newMouseHoverCoordinate[1])) {
- mouseHoverCoordinate = newMouseHoverCoordinate;
+ int[] c = boardRenderer.convertScreenToCoordinates(x, y);
+ if (c != null && !isMouseOver(c[0], c[1])) {
repaint();
- } else {
- mouseHoverCoordinate = newMouseHoverCoordinate;
}
+ mouseOverCoordinate = c;
+ }
+
+ public boolean isMouseOver(int x, int y) {
+ return mouseOverCoordinate != null
+ && mouseOverCoordinate[0] == x
+ && mouseOverCoordinate[1] == x;
}
public void onMouseDragged(int x, int y) {
@@ -939,6 +967,58 @@ public void onMouseDragged(int x, int y) {
}
}
+ /**
+ * Process Comment Mouse Wheel Moved
+ *
+ * @return true when the scroll event was processed by this method
+ */
+ public boolean processCommentMouseWheelMoved(MouseWheelEvent e) {
+ if (Lizzie.config.showComment
+ && commentRect != null
+ && commentRect.contains(e.getX(), e.getY())) {
+ scrollPane.dispatchEvent(e);
+ createCommentImage(true, 0, 0);
+ getGraphics()
+ .drawImage(
+ commentImage,
+ commentRect.x,
+ commentRect.y,
+ commentRect.width,
+ commentRect.height,
+ null);
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Create comment cached image
+ *
+ * @param forceRefresh
+ * @param w
+ * @param h
+ */
+ public void createCommentImage(boolean forceRefresh, int w, int h) {
+ if (forceRefresh
+ || commentImage == null
+ || scrollPane.getWidth() != w
+ || scrollPane.getHeight() != h) {
+ if (w > 0 && h > 0) {
+ scrollPane.setSize(w, h);
+ }
+ commentImage =
+ new BufferedImage(
+ scrollPane.getWidth(), scrollPane.getHeight(), BufferedImage.TYPE_INT_ARGB);
+ Graphics2D g2 = commentImage.createGraphics();
+ scrollPane.doLayout();
+ scrollPane.addNotify();
+ scrollPane.validate();
+ scrollPane.printAll(g2);
+ g2.dispose();
+ }
+ }
+
private void autosaveMaybe() {
int interval =
Lizzie.config.config.getJSONObject("ui").getInt("autosave-interval-seconds") * 1000;
@@ -954,7 +1034,16 @@ public void toggleCoordinates() {
}
public void setPlayers(String whitePlayer, String blackPlayer) {
- setTitle(String.format("%s (%s [W] vs %s [B])", DEFAULT_TITLE, whitePlayer, blackPlayer));
+ this.playerTitle = String.format("(%s [W] vs %s [B])", whitePlayer, blackPlayer);
+ this.updateTitle();
+ }
+
+ public void updateTitle() {
+ StringBuilder sb = new StringBuilder(DEFAULT_TITLE);
+ sb.append(this.playerTitle != null ? " " + this.playerTitle.trim() : "");
+ sb.append(
+ Lizzie.leelaz.engineCommand() != null ? " [" + Lizzie.leelaz.engineCommand() + "]" : "");
+ setTitle(sb.toString());
}
private void setDisplayedBranchLength(int n) {
@@ -976,7 +1065,8 @@ public boolean incrementDisplayedBranchLength(int n) {
}
public void resetTitle() {
- setTitle(DEFAULT_TITLE);
+ this.playerTitle = null;
+ this.updateTitle();
}
public void copySgf() {
@@ -1017,4 +1107,41 @@ public void pasteSgf() {
public void increaseMaxAlpha(int k) {
boardRenderer.increaseMaxAlpha(k);
}
+
+ /**
+ * Draw the Comment of the Sgf file
+ *
+ * @param g
+ * @param x
+ * @param y
+ * @param w
+ * @param h
+ * @param full
+ * @return
+ */
+ private int drawComment(Graphics2D g, int x, int y, int w, int h, boolean full) {
+ String comment =
+ (Lizzie.board.getHistory().getData() != null
+ && Lizzie.board.getHistory().getData().comment != null)
+ ? Lizzie.board.getHistory().getData().comment
+ : "";
+ int cHeight = full ? h : (int) (h * 0.5);
+ int fontSize = (int) (Math.min(getWidth(), getHeight()) * 0.0294);
+ if (Lizzie.config.commentFontSize > 0) {
+ fontSize = Lizzie.config.commentFontSize;
+ } else if (fontSize < 16) {
+ fontSize = 16;
+ }
+ Font font = new Font(systemDefaultFontName, Font.PLAIN, fontSize);
+ commentPane.setFont(font);
+ commentPane.setText(comment);
+ commentPane.setSize(w, cHeight);
+ createCommentImage(comment != null && !comment.equals(this.cachedComment), w, cHeight);
+ commentRect =
+ new Rectangle(x, y + (h - cHeight), scrollPane.getWidth(), scrollPane.getHeight());
+ g.drawImage(
+ commentImage, commentRect.x, commentRect.y, commentRect.width, commentRect.height, null);
+ cachedComment = comment;
+ return cHeight;
+ }
}
diff --git a/src/main/java/featurecat/lizzie/gui/NewGameDialog.java b/src/main/java/featurecat/lizzie/gui/NewGameDialog.java
index 585676d10..668c2a6ed 100644
--- a/src/main/java/featurecat/lizzie/gui/NewGameDialog.java
+++ b/src/main/java/featurecat/lizzie/gui/NewGameDialog.java
@@ -124,20 +124,11 @@ private void initButtonBar() {
okButton.setText("OK");
okButton.addActionListener(e -> apply());
+ int center = GridBagConstraints.CENTER;
+ int both = GridBagConstraints.BOTH;
buttonBar.add(
okButton,
- new GridBagConstraints(
- 1,
- 0,
- 1,
- 1,
- 0.0,
- 0.0,
- GridBagConstraints.CENTER,
- GridBagConstraints.BOTH,
- new Insets(0, 0, 0, 0),
- 0,
- 0));
+ new GridBagConstraints(1, 0, 1, 1, 0.0, 0.0, center, both, new Insets(0, 0, 0, 0), 0, 0));
dialogPane.add(buttonBar, BorderLayout.SOUTH);
}
diff --git a/src/main/java/featurecat/lizzie/gui/VariationTree.java b/src/main/java/featurecat/lizzie/gui/VariationTree.java
index 72f072bf0..aef810e2b 100644
--- a/src/main/java/featurecat/lizzie/gui/VariationTree.java
+++ b/src/main/java/featurecat/lizzie/gui/VariationTree.java
@@ -126,7 +126,9 @@ public void drawTree(
}
public void draw(Graphics2D g, int posx, int posy, int width, int height) {
- if (width <= 0 || height <= 0) return; // we don't have enough space
+ if (width <= 0 || height <= 0) {
+ return; // we don't have enough space
+ }
// Use dense tree for saving space if large-subboard
YSPACING = (Lizzie.config.showLargeSubBoard() ? 20 : 30);
diff --git a/src/main/java/featurecat/lizzie/gui/WinrateGraph.java b/src/main/java/featurecat/lizzie/gui/WinrateGraph.java
index 1b0de91c6..3b4915c96 100644
--- a/src/main/java/featurecat/lizzie/gui/WinrateGraph.java
+++ b/src/main/java/featurecat/lizzie/gui/WinrateGraph.java
@@ -114,21 +114,19 @@ public void draw(Graphics2D g, int posx, int posy, int width, int height) {
wr = bwr;
playouts = stats.totalPlayouts;
}
- {
- // Draw a vertical line at the current move
- Stroke previousStroke = g.getStroke();
- int x = posx + (movenum * width / numMoves);
- g.setStroke(dashed);
- g.setColor(Color.white);
- g.drawLine(x, posy, x, posy + height);
- // Show move number
- String moveNumString = "" + node.getData().moveNumber;
- int mw = g.getFontMetrics().stringWidth(moveNumString);
- int margin = strokeRadius;
- int mx = x - posx < width / 2 ? x + margin : x - mw - margin;
- g.drawString(moveNumString, mx, posy + height - margin);
- g.setStroke(previousStroke);
- }
+ // Draw a vertical line at the current move
+ Stroke previousStroke = g.getStroke();
+ int x = posx + (movenum * width / numMoves);
+ g.setStroke(dashed);
+ g.setColor(Color.white);
+ g.drawLine(x, posy, x, posy + height);
+ // Show move number
+ String moveNumString = "" + node.getData().moveNumber;
+ int mw = g.getFontMetrics().stringWidth(moveNumString);
+ int margin = strokeRadius;
+ int mx = x - posx < width / 2 ? x + margin : x - mw - margin;
+ g.drawString(moveNumString, mx, posy + height - margin);
+ g.setStroke(previousStroke);
}
if (playouts > 0) {
if (wr < 0) {
diff --git a/src/main/java/featurecat/lizzie/rules/Board.java b/src/main/java/featurecat/lizzie/rules/Board.java
index 2e4783a70..2a8d38897 100644
--- a/src/main/java/featurecat/lizzie/rules/Board.java
+++ b/src/main/java/featurecat/lizzie/rules/Board.java
@@ -25,6 +25,9 @@ public class Board implements LeelazListener {
private boolean analysisMode = false;
private int playoutsAnalysis = 100;
+ // Save the node for restore move when in the branch
+ private BoardHistoryNode saveNode = null;
+
// Force refresh board
private boolean forceRefresh = false;
@@ -35,28 +38,17 @@ public Board() {
/** Initialize the board completely */
private void initialize() {
Stone[] stones = new Stone[BOARD_SIZE * BOARD_SIZE];
- for (int i = 0; i < stones.length; i++) stones[i] = Stone.EMPTY;
-
- boolean blackToPlay = true;
- int[] lastMove = null;
+ for (int i = 0; i < stones.length; i++) {
+ stones[i] = Stone.EMPTY;
+ }
capturedStones = null;
scoreMode = false;
- history =
- new BoardHistoryList(
- new BoardData(
- stones,
- lastMove,
- Stone.EMPTY,
- blackToPlay,
- new Zobrist(),
- 0,
- new int[BOARD_SIZE * BOARD_SIZE],
- 0,
- 0,
- 50,
- 0));
+ int[] boardArray = new int[BOARD_SIZE * BOARD_SIZE];
+ BoardData boardData =
+ new BoardData(stones, null, Stone.EMPTY, true, new Zobrist(), 0, boardArray, 0, 0, 50, 0);
+ history = new BoardHistoryList(boardData);
}
/**
@@ -627,6 +619,82 @@ public boolean nextMove() {
}
}
+ /**
+ * Goes to the next coordinate, thread safe
+ *
+ * @param fromBackChildren by back children branch
+ * @return true when has next variation
+ */
+ 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;
+ }
+ return nextVariation(fromBackChildren);
+ }
+ }
+
+ /** Save the move number for restore If in the branch, save the back routing from children */
+ public void saveMoveNumber() {
+ BoardHistoryNode curNode = history.getCurrentHistoryNode();
+ int curMoveNum = curNode.getData().moveNumber;
+ if (curMoveNum > 0) {
+ if (!BoardHistoryList.isMainTrunk(curNode)) {
+ // If in branch, save the back routing from children
+ saveBackRouting(curNode);
+ }
+ goToMoveNumber(0);
+ }
+ saveNode = curNode;
+ }
+
+ /** Save the back routing from children */
+ public void saveBackRouting(BoardHistoryNode node) {
+ if (node != null && node.previous() != null) {
+ node.previous().setFromBackChildren(node.previous().getNexts().indexOf(node));
+ saveBackRouting(node.previous());
+ }
+ }
+
+ /** Restore move number by saved node */
+ public void restoreMoveNumber() {
+ restoreMoveNumber(saveNode);
+ }
+
+ /** Restore move number by node */
+ public void restoreMoveNumber(BoardHistoryNode node) {
+ if (node == null) {
+ return;
+ }
+ int moveNumber = node.getData().moveNumber;
+ if (moveNumber > 0) {
+ if (BoardHistoryList.isMainTrunk(node)) {
+ goToMoveNumber(moveNumber);
+ } else {
+ // If in Branch, restore by the back routing
+ goToMoveNumberByBackChildren(moveNumber);
+ }
+ }
+ }
+
+ /** Go to move number by back routing from children when in branch */
+ public void goToMoveNumberByBackChildren(int moveNumber) {
+ int delta = moveNumber - history.getMoveNumber();
+ for (int i = 0; i < Math.abs(delta); i++) {
+ BoardHistoryNode curNode = history.getCurrentHistoryNode();
+ if (curNode.numberOfChildren() > 1 && delta > 0) {
+ nextMove(curNode.getFromBackChildren());
+ } else {
+ if (!(delta > 0 ? nextMove() : previousMove())) {
+ break;
+ }
+ }
+ }
+ }
+
public boolean goToMoveNumber(int moveNumber) {
return goToMoveNumberHelper(moveNumber, false);
}
diff --git a/src/main/java/featurecat/lizzie/rules/BoardData.java b/src/main/java/featurecat/lizzie/rules/BoardData.java
index 6bc42c018..fff4fcd07 100644
--- a/src/main/java/featurecat/lizzie/rules/BoardData.java
+++ b/src/main/java/featurecat/lizzie/rules/BoardData.java
@@ -12,12 +12,10 @@ public class BoardData {
public Stone lastMoveColor;
public Stone[] stones;
public Zobrist zobrist;
-
public boolean verify;
public double winrate;
public int playouts;
-
public int blackCaptures;
public int whiteCaptures;
diff --git a/src/main/java/featurecat/lizzie/rules/BoardHistoryList.java b/src/main/java/featurecat/lizzie/rules/BoardHistoryList.java
index fb2347f2e..7de27a1a2 100644
--- a/src/main/java/featurecat/lizzie/rules/BoardHistoryList.java
+++ b/src/main/java/featurecat/lizzie/rules/BoardHistoryList.java
@@ -83,18 +83,6 @@ 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
*
diff --git a/src/main/java/featurecat/lizzie/rules/BoardHistoryNode.java b/src/main/java/featurecat/lizzie/rules/BoardHistoryNode.java
index 45aa25ff8..d038123d2 100644
--- a/src/main/java/featurecat/lizzie/rules/BoardHistoryNode.java
+++ b/src/main/java/featurecat/lizzie/rules/BoardHistoryNode.java
@@ -10,6 +10,9 @@ public class BoardHistoryNode {
private BoardData data;
+ // Save the children for restore to branch
+ private int fromBackChildren;
+
/** Initializes a new list node */
public BoardHistoryNode(BoardData data) {
previous = null;
@@ -181,4 +184,14 @@ public void deleteChild(int idx) {
nexts.remove(idx);
}
}
+
+ /** @param fromBackChildren the fromBackChildren to set */
+ public void setFromBackChildren(int fromBackChildren) {
+ this.fromBackChildren = fromBackChildren;
+ }
+
+ /** @return the fromBackChildren */
+ public int getFromBackChildren() {
+ return fromBackChildren;
+ }
}
diff --git a/src/main/java/featurecat/lizzie/rules/GIBParser.java b/src/main/java/featurecat/lizzie/rules/GIBParser.java
index cead794c7..3f5e826ff 100644
--- a/src/main/java/featurecat/lizzie/rules/GIBParser.java
+++ b/src/main/java/featurecat/lizzie/rules/GIBParser.java
@@ -1,7 +1,6 @@
package featurecat.lizzie.rules;
import featurecat.lizzie.Lizzie;
-import featurecat.lizzie.plugin.PluginManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
@@ -35,7 +34,6 @@ public static boolean load(String filename) throws IOException {
}
boolean returnValue = parse(value);
- PluginManager.onSgfLoaded();
return returnValue;
}
diff --git a/src/main/java/featurecat/lizzie/rules/SGFParser.java b/src/main/java/featurecat/lizzie/rules/SGFParser.java
index 2b3a19004..1cc6a931c 100644
--- a/src/main/java/featurecat/lizzie/rules/SGFParser.java
+++ b/src/main/java/featurecat/lizzie/rules/SGFParser.java
@@ -2,7 +2,6 @@
import featurecat.lizzie.Lizzie;
import featurecat.lizzie.analysis.GameInfo;
-import featurecat.lizzie.plugin.PluginManager;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.HashMap;
@@ -40,7 +39,6 @@ public static boolean load(String filename) throws IOException {
}
boolean returnValue = parse(value);
- PluginManager.onSgfLoaded();
return returnValue;
}
@@ -294,8 +292,8 @@ private static void saveToStream(Board board, Writer writer) throws IOException
// collect game info
BoardHistoryList history = board.getHistory().shallowCopy();
GameInfo gameInfo = history.getGameInfo();
- String playerBlack = gameInfo.getPlayerBlack();
- String playerWhite = gameInfo.getPlayerWhite();
+ String playerB = gameInfo.getPlayerBlack();
+ String playerW = gameInfo.getPlayerWhite();
Double komi = gameInfo.getKomi();
Integer handicap = gameInfo.getHandicap();
String date = SGF_DATE_FORMAT.format(gameInfo.getDate());
@@ -307,7 +305,7 @@ private static void saveToStream(Board board, Writer writer) throws IOException
generalProps.append(
String.format(
"KM[%s]PW[%s]PB[%s]DT[%s]AP[Lizzie: %s]",
- komi, playerWhite, playerBlack, date, Lizzie.lizzieVersion));
+ komi, playerW, playerB, date, Lizzie.lizzieVersion));
// move to the first move
history.toStart();
diff --git a/src/main/resources/l10n/DisplayStrings.properties b/src/main/resources/l10n/DisplayStrings.properties
index 05deb875a..be3e5dce7 100644
--- a/src/main/resources/l10n/DisplayStrings.properties
+++ b/src/main/resources/l10n/DisplayStrings.properties
@@ -25,6 +25,7 @@ LizzieFrame.commands.keyEnd=end|go to end
LizzieFrame.commands.keyEnter=enter|force Leela Zero move
LizzieFrame.commands.keyF=f|toggle next move display
LizzieFrame.commands.keyG=g|toggle variation graph
+LizzieFrame.commands.keyT=t|toggle comment display
LizzieFrame.commands.keyHome=home|go to start
LizzieFrame.commands.keyI=i|edit game info
LizzieFrame.commands.keyA=a|run automatic analysis of game
@@ -44,6 +45,7 @@ LizzieFrame.prompt.failedToOpenFile=Failed to open file.
LizzieFrame.prompt.failedToSaveFile=Failed to save file.
LizzieFrame.prompt.sgfExists=The SGF file already exists, do you want to replace it?
LizzieFrame.prompt.showControlsHint=hold x = view controls
+LizzieFrame.prompt.switching=switching...
LizzieFrame.display.lastMove=Last move
LizzieFrame.display.pondering=Pondering
LizzieFrame.display.on=on
diff --git a/src/main/resources/l10n/DisplayStrings_RO.properties b/src/main/resources/l10n/DisplayStrings_RO.properties
index 07b385516..14c54dce5 100644
--- a/src/main/resources/l10n/DisplayStrings_RO.properties
+++ b/src/main/resources/l10n/DisplayStrings_RO.properties
@@ -24,6 +24,7 @@ LizzieFrame.commands.keyEnd=end|mergi la sfârșit
LizzieFrame.commands.keyEnter=enter|forțează mutarea lui Leela Zero
LizzieFrame.commands.keyF=f|afișează/ascunde următoarea mutare
LizzieFrame.commands.keyG=g|afișează/ascunde graficul cu variante
+LizzieFrame.commands.keyT=t|afișează/ascunde comentariul
LizzieFrame.commands.keyHome=home|mergi la început
LizzieFrame.commands.keyI=i|editează informațiile jocului
LizzieFrame.commands.keyA=a|rulează analiza automată a jocului
@@ -43,6 +44,7 @@ LizzieFrame.prompt.failedToOpenSgf=Nu s-a reușit deschiderea fișierului SGF
LizzieFrame.prompt.failedToSaveSgf=Nu s-a reușit salvarea fișierului SGF
LizzieFrame.prompt.sgfExists=Fișierul SGF există deja, doriți să-l înlocuiți?
LizzieFrame.prompt.showControlsHint=x apăsat = afișează comenzi
+LizzieFrame.prompt.switching=comutare...
LizzieFrame.display.lastMove=Ulima mutare
LizzieFrame.display.pondering=Analizeză
LizzieFrame.display.on=pornit
diff --git a/src/main/resources/l10n/DisplayStrings_zh_CN.properties b/src/main/resources/l10n/DisplayStrings_zh_CN.properties
index 95d9c079d..c7effab3e 100644
--- a/src/main/resources/l10n/DisplayStrings_zh_CN.properties
+++ b/src/main/resources/l10n/DisplayStrings_zh_CN.properties
@@ -13,6 +13,7 @@ LizzieFrame.commands.keyEnd=end|\u8DF3\u5230\u68CB\u8C31\u672B\u5C3E
LizzieFrame.commands.keyEnter=enter|\u8BA9Leela Zero\u843D\u5B50
LizzieFrame.commands.keyF=f|\u663E\u793A/\u9690\u85CF\u4E0B\u4E00\u624B
LizzieFrame.commands.keyG=g|\u663E\u793A/\u9690\u85CF\u5206\u652F\u56FE
+LizzieFrame.commands.keyT=t|\u663E\u793A/\u9690\u85CF\u8BC4\u8BBA
LizzieFrame.commands.keyHome=home|\u8DF3\u8F6C\u5230\u68CB\u8C31\u5F00\u5934
LizzieFrame.commands.keyI=i|\u7F16\u8F91\u68CB\u5C40\u4FE1\u606F
LizzieFrame.commands.keyA=a|\u8FD0\u884C\u7B80\u5355\u7684\u81EA\u52A8\u5168\u76D8\u5206\u6790
@@ -32,6 +33,7 @@ LizzieFrame.prompt.failedToOpenFile=\u4E0D\u80FD\u6253\u5F00SGF\u6587\u4EF6.
LizzieFrame.prompt.failedToSaveFile=\u4E0D\u80FD\u4FDD\u5B58SGF\u6587\u4EF6.
LizzieFrame.prompt.sgfExists=SGF\u6587\u4EF6\u5DF2\u7ECF\u5B58\u5728, \u9700\u8981\u66FF\u6362\u5417?
LizzieFrame.prompt.showControlsHint=\u6309\u4F4FX\u4E0D\u653E\u67E5\u770B\u5FEB\u6377\u952E\u63D0\u793A
+LizzieFrame.prompt.switching=\u5207\u6362\u4E2D...
LizzieFrame.display.lastMove=\u6700\u540E\u4E00\u624B
LizzieFrame.display.pondering=\u5206\u6790
LizzieFrame.display.on=\u5F00\u542F
diff --git a/src/test/java/common/Util.java b/src/test/java/common/Util.java
index 2811152d5..7fa429488 100644
--- a/src/test/java/common/Util.java
+++ b/src/test/java/common/Util.java
@@ -1,6 +1,5 @@
package common;
-
import featurecat.lizzie.Lizzie;
import featurecat.lizzie.rules.Board;
import featurecat.lizzie.rules.BoardData;