使用游戏的方式学习编程效率更高

学习编程刚开始都是比较枯燥的阶段,但是如果带着需求和兴趣去体验一门编程语言,学习的效果将事半功倍,而且编程语言的语法和特性带着兴趣去解决一个一个问题,记忆也更加深刻。将学习转为游戏方式的方法就是实际尝试编写一些游戏,下面几个编程游戏是比较流行的:

  • 猜数字
  • 石头剪刀布
  • 太空侵略者
  • 跳棋或者国际象棋

这篇我们就尝试使用Java编写一个井字游戏。

井字游戏

井字游戏的规则:两个玩家,一个打圈(◯),一个打叉(),轮流在3乘3的格上打自己的符号,最先以横、直、斜连成一线则为胜。
在这里插入图片描述
井字游戏难度处于中等水平,你能够编写猜数字游戏或石头剪刀布,那么就能编写Java井字游戏,遇到困难再解决,自然就掌握了,比苦读硬记效果好。Java 井字游戏的编程实现不像跳棋或国际象棋那么困难,但是当你尝试实现更复杂的棋盘游戏时,关于多维数组、异常处理和流控制的许多经验将会有所帮助。

如何编写一个Java的井字游戏

为了实现Java版本的井字游戏,我们先对游戏规则进行梳理,然后抽象流程,并使用合适的数据结构和流程去模拟游戏。下面我整理了八个步骤,逐步实现开发完这个游戏的目标。

  1. 创建一个Java类,命名TicTacToe
  2. 声明所有需要的变量
  3. 打印一个基本的井字游戏棋盘
  4. 接受用户的输入
  5. 检查一方是否获胜
  6. 如果没有获胜,另外一方继续输入
  7. 如果棋盘方格都已被占据,则游戏结束
  8. 运行游戏

Step1:创建Java类

首先,创建一个名为 TicTacToe 的类,该类具有可运行的 main 方法,如下所示。
本例中的所有代码都位于 TicTacToe 类的 main 方法中,或者位于声明为此类一部分的方法中。

/* A Tic Tac Toe Game coded in Java */
public class TicTacToe {

  public static void main(String[] args) 
  {

  }

}

Step2: 声明变量

井字棋盘有 9 个方格。在我们的游戏中,我们会显示每个未选中方格的编号,并询问玩家要标记哪个方格。
board[] 数组的每个元素代表游戏板上的一个位置

char[] board = { '1','2','3',
                 '4','5','6',
                 '7','8','9'  };
```
我们还需要记录轮到谁了,以及已经玩了多少个方格。如果九个方格都玩完了,没有获胜者,游戏就会以平局结束。
```java
var numberOfSquaresPlayed = 0;
var whoseTurnItIs = 'x';
```
我们使用一个字符数组来存储数字1-99个数字就是棋盘每个方格的位置,用户交互时就是根据数字表名,棋子将落在何处。

### Step3:打印出井字棋盘
每次选中一个方块,我们都会重新绘制棋盘。 由于这是一款简单的主机游戏,因此棋盘的显示并不复杂。它看起来就像下面的图片一样:
![在这里插入图片描述](https://i-blog.csdnimg.cn/direct/087f4a89f4db4f3184874112e0f6bc61.png)
我们在名为 printTheBoard() 的方法中输入生成并打印出 Java 井字游戏板的逻辑:
```java
private static void printTheBoard(char[] board) 
{
  System.out.println( board[0] + " | " +  board[1] + " | " + board[2]);
  System.out.println( " - + - + - " );
  System.out.println( board[3] + " | " +  board[4] + " | " + board[5]);
  System.out.println( " - + - + - " );
  System.out.println( board[6] + " | " +  board[7] + " | " + board[8]);
}
```
当第一次输出时:
```java
1 | 2 | 3
- + - + -
4 | 5 | 6
- + - + -
7 | 8 | 9
```
随着游戏的进行,棋盘会用 x 和 o 进行更新。完整的井字游戏 Java 战斗如下所示:
```java
X | O | X
- + - + -
X | 5 | O
- + - + -
X | O | 9
```

### Step4: 接受用户输入
只要 numberOfSquaresPlayed 小于 9,我们就会向玩家显示棋盘的当前状态并提示他们选择一个方块,利用 JavaScanner 类进行用户输入:
```java
        while (numberOfSquaresPlayed < 9) {
            printTheBoard(board);
            getTheUserToSelectTheirSquare(board, whoseTurnItIs);

            if (theyWonTheGame(board, whoseTurnItIs)) {
                gameEndingMessage = "恭喜,你是大赢家!";
                break;
            } else if (numberOfSquaresPlayed == 9) {
                gameEndingMessage = "一场恶战,双方平局!";
            } else {
                numberOfSquaresPlayed++;
                whoseTurnItIs = (whoseTurnItIs == 'x') ? 'o' : 'x';
                continue;
            }
        }
