【问题标题】:AI for Tic Tac Toe井字游戏的人工智能
【发布时间】:2015-02-26 12:07:12
【问题描述】:

我一直在做一个项目,这个项目是创建一个 Noughts and Crosses 游戏。我已经对 GUI 进行了编程,赢了检查等,但我一直坚持对 AI 进行编程。我代表了一个充满 JButtons 的 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");
            }


            //**************************************************************************************



     }

【问题讨论】:

  • 你知道i*1 = i,对吗? (类比,j*1 = j
  • AI 编程有不同的方法。有些更简单,有些则非常复杂,也许你应该检查一下它们并根据你潜在的编程技能来验证它们的难度。也许你应该看看this问题。
  • 请注意,已经有一种算法可以在这场比赛中获胜或至少平局。但如果你想尝试一个简单的 AI 算法,你可以试试 minimax

标签: java eclipse oop tic-tac-toe


【解决方案1】:

为此类游戏编写 AI 代码的最简单方法是 Minimax algorithm,这是一种适用于 2 人游戏的算法,每个玩家都试图做出最好的动作。您基本上只需要按照 wiki 页面上的详细信息编写算法并编写一个简单的评估函数,该函数返回 1 表示获胜,0 表示平局(或没有结果),-1 表示失败(对算法进行更复杂的评估可以愉快地下棋)。 Tic-tac-toe 是一款足够简单的游戏,您可以对每一步都进行全面的深度评估 - 显然,像国际象棋这样更复杂的游戏需要截止。

您还可以查看该算法的稍微复杂但也更高效的版本,称为Alpha-Beta pruning

【讨论】:

    【解决方案2】:

    伪代码中的简单防御策略:

    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>
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-03-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多