Jump to content
Sign in to follow this  
Barney

A Connect 4 Game

Recommended Posts

aiter
Posted (edited)

Charming game, you can be rightfully proud.

The devil AI cannot be beaten!

 

Edited by aiter

Share this post


Link to post
Share on other sites
Barney

Thanks aiter! :D

If I can write a faster evaluation function then the AI can search the tree much

deeper or write code to do Threat Analysis to make the AI much stronger.

Share this post


Link to post
Share on other sites
Chimp

2lorx0.jpg

... a little modified version; CPU vs CPU  ;)

#include <Array.au3>
#include <GDIPlus.au3>
#include <GUIConstantsEx.au3>
#include <Misc.au3>
#include <Sound.au3>
#include <WindowsConstants.au3>
#include <WinAPIRes.au3>

AutoItSetOption("MouseCoordMode", 0)
Opt("GUIOnEventMode", 1)
Opt("MustDeclareVars", 1)
;;-----------------------------------------------------------------------------------------------------------
Global Const $VERSION            = "1.0"
;; Image size ...
Global Const $GUI_WIDTH          = 800
Global Const $GUI_HEIGHT         = 600
Global Const $PIECE_WIDTH        = 64
Global Const $PIECE_HEIGHT       = 64
Global Const $TINY_PIECE_WIDTH   = 36
Global Const $TINY_PIECE_HEIGHT  = 36
;; Coordinates ...
;; to draw the little piece that indicate the color play by Player & CPU.
Global Const $PLAYER_COLOR_X     = 13
Global Const $PLAYER_COLOR_Y     = 127
Global Const $CPU_COLOR_X        = 751
Global Const $CPU_COLOR_Y        = 127
;; to draw the number of games won by Player & CPU.
Global Const $PLAYER_WON_GAMES_X = 55
Global Const $PLAYER_WON_GAMES_Y = 122
Global Const $CPU_WON_GAMES_X    = 709
Global Const $CPU_WON_GAMES_Y    = 122
;; to draw the Player Win image.
Global Const $PLAYER_WIN_X       = 236
Global Const $PLAYER_WIN_Y       = 200
;; to draw the CPU Win image.
Global Const $CPU_WIN_X          = 254
Global Const $CPU_WIN_Y          = 200
;; to draw the Draw image.
Global Const $DRAW_GAME_X        = 235
Global Const $DRAW_GAME_Y        = 200
;; to draw the "Play Again (Y/N)?" image.
Global Const $PLAY_AGAIN_X       = 155
Global Const $PLAY_AGAIN_Y       = 281
;; to draw the think image.
Global Const $PLAYER_THINK_X     = 28
Global Const $PLAYER_THINK_Y     = 243
Global Const $CPU_THINK_X        = 693
Global Const $CPU_THINK_Y        = 243
;; of board Row 0 & Column 0.
Global Const $COL0_X             = 143
Global Const $ROW0_Y             = 480
;; Board square width.
Global Const $SQUARE_WIDTH       = 75
;; Piece drop speed.
Global Const $DROP_SPEED         = 12
;; Piece & empty square.
Global Const $PLAYER             = 'P'
Global Const $CPU                = 'C'
Global Const $DRAW               = 'D'
Global Const $EMPTY_SQUARE       = '-'
;; Values for Minimax & Alpha Beta Pruning.
Global Const $INFINITY           = 10000
Global Const $MAX_DEPTH          = 4
Global Const $CPU_WIN_SCORE      = +$INFINITY / 10
Global Const $PLAYER_WIN_SCORE   = -$INFINITY / 10
Global Const $DRAW_SCORE         = 0
;; Patterns ...
Global Const $3C1E               = 3 * Asc($CPU) + Asc($EMPTY_SQUARE)
Global Const $3P1E               = 3 * Asc($PLAYER) + Asc($EMPTY_SQUARE)
Global Const $2C2E               = 2 * Asc($CPU) + 2 * Asc($EMPTY_SQUARE)
Global Const $2P2E               = 2 * Asc($PLAYER) + 2 * Asc($EMPTY_SQUARE)
Global Const $1C3E               = 1 * Asc($CPU) + 3 * Asc($EMPTY_SQUARE)
Global Const $1P3E               = 1 * Asc($PLAYER) + 3 * Asc($EMPTY_SQUARE)
;;-----------------------------------------------------------------------------------------------------------
Global $aWaitingSound     = _SoundOpen(@ScriptDir & "\Sounds\waiting.mid")
Global $aPlayerWinSound   = _SoundOpen(@ScriptDir & "\Sounds\player_win.wma")
Global $aCPUWinSound      = _SoundOpen(@ScriptDir & "\Sounds\cpu_win.wma")
Global $aDrawSound        = _SoundOpen(@ScriptDir & "\Sounds\draw.wma")
Global $aMoveSound        = _SoundOpen(@ScriptDir & "\Sounds\move.wma")
Global $aInValidMoveSound = _SoundOpen(@ScriptDir & "\Sounds\invalid_move.wma")
Global $aKeyPressSound    = _SoundOpen(@ScriptDir & "\Sounds\key_press.wma")
;;-----------------------------------------------------------------------------------------------------------
Global Const $hGUI = GUICreate("Connect 4", $GUI_WIDTH, $GUI_HEIGHT, -1, -1)
GUISetIcon(@ScriptDir & "\c4.ico", -1)
GUISetState(@SW_SHOW)
;;-----------------------------------------------------------------------------------------------------------
GUISetOnEvent($GUI_EVENT_CLOSE, "CloseApp", $hGUI)
GUISetOnEvent($GUI_EVENT_RESTORE, "DrawBoard", $hGUI)
GUISetOnEvent($GUI_EVENT_PRIMARYDOWN, "PlayerMove", $hGUI)
GUIRegisterMsg($WM_MOVE, "WM_MOVE")
Func WM_MOVE($hWnd, $Msg, $wParam, $lParam)
    DrawBoard()
EndFunc
;;-----------------------------------------------------------------------------------------------------------
_GDIPlus_Startup()
Global Const $hFrameImg     = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\frame.png")
Global Const $hPlayerWinImg = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\player_win.png")
Global Const $hCPUWinImg    = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\cpu_win.png")
Global Const $hDrawImg      = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\draw.png")
Global Const $hPlayAgainImg = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\play_again.png")
Global Const $hThinkImg     = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\think.png")
Global Const $hMarkerImg    = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\marker.png")
Global Const $hDigit0Img    = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\0.png")
Global Const $hDigit1Img    = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\1.png")
Global Const $hDigit2Img    = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\2.png")
Global Const $hDigit3Img    = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\3.png")
Global Const $hDigit4Img    = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\4.png")
Global Const $hDigit5Img    = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\5.png")
Global Const $hDigit6Img    = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\6.png")
Global Const $hDigit7Img    = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\7.png")
Global Const $hDigit8Img    = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\8.png")
Global Const $hDigit9Img    = _GDIPlus_ImageLoadFromFile(@ScriptDir & "\Images\9.png")
Global Const $hGraphics     = _GDIPlus_GraphicsCreateFromHWND($hGUI)
Global Const $hBitmap       = _GDIPlus_BitmapCreateFromGraphics($GUI_WIDTH, $GUI_HEIGHT, $hGraphics)
Global Const $hBuffer       = _GDIPlus_ImageGetGraphicsContext($hBitmap)
Global Const $hBrushRed     = _GDIPlus_BrushCreateSolid(0xFFFF0000)
Global Const $hBrushYellow  = _GDIPlus_BrushCreateSolid(0xFFFFDE00)
Global Const $hBrushWhite   = _GDIPlus_BrushCreateSolid(0xFFFFFFFF)
Global Const $hPenPurple    = _GDIPlus_PenCreate(0xFF372248, 8)
;;-----------------------------------------------------------------------------------------------------------
Global $g_aBoard[6][7], $g_aState[9]
Global $g_iPlayerWonGames, $g_iCPUWonGames
Global $g_hPlayerColor, $g_hCPUColor
Global $g_sTurn, $g_iMove, $g_bDetectPlayerMove
;;-----------------------------------------------------------------------------------------------------------
_GDIPlus_GraphicsSetSmoothingMode($hGraphics, $GDIP_SMOOTHINGMODE_HIGHQUALITY)
_Connect4()
;;-----------------------------------------------------------------------------------------------------------