```

注意,字符数组如何根据轮到谁来更新,是使用 X 还是 O。

```java
board[input-1] = whoseTurnItIs;
```
由于数组是从零开始的,因此我们从用户提供的数字中减一。如果用户选择数字 5 的平方,则它对应于数组中的索引 4。

### Step5: 检查是否获胜
每次用户选择一个方格时,我们都会检查他们是否完成了一条线并赢得了游戏。

井字棋有八种获胜方式:

 - 三条水平线。 
 - 三条垂直线。 
 - 两条穿过方格的对角线。

在这种情况下,条件逻辑可能会变得很复杂,因为必须根据当前玩家的字母检查潜在获胜行中的每个方格。

对第一条水平线的检查可能如下所示:
```java
if ((board[0] == whoseTurnItIs) && 
    (board[1] == whoseTurnItIs) && 
    (board[2] == whoseTurnItIs)) 
{
   System.out.println("You won!!! Congratulations!");
   break;
}  
```

需要进行八次这样的检查才能确定游戏是否已经获胜,这可能会变得很麻烦。

另一种选择稍微简洁一些,它利用了 char 类型的一个鲜为人知的特性。

char 是一种整数类型
很少有人知道,Java 中的 char 实际上是一种整数类型。你可以像对 intdoublefloat 一样对其执行数学运算。

这意味着你可以将给定行中的三个字符相加,然后查看结果是否等于 'x' 字符自身相乘三次的结果。如果是,你就知道一行中有三个 'x' 字符。如果是第二个玩家的回合,对 'o' 字符执行相同的操作。

以下逻辑利用了 char 的基于整数的性质来查看给定的行、列或对角线是否已完成: 
```java
if (  (board[0] + board[1] + board[2] == (whoseTurnItIs * 3)) // first row 
   || (board[3] + board[4] + board[5] == (whoseTurnItIs * 3)) // second row
   || (board[6] + board[7] + board[8] == (whoseTurnItIs * 3)) // third row
   || (board[0] + board[3] + board[6] == (whoseTurnItIs * 3)) // first column
   || (board[1] + board[4] + board[7] == (whoseTurnItIs * 3)) // second column
   || (board[2] + board[5] + board[8] == (whoseTurnItIs * 3)) // third column
   || (board[0] + board[4] + board[8] == (whoseTurnItIs * 3)) // first diagonal
   || (board[2] + board[4] + board[6] == (whoseTurnItIs * 3)) // second diagonal
) 
{
  printTheBoard(board);
  System.out.println("恭喜,你是大赢家!");
  break;
} else {
  numberOfSquaresPlayed++;
  whoseTurnItIs = (whoseTurnItIs == 'x') ? 'o' : 'x';
}
```
编程的过程要灵活使用数据结构的特性,要会变通,虽然char这种使用场景以后都可能不是再遇到了。

### Step6: 回合切换
果我们的条件检查显示一行已经完成,我们会祝贺玩家并跳出循环,正如我们从上面的代码中看到的那样:
```java
  printTheBoard(board);
  System.out.println("恭喜,你是大赢家!");
  break;
