winforms 程序中的某个函数看起来应该可以工作,但偶尔会给出错误的输出

xxe27gdn  于 2023-01-05  发布在  其他
关注(0)|答案(1)|浏览(156)

我编写了一个函数,用于检查网格中给定的单元格是否符合棋盘游戏Reversi(也称为Othello)的规则。规则是,只有当新放置的圆和先前放置的圆限制了对手的圆之一时,才能在网格上放置圆。大多数情况下,函数给出了正确的输出(即,当它是合法移动时为真,而当它是合法移动时为假),但是根据先前所述规则合法的一些移动不被该函数视为合法移动。
我试着在每一步都使用控制台来检查函数当前正在查找的单元格,以及单元格的值是什么,以确定哪里出错了,这只会让我更加困惑。
下面的代码是doomed-function:

bool legalMove(int row, int col)
    {
        // Check if the cell is occupied
        if (board[row,col] != 0)
            return false;

        // Check if there's an opponents circle somewhere around it
        for (int i = -1; i<=1; i++)
            for (int j = -1; j<=1; j++)
            {
                if (i == 0 && j == 0)
                    continue;

                int currentRow = row + i;
                int currentCol = col + j;

                if (currentRow >= 0 && currentRow < board.GetLength(0) && currentCol >= 0 && currentCol < board.GetLength(1) && board[currentRow,currentCol] == -turn)
                {
                    // Now we know that there's an opponents circle somewhere around this space, we now check if it can be captured
                    while(true)
                    {
                        currentRow += i;
                        currentCol += j;

                        Console.WriteLine($"currentRow: {currentRow}, currentCol: {currentCol}, value: {board[currentRow,currentCol]}");

                        if (currentRow < 0 || currentRow >= board.GetLength(0) || currentCol < 0 || currentCol >= board.GetLength(1) || board[currentRow, currentCol] == 0)
                            return false; // Outside of the board or an empty space
                        else if (board[currentRow,currentCol] == turn)
                            return true; // No empty spaces between our cell and another cell of ours 
                    }
                }
            }
        return false; // No cell found around ours
    }

我错过了什么?
先谢了!
编辑:整个程序如下(希望能有所帮助):

/* TO-DO
* Make function out of no legal move and tidy up
* Calculate amount of circles of player to determine the winner
* Make victory label better
!!!Tidy up flipCircles method and fix legalMove method
Create a slider and make it change the gridSize
Make GUI change empty space when board gets smaller or bigger
Tidy up the 2x calling to check what the score is
*/


// Library imports
using System;
using System.Drawing;
using System.Security.Policy;
using System.Windows.Forms;

// Game class
class Game : Form
{
    // Declare variables
    private Board board;
    private Button newGame, help;
    private Font font;
    private Label countRed, countBlue, gameState;
    private TrackBar sizeBoard;

    public int gridSize = 6;

    public Game()
    {
        // Set the form properties
        ClientSize = new Size(520, 670); Text = "Reversi";

        // Creating the GUI and adding it to the form
        newGame = new Button(); Controls.Add(newGame);
        help = new Button(); Controls.Add(help);

        font = new Font("Arial", 14);
        countBlue = new Label(); Controls.Add(countBlue); countBlue.Font = font;
        countRed = new Label(); Controls.Add(countRed); countRed.Font = font;
        gameState = new Label(); Controls.Add(gameState); gameState.Font = font;

        board = new Board(gridSize); Controls.Add(board);

        // Settings of the GUI
        newGame.Size = new Size(100, 30); newGame.Location = new Point(150, 10); newGame.Text = "New Game"; newGame.BackColor = Color.LightSlateGray;
        help.Size = new Size(100, 30); help.Location = new Point(270, 10); help.Text = "Help"; help.BackColor = Color.LightSlateGray;
        countBlue.Size = new Size(110, 30); countBlue.Location = new Point(150, 50); countBlue.Text = $"{board.countBlue} stones"; countBlue.ForeColor = Color.CornflowerBlue;
        countRed.Size = new Size(110, 30); countRed.Location = new Point(150, 90); countRed.Text = $"{board.countRed} stones"; countRed.ForeColor = Color.Firebrick;
        gameState.Size = new Size(150, 30); gameState.Location = new Point(270, 90); gameState.Text = $"{board.playersTurn}";

        // Events //


        // Label events
        newGame.Click += reset;
        help.Click += calculateHelp;

        // Board events
        board.MouseClick += clicked;

        //Paint event
        Paint += paint;
    }

    // Event-handlers //

    // Label event-handlers
    private void reset(object e, EventArgs ea)
    {
        board.Reset();
        countBlue.Text = $"{board.countBlue} stones";
        countRed.Text = $"{board.countRed} stones";
        gameState.Text = $"{board.playersTurn}";
    }

    private void calculateHelp(object e, EventArgs ea)
    {
        board.SetHelp();
    }

    // Board event-handlers
    private void clicked(object e, MouseEventArgs mea)
    {
        board.Clicked(mea.Location);
        countBlue.Text = $"{board.countBlue} stones";
        countRed.Text = $"{board.countRed} stones";
        gameState.Text = $"{board.playersTurn}";

        // Now check if there's a legalMove if not
        //if (timesNoLegalMove > 1)
        gameState.Text = $"{board.playersTurn}";
    }

    // Paint event-handler
    private void paint(object e, PaintEventArgs pea)
    {
        Graphics gr = pea.Graphics;

        gr.FillEllipse(Brushes.CornflowerBlue, 100, 45, 31, 31);
        gr.FillEllipse(Brushes.Firebrick, 100, 85, 31, 31);
    }
}

// Board class
class Board : Label
{
    // Declare all global variables used in this class
    private int[,] board;
    private int size;
    private int turn = 1; // 1 is blue, -1 is red
    private bool legalMoveExists = true;
    public int timesNoLegalMove = 0;
    private bool help = false;

    // Create the board and set settings + events
    public Board(int gridSize)
    {
        size = gridSize;

        Size = new Size(size * 50, size * 50);
        Location = new Point(10 + (25 * (10 - size)), 120 + (25 * (10 - size)));
        BackColor = Color.White;
        board = new int[size, size];

        startingState();

        Paint += Draw;
    }

    // Sets the values of the center 4 squares to that of the starting circles
    private void startingState()
    {
        board[(size / 2)-1, (size / 2)-1] = 1;
        board[(size / 2), (size / 2)] = 1;
        board[(size / 2), (size / 2) - 1] = -1;
        board[(size / 2) - 1, (size / 2)] = -1;
    }

    // Resets the board when New Game is clicked
    public void Reset()
    {
        for (int row = 0; row < board.GetLength(0); row++)
            for (int col = 0; col < board.GetLength(1); col++)
                board[row, col] = 0;
        startingState();
        turn = 1;
        Invalidate();
    }

    public void SetHelp()
    {
        if (help)
            help = false;
        else
            help = true;
        Invalidate();
    }

    public string playersTurn
    {
        get
        {
            if (turn == 1)
                return "It's Blue's turn";
            if (turn == -1)
                return "It's Red's turn";
            else
            {
                if (countBlue > countRed)
                    return "Blue has won the game!";
                if (countRed > countBlue)
                    return "Red has won the game!";
                else
                    return "It's a draw!";
            }
        }

    }

    public int countRed
    {
        get
        {
            return board.Cast<int>().Count(n => n == -1);
        }
    }

    public int countBlue
    {
        get
        {
            return board.Cast<int>().Count(n => n == 1);
        }
    }

    bool legalMove(int row, int col)
    {
        // Check if the cell is occupied
        if (board[row,col] != 0)
            return false;

        // Check if there's an opponents circle somewhere around it
        for (int i = -1; i<=1; i++)
            for (int j = -1; j<=1; j++)
            {
                if (i == 0 && j == 0)
                    continue;

                int currentRow = row + i;
                int currentCol = col + j;

                if (currentRow >= 0 && currentRow < board.GetLength(0) && currentCol >= 0 && currentCol < board.GetLength(1) && board[currentRow,currentCol] == -turn)
                {
                    // Now we know that there's an opponents circle somewhere around this space, we now check if it can be captured
                    while(true)
                    {
                        currentRow += i;
                        currentCol += j;

                        if (currentRow < 0 || currentRow >= board.GetLength(0) || currentCol < 0 || currentCol >= board.GetLength(1) || board[currentRow, currentCol] == 0)
                            return false; // Outside of the board or an empty space
                        else if (board[currentRow,currentCol] == turn)
                            return true; // No empty spaces between our cell and another cell of ours 
                    }
                }
            }
        return false; // No cell found around ours
    }

    private void flipCircles(int row, int col)
    {
        // Check all eight directions from the current position
        for (int r = row - 1; r <= row + 1; r++)
        {
            for (int c = col - 1; c <= col + 1; c++)
            {
                // Skip the current position
                if (r == row && c == col)
                    continue;

                int rr = r;
                int cc = c;

                // Check if the next position in this direction is a valid position on the board
                // and if it is occupied by the opponent's piece
                if (rr >= 0 && rr < board.GetLength(0) && cc >= 0 && cc < board.GetLength(1) && board[rr, cc] == -turn)
                {
                    // Keep moving in this direction until we find the current player's piece or an empty cell
                    while (true)
                    {
                        rr += r - row;
                        cc += c - col;

                        // If we have reached an invalid position or an empty cell, break out of the loop
                        if (rr < 0 || rr >= board.GetLength(0) || cc < 0 || cc >= board.GetLength(1) || board[rr, cc] == 0)
                            break;

                        // If we have found the current player's piece, flip all the pieces between the current position and the player's piece
                        if (board[rr, cc] == turn)
                        {
                            while (rr != r || cc != c)
                            {
                                rr -= r - row;
                                cc -= c - col;
                                board[rr, cc] = turn;
                            }
                            break;
                        }
                    }
                }
            }
        }
    }


    // Sets the value of a clicked cell to either 1 (Blue), or -1 (Red)
    public void Clicked(Point mea)
    {
        int rowClicked = mea.X / 50;
        int colClicked = mea.Y /50;

        if (legalMove(rowClicked, colClicked))
        {
            board[rowClicked, colClicked] = turn;
            flipCircles(rowClicked, colClicked);
            help = false;
            turn = -turn;
            legalMoveExists = false;
            Invalidate();
        }
    }

    // Draws the entire board and all circles
    void Draw(object e, PaintEventArgs pea)
    {
        Graphics gr = pea.Graphics;
        legalMoveExists = false;

        for (int row = 0; row < board.GetLength(0); row++)
            for (int col = 0; col < board.GetLength(1); col++)
            {
                // Draw the background tiles
                if (row % 2 == 0 && col % 2 == 0 || row % 2 != 0 && col % 2 != 0)
                    gr.FillRectangle(Brushes.DarkGray, 50 * row, 50 * col, 50, 50);
                // Draw circles
                if (board[row, col] == 1) // Blue circles
                    gr.FillEllipse(Brushes.CornflowerBlue, 50 * row - 1, 50 * col - 1, 51, 51);
                else if (board[row, col] == -1) // Red circles
                    gr.FillEllipse(Brushes.Firebrick, 50 * row - 1, 50 * col - 1, 51, 51);
                // Check for legal moves and draw help circles if the help button has been pressed
                else if (legalMove(row, col))
                {
                    legalMoveExists = true;
                    timesNoLegalMove = 0;
                    if (help) // Help circles
                        gr.DrawEllipse(Pens.Black, 50 * row + 9, 50 * col + 9, 31, 31);
                }
            }

        //  Make this a function
        if (!legalMoveExists)
        {
            turn = -turn;
            timesNoLegalMove++;
            Invalidate();
            if (timesNoLegalMove > 1)
                turn = 0;
        }
    }
}

// Main run
class Program
{
    static void Main()
    {
        Application.Run(new Game());
    }
}
z6psavjg

z6psavjg1#

