3

This is for a tic tac toe game where the grid can be sized to any number (3x3 4x4 8x8 etc)

When the form loads, a method reads the grid size and populates the form with that many buttons in a 1 dimensional Button Array. The Array is called buttonArray.

With just using the 1-dimensional buttonArray and without using LINQ, how can i compare the .Text values of buttons in buttonArray to see if they are either "X" or "O" for a CheckWinner() function.

After the button grid is created, I have an event handler for button clicks :

private void button_click(object sender, EventArgs e)
    {
        Button b = (Button)sender;
        b.Text = "X";
        b.Enabled = false;
        CheckWinner(buttonArray);
} 

I am then calling my CheckWinner function and passing in the array buttonArray.

Again, I am just looking for a primitive way to check/compare values without using LINQ. If I know the length of each side of the grid, I can say that for Horizontal win lines, I am looking for that many buttons in a row with "X" as their .Text property.

So if I have a grid of 5x5, and I have 25 buttons in buttonArray, how can i check every 5 starting at the 0 index of the grid for their .Text values being "X" and then print a message if 5 in a row are the same, or "X" in this case.

for (int z = 0; z < root; z++) //increments the column to start on
        {
            vCount = 0; //Starts the win counter at zero when moving to the next column
            for (int i = z; i < arrLength; i=i+root) //starts at the first column and increments it by root to test the next button in the column
            {
                string bText = buttonArray[i].Text;
                if (bText == "X")
                    vCount++;
                if (vCount == root)
                {
                    MessageBox.Show("Vertical row winner found !");
                    break;
                }
            }
        }//end of vertical column test

I did the vertical test like that? But I think combining them into one would def be better.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
BackDoorNoBaby
  • 1,445
  • 2
  • 13
  • 20
  • Do you consider diagonal or only horizontal and vertical winning conditions? – Nikola Davidovic Mar 01 '13 at 18:50
  • Yeah absolutely but I figured just starting with a simple horizontal would be the easiest and I could go from there. – BackDoorNoBaby Mar 01 '13 at 18:52
  • 1
    Well what is the winning condition for the 5x5? Only full diagonal? – Nikola Davidovic Mar 01 '13 at 18:53
  • 5 in a row, either horizontal [0-4][5-9][10-14]etc vertical [0,5,10,15,20] etc diagonal [0,6,12,18,24] and [4,8,12,16,20] if a variable side is equal to the amount of buttons on one side of the grid, so side=5 in the 5x5 grid Then diagonals would be [0,side+1,2*(side+1),3*(side+1),4*(side+1)] – BackDoorNoBaby Mar 01 '13 at 18:58
  • I'm just looking for how I can pass in the array of buttons called buttonArray into my CheckWinner() function, maybe even CheckWinnerX() to keep the O and X separated, and test the .Text properties for the buttons in the array. I would start with the first group of buttons with the group size equal to the length of a side of the grid, and see if they are all "X", if so, produce a winner message, if not, then test the next group. – BackDoorNoBaby Mar 01 '13 at 19:01
  • 1
    I'd also suggest a slight change to the question title - maybe something like... "Finding patterns in array using LINQ" or something? – JerKimball Mar 01 '13 at 19:02
  • I'm trying to avoid using LINQ is the whole thing. – BackDoorNoBaby Mar 01 '13 at 19:16

3 Answers3

2

Assuming that you want to check if Button.Text == "X" in every element of a horizontal row. Following is a very basic way...

public void CheckWinner(Buttons[] buttonArray)
{
    int arrLength = buttonArray.Length; 
    int hCount = 0;
    //int vCount = 0;
    int root = (int)Math.Sqrt(Convert.ToDouble(arrLength));  

    for (int i = 0; i < arrLength ; i++)
    { 
        string bText = buttonArray[i].Text;

        hCount = i % root == 0? 0 : hCount;

        if(bText == "X") 
            hCount++;

        if(hCount == root) 
        {
            Console.WriteLine("Horizontal row winner found !");
            break;
        }
    }

}

To check virtical and/or horizontal winner:

public void CheckWinner(Buttons[] buttonArray)
{
    int arrLength = buttonArray.Length; 
    int hCount = 0;
    Int vCount = 0;
    int root = (int)Math.Sqrt(Convert.ToDouble(arrLength));  

    for (int i = 0;  i < root;  i++)
    {
        hCount = 0;
        vCount = 0;
        for(int j = 0;  j < root; j++)
        {
           if(buttonArray[ (i * root) + j ].Text == "X")
              hCount++;

           if(buttonArray[ i + (root * j) ].Text == "X")
              vCount++;
        }

        if( hCount + vCount == 2 * root)
        {
           Console.WriteLine("Horizontal and Virtical winner found !");
           break;
        } 
        else if ( hCount == root )
        { 
           Console.WriteLine("Horizontal winner found !");
           break;
        }
        else if ( vCount == root )
        { 
           Console.WriteLine("Virtical winner found !");
           break;
        }

    }
}
Kaf
  • 33,101
  • 7
  • 58
  • 78
  • This one is on the right track for what Im looking for but Im getting errors for the (i+1)%5 – BackDoorNoBaby Mar 01 '13 at 19:12
  • Yeah that one pops up when anything on the diagonal is clicked. What im trying to test for is I have a grid that is of side D x D. It is populated with a single dimensional array of Buttons, and I want to check, starting at 0, every D Buttons to see if their .Text property is "X" and if it is, I have winner for a horizontal row. – BackDoorNoBaby Mar 01 '13 at 19:20
  • @BackDoorNoBaby: confused.com. do you want to check `text of every button` or `text of every 5th occurrence`? – Kaf Mar 01 '13 at 19:26
  • text of every button. So after the buttons are created, when I click one, it changes the '.Text == "X"' and then runs the CheckWinner to see if there is a winner. For a 5x5 grid, 5 in a row wins. For a 3x3 grid its every 3 and so on. I'm starting out with horizontal because that is simply the first X amount of buttons in a row, starting with 0 where X is equal to the Sqrt(buttonarray.length) – BackDoorNoBaby Mar 01 '13 at 19:33
  • thats perfect!! I think thats doing what I was looking for! What is the line `hCount = i % 5 == 0? 0 : hCount;` doing there? – BackDoorNoBaby Mar 01 '13 at 19:52
  • Actually, it should be `i % Convert.ToInt32(root)`. Otherwise, you're hard-coded to 5x5. – Scott Mermelstein Mar 01 '13 at 19:56
  • Okay, that is for: `if(i % 5 == 0){ hCount = 0;}else{ hCount = hCount}` in a single line. but you can replace it with `if(i % 5 == 0){ hCount = 0;}`. and `%` is for `modulus` @ScottMermelstein: my eyes are hurting now!... thanks :) ! – Kaf Mar 01 '13 at 19:56
  • I gave you a +1, Kaf. Your answer is now generic for any number, and works right for horizontal. Though I still don't see what's wrong with mine... :-P – Scott Mermelstein Mar 01 '13 at 19:58
  • You guys rock, thank you so much. How can i give you +1 and all that (I'm new to posting on here) – BackDoorNoBaby Mar 01 '13 at 20:05
  • Refresh your page and see if you can click `^` button next to each response you like to up vote. I am not sure if you have enough reputation to up vote but you can certainly accept the answer which would satisfy the unpaid volunteer who supported you from other side of the wire... – Kaf Mar 01 '13 at 20:10
  • @Kaf If i want to modify the loop to check for the vertical win lines, can i keep the same structure or do I need to add another loop to keep could of the column that im checking? same principle as before start at ` 0, check 0+root, 0+2root` etc, and then start at `1, check 1+root, 1+2root` – BackDoorNoBaby Mar 01 '13 at 20:26
  • For virtical check: for 5x5 you need to go like 0,5,10...or 1,6,11...or 2,7,12...etc. basically they are i % root ==0 for 1st col, i %root ==1 for 2nd col ...etc – Kaf Mar 01 '13 at 21:33
  • Okay awesome so rather than increment a value for the column you can use the modulus because as you move up the modulus remainder will account for that? – BackDoorNoBaby Mar 01 '13 at 22:15
  • Would i need to put a loop around around the vertical test to count from 0 until < root in order to have it running for root-1 times? – BackDoorNoBaby Mar 01 '13 at 22:26
  • I guess thats the same as the vertical, maybe a seperate loop for the diags? – BackDoorNoBaby Mar 02 '13 at 00:09
  • got it, it needs to be `buttonArray[j + (j * root)].Text` – BackDoorNoBaby Mar 02 '13 at 00:41
2

I'll throw my option in to the ring, though it looks like all the other answers work, as well. Mine is a basic implementation. It simply goes along each row, and checks to see if you have the same value in each row. The Vertical version of this is as simple as rearranging my two for-loops, and the diagonal isn't too much harder.

It seems to me that the main trick of this question is just to show an understanding of array row and columns, and how to imitate a 2D array given only a 1D array.

For horizontal:

string CheckWinnerHorizontal(Button[] buttonArray) {
    int N = (int)Math.Sqrt(buttonArray.Length);
    for (int row = 0; row < N; ++row)
    {
        string winner = "";
        for (int col = 0; col < N; ++col)
        {
            string value = buttonArray[row * N + col].Text;
            if (winner == "") { winner = value; }
            else if (winner != value) { winner = "none"; }
        }
        if (winner != "none" && winner != "")
        {
            return winner;
        }
    }
    return "";
Scott Mermelstein
  • 15,174
  • 4
  • 48
  • 76
  • its saying cannot convert source type double to int for the Math.Sqrt? – BackDoorNoBaby Mar 01 '13 at 19:24
  • Sorry. Fixed in the edit. Alternatively, if you know your grid size, just pass it in to the function, it'll be more efficient. Since the grid is always a square of the grid size, I was capitalizing on that. – Scott Mermelstein Mar 01 '13 at 19:26
  • Errors are gone but nothing is happening when i get 5 Xs in a row. I dont have a method for the computer to go yet, so Im just focused on clicking a button, putting an X there, and then the program recognizing when I have 5 in a row for a win. – BackDoorNoBaby Mar 01 '13 at 19:35
  • I feel pretty confident in the algorithm. Are you sure it's being executed, and that the buttonArray you pass in has all the right buttons in the right order? Can you step through and see what `value` is showing and if it matches what you clicked? – Scott Mermelstein Mar 01 '13 at 19:37
  • Yeah I clicked the 4th button in the row buttonArray[3] and when it came to that point in the loop it said value was "X" – BackDoorNoBaby Mar 01 '13 at 19:43
  • If you have time, I'd love to know what happens if you click on the top five buttons, and look at winner on the `string value = buttonArray[row * N + col].Text;` line for the first five times. (It should be: "", "X", "X", "X", "X", and then jump down outside of the loop and return winner.) – Scott Mermelstein Mar 01 '13 at 19:53
  • Kaf's example below works but I cant figure out what the `hCount = i % 5 == 0 ? 0 : hCount;` is supposed to be doing? – BackDoorNoBaby Mar 01 '13 at 20:01
  • 1
    `else if (winner != value { winner = "none"; }` what is this for? I think there is a `syntax error` here. – Kaf Mar 01 '13 at 20:03
  • @Kaf's example uses Modulus arithmetic to start new rows. When i % root = 0, that means i is an even multiple of root. So he goes through only one loop 25 times, while I go through two loops, five times each. When his evaluates to zero, he restarts his counter, because he's on a new row. Our loop logic is effectively the same. If mine isn't working, it must be the logic that detects a winner. – Scott Mermelstein Mar 01 '13 at 20:04
  • For mine, on the first column, I keep the Text as a winner. In any other column, if the Text doesn't match the original column, that means there's no winner on that row. Actually, I could've added a break after the `winner = "none"` bit. – Scott Mermelstein Mar 01 '13 at 20:05
  • Bah. I had missed a `)` in my code sample. I'm surprised it compiled for you. Maybe now you'll get the right result. – Scott Mermelstein Mar 01 '13 at 20:10
  • Finally !, Working in partnerships. I'll give you +1 `;)` – Kaf Mar 01 '13 at 20:13
1

Sure, you can give this a shot - I'll do by-rows here, by-cols should be very similar:

(edit - hah, evidently I can't read...but may remain a useful answer for others...since I've already proved I didn't pay attention to the OP, let me expand on that and fill in the vertical/diagonals...)

(LINQPad-ready)

void Main()
{
    // Quickish and very dirty way to generate the grid
    var lineLength = 3;
    var rnd = new Random();
    var gridSrc = 
        from r in Enumerable.Range(0, lineLength)
        from c in Enumerable.Range(0, lineLength)
        select new { Row = r, Col = c, Text = rnd.Next(0,2) > 0 ? "X" : "O" };
    var grid = gridSrc.ToArray();

    // ok, now for the query
    var horizontalWinners =
        // need the cell and it's index - this is one way to do that
        from cellTuple in grid.Select((cell, idx) => Tuple.Create(idx, cell))
        let idx = cellTuple.Item1
        let cell = cellTuple.Item2
        // figure out which row its in
        let row = idx / lineLength
        // figure out which column its in
        let col = idx % lineLength
        // for rows, group by row #
        group cell by row into byRow
        // only count if all cells in that row are same
        where byRow.All(rowCell => rowCell.Text == "X") 
             || byRow.All(rowCell => rowCell.Text == "O")
        // tell us what row (and who won)
        select new { byRow.Key, byRow.First().Text };

var verticalWinners =
    from cellTuple in grid.Select((cell, idx) => Tuple.Create(idx, cell))
    let idx = cellTuple.Item1
    let cell = cellTuple.Item2
    let row = idx / lineLength
    let col = idx % lineLength
    group cell by col into byCol
    where byCol.All(colCell => colCell.Text == "X") 
                 || byCol.All(colCell => colCell.Text == "O")
    select new { byCol.Key, byCol.First().Text };

var topLeftBottomRightDiagonalWinners =
    from cellTuple in grid.Select((cell, idx) => Tuple.Create(idx, cell))
    let idx = cellTuple.Item1
    let cell = cellTuple.Item2
    let row = idx / lineLength
    let col = idx % lineLength
    let fwdSlash = (row == col)
    group cell by fwdSlash into byDiag
    where byDiag.Key && byDiag.All(d => d.Text == byDiag.First().Text)
    select new { 
                 Text = byDiag.First().Text, 
                 Pos = string.Join("", byDiag.Select(c => Tuple.Create(c.Col, c.Row).ToString())) 
            };

var topRightBottomLeftDiagonalWinners =
    from cellTuple in grid.Select((cell, idx) => Tuple.Create(idx, cell))
    let idx = cellTuple.Item1
    let cell = cellTuple.Item2
    let row = idx / lineLength
    let col = idx % lineLength
    let backSlash = (row + col) == (lineLength - 1)
    group cell by backSlash into byDiag     
    where byDiag.Key && byDiag.All(d => d.Text == byDiag.First().Text)
    select new { 
               Text = byDiag.First().Text, 
               Pos = string.Join("", byDiag.Select(c => Tuple.Create(c.Col, c.Row).ToString())) 
            };

for(int r=0;r<lineLength;r++)
{
    for(int c=0;c<lineLength;c++)
    {
        Console.Write(grid[r*lineLength+c].Text + " ");
    }
    Console.WriteLine();
}
foreach(var row in horizontalWinners)
{
    Console.WriteLine("{0} wins on row {1}", row.Text, row.Key);
}
foreach(var col in verticalWinners)
{
    Console.WriteLine("{0} wins on col {1}", col.Text, col.Key);
}
foreach (var diag in topLeftBottomRightDiagonalWinners
                .Concat(topRightBottomLeftDiagonalWinners)) 
{
    Console.WriteLine("{0} wins on diagonal {1}", diag.Text, diag.Pos);     
}
}
JerKimball
  • 16,584
  • 3
  • 43
  • 55