// Adapter.java
package net.sf.gogui.tools.adapter;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.PrintStream;
import java.util.ArrayList;
import java.util.Locale;
import net.sf.gogui.game.TimeSettings;
import net.sf.gogui.gamefile.GameFileUtil;
import net.sf.gogui.go.ConstPointList;
import net.sf.gogui.go.Board;
import net.sf.gogui.go.BoardUtil;
import net.sf.gogui.go.GoColor;
import static net.sf.gogui.go.GoColor.BLACK;
import static net.sf.gogui.go.GoColor.WHITE;
import static net.sf.gogui.go.GoColor.EMPTY;
import net.sf.gogui.go.GoPoint;
import net.sf.gogui.go.InvalidKomiException;
import net.sf.gogui.go.Komi;
import net.sf.gogui.go.Move;
import net.sf.gogui.go.PointList;
import net.sf.gogui.gtp.GtpCallback;
import net.sf.gogui.gtp.GtpClient;
import net.sf.gogui.gtp.GtpClientBase;
import net.sf.gogui.gtp.GtpCommand;
import net.sf.gogui.gtp.GtpEngine;
import net.sf.gogui.gtp.GtpError;
import net.sf.gogui.gtp.GtpResponseFormatError;
import net.sf.gogui.gtp.GtpSynchronizer;
import net.sf.gogui.gtp.GtpUtil;
import net.sf.gogui.util.ErrorMessage;
/** GTP adapter for logging or protocol translations. */
public class Adapter
extends GtpEngine
{
/** Constructor.
@param program Command line for executing the actual GTP engine.
@param log Stream to log commands and responses of the adapter.
@param gtpFile File with GTP commands to send engine at startup.
@param verbose Enable logging of commands sent from the adapter to
the actual GTP engine to standard error.
@param noScore Hide final_score and final_status_list commands, even
if the angine supports them.
@param version1 Whether the adapter reports and implements GTP version
1 commands.
@param fillPasses Fill moves of non-alternating colors with pass
moves.
@param lowerCase Translate move commands to the engine to lower-case.
@param size Board size at startup. */
public Adapter(String program, PrintStream log, String gtpFile,
boolean verbose, boolean noScore, boolean version1,
boolean fillPasses, boolean lowerCase, int size)
throws Exception
{
super(log);
if (program.equals(""))
throw new Exception("No program is set.");
m_gtp = new GtpClient(program, null, verbose, null);
if (lowerCase)
m_gtp.setLowerCase();
m_synchronizer = new GtpSynchronizer(m_gtp, null, fillPasses);
if (gtpFile != null)
sendGtpFile(gtpFile);
init(noScore, version1, size);
}
/** Construct with existing GtpClientBase.
For testing this class. */
public Adapter(GtpClientBase gtp, PrintStream log, boolean noScore,
boolean version1, boolean lowerCase, int size)
throws GtpError
{
super(log);
m_gtp = gtp;
if (lowerCase)
m_gtp.setLowerCase();
m_synchronizer = new GtpSynchronizer(m_gtp, null, false);
init(noScore, version1, size);
}
public void close()
{
m_gtp.close();
m_gtp.waitForExit();
}
public void cmdBlack(GtpCommand cmd) throws GtpError
{
cmd.checkNuArg(1);
play(BLACK, getPointArg(cmd, 0));
}
public void cmdBoardsize(GtpCommand cmd) throws GtpError
{
cmd.checkNuArg(1);
int size = cmd.getIntArg(0, 1, GoPoint.MAX_SIZE);
m_board.init(size);
synchronize();
}
public void cmdClearBoard(GtpCommand cmd) throws GtpError
{
cmd.checkArgNone();
m_board.init(m_board.getSize());
synchronize();
}
public void cmdForward(GtpCommand cmd) throws GtpError
{
send(cmd.getLine(), cmd.getResponse());
}
public void cmdGenmove(GtpCommand cmd) throws GtpError
{
GoColor c = cmd.getColorArg();
cmdGenmove(c, cmd, m_gtp.getCommandGenmove(c));
}
public void cmdGenmoveCleanup(GtpCommand cmd) throws GtpError
{
GoColor c = cmd.getColorArg();
cmdGenmove(c, cmd, "kgs-genmove_cleanup " + c.getUppercaseLetter());
}
public void cmdGenmoveBlack(GtpCommand cmd) throws GtpError
{
cmdGenmove(BLACK, cmd, m_gtp.getCommandGenmove(BLACK));
}
public void cmdGenmoveWhite(GtpCommand cmd) throws GtpError
{
cmdGenmove(WHITE, cmd, m_gtp.getCommandGenmove(WHITE));
}
public void cmdGGUndo(GtpCommand cmd) throws GtpError
{
cmd.checkNuArgLessEqual(1);
int n = 1;
if (cmd.getNuArg() == 1)
n = cmd.getIntArg(0, 1, m_board.getNumberMoves());
m_board.undo(n);
synchronize();
}
public void cmdGoGuiAnalyzeCommands(GtpCommand cmd) throws GtpError
{
cmd.checkArgNone();
String response =
"string/Adapter ShowBoard/gogui-adapter-showboard\n";
String command = null;
if (m_gtp.isSupported("gogui-analyze_commands"))
command = "gogui-analyze_commands";
else if (m_gtp.isSupported("gogui_analyze_commands"))
command = "gogui_analyze_commands"; // deprecated
if (command != null)
response += send(command);
cmd.setResponse(response);
}
public void cmdAdapterShowBoard(GtpCommand cmd) throws GtpError
{
cmd.getResponse().append("\n");
cmd.getResponse().append(BoardUtil.toString(m_board, true, false));
}
public void cmdKomi(GtpCommand cmd) throws GtpError
{
try
{
m_komi = Komi.parseKomi(cmd.getArg());
}
catch (InvalidKomiException e)
{
throw new GtpError("invalid komi");
}
}
public void cmdLoad(GtpCommand cmd) throws GtpError
{
cmd.checkNuArgLessEqual(2);
File file = new File(cmd.getArg(0));
int maxMove = -1;
if (cmd.getNuArg() == 2)
maxMove = cmd.getIntArg(1);
try
{
BoardUtil.copy(m_board, GameFileUtil.load(file, maxMove));
}
catch (ErrorMessage e)
{
throw new GtpError(e.getMessage());
}
synchronize();
}
public void cmdPlaceFreeHandicap(GtpCommand cmd) throws GtpError
{
ConstPointList stones;
if (m_gtp.isSupported("place_free_handicap"))
{
String response = send(cmd.getLine());
try
{
stones = GtpUtil.parsePointList(response, m_board.getSize());
}
catch (GtpResponseFormatError e)
{
throw new GtpError(e.getMessage());
}
}
else
{
int n = cmd.getIntArg();
stones = Board.getHandicapStones(m_board.getSize(), n);
if (stones == null)
throw new GtpError("Invalid number of handicap stones");
}
StringBuilder response = new StringBuilder(128);
PointList points = new PointList();
for (GoPoint p : stones)
{
points.add(p);
if (response.length() > 0)
response.append(' ');
response.append(p);
}
m_board.setup(points, null, null);
cmd.setResponse(response.toString());
synchronize();
}
public void cmdPlay(GtpCommand cmd) throws GtpError
{
cmd.checkNuArg(2);
GoColor color = cmd.getColorArg(0);
GoPoint point = getPointArg(cmd, 1);
if (point != null && m_board.getColor(point) != EMPTY)
throw new GtpError("point is occupied");
play(color, point);
}
public void cmdProtocolVersion1(GtpCommand cmd) throws GtpError
{
cmd.checkArgNone();
cmd.setResponse("1");
}
public void cmdQuit(GtpCommand cmd) throws GtpError
{
send("quit");
super.cmdQuit(cmd);
}
public void cmdSetFreeHandicap(GtpCommand cmd) throws GtpError
{
PointList points = new PointList();
for (int i = 0; i < cmd.getNuArg(); ++i)
points.add(getPointArg(cmd, i));
m_board.setup(points, null, null);
synchronize();
}
public void cmdTimeSettings(GtpCommand cmd) throws GtpError
{
cmd.checkNuArg(3);
long mainTime = cmd.getIntArg(0, 0, Integer.MAX_VALUE) * 1000L;
long byoyomiTime = cmd.getIntArg(0, 0, Integer.MAX_VALUE) * 1000L;
int byoyomiStones = cmd.getIntArg(0, 0, Integer.MAX_VALUE);
if (byoyomiTime == 0)
m_timeSettings = new TimeSettings(mainTime);
else if (byoyomiStones == 0)
m_timeSettings = null;
else
m_timeSettings =
new TimeSettings(mainTime, byoyomiTime, byoyomiStones);
}
public void cmdUndo(GtpCommand cmd) throws GtpError
{
cmd.checkArgNone();
m_board.undo();
synchronize();
}
public void cmdWhite(GtpCommand cmd) throws GtpError
{
cmd.checkNuArg(1);
play(WHITE, getPointArg(cmd, 0));
}
public void interruptCommand()
{
try
{
if (m_gtp.isInterruptSupported())
m_gtp.sendInterrupt();
}
catch (GtpError e)
{
System.err.println(e);
}
}
public void setName(String name)
{
if (name == null)
{
register("name", m_callbackForward);
register("version", m_callbackForward);
return;
}
int index = name.indexOf(':');
if (index < 0)
{
super.setName(name);
super.setVersion("");
}
else
{
super.setName(name.substring(0, index));
super.setVersion(name.substring(index + 1));
}
register("name", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdName(cmd); } });
register("version", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdVersion(cmd); } });
}
private Board m_board;
private final GtpCallback m_callbackForward = new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdForward(cmd); } };
private final GtpClientBase m_gtp;
private final GtpSynchronizer m_synchronizer;
private Komi m_komi;
private TimeSettings m_timeSettings;
private void cmdGenmove(GoColor color, GtpCommand cmd, String command)
throws GtpError
{
String response = send(command);
if (response.toLowerCase(Locale.ENGLISH).trim().equals("resign"))
{
cmd.setResponse("resign");
return;
}
try
{
GoPoint point = GtpUtil.parsePoint(response, m_board.getSize());
m_board.play(color, point);
m_synchronizer.updateAfterGenmove(m_board);
cmd.setResponse(response);
}
catch (GtpResponseFormatError e)
{
throw new GtpError(e.getMessage());
}
}
private GoPoint getPointArg(GtpCommand cmd, int i) throws GtpError
{
return cmd.getPointArg(i, m_board.getSize());
}
private void init(boolean noScore, boolean version1, int size)
throws GtpError
{
m_gtp.queryProtocolVersion();
m_gtp.querySupportedCommands();
m_board = new Board(size);
registerCommands(noScore, version1);
synchronize();
}
private void play(GoColor color, GoPoint point) throws GtpError
{
Move move = Move.get(color, point);
m_board.play(move);
synchronize();
}
private void registerCommands(boolean noScore, boolean version1)
{
ArrayList<String> commands = m_gtp.getSupportedCommands();
for (int i = 0; i < commands.size(); ++i)
{
String command = commands.get(i);
if (! GtpUtil.isStateChangingCommand(command))
register(command, m_callbackForward);
}
if (m_gtp.isSupported("kgs-genmove_cleanup"))
register("kgs-genmove_cleanup", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdGenmoveCleanup(cmd); } });
register("boardsize", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdBoardsize(cmd); } });
register("gogui-analyze_commands", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdGoGuiAnalyzeCommands(cmd); } });
register("gogui-adapter-showboard", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdAdapterShowBoard(cmd); } });
register("komi", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdKomi(cmd); } });
register("loadsgf", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdLoad(cmd); } });
register("loadxml", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdLoad(cmd); } });
setName(null);
register("place_free_handicap", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdPlaceFreeHandicap(cmd); } });
if (version1)
register("protocol_version", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdProtocolVersion1(cmd); } });
register("set_free_handicap", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdSetFreeHandicap(cmd); } });
register("time_settings", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdTimeSettings(cmd); } });
if (noScore)
{
unregister("final_score");
unregister("final_status_list");
}
if (version1)
{
register("black", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdBlack(cmd); } });
register("genmove_black", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdGenmoveBlack(cmd); } });
register("genmove_white", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdGenmoveWhite(cmd); } });
register("help", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdListCommands(cmd); } });
unregister("list_commands");
register("white", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdWhite(cmd); } });
}
else
{
register("clear_board", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdClearBoard(cmd); } });
register("genmove", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdGenmove(cmd); } });
unregister("help");
register("known_command", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdKnownCommand(cmd); } });
register("list_commands", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdListCommands(cmd); } });
register("play", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdPlay(cmd); } });
}
register("undo", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdUndo(cmd); } });
register("gg-undo", new GtpCallback() {
public void run(GtpCommand cmd) throws GtpError {
cmdGGUndo(cmd); } });
}
private String send(String cmd) throws GtpError
{
return m_gtp.send(cmd);
}
private void send(String cmd, StringBuilder response) throws GtpError
{
response.append(m_gtp.send(cmd));
}
private void sendGtpFile(String filename)
{
java.io.Reader reader;
try
{
reader = new FileReader(new File(filename));
}
catch (FileNotFoundException e)
{
System.err.println("File not found: " + filename);
return;
}
java.io.BufferedReader in;
in = new BufferedReader(reader);
try
{
while (true)
{
try
{
String line = in.readLine();
if (line == null)
{
in.close();
break;
}
line = line.trim();
if (line.equals("") || line.startsWith("#"))
continue;
try
{
GtpCommand cmd = new GtpCommand(line);
if (GtpUtil.isStateChangingCommand(cmd.getCommand()))
{
System.err.println("Command " + cmd.getCommand()
+ " not allowed in GTP file");
break;
}
send(line);
}
catch (GtpError e)
{
System.err.println("Sending commands aborted:"
+ e.getMessage());
break;
}
}
catch (IOException e)
{
System.err.println("Sending commands aborted:"
+ e.getMessage());
break;
}
}
}
finally
{
try
{
in.close();
}
catch (IOException e)
{
}
}
}
private void synchronize() throws GtpError
{
m_synchronizer.synchronize(m_board, m_komi, m_timeSettings);
}
}