; CPU vs CPU
Func PlayerCPUMove()
    Local $iPieces = 7
    For $i = 0 to 6
        $iPieces -= $g_aBoard[0][$i] = "-"
    Next

    $g_bDetectPlayerMove = False

    If $iPieces Then
        $g_iMove = AI($g_aBoard)
    Else
        $g_iMove = Random(0, 6, 1)
    EndIf

    DrawBoard()
    _SoundPlay($aMoveSound, 1)
    $g_bDetectPlayerMove = True
    Return True
EndFunc   ;==>PlayerCPUMove

Func _Connect4()
    Initiate()
    DrawBoard()

    While True
        Select
            ;; Player turn.
            Case $g_sTurn = $PLAYER And PlayerCPUMove() ; PlayerMoved()
                $g_aState = CheckState($g_aBoard, $PLAYER)
                DrawBoard()
                Switch $g_aState[0]
                    Case "Win"
                        _SoundPlay($aPlayerWinSound)
                        Switch PlayAgain()
                            Case 'Y'
                                Reset()
                                DrawBoard()
                            Case 'N'
                                CloseApp()
                        EndSwitch
                    Case "Draw"
                        _SoundPlay($aDrawSound)
                        Switch PlayAgain()
                            Case 'Y'
                                Reset()
                                DrawBoard()
                            Case 'N'
                                CloseApp()
                        EndSwitch
                    Case "GoOn"
                        $g_sTurn = $CPU
                        DrawBoard()
                EndSwitch
            ;; CPU turn.
            Case $g_sTurn = $CPU And CPUMoved()
                $g_aState = CheckState($g_aBoard, $CPU)
                DrawBoard()
                Switch $g_aState[0]
                    Case "Win"
                        _SoundPlay($aCPUWinSound)
                        Switch PlayAgain()
                            Case 'Y'
                                Reset()
                                DrawBoard()
                            Case 'N'
                                CloseApp()
                        EndSwitch
                    Case "Draw"
                        _SoundPlay($aDrawSound)
                        Switch PlayAgain()
                            Case 'Y'
                                Reset()
                                DrawBoard()
                            Case 'N'
                                CloseApp()
                        EndSwitch
                    Case "GoOn"
                        $g_sTurn = $PLAYER
                        DrawBoard()
                EndSwitch
        EndSelect
    WEnd
EndFunc

Func AI($aBoard)
    Local $iRow, $iScore, $iBestMove, $iBestScore = -$INFINITY

    For $iMove = 0 To 6
        If IsValidMove($aBoard, $iMove) Then
            $iRow = PiecesInColumn($aBoard, $iMove)
            $aBoard[$iRow][$iMove] = $CPU
            $iScore = AlphaBeta($aBoard, $MAX_DEPTH, -$INFINITY, +$INFINITY, False)
            DisplayScore($iMove, $iScore)

            If $iScore > $iBestScore Then
                $iBestScore = $iScore
                $iBestMove = $iMove
            ElseIf $iScore = $iBestScore Then
                If Abs($iMove-3) < Abs($iBestMove-3) Then
                    $iBestMove = $iMove
                EndIf
            EndIf

            $aBoard[$iRow][$iMove] = $EMPTY_SQUARE
        EndIf
    Next

    Return $iBestMove
EndFunc

Func CheckState($aBoard, $sSide)
    Local $aState[9] = ["GoOn", -1, -1, -1, -1, -1, -1, -1, -1]
    Local $bBoardFull = True

    For $iRow = 0 To 5
        For $iCol = 0 To 6
            If $aBoard[$iRow][$iCol] <> $EMPTY_SQUARE Then
                ;; Check horizontal.
                If $iCol+3 <= 6 Then
                    If $aBoard[$iRow][$iCol] = $sSide And $aBoard[$iRow][$iCol+1] = $sSide And _
                        $aBoard[$iRow][$iCol+2] = $sSide And $aBoard[$iRow][$iCol+3] = $sSide Then
                        $aState[0] = "Win"
                        $aState[1] = $iRow
                        $aState[2] = $iCol
                        $aState[3] = $iRow
                        $aState[4] = $iCol+1
                        $aState[5] = $iRow
                        $aState[6] = $iCol+2
                        $aState[7] = $iRow
                        $aState[8] = $iCol+3
                        Return $aState
                    EndIf
                EndIf
                ;; Check vertical.
                If $iRow+3 <= 5 Then
                    If $aBoard[$iRow][$iCol] = $sSide And $aBoard[$iRow+1][$iCol] = $sSide And _
                        $aBoard[$iRow+2][$iCol] = $sSide And $aBoard[$iRow+3][$iCol] = $sSide Then
                        $aState[0] = "Win"
                        $aState[1] = $iRow
                        $aState[2] = $iCol
                        $aState[3] = $iRow+1
                        $aState[4] = $iCol
                        $aState[5] = $iRow+2
                        $aState[6] = $iCol
                        $aState[7] = $iRow+3
                        $aState[8] = $iCol
                        Return $aState
                    EndIf
                EndIf
                ;; Check ascending diagonal.
                If $iRow+3 <= 5 And $iCol+3 <= 6 Then
                    If $aBoard[$iRow][$iCol] = $sSide And $aBoard[$iRow+1][$iCol+1] = $sSide And _
                        $aBoard[$iRow+2][$iCol+2] = $sSide And $aBoard[$iRow+3][$iCol+3] = $sSide Then
                        $aState[0] = "Win"
                        $aState[1] = $iRow
                        $aState[2] = $iCol
                        $aState[3] = $iRow+1
                        $aState[4] = $iCol+1
                        $aState[5] = $iRow+2
                        $aState[6] = $iCol+2
                        $aState[7] = $iRow+3
                        $aState[8] = $iCol+3
                        Return $aState
                    EndIf
                EndIf
                ;; Check descending diagonal.
                If $iRow-3 >= 0 And $iCol+3 <= 6 Then
                    If $aBoard[$iRow][$iCol] = $sSide And $aBoard[$iRow-1][$iCol+1] = $sSide And _
                        $aBoard[$iRow-2][$iCol+2] = $sSide And $aBoard[$iRow-3][$iCol+3] = $sSide Then
                        $aState[0] = "Win"
                        $aState[1] = $iRow
                        $aState[2] = $iCol
                        $aState[3] = $iRow-1
                        $aState[4] = $iCol+1
                        $aState[5] = $iRow-2
                        $aState[6] = $iCol+2
                        $aState[7] = $iRow-3
                        $aState[8] = $iCol+3
                        Return $aState
                    EndIf
                EndIf
            Else
                $bBoardFull = False
            EndIf
        Next
    Next

    If $bBoardFull Then
        $aState[0] = "Draw"
    EndIf

    Return $aState
