Jump to content
Sign in to follow this  
SadBunny

Tic Tac Toe (boter, kaas en eieren in Dutch)

Recommended Posts

SadBunny

Hi people!

I thought it'd be fun to try and use AutoIt and put up an excersize for myself to make a tic tac toe game with a computer player with a recursive exhaustive depth-first score calculating algorithm (nice and easy to code, though hard on CPU time) to determine it's moves.

The idea of the game (just in case): the user starts and chooses first place to put his/her symbol (the X in this case). The computer then puts an O at another place. That order is repeated until one of the players wins or the board is full. The object of the game is to make sure you get three-in-a-row, horizontally, vertically or diagonally. or at least make sure that the other player doesn't. For anyone with an IQ over that of a desert cactus, this game always ends in a draw. If I made the algorithm perfect, which I believe I did, it should not be possible to beat the computer. It is perfectly possible to lose from it though.

Note that I applied a hack to speed up the first move the computer needs to calculate (because it took like 15 seconds before the computer knew what to do): if user placed X in the middle then computer places first O in upper left corner. If the center square is free, the computer places it's first O there. (This is in fact always the resuilt of the algorithm anyway, so it's not influencing the AI, but a mere timesaver shortcut.)

This is very possibly FAR from perfectly efficient, because this is the first way that I thought of how to do this. Some googling shows that there are TONS of efficient algorithms to play a perfect tic-tac-toe game, but this was about quickly figuring it out for myself, not about following blindly and mindlessly in other people's footsteps.

#include <guiconstants.au3>

Dim $knop[3][3]
Dim $grid[3][3]
$eerstemove = True

$mainGui = GUICreate("test", 105, 105)

For $x = 0 To 2
    For $y = 0 To 2
        $knop[$x][$y] = GUICtrlCreateButton(" ", 10 + $x * 30, 10 + $y * 30, 25, 25)
        $grid[$x][$y] = 0
    Next
Next

GUISetState()

While 1
    _waitForUserMove()
    _doeEindDing($grid)
    _makeOwnMove($grid)
    _doeEindDing($grid)
WEnd

Func _doeEindDing($grid)
    If _checkForFinish($grid) = -1 Then
        MsgBox(0, 0, "You win!")
        Exit
    ElseIf _checkForFinish($grid) = 1 Then
        MsgBox(0, 0, "I win!")
        Exit
    EndIf
    If _gridIsFull($grid) Then
        MsgBox(0, 0, "Draw!")
        Exit
    EndIf
EndFunc  ;==>_doeEindDing

Func _gridIsFull($ag4)
    For $x=0 To 2
        For $y=0 To 2
            If $ag4[$x][$y]=0 Then Return False
        Next
    Next
    Return True
EndFunc

Func _makeOwnMove($ag)
    If $eerstemove Then
        $eerstemove = False
        If $ag[1][1]=0 Then 
            $x=1
        Else
            $x=0
        EndIf
        $grid[$x][$x] = 2
        GUICtrlSetData($knop[$x][$x], "O")
        Return
    EndIf
    Dim $aScore[3][3]
    $maxScore = -100000
    $maxScoreX = -1
    $maxScoreY = -1
    For $x = 0 To 2
        For $y = 0 To 2
            If $ag[$x][$y] = 0 Then
                $aScore[$x][$y] = _calculateScore($x, $y, $ag, 0, 2)
                If $aScore[$x][$y] > $maxScore Then
                    $maxScore = $aScore[$x][$y]
                    $maxScoreX = $x
                    $maxScoreY = $y
                EndIf
            EndIf
        Next
    Next
    $grid[$maxScoreX][$maxScoreY] = 2
    GUICtrlSetData($knop[$maxScoreX][$maxScoreY], "O")
EndFunc  ;==>_makeOwnMove

Func _calculateScore($x, $y, $ag2, $startScore, $player)
    $ag2[$x][$y] = $player
    Local $recPlayer = 0
    If $player = 1 Then
        $recPlayer = 2
    Else
        $recPlayer = 1
    EndIf
    Local $score = 0
    $score += _checkForFinish($ag2)
    For $x = 0 To 2
        For $y = 0 To 2
            If $ag2[$x][$y] = 0 Then
                $score += _calculateScore($x, $y, $ag2, 0, $recPlayer)
            EndIf
        Next
    Next
    Return $score
EndFunc  ;==>_calculateScore

Func _checkForFinish($ag3)
    For $p = 1 To 2
        For $i = 0 To 2
            If ($ag3[$i][0] == $p And $ag3[$i][1] == $p And $ag3[$i][2] == $p) Or ($ag3[0][$i] == $p And $ag3[1][$i] == $p And $ag3[2][$i] == $p) Then
                Return (($p - 2) * 3) + 1
            EndIf
        Next
        If ($ag3[0][0] == $p And $ag3[1][1] == $p And $ag3[2][2] == $p) Or ($ag3[2][0] == $p And $ag3[1][1] == $p And $ag3[0][2] == $p) Then
            Return (($p - 2) * 3) + 1
        EndIf
    Next
    Return 0
EndFunc  ;==>_checkForFinish

Func _waitForUserMove()
    Do
        $msg = GUIGetMsg()
        Sleep(10)
    Until _msgIsKnop($msg) <> 0
    Dim $knopInd[2]
    $knopInd = _msgIsKnop($msg)
    GUICtrlSetData($knop[$knopInd[0]][$knopInd[1]], "X")
    $grid[$knopInd[0]][$knopInd[1]] = 1
EndFunc  ;==>_waitForUserMove

Func _msgIsKnop($msg)
    For $x = 0 To 2
        For $y = 0 To 2
            If $msg = $knop[$x][$y] Then
                If $grid[$x][$y] <> "0" Then
                    Beep(1000, 5)
                    Return
                EndIf
                Dim $knopInd[2]
                $knopInd[0] = $x
                $knopInd[1] = $y
                Return $knopInd
            EndIf
        Next
    Next
    If $msg = $GUI_EVENT_CLOSE Then Exit
    Return 0
EndFunc  ;==>_msgIsKnop

Roses are FF0000, violets are 0000FF... All my base are belong to you.

Share this post


Link to post
Share on other sites
BigDod

I am afraid it does not know when it is beat. If you select a corner it selects the middle then you select the opposite corner and do the obvious to win.



Get Beta versions Here Get latest SciTE editor Here AutoIt 1-2-3 by Valuater - A great starting point.

Time you enjoyed wasting is not wasted time ......T.S. Elliot
Suspense is worse than disappointment................Robert Burns
God help the man who won't help himself, because no-one else will...........My Grandmother

Share this post


Link to post
Share on other sites
SadBunny

I am afraid it does not know when it is beat. If you select a corner it selects the middle then you select the opposite corner and do the obvious to win.

LOL! I found that out too after I posted it (i only tested like one or two games and was not even playing to win :)). Better do the whole thing from scratch, this AI sucks balls ^_^

Thanks anyway for rubbing it in, hahaha... :)


Roses are FF0000, violets are 0000FF... All my base are belong to you.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
Sign in to follow this  

×