Juego Tic Tac Toe

Tic-tac-toe program written in QBASIC dialect (using QB64. FreeBASIC)



 ' Tic-Tac-Toe
' Ref: https://www.geeksforgeeks.org/implementation-of-tic-tac-toe-game/
'      Tic-Tac-Toe in COMPUTE! Magazine
'
' 3 Jun 2021 -- changed to QB64
'
'--------------------------------------------------------------------------------------------------

' Screen 12's colours (Add 8 FOR bright colours)
Const Black = 0
Const Blue = 1
Const Green = 2
Const Cyan = 3
Const Red = 4
Const Magenta = 5
Const Yellow = 6
Const White = 7
Const Bright = 8

' For the game area
Const Xoffset = 10
Const Xsize = 120
Const Yoffset = 10
Const Ysize = 120

' For text row and column when using LOCATE
Const ColumnStart = 8
Const ColumnSize = 16
Const RowStart = 4
Const RowSize = 8
Const TextRow = 25
Const TextColumn = 50

' Game constants
Const Computer = 1
Const Human = 2
Const Side = 3 'Length of board

'Computer will move with 'O', and human with 'X'
Const Nought = "O"
Const Cross = "X"

'--------------------------------------------------------------------------------------------------
' Main program - Tic Tac Toe
'--------------------------------------------------------------------------------------------------

' Initialise random number generator
Randomize Timer
Screen 12 '640x480 256k colors

Dim choice As String
Print
Print "TIC-TAC-TOE"
Print

Do
    Print
    Print "Do you want to have the first move? (Y/N): ";
    Do
        Input choice
        If choice <> "Y" And choice <> "y" And choice <> "N" And choice <> "n" Then
            Print "Please enter Y or N: ";
        Else
            Exit Do
        End If
    Loop
    If choice = "Y" Or choice = "y" Then
        Call playTicTacToe(Human)
    Else
        Call playTicTacToe(Computer)
    End If

    Print
    Print "Do you want to play again? (Y/N): ";
    Do
        Input choice
        If choice <> "Y" And choice <> "y" And choice <> "N" And choice <> "n" Then
            Print "Please enter Y or N: ";
        Else
            Exit Do
        End If
    Loop
    If choice = "N" Or choice = "n" Then
        Print
        Print "Bye."
        Print
        Exit Do
    End If
Loop



'--------------------------------------------------------------------------------------------------
' The Tic-tac-toe game procedure
'
Sub playTicTacToe (whoseTurn As Integer)

    ' The board and moves
    Dim board(Side, Side) As String

    Call initialise(board())
    Call showBoardGraphic(board())
    Call showInstructions

    moveIndex = 0

    ' Keep playing game until game is over
    While gameOver(board()) = 0 And moveIndex <> Side * Side
        If whoseTurn = Computer Then
            computerMove = getComputerMove(board())
            x = Int(computerMove / Side)
            y = computerMove Mod Side
            board(x, y) = Nought
            Locate TextRow + 1, 2
            Print "Computer has put a "; Nought; " in cell "; computerMove + 1
            Call showBoardGraphic(board())
            moveIndex = moveIndex + 1
            whoseTurn = Human
        Else 'whoseTurn = Human
            humanMove = getUserInput(board())
            x = Int(humanMove / Side)
            y = humanMove Mod Side
            board(x, y) = Cross
            Locate TextRow + 1, 2
            Print "YOU have put a "; Cross; " in cell "; humanMove + 1
            Call showBoardGraphic(board())
            moveIndex = moveIndex + 1
            whoseTurn = Computer
        End If
    Wend

    If gameOver(board()) = 0 And moveIndex = Side * Side Then
        Locate TextRow + 1, 2
        Print "It's a DRAW"
    Else
        If whoseTurn = Computer Then
            whoseTurn = Human
        Else
            whoseTurn = Computer
        End If

        Call declareWinner(whoseTurn)
    End If
End Sub


'--------------------------------------------------------------------------------------------------
' Procedure to initialize the game
'
Sub initialise (board() As String)
    ' Initially the board is empty
    For r = 0 To Side - 1
        For c = 0 To Side - 1
            board(r, c) = " "
        Next
    Next
End Sub


'--------------------------------------------------------------------------------------------------
' Procedure to show board on graphic screen
'
Sub showBoardGraphic (board() As String)
    Dim As Integer x, y, x2, y2

    Cls
    Color Blue + Bright
    x = Xoffset
    y = Yoffset
    x2 = Xoffset + Xsize * 3
    y2 = Yoffset + Ysize * 3
    ' Draw outer box
    Line (x, y)-(x2, y)
    Line -(x2, y2)
    Line -(x, y2)
    Line -(x, y)
    ' Draw lines inside the box
    Line (Xoffset, Yoffset + Ysize)-(Xoffset + Xsize * 3, Yoffset + Ysize)
    Line (Xoffset, Yoffset + Ysize * 2)-(Xoffset + Xsize * 3, Yoffset + Ysize * 2)
    Line (Xoffset + Xsize, Yoffset)-(Xoffset + Xsize, Yoffset + Ysize * 3)
    Line (Xoffset + Xsize * 2, Yoffset)-(Xoffset + Xsize * 2, Yoffset + Ysize * 3)

    ' Draw symbols or put cell numbers
    For i = 0 To Side - 1 ' i = row
        For j = 0 To Side - 1 ' j = column
            If board(i, j) = Nought Then
                x = Xoffset + Xsize * j + Xsize / 2
                y = Yoffset + Ysize * i + Ysize / 2
                Call drawNought(x, y, Red + Bright)
            ElseIf board(i, j) = Cross Then
                x = Xoffset + Xsize * j + Xsize / 2
                y = Yoffset + Ysize * i + Ysize / 2
                Call drawCross(x, y, Yellow + Bright)
            Else
                Color Blue + Bright
                col = j * ColumnSize + ColumnStart
                row = i * RowSize + RowStart
                Locate row, col
                Print i * 3 + j + 1
            End If
        Next
    Next
End Sub


'--------------------------------------------------------------------------------------------------
' Procedure to draw Nought (CIRCLE) in specific cell
'
Sub drawNought (x As Integer, y As Integer, colr As Integer)
    Color colr
    For i = 0 To 5
        Circle (x, y), Xsize / 3 - i
    Next
End Sub


'--------------------------------------------------------------------------------------------------
' Procedure to draw cross (X) in specific cell
'
Sub drawCross (x As Integer, y As Integer, colr As Integer)
    Color colr
    For i = 0 To 5
        Line (x - Xsize / 3 + i, y - Ysize / 3)-(x + Xsize / 3 + i, y + Ysize / 3)
        Line (x - Xsize / 3 + i, y + Ysize / 3)-(x + Xsize / 3 + i, y - Ysize / 3)
    Next
End Sub


'--------------------------------------------------------------------------------------------------
' Procedure to show the instructions
'
Sub showInstructions
    Locate 2, TextColumn
    Print "Tic-Tac-Toe"
    Locate TextRow, 2
    Print "Choose a cell numbered from 1 to 9 as above and enter"
End Sub


'--------------------------------------------------------------------------------------------------
' Function that returns true IF game is over
' ELSE return false
Function gameOver (board() As String)

    check = rowCrossed(board()) + columnCrossed(board()) + diagonalCrossed(board())
    'check = rowCrossed( board())
    'PRINT "Check = "; rowCrossed( board() )
    'PRINT
    If check > 0 Then
        gameOver = 1
    Else
        gameOver = 0
    End If
End Function


'--------------------------------------------------------------------------------------------------
' Function that returns true IF any row is crossed
'
Function rowCrossed (board() As String)
    crossed = 0
    For i = 0 To Side - 1
        'PRINT "ROW:";i;" = ";board(i,0);board(i,1);board(i,2)
        IF board(i,0)=board(i,1) and board(i,1)=board(i,2) and _
           board(i,0)<>" " THEN
            crossed = 1
            'PRINT "***"
            Exit For
        End If
        'PRINT "??"
    Next
    rowCrossed = crossed
End Function


'--------------------------------------------------------------------------------------------------
' Function that returns true IF any column is crossed
'
Function columnCrossed (board() As String)
    crossed = 0
    For j = 0 To Side - 1
        IF board(0,j)=board(1,j) and board(1,j)=board(2,j) and _
           board(0,j)<>" "  THEN
            crossed = 1
            Exit For
        End If
    Next
    columnCrossed = crossed
End Function