EndFunc

Func CloseApp()
    _SoundClose($aWaitingSound)
    _SoundClose($aPlayerWinSound)
    _SoundClose($aCPUWinSound)
    _SoundClose($aDrawSound)
    _SoundClose($aMoveSound)
    _SoundClose($aInValidMoveSound)
    _SoundClose($aKeyPressSound)
    _GDIPlus_PenDispose($hPenPurple)
    _GDIPlus_BrushDispose($hBrushWhite)
    _GDIPlus_BrushDispose($hBrushYellow)
    _GDIPlus_BrushDispose($hBrushRed)
    _GDIPlus_GraphicsDispose($hBuffer)
    _GDIPlus_BitmapDispose($hBitmap)
    _GDIPlus_GraphicsDispose($hGraphics)
    _GDIPlus_Shutdown()
    GUIDelete($hGUI)
    Exit
EndFunc

Func CPUMoved()
    $g_bDetectPlayerMove = False
    $g_iMove = AI($g_aBoard)
    DrawBoard()
    _SoundPlay($aMoveSound, 1)
    $g_bDetectPlayerMove = True
    Return True
EndFunc

Func DisplayScore($iCol, $iScore)
    Local Static $iLastCol = 0, $sScore

    If $iCol <= $iLastCol Then
        $sScore = ''
        $sScore = $sScore &  "   " & $iCol+1 & " = " & $iScore
    Else
        $sScore = $sScore &  " | " & $iCol+1 & " = " & $iScore
    EndIf

    $iLastCol = $iCol
    WinSetTitle($hGUI, "", "Connect 4" & $sScore)
EndFunc

Func DrawBoard()
    Local $iX, $iY, $iTopPieceY, $iPiecesInColumn
    Local Static $iLastMove = -1

    $g_bDetectPlayerMove = False

    If $g_iMove > -1 Then
        $iX = $COL0_X+$g_iMove*$SQUARE_WIDTH
        $iY = $ROW0_Y-6*$SQUARE_WIDTH
        $iPiecesInColumn = PiecesInColumn($g_aBoard, $g_iMove)
        $iTopPieceY = $ROW0_Y-$iPiecesInColumn * $SQUARE_WIDTH

        Do
            DrawBackground()
            DrawPiecesOnBoard()
            DrawPieceDropping($iX, $iY)
            DrawFrame()
            DrawColorIndicators()
            DrawWinCounter()
            DrawWhoseTurn()
            DrawBufferToGUI()
        Until $iY > $iTopPieceY

        $g_aBoard[$iPiecesInColumn][$g_iMove] = $g_sTurn
        $iLastMove = $g_iMove
        $g_iMove = -1
    EndIf

    DrawBackground()
    DrawPiecesOnBoard()
    DrawFrame()
    DrawColorIndicators()
    DrawWinCounter()
    DrawWhoseTurn()
    DrawMoveMarker($iLastMove)

    Switch $g_aState[0]
        Case "Win"
            For $iIdx = 1 To 8 Step 2
                $iX = $COL0_X+$g_aState[$iIdx+1]*$SQUARE_WIDTH
                $iY = $ROW0_Y-$g_aState[$iIdx]*$SQUARE_WIDTH
                _GDIPlus_GraphicsDrawEllipse($hBuffer, $iX, $iY, $PIECE_WIDTH, $PIECE_HEIGHT, $hPenPurple)
            Next
            Switch $g_sTurn
                Case $PLAYER
                    DrawPlayerWin()
                Case $CPU
                    DrawCPUWin()
            EndSwitch
            DrawPlayAgain()
            $iLastMove = -1
        Case "Draw"
            DrawItsADraw()
            DrawPlayAgain()
            $iLastMove = -1
    EndSwitch

    DrawBufferToGUI()
    $g_bDetectPlayerMove = True
EndFunc

Func DrawBackground()
    _GDIPlus_GraphicsFillRect($hBuffer, 0, 0, $GUI_WIDTH, $GUI_HEIGHT, $hBrushWhite)
EndFunc

Func DrawBufferToGUI()
    _GDIPlus_GraphicsDrawImageRect($hGraphics, $hBitmap, 0, 0, $GUI_WIDTH, $GUI_HEIGHT)
EndFunc

Func DrawColorIndicators()
    _GDIPlus_GraphicsFillEllipse($hBuffer, $PLAYER_COLOR_X, $PLAYER_COLOR_Y, $TINY_PIECE_WIDTH, $TINY_PIECE_HEIGHT, $g_hPlayerColor)
    _GDIPlus_GraphicsFillEllipse($hBuffer, $CPU_COLOR_X, $CPU_COLOR_Y, $TINY_PIECE_WIDTH, $TINY_PIECE_HEIGHT, $g_hCPUColor)
EndFunc

Func DrawFrame()
    _GDIPlus_GraphicsDrawImageRect($hBuffer, $hFrameImg, 0, 0, $GUI_WIDTH, $GUI_HEIGHT)
EndFunc

Func DrawMoveMarker($iLastMove)
    Local $aX = [155, 231, 306, 381, 455, 532, 605], $iY = 550
    Local $iImgWidth, $iImgHeight

    If $iLastMove > -1 Then
        $iImgWidth = _GDIPlus_ImageGetWidth($hMarkerImg)
        $iImgHeight = _GDIPlus_ImageGetHeight($hMarkerImg)
        _GDIPlus_GraphicsDrawImageRect($hBuffer, $hMarkerImg, $aX[$iLastMove], $iY, $iImgWidth, $iImgHeight)
    EndIf
EndFunc

Func DrawPieceDropping($iX, ByRef $iY)
    Switch $g_sTurn
        Case $PLAYER
            _GDIPlus_GraphicsFillEllipse($hBuffer, $iX, $iY, $PIECE_WIDTH, $PIECE_HEIGHT, $g_hPlayerColor)
        Case $CPU
            _GDIPlus_GraphicsFillEllipse($hBuffer, $iX, $iY, $PIECE_WIDTH, $PIECE_HEIGHT, $g_hCPUColor)
    EndSwitch

    $iY += $DROP_SPEED
EndFunc

Func DrawPiecesOnBoard()
    Local $iX, $iY

    For $iRow = 0 To 5
        For $iCol = 0 To 6
            $iX = $COL0_X+$iCol*$SQUARE_WIDTH
            $iY = $ROW0_Y-$iRow*$SQUARE_WIDTH
            Switch $g_aBoard[$iRow][$iCol]
                Case $PLAYER
                    _GDIPlus_GraphicsFillEllipse($hBuffer, $iX, $iY, $PIECE_WIDTH, $PIECE_HEIGHT, $g_hPlayerColor)
                Case $CPU
                    _GDIPlus_GraphicsFillEllipse($hBuffer, $iX, $iY, $PIECE_WIDTH, $PIECE_HEIGHT, $g_hCPUColor)
            EndSwitch
        Next
    Next
EndFunc

Func DrawCPUWin()
    Local $iImgWidth, $iImgHeight

    $iImgWidth = _GDIPlus_ImageGetWidth($hCPUWinImg)
    $iImgHeight = _GDIPlus_ImageGetHeight($hCPUWinImg)
    _GDIPlus_GraphicsDrawImageRect($hBuffer, $hCPUWinImg, $CPU_WIN_X, $CPU_WIN_Y, $iImgWidth, $iImgHeight)
