6
\$\begingroup\$

This program is supposed to be a player VS player Tic-Tac-Toe game. The computer then checks whether you've won, lost, or tied. The code does work, but I don't want it to work through hacking; I want it to be done the right way.

Sub Main()
'Initialise variables

Dim StrCoordinate(3, 3) As String
Dim BooGameOver As Boolean = False
Dim BooIsNextANaught As Boolean = False
Dim IntLoopCounterX As Integer = 0
Dim IntLoopCounterY As Integer = 0
Dim IntTempStorage As Integer = 1
Dim StrPlayAgain As String
Dim IntTempStorage2 As Integer = 0
Dim BooIsInputValid As Boolean = False


Do
    'Set all coordinates' contents to "-"
    For IntLoopCounterY = 0 To 2
        For IntLoopCounterX = 0 To 2
            StrCoordinate(IntLoopCounterX, IntLoopCounterY) = ("-")
        Next
    Next


    'Display Instructions
    Console.WriteLine("╔═════════════════════════════════════════════╗")
    Console.WriteLine("║Welcome to this tic-tac-toe game!            ║")
    Console.WriteLine("║Please enter the coordinates as you are told ║")
    Console.WriteLine("║in order to place your X or 0 there.         ║")
    Console.WriteLine("╚═════════════════════════════════════════════╝")
    Console.WriteLine()
    Console.WriteLine("Coordinates are as follows:")
    Console.WriteLine(vbTab & "╔═══╦═══╦═══╗")
    Console.WriteLine(vbTab & "║1,1║2,1║3,1║")
    Console.WriteLine(vbTab & "╠═══╬═══╬═══╣")
    Console.WriteLine(vbTab & "║1,2║2,2║3,2║")
    Console.WriteLine(vbTab & "╠═══╬═══╬═══╣")
    Console.WriteLine(vbTab & "║1,3║2,3║3,3║")
    Console.WriteLine(vbTab & "╚═══╩═══╩═══╝")
    Console.ReadKey()

    Console.WriteLine("Press any key once you have understood the instructions.")
    Console.Clear()


    Do
        'Display Empty Table
        Console.WriteLine()
        Console.WriteLine(vbTab & "╔═══╦═══╦═══╗")
        For IntLoopCounterY = 0 To 2
            Console.Write(vbTab & "║ ")
            For IntLoopCounterX = 0 To 2
                Console.Write(StrCoordinate(IntLoopCounterX, IntLoopCounterY) & " ║ ")
            Next
            Console.WriteLine()
            If IntLoopCounterY = 2 Then
                Console.WriteLine(vbTab & "╚═══╩═══╩═══╝")
            Else
                Console.WriteLine(vbTab & "╠═══╬═══╬═══╣")
            End If
        Next

        Console.WriteLine()

        'As player one or two for input
        If BooIsNextANaught = False Then
            Do
                'Ask player 1 for input
                Console.ForegroundColor = ConsoleColor.Cyan
                Console.WriteLine(" PLAYER ONE")
                Console.WriteLine("═══════════════════════════════════════════════════════════════════════════")
                Console.WriteLine("Please enter coordinate X, then enter. Then enter coordinate Y, then enter.")
                Console.WriteLine("Coordinates range from 1 to 3.")

                'Input coordinates for X
                Do
                    Console.Write("X (Column)> ")
                    IntTempStorage = Console.ReadLine
                    Console.Write("Y (Row)> ")
                    IntTempStorage2 = Console.ReadLine
                Loop Until IntTempStorage > 0 And IntTempStorage < 4 And IntTempStorage2 > 0 And IntTempStorage2 < 4

                'Check input
                Select Case StrCoordinate((IntTempStorage - 1), (IntTempStorage2 - 1))
                    'Is input being used allready?
                    Case Is = ("X"), ("0")
                        BooIsInputValid = False
                        Console.Clear()
                        Console.BackgroundColor = ConsoleColor.DarkRed
                        Console.WriteLine("══════════════════")
                        Console.WriteLine("ALREADY USED SPACE")
                        Console.WriteLine("Please try again: ")
                        Console.WriteLine("══════════════════")
                        Console.WriteLine()
                        Console.WriteLine()
                        Console.WriteLine(vbTab & "╔═══╦═══╦═══╗")
                        For IntLoopCounterY = 0 To 2
                            Console.Write(vbTab & "║ ")
                            For IntLoopCounterX = 0 To 2
                                Console.Write(StrCoordinate(IntLoopCounterX, IntLoopCounterY) & " ║ ")
                            Next
                            Console.WriteLine()
                            If IntLoopCounterY = 2 Then
                                Console.WriteLine(vbTab & "╚═══╩═══╩═══╝")
                            Else
                                Console.WriteLine(vbTab & "╠═══╬═══╬═══╣")
                            End If
                        Next

                        Console.WriteLine()
                        Console.BackgroundColor = ConsoleColor.Black

                    Case Is = ("-")
                        'Set StrCoordinate(input) with X
                        StrCoordinate((IntTempStorage - 1), (IntTempStorage2 - 1)) = ("X")
                        BooIsInputValid = True
                        BooIsNextANaught = True

                        'check if winner:
                        For IntLoopCounterY = 0 To 2
                            'check all rows:
                            If StrCoordinate(0, IntLoopCounterY) = ("X") And StrCoordinate(1, IntLoopCounterY) = ("X") And StrCoordinate(2, IntLoopCounterY) = ("X") Then
                                BooGameOver = True
                                Console.WriteLine("══Player 1 wins══")
                                Console.ReadKey()
                                'check both diagonals:
                            ElseIf StrCoordinate(0, 0) = ("X") And StrCoordinate(1, 1) = ("X") And StrCoordinate(2, 2) = ("X") Then
                                BooGameOver = True
                                Console.WriteLine("══Player 1 wins══")
                                Console.ReadKey()
                            ElseIf StrCoordinate(0, 2) = ("X") And StrCoordinate(1, 1) = ("X") And StrCoordinate(3, 1) = ("X") Then
                                BooGameOver = True
                                Console.WriteLine("══Player 1 wins══")
                                Console.ReadKey()
                                'check all columns
                            ElseIf StrCoordinate(IntLoopCounterY, 0) = ("X") And StrCoordinate(IntLoopCounterY, 1) = ("X") And StrCoordinate(IntLoopCounterY, 2) = ("X") Then
                                BooGameOver = True
                                Console.WriteLine("══Player 1 wins══")
                                Console.ReadKey()
                            End If
                        Next
                    Case Else
                        Console.WriteLine("It's Dead, Jim!")
                End Select

                Console.ResetColor()
            Loop Until BooIsInputValid = True

        Else
            Do
                'Ask player 2 for input
                Console.ForegroundColor = ConsoleColor.Yellow
                Console.WriteLine(" PLAYER TWO")
                Console.WriteLine("═══════════════════════════════════════════════════════════════════════════")
                Console.WriteLine("Please enter coordinate X, then enter. Then enter coordinate Y, then enter.")
                Console.WriteLine("Coordinates range from 1 to 3.")

                Do
                    Console.Write("X (Column)> ")
                    IntTempStorage = Console.ReadLine
                    Console.Write("Y (Row)> ")
                    IntTempStorage2 = Console.ReadLine
                Loop Until IntTempStorage > 0 And IntTempStorage < 4 And IntTempStorage2 > 0 And IntTempStorage2 < 4

                Select Case StrCoordinate((IntTempStorage - 1), (IntTempStorage2 - 1))
                    Case Is = ("X"), ("0")
                        BooIsInputValid = False
                        Console.Clear()
                        Console.BackgroundColor = ConsoleColor.DarkRed
                        Console.WriteLine("══════════════════")
                        Console.WriteLine("ALREADY USED SPACE")
                        Console.WriteLine("Please try again: ")
                        Console.WriteLine("══════════════════")
                        Console.WriteLine()
                        Console.BackgroundColor = ConsoleColor.Black
                        Console.WriteLine(vbTab & "╔═══╦═══╦═══╗")
                        For IntLoopCounterY = 0 To 2
                            Console.Write(vbTab & "║ ")
                            For IntLoopCounterX = 0 To 2
                                Console.Write(StrCoordinate(IntLoopCounterX, IntLoopCounterY) & " ║ ")
                            Next
                            Console.WriteLine()
                            If IntLoopCounterY = 2 Then
                                Console.WriteLine(vbTab & "╚═══╩═══╩═══╝")
                            Else
                                Console.WriteLine(vbTab & "╠═══╬═══╬═══╣")
                            End If
                        Next

                        Console.WriteLine()


                    Case Is = ("-")
                        StrCoordinate((IntTempStorage - 1), (IntTempStorage2 - 1)) = ("0")
                        BooIsInputValid = True
                        BooIsNextANaught = False

                        'check if winner:
                        For IntLoopCounterY = 0 To 2
                            'check all rows:
                            If StrCoordinate(0, IntLoopCounterY) = ("0") And StrCoordinate(1, IntLoopCounterY) = ("0") And StrCoordinate(2, IntLoopCounterY) = ("0") Then
                                BooGameOver = True
                                Console.WriteLine("══Player 2 wins══")
                                Console.ReadKey()
                                'check both diagonals:
                            ElseIf StrCoordinate(0, 0) = ("0") And StrCoordinate(1, 1) = ("0") And StrCoordinate(2, 2) = ("0") Then
                                BooGameOver = True
                                Console.WriteLine("══Player 2 wins══")
                                Console.ReadKey()
                            ElseIf StrCoordinate(0, 2) = ("0") And StrCoordinate(1, 1) = ("0") And StrCoordinate(3, 1) = ("0") Then
                                BooGameOver = True
                                Console.WriteLine("══Player 2 wins══")
                                Console.ReadKey()
                                'check all columns
                            ElseIf StrCoordinate(IntLoopCounterY, 0) = ("0") And StrCoordinate(IntLoopCounterY, 1) = ("0") And StrCoordinate(IntLoopCounterY, 2) = ("0") Then
                                BooGameOver = True
                                Console.WriteLine("══Player 2 wins══")
                                Console.ReadKey()
                            End If
                        Next

                    Case Else
                        Console.WriteLine("It's Dead, Jim!")
                End Select

                If Not StrCoordinate((IntTempStorage - 1), (IntTempStorage2 - 1)) = ("-") Then

                Else
                    StrCoordinate((IntTempStorage - 1), (IntTempStorage2 - 1)) = ("0")
                    BooIsInputValid = True
                    BooIsNextANaught = False

                End If
                Console.ResetColor()
            Loop Until BooIsInputValid = True

        End If

        IntTempStorage = 0
        IntTempStorage2 = 0
        Console.Clear()

        If BooGameOver = False Then
            For IntLoopCounterY = 0 To 2
                For IntLoopCounterX = 0 To 2
                    If Not StrCoordinate(IntLoopCounterX, IntLoopCounterY) = ("-") Then
                        IntTempStorage2 = IntTempStorage2 + 1
                    End If
                Next
            Next
            If IntTempStorage2 = 9 Then
                BooGameOver = True
                Console.WriteLine("It's a draw")
            End If
            IntTempStorage = 0
            IntTempStorage2 = 0
        End If

    Loop Until BooGameOver = True
    Console.WriteLine("Would you like to play again? Y/N")

    StrPlayAgain = UCase(Console.ReadLine)
    Select Case StrPlayAgain
        Case Is = ("Y")
            BooGameOver = False
        Case Else
            BooGameOver = True
    End Select

