Java 用Tic-Tac-Toe表示游戏状态
我目前正在为我的Data Structures课程做的作业的目标是创建一个量子Tic Tac Toe模型,该模型使用一个AI来赢得比赛 目前,我在寻找代表州的最有效方法时遇到了一些困难 当前结构概述: 抽象游戏Java 用Tic-Tac-Toe表示游戏状态,java,design-patterns,optimization,Java,Design Patterns,Optimization,我目前正在为我的Data Structures课程做的作业的目标是创建一个量子Tic Tac Toe模型,该模型使用一个AI来赢得比赛 目前,我在寻找代表州的最有效方法时遇到了一些困难 当前结构概述: 抽象游戏 拥有并管理AbstractPlayer(game.nextPlayer()通过int ID返回下一个玩家) 在游戏开始时使用并初始化AbstractBoard 具有游戏树(如果在初始化中调用,则完成,否则不完整) AbstractBoard 有一个状态、一个维度和一个父游戏 是玩家
- 拥有并管理AbstractPlayer(game.nextPlayer()通过int ID返回下一个玩家)
- 在游戏开始时使用并初始化AbstractBoard
- 具有游戏树(如果在初始化中调用,则完成,否则不完整)
- 有一个状态、一个维度和一个父游戏
- 是玩家和状态之间的中介(将状态从行集合转换为点表示)
- 他是国家消费者
- 他是国家制片人
- 有一个具体的评估策略来评估当前董事会
- 预计算“三态”的可能横截
- 将它们存储在HashMap中,其中集合包含给定“3状态”的nextStates
- 包含3组--一组X-Moves、O-Moves和电路板
- 集合中的每个整数都是一行。这些整数值可用于从StateCranversalPool获取下一行状态
From the Set<Integer> board perspective:
X_X R1 might be: 101
OO_ R2 might be: 110
X_X R3 might be: 101, where 1 is an open space, and 0 is a closed space
From the Set<Integer> xMoves perspective:
X_X R1 might be: 101
OO_ R2 might be: 000
X_X R3 might be: 101, where 1 is an X and 0 is not
From the Set<Integer> oMoves perspective:
X_X R1 might be: 000
OO_ R2 might be: 110
X_X R3 might be: 000, where 1 is an O and 0 is not
编辑:我从普通的TTT开始,转向量子TTT。给定框架,它应该像创建几个新的具体类和调整一些东西一样简单。每个方块不应该只有三种可能的状态(,X,O) 存储一个由3个状态方块组成的网格,或者存储2个移动列表。你不需要存储整个棋盘,因为它是由移动定义的 还有,你所说的: 为下一个状态生成 配子树 什么是游戏树?哪些是“下一个州”的例子?我的建议:
- 考虑表示单个正方形而不是行,其中
,+1==O
和-1==X
表示空正方形。这允许您通过检查水平、垂直或对角线行的和是否等于0
或+3
来检测结束状态-3
- 其次,将该2D 3x3矩阵“展平”为单个数组,其中元素[0-2]表示第一行,元素[3-5]表示第二行,元素[6-8]表示第三行
- 在给定棋盘当前状态的情况下,使用递归或迭代方法生成后续游戏状态
$ java Board
Creating board:
---
---
---
Initialising board:
-OX
O--
XO-
Terminal state: false
Generating next move states:
XOX
O--
XO-
-OX
OX-
XO-
-OX
O-X
XO-
-OX
O--
XOX
代码
import java.util.List;
import java.util.LinkedList;
import java.util.Random;
public class Board {
private final int[] squares;
public Board() {
this.squares = new int[9];
}
protected Board(int[] squares) {
this.squares = squares;
}
public void init() {
Random rnd = new Random();
int turn = 1; // 'O' always goes first.
for (int i=0; i<squares.length; ++i) {
double d = rnd.nextDouble();
if (d < 0.75) {
squares[i] = turn;
turn = turn == 1 ? -1 : 1; // Flip to other player's turn.
} else {
squares[i] = 0; // Empty square.
}
if (isTerminalState()) {
break;
}
}
}
public boolean isTerminalState() {
boolean ret = false;
boolean foundEmpty = false;
int hSum = 0;
int[] vSum = new int[3];
for (int i=0; i<squares.length; ++i) {
hSum += squares[i];
if (isWinningRow(hSum)) {
ret = true;
break;
} else if (i == 2 || i == 5) {
hSum = 0;
}
int col = i % 3;
vSum[col] += squares[i];
if (isWinningRow(vSum[col])) {
ret = true;
break;
}
if (squares[i] == 0) {
foundEmpty = true;
}
}
if (!ret) {
if (!foundEmpty) {
ret = true;
} else {
int diag1 = 0;
int diag2 = 0;
int rowSz = (int)Math.sqrt(squares.length);
for (int i=0; i<squares.length; ++i) {
if (i % (rowSz + 1) == 0) {
diag1 += squares[i];
if (isWinningRow(diag1)) {
ret = true;
break;
}
}
if (i > 0 && i % (rowSz - 1) == 0) {
diag2 += squares[i];
if (isWinningRow(diag2)) {
ret = true;
break;
}
}
}
}
}
return ret;
}
private boolean isWinningRow(int rowSum) {
return rowSum == 3 || rowSum == -3;
}
public List<Board> getNextStates() {
List<Board> ret = new LinkedList<Board>();
int tmp = 0;
for (int i=0; i<squares.length; ++i) {
tmp += squares[i];
}
// Next turn is 'O' (i.e. +1) if the board sums to 0.
// Otherwise it's 'X's turn.
int turn = tmp == 0 ? 1 : -1;
if (!isTerminalState()) {
for (int i=0; i<squares.length; ++i) {
if (squares[i] == 0) { // Empty square
int[] squaresA = new int[squares.length];
System.arraycopy(squares, 0, squaresA, 0, squares.length);
squaresA[i] = turn;
ret.add(new Board(squaresA));
}
}
}
return ret;
}
public String toString() {
StringBuilder sb = new StringBuilder();
for (int i=0; i<squares.length; ++i) {
if (squares[i] == 1) {
sb.append('O');
} else if (squares[i] == -1) {
sb.append('X');
} else {
assert squares[i] == 0;
sb.append('-');
}
if (i == 2 || i == 5) {
sb.append('\n');
}
}
return sb.toString();
}
public static void main(String[] args) {
System.err.println("Creating board:\n");
Board bd = new Board();
System.err.println(bd);
System.err.println("\nInitialising board:\n");
bd.init();
System.err.println(bd);
System.err.println("Terminal state: " + bd.isTerminalState() + '\n');
System.err.println("\nGenerating next move states:\n");
List<Board> nextStates = bd.getNextStates();
for (Board bd1 : nextStates) {
System.err.println(bd1.toString() + '\n');
}
}
}
import java.util.List;
导入java.util.LinkedList;
导入java.util.Random;
公共班级委员会{
私人最终整数[]平方;
公共委员会(){
this.squares=新整数[9];
}
受保护板(整数[]平方){
这个。正方形=正方形;
}
公共void init(){
随机rnd=新随机();
int turn=1;//“O”总是先到。
对于(int i=0;i您的示例棋盘表示似乎有误。怎么回事?对于集合棋盘,0表示没有玩家移动到那里。对于集合X移动,0表示X没有移动到那里。对于集合O移动,0表示O没有移动到那里。好吧,除了O只有两个移动之外。.lolEdit:修复了在同一位置有多个移动的问题(我认为)游戏树是n个游戏级别的树表示。我代表每一行,而不是每一个方块。每一行有2^3个状态——二进制000到111。给定行:000,它的下一个状态是:100010001,其中1表示移动。棋盘的下一个可能状态是福林集:{row1 next states,row2,row3},{row1,row2下一个州,row3},{row1,row2,row3下一个州}为什么要展平它?作为一个抽象,单个数组是很弱的。在内部它可能是扁平的,但所提供的抽象应该提供行和列。@tster:同意。我说的是内部表示,但你是正确的,因为你可以将其封装在一个提供getSquareValue(int row,int column)等方法的Board类中.但是,使用单个数组作为表示形式可以更轻松地使用递归生成未来游戏状态,因为您只需在数组上迭代并插入“O”或“X”,而不必真正关心它是一个3x3板。
$ java Board
Creating board:
---
---
---
Initialising board:
-OX
O--
XO-
Terminal state: false
Generating next move states:
XOX
O--
XO-
-OX
OX-
XO-
-OX
O-X
XO-
-OX
O--
XOX
import java.util.List;
import java.util.LinkedList;
import java.util.Random;
public class Board {
private final int[] squares;
public Board() {
this.squares = new int[9];
}
protected Board(int[] squares) {
this.squares = squares;
}
public void init() {
Random rnd = new Random();
int turn = 1; // 'O' always goes first.
for (int i=0; i<squares.length; ++i) {
double d = rnd.nextDouble();
if (d < 0.75) {
squares[i] = turn;
turn = turn == 1 ? -1 : 1; // Flip to other player's turn.
} else {
squares[i] = 0; // Empty square.
}
if (isTerminalState()) {
break;
}
}
}
public boolean isTerminalState() {
boolean ret = false;
boolean foundEmpty = false;
int hSum = 0;
int[] vSum = new int[3];
for (int i=0; i<squares.length; ++i) {
hSum += squares[i];
if (isWinningRow(hSum)) {
ret = true;
break;
} else if (i == 2 || i == 5) {
hSum = 0;
}
int col = i % 3;
vSum[col] += squares[i];
if (isWinningRow(vSum[col])) {
ret = true;
break;
}
if (squares[i] == 0) {
foundEmpty = true;
}
}
if (!ret) {
if (!foundEmpty) {
ret = true;
} else {
int diag1 = 0;
int diag2 = 0;
int rowSz = (int)Math.sqrt(squares.length);
for (int i=0; i<squares.length; ++i) {
if (i % (rowSz + 1) == 0) {
diag1 += squares[i];
if (isWinningRow(diag1)) {
ret = true;
break;
}
}
if (i > 0 && i % (rowSz - 1) == 0) {
diag2 += squares[i];
if (isWinningRow(diag2)) {
ret = true;
break;
}
}
}
}
}
return ret;
}
private boolean isWinningRow(int rowSum) {
return rowSum == 3 || rowSum == -3;
}
public List<Board> getNextStates() {
List<Board> ret = new LinkedList<Board>();
int tmp = 0;
for (int i=0; i<squares.length; ++i) {
tmp += squares[i];
}
// Next turn is 'O' (i.e. +1) if the board sums to 0.
// Otherwise it's 'X's turn.
int turn = tmp == 0 ? 1 : -1;
if (!isTerminalState()) {
for (int i=0; i<squares.length; ++i) {
if (squares[i] == 0) { // Empty square
int[] squaresA = new int[squares.length];
System.arraycopy(squares, 0, squaresA, 0, squares.length);
squaresA[i] = turn;
ret.add(new Board(squaresA));
}
}
}
return ret;
}
public String toString() {
StringBuilder sb = new StringBuilder();
for (int i=0; i<squares.length; ++i) {
if (squares[i] == 1) {
sb.append('O');
} else if (squares[i] == -1) {
sb.append('X');
} else {
assert squares[i] == 0;
sb.append('-');
}
if (i == 2 || i == 5) {
sb.append('\n');
}
}
return sb.toString();
}
public static void main(String[] args) {
System.err.println("Creating board:\n");
Board bd = new Board();
System.err.println(bd);
System.err.println("\nInitialising board:\n");
bd.init();
System.err.println(bd);
System.err.println("Terminal state: " + bd.isTerminalState() + '\n');
System.err.println("\nGenerating next move states:\n");
List<Board> nextStates = bd.getNextStates();
for (Board bd1 : nextStates) {
System.err.println(bd1.toString() + '\n');
}
}
}