对井字游戏获胜者的二维数组进行排序

Posted

技术标签:

【中文标题】对井字游戏获胜者的二维数组进行排序【英文标题】:Sorting through 2D array for Tic Tac Toe winner 【发布时间】:2022-01-15 08:15:19 【问题描述】:

我用这种非常令人讨厌的方式在井字游戏中找到获胜者,我只是想知道是否有更简单的方法来做到这一点。 这是我目前使用的方法,正如您所见,它非常多余和重复。任何缩小范围的技巧都会很棒。我在想也许嵌套的 for 循环可能会起作用,但不完全确定如何在其中进行设置。

public static boolean isGameOver(char[][] gameBoard)

    //Testing for Horizontal Win
    if(gameBoard[0][0] == 'X' && gameBoard[0][2] == 'X' && gameBoard [0][4] == 'X') 
        System.out.println("Player Wins!\n");
        playerScore++;
        return true;
    
    if(gameBoard[0][0] == 'O' && gameBoard[0][2] == 'O' && gameBoard [0][4] == 'O') 
        System.out.println("CPU Wins!\n");
        cpuScore++;
        return true;
    
    if(gameBoard[2][0] == 'X' && gameBoard[2][2] == 'X' && gameBoard [2][4] == 'X') 
        System.out.println("Player Wins!\n");
        playerScore++;
        return true;
    
    if(gameBoard[2][0] == 'O' && gameBoard[2][2] == 'O' && gameBoard [2][4] == 'O') 
        System.out.println("CPU Wins!\n");
        cpuScore++;
        return true;
    
    if(gameBoard[4][0] == 'X' && gameBoard[4][2] == 'X' && gameBoard [4][4] == 'X') 
        System.out.println("Player Wins!\n");
        playerScore++;
        return true;
    
    if(gameBoard[4][0] == 'O' && gameBoard[4][2] == 'O' && gameBoard [4][4] == 'O') 
        System.out.println("CPU Wins!\n");
        cpuScore++;
        return true;
    

    //Testing for Vertical Win
    if(gameBoard[0][0] == 'X' && gameBoard[2][0] == 'X' && gameBoard [4][0] == 'X') 
        System.out.println("Player Wins!\n");
        playerScore++;
        return true;
    
    if(gameBoard[0][0] == 'O' && gameBoard[2][0] == 'O' && gameBoard [4][0] == 'O') 
        System.out.println("CPU Wins!\n");
        cpuScore++;
        return true;
    
    if(gameBoard[0][2] == 'X' && gameBoard[2][2] == 'X' && gameBoard [4][2] == 'X') 
        System.out.println("Player Wins!\n");
        playerScore++;
        return true;
    
    if(gameBoard[0][2] == 'O' && gameBoard[2][2] == 'O' && gameBoard [4][2] == 'O') 
        System.out.println("CPU Wins!\n");
        cpuScore++;
        return true;
    
    if(gameBoard[0][4] == 'X' && gameBoard[2][4] == 'X' && gameBoard [4][4] == 'X') 
        System.out.println("Player Wins!\n");
        playerScore++;
        return true;
    
    if(gameBoard[0][4] == 'O' && gameBoard[2][4] == 'O' && gameBoard [4][4] == 'O') 
        System.out.println("CPU Wins!\n");
        cpuScore++;
        return true;
    

    //Testing for Diagonal Win
    if(gameBoard[0][0] == 'X' && gameBoard[2][2] == 'X' && gameBoard [4][4] == 'X') 
        System.out.println("Player Wins!\n");
        playerScore++;
        return true;
    
    if(gameBoard[0][0] == 'O' && gameBoard[2][2] == 'O' && gameBoard [4][4] == 'O') 
        System.out.println("CPU Wins!\n");
        cpuScore++;
        return true;
    
    if(gameBoard[4][0] == 'X' && gameBoard[2][2] == 'X' && gameBoard [0][4] == 'X') 
        System.out.println("Player Wins!\n");
        playerScore++;
        return true;
    
    if(gameBoard[4][0] == 'O' && gameBoard[2][2] == 'O' && gameBoard [0][4] == 'O') 
        System.out.println("CPU Wins!\n");
        cpuScore++;
        return true;
    

    //Testing for Tie
    if(gameBoard[0][0] != ' ' && gameBoard[0][2] != ' ' && gameBoard[0][4] != ' ' &&
            gameBoard[2][0] != ' ' && gameBoard[2][2] != ' ' && gameBoard[2][4] != ' ' &&
            gameBoard[4][0] != ' ' && gameBoard[4][2] != ' ' && gameBoard[4][4] != ' ') 
        System.out.println("It's a tie!!!\n");
        numOfTies++;
        return true;
    
    return false;