Loop Until BooGameOver = True

REM End
Console.WriteLine()
Console.ForegroundColor = ConsoleColor.Blue
Console.Write("EndOfProgram. ")
Console.ForegroundColor = ConsoleColor.DarkBlue
Console.Write("Enter any key to close.")
Console.WriteLine()
Console.ReadKey()
Console.ResetColor()
End Sub
\$\endgroup\$
7
\$\begingroup\$

You have a big bad block of monolithic code here, stuffed in a does-it-all Main() procedure that is begging to be broken down into smaller, more focused procedures and functions.

You need to first identify concerns:

  • Writing to console
  • Reading from console
  • Evaluating game state

Writing to the console involves several distinct operations:

  • Rendering the game title/instructions screen.
  • Rendering the game screen (the 3x3 grid).
  • Rendering the game over screen.

Reading from the console also involves several distinct operations:

  • Displaying a message and waiting for Enter to be pressed.
  • Displaying a message and getting valid coordinates from the user.
  • Displaying a message and getting a Y or N answer from the user.

Create classes to encapsulate each concern, and write methods for each operation. As far as best practices are concerned, I'm biased with IoC and , so my Sub Main() would probably look something like this:

Public Class Program

    Sub Main()

        Dim ConsoleReader As New ConsoleUserInputProvider()
        Dim Game As New TicTacToeGame(ConsoleReader)
        Game.Run()

    End Sub

End Class

The Game object encapsulates the game's logic; the ConsoleUserInputProvider exposes methods that TicTacToeGame uses to get the user's input. It implements some IUserInputProvider interface, which could just as well be implemented by some WinFormsUserInputProvider or WpfUserInputProvider if you wanted some fancypants GUI instead of just a console; the way you have your code, the concerns of game logic and console input/output are so tightly coupled it could very well be simpler to just rewrite the application from scratch to give it a new UI.


Your naming style is - I'll be gentle - from another era. Hungarian Notation is outdated and doesn't contribute in any way to make your code more readable: drop those type prefixes!

\$\endgroup\$
  • 2
    \$\begingroup\$ Small point in defence of the OP, I've personally mentored his computing class, and I can confirm that the teacher is from another era. Hungarian Notation is but one of his sins, and he has enforced his coding peculiarities onto the whole class. \$\endgroup\$ – Lou Jul 15 '14 at 21:34

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.