Thread Rating:
  • 0 Vote(s) - 0 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Help with an Othello (AKA Reversi) Game
#1
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 Sad ). 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.
Reply
#2
*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
Reply
#3
*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 Smile 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 :-).
hen they say it can't be done, THAT's when they call me ;-).

[Image: kaffee.gif]
[Image: mystikshadows.png]

need hosting: http://www.jc-hosting.net
All about ASCII: http://www.ascii-world.com
Reply
#4
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.
Reply
#5
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 Smile . 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.
Reply


Forum Jump:


Users browsing this thread: 1 Guest(s)