EndFunc

Func DrawItsADraw()
    Local $iImgWidth, $iImgHeight

    $iImgWidth = _GDIPlus_ImageGetWidth($hDrawImg)
    $iImgHeight = _GDIPlus_ImageGetHeight($hDrawImg)
    _GDIPlus_GraphicsDrawImageRect($hBuffer, $hDrawImg, $DRAW_GAME_X, $DRAW_GAME_Y, $iImgWidth, $iImgHeight)
EndFunc

Func DrawPlayAgain()
    Local $iImgWidth, $iImgHeight

    $iImgWidth = _GDIPlus_ImageGetWidth($hPlayAgainImg)
    $iImgHeight = _GDIPlus_ImageGetHeight($hPlayAgainImg)
    _GDIPlus_GraphicsDrawImageRect($hBuffer, $hPlayAgainImg, $PLAY_AGAIN_X, $PLAY_AGAIN_Y, $iImgWidth, $iImgHeight)
EndFunc

Func DrawPlayerWin()
    Local $iImgWidth, $iImgHeight

    $iImgWidth = _GDIPlus_ImageGetWidth($hPlayerWinImg)
    $iImgHeight = _GDIPlus_ImageGetHeight($hPlayerWinImg)
    _GDIPlus_GraphicsDrawImageRect($hBuffer, $hPlayerWinImg, $PLAYER_WIN_X, $PLAYER_WIN_Y, $iImgWidth, $iImgHeight)
EndFunc

Func DrawWinCounter()
    Local $aDigitImg[10] = [$hDigit0Img, $hDigit1Img, $hDigit2Img, $hDigit3Img, $hDigit4Img, $hDigit5Img, $hDigit6Img, $hDigit7Img, $hDigit8Img, $hDigit9Img]
    Local $sPlayerWonGames = String($g_iPlayerWonGames), $sCPUWonGames = StringReverse(String($g_iCPUWonGames))
    Local $aDigit, $iDigitImgWidth, $iDigitImgHeight, $iXP = $PLAYER_WON_GAMES_X, $iXC = $CPU_WON_GAMES_X

    $aDigit = StringSplit($sPlayerWonGames, '')

    For $iIdx = 1 To $aDigit[0]
        $iDigitImgWidth  = _GDIPlus_ImageGetWidth($aDigitImg[$aDigit[$iIdx]])
        $iDigitImgHeight = _GDIPlus_ImageGetHeight($aDigitImg[$aDigit[$iIdx]])
        _GDIPlus_GraphicsDrawImageRect($hBuffer, $aDigitImg[$aDigit[$iIdx]], $iXP, $PLAYER_WON_GAMES_Y, $iDigitImgWidth, $iDigitImgHeight)
        $iXP += $iDigitImgWidth-5
    Next

    $aDigit = StringSplit($sCPUWonGames, '')

    For $iIdx = 1 To $aDigit[0]
        $iDigitImgWidth  = _GDIPlus_ImageGetWidth($aDigitImg[$aDigit[$iIdx]])
        $iDigitImgHeight = _GDIPlus_ImageGetHeight($aDigitImg[$aDigit[$iIdx]])
        _GDIPlus_GraphicsDrawImageRect($hBuffer, $aDigitImg[$aDigit[$iIdx]], $iXC, $CPU_WON_GAMES_Y, $iDigitImgWidth, $iDigitImgHeight)
        If $iIdx < $aDigit[0] Then
            $iDigitImgWidth  = _GDIPlus_ImageGetWidth($aDigitImg[$aDigit[$iIdx+1]])
            $iXC -= $iDigitImgWidth-5
        EndIf
    Next
EndFunc

Func DrawWhoseTurn()
    Local $iImgWidth, $iImgHeight

    $iImgWidth = _GDIPlus_ImageGetWidth($hThinkImg)
    $iImgHeight = _GDIPlus_ImageGetHeight($hThinkImg)

    Switch $g_sTurn
        Case $PLAYER
            _GDIPlus_GraphicsDrawImageRect($hBuffer, $hThinkImg, $PLAYER_THINK_X, $PLAYER_THINK_Y, $iImgWidth, $iImgHeight)
        Case $CPU
            _GDIPlus_GraphicsDrawImageRect($hBuffer, $hThinkImg, $CPU_THINK_X, $CPU_THINK_Y, $iImgWidth, $iImgHeight)
    EndSwitch
EndFunc

Func Initiate()
    For $iRow = 0 To 5
        For $iCol = 0 To 6
            $g_aBoard[$iRow][$iCol] = $EMPTY_SQUARE
        Next
    Next

    $g_iPlayerWonGames = 0
    $g_iCPUWonGames = 0
    $g_hPlayerColor = $hBrushRed
    $g_hCPUColor = $hBrushYellow
    $g_sTurn = $PLAYER
    $g_iMove = -1
    $g_bDetectPlayerMove = True
EndFunc

Func IsValidMove($aBoard, $iMove)
    Return ($aBoard[5][$iMove] = $EMPTY_SQUARE) ? True : False
EndFunc

Func Max($iA, $iB)
    Return ($iA > $iB) ? $iA : $iB
EndFunc

Func Min($iA, $iB)
    Return ($iA < $iB) ? $iA : $iB
EndFunc

Func PiecesInColumn($aBoard, $iCol)
    Local $iPiecesInColumn = 0

    For $iRow = 0 To 5
        If $aBoard[$iRow][$iCol] = $EMPTY_SQUARE Then
            ExitLoop
        Else
            $iPiecesInColumn += 1
        EndIf
    Next

    Return $iPiecesInColumn
EndFunc

Func PlayAgain()
    Local $hDLL = DllOpen("user32.dll"), $sAnswer

    $g_bDetectPlayerMove = False
    _SoundPlay($aWaitingSound)

    While True
        Select
            Case _IsPressed('59')
                $sAnswer = 'Y'
                ExitLoop
            Case _IsPressed('4E')
                $sAnswer = 'N'
                ExitLoop
        EndSelect
    WEnd

    _SoundPlay($aKeyPressSound, 1)
    _SoundPause($aWaitingSound)
    DllClose($hDLL)
    $g_bDetectPlayerMove = True
    Return $sAnswer
EndFunc

Func PlayerMove()
    Local $iX = MouseGetPos(0), $iY = MouseGetPos(1)

    If $g_bDetectPlayerMove Then
        If  $iY > 130 And $iY < 568 Then
            Select
                Case $iX > 143 And $iX < 207
                    $g_iMove = 0
                Case $iX > 218 And $iX < 282
                    $g_iMove = 1
                Case $iX > 293 And $iX < 357
                    $g_iMove = 2
                Case $iX > 368 And $iX < 432
                    $g_iMove = 3
                Case $iX > 443 And $iX < 507
                    $g_iMove = 4
                Case $iX > 518 And $iX < 582
                    $g_iMove = 5
                Case $iX > 593 And $iX < 657
                    $g_iMove = 6
            EndSelect
        EndIf

        If $g_iMove > -1 Then
            If Not(IsValidMove($g_aBoard, $g_iMove)) Then
                _SoundPlay($aInValidMoveSound, 1)
                $g_iMove = -1
            EndIf
        EndIf
    EndIf
