Minimax Algorithm - O's & X's - You can't win! :)
Posted: Sun Dec 30, 2007 11:10 am
But you can draw!.
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
). 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!
There's no dependances like pics or icons so you should be able to just compile and play,
Have Fun

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

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

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