```
如果一行未完成,我们会增加所玩方块的数量并改变回合。我们使用三元运算符来实现这一点。
```java
numberOfSquaresPlayed++;
whoseTurnItIs = (whoseTurnItIs == 'x') ? 'o' : 'x';
```
作为一名新开发人员,我一直不喜欢三元运算符的神秘语法。作为一名经验丰富的 Java 开发人员,我仍然不喜欢它,但他用起来确实方便。

### Step7: 处理平局
如果游戏进行了九次并且没有获胜者,则循环退出。 此时,我们宣布比赛平局并向双方表示祝贺:
```java
if (numberOfSquaresPlayed == 9) 
{
  printTheBoard(board);
  System.out.println("一场恶战,双方平局!");
}
```
### Step8: 运行游戏
将所有代码整合在一起,保存更改,然后运行 Java 井字棋游戏。

只要你提供有效的输入并遵循正常流程,游戏就会完美执行。

以下是到目前为止编写的完整、可运行的 TicTacToe Java 类: 

## 游戏源码
```java
package com.example.demo;

import java.util.Scanner;

/**
 * @author: xiaohei
 * @date: 2024/10/14 14:13
 */
public class TictakToe {
    public static void main(String[] args) {

        char[] board = { '1', '2', '3',
                '4', '5', '6',
                '7', '8', '9' };

        int numberOfSquaresPlayed = 0;
        char whoseTurnItIs = 'x';
        String gameEndingMessage = "";

        while (numberOfSquaresPlayed < 9) {
            printTheBoard(board);
            getTheUserToSelectTheirSquare(board, whoseTurnItIs);

            if (theyWonTheGame(board, whoseTurnItIs)) {
                gameEndingMessage = "恭喜,你是大赢家!";
                break;
            } else if (numberOfSquaresPlayed == 9) {
                gameEndingMessage = "一场恶战,双方平局!";
            } else {
                numberOfSquaresPlayed++;
                whoseTurnItIs = (whoseTurnItIs == 'x') ? 'o' : 'x';
                continue;
            }
        }
        printTheBoard(board);
        System.out.println(gameEndingMessage);
    }

    private static void printTheBoard(char[] board) {
        System.out.printf("%n %s | %s | %s %n", board[0], board[1], board[2]);
        System.out.println(" - + - + - ");
        System.out.printf(" %s | %s | %s %n",   board[3], board[4], board[5]);
        System.out.println(" - + - + - ");
        System.out.printf(" %s | %s | %s %n%n", board[6], board[7], board[8]);
    }

    private static void getTheUserToSelectTheirSquare(char[] board, char whoseTurnItIs) {

        do {
            try {
                System.out.printf("轮到玩家 %s 选择未被占据的方格:", whoseTurnItIs);
                Scanner scanner = new java.util.Scanner(System.in);
                int input = scanner.nextInt();

                if (Character.isDigit(board[input - 1])) {
                    board[input - 1] = whoseTurnItIs;
                    break;
                } else {
                    System.out.println("这个地方已经被占用了");
                }
            } catch (Exception e) {
                System.out.println("程序异常,重试");
            }
            printTheBoard(board);
        } while (true);
    }

    private static boolean theyWonTheGame(char[] board, char whoseTurnItIs) {
        return (board[0] + board[1] + board[2] == (whoseTurnItIs * 3)) // first row
                || (board[3] + board[4] + board[5] == (whoseTurnItIs * 3))     // second row
                || (board[6] + board[7] + board[8] == (whoseTurnItIs * 3))     // third row
                || (board[0] + board[3] + board[6] == (whoseTurnItIs * 3))     // first column
                || (board[1] + board[4] + board[7] == (whoseTurnItIs * 3))     // second column
                || (board[2] + board[5] + board[8] == (whoseTurnItIs * 3))     // third column
                || (board[0] + board[4] + board[8] == (whoseTurnItIs * 3))     // first cross
                || (board[2] + board[4] + board[6] == (whoseTurnItIs * 3));    // second cross
    }
}

```

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部