Help with an Othello (AKA Reversi) Game - JasonSG - 08-03-2005
I am currently working on an Othello (AKA Reversi) game in QBasic. I keep on running into 3 errors in particular, two of them QBasic generated errors (execution stops, QB gives me the error box); the other one is just QB not doing what I want it to most likely due to an error on my part. The two errors that I get are Out of Stack Space (I sometimes get this when the AI is "thinking") (See EDIT 1), and Out of Memory (I never get this during execution, only almost every time I stop the execution--by way of Ctrl-Scroll Lock-- and attempt to edit the code). The second one is extremely annoying because I have to restart QB every time I want to edit code after/while the pogram runs. The third (and most minor) of my errors is that occasionally the AI attempts to do a move that it is not allowed to do. (See EDIT 2) Here is a link to my program: Othello.bas, and the two .bi files it uses: General.bi and Mouse.bi. In order for the program to run, if you are using QB 4.0 or higher, you need to load QB with the /L option. I'm sorry for the complete lack of comments (a bad habbit of mine ). For those who just want to see the code for the AI, here it is:
Code: SUB AIMove
DIM Board2(8, 8) AS INTEGER
DIM value(8, 8)
DIM ARow AS INTEGER
DIM AColumn AS INTEGER
DIM ORow AS INTEGER
DIM OColumn AS INTEGER
DIM current(3)
RANDOMIZE (TIMER)
KEY(15) ON
KEY(16) OFF
ERASE value, current
DIM value(8, 8)
DIM current(3)
FOR ORow = 1 TO 8
FOR OColumn = 1 TO 8
Board2(ORow, OColumn) = Board(ORow, OColumn)
NEXT OColumn
NEXT ORow
FOR ARow = 1 TO 8
FOR AColumn = 1 TO 8
IF LeagMove(ARow, AColumn, AIColor) THEN
value(ARow, AColumn) = 100
ListPlace = 0
UpTo = 0
IF ARow = 8 OR ARow = 1 THEN value(ARow, AColumn) = value(ARow, AColumn) + 50
IF AColumn = 8 OR AColumn = 1 THEN value(ARow, AColumn) = value(ARow, AColumn) + 50
FOR i = 1 TO 9
value(ARow, AColumn) = value(ARow, AColumn) + FlipperL(i) / AIColor
NEXT i
OtherMoves = 0
ListPlace = 0
FOR ORow = 1 TO 8
FOR OColumn = 1 TO 8
Board(ORow, OColumn) = Board2(ORow, OColumn)
NEXT OColumn
NEXT ORow
FOR CurColumn = 1 TO 3
FOR CurRow = 1 TO 3
ListPlace = ListPlace + 1
FlipperM(CurRow, CurColumn) = FlipperL(ListPlace)
NEXT CurRow
NEXT CurColumn
FOR CurColumn = -1 TO 1
FOR CurRow = -1 TO 1
IF NOT (CurColumn - 1 AND CurRow - 1) THEN
UpTo = -1
WHILE UpTo < FlipperM(CurRow + 2, CurColumn + 2)
UpTo = UpTo + 1
DIM Mx AS INTEGER
DIM My AS INTEGER
Mx = ARow + CurRow * UpTo
My = AColumn + CurColumn * UpTo
IF Board(Mx, My) <> PColor THEN
Board(Mx, My) = PColor
END IF
WEND
END IF
NEXT CurRow
NEXT CurColumn
FOR ORow = 1 TO 8
FOR OColumn = 1 TO 8
IF LeagMove(ORow, OColumn, 3 - AIColor) THEN
OtherMoves = OtherMoves + 1
IF ORow = 8 OR ORow = 1 THEN OtherMoves = OtherMoves + 15
IF OColumn = 8 OR OColumn = 1 THEN OtherMoves = OtherMoves + 15
END IF
NEXT OColumn
NEXT ORow
value(ARow, AColumn) = value(ARow, AColumn) - OtherMoves
END IF
NEXT AColumn
NEXT ARow
FOR ORow = 1 TO 8
FOR OColumn = 1 TO 8
Board(ORow, OColumn) = Board2(ORow, OColumn)
NEXT OColumn
NEXT ORow
FOR ARow = 1 TO 8
FOR AColumn = 1 TO 8
IF current(3) < 0 THEN current(3) = 1
IF value(ARow, AColumn) = current(3) AND -INT(RND + .5) AND LeagMove(ARow, AColumn, AIColor) THEN
current(1) = ARow
current(2) = AColumn
current(3) = value(ARow, AColumn)
ELSEIF value(ARow, AColumn) > current(3) AND LeagMove(ARow, AColumn, AIColor) THEN
current(1) = ARow
current(2) = AColumn
current(3) = value(ARow, AColumn)
END IF
NEXT AColumn
NEXT ARow
Row = current(1)
Column = current(2)
ik$ = CkMoveDoIt$
END SUB
FUNCTION LeagMove (PRow AS INTEGER, PColumn AS INTEGER, PieceColor AS INTEGER)
KEY(15) ON
KEY(16) OFF
IF Board(PRow, PColumn) <> 0 THEN
LeagMove = 0
EXIT FUNCTION
END IF
LeagMove = -1
FOR i = 1 TO 9
FlipperL(i) = 0
NEXT i
FlipperL(1) = CheckLeag(PRow, PColumn, PieceColor, -1, -1)
FlipperL(2) = CheckLeag(PRow, PColumn, PieceColor, 0, -1)
FlipperL(3) = CheckLeag(PRow, PColumn, PieceColor, 1, -1)
FlipperL(4) = CheckLeag(PRow, PColumn, PieceColor, -1, 0)
FlipperL(5) = CheckLeag(PRow, PColumn, PieceColor, 0, 0)
FlipperL(6) = CheckLeag(PRow, PColumn, PieceColor, 1, 0)
FlipperL(7) = CheckLeag(PRow, PColumn, PieceColor, -1, 1)
FlipperL(8) = CheckLeag(PRow, PColumn, PieceColor, 0, 1)
FlipperL(9) = CheckLeag(PRow, PColumn, PieceColor, 1, 1)
FOR i = 1 TO 9
IF FlipperL(i) <> 0 THEN EXIT FUNCTION
NEXT i
LeagMove = 0
END FUNCTION
FUNCTION CheckLeag (ORow AS INTEGER, OColumn AS INTEGER, PColor AS INTEGER, RCng AS INTEGER, CCng AS INTEGER)
KEY(15) ON
KEY(16) OFF
CheckLeag = 0
CCure = OColumn + CCng
RCure = ORow + RCng
CurPiece = 1
PiecesSoFar = 0
WHILE RCure > 0 AND RCure < 9 AND CCure > 0 AND CCure < 9 AND CurPiece <> 0
CurPiece = Board(RCure, CCure)
IF CurPiece = PColor THEN
CheckLeag = PiecesSoFar
CurPiece = 0
ELSEIF CurPiece <> 0 THEN
PiecesSoFar = PiecesSoFar + 1
CCure = CCure + CCng
RCure = RCure + RCng
END IF
WEND
END FUNCTION
The temp / 0 is simply to catch the computer at trying to make an illegal move and break the execution. Thanks ahead of time for your help.
EDIT: I made a mistake, the Out of Stack error no longer appers. Also, I updated my program (fixed a few bugs I didn't realize were there, and made it no longer depend on the two header (.bi) files. Here is the updated version: Othello.bas. You no longer need to download the two header files. I also found that the AI isn't as good as I want it to be (it often makes stupid moves). I would welcome any help on the AI also. The logic for the AI is in the sub AIMove.
EDIT 2: The AI no longer tries to make illeagal moves. I updated the code in this post.
Help with an Othello (AKA Reversi) Game - Z!re - 08-03-2005
*throws kiss*
That is some really nice formatted code there..
Really nice...
Sorry I dont have time to look at it.. but I'm sure you'll get help, especially if the rest is as nice as this one..
Good luck
Help with an Othello (AKA Reversi) Game - MystikShadows - 08-03-2005
*whipes his eyes while they remain open...did I just see Z!re throw a kiss?* This has nothing to do with with "Kiss of the spider woman" does it? ;-). lol
I have to agree with Z!re here, well formatted code please to look at.
I suspect it enters the Out Of Stack space when your program enters this particular while loop:
Code: IF NOT (CurColumn - 1 AND CurRow - 1) THEN
UpTo = -1
WHILE UpTo < FlipperM(CurRow + 2, CurColumn + 2)
UpTo = UpTo + 1
DIM Mx AS INTEGER
DIM My AS INTEGER
Mx = ARow + CurRow * UpTo
My = AColumn + CurColumn * UpTo
IF Board(Mx, My) <> PColor THEN
Board(Mx, My) = PColor
END IF
WEND
Reason is simple, I think this is nested in too deep in stacks (all the DO, while, FOR, IF before you get to the if are stack levels, you're probably simple reaching the stack limit when you get to that WHILE up there.
Perhaps you should see if you couldn't maybe break this down into less stack levels (maybe some of those ifs could be grouped and done before then based on certain variables set in those ifs you could then decide which loop you need to execute, this way, all those ifs and dos and whiles wouldn't be within each other and hence less stack space would be used. :-).
COnsequently, doing this will probably solve your Out Of Memory error as well when editing the code.
If you're using VB-DOS or QB P.D.S. 7.1 you can lookup the STACK statement which allows you to reserve more stack space.
As for the AI part, I'm not a big pro at Othello, can you give some examples of illegal moves it sometimes makes? Then I could cross reference the logic for ya and pinpoint it :-).
Help with an Othello (AKA Reversi) Game - JasonSG - 08-03-2005
The problem with the AI isn't in the checking if the move is legal, otherwise it would actually make the move, not try to (if the Legal Move funtion gave back the wrong value, then it wouldn't continue looping). As for the illegal moves it tries, I think they are always in a place that already has a piece in it. The problem occurs in the AI deciding where to go and in what it treats as a leagal move, not with the leagal move checker. The Out of Memory error appears even if I stop the execution before I begin playing, while it shows the menus (which use the mouse):
The following is the code for the main module and the first sub it calls (other than the ones to show the mouse):
Code: OPTION BASE 1
DEFINT A-Z
'$INCLUDE: 'general.bi'
'$INCLUDE: 'mouse.bi'
DECLARE SUB PxlCng (x%, y%)
DECLARE SUB AIMove ()
DECLARE SUB AskAI ()
DECLARE SUB CBoard ()
DECLARE SUB CTurn (CurTurn AS INTEGER)
DECLARE SUB Flip (CRow AS INTEGER, CColumn AS INTEGER, PColor AS INTEGER)
DECLARE SUB Piece (PRow AS INTEGER, PColumn AS INTEGER, BYVAL PieceColor AS INTEGER)
DECLARE SUB Save ()
DECLARE SUB WaitForMouseClick ()
DECLARE FUNCTION CheckLeag! (ORow AS INTEGER, OColumn AS INTEGER, PColor AS INTEGER, RCng AS INTEGER, CCng AS INTEGER)
DECLARE FUNCTION CkMoveDoIt$ ()
DECLARE FUNCTION ExitAsk$ ()
DECLARE FUNCTION LeagFileName! (Name$)
DECLARE FUNCTION LeagMove! (PRow AS INTEGER, PColumn AS INTEGER, PieceColor AS INTEGER)
DECLARE FUNCTION Max! (number1!, number2!)
DECLARE FUNCTION CSTR$ (numeric.expression%)
DECLARE FUNCTION TRIM$ (str AS STRING)
DIM SHARED Board(8, 8) AS INTEGER
DIM SHARED FlipperL(9) AS INTEGER
DIM SHARED FlipperM(3, 3) AS INTEGER
DIM SHARED Pieces(2) AS INTEGER
DIM SHARED row AS INTEGER
DIM SHARED Column AS INTEGER
DIM SHARED Turn AS INTEGER
DIM SHARED Finished AS INTEGER
DIM SHARED AIPlaying AS INTEGER
DIM SHARED AIColor AS INTEGER
DIM SHARED MRow AS INTEGER
DIM SHARED MCol AS INTEGER
DIM SHARED MlButton AS INTEGER
DIM SHARED MrButton AS INTEGER
DIM SHARED PGRow AS INTEGER
DIM SHARED PGColumn AS INTEGER
CLEAR , , STACK
STACK STACK
begin:
RESET
MouseInit
MouseShow
CALL AskAI
CALL CBoard
waitkey:
CALL WaitForMouseClick
SELECT CASE CkMoveDoIt$
CASE "y"
GOTO begin
CASE "n"
GOTO leave
END SELECT
GOTO waitkey
leave:
SCREEN 0
MouseHide
CLS
MouseShow
PRINT "Thank you for using this program."
PRINT
PRINT
PRINT "This program was made by Jason Gross."
END
exiter:
SELECT CASE ExitAsk$
CASE "e"
RETURN leave
CASE "b"
RETURN begin
CASE "s"
CALL Save
CALL CBoard
CASE "r"
CALL CBoard
KEY(15) ON
KEY(16) OFF
RETURN
END SELECT
GOTO exiter
leaver: GOTO leave
SUB AskAI
KEY 15, CHR$(0) + CHR$(1)
KEY 16, CHR$(0) + CHR$(1)
ON KEY(15) GOSUB exiter
ON KEY(16) GOSUB leaver
KEY(15) OFF
KEY(16) ON
MouseHide
CLS
MouseShow
SCREEN 0
PRINT TAB(30); "*******************"
PRINT TAB(30); "* *"
PRINT TAB(30); "* OTHELLO *"
PRINT TAB(30); "* *"
PRINT TAB(30); "* BY *"
PRINT TAB(30); "* Jason Gross *"
PRINT TAB(30); "* *"
PRINT TAB(30); "*******************"
PRINT
PRINT
PRINT
PRINT TAB(26); "************ *************"
PRINT TAB(26); "* New Game * * Load Game *"
PRINT TAB(26); "************ *************"
PRINT
CALL MousePoll(MRow, MCol, MlButton, MrButton)
DIM lastR AS INTEGER
DIM lastC AS INTEGER
ik$ = ""
WHILE ik$ = ""
CALL MousePoll(MRow, MCol, MlButton, MrButton)
ik$ = ""
IF MRow > 11 AND MRow < 15 AND MlButton THEN
IF MCol > 27 AND MCol < 39 THEN ik$ = "n"
IF MCol > 41 AND MCol < 53 THEN ik$ = "l"
END IF
WEND
IF ik$ = "n" THEN
PRINT "Would you like to play against:"
PRINT
PRINT TAB(26); "*********** ****************"
PRINT TAB(26); "* A Human * * The Computer *"
PRINT TAB(26); "*********** ****************"
ik$ = ""
WHILE ik$ = ""
CALL MousePoll(MRow, MCol, MlButton, MrButton)
ik$ = ""
IF MRow > 17 AND MRow < 21 AND MlButton THEN
IF MCol > 27 AND MCol < 38 THEN ik$ = "h"
IF MCol > 40 AND MCol < 55 THEN ik$ = "c"
END IF
WEND
IF ik$ = "c" THEN
AIPlaying = -1
MouseHide
CLS
MouseShow
PRINT "Which would you like to be (black goes first)?"
PRINT
PRINT TAB(30); "********* *********"
PRINT TAB(30); "* Black * * White *"
PRINT TAB(30); "********* *********"
PRINT
CALL MousePoll(MRow, MCol, MlButton, MrButton)
ik$ = ""
WHILE ik$ = ""
CALL MousePoll(MRow, MCol, MlButton, MrButton)
ik$ = ""
IF MRow > 2 AND MRow < 6 AND MlButton THEN
IF MCol > 31 AND MCol < 40 THEN ik$ = "b"
IF MCol > 42 AND MCol < 50 THEN ik$ = "w"
END IF
WEND
IF ik$ = "b" THEN AIColor = 1 ELSE AIColor = 2
END IF
CLS
Board(4, 4) = 1
Board(5, 5) = 1
Board(4, 5) = 2
Board(5, 4) = 2
Turn = 1
Pieces(1) = 2
Pieces(2) = 2
ELSE
MouseHide
CLS
MouseShow
Turn = -3
Pieces(1) = 0
Pieces(2) = 0
askagain:
INPUT "What is the name of the game that you would like to load"; path$
WHILE NOT (LeagFileName(path$))
MouseHide
CLS
MouseShow
PRINT "Invalid file path or name."
PRINT
INPUT "What is the name of the game that you would like to load"; path$
WEND
OPEN path$ FOR APPEND AS #1
IF LOF(1) = 0 THEN
MouseHide
CLS
MouseShow
PRINT "The file does not exist. Choose a valid file."
PRINT
CLOSE #1
GOTO askagain
END IF
CLOSE #1
OPEN path$ FOR INPUT AS #1
counter = 0
FOR MRow = 1 TO 8
FOR MColumn = 1 TO 8
counter = counter + 1
LINE INPUT #1, current$
Board(MRow, MColumn) = VAL(current$)
IF VAL(current$) = 1 THEN
Pieces(1) = Pieces(1) + 1
ELSEIF VAL(current$) = 2 THEN
Pieces(2) = Pieces(2) + 1
END IF
NEXT MColumn
NEXT MRow
LINE INPUT #1, current$
Turn = VAL(current$)
LINE INPUT #1, current$
AIPlaying = VAL(current$)
LINE INPUT #1, current$
AIColor = VAL(current$)
CLOSE #1
END IF
END SUB
If I stop the execution (Ctrl+Scroll) after just clicking New Game, it gives me the out of memory error when I try to edit something. As for the stack, I'm not 100% sure exactly where in the AIMove sub it occurs, and it dosen't occur every time; I just disabled the loop in the AIMove sub (whenever it didn't go, I went for it), and the Out of Stack error didn't occur once. It might have only occured before I added mouse capabilities into the game (when it used on key gosub for the arrow keys; I didn't know what the "real arrow keys" gave to inkey$, so I used on key to trap the num pad arrow keys). If it occurs again (I'll play a few more games), I'll post where it happens in the sub. Thanks again.
Help with an Othello (AKA Reversi) Game - JasonSG - 08-03-2005
I played a few more game with the AI (with the loop disabled), and it only tried to make illegal moves in some games, and apparantly, the Out of Stack error was fixed when I put in the mouse and removed the on key gosub commands . Sorry about that :oops: . However, the out of memory error still occurs, even if I just hit F5 and then, before I even move the mouse, hit Ctrl+Scroll. This makes me think it has something to do with the way I have it polling the mouse, or something like that. This is the code that it is running when I stop it:
Code: SUB AskAI
...
PRINT TAB(26); "************ *************"
PRINT TAB(26); "* New Game * * Load Game *"
PRINT TAB(26); "************ *************"
PRINT
ik$ = ""
WHILE ik$ = ""
CALL MousePoll(MRow, MCol, MlButton, MrButton)
IF MRow > 11 AND MRow < 15 AND MlButton THEN
IF MCol > 27 AND MCol < 39 THEN ik$ = "n"
IF MCol > 41 AND MCol < 53 THEN ik$ = "l"
END IF
WEND
...
END SUB
SUB MousePoll (MouseRow, MouseCol, lButton, rButton) STATIC
' =======================================================================
' Polls mouse driver, then sets parms correctly
' =======================================================================
MouseDriver 3, button, MouseCol, MouseRow
MouseRow = MouseRow / 8 + 1
MouseCol = MouseCol / 8 + 1
IF button AND 1 THEN
lButton = TRUE
ELSE
lButton = FALSE
END IF
IF button AND 2 THEN
rButton = TRUE
ELSE
rButton = FALSE
END IF
END SUB
SUB MouseDriver (m0, m1, m2, m3) STATIC
DIM regs AS RegType
IF MouseChecked = FALSE THEN
DEF SEG = 0
MouseSegment& = 256& * PEEK(207) + PEEK(206)
MouseOffset& = 256& * PEEK(205) + PEEK(204)
DEF SEG = MouseSegment&
IF (MouseSegment& = 0 AND MouseOffset& = 0) OR PEEK(MouseOffset&) = 207 THEN
MousePresent = FALSE
MouseChecked = TRUE
DEF SEG
END IF
END IF
IF MousePresent = FALSE AND MouseChecked = TRUE THEN
EXIT SUB
END IF
' =======================================================================
' Calls interrupt 51 to invoke mouse functions in the MS Mouse Driver.
' =======================================================================
regs.ax = m0
regs.bx = m1
regs.cx = m2
regs.dx = m3
Interrupt 51, regs, regs
m0 = regs.ax
m1 = regs.bx
m2 = regs.cx
m3 = regs.dx
IF MouseChecked THEN EXIT SUB
' =======================================================================
' Check for successful mouse initialization
' =======================================================================
IF m0 AND NOT MouseChecked THEN
MousePresent = TRUE
DEF SEG
END IF
MouseChecked = TRUE
END SUB
BTW, I did not write the code for the mouse; it was from mouse.bas, which came as a tutorial/example with qb when I downloaded it. I only wrote the first of the three subs shown in this post.
|