Here is your code with modifications to accomplish your goal
All differences between this and your code are marked with "*" at the beginning of the comment portion and either the words "added", "changed", or "modified".
Code: Select all
; -----------------------
; Tic Tac Toe
; -----------------------
; Structure Dec
Structure MainWindow;
lx.l;
ly.l;
lWidth.l;
lHeight.l ;
sTitle.s;
lParams.l;
lWID.l;
lHwnd.l;
lGdgtLst.l;
lTurn.l;
lMoves.l;
EndStructure;
Define Windows.MainWindow;
Declare CheckWin(*WindStruct.MainWindow);
Declare MaxMove(*WindStruct.MainWindow,lTurn.l,lScore.l);
Declare MakeMove(*WindStruct.MainWindow);
Declare MainWindowGadgets(*WindStruct.MainWindow);
Declare MWGadgetEvents(*WindStruct.MainWindow);
Declare MainWindowLoop(*WindStruct.MainWindow);
Global Dim BoardPos(8);
Global Dim BoardScore(8)
For v=0 To 8;
BoardPos(v)=0;
BoardScore(v)=0;
Next v;
Procedure CheckWin(*WindStruct.MainWindow);
Protected lScore.l=0;
Protected lWin.l=0;
For v=0 To 2;
lScore.l+BoardPos(v);
Next v;
If lScore=-3 Or lScore=3;
lWin=lScore;
EndIf;
lScore=0;
For v=3 To 5;
lScore.l+BoardPos(v);
Next v;
If lScore=-3 Or lScore=3;
lWin=lScore;
EndIf;
lScore=0;
For v=6 To 8;
lScore.l+BoardPos(v);
Next v;
If lScore=-3 Or lScore=3;
lWin=lScore;
EndIf;
lScore=0;
; ----------------------------------------------------------
lScore.l+BoardPos(0)+BoardPos(3)+BoardPos(6);
If lScore=-3 Or lScore=3;
lWin=lScore;
EndIf;
lScore=0;
lScore.l+BoardPos(1)+BoardPos(4)+BoardPos(7);
If lScore=-3 Or lScore=3;
lWin=lScore;
EndIf;
lScore=0;
lScore.l+BoardPos(2)+BoardPos(5)+BoardPos(8);
If lScore=-3 Or lScore=3;
lWin=lScore;
EndIf;
lScore=0;
; ----------------------------------------------------------
lScore.l+BoardPos(0)+BoardPos(4)+BoardPos(8);
If lScore=-3 Or lScore=3;
lWin=lScore;
EndIf;
lScore=0;
lScore.l+BoardPos(2)+BoardPos(4)+BoardPos(6);
If lScore=-3 Or lScore=3;
lWin=lScore;
EndIf;
lScore=0;
If lWin<>0;
Else;
If *WindStruct\lMoves=8 ;*changed, last move is 8, not 9
lWin=4;
EndIf;
EndIf;
ProcedureReturn lWin;
EndProcedure;
Procedure MaxMove(*WindStruct.MainWindow,lTurn.l,lScore.l);
Protected lBestScore.l=10000;*changed, initialized to a HIGH value so that LOWEST value can be found
Protected Dim BoardScore2(8);
Protected v,P ;*added for clarity, now includes all variables
*WindStruct\lMoves+1 ;*added, advance move count
For v=0 To 8;
If BoardPos(v)=0;;
BoardPos(v)=lTurn;
If CheckWin(*WindStruct.MainWindow)=3*lTurn ;*modified calculation to reflect which player's move is being evaluated,=current player would have a WIN for this move
BoardScore2(v)=lScore*lTurn ;*changed apparent typo to "BoardScore2(v)" and modified calculation to reflect which player's move is being evaluated
EndIf;
If BoardScore2(v)=0 ;=initial value
BoardPos(v)=-lTurn;
If CheckWin(*WindStruct.MainWindow)=3*-lTurn;;*modified calculation to reflect which player's move is being evaluated,=other player would WIN if they took this move
BoardScore2(v)=lScore*lTurn ;*modified calculation to reflect which player's move is being evaluated
EndIf;
BoardPos(v)=lTurn;
EndIf;
If (BoardScore2(v)=0) And (*WindStruct\lMoves<8) ;*changed, =no score yet and at least one more move left
BoardScore2(v)=MaxMove(*WindStruct.MainWindow,-lTurn,lScore/2);
EndIf;
BoardPos(v)=0;
EndIf;
Next v;
For P=0 To 8;
If BoardScore2(P)<lBestScore;
lBestScore=BoardScore2(P);
EndIf;
Next P;
*WindStruct\lMoves-1 ;*added, restore move count
ProcedureReturn lBestScore;
EndProcedure;
Procedure MakeMove(*WindStruct.MainWindow);
Protected lBest.l=-1;
Protected v,P,lBestScore ;*added for clarity, now includes all variables
Protected NewList moveOptions.l() ;*added, this is used to track best move options
For v=0 To 8;
BoardScore(v)=2000 ;*changed, initialize to a HIGH value so that LOWEST value can be found
If BoardPos(v)=0;
BoardPos(v)=-1;
If CheckWin(*WindStruct.MainWindow)=-3 ;*modified to simplify calculation
BoardScore(v)=600;
EndIf;
If BoardScore(v)=2000 ;*changed, =initial value
BoardPos(v)=1;
If CheckWin(*WindStruct.MainWindow)=3
BoardScore(v)=600;
EndIf;
EndIf;
BoardPos(v)=-1;
If (BoardScore(v)=2000) And (*WindStruct\lMoves<8) ;*changed, =no score yet and at least one more move left
BoardScore(v)+MaxMove(*WindStruct.MainWindow,1,600) ;*changed value of lScore parameter to reflect and simplify calculations
EndIf;
BoardPos(v)=0;
EndIf;
Next v;
P=0;
lBestScore=10000 ;*changed, initialize to a HIGH value so that LOWEST value can be found
While P<9 ;*changed to correct loop count to go from element 0 through 8
If BoardScore(P)<lBestScore ;*changed from "is-greater" to "is-less-than"
lBestScore=BoardScore(P)
lBest=P
EndIf
P+1
Wend
If lBest<>-1 ;
For P=0 To 8 ;*added, create a list of equal value moves, all of best value
If BoardScore(P)=lBestScore ;*added
AddElement(moveOptions()) ;*added
moveOptions()=P ;*added
EndIf ;*added
Next P ;*added
SelectElement(moveOptions(),Random(CountList(moveOptions()))) ;*added, select a random move from the list
lBest=moveOptions() ;* ;*added, use random move as next move
BoardPos(lBest)=*WindStruct\lTurn;
SetGadgetText(lBest,Str(*WindStruct\lTurn));
DisableGadget(lBest,1);
EndIf;
EndProcedure;
; Gadget Loading Proc
Procedure MainWindowGadgets(*WindStruct.MainWindow);
*WindStruct\lGdgtLst=CreateGadgetList(*WindStruct\lHwnd); Declare gadget list
ButtonGadget(0,5,5,30,30,"");
ButtonGadget(1,40,5,30,30,"");
ButtonGadget(2,75,5,30,30,"");
ButtonGadget(3,5,40,30,30,"");
ButtonGadget(4,40,40,30,30,"");
ButtonGadget(5,75,40,30,30,"");
ButtonGadget(6,5,75,30,30,"");
ButtonGadget(7,40,75,30,30,"");
ButtonGadget(8,75,75,30,30,"")
ProcedureReturn *WindStruct\lGdgtLst; Return gadget list handle
EndProcedure;
; Gadget Handling Proc
Procedure MWGadgetEvents(*WindStruct.MainWindow);
lEvGad=EventGadget();
For g=0 To 8;
If lEvGad=g;
If GetGadgetText(g)="";
BoardPos(g)=*WindStruct\lTurn;
SetGadgetText(g,Str(*WindStruct\lTurn));
DisableGadget(g,1);
EndIf;
EndIf;
Next g;
EndProcedure;
; Procedures
Procedure MainWindowLoop(*WindStruct.MainWindow);
Protected lWinner,lQuit ;*added for clarity, now includes all variables
lWinner=0 ;*added, initialized to show no winner
Repeat;
Select WindowEvent();
Case #PB_Event_Gadget;
If lWinner=0 ;*changed from "if *WindStruct\lTurn<>-1" and re-ordered conditionals
MWGadgetEvents(*WindStruct.MainWindow) ;assumes a valid gadget is found
lWinner=CheckWin(*WindStruct.MainWindow) ;*added, a check is needed after EACH player's move
If lWinner=3
Debug "Player one wins the game!"
Else
If lWinner=4
Debug "No Player wins, game draw!"
Else
Debug "No Player wins."
*WindStruct\lTurn=-*WindStruct\lTurn ;toggle turn indicator
*WindStruct\lMoves+1 ;advance move count
MakeMove(*WindStruct.MainWindow) ;make a move for player 2
lWinner=CheckWin(*WindStruct.MainWindow)
If lWinner=-3
Debug "Player two wins the game!"
Else
If lWinner=4
Debug "No Player wins, game draw!"
Else
Debug "No Player wins."
EndIf
EndIf
*WindStruct\lTurn=-*WindStruct\lTurn ;toggle turn indicator
*WindStruct\lMoves+1 ;advance move count
EndIf
EndIf
EndIf
Case #PB_Event_CloseWindow;
lQuit=1;
EndSelect;
Until lQuit=1;
EndProcedure;
; Main Program
With Windows;
\lx = 50;
\ly = 50;
\lWidth = 85;
\lHeight = 110;
\sTitle = "Tic Tac Toe";
\lParams = #PB_Window_SystemMenu|#PB_Window_MinimizeGadget|#PB_Window_ScreenCentered;
\lWID = OpenWindow(#PB_Any,\lx,\ly,\lWidth,\lHeight,\sTitle,\lParams);
\lHwnd = WindowID(\lWID);
\lGdgtLst = MainWindowGadgets(Windows);
\lTurn=1;
\lMoves=0;
MainWindowLoop(Windows);
EndWith;
End;
Summary:
CheckWin() had 1 change
MaxMove() had 6 changes & 3 additions
MakeMove() had 8 changes & 1 additions (+8 additions if random move is implemented)
MainWindowLoop() had 1 change & 3 additions and some reorganizing.
Comments:
MaxMove() needed: to account for which future turn was being evaluated, to initialize the lBestScore variable to a high value instead of 0 (because if only positive values were found, we needed to find the LEAST positive), and eliminated evaluating more moves if we already made the last one.
MakeMove() needed: to initialize lBestScore to a high value (same as above), to initialize BoardScore() to a positive value between max (lBestScore) and highest positive score (600), to correct loop values for bestscore review and find the LOWEST score NOT HIGHEST score.
MainWindowLoop() needed: to check for a win after EACH move, reorganizing of conditionals.
In my opinion, for clarity all variables should be Protected in a procedure if any are. (these additions can be removed without any effect as the program is currently written though)
The random move option was added for the sake of interest (from a human's point of view). If the best scored move was equal to 300 then why not randomly select among all scores of 300 instead of only the first one? The 9 lines that add this option can be removed without any effect on the program's functions. It will simply make the game even more predictable.
I changed the program as little as possible so that you could see what was needed to correct the errors in the programs execution. You seem to complicate some things (such as windows) unnecessarily while leaving other things (such as Checkwin) overly simplified. I also noticed you used two different loops to examine scores (and made an error because of it) when the same loop would have worked for both (either both WHILE-WENDS or both FOR-NEXT) .
Merry Christmas! Now see if you can get the computer to learn that it can't expect to win at tic-tac-toe.
Here's a sample of an equivalent CheckWin() that's shorter but functionally the same (it has the added plus of discontinuing the checks when a tic-tac-toe has been found):
Code: Select all
Procedure CheckWin(*WindStruct.MainWindow) ;check if a winning position exists, return -3(player 2 win),3(player 1 win),0(no win), or 4(draw)
Protected lScore.l
Protected lWin.l=0;
lScore=0+BoardPos(0)+BoardPos(1)+BoardPos(2) ;check top row
If Abs(lScore)<>3
lScore=0+BoardPos(3)+BoardPos(4)+BoardPos(5) ;check middle row
If Abs(lScore)<>3
lScore=0+BoardPos(6)+BoardPos(7)+BoardPos(8);check bottom row
If Abs(lScore)<>3
; ---------------------------------------------------
lScore=0+BoardPos(0)+BoardPos(3)+BoardPos(6) ;check left column
If Abs(lScore)<>3
lScore=0+BoardPos(1)+BoardPos(4)+BoardPos(7) ;check middle column
If Abs(lScore)<>3
lScore=0+BoardPos(2)+BoardPos(5)+BoardPos(8) ;check right column
If Abs(lScore)<>3
; -----------------------------------------------
lScore=0+BoardPos(0)+BoardPos(4)+BoardPos(8) ;check diagonal top-left to bottom-right
If Abs(lScore)<>3
lScore=0+BoardPos(2)+BoardPos(4)+BoardPos(6) ;check diagonal top-right to bottom-left
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
EndIf
If Abs(lScore)=3 ;=a win for player 1 or 2
lWin=lScore ;=-3 for player 2, or 3 for a player 1
EndIf
If lWin<>0 ;=a win
ElseIf *WindStruct\lMoves=9 ;=no moves left to be made
lWin=4 ;if no then indicate a draw
EndIf
ProcedureReturn lWin
EndProcedure;