【问题讨论】:

【参考方案1】:

好吧,我有点得意忘形了。但也许你可以使用这里提出的一些想法。我的主要目标是做到这一点,这样每次移动后都不需要检查整个棋盘。这是在程序设计阶段最好考虑的问题类型。

我创建了一个TriGroup 类(它本质上是一个用于保存连续移动的可变字符串。 然后使用地图来保存所有具有共同坐标的分组。 进行移动时,这些分组会附加当前玩家。 并检查该玩家是否获胜。 此程序将使用随机动作自行运行,从而产生胜利或平局。

有些边境案件可能被忽略了。

public class TicTacToeCheck 
    int moveCount = 0;
    static int MAX_MOVES = 27;
    class TriGroup 
        public String group = "";
        
        @Override
        public String toString() 
            return group;
        
    
    
    TriGroup row1 = new TriGroup();
    TriGroup row2 = new TriGroup();
    TriGroup row3 = new TriGroup();
    TriGroup col1 = new TriGroup();
    TriGroup col2 = new TriGroup();
    TriGroup col3 = new TriGroup();
    TriGroup diag1 = new TriGroup();
    TriGroup diag2 = new TriGroup();
    
    Map<String, List<TriGroup>> commonGroupings = new HashMap<>();
    
        commonGroupings.put("00", List.of(row1, col1, diag1));
        commonGroupings.put("02", List.of(row1, col2));
        commonGroupings.put("04", List.of(row1, col3));
        
        commonGroupings.put("20", List.of(row2, col1));
        commonGroupings.put("22", List.of(row2, col2, diag1, diag2));
        commonGroupings.put("24", List.of(row2, col3));
        
        commonGroupings.put("40", List.of(row3, col1, diag1));
        commonGroupings.put("42", List.of(row3, col2));
        commonGroupings.put("44", List.of(row3, col3));
    
    
    public static void main(String[] args) 
        new TicTacToeCheck().start();
    
    
    public void start() 
        
        char player = 'X';
        Random r = new Random();
        outer: while (moveCount < MAX_MOVES) 
            commonGroupings.entrySet().forEach(System.out::println);
            
            System.out.println();
            int row = r.nextInt(3) * 2;
            int col = r.nextInt(3) * 2;
            System.out.println("Move: " + row + ", " + col);
            player = player == 'X' ? 'O' : 'X';
            char val;
            switch (val = recordMove(row, col, player)) 
                case 'X' -> 
                    System.out.println("X wins!");
                    break outer;
                
                case 'O' -> 
                    System.out.println("O wins!");
                    break outer;
                
                case 0 -> 
                    System.out.println("Tie!");
                    break outer;
                
                
                default -> 
                
                
            
        
        commonGroupings.entrySet().forEach(System.out::println);
    
    
    public char recordMove(int row, int col, char c) 
        moveCount++;
        
        for (TriGroup tri : commonGroupings.get(row + "" + col)) 
            if (tri.group.length() > 2) 
                // just ignore the row/col and try the next
                continue;
            
            
            // update group
            tri.group += c;
            if (tri.group.equals(c + "" + c + "" + c)) 
                return c;
            
            
        
        if (moveCount == MAX_MOVES) 
            return 0;
        
        return '#';
    

【讨论】:

我真的很喜欢这种方法(你有一个目标,你成功了;而且代码很漂亮)。但是,我认为作为对 OP 问题的回答,这太过分了。简单就是好的,特别是在游泳池的浅水区。【参考方案2】:

此版本使用 2 个辅助的私有方法来减少代码重复性,而不会改变调用 isGameOver 的行为。主要观察结果是,通过O 检查是否获胜或通过X 检查获胜之间几乎没有区别——只有一个字符。这样就变成了checkWins。下一个观察结果是,检查棋盘上相邻的 3 个位置涉及大量重复。如果你给我expect 的字符,从哪里开始,接下来看哪里(dcoldrow),就变成了allEqual

您的代码跳过了棋盘的不均匀位置;我的代码没有。我觉得将展示(“你如何向用户展示东西”)与模型(“你如何在内部展示东西”)混为一谈是错误的;所以我的代码目前不是你的替代品,但可以通过调整 checkWins 中的值快速修复为这样的替代品(对角线向下将是 0, 0, 2, 2,依此类推)。

请注意,就效率而言,您的代码可能更快。但我发现这个版本更短、更易读,因此更易于调试和维护。

private static boolean allEqual(char expected, char[][] b, 
                                int row, int col, int drow, int dcol) 
    for (int i=0; i<b[0].length; i++) 
        if (b[row][col] != expected) return false;
        row += drow;
        col += dcol;
    
    return true;


private static boolean checkWins(char playerChar, char[][]b) 
    boolean win = allEqual(playerChar, b, 0, 0, 0, 1)  // 1st row 
               || allEqual(playerChar, b, 1, 0, 0, 1) 
               || allEqual(playerChar, b, 2, 0, 0, 1)  // 3rd row
               || allEqual(playerChar, b, 0, 0, 1, 0)  // 1st col
               || allEqual(playerChar, b, 0, 1, 1, 0) 
               || allEqual(playerChar, b, 0, 2, 1, 0)  // 3rd col
               || allEqual(playerChar, b, 0, 0, 1, 1)  // diagonal down
               || allEqual(playerChar, b, 2, 0, 1,-1); // diagonal up
    return win;


public static boolean isGameOver(char[][] gameBoard) 
    if (checkWins('X', gameBoard)) 
        System.out.println("Player Wins!\n");
        playerScore ++;
        return true;
     else if (checkWins('O', gameBoard)) 
        System.out.println("CPU Wins!\n");
        cpuScore ++;
        return true;
     else 
        return false;
    

【讨论】:

【参考方案3】:

看看这个: https://www.geeksforgeeks.org/tic-tac-toe-game-in-java/

添加gameBoard[x][y] 的字符串并在switch 语句中检查它们。 如果复合字符串等于XXXOOO,则可以返回获胜者。

对于您的代码,如下所示:

for (int a = 0; a < 8; a++) 
            String line = null;
  
            switch (a) 
            case 0:
                line = gameBoard[0][0] + gameBoard[0][1] + gameBoard[0][2];
                break;
            case 1:
                line = gameBoard[1][0] + gameBoard[1][1] + gameBoard[1][2];
                break;
            case 2:
                line = gameBoard[2][0] + gameBoard[2][1] + gameBoard[2][2];
                break;
            case 3:
                line = gameBoard[0][0] + gameBoard[1][0] + gameBoard[2][0];
                break;
            case 4:
                line = gameBoard[0][1] + gameBoard[1][1] + gameBoard[2][1];
                break;
            case 5:
                line = gameBoard[0][2] + gameBoard[1][2] + gameBoard[2][2];
                break;
            case 6:
                line = gameBoard[0][0] + gameBoard[1][1] + gameBoard[2][2];
                break;
            case 7:
                line = gameBoard[0][2] + gameBoard[1][1] + gameBoard[2][0];
                break;
            
            //For X winner
            if (line.equals("XXX")) 
                return "X";
            
              
            // For O winner
            else if (line.equals("OOO")) 
                return "O";
            
        

【讨论】:

以上是关于对井字游戏获胜者的二维数组进行排序的主要内容,如果未能解决你的问题,请参考以下文章

我不能在不使用指针的情况下返回二维数组!在制作井字游戏时,这是我的上移功能

sort int[][] 二维数组排序

Vector容器 二维数组sort()排序

PHP中如何对二维数组按某个键值进行排序

如何对二维数组的列进行排序?

JAVA中如何对二维数组的每一列进行排序