Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Lab 5 最佳实践与思考题 #1

Open
geeeeeeeeek opened this issue Oct 18, 2016 · 0 comments
Open

Lab 5 最佳实践与思考题 #1

geeeeeeeeek opened this issue Oct 18, 2016 · 0 comments

Comments

@geeeeeeeeek
Copy link
Member

geeeeeeeeek commented Oct 18, 2016

基本功能

使用我们已有的知识,最优的代码应该在 20 - 30 行左右。大家可以对比一下自己的代码和下面的示例代码,看看你有哪些冗余的代码是可以精简的。

字符串实现

字符串实现完全使用了我们已学的知识,示例如下:

import java.util.Scanner;

public class TicTacToe {

    public static void main(String[] args) {
        String board = ".........";
        boolean player = true;
        Scanner scanner = new Scanner(System.in);
        while (true) {
            System.out.println(board.substring(0, 3));
            System.out.println(board.substring(3, 6));
            System.out.println(board.substring(6, 9));
            System.out.print("Next move: ");
            int nextPosition = scanner.nextInt();
            board = board.substring(0, nextPosition)
                    + ((player) ? 'O' : 'X')
                    + board.substring(nextPosition + 1, board.length());
            player = !player;
        }
    }
}

有以下地方需要同学们注意:

  • 使用 while (true) 循环:基本功能不要求实现终局判断,因此我们只需要一个死循环来实现用户的交替输入。for 循环的等价实现是 for (;;) ,显然没有 while (true) 来得直观。
  • 使用一个 boolean 变量来记录当前玩家:游戏中只有两个玩家,而布尔值恰好只提供两个值(truefalse),因此我们可以用 true 来代表玩家 X ,而 false 代表玩家 O
  • while () {}循环的意思是当()中的表达式值为true时会执行{}中的语句。while(true)因为括号中的布尔表达式恒为true,所以会无限循环下去。请明确布尔变量player的意义,这个布尔变量与循环控制条件没有任何关系。
  • 更新棋盘状态:假设我们要更新第 nextPosition 个位置,其实是把前几位、更新的这一位及后几位拼起来,再重新赋值给 board 。这里还用到了三目运算符,你也可以用 if...else... 替换 。

一维数组实现

上次 lab 最后让大家自学了一维数组。如果使用这种方法,下面的实现应该是最优的:

import java.util.Scanner;

public class TicTacToe {

    public static void main(String[] args) {
        char[] board = {'.', '.', '.', '.', '.', '.', '.', '.', '.'};
        boolean player = true;
        Scanner scanner = new Scanner(System.in);
        while (true) {
            for (int i = 0; i < 3; i++) {
                String row = "" + board[i * 3] + board[i * 3 + 1] + board[i * 3 + 2];
                System.out.println(row);
            }
            System.out.print("Next move: ");
            int nextPosition = scanner.nextInt();
            board[nextPosition] = (player) ? 'O' : 'X';
            player = !player;
        }
    }
}

有以下地方需要同学们注意:

  • 数组的输出需要借助循环来完成。思考里面的 for 循环,它为什么能输出整个棋盘?
  • 思考 row 的赋值语句中,为什么要在最左边加上 ""

二维数组实现

二维数组还没有学到,仅供学有余力的同学了解。

import java.util.Scanner;

public class TicTacToe {

    public static void main(String[] args) {
        char[][] board = {{'.', '.', '.'}, {'.', '.', '.'}, {'.', '.', '.'}};
        boolean player = true;
        Scanner scanner = new Scanner(System.in);
        while (true) {
            for (int i = 0; i < 3; i++) {
                for (int j = 0; j < 3; j++) {
                    System.out.print(board[i][j]);
                }
                System.out.println();
            }
            System.out.print("Next move: ");
            int nextPosition = scanner.nextInt();
            board[nextPosition / 3][nextPosition % 3] = (player) ? 'O' : 'X';
            player = !player;
        }
    }
}

方法调用实现

一些同学使用了方法调用,因此这里也给出了一份在一维数组基础上的方法调用实现。当然,你也可以用其他方式组织你的方法。

import java.util.Scanner;

