📄 game.java
字号:
if (c < size && ss.readStone(r, c + 1) == Move.EMPTY)
positions.addElement(new Position(r, c + 1));
if (c > 0 && ss.readStone(r, c - 1) == Move.EMPTY)
positions.addElement(new Position(r, c - 1));
return positions;
}
public int numberOfLiberties (Position pos) {
int libs = 0;
int r = pos.row;
int c = pos.column;
int size = this.size - 1;
if (r > 0 && ss.readStone(r - 1, c) == Move.EMPTY) libs++;
if (r < size && ss.readStone(r + 1, c) == Move.EMPTY) libs++;
if (c < size && ss.readStone(r, c + 1) == Move.EMPTY) libs++;
if (c > 0 && ss.readStone(r, c - 1) == Move.EMPTY) libs++;
return libs;
}
/****************
* Scoring *
****************/
/**
* The user enters scoring mode by choosing Score Locally from the
* menu or by passing 2 times (NNGS) or 3 times (IGS). The score is
* calculated and displayed immediately upon entering scoring mode,
* and then after each move. Clicking on a stone removes that
* stone's group. Clicking on an empty position places a stone as
* usual. The user exits scoring mode by choosing Score Locally
* from the menu or by backing up past the move where scoring mode
* was entered, at which point the game returns to the position
* where local scoring mode was entered.
*/
public boolean scoringMode () {
return scoringMode;
}
public boolean scoringLocally () {
return scoringLocally;
}
public void enterScoringMode (boolean backupOnExit) {
if (!scoringMode()) {
scoringMode = true;
scoringEntry = currentMove;
scoringLocally = backupOnExit;
scoreGame();
if (window != null)
window.noteScoringMode(true);
}
}
public void exitScoringMode () {
if (scoringMode()) {
Debug.asser(scoringEntry != null,
"Game: scoringEntry == null while in scoring mode.");
scoringMode = false;
clearMarkedTerritory();
if (scoringLocally) {
// Go back to the move where scoring mode was entered. Note
// that if the user has browsed back past scoringEntry we do
// nothing.
Move next = null;
while (scoringEntry.moveNumber() < currentMove.moveNumber()) {
next = currentMove;
backup(false);
}
// Remove local scoring variations.
if (next != null
&& next.isVariation()
&& scoringEntryFollowedByNewMove) // this is so disgusting.
currentMove.removeVariation(next);
ss.emit();
}
scoringEntryFollowedByNewMove = false; // restore to safest value.
scoringLocally = false;
scoringEntry = null;
if (window != null)
window.noteScoringMode(false);
}
}
private void clearMarkedTerritory () {
// Clear all marked territory on the board so it just displays
// as empty space.
for (int row = 0; row < size(); row++)
for (int col = 0; col < size(); col++) {
int stone = ss.readAnyStone(row, col);
switch (stone) {
case Move.WHITE:
case Move.BLACK:
case Move.EMPTY:
break;
default:
ss.writeStone(row, col, Move.EMPTY);
break;
}
}
ss.emit();
}
private PositionVector scoringQueue = new PositionVector(50, 50);
/**
* Takes a board array and an empty position and uses the flood
* filling algorithm to mark all positions inside this territory
* with the appropriate color.
*/
public int markRegion (int row, int col) {
PositionVector q = scoringQueue; // abbrev
int color = Move.EMPTY;
q.setSize(0);
q.addElement(new Position(row, col));
while (q.size() != 0) {
// Guessing that removing the element at size - 1 is more efficient
// than removing the element at 0...
Position pos = q.elementAt(q.size() - 1);
q.removeElementAt(q.size() - 1);
// These next five lines are something of an idiom. Should use a macro
// or a higher-order function...
int[] xcoords = { -1, 1, 0, 0, 0 };
int[] ycoords = { 0, 0, 0, -1, 1 };
for (int z = 0; z < xcoords.length; z++) {
int r = pos.row + ycoords[z];
int c = pos.column + xcoords[z];
if (r >= 0 && c >= 0 && r < size && c < size) {
int it = ss.readAnyStone(r, c);
if (it == Move.EMPTY) {
ss.writeStone(r, c, Move.SEEN, false);
q.addElement(new Position(r, c));
}
else if (it == Move.BLACK || it == Move.WHITE) {
if (color == Move.EMPTY)
// This is the first border stone we've seen, so this will be
// the assumed color of this territory, until we know otherwise.
color = it;
else if (color != it)
// The current stone (it) is not the same color as the previously
// encountered border color, so this territory is undetermined.
color = Move.NONE;
}
}
}
}
// Done marking the territory, so set all SEEN positions to
// the border color found and set all NONE positions back to EMPTY.
switch (color) {
case Move.WHITE:
color = Move.WHITETERRITORY;
break;
case Move.BLACK:
color = Move.BLACKTERRITORY;
break;
}
int count = 0;
boolean markAsDirty = color != Move.NONE;
for (int i = 0; i < size; i++)
for (int j = 0; j < size; j++)
if (ss.readAnyStone(i, j) == Move.SEEN) {
ss.writeStone(i, j, color, markAsDirty);
count++;
}
return count;
}
public void scoreGame () {
if (getCurrentMove() instanceof GameResultMove)
return; // don't override server scoring
blackTerritory = 0;
whiteTerritory = 0;
dame = 0;
// Clear out results of previous scoring, which are left around so they
// get displayed on the screen during scoring mode.
for (int row = 0; row < size; row++)
for (int col = 0; col < size; col++)
switch (ss.readAnyStone(row, col)) {
case Move.WHITETERRITORY:
case Move.BLACKTERRITORY:
ss.writeStone(row, col, Move.EMPTY);
break;
}
for (int row = 0; row < size; row++) {
for (int col = 0; col < size; col++) {
if (ss.readAnyStone(row, col) == Move.EMPTY) {
int count = markRegion(row, col);
int stone = ss.readAnyStone(row, col);
if (stone == Move.BLACKTERRITORY)
blackTerritory += count;
else if (stone == Move.WHITETERRITORY)
whiteTerritory += count;
/*
else if (stone == Move.NONE)
// Set this position back to empty, without marking as dirty.
ss.writeStone(row, col, Move.EMPTY, false);
else
dame += count;
*/
}
else if (scoringRules == 'C') { // chinese rules
switch (ss.readStone(row, col)) {
case Move.BLACK:
blackTerritory += 1;
break;
case Move.WHITE:
whiteTerritory += 1;
break;
}
}
}
}
// Replace all positions marked with Move.NONE with Move.EMPTY again.
// These are all the dame positions.
for (int row = 0; row < size; row++)
for (int col = 0; col < size; col++)
if (ss.readAnyStone(row, col) == Move.NONE) {
ss.writeStone(row, col, Move.EMPTY, false);
dame++;
}
ss.emit();
}
// For GameResultMoves, if the user clicks on a dead group we can assume
// all other groups of that color in the same territory are also dead.
// This method returns a vector of all groups the same color as startPos
// in the same territory as startPos. This doesn't try to determine if
// the group at startPos is _actually_ dead. Assumes the region only
// contains Move.WHITE, Move.BLACK, or Move.EMPTY.
public SimpleGroupVector groupsInRegion (Position startPos) {
SimpleGroupVector groups = new SimpleGroupVector(5, 10);
PositionVector q = scoringQueue; // abbrev
SimpleGroup g = groupAt(startPos.row, startPos.column);
if (g != null) {
int deadColor = g.color();
groups.addElement(g);
q.setSize(0);
// Enqueue the elements of the initial group and mark them SEEN.
for (int i = 0; i < g.size(); i++) {
Position p = g.positionAt(i);
ss.writeStone(p.row, p.column, Move.SEEN, false);
q.addElement(p);
}
while (q.size() != 0) {
// Guessing that removing the element at size - 1 is more efficient
// than removing the element at 0...
Position pos = q.elementAt(q.size() - 1);
q.removeElementAt(q.size() - 1);
int[] xcoords = { -1, 1, 0, 0 };
int[] ycoords = { 0, 0, -1, 1 };
for (int z = 0; z < xcoords.length; z++) {
int r = pos.row + ycoords[z];
int c = pos.column + xcoords[z];
if (r >= 0 && c >= 0 && r < size && c < size) {
int stone = ss.readAnyStone(r, c);
if (stone == deadColor) {
// Found another dead group.
// Enqueue its elements and mark them SEEN.
SimpleGroup g2 = groupAt(r, c);
groups.addElement(g2);
for (int i = 0; i < g2.size(); i++) {
Position p = g2.positionAt(i);
ss.writeStone(p.row, p.column, Move.SEEN, false);
q.addElement(p);
}
}
else if (stone == Move.EMPTY) {
ss.writeStone(r, c, Move.SEEN, false);
q.addElement(new Position(r, c));
}
}
} // for
} // while
// Found all the groups. Now restore the board by putting back all
// removed groups and marking all SEENs as EMPTYs.
for (int i = 0; i < groups.size(); i++) {
// Why doesn't javac let me shadow lexical vars??!??
SimpleGroup g3 = groups.elementAt(i);
for (int j = 0; j < g3.size(); j++) {
Position p = g3.positionAt(j);
ss.writeStone(p.row, p.column, deadColor, false);
}
}
for (int row = 0; row < size; row++)
for (int col = 0; col < size; col++)
if (ss.readAnyStone(row, col) == Move.SEEN)
ss.writeStone(row, col, Move.EMPTY, false);
}
return groups;
}
/*********************************
* Smart Go Format file writing *
*********************************/
public void writeSGF (PrintWriter out) {
if (out == null)
out = new PrintWriter(System.out);
// The root node represents a collection of games.
// Each variation of the root move is an SGF GameTree and is
// also an SGF root node. Output all the root-node and
// game-info properties for each one.
for (int i = -1; ; i++) {
Move m = root.variationAt(i);
if (m == null)
break;
// game-info nodes: GN, RE, PW, PB, WR, BR, PC, DT, TM, KM
out.print("(;FF[4]AP[Ergo:" + Ergo.version() // only two root props
+ "]\nGM[1]\nSZ[" + size()
+ "]\nGN[" + ((window == null) ? "" : window.title(false))
+ "]\nRE[" + SGFresult()
+ "]\nPW[" + whiteName + "]WR[" + whiteRank
+ "]\nPB[" + blackName + "]BR[" + blackRank
+ "]\nPC[" + SGFlocation()
+ "]\nDT[" + SGFdate(startTime)
+ "]\nTM[" + SGFtime()
+ "]KM[" + komi() + "]\n");
m.writeSGF(out, this, SGF.nodesPerLine);
out.print("\n;)\n\n");
}
}
public String SGFtime () {
return "0"; // +++ Not yet implemented
}
public String SGFlocation () {
return ((SGFlocation != null)
? SGFlocation
: ((server != null) ? server.name : "unknown"));
}
public String SGFdate (Date date) {
Calendar cal = new java.util.GregorianCalendar();
cal.setTime(date);
int year = cal.get(Calendar.YEAR);
int month = cal.get(Calendar.MONTH) + 1;
int day = cal.get(Calendar.DAY_OF_MONTH);
return (year + "-"
+ ((month < 10) ? "0" : "") + month + "-"
+ ((day < 10) ? "0" : "") + day);
}
public String SGFresult () {
return (result() == null) ? "?" : result;
}
} // end class Game
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -