⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 game.java

📁 ErGo是一个很早的Java通用围棋服务器(IGS/NNGS)客户端程序。有全部源码和文档
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
    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 + -