package fr.xtof54.sgfsearch;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
import java.util.Vector;
import rene.util.list.ListElement;
import rene.util.xml.XmlReader;
import rene.util.xml.XmlReaderException;
import rene.util.xml.XmlWriter;
//******************* Board ***********************
/**
* This is the main file for presenting a Go board.
* <P>
* Handles the complete display and storage of a Go board. The display is kept
* on an offscreen image. Stores a Go game in a node list (with variants).
* Handles the display of the current node.
* <P>
* This class handles mouse input to set the next move. It also has methods to
* move in the node tree from external sources.
* <P>
* A BoardInterface is used to encorporate the board into an environment.
*/
public class Board
{
int O, W, D, S, OT, OTU, OP; // pixel coordinates
// O=offset, W=total width, D=field width, S=board size (9,11,13,19)
// OT=offset for coordinates to the right and below
// OTU=offset above the board and below for coordinates
int lasti = -1, lastj = 0; // last move (used to highlight the move)
boolean showlast; // internal flag, if last move is to be highlighted
// offscreen images of empty and current board
SGFTree T; // the game tree
Vector Trees; // the game trees (one of them is T)
int CurrentTree; // the currently displayed tree
Position P; // current board position
int number; // number of the next move
TreeNode Pos; // the current board position in this presentation
int State; // states: 1 is black, 2 is white, 3 is set black etc.
// see GoFrame.setState(int)
BoardInterface GF; // frame containing the board
boolean Active;
int MainColor = 1;
public int MyColor = 0;
int sendi = -1, sendj;
// board position which has been sended to server
int SpecialMarker = Field.SQUARE;
String TextMarker = "A";
public int Pw, Pb; // Prisoners (white and black)
BufferedReader LaterLoad = null; // File to be loaded at repaint
int Range = -1; // Numbers display from this one
boolean KeepRange = false;
String NodeName = "", LText = "";
boolean DisplayNodeName = false;
public boolean Removing = false;
boolean Activated = false;
public boolean Teaching = false; // enable teaching mode
boolean VCurrent = false; // show variations to current move
boolean VHide = false; // hide variation markers
// ******************** initialize board *******************
public Board (int size, BoardInterface gf)
{
S = size;
D = 16;
W = S * D;
showlast = true;
GF = gf;
State = 1;
P = new Position(S);
number = 1;
T = new SGFTree(new Node(number));
Trees = new Vector();
Trees.addElement(T);
CurrentTree = 0;
Pos = T.top();
Active = true;
Pw = Pb = 0;
VHide = GF.getParameter("vhide", false);
VCurrent = GF.getParameter("vcurrent", true);
}
// set things on the board
void gotovariation (int i, int j)
// goto the variation at (i,j)
{
TreeNode newpos = P.tree(i, j);
getinformation();
if (VCurrent && newpos.parentPos() == Pos.parentPos())
{
goback();
Pos = newpos;
setnode();
setlast();
}
else if ( !VCurrent && newpos.parentPos() == Pos)
{
Pos = newpos;
setnode();
setlast();
}
copy();
showinformation();
}
public void set (int i, int j)
// set a new move, if the board position is empty
{
Action a;
synchronized (Pos)
{
if (P.color(i, j) == 0) // empty?
{
if (Pos.node().actions() != null || Pos.parentPos() == null)
// create a new node, if there the current node is not
// empty, else use the current node. exception is the first
// move, where we always create a new move.
{
Node n = new Node(++number);
if (P.color() > 0)
{
a = new Action("B", Field.string(i, j));
}
else
{
a = new Action("W", Field.string(i, j));
}
n.addaction(a); // note the move action
setaction(n, a, P.color()); // display the action
// !!! We alow for suicide moves
TreeNode newpos = new TreeNode(n);
Pos.addchild(newpos); // note the move
n.main(Pos);
Pos = newpos; // update current position pointer
}
else
{
Node n = Pos.node();
if (P.color() > 0)
{
a = new Action("B", Field.string(i, j));
}
else
{
a = new Action("W", Field.string(i, j));
}
n.addaction(a); // note the move action
setaction(n, a, P.color()); // display the action
// !!! We alow for suicide moves
}
}
}
}
public void delete (int i, int j)
// delete the stone and note it
{
if (P.color(i, j) == 0) return;
synchronized (Pos)
{
Node n = Pos.node();
if (GF.getParameter("puresgf", true)
&& (n.contains("B") || n.contains("W"))) n = newnode();
String field = Field.string(i, j);
if (n.contains("AB", field))
{
undonode();
n.toggleaction(new Action("AB", field));
setnode();
}
else if (n.contains("AW", field))
{
undonode();
n.toggleaction(new Action("AW", field));
setnode();
}
else if (n.contains("B", field))
{
undonode();
n.toggleaction(new Action("B", field));
setnode();
}
else if (n.contains("W", field))
{
undonode();
n.toggleaction(new Action("W", field));
setnode();
}
else
{
Action a = new Action("AE", field);
n.expandaction(a);
n.addchange(new Change(i, j, P.color(i, j)));
P.color(i, j, 0);
update(i, j);
}
showinformation();
copy();
}
}
public void changemove (int i, int j)
// change a move to a new field (dangerous!)
{
if (P.color(i, j) != 0) return;
synchronized (Pos)
{
ListElement la = Pos.node().actions();
while (la != null)
{
Action a = (Action)la.content();
if (a.type().equals("B") || a.type().equals("W"))
{
undonode();
a.arguments().content(Field.string(i, j));
setnode();
break;
}
}
}
}
public void removegroup (int i0, int j0)
// completely remove a group (at end of game, before count)
// note all removals
{
if (Pos.haschildren()) return;
if (P.color(i0, j0) == 0) return;
Action a;
P.markgroup(i0, j0);
int i, j;
int c = P.color(i0, j0);
Node n = Pos.node();
if (n.contains("B") || n.contains("W")) n = newnode();
for (i = 0; i < S; i++)
for (j = 0; j < S; j++)
{
if (P.marked(i, j))
{
a = new Action("AE", Field.string(i, j));
n
.addchange(new Change(i, j, P.color(i, j), P.number(i,
j)));
n.expandaction(a);
if (P.color(i, j) > 0)
{
n.Pb++;
Pb++;
}
else
{
n.Pw++;
Pw++;
}
P.color(i, j, 0);
update(i, j);
}
}
copy();
}
public void mark (int i, int j)
// Emphasize the field at i,j
{
Node n = Pos.node();
Action a = new MarkAction(Field.string(i, j), GF);
n.toggleaction(a);
update(i, j);
}
public void specialmark (int i, int j)
// Emphasize with the SpecialMarker
{
Node n = Pos.node();
String s;
switch (SpecialMarker)
{
case Field.SQUARE:
s = "SQ";
break;
case Field.CIRCLE:
s = "CR";
break;
case Field.TRIANGLE:
s = "TR";
break;
default:
s = "MA";
break;
}
Action a = new Action(s, Field.string(i, j));
n.toggleaction(a);
update(i, j);
}
public void markterritory (int i, int j, int color)
{
Action a;
if (color > 0)
a = new Action("TB", Field.string(i, j));
else a = new Action("TW", Field.string(i, j));
Pos.node().expandaction(a);
update(i, j);
}
public void textmark (int i, int j)
{
Action a = new Action("LB", Field.string(i, j) + ":" + TextMarker);
Pos.node().expandaction(a);
update(i, j);
GF.advanceTextmark();
}
public void letter (int i, int j)
// Write a character to the field at i,j
{
Action a = new LabelAction(Field.string(i, j), GF);
Pos.node().toggleaction(a);
update(i, j);
}
public Node newnode ()
{
Node n = new Node(++number);
TreeNode newpos = new TreeNode(n);
Pos.addchild(newpos); // note the move
n.main(Pos);
Pos = newpos; // update current position pointerAction a;
setlast();
return n;
}
public void set (int i, int j, int c)
// set a new stone, if the board position is empty
// and we are on the last node.
{
if (Pos.haschildren()) return;
setc(i, j, c);
}
public void setc (int i, int j, int c)
{
synchronized (Pos)
{
Action a;
if (P.color(i, j) == 0) // empty?
{
Node n = Pos.node();
if (GF.getParameter("puresgf", true)
&& (n.contains("B") || n.contains("W"))) n = newnode();
n.addchange(new Change(i, j, 0));
if (c > 0)
{
a = new Action("AB", Field.string(i, j));
}
else
{
a = new Action("AW", Field.string(i, j));
}
n.expandaction(a); // note the move action
P.color(i, j, c);
update(i, j);
}
}
}
int captured = 0, capturei, capturej;
public void capture (int i, int j, Node n)
// capture neighboring groups without liberties
// capture own group on suicide
{
int c = -P.color(i, j);
captured = 0;
if (i > 0) capturegroup(i - 1, j, c, n);
if (j > 0) capturegroup(i, j - 1, c, n);
if (i < S - 1) capturegroup(i + 1, j, c, n);
if (j < S - 1) capturegroup(i, j + 1, c, n);
if (P.color(i, j) == -c)
{
capturegroup(i, j, -c, n);
}
if (captured == 1 && P.count(i, j) != 1) captured = 0;
if ( !GF.getParameter("korule", true)) captured = 0;
}
public void capturegroup (int i, int j, int c, Node n)
// Used by capture to determine the state of the groupt at (i,j)
// Remove it, if it has no liberties and note the removals
// as actions in the current node.
{
int ii, jj;
Action a;
if (P.color(i, j) != c) return;
if ( !P.markgrouptest(i, j, 0)) // liberties?
{
for (ii = 0; ii < S; ii++)
for (jj = 0; jj < S; jj++)
{
if (P.marked(ii, jj))
{
n.addchange(new Change(ii, jj, P.color(ii, jj), P
.number(ii, jj)));
if (P.color(ii, jj) > 0)
{
Pb++;
n.Pb++;
}
else
{
Pw++;
n.Pw++;
}
P.color(ii, jj, 0);
update(ii, jj); // redraw the field (offscreen)
captured++;
capturei = ii;
capturej = jj;
}
}
}
}
public void variation (int i, int j)
{
if (Pos.parentPos() == null) return;
if (P.color(i, j) == 0) // empty?
{
int c = P.color();
goback();
P.color( -c);
set(i, j);
if ( !GF.getParameter("variationnumbers", false))
{
P.number(i, j, 1);
number = 2;
Pos.node().number(2);
}
update(i, j);
}
}
public String formtime (int t)
{
int s, m, h = t / 3600;
if (h >= 1)
{
t = t - 3600 * h;
m = t / 60;
s = t - 60 * m;
return "" + h + ":" + twodigits(m) + ":" + twodigits(s);
}
else
{
m = t / 60;
s = t - 60 * m;
return "" + m + ":" + twodigits(s);
}
}
public String twodigits (int n)
{
if (n < 10)
return "0" + n;
else return "" + n;
}
public String lookuptime (String type)
{
int t;
if (Pos.parentPos() != null)
{
String s = Pos.parentPos().node().getaction(type);
if ( !s.equals(""))
{
try
{
t = Integer.parseInt(s);
return formtime(t);
}
catch (Exception e)
{
return "";
}
}
else return "";
}
return "";
}
public void showinformation ()
// update the label to display the next move and who's turn it is
// and disable variation buttons
// update the navigation buttons
// update the comment
{
Node n = Pos.node();
number = n.number();
NodeName = n.getaction("N");
String ms = "";
if (n.main())
{
if ( !Pos.haschildren())
ms = "** ";
else ms = "* ";
}
switch (State)
{
case 3:
LText = ms + GF.resourceString("Set_black_stones");
break;
case 4:
LText = ms + GF.resourceString("Set_white_stones");
break;
case 5:
LText = ms + GF.resourceString("Mark_fields");
break;
case 6:
LText = ms + GF.resourceString("Place_letters");
break;
case 7:
LText = ms + GF.resourceString("Delete_stones");
break;
case 8:
LText = ms + GF.resourceString("Remove_prisoners");
break;
case 9:
LText = ms + GF.resourceString("Set_special_marker");
break;
case 10:
LText = ms + GF.resourceString("Text__") + TextMarker;
break;
default:
if (P.color() > 0)
{
String s = lookuptime("BL");
if ( !s.equals(""))
LText = ms + GF.resourceString("Next_move__Black_")
+ number + " (" + s + ")";
else LText = ms + GF.resourceString("Next_move__Black_")
+ number;
}
else
{
String s = lookuptime("WL");
if ( !s.equals(""))
LText = ms + GF.resourceString("Next_move__White_")
+ number + " (" + s + ")";
else LText = ms + GF.resourceString("Next_move__White_")
+ number;
}
}
LText = LText + " (" + siblings() + " " + GF.resourceString("Siblings")
+ ", " + children() + " " + GF.resourceString("Children") + ")";
if (NodeName.equals(""))
{
GF.setLabel(LText);
DisplayNodeName = false;
}
else
{
GF.setLabel(NodeName);
DisplayNodeName = true;
}
GF.setState(3, !n.main());
GF.setState(4, !n.main());
GF.setState(7, !n.main() || Pos.haschildren());
if (State == 1 || State == 2)
{
if (P.color() == 1)
State = 1;
else State = 2;
}
GF.setState(1, Pos.parentPos() != null
&& Pos.parentPos().firstChild() != Pos);
GF.setState(2, Pos.parentPos() != null
&& Pos.parentPos().lastChild() != Pos);
GF.setState(5, Pos.haschildren());
GF.setState(6, Pos.parentPos() != null);
if (State != 9)
GF.setState(State);
else GF.setMarkState(SpecialMarker);
int i, j;
// delete all marks and variations
for (i = 0; i < S; i++)
for (j = 0; j < S; j++)
{
if (P.tree(i, j) != null)
{
P.tree(i, j, null);
update(i, j);
}
if (P.marker(i, j) != Field.NONE)
{
P.marker(i, j, Field.NONE);
update(i, j);
}
if (P.letter(i, j) != 0)
{
P.letter(i, j, 0);
update(i, j);
}
if (P.haslabel(i, j))
{
P.clearlabel(i, j);
update(i, j);
}
}
ListElement la = Pos.node().actions();
Action a;
String s;
String sc = "";
int let = 1;
while (la != null) // setup the marks and letters
{
a = (Action)la.content();
if (a.type().equals("C"))
{
sc = (String)a.arguments().content();
}
else if (a.type().equals("SQ") || a.type().equals("SL"))
{
ListElement larg = a.arguments();
while (larg != null)
{
s = (String)larg.content();
i = Field.i(s);
j = Field.j(s);
if (valid(i, j))
{
P.marker(i, j, Field.SQUARE);
update(i, j);
}
larg = larg.next();
}
}
else if (a.type().equals("MA") || a.type().equals("M")
|| a.type().equals("TW") || a.type().equals("TB"))
{
ListElement larg = a.arguments();
while (larg != null)
{
s = (String)larg.content();
i = Field.i(s);
j = Field.j(s);
if (valid(i, j))
{
P.marker(i, j, Field.CROSS);
update(i, j);
}
larg = larg.next();
}
}
else if (a.type().equals("TR"))
{
ListElement larg = a.arguments();
while (larg != null)
{
s = (String)larg.content();
i = Field.i(s);
j = Field.j(s);
if (valid(i, j))
{
P.marker(i, j, Field.TRIANGLE);
update(i, j);
}
larg = larg.next();
}
}
else if (a.type().equals("CR"))
{
ListElement larg = a.arguments();
while (larg != null)
{
s = (String)larg.content();
i = Field.i(s);
j = Field.j(s);
if (valid(i, j))
{
P.marker(i, j, Field.CIRCLE);
update(i, j);
}
larg = larg.next();
}
}
else if (a.type().equals("L"))
{
ListElement larg = a.arguments();
while (larg != null)
{
s = (String)larg.content();
i = Field.i(s);
j = Field.j(s);
if (valid(i, j))
{
P.letter(i, j, let);
let++;
update(i, j);
}
larg = larg.next();
}
}
else if (a.type().equals("LB"))
{
ListElement larg = a.arguments();
while (larg != null)
{
s = (String)larg.content();
i = Field.i(s);
j = Field.j(s);
if (valid(i, j) && s.length() >= 4 && s.charAt(2) == ':')
{
P.setlabel(i, j, s.substring(3));
update(i, j);
}
larg = larg.next();
}
}
la = la.next();
}
TreeNode p;
ListElement l = null;
if (VCurrent)
{
p = Pos.parentPos();
if (p != null) l = p.firstChild().listelement();
}
else if (Pos.haschildren() && Pos.firstChild() != Pos.lastChild())
{
l = Pos.firstChild().listelement();
}
while (l != null)
{
p = (TreeNode)l.content();
if (p != Pos)
{
la = p.node().actions();
while (la != null)
{
a = (Action)la.content();
if (a.type().equals("W") || a.type().equals("B"))
{
s = (String)a.arguments().content();
i = Field.i(s);
j = Field.j(s);
if (valid(i, j))
{
P.tree(i, j, p);
update(i, j);
}
break;
}
la = la.next();
}
}
l = l.next();
}
if ( !GF.getComment().equals(sc))
{
GF.setComment(sc);
}
if (Range >= 0 && !KeepRange) clearrange();
}
public int siblings ()
{
ListElement l = Pos.listelement();
if (l == null) return 0;
while (l.previous() != null)
l = l.previous();
int count = 0;
while (l.next() != null)
{
l = l.next();
count++;
}
return count;
}
public int children ()
{
if ( !Pos.haschildren()) return 0;
TreeNode p = Pos.firstChild();
if (p == null) return 0;
ListElement l = p.listelement();
if (l == null) return 0;
while (l.previous() != null)
l = l.previous();
int count = 1;
while (l.next() != null)
{
l = l.next();
count++;
}
return count;
}
public void clearsend ()
{
if (sendi >= 0)
{
int i = sendi;
sendi = -1;
update(i, sendj);
}
}
public void getinformation ()
// update the comment, when leaving the position
{
ListElement la = Pos.node().actions();
Action a;
clearsend();
while (la != null)
{
a = (Action)la.content();
if (a.type().equals("C"))
{
if (GF.getComment().equals(""))
Pos.node().removeaction(la);
else a.arguments().content(GF.getComment());
return;
}
la = la.next();
}
String s = GF.getComment();
if ( !s.equals(""))
{
Pos.addaction(new Action("C", s));
}
}
public void update (int i, int j)
// update the field (i,j) in the offscreen image Active
// in dependance of the board position P.
// display the last move mark, if applicable.
{
}
public void copy ()
// copy the offscreen board to the screen
{
}
public void undonode ()
// Undo everything that has been changed in the node.
// (This will not correct the last move marker!)
{
Node n = Pos.node();
clearrange();
ListElement p = n.lastchange();
while (p != null)
{
Change c = (Change)p.content();
P.color(c.I, c.J, c.C);
P.number(c.I, c.J, c.N);
update(c.I, c.J);
p = p.previous();
}
n.clearchanges();
Pw -= n.Pw;
Pb -= n.Pb;
}
public void setaction (Node n, Action a, int c)
// interpret a set move action, update the last move marker,
// c being the color of the move.
{
String s = (String)a.arguments().content();
int i = Field.i(s);
int j = Field.j(s);
if ( !valid(i, j)) return;
n.addchange(new Change(i, j, P.color(i, j), P.number(i, j)));
P.color(i, j, c);
P.number(i, j, n.number() - 1);
showlast = false;
update(lasti, lastj);
showlast = true;
lasti = i;
lastj = j;
update(i, j);
P.color( -c);
capture(i, j, n);
}
public void placeaction (Node n, Action a, int c)
// interpret a set move action, update the last move marker,
// c being the color of the move.
{
int i, j;
ListElement larg = a.arguments();
while (larg != null)
{
String s = (String)larg.content();
i = Field.i(s);
j = Field.j(s);
if (valid(i, j))
{
n.addchange(new Change(i, j, P.color(i, j), P.number(i, j)));
P.color(i, j, c);
update(i, j);
}
larg = larg.next();
}
}
public void emptyaction (Node n, Action a)
// interpret a remove stone action
{
int i, j, r = 1;
ListElement larg = a.arguments();
while (larg != null)
{
String s = (String)larg.content();
i = Field.i(s);
j = Field.j(s);
if (valid(i, j))
{
n.addchange(new Change(i, j, P.color(i, j), P.number(i, j)));
if (P.color(i, j) < 0)
{
n.Pw++;
Pw++;
}
else if (P.color(i, j) > 0)
{
n.Pb++;
Pb++;
}
P.color(i, j, 0);
update(i, j);
}
larg = larg.next();
}
}
public void setnode ()
// interpret all actions of a node
{
Node n = Pos.node();
ListElement p = n.actions();
if (p == null) return;
Action a;
String s;
int i, j;
while (p != null)
{
a = (Action)p.content();
if (a.type().equals("SZ"))
{
if (Pos.parentPos() == null)
// only at first node
{
try
{
int ss = Integer.parseInt(a.argument().trim());
if (ss != S)
{
S = ss;
P = new Position(S);
updateall();
copy();
}
}
catch (NumberFormatException e)
{}
}
}
p = p.next();
}
n.clearchanges();
n.Pw = n.Pb = 0;
p = n.actions();
while (p != null)
{
a = (Action)p.content();
if (a.type().equals("B"))
{
setaction(n, a, 1);
}
else if (a.type().equals("W"))
{
setaction(n, a, -1);
}
if (a.type().equals("AB"))
{
placeaction(n, a, 1);
}
if (a.type().equals("AW"))
{
placeaction(n, a, -1);
}
else if (a.type().equals("AE"))
{
emptyaction(n, a);
}
p = p.next();
}
}
public void setlast ()
// update the last move marker applying all
// set move actions in the node
{
Node n = Pos.node();
ListElement l = n.actions();
Action a;
String s;
int i = lasti, j = lastj;
lasti = -1;
lastj = -1;
update(i, j);
while (l != null)
{
a = (Action)l.content();
if (a.type().equals("B") || a.type().equals("W"))
{
s = (String)a.arguments().content();
i = Field.i(s);
j = Field.j(s);
if (valid(i, j))
{
lasti = i;
lastj = j;
update(lasti, lastj);
P.color( -P.color(i, j));
}
}
l = l.next();
}
number = n.number();
}
public void undo ()
// take back the last move, ask if necessary
{ // System.out.println("undo");
if (Pos.haschildren() || Pos.parent() != null
&& Pos.parent().lastchild() != Pos.parent().firstchild()
&& Pos == Pos.parent().firstchild())
{
if (GF.askUndo()) doundo(Pos);
}
else doundo(Pos);
}
public void doundo (TreeNode pos1)
{
if (pos1 != Pos) return;
if (Pos.parentPos() == null)
{
undonode();
Pos.removeall();
Pos.node().removeactions();
showinformation();
copy();
return;
}
TreeNode pos = Pos;
goback();
if (pos == Pos.firstchild())
Pos.removeall();
else Pos.remove(pos);
goforward();
showinformation();
copy();
}
public void goback ()
// go one move back
{
State = 1;
if (Pos.parentPos() == null) return;
undonode();
Pos = Pos.parentPos();
setlast();
}
public void goforward ()
// go one move forward
{
if ( !Pos.haschildren()) return;
Pos = Pos.firstChild();
setnode();
setlast();
}
public void gotoMove (int move)
{
while (number <= move && Pos.firstChild() != null)
{
goforward();
}
}
public void tovarleft ()
{
ListElement l = Pos.listelement();
if (l == null) return;
if (l.previous() == null) return;
TreeNode newpos = (TreeNode)l.previous().content();
goback();
Pos = newpos;
setnode();
}
public void tovarright ()
{
ListElement l = Pos.listelement();
if (l == null) return;
if (l.next() == null) return;
TreeNode newpos = (TreeNode)l.next().content();
goback();
Pos = newpos;
setnode();
}
public boolean hasvariation ()
{
ListElement l = Pos.listelement();
if (l == null) return false;
if (l.next() == null) return false;
return true;
}
static public TreeNode getNext (TreeNode p)
{
ListElement l = p.listelement();
if (l == null) return null;
if (l.next() == null) return null;
return (TreeNode)l.next().content();
}
public void territory (int i, int j)
{
mark(i, j);
copy();
}
public void setpass ()
{
TreeNode p = T.top();
while (p.haschildren())
p = p.firstChild();
Node n = new Node(number);
p.addchild(new TreeNode(n));
n.main(p);
GF.yourMove(Pos != p);
if (Pos == p)
{
getinformation();
int c = P.color();
goforward();
P.color( -c);
showinformation();
GF.addComment(GF.resourceString("Pass"));
}
MainColor = -MainColor;
captured = 0;
}
public void resume ()
// Resume playing after marking
{
getinformation();
State = 1;
showinformation();
}
Node newtree ()
{
number = 1;
Pw = Pb = 0;
Node n = new Node(number);
T = new SGFTree(n);
Trees.setElementAt(T, CurrentTree);
resettree();
return n;
}
void resettree ()
{
Pos = T.top();
P = new Position(S);
lasti = lastj = -1;
Pb = Pw = 0;
updateall();
copy();
}
public boolean deltree ()
{
newtree();
return true;
}
public void active (boolean f)
{
Active = f;
}
public int getboardsize ()
{
return S;
}
public boolean canfinish ()
{
return Pos.isLastMain();
}
public int maincolor ()
{
return MainColor;
}
public boolean ismain ()
{
return Pos.isLastMain();
}
Node firstnode ()
{
return T.top().node();
}
boolean valid (int i, int j)
{
return i >= 0 && i < S && j >= 0 && j < S;
}
public void clearrange ()
{
if (Range == -1) return;
Range = -1;
updateall();
copy();
}
// *****************************************
// Methods to be called from outside sources
// *****************************************
// ****** navigational things **************
// methods to move in the game tree, including
// update of the visible board:
public synchronized void back ()
// one move up
{
State = 1;
getinformation();
goback();
showinformation();
copy();
}
public synchronized void forward ()
// one move down
{
State = 1;
getinformation();
goforward();
showinformation();
copy();
}
public synchronized void fastback ()
// 10 moves up
{
getinformation();
for (int i = 0; i < 10; i++)
goback();
showinformation();
copy();
}
public synchronized void fastforward ()
// 10 moves down
{
getinformation();
for (int i = 0; i < 10; i++)
goforward();
showinformation();
copy();
}
public synchronized void allback ()
// to top of tree
{
getinformation();
while (Pos.parentPos() != null)
goback();
showinformation();
copy();
}
public synchronized void allforward ()
// to end of variation
{
getinformation();
while (Pos.haschildren())
goforward();
showinformation();
copy();
}
public synchronized void varleft ()
// one variation to the left
{
State = 1;
getinformation();
ListElement l = Pos.listelement();
if (l == null) return;
if (l.previous() == null) return;
TreeNode newpos = (TreeNode)l.previous().content();
goback();
Pos = newpos;
setnode();
showinformation();
copy();
}
public synchronized void varright ()
// one variation to the right
{
State = 1;
getinformation();
ListElement l = Pos.listelement();
if (l == null) return;
if (l.next() == null) return;
TreeNode newpos = (TreeNode)l.next().content();
goback();
Pos = newpos;
setnode();
showinformation();
copy();
}
public synchronized void varmain ()
// to the main variation
{
State = 1;
getinformation();
while (Pos.parentPos() != null && !Pos.node().main())
{
goback();
}
if (Pos.haschildren()) goforward();
showinformation();
copy();
}
public synchronized void varmaindown ()
// to end of main variation
{
State = 1;
getinformation();
while (Pos.parentPos() != null && !Pos.node().main())
{
goback();
}
while (Pos.haschildren())
{
goforward();
}
showinformation();
copy();
}
public synchronized void varup ()
// to the start of the variation
{
State = 1;
getinformation();
if (Pos.parentPos() != null) goback();
while (Pos.parentPos() != null
&& Pos.parentPos().firstChild() == Pos.parentPos().lastChild()
&& !Pos.node().main())
{
goback();
}
showinformation();
copy();
}
public synchronized void gotonext ()
// goto next named node
{
State = 1;
getinformation();
goforward();
while (Pos.node().getaction("N").equals(""))
{
if ( !Pos.haschildren()) break;
goforward();
}
showinformation();
copy();
}
public synchronized void gotoprevious ()
// gotoprevious named node
{
State = 1;
getinformation();
goback();
while (Pos.node().getaction("N").equals(""))
{
if (Pos.parentPos() == null) break;
goback();
}
showinformation();
copy();
}
public synchronized void gotonextmain ()
// goto next game tree
{
if (CurrentTree + 1 >= Trees.size()) return;
State = 1;
getinformation();
T.top().setaction("AP", "Jago:" + GF.version(), true);
T.top().setaction("SZ", "" + S, true);
T.top().setaction("GM", "1", true);
T.top()
.setaction("FF", GF.getParameter("puresgf", false)?"4":"1", true);
CurrentTree++;
T = (SGFTree)Trees.elementAt(CurrentTree);
resettree();
setnode();
showinformation();
copy();
}
public synchronized void gotopreviousmain ()
// goto previous game tree
{
if (CurrentTree == 0) return;
State = 1;
getinformation();
T.top().setaction("AP", "Jago:" + GF.version(), true);
T.top().setaction("SZ", "" + S, true);
T.top().setaction("GM", "1", true);
T.top()
.setaction("FF", GF.getParameter("puresgf", false)?"4":"1", true);
CurrentTree--;
T = (SGFTree)Trees.elementAt(CurrentTree);
resettree();
setnode();
showinformation();
copy();
}
public synchronized void addnewgame ()
{
State = 1;
getinformation();
T.top().setaction("AP", "Jago:" + GF.version(), true);
T.top().setaction("SZ", "" + S, true);
T.top().setaction("GM", "1", true);
T.top()
.setaction("FF", GF.getParameter("puresgf", false)?"4":"1", true);
Node n = new Node(number);
T = new SGFTree(n);
CurrentTree++;
if (CurrentTree >= Trees.size())
Trees.addElement(T);
else Trees.insertElementAt(T, CurrentTree);
resettree();
setnode();
showinformation();
copy();
}
public synchronized void removegame ()
{
if (Trees.size() == 1) return;
Trees.removeElementAt(CurrentTree);
if (CurrentTree >= Trees.size()) CurrentTree--;
T = (SGFTree)Trees.elementAt(CurrentTree);
resettree();
setnode();
showinformation();
copy();
}
// ***** change the node at end of main tree ********
// usually called by another board or server
public synchronized void black (int i, int j)
// white move at i,j at the end of the main tree
{
if (i < 0 || j < 0 || i >= S || j >= S) return;
TreeNode p = T.top();
while (p.haschildren())
p = p.firstChild();
Action a = new Action("B", Field.string(i, j));
Node n = new Node(p.node().number() + 1);
n.addaction(a);
p.addchild(new TreeNode(n));
n.main(p);
GF.yourMove(Pos != p);
if (Pos == p) forward();
MainColor = -1;
}
public synchronized void white (int i, int j)
// black move at i,j at the end of the main tree
{
if (i < 0 || j < 0 || i >= S || j >= S) return;
TreeNode p = T.top();
while (p.haschildren())
p = p.firstChild();
Action a = new Action("W", Field.string(i, j));
Node n = new Node(p.node().number() + 1);
n.addaction(a);
p.addchild(new TreeNode(n));
n.main(p);
GF.yourMove(Pos != p);
if (Pos == p) forward();
MainColor = 1;
}
public synchronized void setblack (int i, int j)
// set a white stone at i,j at the end of the main tree
{
if (i < 0 || j < 0 || i >= S || j >= S) return;
TreeNode p = T.top();
while (p.haschildren())
p = p.firstChild();
Action a = new Action("AB", Field.string(i, j));
Node n;
if (p == T.top())
{
TreeNode newpos;
p.addchild(newpos = new TreeNode(1));
if (Pos == p) Pos = newpos;
p = newpos;
p.main(true);
}
n = p.node();
n.expandaction(a);
if (Pos == p)
{
n.addchange(new Change(i, j, P.color(i, j), P.number(i, j)));
P.color(i, j, 1);
update(i, j);
copy();
}
MainColor = -1;
}
public synchronized void setwhite (int i, int j)
// set a white stone at i,j at the end of the main tree
{
if (i < 0 || j < 0 || i >= S || j >= S) return;
TreeNode p = T.top();
while (p.haschildren())
p = p.firstChild();
Action a = new Action("AW", Field.string(i, j));
Node n;
if (p == T.top())
{
TreeNode newpos;
p.addchild(newpos = new TreeNode(1));
if (Pos == p) Pos = newpos;
p = newpos;
p.main(true);
}
n = p.node();
n.expandaction(a);
if (Pos == p)
{
n.addchange(new Change(i, j, P.color(i, j), P.number(i, j)));
P.color(i, j, -1);
update(i, j);
copy();
}
MainColor = 1;
}
public synchronized void pass ()
// pass at current node
// notify BoardInterface
{
if (Pos.haschildren()) return;
if (GF.blocked() && Pos.node().main()) return;
getinformation();
P.color( -P.color());
Node n = new Node(number);
Pos.addchild(new TreeNode(n));
n.main(Pos);
goforward();
setlast();
showinformation();
copy();
GF.addComment(GF.resourceString("Pass"));
captured = 0;
}
public synchronized void remove (int i0, int j0)
// completely remove a group there
{
int s = State;
varmaindown();
State = s;
if (P.color(i0, j0) == 0) return;
Action a;
P.markgroup(i0, j0);
int i, j;
int c = P.color(i0, j0);
Node n = Pos.node();
if (GF.getParameter("puresgf", true)
&& (n.contains("B") || n.contains("W"))) n = newnode();
for (i = 0; i < S; i++)
for (j = 0; j < S; j++)
{
if (P.marked(i, j))
{
a = new Action("AE", Field.string(i, j));
n
.addchange(new Change(i, j, P.color(i, j), P.number(i,
j)));
n.expandaction(a);
if (P.color(i, j) > 0)
{
n.Pb++;
Pb++;
}
else
{
n.Pw++;
Pw++;
}
P.color(i, j, 0);
update(i, j);
}
}
copy();
}
// ************ change the current node *****************
public void clearmarks ()
// clear all marks in the current node
{
getinformation();
undonode();
ListElement la = Pos.node().actions(), lan;
Action a;
while (la != null)
{
a = (Action)la.content();
lan = la.next();
if (a.type().equals("M") || a.type().equals("L")
|| a.type().equals("MA") || a.type().equals("SQ")
|| a.type().equals("SL") || a.type().equals("CR")
|| a.type().equals("TR") || a.type().equals("LB"))
{
Pos.node().removeaction(la);
}
la = lan;
}
setnode();
showinformation();
copy();
}
public void clearremovals ()
// undo all removals in the current node
{
if (Pos.haschildren()) return;
getinformation();
undonode();
ListElement la = Pos.node().actions(), lan;
Action a;
while (la != null)
{
a = (Action)la.content();
lan = la.next();
if (a.type().equals("AB") || a.type().equals("AW")
|| a.type().equals("AE"))
{
Pos.node().removeaction(la);
}
la = lan;
}
setnode();
showinformation();
copy();
}
// *************** change the game tree ***********
public void insertnode ()
// insert an empty node
{
if (Pos.haschildren() && !GF.askInsert()) return;
Node n = new Node(Pos.node().number());
Pos.insertchild(new TreeNode(n));
n.main(Pos);
getinformation();
Pos = Pos.lastChild();
setlast();
showinformation();
copy();
}
public void insertvariation ()
// insert an empty variation to the current
{
if (Pos.parentPos() == null) return;
getinformation();
int c = P.color();
back();
Node n = new Node(2);
Pos.addchild(new TreeNode(n));
n.main(Pos);
Pos = Pos.lastChild();
setlast();
P.color( -c);
showinformation();
copy();
}
public void undo (int n)
// undo the n last moves, notify BoardInterface
{
varmaindown();
for (int i = 0; i < n; i++)
{
goback();
Pos.removeall();
showinformation();
copy();
}
GF.addComment(GF.resourceString("Undo"));
}
// ********** set board state ******************
public void setblack ()
// set black mode
{
getinformation();
State = 3;
showinformation();
}
public void setwhite ()
// set white mode
{
getinformation();
State = 4;
showinformation();
}
public void black ()
// black to play
{
getinformation();
State = 1;
P.color(1);
showinformation();
}
public void white ()
// white to play
{
getinformation();
State = 2;
P.color( -1);
showinformation();
}
public void mark ()
// marking
{
getinformation();
State = 5;
showinformation();
}
public void specialmark (int i)
// marking
{
getinformation();
State = 9;
SpecialMarker = i;
showinformation();
}
public void textmark (String s)
// marking
{
getinformation();
State = 10;
TextMarker = s;
showinformation();
}
public void letter ()
// letter
{
getinformation();
State = 6;
showinformation();
}
public void deletestones ()
// hide stones
{
getinformation();
State = 7;
showinformation();
}
public boolean score ()
// board state is removing groups
{
if (Pos.haschildren()) return false;
getinformation();
State = 8;
Removing = true;
showinformation();
if (Pos.node().main())
return true;
else return false;
}
synchronized public void setsize (int s)
// set the board size
// clears the board !!!
{
if (s < 5 || s > 59) return;
S = s;
P = new Position(S);
number = 1;
Node n = new Node(number);
n.main(true);
lasti = lastj = -1;
T = new SGFTree(n);
Trees.setElementAt(T, CurrentTree);
Pos = T.top();
showinformation();
copy();
}
// ******** set board information **********
void setname (String s)
// set the name of the node
{
Pos.setaction("N", s, true);
showinformation();
}
public void setinformation (String black, String blackrank, String white,
String whiterank, String komi, String handicap)
// set various things like names, rank etc.
{
T.top().setaction("PB", black, true);
T.top().setaction("BR", blackrank, true);
T.top().setaction("PW", white, true);
T.top().setaction("WR", whiterank, true);
T.top().setaction("KM", komi, true);
T.top().setaction("HA", handicap, true);
T.top().setaction("GN", white + "-" + black, true);
T.top().setaction("DT", new Date().toString());
}
// ************ get board information ******
String getname ()
// get node name
{
return T.top().getaction("N");
}
public String getKomi ()
// get Komi string
{
return T.top().getaction("KM");
}
public String extraInformation ()
// get a mixture from handicap, komi and prisoners
{
StringBuffer b = new StringBuffer(GF.resourceString("_("));
Node n = T.top().node();
if (n.contains("HA"))
{
b.append(GF.resourceString("Ha_"));
b.append(n.getaction("HA"));
}
if (n.contains("KM"))
{
b.append(GF.resourceString("__Ko"));
b.append(n.getaction("KM"));
}
b.append(GF.resourceString("__B"));
b.append("" + Pw);
b.append(GF.resourceString("__W"));
b.append("" + Pb);
b.append(GF.resourceString("_)"));
return b.toString();
}
// ***************** several other things ******
public void lastrange (int n)
// set the range for stone numbers
{
int l = Pos.node().number() - 2;
Range = l / n * n;
if (Range < 0) Range = 0;
KeepRange = true;
updateall();
copy();
KeepRange = false;
}
public void addcomment (String s)
// add a string to the comments, notifies comment area
{
TreeNode p = T.top();
while (p.haschildren())
p = p.firstChild();
if (Pos == p) getinformation();
ListElement la = p.node().actions();
Action a;
String Added = "";
ListElement larg;
outer: while (true)
{
while (la != null)
{
a = (Action)la.content();
if (a.type().equals("C"))
{
larg = a.arguments();
if (((String)larg.content()).equals(""))
{
larg.content(s);
Added = s;
}
else
{
larg.content((String)larg.content() + "\n" + s);
Added = "\n" + s;
}
break outer;
}
la = la.next();
}
p.addaction(new Action("C", s));
break;
}
if (Pos == p)
{
GF.appendComment(Added);
showinformation();
}
}
public String done ()
// count territory and return result string
// notifies BoardInterface
{
if (Pos.haschildren()) return null;
clearmarks();
getinformation();
int i, j;
int tb = 0, tw = 0, sb = 0, sw = 0;
P.getterritory();
for (i = 0; i < S; i++)
for (j = 0; j < S; j++)
{
if (P.territory(i, j) == 1 || P.territory(i, j) == -1)
{
markterritory(i, j, P.territory(i, j));
if (P.territory(i, j) > 0)
tb++;
else tw++;
}
else
{
if (P.color(i, j) > 0)
sb++;
else if (P.color(i, j) < 0) sw++;
}
}
String s = GF.resourceString("Chinese_count_") + "\n"
+ GF.resourceString("Black__") + (sb + tb)
+ GF.resourceString("__White__") + (sw + tw) + "\n"
+ GF.resourceString("Japanese_count_") + "\n"
+ GF.resourceString("Black__") + (Pw + tb)
+ GF.resourceString("__White__") + (Pb + tw);
showinformation();
copy();
if (Pos.node().main())
{
GF.result(tb, tw);
}
return s;
}
public String docount ()
// maka a local count and return result string
{
clearmarks();
getinformation();
int i, j;
int tb = 0, tw = 0, sb = 0, sw = 0;
P.getterritory();
for (i = 0; i < S; i++)
for (j = 0; j < S; j++)
{
if (P.territory(i, j) == 1 || P.territory(i, j) == -1)
{
markterritory(i, j, P.territory(i, j));
if (P.territory(i, j) > 0)
tb++;
else tw++;
}
else
{
if (P.color(i, j) > 0)
sb++;
else if (P.color(i, j) < 0) sw++;
}
}
showinformation();
copy();
return GF.resourceString("Chinese_count_") + "\n"
+ GF.resourceString("Black__") + (sb + tb)
+ GF.resourceString("__White__") + (sw + tw) + "\n"
+ GF.resourceString("Japanese_count_") + "\n"
+ GF.resourceString("Black__") + (Pw + tb)
+ GF.resourceString("__White__") + (Pb + tw);
}
public void load (BufferedReader in) throws IOException
// load a game from the stream
{
Vector v = SGFTree.load(in, GF);
synchronized (this)
{
if (v.size() == 0) return;
showlast = false;
update(lasti, lastj);
showlast = true;
lasti = lastj = -1;
newtree();
Trees = v;
T = (SGFTree)v.elementAt(0);
CurrentTree = 0;
resettree();
setnode();
showinformation();
copy();
}
}
public void loadXml (XmlReader xml) throws XmlReaderException
// load a game from the stream
{
Vector v = SGFTree.load(xml, GF);
synchronized (this)
{
if (v.size() == 0) return;
showlast = false;
update(lasti, lastj);
showlast = true;
lasti = lastj = -1;
newtree();
Trees = v;
T = (SGFTree)v.elementAt(0);
CurrentTree = 0;
resettree();
setnode();
showinformation();
copy();
}
}
public void save (PrintWriter o)
// saves the file to the specified print stream
// in SGF
{
getinformation();
T.top().setaction("AP", "Jago:" + GF.version(), true);
T.top().setaction("SZ", "" + S, true);
T.top().setaction("GM", "1", true);
T.top()
.setaction("FF", GF.getParameter("puresgf", false)?"4":"1", true);
for (int i = 0; i < Trees.size(); i++)
((SGFTree)Trees.elementAt(i)).print(o);
}
public void savePos (PrintWriter o)
// saves the file to the specified print stream
// in SGF
{
getinformation();
Node n = new Node(0);
positionToNode(n);
o.println("(");
n.print(o);
o.println(")");
}
public void saveXML (PrintWriter o, String encoding)
// save the file in Jago's XML format
{
getinformation();
T.top().setaction("AP", "Jago:" + GF.version(), true);
T.top().setaction("SZ", "" + S, true);
T.top().setaction("GM", "1", true);
T.top()
.setaction("FF", GF.getParameter("puresgf", false)?"4":"1", true);
XmlWriter xml = new XmlWriter(o);
xml.printEncoding(encoding);
xml.printXls("go.xsl");
xml.printDoctype("Go", "go.dtd");
xml.startTagNewLine("Go");
for (int i = 0; i < Trees.size(); i++)
{
((SGFTree)Trees.elementAt(i)).printXML(xml);
}
xml.endTagNewLine("Go");
}
public void saveXMLPos (PrintWriter o, String encoding)
// save the file in Jago's XML format
{
getinformation();
T.top().setaction("AP", "Jago:" + GF.version(), true);
T.top().setaction("SZ", "" + S, true);
T.top().setaction("GM", "1", true);
T.top()
.setaction("FF", GF.getParameter("puresgf", false)?"4":"1", true);
XmlWriter xml = new XmlWriter(o);
xml.printEncoding(encoding);
xml.printXls("go.xsl");
xml.printDoctype("Go", "go.dtd");
xml.startTagNewLine("Go");
Node n = new Node(0);
positionToNode(n);
SGFTree t = new SGFTree(n);
t.printXML(xml);
xml.endTagNewLine("Go");
}
public void asciisave (PrintWriter o)
// an ASCII image of the board.
{
int i, j;
o.println(T.top().getaction("GN"));
o.print(" ");
for (i = 0; i < S; i++)
{
char a;
if (i <= 7)
a = (char)('A' + i);
else a = (char)('A' + i + 1);
o.print(" " + a);
}
o.println();
o.print(" ");
for (i = 0; i < S; i++)
o.print("--");
o.println("-");
for (i = 0; i < S; i++)
{
o.print(" ");
if (S - i < 10)
o.print(" " + (S - i));
else o.print(S - i);
o.print(" |");
for (j = 0; j < S; j++)
{
switch (P.color(j, i))
{
case 1:
o.print(" #");
break;
case -1:
o.print(" O");
break;
case 0:
if (P.haslabel(j, i))
o.print(" " + P.label(j, i));
else if (P.letter(j, i) > 0)
o.print(" " + (char)(P.letter(j, i) + 'a' - 1));
else if (P.marker(j, i) > 0)
o.print(" X");
else if (ishand(i) && ishand(j))
o.print(" ,");
else o.print(" .");
break;
}
}
o.print(" | ");
if (S - i < 10)
o.print(" " + (S - i));
else o.print(S - i);
o.println();
}
o.print(" ");
for (i = 0; i < S; i++)
o.print("--");
o.println("-");
o.print(" ");
for (i = 0; i < S; i++)
{
char a;
if (i <= 7)
a = (char)('A' + i);
else a = (char)('A' + i + 1);
o.print(" " + a);
}
o.println();
}
public void positionToNode (Node n)
// copy the current position to a node.
{
n.setaction("AP", "Jago:" + GF.version(), true);
n.setaction("SZ", "" + S, true);
n.setaction("GM", "1", true);
n.setaction("FF", GF.getParameter("puresgf", false)?"4":"1", true);
n.copyAction(T.top().node(), "GN");
n.copyAction(T.top().node(), "DT");
n.copyAction(T.top().node(), "PB");
n.copyAction(T.top().node(), "BR");
n.copyAction(T.top().node(), "PW");
n.copyAction(T.top().node(), "WR");
n.copyAction(T.top().node(), "PW");
n.copyAction(T.top().node(), "US");
n.copyAction(T.top().node(), "CP");
int i, j;
for (i = 0; i < S; i++)
{
for (j = 0; j < S; j++)
{
String field = Field.string(i, j);
switch (P.color(i, j))
{
case 1:
n.expandaction(new Action("AB", field));
break;
case -1:
n.expandaction(new Action("AW", field));
break;
}
if (P.marker(i, j) > 0)
{
switch (P.marker(i, j))
{
case Field.SQUARE:
n.expandaction(new Action("SQ", field));
break;
case Field.TRIANGLE:
n.expandaction(new Action("TR", field));
break;
case Field.CIRCLE:
n.expandaction(new Action("CR", field));
break;
default:
n.expandaction(new MarkAction(field, GF));
}
}
else if (P.haslabel(i, j))
n
.expandaction(new Action("LB", field + ":"
+ P.label(i, j)));
else if (P.letter(i, j) > 0)
n.expandaction(new Action("LB", field + ":"
+ P.letter(i, j)));
}
}
}
boolean ishand (int i)
{
if (S > 13)
{
return i == 3 || i == S - 4 || i == S / 2;
}
else if (S > 9)
{
return i == 3 || i == S - 4;
}
else return false;
}
public void handicap (int n)
// set number of handicap points
{
int h = S < 13?3:4;
if (n > 5)
{
setblack(h - 1, S / 2);
setblack(S - h, S / 2);
}
if (n > 7)
{
setblack(S / 2, h - 1);
setblack(S / 2, S - h);
}
switch (n)
{
case 9:
case 7:
case 5:
setblack(S / 2, S / 2);
case 8:
case 6:
case 4:
setblack(S - h, S - h);
case 3:
setblack(h - 1, h - 1);
case 2:
setblack(h - 1, S - h);
case 1:
setblack(S - h, h - 1);
}
MainColor = -1;
}
public void updateall ()
// update all of the board
{
}
public void updateboard ()
// redraw the board and its background
{
}
/**
* Search the string as substring of a comment, go to that node and report
* success. On failure this routine will go up to the root node.
*/
public boolean search (String s)
{
State = 1;
getinformation();
TreeNode pos = Pos;
boolean found = true;
outer: while (Pos.node().getaction("C").indexOf(s) < 0 || Pos == pos)
{
if ( !Pos.haschildren())
{
while ( !hasvariation())
{
if (Pos.parent() == null)
{
found = false;
break outer;
}
else goback();
}
tovarright();
}
else goforward();
}
showinformation();
copy();
return found;
}
// *****************************************************
// procedures that might be overloaded for more control
// (Callback to server etc.)
// *****************************************************
void movemouse (int i, int j)
// set a move at i,j
{
if (Pos.haschildren()) return;
if (captured == 1 && capturei == i && capturej == j
&& GF.getParameter("preventko", true)) return;
set(i, j); // try to set a new move
}
void setmouse (int i, int j, int c)
// set a stone at i,j with specified color
{
set(i, j, c);
undonode();
setnode();
showinformation();
}
void setmousec (int i, int j, int c)
// set a stone at i,j with specified color
{
setc(i, j, c);
undonode();
setnode();
showinformation();
}
void deletemouse (int i, int j)
// delete a stone at i,j
{
if (Pos.haschildren()) return;
deletemousec(i, j);
}
void deletemousec (int i, int j)
// delete a stone at i,j
{
delete(i, j);
undonode();
setnode();
showinformation();
}
void removemouse (int i, int j)
// remove a group at i,j
{
if (Pos.haschildren()) return;
removegroup(i, j);
undonode();
setnode();
showinformation();
}
void setVariationStyle (boolean hide, boolean current)
{
undonode();
VHide = hide;
VCurrent = current;
setnode();
updateall();
copy();
}
}