📄 game.java
字号:
package ergo.logic;
// $Id: Game.java,v 1.4 1999/08/13 01:17:48 sigue Exp $
/*
* Copyright (C) 1999 Carl L. Gay and Antranig M. Basman.
* See the file copyright.txt, distributed with this software,
* for further information.
*/
import ergo.Ergo;
import ergo.util.*;
import ergo.server.GoServer;
import java.util.Calendar;
import java.util.Date;
import java.util.Vector;
import java.io.PrintWriter;
// +++ Should be able to get rid of these imports...
import ergo.ui.GameWindow;
import ergo.ui.TerminalWindow;
/**
* A Game represents the actual Go game, separated from any idea of how
* the game may be displayed on the screen. It can be saved to a Smart
* Go Format file.
* <P>
* Stuctural note - we have strong assumptions in the code, that
* every Game has exactly one associated GameWindow. This has no
* particular effect, since the purpose of the code is to currently
* present games to a single viewer. In case we allow multiple
* views of same games, as requested by study, this might need
* quite a bit of upheaval. AMB at 0.7
* <P>
* I doubt it. We can just copy the game. -sigue
* Actually, I don't think the assumptions are very strong. I made
* three small changes and now the GameWindow associated with the
* Game may be null. This is useful when loading an SGF file. -sigue
*
* @version $Revision: 1.4 $ - $Date: 1999/08/13 01:17:48 $
*/
public class Game {
private GameWindow window = null; // may remain null.
// compromise - this is public, since many, many people may wish to
// read stones, cause emissions, etc. and it would be a waste, given
// our admirably small number of classes, just to create another
// interface class for the sake of good design :)
public StoneStasher ss;
private SimpleGroup[][] groupArray;
// root is the root of the game tree. The root move only exists
// to hold variations on the first move. The root doesn't get
// displayed on the screen as such, but it may have children and
// kibitzes which get displayed when it is the current move.
private RootMove root = new RootMove();
// CurrentMove is the move that has an X displayed on it on the board.
// It may also == root, in which case there is no X but there may
// be variation letters displayed. At no time should currentMove ever
// be null.
// The board (ss + groupArray) should always be consistent with
// current.
private Move currentMove = root; // write to this ONLY with setCurrentMove()
// scoringRules can be 'J' = Japanese, 'C' = Chinese, or 'I' = IGS.
// +++ Currently this is always 'J', though 'C' is supported.
private char scoringRules = 'J';
private boolean scoringMode = false;
private boolean scoringLocally = false;
// The move where scoring mode was entered. Exit scoring mode if
// the user backs up past this move. Backup to this move if the
// user exits local scoring mode.
private Move scoringEntry = null;
// This little beauty is to make sure that if the user replays a
// variation immediatly after entering scoring mode we don't delete
// that variation when scoring mode is exited. Zowie.
private boolean scoringEntryFollowedByNewMove = false;
// The following three are only meaningful when scoringMode is true.
// They hold the calculated score, which will be totally wrong until
// all territory (except dame) is enclosed by one color or another.
// These numbers don't include captured stones or komi.
private int blackTerritory = 0;
private int whiteTerritory = 0;
private int dame = 0;
private Date startTime = new Date(); // initializes to current time
private int size = 0; // size >= 1 when initialized.
public String blackName = "";
public String blackRank = "";
public String whiteName = "";
public String whiteRank = "";
public String SGFlocation = null;
private double komi = 5.5;
private GoServer server = null; // server this game was played on (if any)
private int capturedWhite = 0;
private int capturedBlack = 0;
private Position koPosition = null;
String result = null;
private int byotime = 0; // byo-yomi time in minutes
private boolean isFree = false;
// A vector of SGF properties and their values that should be saved
// when this game is saved. This is normally null unless the game
// was loaded from a file, in which case unhandled root and game-info
// properties are stored here.
protected Vector unhandledSGFproperties = null;
public Game (GameWindow gwin, int size, String white, String wrank,
String black, String brank, double komi) {
window = gwin;
whiteName = white;
whiteRank = wrank;
blackName = black;
blackRank = brank;
this.komi = komi;
this.size = size;
init();
}
public Game (GameWindow gwin, int size, String white, String wrank,
String black, String brank, double komi, int byotime,
boolean freeGame, GoServer server) {
this(gwin, size, white, wrank, black, brank, komi);
this.server = server;
this.byotime = byotime;
isFree = freeGame;
}
// This is used for SGF file loading. It is the responsibility of the
// caller to setup the necessary properties (e.g. size!) before placing
// any moves.
public Game () {
super();
init();
}
/**
* Initialize all mutable game data structures.
**/
private void init () {
if (whiteRank == null) whiteRank = "NR";
if (blackRank == null) blackRank = "NR";
ss = new StoneStasher(size);
if (size > 0)
groupArray = new SimpleGroup[size][size];
root.setChild(null);
goToBeginning();
}
// Used by SGF reader to associate a window with a Game after loading.
// Note to me - called from TerminalWindow.load() - AMB.
public void setWindow (GameWindow w) {
window = w;
}
// Change the size of the board. Return true if successful,
// return false if any move has been placed already. Used
// by SGF reader code since that code may not know the game
// size when the Game is created.
public boolean setSize (int size) {
if (root.child() != null || size < 1)
return false;
else {
this.size = size;
ss = new StoneStasher(size);
groupArray = new SimpleGroup[size][size];
return true;
}
}
public int byotime () { return byotime; }
public boolean isFree () { return isFree; }
public RootMove rootMove () { return root; }
public boolean isNetGame () { return (server != null); }
// Ugh. This has to scan back to find the first move since
// there may be more than one first move. i.e., the root may
// have variations, and the user may be browsing one of them.
public Move firstMove () {
Move m = getCurrentMove();
if (m == rootMove())
return rootMove().child();
else
while (m.parent() != rootMove())
m = m.parent();
return m;
}
// Return the final trunk move in the game tree.
public Move finalMove () {
return root.finalMove(false);
}
// Return the final "server" move in the trunk. In a local game
// there are no server moves, so in that case just do what
// finalMove() does. Server moves only hang out in the trunk.
public Move finalServerMove () {
return root.finalMove(isNetGame());
}
/*
* The "current move" is either the move that the user has backed-up
* to, or the final move if they haven't done any browsing. i.e.,
* it's the move with the X on it on the board. It may also be
* the root if the user has backed up to the beginning. It should
* never be null.
*
* NB - the "current move" is also that one which the stoneArrays[][]
* are consistent with having just played.... AMB.
*/
// Deprecate currentMove().
public Move currentMove () {
return currentMove;
}
public Move getCurrentMove() {
return currentMove;
}
private void setCurrentMove (Move m) {
currentMove = m;
ss.setCurrentMove(m);
}
/*
* Find the trunk move with the given move number.
*/
public Move findMove (int num) {
Move m = firstMove();
while (m != null) {
if (m.moveNumber() == num)
return m;
m = m.child();
}
return null;
}
// Return a move at the given position, or null if none found.
// If there is already a move at the position, search backward
// from the current position for that move. If there is no
// move, search forward down the current game tree branch for
// the first move at the position.
public Move findMove (int row, int column) {
Position pos = new Position(row, column);
if (ss.readStone(row, column) != Move.EMPTY) {
Move move = currentMove;
while (!(move instanceof RootMove)) {
if (move.occupiesPosition(pos))
return move;
move = move.parent();
}
}
else {
Vector queue = new Vector(5, 10);
/// +++ finish me.
}
return null;
}
// This should be generalized to return a Dimension.
public int size () {
return size;
}
// Perhaps size() should be deprecated :)
public int getSize() {
return size;
}
public Position koPosition () {
return koPosition;
}
public void setKoPosition (Position pos) {
koPosition = pos;
if (window != null)
window.setKoPosition(pos);
ss.setKoPosition(pos);
}
public void clearKo () {
koPosition = null;
if (window != null)
window.setKoPosition(null);
ss.setKoPosition(null);
}
public int handicap () {
if (firstMove() instanceof HandicapMove)
return firstMove().numberOfStones();
else
return 0;
}
public int capturedWhite () {
return capturedWhite;
}
public int capturedBlack () {
return capturedBlack;
}
public double komi () {
return komi;
}
public void setKomi (double k) {
komi = k;
}
public String result () {
return result;
}
public void setResult (String res) {
result = res;
}
public int blackTerritory () {
return blackTerritory;
}
public int whiteTerritory () {
return whiteTerritory;
}
public int dame () {
return dame;
}
public SimpleGroup groupAt (int row, int col) {
if (row < 0 || row >= size || col < 0 || col >= size)
return null;
else
return groupArray[row][col];
}
// Is this useful? Is there any occasion on which asking for a stone
// off the board is not result of coding error? AMB...
// See positionsAbutting(). -sigue
// sentinel Move.EMPTY values off-board would simplify coding of
// positionsAbutting(). AMB
// But could not be used by code that calculates liberties, which
// might be confusing. -sigue
public int stoneAt (int row, int col) {
if (row < 0 || row >= size || col < 0 || col >= size)
return Move.EMPTY;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -