1

I'm making this connect4 game for a school project and it seems that I've gotten stuck on the issue of checking if a player has won, aka if a player has 4 tokens/markers in a row. I've written a solution which checks all the possible ways of winning but, and here comes the problem, it throws me the "Index was outside the bounds of the array".

I can understand why it reaches outside the array since I'm adding and subtracting from all possible values in the array. But when you try to play the game a strange thing which I can't explain happends: strange spooky phenomenom I can put a token/marker in every way shown in the picture and even get a fully functional winning situation within. But as soon as I try to place one in the outer layer it stops working. I have tried looking up what other have done and even tried debuging the code but I can't seem to figure out what the problem is. Below is the all the relevant parts of the code.

I will greatly appreciate help, thank you for your time.

using System;

namespace _4_i_rad
{
    class Program
    {
        static void Main(string[] args)
        {
            //variables
            bool p1turn = true;
            int[,] playingField = new int[6, 7];

            //taking turns
            while (CheckWin(playingField) == 0)
            {
                Console.Clear();
                PlayingField(playingField);

                if (p1turn == true)
                {
                    Console.Write("Player1's(X) turn.\nIndicate column of choise: ");
                    PlaceHolder(playingField, p1turn);
                    p1turn = false;
                }
                else
                {
                    Console.Write("Player2's(O) turn.\nIndicate column of choise: ");
                    PlaceHolder(playingField, p1turn);
                    p1turn = true;
                }
            }
            Console.Clear();
            PlayingField(playingField);
            if (CheckWin(playingField) == 1)
            { Console.WriteLine("X wins!"); }
            else
            { Console.WriteLine("O wins!"); }
        }

        //checking if someone has won
        static int CheckWin(int[,] playingField)
        {
            int win = 0;
            for (int a = 0; a < playingField.GetLength(0); a++)
            {
                for (int b = 0; b < playingField.GetLength(1); b++)
                {
                    // X win
                    if (playingField[a, b] == 1 &&
                        playingField[a - 1, b - 1] == 1 &&
                        playingField[a - 2, b - 2] == 1 &&
                        playingField[a - 3, b - 3] == 1)
                    { win = 1; }

                    if (playingField[a, b] == 1 &&
                        playingField[a, b - 1] == 1 &&
                        playingField[a, b - 2] == 1 &&
                        playingField[a, b - 3] == 1)
                    { win = 1; }

                    if (playingField[a, b] == 1 &&
                        playingField[a - 1, b] == 1 &&
                        playingField[a - 2, b] == 1 &&
                        playingField[a - 3, b] == 1)
                    { win = 1; }

                    if (playingField[a, b] == 1 &&
                        playingField[a - 1, b + 1] == 1 &&
                        playingField[a - 2, b + 2] == 1 &&
                        playingField[a - 3, b + 3] == 1)
                    { win = 1; }

                    if (playingField[a, b] == 1 &&
                        playingField[a, b + 1] == 1 &&
                        playingField[a, b + 2] == 1 &&
                        playingField[a, b + 3] == 1)
                    { win = 1; }

                    // O win
                    if (playingField[a, b] == 2 &&
                        playingField[a - 1, b - 1] == 2 &&
                        playingField[a - 2, b - 2] == 2 &&
                        playingField[a - 3, b - 3] == 2)
                    { win = 2; }

                    if (playingField[a, b] == 2 &&
                        playingField[a, b - 1] == 2 &&
                        playingField[a, b - 2] == 2 &&
                        playingField[a, b - 3] == 2)
                    { win = 2; }

                    if (playingField[a, b] == 2 &&
                        playingField[a - 1, b] == 2 &&
                        playingField[a - 2, b] == 2 &&
                        playingField[a - 3, b] == 2)
                    { win = 2; }

                    if (playingField[a, b] == 2 &&
                        playingField[a - 1, b + 1] == 2 &&
                        playingField[a - 2, b + 2] == 2 &&
                        playingField[a - 3, b + 3] == 2)
                    { win = 2; }

                    if (playingField[a, b] == 2 &&
                        playingField[a, b + 1] == 2 &&
                        playingField[a, b + 2] == 2 &&
                        playingField[a, b + 3] == 2)
                    { win = 2; }
                }
            }
            return win;
        }
        //drawing playingfield
        static void PlayingField(int[,] playingField)
        {
            Console.WriteLine("\n   1   2   3   4   5   6   7");
            for (int a = 0; a < playingField.GetLength(0); a++)
            {
                Console.WriteLine(" -----------------------------");
                Console.Write(" ");
                Console.Write("| ");

                for (int b = 0; b < playingField.GetLength(1); b++)
                {
                    if (playingField[a, b] == 1)
                    { Console.Write("X" + " | "); }
                    else if (playingField[a, b] == 2)
                    { Console.Write("O" + " | "); }
                    else
                    { Console.Write(" " + " | "); }
                }
                Console.WriteLine(" ");
            }
            Console.WriteLine(" -----------------------------");
        }

        //choosing column
        static int PlaceHolder(int[,] playingField, bool p1turn)
        {
            int x; int y = 5;
            while (!int.TryParse(Console.ReadLine(), out x) || x < 1 || x > 7)
            { Console.Write("\nOut of bounds.\nSubmit new column between 1 and 7: "); }
            while (playingField[y, x - 1] == 1 || playingField[y, x - 1] == 2)
            {
                y--;
                if (y < 0)
                {
                    Console.Write("\nColumn already has maximum amount of tokens.\nChose new column: ");
                    while (!int.TryParse(Console.ReadLine(), out x) || x < 1 || x > 7)
                    { Console.Write("\nOut of bounds.\nSubmit new column between 1 and 7: "); }
                    y = 5;
                }
            }
            if (p1turn == true)
            { return playingField[y, x - 1] = 1; }
            else
            { return playingField[y, x - 1] = 2; }
        }
    }
}
Evenyon
  • 21
  • 2
  • Your problem, is a logic issue, somewhere in this messy code. But that is more likely the problem, this code is extremely messy. Please refactor you code using SOLID and clean code principles. Then you will likely discover the problem yourself. Especially if you add unit tests. – Morten Bork Apr 04 '22 at 07:40

2 Answers2

1

Your exception probably happens because of the way logic checks are performed in C#. When you use the && operator (conditional logical AND), it checks the left-hand operand first, and if the result is false, then it doesn't check the right-hand operand, since it is already obvious that the result is false. However, when it checks the left-hand operand and finds it to be true, it will then evaluate the rest of the expression. This is sometimes called logical short-circuiting and is covered in more detail, for example, in this answer.

So, in your specific situation, consider for example this part of your code:

if (playingField[a, b] == 1 &&
    playingField[a - 1, b - 1] == 1 &&
    playingField[a - 2, b - 2] == 1 &&
    playingField[a - 3, b - 3] == 1)
{ win = 1; }

Let's imagine we are on the first iteration of both loops (so a = 0, b = 0) and players did not place anything on the first row, so when we check playingField[0, 0] == 1 it returns false. We than do not evaluate the rest of the expression, since it will return false anyway.

But what if players did place something on the first row? We then check playingField[0, 0] == 1, find it to be true, and go on to check the rest of the conditions in if statement. The next one to check is playingField[a - 1, b - 1] == 1. We are now at a = 0, b = 0, so we are checking the contents of playingField[-1,-1]. And here is your problem: there is no [-1,-1] element in a two-dimensional array, so it throws an exception.

defaultUsernameN
  • 365
  • 5
  • 14
1

Some comments:

  1. Use Access Modifiers (internal, public, private, ...) and preferably also the 'sealed' modifier.
  2. Don't write '== true'
  3. You are trying to access non existing array indices in the 'CheckWin' function.
  4. Instead of passing the variable 'PlayingField' everywhere, move it up. Same goes for 'p1Turn'.
  5. Writing the same code twice is almost always a sign you can improve the code. (See 'PlaceHolder' function)

