Page 1 of 2

Minimax Algorithm - O's & X's - You can't win! :)

Posted: Sun Dec 30, 2007 11:10 am
by pdwyer
But you can draw!. :twisted:

I wanted to try out the Minimax algorithm as it's supposed to be a very simple way to create a computer player in a two player game. It turned out to be a little harder to impliment than I thought but I got it running. Pseudocode came from wikipedia http://en.wikipedia.org/wiki/Minimax (where else 8) ). Now this works I can hopefully move on to harder stuff like alpha-beta trees etc.

If you beat it let me know because it means theres a bug! :oops:

There's no dependances like pics or icons so you should be able to just compile and play,

Have Fun

Code: Select all

    #lbl_1 = 1
    #lbl_2 = 2
    #lbl_3 = 3
    #lbl_4 = 4
    #lbl_5 = 5
    #lbl_6 = 6
    #lbl_7 = 7
    #lbl_8 = 8
    #lbl_9 = 9
   
Enumeration 100
    #frm_main
    #Cbo_player
    #CMDStart
    #lbl_status
EndEnumeration

#EmptySquare        =  0 
#Game_Drawn         =  0
#Nought             = -1
#Cross              =  1
#Game_Not_Finished  =  2 


;- Fonts
Global FontID1
FontID1 = LoadFont(1, "Arial", 20, #PB_Font_Bold)
Global FontID2
FontID2 = LoadFont(2, "Arial", 28, #PB_Font_Bold)
Global FontID3
FontID3 = LoadFont(3, "Arial", 10, #PB_Font_Bold)

Global lPlayer.l 
Global GameStarted.l = #False
Global WhosTurn.l
Global Dim MainBoard.l(9)

Declare Open_frm_main()
Declare PlayersGo(Position.l)
Declare.l Min(x.l, y.l)
Declare.l Max(x.l, y.l)
Declare.l Minimax(Node, Player.l,board.l(1))
Declare.l EvalBoard(Board.l(1))
Declare UpdateBoard()
Declare ComputersGo()

;===================================================

Open_frm_main()

;===================================================

Procedure Open_frm_main()
    If OpenWindow(#frm_main, 281, 149, 155, 235, "O's & X's",  #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_TitleBar )
        If CreateGadgetList(WindowID(#frm_main))
            TextGadget(#lbl_1, 6, 63, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_1, FontID2)
            ComboBoxGadget(#Cbo_player, 3, 3, 147, 54)
            TextGadget(#lbl_2, 57, 63, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_2, FontID2)
            TextGadget(#lbl_3, 108, 63, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_3, FontID2)
            TextGadget(#lbl_4, 6, 114, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_4, FontID2)
            TextGadget(#lbl_5, 57, 114, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_5, FontID2)
            TextGadget(#lbl_6, 108, 114, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_6, FontID2)
            TextGadget(#lbl_7, 6, 165, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_7, FontID2)
            TextGadget(#lbl_8, 57, 165, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_8, FontID2)
            TextGadget(#lbl_9, 108, 165, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_9, FontID2)
            ButtonGadget(#CMDStart, 3, 27, 147, 21, "Start Game")
            TextGadget(#lbl_status, 6, 213, 147, 21, "")
            SetGadgetFont(#lbl_status, FontID3)
      
        EndIf
    EndIf
    
    SetGadgetColor(#lbl_1, #PB_Gadget_BackColor, RGB(255,255,255))
    SetGadgetColor(#lbl_2, #PB_Gadget_BackColor, RGB(255,255,255))
    SetGadgetColor(#lbl_3, #PB_Gadget_BackColor, RGB(255,255,255))
    SetGadgetColor(#lbl_4, #PB_Gadget_BackColor, RGB(255,255,255))
    SetGadgetColor(#lbl_5, #PB_Gadget_BackColor, RGB(255,255,255))
    SetGadgetColor(#lbl_6, #PB_Gadget_BackColor, RGB(255,255,255))
    SetGadgetColor(#lbl_7, #PB_Gadget_BackColor, RGB(255,255,255))
    SetGadgetColor(#lbl_8, #PB_Gadget_BackColor, RGB(255,255,255))
    SetGadgetColor(#lbl_9, #PB_Gadget_BackColor, RGB(255,255,255))

    AddGadgetItem(#Cbo_player, 0, "Human plays first")
    AddGadgetItem(#Cbo_player, 1, "Computer plays first")
    SetGadgetText(#Cbo_player,"Human plays first")
    SetGadgetText(#lbl_status,"Not started")
    
    Repeat
        Event = WaitWindowEvent()
        Select Event
            Case #PB_Event_Gadget
                EventGadget = EventGadget()

                Select EventGadget
                    Case #lbl_1
                        PlayersGo(#lbl_1)
                    Case #lbl_2
                        PlayersGo(#lbl_2)
                    Case #lbl_3
                        PlayersGo(#lbl_3)    
                    Case #lbl_4
                        PlayersGo(#lbl_4) 
                    Case #lbl_5
                        PlayersGo(#lbl_5)
                    Case #lbl_6
                        PlayersGo(#lbl_6) 
                    Case #lbl_7
                        PlayersGo(#lbl_7)                                                                         
                    Case #lbl_8
                        PlayersGo(#lbl_8) 
                    Case #lbl_9
                        PlayersGo(#lbl_9) 
                    Case #CMDStart
                        
                        GameStarted = #True
                        ; clear board
                        For i = 1 To 9
                            MainBoard(i) = 0
                        Next
                        UpdateBoard()
                        
                        If GetGadgetText(#Cbo_player) = "Human plays first"
                            ;sPlayer = "X"
                            lPlayer = #Cross
                            WhosTurn = #Cross
                            SetGadgetText(#lbl_status,"Waiting for player...")
                        Else
                            ;sPlayer = "O"
                            lPlayer = #Nought
                            WhosTurn = #Cross
                            SetGadgetText(#lbl_status,"Thinking...")
                            Delay(100)
                            ComputersGo()
                            
                        EndIf 
                EndSelect

            Case #PB_Event_CloseWindow
                EventWindow = EventWindow()
                If EventWindow = #Frm_Main
                    CloseWindow(#Frm_Main)
                Break
                EndIf
        EndSelect
    ForEver
    
EndProcedure

;===================================================

Procedure PlayersGo(Position.l)

    If GameStarted = #False
        MessageRequester("Wait!","Click 'Start Game' to start")       
    Else
        If MainBoard(Position) = 0
            MainBoard(Position) = lPlayer
            UpdateBoard()
            SetGadgetText(#lbl_status,"Thinking...")
            Delay(200)          
            ComputersGo()
        Else
            MessageRequester("Illegal Move","This space has already been played in")
        EndIf
    EndIf

EndProcedure

;===================================================

Procedure ComputersGo()

    WhosTurn = -1 * lplayer
    
    Define BestPosition.l, BestScore.l, CurrentScore.l, FirstMove.l, GameStatus.l
    
    FirstMove = #True
    
    For i = 1 To 9
        If MainBoard(i) = 0
        
        Else
            FirstMove = #False
        EndIf        
    Next
    
    If FirstMove = #True
        MainBoard(1) = WhosTurn
        UpdateBoard()
    Else    
        BestScore = -10
        For i = 1 To 9
            If MainBoard(i) = 0
                CurrentScore = Minimax(i,WhosTurn, MainBoard())
                If currentscore > BestScore
                    BestScore = CurrentScore
                    BestPosition = i
                EndIf
            EndIf
        Next
        
        MainBoard(BestPosition) = WhosTurn        
        UpdateBoard()   
    EndIf
    
    GameStatus = EvalBoard(MainBoard()) 
    
    If GameStatus = #Game_Not_Finished 
        SetGadgetText(#lbl_status,"Waiting for player...")
    ElseIf GameStatus = WhosTurn
        SetGadgetText(#lbl_status,"Computer Wins!")
        GameStarted = #False
    ElseIf gamestatus = -1 * WhosTurn
        SetGadgetText(#lbl_status,"Player Wins!")
        GameStarted = #False
    Else
        SetGadgetText(#lbl_status,"Draw!")
        GameStarted = #False
    EndIf    
    
EndProcedure

;===================================================

Procedure UpdateBoard()

    sXO.s
    For i = 1 To 9

        If MainBoard(i) = 1
            sXO = "X"
        ElseIf MainBoard(i) = -1
            sXO = "O"
        Else
            sXO = " "
        EndIf            

        SetGadgetText(i,sXO)
    Next

EndProcedure

;===================================================


Procedure.l Min(x.l, y.l)

    If x > y
        ProcedureReturn y
    Else
        ProcedureReturn x
    EndIf

EndProcedure

;===================================================

Procedure.l Max(x.l, y.l)

    If x < y
        ProcedureReturn y
    Else
        ProcedureReturn x
    EndIf

EndProcedure

;===================================================

Procedure.l EvalBoard(Board.l(1))
	
	Define BrdLoop.l, FuncRet.l, FoundEmptySpace.l
    FuncRet = 0

    ;X wins
    If (Board(1) + Board(2) + Board(3)) = 3 Or (Board(4) + Board(5) + Board(6)) = 3 Or (Board(7) + Board(8) + Board(9)) = 3 Or (Board(1) + Board(4) + Board(7)) = 3 Or (Board(2) + Board(5) + Board(8)) = 3 Or (Board(3) + Board(6) + Board(9)) = 3 Or (Board(1) + Board(5) + Board(9)) = 3 Or (Board(3) + Board(5) + Board(7)) = 3
        FuncRet = #Cross
    EndIf
    
    ;Y wins   
    If (Board(1)+Board(2)+Board(3)) = -3 Or (Board(4)+Board(5)+Board(6)) = -3 Or (Board(7)+Board(8)+Board(9)) = -3 Or (Board(1)+Board(4)+Board(7)) = -3 Or (Board(2)+Board(5)+Board(8)) = -3 Or (Board(3)+Board(6)+Board(9)) = -3 Or (Board(1)+Board(5)+Board(9)) = -3 Or (Board(3)+Board(5)+Board(7)) = -3
        FuncRet = #Nought  
    EndIf
      
	If FuncRet = 0 ;still
	
	    ;check empties or draw
	    FoundEmptySpace = #False
	    For BrdLoop = 1 To 9
	        If Board(BrdLoop) = #EmptySquare 
	            FoundEmptySpace = #True
	        EndIf 
	    Next
	    
	    If FoundEmptySpace = #True 
	        FuncRet = #Game_Not_Finished
	    Else
	        FuncRet = #Game_Drawn 
	    EndIf
	    
	EndIf

	ProcedureReturn FuncRet

EndProcedure

;===================================================

Procedure.l Minimax(Node, Player.l, Board.l(1))

    Dim Tmpboard.l(9)
    
    CopyMemory(@Board(),@TmpBoard(),10*4)
    TmpBoard(Node) = Player    
    PlayResult.l = EvalBoard(TmpBoard())

    If PlayResult = #Game_Not_Finished 
        If Player = WhosTurn  ;adversary plays next
            MaxRet = 10
            For i = 1 To 9
                If TmpBoard(i) = 0 ; empty
                    MaxRet = min(MaxRet, Minimax(i,Player * -1,TmpBoard())) 
                EndIf   
            Next
        Else
            MaxRet = -10
            For i = 1 To 9
                If TmpBoard(i) = 0 ; empty
                    MaxRet = max(MaxRet, Minimax(i,Player * -1,TmpBoard())) 
                EndIf   
            Next
        EndIf     
    Else

        If PlayResult = WhosTurn
            RetVal = 5
        ElseIf PlayResult = #Game_Drawn
            RetVal = 0
        ElseIf PlayResult = (WhosTurn * -1)
            RetVal = -5    
        Else
            Retval = MaxRet 
        EndIf
        
        ProcedureReturn retval
        
    EndIf
    
    ProcedureReturn MaxRet 

EndProcedure



Posted: Sun Dec 30, 2007 11:26 am
by DarkDragon
I played about 10 times now and no one won. :lol:

Posted: Sun Dec 30, 2007 11:42 am
by Joakim Christiansen
Hehe, nice, impossible to beat it!
But when it starts first it's kinda boring since it does the same moves every time :P

Posted: Sun Dec 30, 2007 12:03 pm
by pdwyer
Yeah, I could randomise the first move I guess, minimax recurses and sees it's a draw on every position if it goes first. Corners have more chances of winning so I guess I could randomise that.

Or, just make a little randomise function for whenever the results are the same from minimax or at start...

It's only tic-tac-toe though which I chose to be sure I had the alorithm right, is there really a point trying to make this game more interesting? :wink:

Posted: Sun Dec 30, 2007 1:54 pm
by akj
@ pdwyer
I think there is an error in your game playing logic as sometimes the computer declines to win when able to do so. Here is an example:
Computer goes first
Computer X in cell 1
Human O in cell 2
Computer X in cell 4
Human O in cell 3
Computer X in cell 5
But the last move should be: Computer X in cell 7 to win

Also, as a cosmetic detail, I think the combo box #Cbo_player should be replaced by two radio buttons (option gadgets) so as to reduce the number of mouse clicks.

Posted: Sun Dec 30, 2007 2:58 pm
by pdwyer
Well spotted.

There's no granuality in this. The evaluation function returns x-win/o-win/draw/not-finished and this gets passed back up the recurse tree.

Which means if there is more that one "sure path to victory" the system, lacking any randomization or optimisation in the sense of "which is the best path to victory" it passes the first one it finds.

Playing on 5 is also a sure path to victory and it gets found before 7 so it plays on 5.

Good point though, it would look more realistic if it scored it's results for two things "Fastest victory" and in the case where all options are draws, "most likely victory"

I'll have a play with it. :)

Thanks

Posted: Sun Dec 30, 2007 4:14 pm
by Demivec
Here's another working version of tic-tac-toe using mini-max:
http://www.purebasic.fr/english/viewtop ... ht=#174239

It's interface makes it hard to read. The program there isn't as pretty as yours, but it is functional. The code's variables and layout are mostly due to hydrate's design, who was more interrested in demonstrating the algorithym than appearance. I added to it and made changes to make it functional and commented it heavily. It does handle scoring of moves and does a random choice of winning moves (among equals).

Posted: Mon Dec 31, 2007 12:15 am
by Rook Zimbabwe
I won.

0,0 - ME
1,2 - CPU
2,0 - ME
1,0 - CPU
2,1 - ME... and two corners to chose from no matter what the computer picked I was home free...

Posted: Mon Dec 31, 2007 3:00 am
by pdwyer
:?:

if you play in the top left corner the computer will invariably play in the middle. to play in the bottom edge is certain failure and i cant reproduce that. have you modified the code?

Posted: Mon Dec 31, 2007 3:03 am
by pdwyer
Demivec wrote:Here's another working version of tic-tac-toe using mini-max:
http://www.purebasic.fr/english/viewtop ... ht=#174239

It's interface makes it hard to read. The program there isn't as pretty as yours, but it is functional. The code's variables and layout are mostly due to hydrate's design, who was more interrested in demonstrating the algorithym than appearance. I added to it and made changes to make it functional and commented it heavily. It does handle scoring of moves and does a random choice of winning moves (among equals).
cool ill take a look

ill be interested to see how someone put less effort into the interface :wink: i put very little!

Posted: Mon Dec 31, 2007 1:31 pm
by Foz
just to note, I removes the SS_NOTIFY and replaced all the labels with buttons, and it works instant on Linux.

Posted: Mon Dec 31, 2007 2:32 pm
by pdwyer
Is there no way to have clickable lable controls in linux? or is it just a painful thing to bother with?

Anyway, I updated the code and hopefully I have the bugs out, I added "Depth" and the shortest depth (ie the quickest win) is the way it goes. so the problem of it not playing a winning position when it was right in front of its face should be gone. Hopefully this hasn't added any new bugs, there were a few where the player could win after I added the depth

Code: Select all


#lbl_1 = 1
#lbl_2 = 2
#lbl_3 = 3
#lbl_4 = 4
#lbl_5 = 5
#lbl_6 = 6
#lbl_7 = 7
#lbl_8 = 8
#lbl_9 = 9

Enumeration 100
    #frm_main
    #Cbo_player
    #CMDStart
    #lbl_status
EndEnumeration

#EmptySquare        =  0 
#Game_Drawn         =  0
#Nought             = -1
#Cross              =  1
#Game_Not_Finished  =  2 


;- Fonts
Global FontID1
FontID1 = LoadFont(1, "Arial", 20, #PB_Font_Bold)
Global FontID2
FontID2 = LoadFont(2, "Arial", 28, #PB_Font_Bold)
Global FontID3
FontID3 = LoadFont(3, "Arial", 10, #PB_Font_Bold)

Global lPlayer.l 
Global GameStarted.l = #False
Global WhosTurn.l
Global Dim MainBoard.l(9)
Global Depth.l

Declare Open_frm_main()
Declare PlayersGo(Position.l)
Declare.l Min(x.l, y.l)
Declare.l Max(x.l, y.l)
Declare.l Minimax(Node, Player.l,board.l(1))
Declare.l EvalBoard(Board.l(1))
Declare UpdateBoard()
Declare ComputersGo()

;===================================================

Open_frm_main()

;===================================================

Procedure Open_frm_main()
    If OpenWindow(#frm_main, 281, 149, 155, 235, "O's & X's",  #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_TitleBar )
        If CreateGadgetList(WindowID(#frm_main))
            TextGadget(#lbl_1, 6, 63, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_1, FontID2)
            ComboBoxGadget(#Cbo_player, 3, 3, 147, 54)
            TextGadget(#lbl_2, 57, 63, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_2, FontID2)
            TextGadget(#lbl_3, 108, 63, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_3, FontID2)
            TextGadget(#lbl_4, 6, 114, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_4, FontID2)
            TextGadget(#lbl_5, 57, 114, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_5, FontID2)
            TextGadget(#lbl_6, 108, 114, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_6, FontID2)
            TextGadget(#lbl_7, 6, 165, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_7, FontID2)
            TextGadget(#lbl_8, 57, 165, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_8, FontID2)
            TextGadget(#lbl_9, 108, 165, 42, 42, "", #PB_Text_Center | #SS_NOTIFY)
            SetGadgetFont(#lbl_9, FontID2)
            ButtonGadget(#CMDStart, 3, 27, 147, 21, "Start Game")
            TextGadget(#lbl_status, 6, 213, 147, 21, "")
            SetGadgetFont(#lbl_status, FontID3)
      
        EndIf
    EndIf
    
    SetGadgetColor(#lbl_1, #PB_Gadget_BackColor, RGB(255,255,255))
    SetGadgetColor(#lbl_2, #PB_Gadget_BackColor, RGB(255,255,255))
    SetGadgetColor(#lbl_3, #PB_Gadget_BackColor, RGB(255,255,255))
    SetGadgetColor(#lbl_4, #PB_Gadget_BackColor, RGB(255,255,255))
    SetGadgetColor(#lbl_5, #PB_Gadget_BackColor, RGB(255,255,255))
    SetGadgetColor(#lbl_6, #PB_Gadget_BackColor, RGB(255,255,255))
    SetGadgetColor(#lbl_7, #PB_Gadget_BackColor, RGB(255,255,255))
    SetGadgetColor(#lbl_8, #PB_Gadget_BackColor, RGB(255,255,255))
    SetGadgetColor(#lbl_9, #PB_Gadget_BackColor, RGB(255,255,255))

    AddGadgetItem(#Cbo_player, 0, "Human plays first")
    AddGadgetItem(#Cbo_player, 1, "Computer plays first")
    SetGadgetText(#Cbo_player,"Human plays first")
    SetGadgetText(#lbl_status,"Not started")
    
    Repeat
        Event = WaitWindowEvent()
        Select Event
            Case #PB_Event_Gadget
                EventGadget = EventGadget()

                Select EventGadget
                    Case #lbl_1
                        PlayersGo(#lbl_1)
                    Case #lbl_2
                        PlayersGo(#lbl_2)
                    Case #lbl_3
                        PlayersGo(#lbl_3)    
                    Case #lbl_4
                        PlayersGo(#lbl_4) 
                    Case #lbl_5
                        PlayersGo(#lbl_5)
                    Case #lbl_6
                        PlayersGo(#lbl_6) 
                    Case #lbl_7
                        PlayersGo(#lbl_7)                                                                         
                    Case #lbl_8
                        PlayersGo(#lbl_8) 
                    Case #lbl_9
                        PlayersGo(#lbl_9) 
                    Case #CMDStart
                        
                        GameStarted = #True
                        ; clear board
                        For i = 1 To 9
                            MainBoard(i) = 0
                        Next
                        UpdateBoard()
                        
                        If GetGadgetText(#Cbo_player) = "Human plays first"
                            ;sPlayer = "X"
                            lPlayer = #Cross
                            WhosTurn = #Cross
                            SetGadgetText(#lbl_status,"Waiting for player...")
                        Else
                            ;sPlayer = "O"
                            lPlayer = #Nought
                            WhosTurn = #Cross
                            SetGadgetText(#lbl_status,"Thinking...")
                            Delay(100)
                            ComputersGo()
                            
                        EndIf 
                EndSelect

            Case #PB_Event_CloseWindow
                EventWindow = EventWindow()
                If EventWindow = #Frm_Main
                    CloseWindow(#Frm_Main)
                Break
                EndIf
        EndSelect
    ForEver
    
EndProcedure

;===================================================

Procedure PlayersGo(Position.l)

    If GameStarted = #False
        MessageRequester("Wait!","Click 'Start Game' to start")       
    Else
        If MainBoard(Position) = 0
            MainBoard(Position) = lPlayer
            UpdateBoard()
            SetGadgetText(#lbl_status,"Thinking...")
            Delay(200)          
            ComputersGo()
        Else
            MessageRequester("Illegal Move","This space has already been played in")
        EndIf
    EndIf

EndProcedure

;===================================================

Procedure ComputersGo()

    WhosTurn = -1 * lplayer
    
    Define BestPosition.l, BestScore.l, CurrentScore.l, FirstMove.l, GameStatus.l, leastDepth.l
    
    
    
    FirstMove = #True
    
    For i = 1 To 9
        If MainBoard(i) = 0
        
        Else
            FirstMove = #False
        EndIf        
    Next
    
    If FirstMove = #True
        MainBoard(1) = WhosTurn
        UpdateBoard()
    Else    
        BestScore = -10
        
        Dim PossiblePosition.l(9,2)
        
        For i = 1 To 9
            If MainBoard(i) = 0
                
                Depth = 0
                PossiblePosition(i,1) = Minimax(i,WhosTurn, MainBoard())
                PossiblePosition(i,2) = depth
                
                Debug "Score " + Str(bestscore)
                Debug "Depth " + Str(depth)
                    
                If PossiblePosition(i,1) > BestScore
                    
                    BestScore = PossiblePosition(i,1)
                    
                    ;BestPosition = i
                EndIf
            EndIf
        Next
        
        leastDepth = 1000000
        For i = 1 To 9
            If PossiblePosition(i,1) = bestscore And MainBoard(i) = 0
                Debug bestscore
                If PossiblePosition(i,2) < leastDepth
                    BestPosition = i
                    leastDepth = PossiblePosition(i,2)
                EndIf
            EndIf     
        Next
        
        Debug "played at " + Str(BestPosition)
        MainBoard(BestPosition) = WhosTurn        
        UpdateBoard()   
    EndIf
    
    GameStatus = EvalBoard(MainBoard()) 
    
    If GameStatus = #Game_Not_Finished 
        SetGadgetText(#lbl_status,"Waiting for player...")
    ElseIf GameStatus = WhosTurn
        SetGadgetText(#lbl_status,"Computer Wins!")
        GameStarted = #False
    ElseIf gamestatus = -1 * WhosTurn
        SetGadgetText(#lbl_status,"Player Wins!")
        GameStarted = #False
    Else
        SetGadgetText(#lbl_status,"Draw!")
        GameStarted = #False
    EndIf    
    
EndProcedure

;===================================================

Procedure UpdateBoard()

    sXO.s
    For i = 1 To 9

        If MainBoard(i) = 1
            sXO = "X"
        ElseIf MainBoard(i) = -1
            sXO = "O"
        Else
            sXO = " "
        EndIf            

        SetGadgetText(i,sXO)
    Next

EndProcedure

;===================================================


Procedure.l Min(x.l, y.l)

    If x > y
        ProcedureReturn y
    Else
        ProcedureReturn x
    EndIf

EndProcedure

;===================================================

Procedure.l Max(x.l, y.l)

    If x < y
        ProcedureReturn y
    Else
        ProcedureReturn x
    EndIf

EndProcedure

;===================================================

Procedure.l EvalBoard(Board.l(1))
	
	Define BrdLoop.l, FuncRet.l, FoundEmptySpace.l
    FuncRet = 0

    ;X wins
    If (Board(1) + Board(2) + Board(3)) = 3 Or (Board(4) + Board(5) + Board(6)) = 3 Or (Board(7) + Board(8) + Board(9)) = 3 Or (Board(1) + Board(4) + Board(7)) = 3 Or (Board(2) + Board(5) + Board(8)) = 3 Or (Board(3) + Board(6) + Board(9)) = 3 Or (Board(1) + Board(5) + Board(9)) = 3 Or (Board(3) + Board(5) + Board(7)) = 3
        FuncRet = #Cross
    EndIf
    
    ;Y wins   
    If (Board(1)+Board(2)+Board(3)) = -3 Or (Board(4)+Board(5)+Board(6)) = -3 Or (Board(7)+Board(8)+Board(9)) = -3 Or (Board(1)+Board(4)+Board(7)) = -3 Or (Board(2)+Board(5)+Board(8)) = -3 Or (Board(3)+Board(6)+Board(9)) = -3 Or (Board(1)+Board(5)+Board(9)) = -3 Or (Board(3)+Board(5)+Board(7)) = -3
        FuncRet = #Nought  
    EndIf
      
	If FuncRet = 0 ;still
	
	    ;check empties or draw
	    FoundEmptySpace = #False
	    For BrdLoop = 1 To 9
	        If Board(BrdLoop) = #EmptySquare 
	            FoundEmptySpace = #True
	        EndIf 
	    Next
	    
	    If FoundEmptySpace = #True 
	        FuncRet = #Game_Not_Finished
	    Else
	        FuncRet = #Game_Drawn 
	    EndIf
	    
	EndIf

	ProcedureReturn FuncRet

EndProcedure

;===================================================

Procedure.l Minimax(Node, Player.l, Board.l(1))

    Dim Tmpboard.l(9)
    
    Depth = Depth + 1
    
    CopyMemory(@Board(),@TmpBoard(),10*4)
    TmpBoard(Node) = Player    
    PlayResult.l = EvalBoard(TmpBoard())

    If PlayResult = #Game_Not_Finished 
        If Player = WhosTurn  ;adversary plays next
            MaxRet = 10
            For i = 1 To 9
                If TmpBoard(i) = 0 ; empty
                    MaxRet = min(MaxRet, Minimax(i,Player * -1,TmpBoard())) 
                EndIf   
            Next
        Else
            MaxRet = -10
            For i = 1 To 9
                If TmpBoard(i) = 0 ; empty
                    MaxRet = max(MaxRet, Minimax(i,Player * -1,TmpBoard())) 
                EndIf   
            Next
        EndIf     
    Else

        If PlayResult = WhosTurn
            RetVal = 5
        ElseIf PlayResult = #Game_Drawn
            RetVal = 0
        ElseIf PlayResult = (WhosTurn * -1)
            RetVal = -5    
        Else
            Retval = MaxRet 
        EndIf
        
        ProcedureReturn retval
        
    EndIf
    
    ProcedureReturn MaxRet 

EndProcedure


Posted: Mon Dec 31, 2007 4:12 pm
by Rook Zimbabwe
I must have been using older code... Not the new code. I like the new look... 8)

Posted: Mon Dec 31, 2007 5:13 pm
by Num3
Minimax Algorithm - O's & X's - You can't win! :)
But you can LOOSE :lol: :lol: :lol:

Posted: Mon Dec 31, 2007 9:18 pm
by SFSxOI
I won !!!!!!!

....because i just didn't play it :)

like the code tho, Thank You :)