'--------------------------------------------------------------------------------------------------
' Function that returns true IF any diagonal is crossed
'
Function diagonalCrossed (board() As String)
    crossed = 0
    IF board(0,0) = board(1,1) and  board(1,1) = board(2,2) and _
       board(0,0)<>" " THEN
        crossed = 1
    End If
    IF board(0,2) = board(1,1) and  board(1,1) = board(2,0) and _
       board(0,2)<>" " THEN
        crossed = 1
    End If
    diagonalCrossed = crossed
End Function


'--------------------------------------------------------------------------------------------------
' Procedure to declare the winner
'
Sub declareWinner (whoseTurn As Integer)
    Locate TextRow, 2
    If whoseTurn = Computer Then
        Print "Computer wins!"
    Else
        Print "YOU win!"
    End If
End Sub


'--------------------------------------------------------------------------------------------------
' Function to compute Computer's move
'
Function getComputerMove (board() As String)

    ' Check for winning moves
    move = winBlockRow(board(), Nought)
    If move = -1 Then
        move = winBlockColumn(board(), Nought)
    End If
    If move = -1 Then
        move = winBlockDiagonal(board(), Nought)
    End If

    'Check for blocking moves"
    If move = -1 Then
        move = winBlockRow(board(), Cross)
    End If
    If move = -1 Then
        move = winBlockColumn(board(), Cross)
    End If
    If move = -1 Then
        move = winBlockDiagonal(board(), Cross)
    End If

    If move = -1 Then
        ' Check middle cell if empty
        If board(1, 1) = " " Then
            move = 4
        Else 'move to a random cell
            Do
                move = Int(Rnd * Side * Side)
                x = Int(move / Side)
                y = move Mod Side
            Loop While board(x, y) <> " "
        End If
    End If
    getComputerMove = move
End Function


'--------------------------------------------------------------------------------------------------
' Function to check rows FOR a winning move or to block opponent's move
'
Function winBlockRow (board() As String, symbol As String)
    move = -1
    For i = 0 To Side - 1
        If board(i, 0) = symbol Then
            If board(i, 1) = symbol And board(i, 2) = " " Then
                move = i * Side + 2
                Exit For
            End If
            If board(i, 2) = symbol And board(i, 1) = " " Then
                move = i * Side + 1
                Exit For
            End If
        End If
        If board(i, 1) = symbol Then
            If board(i, 2) = symbol And board(i, 0) = " " Then
                move = i * Side
                Exit For
            End If
        End If
    Next
    winBlockRow = move
End Function


'--------------------------------------------------------------------------------------------------
' Function to check columns FOR a winning move or to block opponent's move
'
Function winBlockColumn (board() As String, symbol As String)
    move = -1
    For j = 0 To Side - 1
        If board(0, j) = symbol Then
            If board(1, j) = symbol And board(2, j) = " " Then
                move = 2 * Side + j
                Exit For
            End If
            If board(2, j) = symbol And board(1, j) = " " Then
                move = Side + j
                Exit For
            End If
        End If
        If board(1, j) = symbol Then
            If board(2, j) = symbol And board(0, j) = " " Then
                move = j
                Exit For
            End If
        End If
    Next
    winBlockColumn = move
End Function


'--------------------------------------------------------------------------------------------------
' Function that check FOR a winning move or to block opponent's move
'
Function winBlockDiagonal (board() As String, symbol As String)
    move = -1
    If board(1, 1) = symbol Then
        If board(0, 0) = symbol And board(2, 2) = " " Then
            move = 8
        ElseIf board(0, 2) = symbol And board(2, 0) = " " Then
            move = 6
        End If
    End If
    winBlockDiagonal = move
End Function

'--------------------------------------------------------------------------------------------------
' Function to get user input/move
'
Function getUserInput (board() As String)
    Locate TextRow, 2
    userInput = 0
    While userInput < 1 Or userInput > 9
        Input "Enter move # (1 to 9): ", userInput
        If userInput < 1 Or userInput > 9 Then
            Print "INVALID input"
        Else
            index = userInput - 1
            r = Int(index / Side)
            c = index Mod Side
            If board(r, c) <> " " Then
                Print "Cell "; userInput; " is NOT empty"
                userInput = 0
            End If
        End If
    Wend
    getUserInput = userInput - 1
End Function