EndFunc

Func PlayerMoved()
    If $g_iMove > -1 Then
        DrawBoard()
        _SoundPlay($aMoveSound, 1)
        Return True
    Else
        Return False
    EndIf
EndFunc

Func Reset()
    WinSetTitle($hGUI, "", "Connect 4")

    Select
        Case $g_sTurn = $PLAYER And $g_aState[0] = "Win"
            $g_iPlayerWonGames += 1
        Case $g_sTurn = $CPU And $g_aState[0] = "Win"
            $g_iCPUWonGames += 1
    EndSelect

    Swap($g_hPlayerColor, $g_hCPUColor)

    Select
        Case $g_hPlayerColor = $hBrushRed
            $g_sTurn = $PLAYER
        Case $g_hCPUColor = $hBrushRed
            $g_sTurn = $CPU
    EndSelect

    For $iRow = 0 To 5
        For $iCol = 0 To 6
            $g_aBoard[$iRow][$iCol] = $EMPTY_SQUARE
        Next
    Next

    $g_aState[0] = "GoOn"
    For $iIdx = 1 To 8
        $g_aState[$iIdx] = -1
    Next
EndFunc

Func Swap(ByRef $A, ByRef $B)
    Local $Temp = $A

    $A = $B
    $B = $Temp
EndFunc
;;-----------------------------------------------------------------------------------------------------------
Func AlphaBeta($aNode, $iDepth, $iAlpha, $iBeta, $bMaximizingPlayer)
    Local $aChildNodes[7], $iV, $iNumOfNodes, $sState

    If $iDepth = 0 Or IsTerminal($aNode, $sState) Then
        Return Score($aNode, $iDepth, $sState)
    EndIf

    If $bMaximizingPlayer Then
        $iV = -$INFINITY
        GetChildNodes($aNode, $CPU, $iNumOfNodes, $aChildNodes)

        For $iIdx = 0 To $iNumOfNodes-1
            $iV = Max($iV, AlphaBeta($aChildNodes[$iIdx], $iDepth-1, $iAlpha, $iBeta, False))
            $iAlpha = Max($iAlpha, $iV)
            If $iBeta <= $iAlpha Then
                ExitLoop
            EndIf
        Next

        Return $iV
    Else
        $iV = +$INFINITY
        GetChildNodes($aNode, $PLAYER, $iNumOfNodes, $aChildNodes)

        For $iIdx = 0 To $iNumOfNodes-1
            $iV = Min($iV, AlphaBeta($aChildNodes[$iIdx], $iDepth-1, $iAlpha, $iBeta, True))
            $iBeta = Min($iBeta, $iV)
            If $iBeta <= $iAlpha Then
                ExitLoop
            EndIf
        Next

        Return $iV
    EndIf
EndFunc

Func GetChildNodes($aNode, $sSide, ByRef $iNumOfNodes, ByRef $aChildNodes)
    Local $aMoveOrder = [3, 2, 4, 1, 5, 0, 6]
    Local $iRow

    $iNumOfNodes = 0

    For $iIdx = 0 To 6
        If IsValidMove($aNode, $aMoveOrder[$iIdx]) Then
            $iRow = PiecesInColumn($aNode, $aMoveOrder[$iIdx])
            $aNode[$iRow][$aMoveOrder[$iIdx]] = $sSide
            $aChildNodes[$iNumOfNodes] = $aNode
            $aNode[$iRow][$aMoveOrder[$iIdx]] = $EMPTY_SQUARE
            $iNumOfNodes += 1
        EndIf
    Next
EndFunc

Func IsTerminal($aNode, ByRef $sResult)
    Local $bBoardFull = True

    For $iRow = 0 To 5
        For $iCol = 0 To 6
            If $aNode[$iRow][$iCol] <> $EMPTY_SQUARE Then
                ;; Check horizontal.
                If $iCol+3 <= 6 Then
                    If $aNode[$iRow][$iCol] = $aNode[$iRow][$iCol+1] And _
                        $aNode[$iRow][$iCol+1] = $aNode[$iRow][$iCol+2] And _
                        $aNode[$iRow][$iCol+2] = $aNode[$iRow][$iCol+3] Then
                        $sResult = $aNode[$iRow][$iCol]
                        Return True
                    EndIf
                EndIf
                ;; Check vertical.
                If $iRow+3 <= 5 Then
                    If $aNode[$iRow][$iCol] = $aNode[$iRow+1][$iCol] And _
                        $aNode[$iRow+1][$iCol] = $aNode[$iRow+2][$iCol] And _
                        $aNode[$iRow+2][$iCol] = $aNode[$iRow+3][$iCol] Then
                        $sResult = $aNode[$iRow][$iCol]
                        Return True
                    EndIf
                EndIf
                ;; Check ascending diagonal.
                If $iRow+3 <= 5 And $iCol+3 <= 6 Then
                    If $aNode[$iRow][$iCol] = $aNode[$iRow+1][$iCol+1] And _
                        $aNode[$iRow+1][$iCol+1] = $aNode[$iRow+2][$iCol+2] And _
                        $aNode[$iRow+2][$iCol+2] = $aNode[$iRow+3][$iCol+3] Then
                        $sResult = $aNode[$iRow][$iCol]
                        Return True
                    EndIf
                EndIf
                ;; Check descending diagonal.
                If $iRow-3 >= 0 And $iCol+3 <= 6 Then
                    If $aNode[$iRow][$iCol] = $aNode[$iRow-1][$iCol+1] And _
                        $aNode[$iRow-1][$iCol+1] = $aNode[$iRow-2][$iCol+2] And _
                        $aNode[$iRow-2][$iCol+2] = $aNode[$iRow-3][$iCol+3] Then
                        $sResult = $aNode[$iRow][$iCol]
                        Return True
                    EndIf
                EndIf
            Else
                $bBoardFull = False
            Endif
        Next
    Next

    If $bBoardFull Then
        $sResult = $DRAW
        Return True
    Else
        $sResult = "None"
        Return False
    EndIf
EndFunc

Func Score($aNode, $iDepth, $sState)
    Local $iScore = 0

    Switch $sState
        Case "None"
        Case $CPU
            Return $CPU_WIN_SCORE+$iDepth
        Case $PLAYER
            Return $PLAYER_WIN_SCORE-$iDepth
        Case $DRAW
            Return $DRAW_SCORE
    EndSwitch

    For $iRow = 0 To 5
        For $iCol = 0 To 6
            If $aNode[$iRow][$iCol] = $EMPTY_SQUARE Then
                ;; Calculate left.
                If $iRow-3 >= 0 Then
                    Evaluate(Asc($aNode[$iRow][$iCol])+Asc($aNode[$iRow-1][$iCol])+Asc($aNode[$iRow-2][$iCol])+Asc($aNode[$iRow-3][$iCol]), $iScore)
                EndIf
                ;; Calculate upper left.
                If $iRow+3 <= 5 And $iCol-3 >= 0 Then
                    Evaluate(Asc($aNode[$iRow][$iCol])+Asc($aNode[$iRow+1][$iCol-1])+Asc($aNode[$iRow+2][$iCol-2])+Asc($aNode[$iRow+3][$iCol-3]), $iScore)
                EndIf
                ;; Calculate up.
                If $iRow+3 <= 5 Then
                    Evaluate(Asc($aNode[$iRow][$iCol])+Asc($aNode[$iRow+1][$iCol])+Asc($aNode[$iRow+2][$iCol])+Asc($aNode[$iRow+3][$iCol]), $iScore)
                EndIf
                ;; Calculate upper right.
                If $iRow+3 <= 5 And $iCol+3 <= 6 Then
                    Evaluate(Asc($aNode[$iRow][$iCol])+Asc($aNode[$iRow+1][$iCol+1])+Asc($aNode[$iRow+2][$iCol+2])+Asc($aNode[$iRow+3][$iCol+3]), $iScore)
                EndIf
                ;; Calculate right.
                If $iCol+3 <= 6 Then
                    Evaluate(Asc($aNode[$iRow][$iCol])+Asc($aNode[$iRow][$iCol+1])+Asc($aNode[$iRow][$iCol+2])+Asc($aNode[$iRow][$iCol+3]), $iScore)
                EndIf
            EndIf
        Next
    Next

    Return $iScore
