我一直致力于一个项目,该项目是创建一个Noughts和Crosses游戏。我已经编程了GUI,赢得了e.t.c的检查,但我一直在编程AI。我代表了一个充满JButton的3x3阵列。这一直非常具有挑战性。
我正在寻找可以帮助我编写更高效和更有效的代码的想法。 我认为我的方法效率不高,我想编制AI来横向和纵向制作标记(攻击和防御策略)。
提前致谢
这是我到目前为止所做的:
公共类计算机{
public void AI()
{
for(int i=0; i<3; i++ )
{
for (int j=0; j<3; j++)
{
// Diagonal Defensive Strategy for computerX and computerO
if ( Game.computerX == true && Game.Board[i*1][j*1].getText().equals("O") && Game.Board[i*1+1][j*1+1].getText().equals("O") && Game.Board[i*1+2][j*1+2].getText().equals(""))
{
Game.Board[i*1+2][j*1+2].setText("X");
}
else if (Game.computerX == true && Game.Board[i*1+2][j*1+2].getText().equals("O") && Game.Board[i*1+1][j*1+1].getText().equals("O") && Game.Board[i*1][j*1].getText().equals(""))
{
Game.Board[i*1][j*1].setText("X");
}
else if (Game.computerX == true && Game.Board[i*1+2][j*1+2].getText().equals("O") && Game.Board[i*1+1][j*1+1].getText().equals("") && Game.Board[i*1][j*1].getText().equals("O"))
{
Game.Board[i*1+1][j*1+1].setText("X");
}
//*************************************** For computerO *******************************
else if ( Game.computerO == true && Game.Board[i*1][j*1].getText().equals("X") && Game.Board[i*1+1][j*1+1].getText().equals("X") && Game.Board[i*1+2][j*1+2].getText().equals(""))
{
Game.Board[i*1+2][j*1+2].setText("O");
}
else if (Game.computerO == true && Game.Board[i*1+2][j*1+2].getText().equals("X") && Game.Board[i*1+1][j*1+1].getText().equals("X") && Game.Board[i*1][j*1].getText().equals(""))
{
Game.Board[i*1][j*1].setText("O");
}
else if (Game.computerO == true && Game.Board[i*1+2][j*1+2].getText().equals("X") && Game.Board[i*1+1][j*1+1].getText().equals("") && Game.Board[i*1][j*1].getText().equals("X"))
{
Game.Board[i*1+1][j*1+1].setText("O");
}
//*********************************************************************************************
// Diagonal Offensive Strategy for computerX and computerO
if ( Game.computerX == true && Game.Board[i*1][j*1].getText().equals("X") && Game.Board[i*1+1][j*1+1].getText().equals("X") && Game.Board[i*1+2][j*1+2].getText().equals(""))
{
Game.Board[i*1+2][j*1+2].setText("X");
}
else if (Game.computerX == true && Game.Board[i*1+2][j*1+2].getText().equals("X") && Game.Board[i*1+1][j*1+1].getText().equals("X") && Game.Board[i*1][j*1].getText().equals(""))
{
Game.Board[i*1][j*1].setText("X");
}
else if (Game.computerX == true && Game.Board[i*1+2][j*1+2].getText().equals("X") && Game.Board[i*1+1][j*1+1].getText().equals("") && Game.Board[i*1][j*1].getText().equals("X"))
{
Game.Board[i*1+1][j*1+1].setText("X");
}
//*************************************** For computerO *******************************
else if ( Game.computerO == true && Game.Board[i*1][j*1].getText().equals("O") && Game.Board[i*1+1][j*1+1].getText().equals("O") && Game.Board[i*1+2][j*1+2].getText().equals(""))
{
Game.Board[i*1+2][j*1+2].setText("O");
}
else if (Game.computerO == true && Game.Board[i*1+2][j*1+2].getText().equals("O") && Game.Board[i*1+1][j*1+1].getText().equals("O") && Game.Board[i*1][j*1].getText().equals(""))
{
Game.Board[i*1][j*1].setText("O");
}
else if (Game.computerO == true && Game.Board[i*1+2][j*1+2].getText().equals("O") && Game.Board[i*1+1][j*1+1].getText().equals("") && Game.Board[i*1][j*1].getText().equals("O"))
{
Game.Board[i*1+1][j*1+1].setText("O");
}
//**************************************************************************************
}
答案 0 :(得分:1)
伪代码中的简单防御策略:
for every line
if line is not full and opponent has two Xs in this line
place your O in the remaining space
for every pair of lines
if lines intersect, there is no element in the intersection and opponent has X in each line and you have no Xs in any of these two
place your O in the intersecton
您还应该尝试提高代码的可读性,尝试编写上述算法:
for (Line line : allLines()) {
if (line.has(2, "X") && line.has(0, "O")) {
place(line.getEmptySpace(), "O");
}
for (PairOfLines pairOfLines : allPairsOfLines()) {
Line line1 = pairOfLines.getOne();
Line line2 = pairOfLines.getTwo();
if (line1.intersects(line2)
&& pairOfLines.getIntersection().isEmpty()
&& line1.has(1, "X")
&& line2.has(1, "X")
&& pairOfLines.has(0, "O")) {
place(pairofLines.getIntersection(), "O");
}
}
您需要创建一些辅助方法来使其编译,但代码很干净,您可以轻松地从代码中查看行为。
然后你可以采取类似的进攻策略,例如:找到两条相交的线,其中有一个O而没有对手的X.
我试一试,这就是我想出来的。它没有经过适当的测试驱动(存在空白),需要更多的重构:
import org.junit.Test;
import static blah.tictactoe.Cog.O;
import static blah.tictactoe.Cog.X;
import static com.shazam.shazamcrest.MatcherAssert.assertThat;
import static com.shazam.shazamcrest.matcher.Matchers.sameBeanAs;
import static org.hamcrest.CoreMatchers.is;
public class BoardTest {
@Test
public void blocksLineOfTwo() {
Board board = new Board(new Cog[][]{
{X, null, null},
{null, X, null},
{null, null, null}
});
board.placeOAutomatically();
assertThat(board, is(sameBeanAs(new Board(new Cog[][]{
{X, null, null},
{null, X, null},
{null, null, O }
}))));
}
@Test
public void blocksIntersectionForOneCogOnEachOfLines() {
Board board = new Board(new Cog[][]{
{null, null, null},
{O, O, X },
{X, null, null}
});
board.placeOAutomatically();
assertThat(board, is(sameBeanAs(new Board(new Cog[][]{
{null, null, null},
{O, O, X },
{X, null, O }
}))));
}
@Test
public void placesWinningCog() {
Board board = new Board(new Cog[][]{
{O, O, null},
{null, null, null},
{X, X, null}
});
board.placeOAutomatically();
assertThat(board, is(sameBeanAs(new Board(new Cog[][]{
{O, O, O },
{null, null, null},
{X, X, null}
}))));
}
}
----
public enum Cog {
X, O
}
----
public class Field {
private Cog cog;
public Field(Cog cog) {
this.cog = cog;
}
public Cog getCog() {
return cog;
}
public boolean isEmpty() {
return cog == null;
}
public void setCog(Cog cog) {
this.cog = cog;
}
}
----
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import java.util.Set;
public class Line {
private final Set<Field> fields;
public Line(Field fieldOne, Field fieldTwo, Field fieldThree) {
fields = ImmutableSet.of(fieldOne, fieldTwo, fieldThree);
}
public Set<Field> getFields() {
return fields;
}
public boolean has(int count, Cog cog) {
return fields.stream().map(Field::getCog).filter(c -> c == cog).count() == count;
}
public Field getEmptySpace() {
long emptyFields = fields.stream().filter(Field::isEmpty).count();
if (emptyFields != 1) {
throw new IllegalStateException("there are " + emptyFields + " empty fields");
}
return fields.stream().filter(Field::isEmpty).findFirst().get();
}
public boolean intersects(Line line) {
return !Sets.intersection(this.fields, line.fields).isEmpty();
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Line line = (Line) o;
return fields.equals(line.fields);
}
}
----
import com.google.common.collect.Sets;
import java.util.HashSet;
import java.util.Set;
public class PairOfLines {
private final Line lineOne;
private final Line lineTwo;
public PairOfLines(Line lineOne, Line lineTwo) {
this.lineOne = lineOne;
this.lineTwo = lineTwo;
}
public Line getOne() {
return lineOne;
}
public Line getTwo() {
return lineTwo;
}
public Field getIntersection() {
return Sets.intersection(lineOne.getFields(), lineTwo.getFields()).iterator().next();
}
public boolean has(int count, Cog cog) {
Set<Field> allFields = new HashSet<>();
allFields.addAll(lineOne.getFields());
allFields.addAll(lineTwo.getFields());
return allFields.stream().map(Field::getCog).filter(c -> c == cog).count() == count;
}
}
----
import com.google.common.collect.ImmutableSet;
import java.util.Set;
import static blah.tictactoe.Cog.X;
import static blah.tictactoe.Cog.O;
public class Board {
private final Field[][] matrix = new Field[3][3];
public Board(Cog[][] matrix) {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
this.matrix[i][j] = new Field(matrix[i][j]);
}
}
}
public void placeOAutomatically() {
// winning move
for (Line line : allLines()) {
if (line.has(2, O) && line.has(0, X)) {
line.getEmptySpace().setCog(O);
return;
}
}
// block line of two
for (Line line : allLines()) {
if (line.has(2, X) && line.has(0, O)) {
line.getEmptySpace().setCog(O);
return;
}
}
// block intersection
for (PairOfLines pairOfLines : allPairsOfIntersectingLines()) {
if (pairOfLines.getIntersection().isEmpty()
&& pairOfLines.getOne().has(1, X)
&& pairOfLines.getTwo().has(1, X)
&& pairOfLines.has(0, O)) {
pairOfLines.getIntersection().setCog(O);
return;
}
}
}
private Set<Line> allLines() {
return ImmutableSet.of(
new Line(matrix[0][0], matrix[0][1], matrix[0][2]),
new Line(matrix[1][0], matrix[1][1], matrix[1][2]),
new Line(matrix[2][0], matrix[2][1], matrix[2][2]),
new Line(matrix[0][0], matrix[1][0], matrix[2][0]),
new Line(matrix[0][1], matrix[1][1], matrix[2][1]),
new Line(matrix[0][2], matrix[1][2], matrix[2][2]),
new Line(matrix[0][0], matrix[1][1], matrix[2][2]),
new Line(matrix[0][2], matrix[1][1], matrix[2][0])
);
}
private Set<PairOfLines> allPairsOfIntersectingLines() {
ImmutableSet.Builder<PairOfLines> builder = new ImmutableSet.Builder<>();
for (Line lineOne : allLines()) {
for (Line lineTwo : allLines()) {
if (!lineOne.equals(lineTwo) && lineOne.intersects(lineTwo)) {
builder.add(new PairOfLines(lineOne, lineTwo));
}
}
}
return builder.build();
}
}
需要一些Maven依赖项:
<dependencies>
<!-- COMPILE -->
<dependency>
<groupId>com.google.collections</groupId>
<artifactId>google-collections</artifactId>
<version>1.0</version>
</dependency>
<!-- TEST -->
<dependency>
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-library</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.hamcrest</groupId>
<artifactId>hamcrest-core</artifactId>
<version>1.3</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.shazam</groupId>
<artifactId>shazamcrest</artifactId>
<version>0.9</version>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
答案 1 :(得分:1)
为此类游戏编写AI代码的最简单方法是Minimax algorithm,这是一种针对双人游戏的算法,其中每个玩家都在尝试提供最佳移动。你基本上只需要在维基页面上详细编码算法并编写一个简单的评估函数,它返回1表示获胜,0表示抽奖(或者没有结果)和-1表示丢失(更复杂的评估算法)可以愉快地下棋)。 Tic-tac-toe是一款足够简单的游戏,你可以对每一个动作进行全面深度评估 - 显然像国际象棋这样的复杂游戏需要截止。
您还可以查看此算法稍微复杂但效率更高的版本Alpha-Beta pruning。