// Game.java
package net.sf.gogui.game;
import net.sf.gogui.go.ConstBoard;
import net.sf.gogui.go.ConstPointList;
import net.sf.gogui.go.Board;
import net.sf.gogui.go.GoColor;
import static net.sf.gogui.go.GoColor.EMPTY;
import net.sf.gogui.go.GoPoint;
import net.sf.gogui.go.Komi;
import net.sf.gogui.go.Move;
import net.sf.gogui.util.ObjectUtil;
/** Manages a tree, board, current node and clock. */
public class Game
implements ConstGame
{
public Game(int boardSize)
{
this(boardSize, null, null, "", null);
}
public Game(int boardSize, Komi komi, ConstPointList handicap,
String rules, TimeSettings timeSettings)
{
m_board = new Board(boardSize);
m_clock = new Clock();
init(boardSize, komi, handicap, rules, timeSettings);
}
public Game(GameTree tree)
{
int boardSize = tree.getBoardSize();
m_board = new Board(boardSize);
m_clock = new Clock();
init(tree);
}
/** Add a mark property to current node. */
public void addMarked(GoPoint point, MarkType type)
{
m_current.addMarked(point, type);
setModified();
}
/** Clear modified flag.
Can be used for instance after game was saved.
@see #isModified() */
public void clearModified()
{
m_modified = false;
}
/** Append new empty node and make it current node.
Can be use for instance to prepare for setup stones if current node
contains a move. */
public void createNewChild()
{
Node node = new Node();
m_current.append(node);
m_current = node;
setModified();
}
public ConstBoard getBoard()
{
return m_board;
}
public ConstClock getClock()
{
return m_clock;
}
public ConstNode getCurrentNode()
{
return m_current;
}
public ConstGameInfo getGameInfo(ConstNode node)
{
return m_tree.getGameInfoConst(node);
}
public ConstNode getGameInfoNode()
{
return m_tree.getGameInfoNode(m_current);
}
public ConstNode getGameInfoNode(ConstNode node)
{
return m_tree.getGameInfoNode(node);
}
public int getMoveNumber()
{
return NodeUtil.getMoveNumber(getCurrentNode());
}
public ConstNode getRoot()
{
return m_tree.getRoot();
}
public int getSize()
{
return m_board.getSize();
}
public GoColor getToMove()
{
return m_board.getToMove();
}
public ConstGameTree getTree()
{
return m_tree;
}
public void gotoNode(ConstNode node)
{
assert node != null;
assert NodeUtil.getRoot(node) == getRoot();
m_current = (Node)node;
updateBoard();
}
public void haltClock()
{
m_clock.halt();
}
/** Return last node when startClock() was called.
@return The last such node or null, if the clock was never running. */
public ConstNode getClockNode()
{
return m_clockNode;
}
public final void init(int boardSize, Komi komi, ConstPointList handicap,
String rules, TimeSettings timeSettings)
{
init(new GameTree(boardSize, komi, handicap, rules, timeSettings));
}
public final void init(GameTree tree)
{
m_tree = tree;
m_current = m_tree.getRoot();
updateBoard();
updateClock();
m_clock.reset();
m_clock.halt();
m_modified = false;
m_clockNode = null;
}
/** Check if game was modified.
@return true, if game was mofified since constructor or last call to
one of the init() functions or to clearModified(). */
public boolean isModified()
{
return m_modified;
}
public void keepOnlyMainVariation()
{
m_tree.keepOnlyMainVariation();
setModified();
}
public void keepOnlyPosition()
{
ConstGameInfo info = getGameInfo(m_current);
m_tree = NodeUtil.makeTreeFromPosition(info, m_board);
m_board.init(m_board.getSize());
m_current = m_tree.getRoot();
updateBoard();
setModified();
}
/** Make current node the main variation. */
public void makeMainVariation()
{
NodeUtil.makeMainVariation(m_current);
setModified();
}
public void play(Move move)
{
m_clock.stopMove();
Node node = new Node(move);
GoColor color = move.getColor();
// Only save time left information, if it also was known before the move
if (m_clock.isInitialized()
&& NodeUtil.isTimeLeftKnown(m_current, color))
{
assert ! m_clock.isRunning();
// Round time to seconds
long timeLeft = m_clock.getTimeLeft(color) / 1000L;
node.setTimeLeft(color, (double)timeLeft);
if (m_clock.isInByoyomi(color))
node.setMovesLeft(color, m_clock.getMovesLeft(color));
}
m_current.append(node);
m_current = node;
updateBoard();
setModified();
m_clock.startMove(getToMove());
}
/** Remove a mark property from current node. */
public void removeMarked(GoPoint point, MarkType type)
{
m_current.removeMarked(point, type);
setModified();
}
public void resetClock()
{
m_clock.reset();
}
public void restoreClock()
{
if (! m_clock.isInitialized())
return;
NodeUtil.restoreClock(getCurrentNode(), m_clock);
}
public void resumeClock()
{
m_clock.resume();
}
/** Set clock listener.
If the clock has a listener, the clock should be stopped with
haltClock() if it is no longer used, otherwise the timer thread can
keep an application from terminating. */
public void setClockListener(Clock.Listener listener)
{
m_clock.setListener(listener);
}
/** Set comment in current node. */
public void setComment(String comment)
{
setComment(comment, m_current);
}
public void setComment(String comment, ConstNode node)
{
assert NodeUtil.getRoot(node) == getRoot();
if (! ObjectUtil.equals(comment, node.getComment()))
setModified();
((Node)node).setComment(comment);
}
public void setGameInfo(ConstGameInfo info, ConstNode node)
{
assert NodeUtil.getRoot(node) == getRoot();
((Node)node).createGameInfo();
if (! ((Node)node).getGameInfo().equals(info))
{
((Node)node).getGameInfo().copyFrom(info);
updateClock();
setModified();
}
}
public void setKomi(Komi komi)
{
Node node = m_tree.getGameInfoNode(m_current);
GameInfo info = node.getGameInfo();
info.setKomi(komi);
setGameInfo(info, node); // updates m_modified
}
/** Set label in current node. */
public void setLabel(GoPoint point, String value)
{
if (! ObjectUtil.equals(value, m_current.getLabel(point)))
setModified();
m_current.setLabel(point, value);
}
public void setPlayer(GoColor c, String name)
{
Node node = m_tree.getGameInfoNode(m_current);
GameInfo info = node.getGameInfo();
info.set(StringInfoColor.NAME, c, name);
setGameInfo(info, node); // updates m_modified
}
public void setResult(String result)
{
Node node = m_tree.getGameInfoNode(m_current);
GameInfo info = node.getGameInfo();
info.set(StringInfo.RESULT, result);
setGameInfo(info, node); // updates m_modified
}
public void setToMove(GoColor color)
{
assert color != null;
assert ! color.equals(EMPTY);
if (! ObjectUtil.equals(color, m_current.getPlayer())
|| color.equals(m_board.getToMove()))
setModified();
m_current.setPlayer(color);
updateBoard();
}
/** Change the time settings.
If the current node is the root node, the clock will also be reset. */
public void setTimeSettings(TimeSettings timeSettings)
{
Node node = m_tree.getGameInfoNode(m_current);
GameInfo info = node.getGameInfo();
info.setTimeSettings(timeSettings);
setGameInfo(info, node); // updates m_modified
m_clock.setTimeSettings(timeSettings);
if (m_current == node)
m_clock.reset();
}
public void setTimeLeft(GoColor c, double seconds)
{
setTimeLeft(getCurrentNode(), c, seconds);
}
public void setTimeLeft(ConstNode node, GoColor c, double seconds)
{
((Node)node).setTimeLeft(c, seconds);
}
public void setMovesLeft(GoColor c, int moves)
{
setMovesLeft(getCurrentNode(), c, moves);
}
public void setMovesLeft(ConstNode node, GoColor c, int moves)
{
((Node)node).setMovesLeft(c, moves);
}
/** Set a stone on the board or remove a stone.
@param p The location.
@param c The color of the stone (EMPTY for removal). */
public void setup(GoPoint p, GoColor c)
{
assert p != null;
m_current.removeSetup(p);
Node father = m_current.getFather();
if (father != null)
{
m_boardUpdater.update(getTree(), father, m_board);
GoColor oldColor = m_board.getColor(p);
if (oldColor == c)
{
updateBoard();
return;
}
}
if (c != EMPTY || father != null)
m_current.addStone(c, p);
setModified();
updateBoard();
}
public void startClock()
{
m_clock.startMove(getToMove());
m_clockNode = m_current;
}
/** Truncate current node and subtree.
New current node is the father of the old current node. */
public void truncate()
{
Node father = m_current.getFather();
assert father != null;
Node oldCurrentNode = m_current;
m_current = father;
m_current.removeChild(oldCurrentNode);
setModified();
}
/** Remove children of currentNode. */
public void truncateChildren()
{
NodeUtil.truncateChildren(m_current);
setModified();
}
/** See #isModified() */
private boolean m_modified;
private final Board m_board;
private final BoardUpdater m_boardUpdater = new BoardUpdater();
private GameTree m_tree;
private Node m_current;
/** See getClockNode() */
private ConstNode m_clockNode;
private final Clock m_clock;
private void setModified()
{
m_modified = true;
}
private void updateBoard()
{
m_boardUpdater.update(m_tree, m_current, m_board);
}
private void updateClock()
{
ConstNode node = getGameInfoNode();
ConstGameInfo info = node.getGameInfoConst();
if (info != null)
m_clock.setTimeSettings(info.getTimeSettings());
}
}