過去のJava演習をいくつか行って、プログラミング全般を改善できるようにしていますが、かなり長い間これにこだわっています。このプロジェクトは非常に大きく、混乱を招くだけのインターフェースとサブクラスがたくさんあるので、必要なすべてのソースを投稿します。
public class Board {
public final static char NOUGHT = 'O';
public final static char CROSS = 'X';
public final static char EMPTY = ' ';
// Each cell is indexed as follows:
// 1 2 3
// 4 5 6
// 7 8 9
private char[][] grid; // a matrix to store the positions of the board
private int numOfMarks; // number of moves made on the board
private int lastMarkPosition; //position of last move maode in the board
public Board() {
grid = new char[3][3];
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
grid[row][col] = EMPTY;
}
}
numOfMarks = 0;
lastMarkPosition = 0;
}
//post: Returns true if the board is finished.
// A board is finished because either there is a winner or the board is full.
public boolean isFinished() {
return numOfMarks == 9 || getWinnerMark() != EMPTY;
}
//post: Records the position of the last mark made on the board.
public void setLastMarkPosition(int lastPosition){
lastMarkPosition = lastPosition;
}
//post: Returns the position of the last mark
public int getLastMarkPosition(){
return lastMarkPosition;
}
//post: Returns the mark ('X' or 'O') of the winner player if a winning condition exists in the board,
// returns EMPTY otherwise.
public char getWinnerMark() {
for (int i = 0; i < 3; i++) {
// check if there are three in a horizontal row
if (grid[i][0] != EMPTY && grid[i][0] == grid[i][1]
&& grid[i][1] == grid[i][2]) {
return grid[i][0];
}
// check if there are three in a vertical row
if (grid[0][i] != EMPTY && grid[0][i] == grid[1][i]
&& grid[1][i] == grid[2][i]) {
return grid[0][i];
}
}
// check if there are three in a diagonal row
if (grid[1][1] != EMPTY
&& (grid[1][1] == grid[0][0] && grid[1][1] == grid[2][2] || grid[1][1] == grid[0][2]
&& grid[1][1] == grid[2][0])) {
return grid[1][1];
}
// otherwise, return EMPTY as there is no winner
return EMPTY;
}
//post: Sets the given mark at the given position in the board
public void setMark(int pos, char mark) throws GameException {
if (numOfMarks == 9) {
throw new GameException("attempted to set mark on a full board.");
}
if (pos < 1 || pos > 9) {
throw new GameException(
"attempted to set mark in a wrong position: " + pos);
}
if (mark != NOUGHT && mark != CROSS) {
throw new GameException("attempted to set an invalid mark: "
+ String.valueOf(mark));
}
// perform board update
int row = (pos - 1) / 3;
int col = (pos - 1) % 3;
if (grid[row][col] != EMPTY) {
throw new GameException(
"attempted to set mark on a non-empty position: "
+ pos);
} else {
grid[row][col] = mark;
numOfMarks++;
}
}
//post: Returns the mark that is at a given position in the board
public char getMark(int pos) {
return grid[(pos-1)/3][(pos-1)%3];
}
//post: If the grid is not full, calculates whose turn is, based on the marks in the grid.
// Returns EMPTY if the board is already full.
public char getTurn() {
if (numOfMarks == 9) {
return EMPTY;
} else if (numOfMarks % 2 == 0) {
// by default, CROSS should go first
return CROSS;
} else {
return NOUGHT;
}
}
//post: Copy the board and returns it
public Board makeCopy() {
Board copy = new Board();
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
copy.grid[row][col] = this.grid[row][col];
}
}
copy.numOfMarks = this.numOfMarks;
return copy;
}
//post: Prints the given board
public static void display(Board board) {
for (int row = 0; row < 3; row++) {
for (int col = 0; col < 3; col++) {
System.out.print(" ");
char mark = board.grid[row][col];
if (mark != EMPTY) {
System.out.print(mark);
} else {
System.out.print((row)*3+(col+1));
}
System.out.print(" ");
if (col < 2) {
System.out.print("|");
}
}
System.out.println();
if (row < 2) {
System.out.println("-----------");
}
}
}
ゲーム ツリー インターフェイス
public interface GameTreeInterface {
//post: Returns the board at the root of the game tree.
public Board getRootItem();
//post: Expands the game tree fully by adding all possible boards in the game.
// It uses a recursive auxiliary method.
public void expand();
//pre: The game tree is fully expanded.
//post: Assigns a score to each board in the game tree.
// It uses a recursive auxiliary method.
public void assignScores();
//pre: Each board in the game tree has a score.
//post: Computes an array of positions (1..9) optimal available moves.
// These are the last mark positions in the children boards that have the highest score.
public int[] BestMoves();
//post: Returns the number of boards stored in a game tree.
// It uses a recursive auxiliary method.
public int size();
}
ゲームツリー
public class GameTree implements GameTreeInterface {
private GameTreeNode root; // reference to the root board of a game tree
public GameTree(Board board) {
this.root = new GameTreeNode(board);
}
// post: Returns the board at the root of a game tree.
public Board getRootItem() {
return root.getBoard();
}
// post: Returns the number of boards stored in a game tree.
// It uses a recursive auxiliary method.
public int size() {
return sizeTree(root) + 1;
}
// post: Returns the number of boards stored in a game tree, excluded
// the root.
private int sizeTree(GameTreeNode node) {
int total = 0;
for (int i = 1; i < node.numberOfChildren(); i++) {
if (node.getChild(i).getBoard() != null)
total++;
}
return total;
}
// post: Expands the game tree fully by adding all possible boards in
// the game.
// It uses a recursive auxiliary method.
public void expand() {
expandTree(root);
}
// post: Expands the game tree from the given node by adding
// all the possible moves that the computer and the user player
// can make, until the game is finished, from the given node onwards.
private void expandTree(GameTreeNode node) {
if (!node.getBoard().isFinished()) {
char c = node.getBoard().getTurn();
for (int i = 1; i < 9; i++) {
if (node.getBoard().getMark(i) == Board.EMPTY) {
GameTreeNode n = new GameTreeNode(node.getBoard());
n.getBoard().setMark(i, c);
n.getBoard().setLastMarkPosition(i);
node.getChildren().add(2, n);
expandTree(n);
}
}
}
}
// pre: The game tree is fully expanded.
// post: Assigns a score to each board in the game tree.
// It uses a recursive auxiliary method.
public void assignScores() {
char player = (root.getBoard()).getTurn();
assignScoresTree(root, player);
}
// post: Assigns scores to each board in a game tree for the computer
// player.
private void assignScoresTree(GameTreeNode node, char player) {
Board board = node.getBoard();
if (board.isFinished()) {
// base case of recursion
// score 3 for a winning board for the given player,
// score 2 for a draw baord,
// score 1 for a losing board for the given player
char winner = board.getWinnerMark();
if (winner == Board.EMPTY) {
// this is a draw!
node.setScore(2);
} else {
node.setScore(winner == player ? 3 : 1);
}
}
else {
// tries to assign the scores to all the children boards
// first, recursively
int minScore = Integer.MAX_VALUE;
int maxScore = Integer.MIN_VALUE;
GenericList<GameTreeNode> children = node.getChildren();
for (Iterator<GameTreeNode> it = children.iterator(); it
.hasNext();) {
GameTreeNode child = it.next();
assignScoresTree(child, player);
// keeps track of the maximum and minimum scores
// of the children boards so far
int childScore = child.getScore();
if (childScore > maxScore)
maxScore = childScore;
if (childScore < minScore)
minScore = childScore;
}
// Assigns score to the current board in the recursion
// according to the player's turn
if (board.getTurn() == player) {
// get the maximum score as the player wants to
// win
node.setScore(maxScore);
} else {
// get the minimum score (as the player wants
// the opponent to lose;)
node.setScore(minScore);
}
}
}
// pre: Each board in the game tree has a score.
// post: Computes an array of positions (1..9) optimal available moves.
// These are the last mark positions in the children boards that have
// the highest score.
public int[] BestMoves() {
int maxScore = Integer.MIN_VALUE;
GenericList<GameTreeNode> highestScoreBoards = new GenericList<GameTreeNode>();
GenericList<GameTreeNode> children = root.getChildren();
for (Iterator<GameTreeNode> it = children.iterator(); it
.hasNext();) {
GameTreeNode nextBoard = it.next();
int curScore = nextBoard.getScore();
if (maxScore < curScore) {
maxScore = curScore;
highestScoreBoards.clear();
highestScoreBoards.add(1, nextBoard);
} else if (maxScore == curScore) {
highestScoreBoards.add(1, nextBoard);
}
}
int[] moves = new int[highestScoreBoards.size()];
for (int i = 0; i < moves.length; i++) {
Board board = (highestScoreBoards.get(i + 1))
.getBoard();
moves[i] = board.getLastMarkPosition();
}
return moves;
}
}
ゲームツリーノード
public class GameTreeNode{
private GameTreeItem item; // includes the board object and the score
private GenericList<GameTreeNode> children; //list of gameTreeNodes of possible next moves.
public GameTreeNode(Board newBoard) {
this.item = new GameTreeItem(newBoard);
this.children = new GenericList<GameTreeNode>();
}
//post: Returns the board stored in a GameTreeNode
public Board getBoard() {
return item.board;
}
//post: Returns the children stored in a GameTreeNode, as a list of GameTreeNodes
public GenericList<GameTreeNode> getChildren(){
return children;
}
//post: Returns the score of the board
public int getScore() {
return item.score;
}
//post: Sets the score of the board to be equal to the given score
public void setScore(int score) {
item.score = score;
}
//post: Removes all the children
public void removeChildren(){
children = null;
}
//post: Returns the number of children
public int numberOfChildren(){
return children.size();
}
//post: Returns the child at a given position in the list of children, as a
GameTreeNode
public GameTreeNode getChild(int i){
return children.get(i);
}
//Inner class for storing a board and the score
private class GameTreeItem{
private Board board;
private int score;
public GameTreeItem(Board newBoard) {
this.board = newBoard;
score = 0;
}
}
かなり大量のコードで申し訳ありませんが、ここにすべてがなければ問題を説明できないと思います。GenericList にはまだ多くの余分なコードがありますが、汎用リンク リストの非常に簡単な実装です。
誰かがこのコードを実行した場合、私の問題は sizeTree() メソッドを持つ GameTree クラスにあります。私は解決策を提供しましたが、それは単純すぎて正しくないと思います。私が理解しているように、GameTreeNode には、現在のプレイ状態を含む GameTreeItem と、GameTreeNode のリンクされたリストへの参照が含まれています。ただし、私の仕様では、このメソッドを実装するには再帰を使用する必要があると書かれていますが、その方法はわかりません。ルートのすべての子を通過し、ボードをチェックするため、私の方法は機能しますか?
私はそれがロングショットであることを知っていますが、誰かが助けを提供できるなら、私は本当に感謝しています!