📄 move.java
字号:
package ergo.logic;
// $Id: Move.java,v 1.2 1999/08/13 01:17:49 sigue Exp $
import ergo.util.*;
import java.util.Vector;
import java.io.PrintWriter;
/**
* Move represents a Go move and at the same time represents a game tree
* consisting of this move and all moves below it. I might want to
* separate out that functionality into a GameTree class but this seemed
* to provide better access to the tree since most of the time it's a move
* that is passed around. Move is the base class for the various types
* of moves such as StoneMove, HandicapMove, and PassMove.
*
* For server games there is a distinction between "trunk" moves and
* "variations", with the intuitive meanings. The trunk, or actual
* game, is stored in the successive "child" fields of each move. If
* there is no trunk child move for a given move then the child may
* contain a variation; a non-trunk move. If it does contain a
* variation, then when a trunk move arrives it occupies the child
* slot and the variation is moved into the variations vector. This
* makes browsing simpler since it means that if the child is null
* then there are no variations either. [later] It also means a bunch
* of other things are harder, so I'm not sure it was a good choice.
* [even later] This is a mess.
*
* +++ The class hierarchy is broken: HandicapMove should be a subclass
* of StoneMove.
*
* +++ Since there can be hundreds of moves to a game, we may want to
* combine several of the fields that hold small integers or booleans
* into one integer field and extract the subfields via bit-swizzling.
*/
public abstract class Move {
// These constants are chosen to correspond with the
public static final int BLACK = 0;
public static final int WHITE = 1;
public static final int EMPTY = 3;
public static final int WHITETERRITORY = 4;
public static final int BLACKTERRITORY = 5;
public static final int SEEN = 6; // for scoring only
public static final int NONE = 7; // for scoring only
public static final int BLACKSHADOW = 10;
public static final int WHITESHADOW = 11;
public static final int BOTH = 12; // never stored in board array
protected Move parent; // The previous move.
protected Move child; // The next trunk move (or variation, if no trunk).
protected int color;
// I've already forgotten for sure, but I think the deal is that
// a move is a variation if it's NOT a server move.
protected boolean isVariation; // true if variation, false if trunk move.
// Vector of kibitzes that were made BEFORE this move was played
// and after the previous move.
protected Vector kibitzes;
// The variations field is a vector of moves, which represent
// branches in the game tree.
protected Vector variations;
// Time left (in seconds) after this move was played. This is unused
// if playing local-only moves.
protected int timeLeft = -1;
// The move number is the move number that the server assigns to this
// move, or, if this is a local move the move number will be assigned
// sequentially starting at the number of the previous branch in the
// move tree.
protected int moveNumber;
// A move has been committed if Move.commit() has been called.
// It's necessary to have this field in order to know whether
// or not captured groups have been calculated for this move, etc.
protected boolean isCommitted = false;
// A vector of SGF properties and their values that should be saved
// when this move is saved. This is normally null unless the game
// was loaded from a file, in which case unhandled properties
// corresponding to this move are stored here.
protected Vector unhandledSGFproperties = null;
Move () { }
Move (Move parent, int timeleft, int number, int color, boolean isVar) {
this.parent = parent;
if ((parent == null && color != WHITE) // is root white?
|| (parent != null && color != parent.nextColor()))
Debug.println("Incorrect color for move #" + number);
if (parent != null && number != parent.moveNumber() + 1)
Debug.println("Move numbers not in sequence: " + number);
// The root is not displayed. If we set its color to white
// the first displayed move will be black.
this.color = (parent == null) ? WHITE : parent.nextColor();
/*
if (parent == null && number != 0)
throw new GoMoveOutOfOrder(number);
else if (parent != null && number != (parent.moveNumber() + 1))
throw new GoMoveOutOfOrder(number, parent.moveNumber());
*/
this.timeLeft = timeleft;
moveNumber = number;
isVariation = isVar;
}
public int moveNumber () { return moveNumber; }
public Move child () { return child; }
public void setChild (Move hija) { child = hija; }
public Move parent () { return parent; }
public int color () { return color; }
public Vector kibitzes () { return kibitzes; }
public boolean hasKibitzes () {
return (kibitzes != null && kibitzes.size() > 0);
}
public SimpleGroupVector newGroups (Game g) { return null; }
public boolean isCommitted () { return isCommitted; }
public void commit () {
isCommitted = true;
link();
}
public void addSGFproperty (String property, Vector values) {
//Debug.println("Adding prop " + property + " to move " + this);
//for (int i = 0; i < values.size(); i++)
// Debug.println("val " + i + " = " + values.elementAt(i));
if (unhandledSGFproperties == null)
unhandledSGFproperties = new Vector(2, 4);
Vector pair = new Vector(2);
pair.addElement(property);
pair.addElement(values);
unhandledSGFproperties.addElement(pair);
}
// Make this move a permanent part of the game tree. This is done
// after it has been determined that the move is legal. (Well, for
// server moves this is called before we _check_ that it's a legal
// move, but we _assume_ it's legal if it's from the server.)
public void link () {
if (parent == null)
Debug.println("Error: Attempt to link move with null parent.");
else if (parent.child() == null)
parent.setChild(this); // The simple case.
else if (this.equals(parent.child())) {
// +++ I can't remember what this case is for. Is it a bug???
// Err, one case where it operates is the case where a server move is
// received while browsing, and link() is called - then subsequently
// it is checked (unnecessarily) for legality, which as a byproduct
// commits it; then it is in fact _actually_ committed , which
// subsequently links it (again!) to the tree.....
// HTH! Although I suspect this wasn't what you were thinking when you
// wrote this bit..... AMB
//
// This move is functionally equivalent to the old parent.child()
// so replace the old one but copy its children into the new one
// so that Game.placeMove() won't get confused about which move
// is the current move. "this" will be the current move after
// linking.
Move old = parent.child();
parent.setChild(this);
child = old.child();
for (int i = 0; ; i++) {
if (old.variationAt(i) == null)
break;
else
addVariation(old.variationAt(i));
}
}
else if (!isVariation) {
if (parent.child().isVariation) {
// Make this move the trunk move since it's not a variation
// and there is a variation in the trunk move position.
parent.addVariation(parent.child());
parent.setChild(this);
}
else
Debug.println("Attempt to link two server moves to the same parent."
+ " Not linking move.");
}
else
parent.addVariation(this);
}
/*
* Return the variation at the given index. An index of -1 means
* to return the child of this move if the child is a variation.
*/
public Move variationAt (int var) {
if (var == -1)
return child;
else if (variations == null || variations.size() <= var)
return null;
else
return (Move) variations.elementAt(var);
}
/*
* Return a move that is an immediate child or variation of this
* move at the given Position, or null.
*/
public Move variationAt (Position pos) {
for (int i = -1; variationAt(i) != null; i++) {
if (variationAt(i).occupiesPosition(pos))
return variationAt(i);
}
return null;
}
public boolean isVariation () {
return isVariation;
}
/*
* Is the given move, m, a variation of this move?
*/
public boolean isVariation (Move m) {
if (variations != null)
for(int i = 0; i < variations.size(); i++)
if(m == variationAt(i))
return true;
return false;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -