How to determine 5 labels in a row of the same backcolor in a C# array?

送分小仙女□ 提交于 2019-12-13 03:01:15

问题


I created a two dimensional array [19,19] of labels. On click a user changes the color of a label to either black or white depending on the iteration. I want a message box to pop up when there are five labels of the same color lined up horizontally. This is to be done without recursion. Any help would be much appreciated.

public partial class Form1 : Form
{
int labelCount = 0;
        int iteration = 0;
        public Label[,] board = new Label[19,19];
        const int WinLength = 5;
        const int BoardWidth = 19;
        const int BoardHeight = 19;
        gamePlay obj = new gamePlay();
    public Form1()
    {
        InitializeComponent();
}

private void labelClick (object sender, EventArgs e)
{
Label x = (Label)sender;

            if (x.BackColor == Color.Transparent)
            {
                if (iteration % 2 == 0)
                {
                    x.BackColor = Color.Black;
                }
                else
                {
                    x.BackColor = Color.White;
                }
                iteration++;
            }
            else
            {

            }

            for (int r = 0; r < BoardHeight; r++)
            {
                for (int c = 0; c < BoardWidth; c++)
                {
                    if (board[r, c] == x)
                    {
                        Color? winner = obj.CheckForWinner(board, r, c);
                        if (winner == Color.Black)
                        {
                            MessageBox.Show("Black is the winner!");
                        }
                        else if (winner == Color.White)
                        {
                            MessageBox.Show("White is the winner!");
                        }
                        // else winner is null, meaning no winner yet. 
                    }
                }
            }

        private int[] FindClickedLabelCoordinates(Label[,] board, Label label)
        {
            for (int r = 0; r < BoardHeight; r++)
            {
                for (int c = 0; c < BoardWidth; c++)
                {
                    if (board[r, c] == label)
                        return new int[] { r, c };
                }
            }
            return null;
        }
}


    class gamePlay
{
        const int WinLength = 5;
const int BoardWidth = 19;
const int BoardHeight = 19;

    private Color? CheckForWinner(Label[,] board, int r, int c)
    {
        Color startColor = board[r, c].BackColor;
        for (int c1 = c - WinLength + 1; c1 <= c; c1++)
        {
            if (c1 >= 0 && c1 < BoardWidth && board[r, c1].BackColor == startColor)
            {
                MessageBox.Show("you win!");
                bool win = true;
                for (int c2 = c1 + 1; c2 < c1 + WinLength; c2++)
                {
                    if (c2 < 0 || c2 >= BoardWidth || board[r, c2].BackColor != startColor)
                    {
                        win = false;
                        break;
                    }
                }
                if (win)
                {

                    return startColor;
                }

            }
        }
        return null;
    }

回答1:


You actually don't need recursion to solve this; just a couple of nested loops. Since you said you wanted to start just by checking for horizontal wins, I'll start there. I'll use the term "stone" to represent a space on the board that has been clicked to turn it black or white.

If there are no stones on the board, we know there is not a win present. That is the default state of the game. A win can only be present once 5 stones of the same color are placed on the board. And since only one stone is placed at a time, and we will be checking for wins after each placement, then the last stone placed must be part of the winning move, and the winning color must be the color of that stone. It sounds obvious, but it means that it is not necessary to check the entire board for a win each time. We only need to check the possible wins that could involve the last stone placed. Furthermore, we don't need to check separately that they are all black or all white; we just need to check that they are all the same color as the last move, whatever color that was.

So, if the only possible win conditions are horizontal, what squares do we need to check? Well, let's say that the last stone placed was in the very center of a row. Let's call that square x. If the stone was part of a win, and a win is 5 in a row, then there are 5 possible wins that stone could be part of:

  • {x-4, x-3, x-2, x-1, x}
  • {x-3, x-2, x-1, x, x+1}
  • {x-2, x-1, x, x+1, x+2}
  • {x-1, x, x+1, x+2, x+3}
  • {x, x+1, x+2, x+3, x+4}

So, that suggests an algorithm:

  1. Get the row and column of the last stone placed on the board.
  2. Get the color of that stone.
  3. For each of the 5 possible win sequences:
    a) Start at the leftmost board space of the sequence (e.g. x-4 for the first one)
    b) Check that it and the next 4 board spaces to the right of it contain stones of the same color as the color from step 2.
    c) If any space in the sequence does not have a stone matching the color from step 2, it's not a win; we can stop checking and immediately move onto the next possible win sequence.
    d) If we make it through the whole sequence and all the spaces have stones of the correct color, then it's a win and we can return the winning color.
  4. If we go through all possible sequences without finding a win, then the game continues.

But, there's one more thing to consider. What if the last stone was placed near the left or right edge of the board? For example, if x is 2, then the first two possible win sequences, starting with x-4 and x-3, respectively, are invalid because they are partially off the board! So we'll need to add some checks to make sure that the spaces we want to look at are actually in bounds.

Here is what the above algorithm might look like in code. The r and c parameters are the row and column of the last stone placed:

const int WinLength = 5;
const int BoardWidth = 19;
const int BoardHeight = 19;

private Color? CheckForWinner(Label[,] board, int r, int c)
{
    Color startColor = board[r, c].BackColor;
    for (int c1 = c - WinLength + 1; c1 <= c; c1++)
    {
        if (c1 >= 0 && c1 < BoardWidth && board[r, c1].BackColor == startColor)
        {
            bool win = true;
            for (int c2 = c1 + 1; c2 < c1 + WinLength; c2++)
            {
                if (c2 < 0 || c2 >= BoardWidth || board[r, c2].BackColor != startColor)
                {
                    win = false;
                    break;
                }
            }
            if (win) return startColor;
        }
    }
    return null;
}

Here is how you might call the above method from your click handler:

private void labelClick(object sender, EventArgs e)
{
    // set background color of clicked label here based on whose turn it is

    int[] coords = FindClickedLabelCoordinates(board, (Label)sender);

    Color? winner = CheckForWinner(board, coords[0], coords[1]);
    if (winner == Color.Black)
    {
        MessageBox.Show("Black is the winner!");
    }
    else if (winner == Color.White)
    {
        MessageBox.Show("White is the winner!");
    }
    // else winner is null, meaning no winner yet.

}

private int[] FindClickedLabelCoordinates(Label[,] board, Label label)
{
    for (int r = 0; r < BoardHeight; r++)
    {
        for (int c = 0; c < BoardWidth; c++)
        {
            if (board[r, c] == label)
                return new int[] { r, c };
        }
    }
    return null;
}

Hopefully that is enough to get you started.




回答2:


At 19 x 19 they would need to click an awful lot to slow your program down even if you did check the whole board. Something to think about: unless you can show it's too slow for your use case, don't optimize too early because it's wasted effort. If you have the click location and can figure out the location, you can recurse from there, trying to find adjacent locations with the same color.

The basic method is to start in one spot and recurse left/right or up/down (or whatever your requirements are) while keeping a counter. Once you reach your success condition, return all the way up.




回答3:


Firstly, define "horizontally". Assume its the first array bounds [x][y].

Then you just need a function to check one row.

 // Has the player won (5 horizontal ticks)?
 public bool HasWinner(Label[,] board, Colour player)
 {
     for (int y = 0; y < 19; y++)
     {
        if (RowHasLine(y, board, player)
           return true;
     }

     return false;
 }

 // Returns true if player has a winner in row y
 private bool RowHasLine(int y, Label[,] board, Colour player)
 {
     for (int x = 0; x < 19; x++)
     {
         if (Ticked(x + 0, y, board, player) &&
             Ticked(x + 1, y, board, player) &&
             Ticked(x + 2, y, board, player) &&
             Ticked(x + 3, y, board, player) &&
             Ticked(x + 4, y, board, player))
         {
             return true;
         }
     }

     return false;
 }

 // Has the player ticked this box?
 private bool Ticked(int x, int y, Label[,] board, Colour player)
 {
     if (x >= 19 || y >= 19)
         return false;

     return (board[x][y].BackgroundColor == player);
 }


来源:https://stackoverflow.com/questions/33770469/how-to-determine-5-labels-in-a-row-of-the-same-backcolor-in-a-c-sharp-array

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!