Code:

using System;

namespace _4_i_rad;

internal sealed class Program
{
    private static readonly int[,] _playingField = new int[6, 7];
    private static bool _p1Turn = true;
    
    private static void Main()
    {
        //taking turns
        while (CheckWin() == 0)
        {
            Console.Clear();
            PlayingField();

            if (_p1Turn)
            {
                Console.Write("Player1's(X) turn.\nIndicate column of choice: ");
                PlaceHolder();
                _p1Turn = false;
            }
            else
            {
                Console.Write("Player2's(O) turn.\nIndicate column of choice: ");
                PlaceHolder();
                _p1Turn = true;
            }
        }
        
        Console.Clear();
        PlayingField();
        Console.WriteLine(CheckWin() == 1 
            ? "X wins!" 
            : "O wins!");
    }

    //checking if someone has won
    private static int CheckWin() {
        var height = _playingField.GetLength(0);
        var width = _playingField.GetLength(1);
        
        const int EMPTY_SLOT = 0;
        
        for (var r = 0; r < height; r++) { // iterate rows, bottom to top
            for (var c = 0; c < width; c++) { // iterate columns, left to right
                var player = _playingField[r,c];
                if (player == EMPTY_SLOT)
                    continue; // don't check empty slots

                if (c + 3 < width &&
                    player == _playingField[r,c+1] && // look right
                    player == _playingField[r,c+2] &&
                    player == _playingField[r,c+3])
                    return player;
                if (r + 3 < height) {
                    if (player == _playingField[r+1,c] && // look up
                        player == _playingField[r+2,c] &&
                        player == _playingField[r+3,c])
                        return player;
                    if (c + 3 < width &&
                        player == _playingField[r+1,c+1] && // look up & right
                        player == _playingField[r+2,c+2] &&
                        player == _playingField[r+3,c+3])
                        return player;
                    if (c - 3 >= 0 &&
                        player == _playingField[r+1,c-1] && // look up & left
                        player == _playingField[r+2,c-2] &&
                        player == _playingField[r+3,c-3])
                        return player;
                }
            }
        }
        return EMPTY_SLOT; // no winner found
    }
    
    //drawing playing field
    private static void PlayingField()
    {
        Console.WriteLine("\n   1   2   3   4   5   6   7");
        for (var a = 0; a < _playingField.GetLength(0); a++)
        {
            Console.WriteLine(" -----------------------------");
            Console.Write(" ");
            Console.Write("| ");

            for (var b = 0; b < _playingField.GetLength(1); b++)
            {
                switch (_playingField[a, b])
                {
                    case 1:
                        Console.Write("X" + " | ");
                        break;
                    case 2:
                        Console.Write("O" + " | ");
                        break;
                    default:
                        Console.Write(" " + " | ");
                        break;
                }
            }
            Console.WriteLine(" ");
        }
        Console.WriteLine(" -----------------------------");
    }

    //choosing column
    private static void PlaceHolder()
    {
        while (true)
        {
            if (!int.TryParse(Console.ReadLine(), out var x))
            {
                Console.WriteLine("Invalid Input.");
                continue;
            }

            if (x is < 1 or > 7)
            {
                Console.Write("\nOut of bounds.\nSubmit new column between 1 and 7: ");
                continue;
            }

            var y = 5;


            while (_playingField[y, x - 1] != 0)
            {
                y--;
                if (y < 0) break;
            }
            
            if (y < 0)
            {
                Console.Write("\nColumn already has maximum amount of tokens.\nChose new column: ");
                continue;
            }

            if (_p1Turn)
            {
                _playingField[y, x - 1] = 1;
                break;
            }

            _playingField[y, x - 1] = 2;
            break;
        }
    }
}