EndFunc

Func Evaluate($iSum, ByRef $iScore)
    Switch $iSum
        Case $3C1E
            $iScore += 24
        Case $3P1E
            $iScore += -24
        Case $2C2E
            $iScore += 8
        Case $2P2E
            $iScore += -8
        Case $1C3E
            $iScore += 2
        Case $1P3E
            $iScore += -2
    EndSwitch
EndFunc

 


small minds discuss people average minds discuss events great minds discuss ideas.... and use AutoIt....

Share this post


Link to post
Share on other sites
UEZ
Posted (edited)

Nice implementation. :thumbsup:

 

But when when the score is even (all coins were used) the game doesn't end -> no "it is a draw" message.

Edited by UEZ

Please don't send me any personal message and ask for support! I will not reply!

Selection of finest graphical examples at Codepen.io

The own fart smells best!
Her 'sikim hıyar' diyene bir avuç tuz alıp koşma!
¯\_(ツ)_/¯  ٩(●̮̮̃•̃)۶ ٩(-̮̮̃-̃)۶ૐ

Share this post


Link to post
Share on other sites
Barney

Hi Chimp, nice play! If we play carefully, we have the chance to win. I set the $MAX_DEPTH to 4,

If it's bigger then 4, it's slow ... the evaluation function is the key!

Share this post


Link to post
Share on other sites
Barney

Thanks UEZ! :D

Oh, a bug found?! I will try to fix it.

Share this post


Link to post
Share on other sites
Barney