public class TicTacToe {

    public static void main(String[] args) {
        char[] board = {'.', '.', '.', '.', '.', '.', '.', '.', '.'};
        boolean player = false;
        Scanner scanner = new Scanner(System.in);
        while (true) {
            printBoard(board);
            board = setBoard(board, getNextPosition(scanner), player = !player);
        }
    }

    private static int getNextPosition(Scanner scanner) {
        System.out.print("Next move: ");
        return scanner.nextInt();
    }

    private static void printBoard(char[] board) {
        for (int i = 0; i < 3; i++) {
            String row = "" + board[i * 3] + board[i * 3 + 1] + board[i * 3 + 2];
            System.out.println(row);
        }
    }

    private static char[] setBoard(char[] board, int position, boolean player) {
        board[position] = (player) ? 'O' : 'X';
        return board;
    }
}

附加功能

提供一份在一维数组基础上实现的附加功能示例代码。

import java.util.Scanner;

public class TicTacToe {

    public static void main(String[] args) {
        char[] board = {'.', '.', '.', '.', '.', '.', '.', '.', '.'};
        int availablePositions = 9;
        Scanner scanner = new Scanner(System.in);
        printBoard(board);
        while (availablePositions > 0) {
            int nextPosition = getNextPosition(scanner, board);

            board[nextPosition] = (availablePositions % 2 == 1) ? 'O' : 'X';
            availablePositions -= 1;

            printBoard(board);
            if (checkGameOver(board, nextPosition, availablePositions)) return;
        }
    }

    /**
     * Read next position from stdin.
     */
    private static int getNextPosition(Scanner scanner, char[] board) {
        System.out.print("Next move: ");
        int nextPosition = scanner.nextInt();
        while (!checkIsValidInput(nextPosition, board)) {
            System.out.println("Invalid input. Try again with an available position by an integer between [0, 8].");
            nextPosition = scanner.nextInt();
        }
        return nextPosition;
    }

    /**
     * Check if the game has ended.
     */
    private static boolean checkGameOver(char[] board, int nextPosition, int availablePositions) {
        boolean hasWon = checkHasWon(board, nextPosition);
        // Check if the game is over
        if (hasWon) {
            System.out.println(((availablePositions % 2 == 0) ? 'O' : 'X') + " has won. Exit.");
            return true;
        } else if (availablePositions == 0) {
            System.out.println("Ended in a draw. Exit.");
            return true;
        }
        return false;
    }

    /**
     * Check if a player has won.
     */
    private static boolean checkHasWon(char[] board, int nextPosition) {
        int row = nextPosition / 3, col = nextPosition % 3;
        boolean hasWon = ((board[row * 3] == board[row * 3 + 1]) && (board[row * 3 + 1] == board[row * 3 + 2]))
                || ((board[col] == board[col + 3]) && (board[col + 3] == board[col + 6]))
                || ((row == col) && (board[0] == board[4]) && (board[4] == board[8]))
                || ((row == 2 - col) && (board[2] == board[4]) && (board[4] == board[6]));
        return hasWon;
    }

    /**
     * Check if input is valid.
     */
    private static boolean checkIsValidInput(int nextPosition, char[] board) {
        return nextPosition >= 0 && nextPosition <= 8 && board[nextPosition] == '.';
    }

    /**
     * Print board.
     */
    private static void printBoard(char[] board) {
        for (int i = 0; i < 3; i++) {
            String row = "" + board[i * 3] + board[i * 3 + 1] + board[i * 3 + 2];
            System.out.println(row);
        }
    }
}

有以下地方需要同学们注意:

  • 思考终局判断中下列四行的作用分别是什么?

    boolean hasWon = ((board[row * 3] == board[row * 3 + 1]) && (board[row * 3 + 1] == board[row * 3 + 2]))
    || ((board[col] == board[col + 3]) && (board[col + 3] == board[col + 6]))
    || ((row == col) && (board[0] == board[4]) && (board[4] == board[8]))
    || ((row == 2 - col) && (board[2] == board[4]) && (board[4] == board[6]));
  • 程序是如何判断能否落子的?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant