首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >基于文本的连接4游戏

基于文本的连接4游戏
EN

Code Review用户
提问于 2018-10-26 18:41:22
回答 1查看 1.6K关注 0票数 2

我在C#中做了连接4。如果你从来没有玩过连接4,它就像Tic脚趾,除了碎片落在栅格底部,而且网格通常是6x7。

这是我在C#中做的第一个大型项目,所以我不知道我是否写得很好。如何改进我的代码?

代码语言:javascript
运行
复制
using System;

namespace Connect4
{
    class Program
    {
        static void Main(string[] args)
        {
            Engine game = new Engine();

            char player = '1';
            int column;

            bool gameLoop = true;
            bool inputLoop;

            while (gameLoop) {

                System.Console.Clear();
                game.DisplayGrid();

                do {                    
                    inputLoop = true;

                    Console.Write("\nPlayer ");
                    Console.Write(player);
                    Console.Write(": ");

                    if (Int32.TryParse(Console.ReadLine(), out column)) {
                        if (1 <= column && column <= 7) {
                            if (game.DropPieceInGrid(player, column)) {
                                inputLoop = false;
                            }
                            else {
                                System.Console.Clear();
                                game.DisplayGrid();
                                Console.WriteLine("\nThat column is full.");
                            }
                        }
                        else {
                            System.Console.Clear();
                            game.DisplayGrid();
                            Console.WriteLine("\nThe integer must be between 1 and 7.");
                        }
                    }
                    else {
                        System.Console.Clear();
                        game.DisplayGrid();
                        Console.WriteLine("\nPlease enter an integer.");
                    }
                } while (inputLoop);

                if (game.FourInARow(player)) {
                    System.Console.Clear();
                    game.DisplayGrid();
                    Console.Write("\nPlayer ");
                    Console.Write(player);
                    Console.Write(" has won!\n");
                    Console.WriteLine("\nPress enter to quit.");
                    gameLoop = false;
                }
                else if (game.GridIsFull()) {
                    System.Console.Clear();
                    game.DisplayGrid();
                    Console.WriteLine("\nIt is a draw.");
                    Console.WriteLine("\nPress enter to quit.");
                    gameLoop = false;
                }
                else {
                    player = player == '1' ? '2' : '1';
                }
            }

            Console.ReadKey();
        }
    }

    class Engine
    {
        const int NUMBER_OF_ROWS = 6, NUMBER_OF_COLUMNS = 7;
        const char EMPTY = '0', PLAYER1 = '1', PLAYER2 = '2';

        private char[,] grid;

        int pieceCount;

        public Engine()
        {
            grid = new char[NUMBER_OF_ROWS, NUMBER_OF_COLUMNS];

            for (int y = 0; y < NUMBER_OF_ROWS; y++)
                for(int x = 0; x < NUMBER_OF_COLUMNS; x++)
                    grid[y, x] = EMPTY;
        }

        public void DisplayGrid()
        {
            for (int y = 0; y < NUMBER_OF_ROWS; y++) {
                for (int x = 0; x < NUMBER_OF_COLUMNS; x++) {
                    Console.Write(grid[y, x]);
                    Console.Write(' ');
                }
                Console.Write('\n');
            } 
        }

        // Returns true if the piece can be dropped in that column.
        public bool DropPieceInGrid(char player, int column)
        {
            column--;

            if (grid[0, column] != EMPTY)
                return false;

            for (int y = 0; y < NUMBER_OF_ROWS; y++) {
                if ((y == NUMBER_OF_ROWS - 1) || (grid[y + 1, column] != EMPTY)) {
                    grid[y, column] = player;
                    break;
                }
            }

            pieceCount++;
            return true;
        }

        public bool FourInARow(char player)
        {
            // Horizontal check:

            for (int y = 0; y < NUMBER_OF_ROWS; y++)
                for (int x = 0; x < 4; x++)
                    if (grid[y, x] == player && grid[y, x + 1] == player)
                        if (grid[y, x + 2] == player && grid[y, x + 3] == player)
                            return true;

            // Vertical check:

            for (int y = 0; y < 3; y++)
                for (int x = 0; x < NUMBER_OF_COLUMNS; x++)
                    if (grid[y, x] == player && grid[y + 1, x] == player)
                        if (grid[y + 2, x] == player && grid[y + 3, x] == player)
                            return true;

            // Diagonal check:

            for (int y = 0; y < 3; y++) {
                for (int x = 0; x < NUMBER_OF_COLUMNS; x++) {

                    if (grid[y, x] == player) {

                        // Diagonally left:
                        try {
                            if (grid[y + 1, x - 1] == player) {
                                if (grid[y + 2, x - 2] == player)
                                    if (grid[y + 3, x - 3] == player)
                                        return true;
                            }
                        }
                        catch (IndexOutOfRangeException) {}

                        // Diagonally right:
                        try {
                            if (grid[y + 1, x + 1] == player) {
                                if (grid[y + 2, x + 2] == player)
                                    if (grid[y + 3, x + 3] == player)
                                        return true;
                            }
                        }
                        catch (IndexOutOfRangeException) {}
                    }
                }
            }

            return false;
        }

        public bool GridIsFull()
        {
            return pieceCount >= NUMBER_OF_ROWS * NUMBER_OF_COLUMNS;
        }
    }
}
EN

回答 1

Code Review用户

回答已采纳

发布于 2018-10-27 00:13:11

我假设代码按预期工作,因此不会对任何逻辑进行注释。

通用备注

您需要将代码分为函数或类。for循环和Main几乎是可读的。这是您应该做的第一件事情(在编写了一些单元测试之后,这样您就知道您没有破坏任何东西)。如果没有拆分,甚至很难对代码进行注释,因为大多数注释都需要某种形式的逻辑封装。正因为如此,我将发表一篇关于将代码重构为函数的大型文章,也许其他人也会从中受益。为了简洁起见,我将简化和省略一些事情。

主回路

从一开始,您就可以将整个while循环提取到一个函数(甚至是一个类!)。为了简化事情,我现在只使用三个类,当你让它开始工作时,我们可以更进一步。让我们从一个小的依赖关系图开始:

上面的意思是Program类包括包含EngineGame,这将是类似父级的依赖关系。

您的主要方法应该是这样的:

代码语言:javascript
运行
复制
    public static void Main(string[] args){
        var engine = new Engine();
        var game = new Game(engine);
        game.Start();
    }

Game.Start()将是您的主循环。注意,我是通过在构造函数中传递engine对象来向游戏注入引擎。这使您有机会删除依赖项(请参阅这个问题)。

下一个清晰的地方是画一个板。根据干原理,您应该抽象如下:

代码语言:javascript
运行
复制
System.Console.Clear();
game.DisplayGrid();
Console.WriteLine("\nPlease enter an integer.");

对于一个函数(请记住,这发生在game对象中):

代码语言:javascript
运行
复制
void DrawWithMessage(string message)
{
    DisplayGrid();
    Console.WriteLine(message); 
}

其中DisplayGrid()看起来是这样的:

代码语言:javascript
运行
复制
void DisplayGrid(string message)
{
    System.Console.Clear();
    engine.DisplayGrid();
}

为什么我要把它分成你可能会问的两个函数?原因是SRP-ish方法,如果您需要更改网格的显示方式,最好是更改一个专用函数,而不是一个包含其他内容的函数。

接下来,还应该将来自用户的输入提取为两个函数。

代码语言:javascript
运行
复制
// below is inside the input loop
var userInput = Console.ReadLine();
if(IsInputCorrect(userInput)){ 
    game.DropPieceInGrid(player, column); // this no longer returns bool
    inputLoop = false;
}

现在,您的验证封装在一个函数中。此外,DropPieceInGrid不再验证这个位置(它已经在IsInputCorrect中完成了),因为SRP再次出现了- funcion说它会掉进网格中,验证更改不应该适用于这个函数。

现在,验证将如下所示(注意它在DrawWithMessage更改后处理得有多好):

代码语言:javascript
运行
复制
bool IsInputCorrect(string userInput){
    if (Int32.TryParse(Console.ReadLine(), out column)) {
        if (1 <= column && column <= 7) {
            if (CanDropOnGrid(player, column)) { // this would be a new function with validation logic from DropPieceInGrid
                return true;
            }
            else {
                DrawWithMessage("\nThat column is full.");
            }
        }
        else {
            DrawWithMessage("\nThe integer must be between 1 and 7.") 
        }
    }
    else {
        DrawWithMessage("\nPlease enter an integer.")
    }

}

当然,这应该进一步拆分成更小的函数,甚至移到某个Validator类(SRP )中。

以上内容将使您全面了解如何划分代码。这是您可能希望在编程过程中应用的模式列表,以使代码在将来更加清晰:干的接吻Unix规则

票数 2
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/206348

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档