Hi, I just upload a new version 2.0 to solve a problem, In version 1.0 when we reach an end position(either
CPU or Player win) with two groups of "connect 4" pieces join together, the program just highlight one group, now
both groups will be highlighted, and I change the search algorithm from MiniMax to NegaMax, it's a bit faster now.

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  

  • Similar Content

    • spikespaz
      By spikespaz
      The rule against game automation is misguided and unhelpful for the following reasons.
       
      I am a developer, using AutoIt to automate testing for a game that I am developing. Singleplayer games can be mundane and sometimes cheating can be overlooked. Multiplayer games usually have anticheat against repetitive robotic actions anyways. People want to override the control schemes of games with bad customization. Eg. Axiom Verge, Fortnite (Crouching). Game automation is not always bad. If the user feels the need to automate a singleplayer game for boring or mundane tasks, that is their choice. Similarly, if I wish to use scripts to automate testing, that is my choice.
      Game automation can be a problem for online multiplayer games, giving players a competitive advantage. This could be countered by common sense; ask OP what game they are automating, and is it an online game? What is this script for, and what does it seem to do?

      Please revise the rule as it seems very unnecessary and harmful to people seeking help with innocent attempts at game automation.
    • Mannyfresh31
      By Mannyfresh31
      This is a game that I'm developing for Slot machines I just finished translating it to English my original version is in Spanish so if I miss spelled or translated something wrongly please accept my apologies I just releasing the Compile version this time just the .exe file Because I'm saving the code for myself since the software was originally written for my business or if someone wants it they have to pay but not you you getting it for free here at Autoit .
      once you run the software for first time is going to ask you for a license and it wont work without it you can get an Access code (license) by request just PM me and provide the code that the software will give when you press the cancel button three times.
      anyways once you're in the game the following hotkeys are set for interacting with it
      "q" for slot one or one credit
      "d" for slot two or five credits
      "1" or "2" or "3" or "4" or "5" to play simple, double, threeple, etc.  once you have credits of course
      "p" to stop the numbers once you're playing
      "c" for configurations or settings
      "i" for printing the receipt
      "s" for exit
      "t" for trading points for credits
       
      Link to download the game https://drive.google.com/open?id=1x22AM80fjrDjTwwAp_TqbdWyTabawoQX
       
      and here are some videos so you can see what the game is about
       
       
       
       
       
       
       
       
    • FMS
      By FMS
      Hello,
      I'm building whit mine limited coding know-how a AI in Autoit....
      Just because..... why not
      It's not doing what i tought it would do.
      I hope somebody could help me whit this script?
      so far :
      -building an learning grid --> AI needs to guess the label in the grin whit only the X and Y value.
      -Building an array filled whit random values as weights.
      -quess the label
      -learn - if quess not the same as the label go change the weights

      At this point (the changing of the weights) I've some strange result and hope somebody could point me in the right direction
      I think that the problem is in the formula for changing the weights.

      PS. I'm also open for good coding practice   I'm learning coding as i go
      #AutoIt3Wrapper_Au3Check_Parameters=-d -w 1 -w 2 -w 3 -w 4 -w 5 -w 6 -w 7 #include <MsgBoxConstants.au3> #include <Array.au3> build_grid_list() build_neurons() _ArrayDisplay($gridlist,"$gridlist 0","",32) _ArrayDisplay($neurons,"$neurons 0","",32) For $i = 1 To UBound($gridlist,1) - 1 If $gridlist[$i][3] = 1 Then go_learn($neurons[1][0],$gridlist[$i][0]) EndIf Next _ArrayDisplay($gridlist,"$gridlist 1","",32) _ArrayDisplay($neurons,"q$neurons 1","",32) Func go_learn($neuron_ID,$gridlist_ID) Local $test = guess($gridlist_ID , $neuron_ID ) For $i = 1 To 5000 If $test <> $gridlist[$gridlist_ID][4] Then change_wieght($neuron_ID , $gridlist_ID ) $test = guess($gridlist_ID , $neuron_ID ) Else ExitLoop EndIf Next EndFunc Func guess($gridlist_ID , $neuron_ID = 1 ,$raw = 0);, $loop = 0 ) Local $temp = 0 ;~ For $i = 1 To ubound($neurons,1) -1 ; input1 * W1 + input2 * W2 + Bias(1) * W3 $temp = $gridlist[$gridlist_ID][1] * $neurons[$neuron_ID][1] + $gridlist[$gridlist_ID][2] * $neurons[$neuron_ID][2] + 1 * $neurons[$neuron_ID][3] ;activate (sign) If $temp >= 0 Then $neurons[$neuron_ID][6] = 1 Else $neurons[$neuron_ID][6] = -1 EndIf $gridlist[$gridlist_ID][5] = $neurons[$neuron_ID][6] $gridlist[$gridlist_ID][6] = $gridlist[$gridlist_ID][6] + 1 $neurons[$neuron_ID][9] = $neurons[$neuron_ID][9] + 1 If $raw = 0 Then Return $neurons[$neuron_ID][6] Else Return $temp EndIf EndFunc Func build_neurons($needed_neurons = 10 ) Global $neurons[$needed_neurons + 1][10] $neurons[0][0] = "id" $neurons[0][1] = "wieght1" $neurons[0][2] = "wieght2" $neurons[0][3] = "wieght3" $neurons[0][4] = "input1" $neurons[0][5] = "input2" $neurons[0][6] = "output" $neurons[0][7] = "tweak_counter" $neurons[0][8] = "not_tweak_counter" $neurons[0][9] = "quess_counter" Local $temp = 1 For $i = 1 To $needed_neurons; -1 $neurons[$i][0] = $i ;id Do ;zero_check output wieght1 $temp = Random(-1 , 1) Until $temp <> 0 $neurons[$i][1] = $temp ;wieght1 Do ;zero_check output wieght2 $temp = Random(-1 , 1) Until $temp <> 0 $neurons[$i][2] = $temp ;wieght2 Do ;zero_check output wieght3 $temp = Random(-1 , 1) Until $temp <> 0 $neurons[$i][3] = $temp ;wieght3 $neurons[$i][4] = 0 ;input1 $neurons[$i][5] = 0 ;input2 Do ;zero_check output $temp = Random(-1 , 1 , 1) Until $temp <> 0 $neurons[$i][6] = $temp ;output +1 / -1 ;~ $neurons[$i][6] = 0 ;output $neurons[$i][7] = 0 ;tweak_counter $neurons[$i][8] = 0 ;not_tweak_counter $neurons[$i][9] = 0 ;quess_counter Next EndFunc Func build_grid_list($grid_x = 10 ,$grid_y = 10 ) Global $gridlist[($grid_x * $grid_y) + 1 ][7] Local $counter = 1 $gridlist[0][0] = "ID" $gridlist[0][1] = "X" $gridlist[0][2] = "Y" $gridlist[0][3] = "Active" $gridlist[0][4] = "Label" $gridlist[0][5] = "quessed" $gridlist[0][6] = "quessed_counter" For $x = 0 to $grid_x - 1 For $y = 0 to $grid_y - 1 $gridlist[$counter][0] = $counter $gridlist[$counter][1] = $x $gridlist[$counter][2] = $y If Random(-1 , 1) >= 0 Then $gridlist[$counter][3] = 0 Else $gridlist[$counter][3] = 1 EndIf If $x > $y Then $gridlist[$counter][4] = 1 Else $gridlist[$counter][4] = -1 EndIf $gridlist[$counter][5] = -99 $gridlist[$counter][6] = 0 $counter = $counter + 1 Next Next EndFunc Func change_wieght($neuron_id , $grid_id );, $W1 , $W2 ) ;W1 = W1 + ^W1 (some change in W1) ;^W = err(known) * input ($neurons[$id][3] = "output") * learningrate ;$neurons[$id][1] = $neurons[$id][1] + "wieght1" Local $iReturn = False ; Desired | Quess | Error ; -1 -1 0 ; -1 +1 -2 ; +1 -1 +2 ; +1 +1 0 Local $error = $gridlist[$grid_id][4] - $neurons[$neuron_id][6] If $error <> 0 Then Local $learningrate = 0.1 Local $str_len1 = StringLen($neurons[$neuron_id][1]) Local $str_len2 = StringLen($neurons[$neuron_id][2]) Local $str_len3 = StringLen($neurons[$neuron_id][3]) Local $dif_weights1 = $error * $neurons[$neuron_id][1] * $learningrate Local $dif_weights2 = $error * $neurons[$neuron_id][2] * $learningrate Local $dif_weights3 = $error * $neurons[$neuron_id][3] * $learningrate Local $verschil1 = StringLeft($dif_weights1,$str_len1) Local $verschil2 = StringLeft($dif_weights2,$str_len2) Local $verschil3 = StringLeft($dif_weights3,$str_len3) Local $new_wieght1 = $neurons[$neuron_id][1] + $verschil1 Local $new_wieght2 = $neurons[$neuron_id][2] + $verschil2 Local $new_wieght3 = $neurons[$neuron_id][3] + $verschil3 $neurons[$neuron_id][1] = StringLeft($new_wieght1,$str_len1) $neurons[$neuron_id][2] = StringLeft($new_wieght2,$str_len2) $neurons[$neuron_id][3] = StringLeft($new_wieght3,$str_len3) $neurons[$neuron_id][7] = $neurons[$neuron_id][7] + 1 ;"counter" $iReturn = False Else $neurons[$neuron_id][8] = $neurons[$neuron_id][8] + 1 ;"not counter" $iReturn = True EndIf Return $iReturn EndFunc thanks in advanced.
       
    • Slaventii_M
      By Slaventii_M
      Hi everybody!
      I've been studying Autoit not so long ago and today I want to share my game with you.
      Please check it out and tell me your opinion.
      Files in the archive:
      BG.jpg
      Mole.png
      Mole_Dead.png
      Shoot.wav
      Cursor.cur
      Game_Icon.iso
      Mole Shooter.au3
      Mole Shooter.exe
      Screenshots:
      Screenshot 1
      Screanshot 2
      Screanshot 3
      Files:
      Mole Shooter.rar
      Created and tested:
      Windows XP SP3 Game Edition (x86)
      Autoit Version: 3.3.10.2
    • seangriffin
      By seangriffin
      This is my first release of Box2D for AutoIT.  A very fun and exciting UDF to be bringing to you all.  For those who don't know, Box2D is a physics engine (see http://box2d.org/).  It simulates physical systems using rigid bodies and collision detection.  It is arguably the most prevalent physics engine in existence, within popular physics-based games like Angry Birds using it, and is also at the core of other advanced physics engines such as Unity.
      For a quick demonstration of this UDF in action see the following YouTube video ->  https://youtu.be/h5QH1O63Wik
      Or play "Angry Nerds", the demo game, for yourself.  Visit the EXAMPLES section below.
      Box2D is purely a mathematical engine.  It can model physics numerically.  This in itself is quite interesting, however it really shines when applied to a good graphics engine.  Recently I posted my SFML UDF over here ...
      After benchmarking several popular graphics engines (i.e. GDI+, Direct2D, Irrlicht and SFML) with this Box2D UDF I've selected SFML as my favourite, and the engine that I believe performs the best (fastest) with a rich set of functions appropriate for any physics engine.  With Box2D married with SFML, and running on AutoIT, the results are stunning.
      A HUGE THANK-YOU to jRowe and linus (for the Irrlicht UDF) and UEZ for his post on GDI+ loading screens and trancexx and Eukalyptus for their work on Direct2D.  Without their talents I would not have been able to reach this point on my own.
      The Box2D library is available for C++.  Way back in 2010 it was ported to Pure C by "Paril" and made available on Google Code and known as "Box2C".  Google Code has since been shut down but the but the archive of Box2C still exists to this day here -> https://code.google.com/archive/p/box2c.  This is the library which I have ported to AutoIT in this UDF.
      SFML I am also porting to AutoIT under a separate UDF, as linked above.
      Building this UDF has been a dream come true.  I've been fascinated by physics-based games as far back as the golden age of gaming in the 80's, with thrust style games like Asteroids and platformers like Donkey Kong.  I admired game developers like Jeremy Smith who created what may have been the first true game physics engines for the home computers.  I was astonished by their talents in games like Thrust and Exile for the C64.  Over the years I've attempted to mimic at least some of their success in my own games but alas I can not match their skills.  Now much older automation tools have become my game.  I use them almost every day, AutoIT included.  I've dabbled in other languages for physics game development, like Scratch and Unity, but sadly I had wondered why AutoIT, with all it's glorious capabilities and rapid scripting features, didn't have such a feature.  Hence this UDF.
      This UDF demands a big time investment, and I am time poor, but I have a keen interest in the topic and will do my best to continue it's development.  I am only a hobbyist game developer and welcome comments and suggestions from those far smarter than I on this topic.
      REQUIREMENTS:
      AutoIt3 3.2 or higher LIST OF FUNCTIONS (in progress):
      I've split this UDF into two halves.  "Box2C.au3" is a UDF specifically for Box2C, the C API for Box2D.  It provides the mathematics behind the engine.  Additionally I'm providing "Box2CEx.au3" as an Extension that provides the graphics and gaming functions for Box2D.
      Within the core "Box2C.au3" UDF:
      _Box2C_Startup _Box2C_Shutdown _Box2C_b2Vec2_Constructor _Box2C_b2Vec2_Length _Box2C_b2Vec2_Distance _Box2C_b2World_Constructor _Box2C_b2World_CreateBody _Box2C_b2World_DestroyBody _Box2C_b2World_CreateFixture _Box2C_b2World_CreateFixtureFromShape _Box2C_b2World_Step _Box2C_b2BoxShape_Constructor _Box2C_b2CircleShape_Constructor _Box2C_b2PolygonShape_Constructor _Box2C_b2PolygonShape_Set _Box2C_b2PolygonShape_CrossProductVectorScalar _Box2C_b2PolygonShape_CrossProductVectorVector _Box2C_b2PolygonShape_Normalize _Box2C_b2PolygonShape_ComputeCentroid _Box2C_b2BodyDef_Constructor _Box2C_b2Body_DestroyFixture _Box2C_b2Body_GetPosition _Box2C_b2Body_SetPosition _Box2C_b2Body_GetAngle _Box2C_b2Body_SetAngle _Box2C_b2Body_SetAwake _Box2C_b2Body_SetTransform _Box2C_b2Body_GetLinearVelocity _Box2C_b2Body_SetLinearVelocity _Box2C_b2Body_GetAngularVelocity _Box2C_b2Body_SetAngularVelocity _Box2C_b2Body_ApplyForce _Box2C_b2Body_ApplyForceAtBody _Box2C_b2Body_ApplyDirectionalForceAtBody _Box2C_b2Body_ApplyTorque _Box2C_b2Fixture_GetShape _Box2C_b2Fixture_GetDensity _Box2C_b2Fixture_SetDensity _Box2C_b2Fixture_GetRestitution _Box2C_b2Fixture_SetRestitution _Box2C_b2Fixture_GetFriction _Box2C_b2Fixture_SetFriction _Box2C_b2Fixture_SetSensor Within y "Box2CEx.au3" extension:
      x_metres_to_gui_x y_metres_to_gui_y metres_to_pixels atan2 radians_to_degrees degrees_to_radians _Box2C_Setup_SFML _Box2C_b2Vec2_GetGUIPosition _Box2C_b2World_Setup _Box2C_b2World_GDIPlusSetup _Box2C_b2World_SetPixelsPerMetre _Box2C_b2World_SetGUIArea _Box2C_b2World_GetGUIArea _Box2C_b2World_GetGUIAreaCenter _Box2C_b2World_Create _Box2C_b2World_Step_Ex _Box2C_b2World_StartAnimation _Box2C_b2World_Animate_SFML _Box2C_b2World_Animate_GDIPlus _Box2C_b2World_WaitForAnimateEnd _Box2C_b2ShapeArray_AddItem_SFML _Box2C_b2ShapeArray_SetItem_SFML _Box2C_b2ShapeArray_GetItemImagePath_SFML _Box2C_b2PolygonShape_ArrayAdd_Irrlicht _Box2C_b2PolygonShape_ArrayAdd_GDIPlus _Box2C_b2BodyDefArray_AddItem _Box2C_b2FixtureArray_SetItemSensor _Box2C_b2FixtureArray_GetItemDensity _Box2C_b2FixtureArray_SetItemDensity _Box2C_b2FixtureArray_GetItemRestitution _Box2C_b2FixtureArray_SetItemRestitution _Box2C_b2FixtureArray_GetItemFriction _Box2C_b2FixtureArray_SetItemFriction _Box2C_b2BodyArray_AddItem_SFML _Box2C_b2Body_ArrayAdd_Irrlicht _Box2C_b2Body_ArrayAdd_GDIPlus _Box2C_b2BodyArray_GetItemCount _Box2C_b2BodyArray_GetItemPosition _Box2C_b2BodyArray_SetItemPosition _Box2C_b2BodyArray_GetItemAngle _Box2C_b2BodyArray_SetItemAngle _Box2C_b2BodyArray_GetItemLinearVelocity _Box2C_b2BodyArray_SetItemActive _Box2C_b2BodyArray_SetItemAwake _Box2C_b2BodyArray_SetItemImage_SFML _Box2C_b2BodyArray_SetItemDraw _Box2C_b2BodyArray_ApplyItemForceAtBody _Box2C_b2BodyArray_ApplyItemDirectionalForceAtBody _Box2C_b2BodyArray_Transform_SFML _Box2C_b2Body_Transform_GDIPlus _Box2C_b2BodyArray_Draw_SFML _Box2C_b2Body_ArrayDrawDisplay_SFML _Box2C_b2Body_Destroy _Box2C_b2Body_Destroy_SFML _Box2C_b2Body_Rotate_GDIPlus The SFML functions used in the tests and demos will be available in the SFML UDF post (see reference above).
      EXAMPLES:
      For such a powerful physics engine coupled to such a powerful graphics engine (SFML) it's sad that I've only had time to build one functional game, thus far.  But it's a start.
      My self-titled "Angry Nerds" is merely a demo of the same game concept as the ever-so-popular Angry Birds game.  Angry Birds itself is built on top of Box2D.  Likewise Angry Nerds.  AutoIT + Box2D + SFML to be exact.  I've compiled Angry Nerds and provided an installer also, so you can quickly run the demo for yourself.  From the Github site below (in the DOWNLOAD section) run "Box2C_Angry_Nerds_Game_SFML_installer.exe" to install the demo to a location on your computer (desktop by default).  Go into this folder and run "Box2C_Angry_Nerds_Game_SFML.exe".  All instructions are displayed in-game.  Should be quite easy to work out.
      Aside from Angry Nerds there are also two test scripts:
      Box2C_linear_forces_test_SFML.au3 Box2C_angular_forces_test_SFML.au3 Feel free to run these for a basic demonstration of rigid bodies, forces and collisions.  The heart of Box2D and any physics engine.
      Lastly I also have four speed tests as follows:
      Box2C_speed_test_SFML.au3 Box2C_speed_test_Irrlicht.au3 Box2C_speed_test_D2D.au3 Box2C_speed_test_GDIPlus.au3 These were my initial evaluations into a suitable graphics engine for Box2D.  I've since settled on SFML, but feel free to execute these.  Note they may become quick defective over time as the SFML functions slowly take over.
      DOWNLOADS:
      You can download this UDF, including the examples above and associated files, from the following GitHub page:
      https://github.com/seanhaydongriffin/Box2C-UDF
      Hope you all enjoy!  I certainly am :-)
      Cheers,
      Sean.
       
×