你的问题是,你编写的扫描周围细胞的函数偶尔会出现故障,而且很难诊断。通过运行你的代码很容易重现故障,但我无法找到有效调试它的明显方法。
它“可能”更有效地 * 改进算法 *,在那里它在如何检查周围的细胞摆在首位更有条理,如果必要的话,这也会更容易调试。一个可靠的方法是使用定制的迭代器,在那里你可以使用标准的foreach模式来检查在八个方向上辐射的虚拟“线”。在每个“yield”可以检查以查看是否可以根据“法律的移动”或“捕获”来做出确定。
这是一个概念验证网格,旨在演示迭代器是如何工作的。它 * 不会 * 以任何方式从游戏的Angular 评估单元格,但您可以看到它是如何帮助自己做到这一点的。这里的想法是单击任何单元格并观察U-R-D-L的标记。它也可能有助于查看它的工作,以便您可以clone此示例并设置断点。

显示了左、右、上、下迭代器示例-对角线将遵循相同的模式。鼠标按下控件将起始单元格坐标位置作为Point传递:

public IEnumerable<Point> CellsUp(Point point)
{
    while (true)
    {
        point = new Point(point.X, point.Y - 1);
        if (point.Y < 0) break;
        yield return point;
    }
}
public IEnumerable<Point> CellsRight(Point point, int max)
{
    while (true)
    {
        point = new Point(point.X + 1, point.Y);
        if (point.X == max) break;
        yield return point;
    }
}
public IEnumerable<Point> CellsDown(Point point, int max)
{
    while (true)
    {
        yield return point;
        point = new Point(point.X, point.Y + 1);
        if (point.Y == max) break;
    }
}
public IEnumerable<Point> CellsLeft(Point point)
{
    while (true)
    {
        yield return point;
        point = new Point(point.X - 1, point.Y);
        if (point.X < 0) break;
    }
}

该代码为从任何给定点向外进行系统扫描奠定了基础。

private void legalMoveIterationStub(object? sender, EventArgs e)
{
    clear();
    if(sender is Control control)
    {
        control.BackColor = Color.Blue;
        control.Refresh();
        var pos = board.GetCellPosition(control);
        var pt = new Point(pos.Column, pos.Row);
        Control ctrl;
        foreach (var point in CellsUp(pt))
        {
            ctrl = board.GetControlFromPosition(point.X, point.Y);
            ctrl.Text = "U";
            ctrl.Refresh();
            Thread.Sleep(DEMO_DELAY_MS);
            // This is where the cell inspects e.g. for "empty square"
            // or color. Chances are, some condition will be met
            // and you will break from here rather than iterate
            // all the way to the edge of the board each time.
        }
        foreach (var point in CellsRight(pt, board.ColumnCount))
        {
            ctrl = board.GetControlFromPosition(point.X, point.Y);
            ctrl.Text = "R";
            ctrl.Refresh();
            Thread.Sleep(DEMO_DELAY_MS);
        }
        foreach (var point in CellsDown(pt, board.ColumnCount))
        {
            ctrl = board.GetControlFromPosition(point.X, point.Y);
            ctrl.Text = "D";
            ctrl.Refresh();
            Thread.Sleep(DEMO_DELAY_MS);
        }
        foreach (var point in CellsLeft(pt))
        {
            ctrl = board.GetControlFromPosition(point.X, point.Y);
            ctrl.Text = "L";
            ctrl.Refresh();
            Thread.Sleep(DEMO_DELAY_MS);
        }
    }
}

出于测试目的,演示板被模拟成这样:

public partial class Game : Form
{
    public Game()
    {
        InitializeComponent();
        for (int col = 0; col < board.ColumnCount; col++)
        {
            for (int row = 0; row < board.RowCount; row++)
            {
                var tile = new Label
                {
                    BorderStyle = BorderStyle.FixedSingle,
                    Anchor = (AnchorStyles)0xF,
                    Margin = new Padding(1),
                    TextAlign = ContentAlignment.MiddleCenter
                };
                board.Controls.Add(tile, col, row);
                tile.MouseDown += legalMove;
            }
        }
    }
    void clear()
    {
        foreach (Control control in board.Controls)
        {
            control.Text = string.Empty;
            control.BackColor = SystemColors.Control;
        }
        board.Refresh();
    }
    .
    .
    .
}

相关问题