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

📄 game.java

📁 ErGo是一个很早的Java通用围棋服务器(IGS/NNGS)客户端程序。有全部源码和文档
💻 JAVA
📖 第 1 页 / 共 4 页
字号:
    else
      return ss.readStone(row,col);
  }

  
  public void addSGFproperty (String property, Vector values) {
    if (unhandledSGFproperties == null)
      unhandledSGFproperties = new Vector(10);
    Vector pair = new Vector(2);
    pair.addElement(property);
    pair.addElement(values);
    unhandledSGFproperties.addElement(pair);
  }


  /**
   * Place a move on the board unless it's an illegal move.
   *
   * THIS COMMENT IS OUT OF DATE, AND LEAVES OUT A LOT OF DETAILS.
   *
   * The move is legal if:
   *   - no stone at position
   *   - AND position is not ko
   *   - AND the move captures a group
   *         OR
   *         the group resulting from the move has >= 1 liberty
   *           - so create a new, composite group:
   *             - find newly connected groups (must all abut new stone)
   *             - create a new group with liberties that are the union
   *               of all the groups' libs and the new stone's libs
   *           - remember the old groups that make up the composite so
   *             the groupArray can be updated.
   *           - if composite has >= 1 libs, then
   *             - place the stone,
   *             - update the board with composite group (delete old ones)
   *             - update liberties of composite group
   *             - update liberties of any enemy group touching the new stone
   *
   * Note: fromServer should only be true the FIRST time this move
   *       is placed.  When it is re-placed (i.e. during browsing)
   *       fromServer should be false.
   *
   * Note: In case it's not obvious, the move being placed must have
   *       as its parent the move reflected by the current board position.
   *
   * Returns: legal-move-p
   */

  // This is the big one. Responsibilities are 
  // i) check legality (if local move, and not already done)
  // ii) link move to tree (if legal, and not already linked)
  // iii) "commit" the move - some complications here.
  // RemovalMove has a commit function, which amalgamates captured groups,
  //         *** NB no longer as of 0.25 AMB
  // but other moves just inherit from default,
  // which does nothing other than link(), and set the flag!
  // Commission is defined as the computation of captured groups,
  // etc., which is deferred until the CURRENT GAME POSITION is at the
  // move, in the case of server games....
  // however, interestingly, commission responsibilities are handled
  // by isLegal().... therefore conditions on iii) are (if we are not
  // browsing, + ??? 

  // so, if a move is (can be) checked to be legal, it is committed in
  // all but the fact of having the isCommitted flag set... which is
  // the responsibility of us!! 
  // iv) update the arrays


  // enter scoring mode
  // - after the second/third PASS is placed
  // - after the Score Locally menu is chosen (set scoringEntry)
  // - after a GameResultMove is undone
  //
  // exit scoring mode
  // - before the second/third PASS is undone
  // - before the scoringEntry move is undone
  // - before a GameResultMove is placed

  ILMV isLegalResult = new ILMV();

  public synchronized boolean placeMove (Move m, TerminalWindow term,
					 boolean browsing,
					 boolean fromServer) {
    // Exit scoring mode BEFORE a GameResultMove is placed.
    // See backup() for the opposite logic.
    if (scoringMode() && m instanceof GameResultMove)
      exitScoringMode();

    isLegalResult.newGroups = null;
    isLegalResult.opponentGroups = null;
    isLegalResult.failureReason = null;
    boolean isNewMove = !m.isCommitted(); // save this till later...
    
    // Moves from the server are assumed to be legal!  If we're
    // browsing and a server move is received, just skip this whole
    // section of checking whether the move is legal or not, and just
    // link the move without placing it.  Later, when the user browses
    // forward to this move it will be placed for real, computing
    // captured groups, liberties, etc.
    if (browsing && fromServer) {
      m.link();  // Should be m.commit()?
      return true;
    }
    else {
      // If this move has already been "committed" then it has
      // been placed before, so there's no need to check whether
      // it's legal again.
      SimpleGroupVector newGroups = null;
      if (m.isCommitted()) {
	newGroups = m.newGroups(this); // Need this below...
	updateArrays(m, newGroups, groupsAbutting(m));
      }
      else {
	if (m.isLegal(this, false, isLegalResult)) {
	  m.commit();
	  newGroups = isLegalResult.newGroups;
	  updateArrays(m, newGroups, isLegalResult.opponentGroups);
	}
	else if (isLegalResult.failureReason != null) {
	  String message = ("Illegal move: " + m + " "
			    + isLegalResult.failureReason);
	  if (term == null)
	    Debug.println(message);
	  else
	    term.displayString(message);
	  return false;
	}
      }
      // If we get here the move is definitely legal.
      clearKo();
      
      // Is the position just captured (if any) now ko?
      // Yes, if exactly one stone was captured,
      //      && the capturing stone is a one-stone group with one liberty.
      SimpleGroupVector capturedGroups = m.capturedGroups();
      if ((capturedGroups != null
	   && capturedGroups.size() == 1
	   && capturedGroups.elementAt(0).size() == 1)
	  // The following depends on the fact that Game.captureGroup(),
	  // called above (in updateArrays()), has already updated
	  // Game.stoneArray.
	  && (newGroups != null
	      &&newGroups.elementAt(0).size() == 1
	      && newGroups.elementAt(0).numberOfLiberties() == 1))
	setKoPosition(capturedGroups.elementAt(0).positionAt(0));
    }
    setCurrentMove(m);

    if (scoringMode()) {
      if (scoringLocally && scoringEntry == m.parent())
	scoringEntryFollowedByNewMove = isNewMove;
      scoreGame();
    }
    else if (m instanceof PassMove
	     && m.parent() instanceof PassMove
	     && (!isNetGame()
		 || server.isNNGStype()
		 || (server.isIGStype()
		     && m.parent().parent() instanceof PassMove))) {
      // Enter scoring mode after the second/third PASS is placed.
      // See backup() for opposite logic.
      enterScoringMode(false);	// calls scoreGame()
    }
    m.noteMovePlaced(ss);
    return true;
  }  // end method placeMove()
  
  
  // Backup to the previous move.
  //  - If any groups were captured by this move they must be re-placed.
  //  - The liberties of the groups abutting this move must be incremented.
  //  - If the move before this one captured a ko, set the ko.
  //  - If this stone connected two groups they must be disconnected.
  private synchronized void backup (boolean checkEnterExit) {
    if (currentMove == root) {
      //Debug.println("Error: Attempt to backup past the root move ignored.");
    }
    else {
      Move savedCurrentMove = currentMove;

      // exit scoring mode
      // - BEFORE the second/third PASS is undone
      // - BEFORE the scoringEntry move is undone
      // check placeMove for opposite logic.
      if (checkEnterExit && scoringMode()) {
	if (currentMove == scoringEntry) {
	  exitScoringMode();
/* This attempted to remove variations that are just local scoring.
 * Not sure this is a good idea in general, and it's broken anyway.
	  if (scoringLocally
	      && currentMove.isVariation()
	      && scoringEntryFollowedByNewMove)
	    scoringEntry.removeVariation(currentMove);  // !wrong
 */
	}
	else if (currentMove instanceof PassMove
		 && currentMove.parent() instanceof PassMove
		 && (!isNetGame()
		     || server.isNNGStype()
		     || (server.isIGStype()
			 && currentMove.parent().parent()
			    instanceof PassMove))) {
	  exitScoringMode();
	}
      }
      // Locally undo the current move.
      
      // Remove the placed positions from the board and from their
      // respective groups and recompute the groups that this move
      // touched.  Since this move may have connected some groups
      // it's necessary to recreate the separate groups that existed
      // before this move was placed.
      PositionVector positions = currentMove.placedPositions(size());
      if (positions != null) {	// various moves don't place any stones
	for (int i = 0; i < positions.size(); i++) {
	  Position pos = positions.elementAt(i);
	  ss.writeStone(pos.row, pos.column, Move.EMPTY);
	  groupArray[pos.row][pos.column] = null;
	  
	  SimpleGroupVector newGroups = new SimpleGroupVector(0, 4);
	  PositionVector abutters = positionsAbutting(pos);
	  if (abutters != null) {
	    for (int j = 0; j < abutters.size(); j++) {
	      Position p = abutters.elementAt(j);
	      SimpleGroup agroup = groupArray[p.row][p.column];
	      if (newGroups.indexOf(agroup) == -1) {
		// The group at this abutting position has not already been
		// recomputed.  (It would have been if it were connected
		// to another abutting position.)
		if (agroup.color() != currentMove.color()) {
		  // The abutting group is a different color than the
		  // stone being removed so we can't be disconnecting
		  // the abutting group from any other group.  Just
		  // add a liberty to the abutting group.
		  agroup.addLiberty(pos);
		}
		else {
		  // The abutting group is the same color, so we may be
		  // separating one group into two.  Therefore we need to
		  // recompute the group at position p.  Note that 
		  // recomputeGroupAt() modifies groupArray[][].
		  
		  // +++ Could add an optimization here.  If the move
		  // being undone is black and there are < 2 black
		  // stones abutting it, then we're not separating a
		  // group into two (or more).  Therefore just calling
		  // agroup.removePosition(p) would suffice here, and
		  // is much faster.
		  newGroups.addElement(recomputeGroupAt(p));
		}
	      }
	    }
	  }
	}
      }

      // Replace any groups that were captured by this move.
      // Add liberties to the uncaptured groups.
      // Remove liberties from the groups they touched.
      SimpleGroupVector capturedGroups = currentMove.capturedGroups();
      if (capturedGroups != null) {
	for (int i = 0; i < capturedGroups.size(); i++) {
	  uncaptureGroup(capturedGroups.elementAt(i));
	  // If this group was captured by a StoneMove (rather than
	  // by a RemovalMove) then there is now a liberty on the
	  // group being uncaptured.  Add it.
	  if (positions != null && positions.size() == 1)
	    capturedGroups.elementAt(i).addLiberty(positions.elementAt(0));
	}
      }
      
      // Is the previous position (current.parent()) now ko?
      // Yes, if it captured exactly one stone
      //      && it's a one-stone group with one liberty.
      SimpleGroupVector pcgroups = currentMove.parent().capturedGroups();
      PositionVector parentPositions
	= currentMove.parent().placedPositions(getSize());
      boolean koset = false;
      if (currentMove.parent() != root
	  && pcgroups != null
	  && pcgroups.size() == 1
	  && pcgroups.elementAt(0).size() == 1
	  && parentPositions != null
	  && parentPositions.size() == 1) {
	Position pos = parentPositions.elementAt(0);
	SimpleGroup parentGroup = groupAt(pos.row, pos.column);
	if (parentGroup != null
	    && parentGroup.size() == 1
	    && parentGroup.numberOfLiberties() == 1) {
	  setKoPosition(pcgroups.elementAt(0).positionAt(0));
	  koset = true;
	}
      }
      if (!koset) clearKo();
      savedCurrentMove.noteMoveUndone(ss);
      setCurrentMove(currentMove.parent()); // Update current move.

      if (checkEnterExit) {
	if (scoringMode())
	  scoreGame();
	else if (savedCurrentMove instanceof GameResultMove)
	  // enter scoring mode AFTER a GameResultMove is undone
	  // check placeMove() for opposite logic.
	  enterScoringMode(false);  // false = don't backupOnExit
      }
    }
  } // end method backup()
  
  /**
   * Recompute the group at the given position using the paint fill
   * algorithm.  This side-effects the groupArray[][] only.
   */
  private SimpleGroup recomputeGroupAt (Position pos) {
    int[] xcoords = { -1, 1, 0, 0 };
    int[] ycoords = { 0, 0, -1, 1 };
    int mycolor = ss.readStone(pos.row, pos.column);
    SimpleGroup group = new SimpleGroup(mycolor);
    // +++ might want to pull these out as globals.  They're kinda big.
    PositionVector q = new PositionVector(20, 30);
    PositionVector seen = new PositionVector(30, 40);
    
    q.addElement(pos);
    while (q.size() != 0) {
      Position p = q.elementAt(0);
      PositionVector libs = getLiberties(p);
      int row = p.row;
      int col = p.column;
      
      group.addPosition(p);
      for (int z = 0; z < libs.size(); z++)
	group.addLiberty(libs.elementAt(z));
      groupArray[row][col] = group;
      q.removeElementAt(0);
      seen.addElement(p);
      
      for (int i = 0; i < 4; i++) {
	int r = row - ycoords[i];
	int c = col - xcoords[i];
	if (r >= 0 && c >= 0 && r < size && c < size
	    && ss.readStone(r, c) == mycolor
	    && !seen.isMember(r, c)) {
	  q.addElement(new Position(r, c));
	}
      }
    }
    return group;
  }
  
  /*
   * Backup (locally only) to a previous move.
   * nmoves is the number of moves to go back.
   * If nmoves = -1 then go to beginning.
   */
  public void goBackward (int nmoves, boolean stopAtBranch) {
    do {
      backup(true);

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -