2d packman

Blitz3D Forums/Blitz3D Beginners Area/2d packman

I want to make a 2d packman game where packman is one image and the walls are another image.

How would I get packman to not go through the walls?

there are many possibilities... you could do a simple readpixelfast around your packmans current position from the map image. or you could setup collision boxes and use blitz collision detection. or you could setup a memorybank with the walls data and peek from that. or or or ;)

The best way to do this (imo) is not to use collisions at all, but have a 2D array holding the wall data. All your sprites (PacMan, ghosts etc.) should be made the same size (e.g. 32x32) so then you set up an array with each element saying whether that 32x32 area of the screen is a wall or a space.

Give your PacMan and Ghosts a position in this array so that when they need to make a change in direction, you can simply reference the array to see if there's a wall in the way or not. Remember: PacMan and the ghosts can only change direction when they're exactly aligned to a position in the grid array.

Thanks for the advice. After reading big10p's post, I tried out using an array, it worked great, but this project is not so much how to get a good final product as to learn about the blitz 2d commands.

So, does anybody know how to do this with the blitz ImagesCollide command?

Consider using ImagesCollide with Pacman and the Ghosts to test for player death - the 2d array solution is really the best for wall collision for this type of game.

Have the 'gaps' (e.g. the bits pacman walks on) the maskcolour for the maze and use imagescollide?

Yes, but is there a realy simple way to find out which side the collision was on so that I can prohibit movement in that direction?

Why do you need to know which side the collision was on? All you need to know is that the pacman collided with a non-transparent pixel of the maze.
Check the movement key from the player, check whether Pacman's new position would collide with the wall image. If it does don't allow the move.

Heres the entire code I once wrote for a PacMan game in QuickBASIC. It should give you some ideas on how to set up and execute such a game.

Note that it uses no external images, nor does it make use of the vertical refresh, so it's written to take advantage of 'dirty rectangles' to minimize screen flicker.

You can actually play it from my Projects Gallery on my website.
'
' PacMan BASIC Program
'
' Written by WolRon
' CopyRight 2001
'
'
' To run this program press Shift+F5
' To move pacman, use keys j, k, l, and i or the arrow keys
' To end game, press the escape key
'
'
' THINGS TO DO:
' Make PacMan face direction he is heading
' Make PacMan disappear correctly (when he is caught)
' Create fruit that pops up under cage.  Appears after 40 dots are eaten and
'   hangs around for about 20 seconds.
' Sound effects
' Make ghosttimer get shorter and shorter as level increases
' Intermissions
'
'
' Variables used in this program:
'
' Array A(18,19) keeps track of board and dots
' grid 19 blocks wide (0-18) by 20 blocks high (0-19)
'
' Array S(4,8) keeps track of sprite positions/attributes
' 1st dimension: 0=pacman, 1,2,3,4=ghosts
' 2nd dim: 0 and 1 = X and Y of position for each sprite
' 2nd dim: 2 and 3 = X and Y of old position for each sprite
' 2nd dim: 4 and 5 = left/right and up/down of pacmans current direction
' 2nd dim: 4 current ghost direction (0,1,2,3)
' 2nd dim: 5 = ghost active(1) or inactive (0)
' 2nd dim: 6 = ghost normal color           :pacman next direction L/R
' 2nd dim: 7 = ghost dead (0) or alive (1)  :pacman next direction U/D
' 2nd dim: 8 = ghost scared (1) or not scared (0)
' example: S(2,0) refers to 2nd ghosts X position
' example: S(3,7) refers to whether 3rd ghost is dead/alive
' example: S(0,6) refers to next left/right direction for pacman
'
' Array DIR(3,1) keeps track of 4 possible directions
' 1st dim: number of direction 0,1,2, or 3
' 2nd dim: 0 = X value; 1 = Y value
'
' Array REVDIR(3) keeps track of the reverse direction of Dir(3,1)
'
' Array POSSDIR(3,1)
'
' X and Y temporarily store integer position values (for use in arrays)
' X1 and Y1 temporarily store positions (for use in arguments)
' X2 and Y2 are used to calculate X1 and Y1
' H, I, and J are used for counting in loops (miscellaneous)
' I& is same as I, only is a long integer (can count up to 2,147,483,647)
' K$ is used for keyboard inputs
' INTER is either 1 or 0; 1 means Pacman is at intersection
' WARP is either 1 or 0; 1 means Pacman is at either end of warp tunnel
' GAMETIME is used to determine when certain things should happen
' DOTBLINK is used to count to blink large dots on/off
' GHOSTTIMER is used to time when the ghosts turn blue and can be eaten
' LIVES keeps track of pacmen remaining
' COLL is either 1 or 0; 1 means Pacman has collided with a ghost
' GHSTCOLL keeps track of which ghost Pacman collided with; 1 to 4
' DOTS keeps track of the number of dots eaten on this level
' BOARDCOL sets color of maze when drawn
' SPEED& holds number of counts this computer made in 1 sec; used for timing
' TIMECOUNT is used to calculate SPEED&
' SPEED is set by player to determine game speed
' EATEN keeps track of how many ghosts have been eaten (during GHOSTTIMER)
' GHDIR is used in arguments regarding next ghost direction
' GHOSTMOVE keeps track of when to advance ghosts
' GHOSTSPEED sets how fast ghosts move
' LEVEL keeps track of current level
' GAMEOVER is either 1 or 0; 1 means game is over
'
'
' Other notes:
'
' Old positions of sprites are used for erasure of sprite on next refresh
' Next Pacman direction is stored and used when he reaches next intersection
'
' To check if an arrow key has been pressed is a two step process.  Most keys
' on the keyboard return one character representing the key pressed.  But
' certain keys return two characters.  The first character is 0 and tells us
' that a special key has been pressed.  The second character is the code for
' the key pressed.  This is why there are two seperate sections of code to
' determine what key has been pressed.  One for normal keys, one for special.
'
'
' Program Starts Here:

REM DEFINE ALL VARIABLES AS INTEGERS TO INCREASE PROCESSING SPEED
DEFINT A-Z

REM INITIALIZE ARRAY FOR MAZE
DIM A(18, 19)
REM READ IN DATA FOR BOARD
FOR Y = 0 TO 19: FOR X = 0 TO 18
READ A(X, Y)
NEXT X: NEXT Y

REM INITIALIZE PACMAN/GHOST ARRAY AND POSITIONS
DIM S(4, 8)
GOSUB SETSPRITEPOSITIONS
REM SET GHOST COLORS
S(1, 6) = 4: REM RED, BLINKY
S(2, 6) = 11: REM BLUE, INKY
S(3, 6) = 12: REM PINK, PINKY
S(4, 6) = 6: REM BROWN, CLYDE
REM SET GHOST ALIVE AND NOT SCARED
FOR I = 1 TO 4: S(I, 7) = 1: S(I, 8) = 0: NEXT I
REM SET BLINKY ACTIVE
S(1, 5) = 1
GHOSTMOVE = 0
GHOSTSPEED = 30

REM INITIALIZE OTHER ARRAYS
DIM DIR(3, 1)
DIR(0, 0) = -1: DIR(0, 1) = 0: REM LEFT
DIR(1, 0) = 1: DIR(1, 1) = 0: REM RIGHT
DIR(2, 0) = 0: DIR(2, 1) = -1: REM UP
DIR(3, 0) = 0: DIR(3, 1) = 1: REM DOWN
DIM REVDIR(3)
REVDIR(0) = 1: REM RIGHT
REVDIR(1) = 0: REM LEFT
REVDIR(2) = 3: REM DOWN
REVDIR(3) = 2: REM UP
DIM POSSDIR(3, 1)

REM INTIALIZE VARIABLES
LIVES = 3
EATEN = 0
LEVEL = 1
SPEED = 50
S(0, 4) = -1: S(0, 5) = 0
S(0, 2) = 108: S(0, 3) = 140
S(0, 6) = -1: S(0, 7) = 0
S(1, 4) = 0: REM LEFT
S(2, 4) = 1: REM UP
S(3, 4) = 2: REM RIGHT
S(4, 4) = 0: REM LEFT
DOTTED = 21845: REM USED TO DRAW DOTTED LINES (BOTTOM OF GHOST)
DOTTED2 = 15872: REM USED TO DRAW TOP OF GHOSTS MOUTH
DOTTED3 = 16640: REM USED TO DRAW BOTTOM OF GHOSTS MOUTH


REM INITIALIZE SCREEN FOR GRAPHICS
REM 320X200 VGA
SCREEN 7
COLOR 1, 0

REM CALCULATE SPEED OF SYSTEM (1 SEC)
ON TIMER(1) GOSUB CALCSPEED
TIMER ON
SPEED& = 0: TIMECOUNT = 0
WHILE TIMECOUNT = 0
SPEED& = SPEED& + 1
WEND
GOTO ENDCALC

CALCSPEED:
  TIMER OFF
  TIMECOUNT = 1
RETURN

ENDCALC:

GOSUB NEWGAME
CLS

REM DRAW SCREEN FIRST TIME (ONLY NEED TO DRAW WALLS ONCE)
BOARDCOL = 1
GOSUB DRAWBOARD

REM DRAW SCORE/LIVES/LEVEL
GOSUB DRAWLIVES
GOSUB DRAWTEXT



'*************************************************************************
REM BEGINNING OF GAME LOOP
GAMELOOP:

GOSUB DRAWSCORE

REM ERASE OLD POSITION OF PACMAN
GOSUB DRAWBLANKPACMAN
REM DRAW PACMAN (TO KEEP BLANK TIME SHORT AS POSSIBLE)
X1 = S(0, 0): Y1 = S(0, 1)
GOSUB DRAWPACMAN

ERASEGHOSTS:
REM REDRAW TOP OF CAGE
X1 = 108: Y1 = 80
GOSUB DRAWWALLOTH
REM ERASE OLD POSITIONS OF GHOSTS AND
REM DRAW GHOSTS IMMEDIATELY TO KEEP BLANK TIME SHORT AS POSSIBLE
FOR I = 1 TO 4
X1 = S(I, 2): Y1 = S(I, 3)
GOSUB DRAWBLANKGHOST
X1 = S(I, 0): Y1 = S(I, 1)
IF S(I, 7) = 0 THEN GOSUB DRAWDEADGHOST: GOTO DRWNXTGHST
IF S(I, 8) = 0 THEN
  GHSTCOL = S(I, 6)
  GOSUB DRAWGHOST
ELSE
  GHSTCOL = GHOSTCOLOR
  GOSUB DRAWSCAREDGHOST
END IF
DRWNXTGHST:
NEXT I
      
REM DRAW DOTS
REM ONLY NEED TO REDRAW DOTS WHERE SPRITES ARE
FOR I = 0 TO 4
IF S(I, 0) MOD 12 = 0 THEN GOTO DRAWVER
X2 = S(I, 0) MOD 12: X2 = S(I, 0) - X2
Y2 = S(I, 1) MOD 10: Y1 = S(I, 1) - Y2
FOR X1 = (X2 - 12) TO (X2 + 12) STEP 12
IF X1 < 0 THEN GOTO NEXTX1: REM LESS THAN ZERO WOULD CAUSE SUBSCRIPT ERROR
IF X1 > 216 THEN GOTO NEXTX1: REM GREATER THAN 18 WOULD CAUSE SUBSCRIPT ERROR
IF A(X1 / 12, Y1 / 10) = 2 THEN GOSUB DRAWDOT
NEXTX1:
NEXT X1
GOTO DRAWDOTENDLOOP
DRAWVER:
X1 = S(I, 0)
Y1 = S(I, 1) MOD 10: Y2 = S(I, 1) - Y1
FOR Y1 = (Y2 - 10) TO (Y2 + 10) STEP 10
IF A(X1 / 12, Y1 / 10) = 2 THEN GOSUB DRAWDOT
NEXT Y1
DRAWDOTENDLOOP:
NEXT I

REM DRAW BIG DOTS
DOTBLINK = DOTBLINK + 1
IF DOTBLINK > 30 THEN DOTBLINK = 0
IF DOTBLINK > 20 THEN GOSUB DRAWBLANKBIGDOT: GOTO DRWPCMN
FOR X1 = 12 TO 204 STEP 192: FOR Y1 = 20 TO 140 STEP 120
IF A(X1 / 12, Y1 / 10) = 1 THEN GOSUB DRAWBIGDOT
NEXT Y1: NEXT X1

DRWPCMN:
REM DRAW PACMAN
X1 = S(0, 0): Y1 = S(0, 1)
GOSUB DRAWPACMAN

REM DRAW GHOSTS
IF GHOSTTIMER > 0 THEN
  GHOSTTIMER = GHOSTTIMER - 1
ELSE
  EATEN = 0
  FOR I = 1 TO 4
  IF S(I, 7) = 1 THEN S(I, 8) = 0: REM IF DEAD THEN REMAIN SCARED
  NEXT I
END IF
GHOSTCOLOR = 0
IF GHOSTTIMER > 0 THEN GHOSTCOLOR = 7 ELSE GOTO DRWGHST
IF GHOSTTIMER > 0 AND GHOSTTIMER < 5 THEN GHOSTCOLOR = 9: GOTO DRWGHST
IF GHOSTTIMER > 10 AND GHOSTTIMER < 17 THEN GHOSTCOLOR = 9: GOTO DRWGHST
IF GHOSTTIMER > 24 AND GHOSTTIMER < 34 THEN GHOSTCOLOR = 9: GOTO DRWGHST
IF GHOSTTIMER > 44 AND GHOSTTIMER < 59 THEN GHOSTCOLOR = 9: GOTO DRWGHST
IF GHOSTTIMER > 72 AND GHOSTTIMER < 93 THEN GHOSTCOLOR = 9: GOTO DRWGHST
IF GHOSTTIMER > 115 THEN GHOSTCOLOR = 9
DRWGHST:
FOR I = 1 TO 4
X1 = S(I, 0): Y1 = S(I, 1)
IF S(I, 7) = 0 THEN GOSUB DRAWDEADGHOST: GOTO NXTGHST
IF S(I, 8) = 0 THEN
  GHSTCOL = S(I, 6)
  GOSUB DRAWGHOST
ELSE GHSTCOL = GHOSTCOLOR
  GOSUB DRAWSCAREDGHOST
END IF
NXTGHST:
NEXT I

REM GET INPUTS
K$ = INKEY$: IF K$ = "" THEN GOTO GMETME
IF LEN(K$) > 1 THEN GOTO ARROWKEYS
IF K$ = CHR$(27) THEN GAMEOVER = 1: REM ESCAPE KEY
IF K$ = "j" OR K$ = "J" THEN S(0, 6) = -1: S(0, 7) = 0
IF K$ = "l" OR K$ = "L" THEN S(0, 6) = 1: S(0, 7) = 0
IF K$ = "i" OR K$ = "I" THEN S(0, 6) = 0: S(0, 7) = -1
IF K$ = "k" OR K$ = "K" THEN S(0, 6) = 0: S(0, 7) = 1
GOTO GMETME
ARROWKEYS:
IF ASC(RIGHT$(K$, 1)) = 75 THEN S(0, 6) = -1: S(0, 7) = 0: REM LEFT ARROW
IF ASC(RIGHT$(K$, 1)) = 77 THEN S(0, 6) = 1: S(0, 7) = 0: REM RIGHT ARROW
IF ASC(RIGHT$(K$, 1)) = 72 THEN S(0, 6) = 0: S(0, 7) = -1: REM UP ARROW
IF ASC(RIGHT$(K$, 1)) = 80 THEN S(0, 6) = 0: S(0, 7) = 1: REM DOWN ARROW
                        
GMETME:
GAMETIME = GAMETIME + 1
IF GAMETIME < 300 THEN FOR I& = 1 TO (SPEED& / 100): NEXT I&: GOTO ENDLOOP
IF GAMETIME = 32000 THEN GAMETIME = 10000
                                                       

REM MOVE PACMAN
REM SAVE OLD POSITION FIRST (SO WE CAN ERASE PACMAN LATER)
S(0, 2) = S(0, 0): S(0, 3) = S(0, 1)
REM FIND OUT IF PACMAN IS AT INTERSECTION
INTER = 0: WARP = 0
IF (S(0, 0) MOD 12 = 0) AND (S(0, 1) MOD 10 = 0) THEN
  INTER = 1: X = S(0, 0) / 12: Y = S(0, 1) / 10
  IF (X = 18 OR X = 0) AND Y = 9 THEN WARP = 1
END IF
REM IF CAN, CHANGE DIRECTION
IF WARP = 1 THEN GOTO ADV
IF INTER = 1 THEN IF A(X + S(0, 6), Y + S(0, 7)) < 3 THEN S(0, 4) = S(0, 6): S(0, 5) = S(0, 7): GOTO ADV
IF S(0, 4) <> 0 AND S(0, 6) <> 0 THEN S(0, 4) = S(0, 6): S(0, 5) = 0
IF S(0, 5) <> 0 AND S(0, 7) <> 0 THEN S(0, 5) = S(0, 7): S(0, 4) = 0
ADV:
REM IF CAN, ADVANCE PACMAN
IF WARP = 1 THEN
  IF X = 0 THEN S(0, 0) = 216 ELSE IF X = 18 THEN S(0, 0) = 0
  GOTO ADVWARP
END IF
IF INTER = 1 THEN IF A(X + S(0, 4), Y + S(0, 5)) > 2 THEN GOTO DONEADV
ADVWARP:
S(0, 0) = S(0, 0) + S(0, 4): S(0, 1) = S(0, 1) + S(0, 5)
DONEADV:


            
REM MOVE GHOSTS
IF GAMETIME > 1000 THEN S(4, 5) = 1: GOTO MVGHSTS: REM CLYDE
IF GAMETIME > 700 THEN S(2, 5) = 1: GOTO MVGHSTS: REM INKY
IF GAMETIME > 335 THEN S(3, 5) = 1: REM PINKY
MVGHSTS:
GHOSTMOVE = GHOSTMOVE + GHOSTSPEED
IF GHOSTMOVE < 100 THEN GOTO CHECKCOLL
GHOSTMOVE = GHOSTMOVE - 100
FOR I = 1 TO 4
  IF S(I, 5) = 1 THEN
    REM GHOST IS ACTIVE
    REM SAVE OLD POSITION FIRST (SO WE CAN ERASE GHOST LATER)
    S(I, 2) = S(I, 0): S(I, 3) = S(I, 1)
    REM IF GHOST IS AT CENTER OF CAGE, SET DIRECTION UP
    IF S(I, 0) = 108 AND S(I, 1) = 85 THEN
      S(I, 4) = 2: S(I, 7) = 1: GOTO GHMOVEND: REM SET GHOST ALIVE TOO
    END IF
    REM IF GHOST IS ABOVE CAGE, AND IS DEAD, SET DIRECTION DOWN
    IF S(I, 0) = 108 AND S(I, 1) = 70 AND S(I, 7) = 0 THEN
      S(I, 4) = 3: GOTO GHMOVEND
    END IF
    REM IF GHOST IS ABOVE/RIGHT OF CAGE, AND IS DEAD, SET DIRECTION LEFT
    IF S(I, 0) = 120 AND S(I, 1) = 70 AND S(I, 7) = 0 THEN
      S(I, 4) = 0: GOTO GHMOVEND
    END IF
    REM IF GHOST IS RIGHT OF CAGE, AND IS DEAD, SET DIRECTION UP
    IF S(I, 0) = 144 AND (S(I, 1) = 90 OR S(I, 1) = 100) AND S(I, 7) = 0 THEN
      S(I, 4) = 2: GOTO GHMOVEND
    END IF
    REM FIND OUT IF GHOST IS AT INTERSECTION
    INTER = 0: WARP = 0
    IF (S(I, 0) MOD 12 = 0) AND (S(I, 1) MOD 10 = 0) THEN
      INTER = 1: X = S(I, 0) / 12: Y = S(I, 1) / 10
      IF (X = 18 OR X = 0) AND Y = 9 THEN WARP = 1
    END IF
    IF WARP = 1 THEN
      IF X = 0 THEN S(I, 0) = 216 ELSE S(I, 0) = 0
      GOTO GHMOVEND
    END IF
    IF INTER = 1 THEN
      IF S(I, 7) = 1 THEN
        REM GHOST IS ALIVE
        IF S(I, 8) = 1 THEN
          REM GHOST IS SCARED
          REM RUN FROM PACMAN
          DO
            DO
              GHDIR = INT(RND * 4)
            LOOP WHILE A(X + DIR(GHDIR, 0), Y + DIR(GHDIR, 1)) > 2
          LOOP WHILE GHDIR = REVDIR(S(I, 4))
          S(I, 4) = GHDIR
        ELSE
          REM GHOSTS AREN'T SCARED
          REM SEARCH FOR PACMAN
          IF GAMETIME < 1200 THEN
            REM RANDOM MODE
            DO
            DO
            GHDIR = INT(RND * 4)
            LOOP WHILE A(X + DIR(GHDIR, 0), Y + DIR(GHDIR, 1)) > 2
            LOOP WHILE GHDIR = REVDIR(S(I, 4))
            S(I, 4) = GHDIR
          ELSE
            REM CHASE MODE
            REM SEARCH FOR PACMAN
            FOR H = 0 TO 3
              POSSDIR(H, 0) = 0: POSSDIR(H, 1) = 0
            NEXT H
            REM CHECK UP DIRECTION
            IF A(X, Y - 1) < 3 THEN
              POSSDIR(2, 0) = 1: POSSDIR(2, 1) = Y - (S(0, 1) / 10)
            END IF
            REM CHECK LEFT DIRECTION
            IF A(X - 1, Y) < 3 THEN
              POSSDIR(0, 0) = 1: POSSDIR(0, 1) = X - (S(0, 0) / 12)
            END IF
            REM CHECK RIGHT DIRECTION
            IF A(X + 1, Y) < 3 THEN
              POSSDIR(1, 0) = 1: POSSDIR(1, 1) = (S(0, 0) / 12) - X
            END IF
            REM CHECK DOWN DIRECTION
            IF A(X, Y + 1) < 3 THEN
              POSSDIR(3, 0) = 1: POSSDIR(3, 1) = (S(0, 1) / 10) - Y
            END IF
            REM SORT OUT BEST MOVE
            J = 0: GHDIR = -1
            POSSDIR(REVDIR(S(I, 4)), 0) = 0
            FOR H = 0 TO 3
              IF POSSDIR(H, 0) = 1 AND SGN(POSSDIR(H, 1)) = 1 THEN
                IF POSSDIR(H, 1) > J THEN J = POSSDIR(H, 1): GHDIR = H
              END IF
            NEXT H
            IF GHDIR = -1 THEN
              FOR H = 0 TO 3
                IF POSSDIR(H, 0) = 1 THEN GHDIR = H
              NEXT H
            END IF
            S(I, 4) = GHDIR
          END IF
        END IF
      ELSE
        REM GHOST IS DEAD
        REM SEARCH FOR CAGE
        FOR H = 0 TO 3
          POSSDIR(H, 0) = 0: POSSDIR(H, 1) = 0
        NEXT H
        REM CHECK UP DIRECTION
        IF A(X, Y - 1) < 3 THEN
          POSSDIR(2, 0) = 1: POSSDIR(2, 1) = Y - 9
        END IF
        REM CHECK LEFT DIRECTION
        IF A(X - 1, Y) < 3 THEN
          POSSDIR(0, 0) = 1: POSSDIR(0, 1) = X - 11
        END IF
        REM CHECK RIGHT DIRECTION
        IF A(X + 1, Y) < 3 THEN
          POSSDIR(1, 0) = 1: POSSDIR(1, 1) = 11 - X
        END IF
        REM CHECK DOWN DIRECTION
        IF A(X, Y + 1) < 3 THEN
          POSSDIR(3, 0) = 1: POSSDIR(3, 1) = 9 - Y
        END IF
        REM SORT OUT BEST MOVE
        J = 0: GHDIR = -1
        POSSDIR(REVDIR(S(I, 4)), 0) = 0
        FOR H = 0 TO 3
          IF POSSDIR(H, 0) = 1 AND SGN(POSSDIR(H, 1)) = 1 THEN
            IF POSSDIR(H, 1) > J THEN J = POSSDIR(H, 1): GHDIR = H
          END IF
        NEXT H
        IF GHDIR = -1 THEN
          FOR H = 0 TO 3
            IF POSSDIR(H, 0) = 1 THEN GHDIR = H
          NEXT H
        END IF
        IF GHDIR = -1 THEN
          S(I, 7) = 1: S(I, 4) = 2: S(I, 8) = 0
        ELSE
          S(I, 4) = GHDIR
        END IF
      END IF
    END IF
    REM ADVANCE GHOST
GHMOVEND:
    S(I, 0) = S(I, 0) + DIR(S(I, 4), 0): S(I, 1) = S(I, 1) + DIR(S(I, 4), 1)
  END IF
NEXT I


CHECKCOLL:
REM CHECK FOR COLLISIONS
REM CHECK IF PACMAN HAS EATEN A DOT
REM FIRST FIND OUT IF PACMAN IS AT INTERSECTION
REM (NEED TO CHECK AGAIN SINCE PACMAN HAS ADVANCED)
INTER = 0
IF (S(0, 0) MOD 12 = 0) AND (S(0, 1) MOD 10 = 0) THEN
  INTER = 1: X1 = S(0, 0) / 12: Y1 = S(0, 1) / 10
  IF A(X1, Y1) = 2 THEN
    A(X1, Y1) = 0: SCORE& = SCORE& + 10: DOTS = DOTS + 1: REM SMALL DOT
  END IF
  IF A(X1, Y1) = 1 THEN
    A(X1, Y1) = 0: SCORE& = SCORE& + 50: REM LARGE DOT
    DOTS = DOTS + 1: EATEN = 0: GHOSTTIMER = 500 - (10 * LEVEL)
    IF GHOSTTIMER < 10 THEN GHOSTTIMER = 10
    FOR I = 1 TO 4
      S(I, 8) = 1
    NEXT I
  END IF
END IF
REM CHECK IF PACMAN HAS COLLIDED WITH A GHOST
X1 = S(0, 0): Y1 = S(0, 1): COLL = 0
FOR I = 1 TO 4
  IF S(I, 0) > (X1 - 5) AND S(I, 0) < (X1 + 5) THEN
    IF S(I, 1) > (Y1 - 4) AND S(I, 1) < (Y1 + 4) THEN
      IF S(I, 7) = 1 THEN COLL = 1: GHSTCOLL = I
    END IF
  END IF
NEXT I
IF COLL = 0 THEN GOTO TIMEADJ
REM IF GHOST IS SCARED THEN EAT GHOST
REM IF GHOST ISN'T THEN KILL PACMAN
IF S(GHSTCOLL, 8) = 1 THEN
  S(GHSTCOLL, 7) = 0: EATEN = EATEN + 1
  SCORE& = SCORE& + (100 * (2 ^ EATEN)): GOTO TIMEADJ
END IF
GOSUB KILLPACMAN


TIMEADJ:
REM SLOW GAME DOWN TO PROPER SPEED
IF SPEED <> 0 THEN FOR I& = 1 TO (SPEED& / SPEED): NEXT I&

REM CHECK TO SEE IF ALL DOTS HAVE BEEN EATEN (148 TOTAL)
IF DOTS = 148 THEN GOSUB ENDLEVEL

REM IF GAME IS OVER, PAUSE AND WAIT FOR PLAYER TO PRESS A KEY
IF GAMEOVER = 1 THEN
  FOR I = 1 TO 5: FOR I& = 1 TO SPEED&: NEXT I&: NEXT I
  DO
  LOOP WHILE INKEY$ <> "": REM CLEAR OUT EXTRA KEYPRESSES
  LINE (67, 99)-(163, 131), 9, BF
  LINE (69, 101)-(161, 129), 0, BF
  LOCATE 14, 10, 0
  PRINT " GAME OVER "
  LOCATE 16, 10, 0
  PRINT "PRESS A KEY"
  DO
  LOOP WHILE INKEY$ = "": REM WAIT FOR KEYPRESS
  LINE (64, 50)-(213, 132), 9, BF
  LINE (66, 52)-(211, 130), 0, BF
  GOSUB NEWGAME
  GOSUB SETSPRITEPOSITIONS
  GOSUB RESETLEVEL
  REM READ IN DATA FOR BOARD AGAIN
  RESTORE
  FOR Y = 0 TO 19: FOR X = 0 TO 18
  READ A(X, Y)
  NEXT X: NEXT Y
  GOSUB DRAWBOARD
  DO
  LOOP WHILE INKEY$ <> "": REM CLEAR OUT EXTRA KEYPRESSES
  GHOSTSPEED = 30
  LEVEL = 1
  LIVES = 3
  SCORE& = 0
  GOSUB DRAWTEXT
  GOSUB DRAWSCORE
  GOSUB DRAWLIVES
  GAMEOVER = 0
END IF


ENDLOOP:
REM REPEAT GAME LOOP FROM BEGINNING
GOTO GAMELOOP


'*************************************************************************
REM SUBROUTINES

DRAWTEXT:
LOCATE 3, 32, 0
PRINT "PacMan"
LOCATE 6, 33, 0
PRINT "Score"
LINE (245, 47)-(300, 56), 0, BF
LOCATE 9, 33, 0
PRINT "Lives"
LOCATE 12, 33, 0
PRINT "Level"
RETURN


DRAWSCORE:
  I = 36
  IF SCORE& > 9999 THEN I = 32: GOTO PRSCR
  IF SCORE& > 999 THEN I = 33: GOTO PRSCR
  IF SCORE& > 99 THEN I = 34: GOTO PRSCR
  IF SCORE& > 9 THEN I = 35
PRSCR:
  LOCATE 7, I, 0
  PRINT SCORE&
  I = 36
  IF LEVEL > 99 THEN I = 34: GOTO PRLVL
  IF LEVEL > 9 THEN I = 35
PRLVL:
  LOCATE 13, I, 0
  PRINT LEVEL
RETURN

KILLPACMAN:
  FOR I& = 1 TO (SPEED& / 50): NEXT I&
  X2 = S(0, 0): Y2 = S(0, 1)
  FOR H = 0 TO 9
  LINE (X2 - 1, Y2 + H)-(X2 + 11, Y2 + H), 0
  FOR I = 1 TO 4
  X1 = S(I, 0): Y1 = S(I, 1)
  IF S(I, 7) = 0 THEN GOSUB DRAWDEADGHOST: GOTO DRAWNEXTGHOST
  GHSTCOL = S(I, 6)
  GOSUB DRAWGHOST
DRAWNEXTGHOST:
  NEXT I
  FOR I& = 1 TO (SPEED& / 5): NEXT I&
  NEXT H
  LIVES = LIVES - 1
  GOSUB DRAWLIVES
  FOR I& = 1 TO (SPEED&): NEXT I&
  IF LIVES = 0 THEN GAMEOVER = 1: RETURN
  GOSUB DRAWBLANKPACMAN
  GHOSTTIMER = 0
  FOR I = 1 TO 4
  S(I, 8) = 0
  X1 = S(I, 2): Y1 = S(I, 3)
  GOSUB DRAWBLANKGHOST
  X1 = S(I, 0): Y1 = S(I, 1)
  GOSUB DRAWBLANKGHOST
  NEXT I
  GOSUB SETSPRITEPOSITIONS
  GOSUB DRAWBOARD
RETURN

SETSPRITEPOSITIONS:
  REM SET PACMAN
  S(0, 0) = 108: S(0, 1) = 140
  S(0, 2) = 108: S(0, 3) = 140
  S(0, 4) = -1: S(0, 5) = 0
  S(0, 6) = -1: S(0, 7) = 0
  REM SET GHOSTS
  S(1, 0) = 108: S(1, 1) = 70
  S(1, 2) = 108: S(1, 3) = 70
  FOR I = 2 TO 4
  S(I, 0) = 72 + (I * 12): S(I, 1) = 85
  S(I, 2) = 72 + (I * 12): S(I, 3) = 85
  NEXT I
  S(1, 4) = 0: REM LEFT
  S(2, 4) = 1: REM UP
  S(3, 4) = 2: REM RIGHT
  S(4, 4) = 0: REM LEFT
  FOR I = 1 TO 4
  S(I, 7) = 1
  S(I, 5) = 0
  NEXT I
  S(1, 5) = 1
  GAMETIME = 0
RETURN

DRAWLIVES:
  LINE (253, 71)-(295, 81), 0, BF
  X1 = 298: Y1 = 71
  FOR I = 1 TO LIVES - 1
  X1 = X1 - 15
  CIRCLE (X1 + 5, Y1 + 5), 5, 14, 3.8, 2.5, .8
  LINE (X1 + 2, Y1 + 3)-(X1 + 7, Y1 + 5), 14
  LINE (X1 + 2, Y1 + 7)-(X1 + 7, Y1 + 5), 14
  PAINT (X1 + 8, Y1 + 5), 14, 14
  NEXT I
RETURN

ENDLEVEL:
  X1 = S(0, 0): Y1 = S(0, 1)
  LINE (X1 - 1, Y1 - 1)-(X1 + 1, Y1 - 1), 0
  LINE (X1 - 1, Y1)-(X1 + 1, Y1), 0
  LINE (X1 - 1, Y1 + 1)-(X1 + 1, Y1 + 1), 0
  GOSUB DRAWPACMAN
  GOSUB DRAWSCORE
  FOR I& = 1 TO (SPEED&): NEXT I&
  BOARDCOL = 7
  GOSUB DRAWBOARD
  FOR I& = 1 TO (SPEED&): NEXT I&
  BOARDCOL = 1
  GOSUB DRAWBOARD
  FOR I& = 1 TO (SPEED&): NEXT I&
  BOARDCOL = 7
  GOSUB DRAWBOARD
  FOR I& = 1 TO (SPEED&): NEXT I&
  BOARDCOL = 1
  GOSUB DRAWBOARD
  FOR I& = 1 TO (SPEED&): NEXT I&
  BOARDCOL = 7
  GOSUB DRAWBOARD
  FOR I& = 1 TO (SPEED&): NEXT I&
  BOARDCOL = 1
  GOSUB DRAWBOARD
  FOR I& = 1 TO (SPEED&): NEXT I&
  BOARDCOL = 7
  GOSUB DRAWBOARD
  FOR I& = 1 TO (SPEED&): NEXT I&
  BOARDCOL = 1
  GOSUB DRAWBOARD
  FOR I& = 1 TO (SPEED&): NEXT I&
  REM RESET EVERYTHING
  GOSUB DRAWBLANKPACMAN
  FOR I = 1 TO 4
  X1 = S(I, 2): Y1 = S(I, 3)
  GOSUB DRAWBLANKGHOST
  X1 = S(I, 0): Y1 = S(I, 1)
  GOSUB DRAWBLANKGHOST
  NEXT I
  GOSUB RESETLEVEL
  GHOSTSPEED = GHOSTSPEED + 10
  IF GHOSTSPEED > 100 THEN GHOSTSPEED = 100
  LEVEL = LEVEL + 1
  REM READ IN DATA FOR BOARD AGAIN
  RESTORE
  FOR Y = 0 TO 19: FOR X = 0 TO 18
  READ A(X, Y)
  NEXT X: NEXT Y
  GOSUB DRAWBOARD
  DO
  LOOP WHILE INKEY$ <> "": REM CLEAR OUT EXTRA KEYPRESSES
  GOSUB SETSPRITEPOSITIONS
RETURN

RESETLEVEL:
  EATEN = 0
  DOTS = 0
  GHOSTTIMER = 0
  GHOSTMOVE = 0
RETURN

NEWGAME:
  LOCATE 8, 11, 0
  PRINT "PRESS NUMBER TO"
  LOCATE 9, 10, 0
  PRINT "SELECT GAME SPEED"
  LOCATE 11, 11, 0
  PRINT "1 EASIEST"
  LOCATE 12, 11, 0
  PRINT "2 EASY"
  LOCATE 13, 11, 0
  PRINT "3 NORMAL"
  LOCATE 14, 11, 0
  PRINT "4 HARD"
  LOCATE 15, 11, 0
  PRINT "5 HARDEST"
  LOCATE 16, 11, 0
  PRINT "6 IMPOSSIBLE"
  I = 0: H = 0
  DO
  K$ = INKEY$
  I = VAL(K$)
  IF K$ = CHR$(13) THEN H = 1: REM ENTER KEY
  IF K$ = CHR$(27) THEN END: REM ESCAPE KEY
  LOOP UNTIL ((I > 0 AND I < 7) OR (H = 1))
  IF H = 0 THEN
    SPEED = I * 17
    IF I = 6 THEN SPEED = 0
  END IF
  CLS
RETURN


DRAWBOARD:
FOR X = 0 TO 18
FOR Y = 0 TO 19
X1 = X * 12: Y1 = Y * 10
SELECT CASE A(X, Y)
REM CASE 1 is last to increase loop speed because it is very infrequent
CASE 2
GOSUB DRAWDOT
CASE 3
GOSUB DRAWWALLL
CASE 4
GOSUB DRAWWALLR
CASE 5
GOSUB DRAWWALLU
CASE 6
GOSUB DRAWWALLD
CASE 7
GOSUB DRAWWALLH
CASE 8
GOSUB DRAWWALLV
CASE 9
GOSUB DRAWWALLTD
CASE 10
GOSUB DRAWWALLTU
CASE 11
GOSUB DRAWWALLTL
CASE 12
GOSUB DRAWWALLTR
CASE 13
GOSUB DRAWWALLLT
CASE 14
GOSUB DRAWWALLRT
CASE 15
GOSUB DRAWWALLLUT
CASE 16
GOSUB DRAWWALLRUT
CASE 17
GOSUB DRAWWALLTH
CASE 18
GOSUB DRAWWALLTV
CASE 19
GOSUB DRAWWALLTTD
CASE 20
GOSUB DRAWWALLTTU
CASE 21
GOSUB DRAWWALLTTL
CASE 22
GOSUB DRAWWALLTTR
CASE 23
GOSUB DRAWWALLTLT
CASE 24
GOSUB DRAWWALLTRT
CASE 25
GOSUB DRAWWALLTLUT
CASE 26
GOSUB DRAWWALLTRUT
CASE 27
GOSUB DRAWWALLOTH
CASE 28
GOSUB DRAWWALLOTV
CASE 29
GOSUB DRAWWALLOTLT
CASE 30
GOSUB DRAWWALLOTRT
CASE 31
GOSUB DRAWWALLOTLUT
CASE 32
GOSUB DRAWWALLOTRUT
CASE 33
GOSUB DRAWWALLGOTH
CASE 34
GOSUB DRAWWALLGTH
CASE 1
GOSUB DRAWBIGDOT
END SELECT
NEXT Y
NEXT X
RETURN

DRAWPACMAN:
  IF (GAMETIME MOD 24) > 12 AND (GAMETIME MOD 24) < 18 THEN
    CIRCLE (X1 + 5, Y1 + 5), 5, 14, 4.4, 1.9, .8
    LINE (X1 + 2, Y1 + 2)-(X1 + 8, Y1 + 5), 14
    LINE (X1 + 2, Y1 + 8)-(X1 + 8, Y1 + 5), 14
    PAINT (X1 + 8, Y1 + 4), 14, 14
    RETURN
  END IF
  IF (GAMETIME MOD 24) > 6 THEN
    CIRCLE (X1 + 5, Y1 + 5), 5, 14, 3.8, 2.5, .8
    LINE (X1 + 2, Y1 + 3)-(X1 + 7, Y1 + 5), 14
    LINE (X1 + 2, Y1 + 7)-(X1 + 7, Y1 + 5), 14
    PAINT (X1 + 8, Y1 + 5), 14, 14
    RETURN
  END IF
  CIRCLE (X1 + 5, Y1 + 5), 5, 14, , , .8
  PAINT (X1 + 5, Y1 + 5), 14, 14
RETURN

DRAWBLANKPACMAN:
  X1 = S(0, 2): Y1 = S(0, 3)
  LINE (X1 + 2, Y1)-(X1 + 8, Y1), 0
  LINE (X1 + 1, Y1 + 1)-(X1 + 9, Y1 + 1), 0
  LINE (X1, Y1 + 2)-(X1 + 10, Y1 + 2), 0
  LINE (X1 - 1, Y1 + 3)-(X1 + 11, Y1 + 3), 0
  LINE (X1 - 1, Y1 + 4)-(X1 + 11, Y1 + 4), 0
  LINE (X1 - 1, Y1 + 5)-(X1 + 11, Y1 + 5), 0
  LINE (X1 - 1, Y1 + 6)-(X1 + 11, Y1 + 6), 0
  LINE (X1 - 1, Y1 + 7)-(X1 + 11, Y1 + 7), 0
  LINE (X1, Y1 + 8)-(X1 + 10, Y1 + 8), 0
  LINE (X1, Y1 + 9)-(X1 + 10, Y1 + 9), 0
  LINE (X1 + 2, Y1 + 10)-(X1 + 8, Y1 + 10), 0
RETURN

DRAWGHOST:
  LINE (X1 + 3, Y1 + 1)-(X1 + 7, Y1 + 1), GHSTCOL
  LINE (X1 + 1, Y1 + 2)-(X1 + 9, Y1 + 2), GHSTCOL
  LINE (X1, Y1 + 3)-(X1 + 10, Y1 + 8), GHSTCOL, BF
  PAINT (X1 + 5, Y1 + 3), GHSTCOL, GHSTCOL
  IF (GAMETIME MOD 30) > 15 THEN
    LINE (X1, Y1 + 9)-(X1 + 10, Y1 + 9), GHSTCOL, , DOTTED
  ELSE
    LINE (X1 - 1, Y1 + 9)-(X1 + 10, Y1 + 9), GHSTCOL, , DOTTED
  END IF
  CIRCLE (X1 + 3, Y1 + 3), 1, 15
  CIRCLE (X1 + 7, Y1 + 3), 1, 15
  LINE (X1 + 3, Y1 + 3)-(X1 + 3, Y1 + 3), 1
  LINE (X1 + 7, Y1 + 3)-(X1 + 7, Y1 + 3), 1
RETURN

DRAWSCAREDGHOST:
  LINE (X1 + 3, Y1 + 1)-(X1 + 7, Y1 + 1), GHSTCOL
  LINE (X1 + 1, Y1 + 2)-(X1 + 9, Y1 + 2), GHSTCOL
  LINE (X1, Y1 + 3)-(X1 + 10, Y1 + 8), GHSTCOL, BF
  PAINT (X1 + 5, Y1 + 3), GHSTCOL, GHSTCOL
  IF (GAMETIME MOD 30) > 15 THEN
    LINE (X1, Y1 + 9)-(X1 + 10, Y1 + 9), GHSTCOL, , DOTTED
  ELSE
    LINE (X1 - 1, Y1 + 9)-(X1 + 10, Y1 + 9), GHSTCOL, , DOTTED
  END IF
  LINE (X1 + 2, Y1 + 3)-(X1 + 3, Y1 + 3), 15
  LINE (X1 + 7, Y1 + 3)-(X1 + 8, Y1 + 3), 15
  LINE (X1 + 1, Y1 + 6)-(X1 + 8, Y1 + 6), 15, , DOTTED2
  LINE (X1 + 1, Y1 + 7)-(X1 + 9, Y1 + 7), 15, , DOTTED3
RETURN
         
DRAWBLANKGHOST:
  LINE (X1 + 1, Y1 + 1)-(X1 + 7, Y1 + 1), 0
  LINE (X1, Y1 + 2)-(X1 + 9, Y1 + 2), 0
  LINE (X1, Y1 + 3)-(X1 + 10, Y1 + 3), 0
  LINE (X1, Y1 + 4)-(X1 + 10, Y1 + 4), 0
  LINE (X1, Y1 + 5)-(X1 + 10, Y1 + 9), 0, BF
RETURN

DRAWDEADGHOST:
  CIRCLE (X1 + 3, Y1 + 3), 1, 1, , , .8
  CIRCLE (X1 + 7, Y1 + 3), 1, 1, , , .8
RETURN

REM THE FOLLOWING SUBROUTINES DISPLAY A NUMBER AFTER A REM STATEMENT THAT
REM CORRESPOND TO THE NUMBERS USED IN THE DATA LIST

DRAWDOT:
REM 2
  LINE (X1 + 5, Y1 + 4)-(X1 + 5, Y1 + 6), 15
  LINE (X1 + 4, Y1 + 5)-(X1 + 6, Y1 + 5), 15
RETURN

DRAWBIGDOT:
REM 1
  CIRCLE (X1 + 5, Y1 + 5), 3, 15, , , .8
  PAINT (X1 + 5, Y1 + 5), 15, 15
RETURN

DRAWBLANKBIGDOT:
  FOR X1 = 12 TO 204 STEP 192: FOR Y1 = 20 TO 140 STEP 120
  CIRCLE (X1 + 5, Y1 + 5), 3, 0, , , .8
  PAINT (X1 + 5, Y1 + 5), 0, 0
  NEXT Y1: NEXT X1
RETURN

DRAWWALLL:
REM 3
  CIRCLE (X1 + 5, Y1 + 5), 5, BOARDCOL, 1.57, 4.71
  LINE (X1 + 5, Y1 + 1)-(X1 + 11, Y1 + 1), BOARDCOL
  LINE (X1 + 5, Y1 + 9)-(X1 + 11, Y1 + 9), BOARDCOL
RETURN

DRAWWALLR:
REM 4
  CIRCLE (X1 + 5, Y1 + 5), 5, BOARDCOL, 4.71, 1.57
  LINE (X1, Y1 + 1)-(X1 + 5, Y1 + 1), BOARDCOL
  LINE (X1, Y1 + 9)-(X1 + 5, Y1 + 9), BOARDCOL
RETURN

DRAWWALLU:
REM 5
  CIRCLE (X1 + 5, Y1 + 5), 5, BOARDCOL, 0, 3.14
  LINE (X1, Y1 + 5)-(X1, Y1 + 9), BOARDCOL
  LINE (X1 + 10, Y1 + 5)-(X1 + 10, Y1 + 9), BOARDCOL
RETURN

DRAWWALLD:
REM 6
  CIRCLE (X1 + 5, Y1 + 5), 5, BOARDCOL, 3.14, 0
  LINE (X1, Y1)-(X1, Y1 + 5), BOARDCOL
  LINE (X1 + 10, Y1)-(X1 + 10, Y1 + 5), BOARDCOL
RETURN

DRAWWALLH:
REM 7
  LINE (X1, Y1 + 1)-(X1 + 11, Y1 + 1), BOARDCOL
  LINE (X1, Y1 + 9)-(X1 + 11, Y1 + 9), BOARDCOL
RETURN

DRAWWALLV:
REM 8
  LINE (X1, Y1)-(X1, Y1 + 9), BOARDCOL
  LINE (X1 + 10, Y1)-(X1 + 10, Y1 + 9), BOARDCOL
RETURN

DRAWWALLTD:
REM 9
  LINE (X1, Y1 + 1)-(X1 + 11, Y1 + 1), BOARDCOL
  LINE (X1 + 11, Y1 + 9)-(X1 + 11, Y1 + 9), BOARDCOL
RETURN

DRAWWALLTU:
REM 10
  LINE (X1, Y1 + 9)-(X1 + 11, Y1 + 9), BOARDCOL
  LINE (X1, Y1)-(X1, Y1), BOARDCOL
  LINE (X1 + 10, Y1)-(X1 + 11, Y1 + 1), BOARDCOL
RETURN

DRAWWALLTL:
REM 11
  LINE (X1 + 10, Y1)-(X1 + 10, Y1 + 9), BOARDCOL
  LINE (X1, Y1)-(X1, Y1), BOARDCOL
RETURN

DRAWWALLTR:
REM 12
  LINE (X1, Y1)-(X1, Y1 + 9), BOARDCOL
  LINE (X1 + 11, Y1 + 9)-(X1 + 11, Y1 + 9), BOARDCOL
  LINE (X1 + 10, Y1)-(X1 + 11, Y1 + 1), BOARDCOL
RETURN

DRAWWALLLT:
REM 13
  CIRCLE (X1 + 5, Y1 + 5), 5, BOARDCOL, 0, 1.57
  LINE (X1, Y1 + 1)-(X1 + 5, Y1 + 1), BOARDCOL
  LINE (X1 + 10, Y1 + 5)-(X1 + 10, Y1 + 9), BOARDCOL
RETURN

DRAWWALLRT:
REM 14
  CIRCLE (X1 + 5, Y1 + 5), 5, BOARDCOL, 1.57, 3.14
  LINE (X1 + 5, Y1 + 1)-(X1 + 11, Y1 + 1), BOARDCOL
  LINE (X1, Y1 + 5)-(X1, Y1 + 9), BOARDCOL
  LINE (X1 + 11, Y1 + 9)-(X1 + 11, Y1 + 9), BOARDCOL
RETURN

DRAWWALLLUT:
REM 15
  CIRCLE (X1 + 5, Y1 + 5), 5, BOARDCOL, 3.14, 4.71
  LINE (X1 + 5, Y1 + 9)-(X1 + 11, Y1 + 9), BOARDCOL
  LINE (X1, Y1)-(X1, Y1 + 5), BOARDCOL
  LINE (X1 + 10, Y1)-(X1 + 11, Y1 + 1), BOARDCOL
RETURN

DRAWWALLRUT:
REM 16
  CIRCLE (X1 + 5, Y1 + 5), 5, BOARDCOL, 4.71, 0
  LINE (X1, Y1 + 9)-(X1 + 5, Y1 + 9), BOARDCOL
  LINE (X1 + 10, Y1)-(X1 + 10, Y1 + 5), BOARDCOL
  LINE (X1, Y1)-(X1, Y1), BOARDCOL
RETURN

DRAWWALLTH:
REM 17
  LINE (X1, Y1 + 7)-(X1 + 11, Y1 + 7), BOARDCOL
  LINE (X1, Y1 + 9)-(X1 + 11, Y1 + 9), BOARDCOL
RETURN

DRAWWALLTV:
REM 18
  LINE (X1, Y1)-(X1, Y1 + 9), BOARDCOL
  LINE (X1 + 2, Y1)-(X1 + 2, Y1 + 9), BOARDCOL
RETURN

DRAWWALLTTD:
REM 19
  LINE (X1, Y1 + 7)-(X1 + 11, Y1 + 7), BOARDCOL
  LINE (X1 + 11, Y1 + 9)-(X1 + 11, Y1 + 9), BOARDCOL
RETURN

DRAWWALLTTU:
REM 20 - NOT USED
  LINE (X1, Y1 + 2)-(X1 + 11, Y1 + 2), BOARDCOL
  LINE (X1, Y1)-(X1, Y1), BOARDCOL
  LINE (X1 + 10, Y1)-(X1 + 11, Y1 + 1), BOARDCOL
RETURN

DRAWWALLTTL:
REM 21
  LINE (X1 + 2, Y1)-(X1 + 2, Y1 + 9), BOARDCOL
  LINE (X1, Y1)-(X1, Y1), BOARDCOL
RETURN

DRAWWALLTTR:
REM 22
  LINE (X1 + 8, Y1)-(X1 + 8, Y1 + 9), BOARDCOL
  LINE (X1 + 11, Y1 + 9)-(X1 + 11, Y1 + 9), BOARDCOL
  LINE (X1 + 10, Y1)-(X1 + 11, Y1 + 1), BOARDCOL
RETURN

DRAWWALLTLT:
REM 23
  CIRCLE (X1 + 5, Y1 + 5), 5, BOARDCOL, 0, 1.57
  LINE (X1, Y1 + 1)-(X1 + 5, Y1 + 1), BOARDCOL
  LINE (X1 + 10, Y1 + 5)-(X1 + 10, Y1 + 9), BOARDCOL
  LINE (X1 + 8, Y1 + 5)-(X1 + 8, Y1 + 9), BOARDCOL
  LINE (X1, Y1 + 3)-(X1 + 6, Y1 + 3), BOARDCOL
  LINE (X1 + 7, Y1 + 4)-(X1 + 7, Y1 + 4), BOARDCOL
RETURN

DRAWWALLTRT:
REM 24
  CIRCLE (X1 + 5, Y1 + 5), 5, BOARDCOL, 1.57, 3.14
  LINE (X1 + 5, Y1 + 1)-(X1 + 11, Y1 + 1), BOARDCOL
  LINE (X1, Y1 + 5)-(X1, Y1 + 9), BOARDCOL
  LINE (X1 + 2, Y1 + 5)-(X1 + 2, Y1 + 9), BOARDCOL
  LINE (X1 + 4, Y1 + 3)-(X1 + 11, Y1 + 3), BOARDCOL
  LINE (X1 + 3, Y1 + 4)-(X1 + 3, Y1 + 4), BOARDCOL
RETURN

DRAWWALLTLUT:
REM 25
  CIRCLE (X1 + 5, Y1 + 5), 5, BOARDCOL, 3.14, 4.71
  LINE (X1 + 5, Y1 + 9)-(X1 + 11, Y1 + 9), BOARDCOL
  LINE (X1, Y1)-(X1, Y1 + 5), BOARDCOL
  LINE (X1 + 2, Y1)-(X1 + 2, Y1 + 5), BOARDCOL
  LINE (X1 + 4, Y1 + 7)-(X1 + 11, Y1 + 7), BOARDCOL
  LINE (X1 + 3, Y1 + 6)-(X1 + 3, Y1 + 6), BOARDCOL
RETURN

DRAWWALLTRUT:
REM 26
  CIRCLE (X1 + 5, Y1 + 5), 5, BOARDCOL, 4.71, 0
  LINE (X1, Y1 + 9)-(X1 + 5, Y1 + 9), BOARDCOL
  LINE (X1 + 10, Y1)-(X1 + 10, Y1 + 5), BOARDCOL
  LINE (X1, Y1 + 7)-(X1 + 6, Y1 + 7), BOARDCOL
  LINE (X1 + 8, Y1)-(X1 + 8, Y1 + 5), BOARDCOL
  LINE (X1 + 7, Y1 + 6)-(X1 + 7, Y1 + 6), BOARDCOL
RETURN

DRAWWALLOTH:
REM 27
  LINE (X1, Y1 + 1)-(X1 + 11, Y1 + 1), BOARDCOL
  LINE (X1, Y1 + 3)-(X1 + 11, Y1 + 3), BOARDCOL
RETURN

DRAWWALLOTV:
REM 28
  LINE (X1 + 10, Y1)-(X1 + 10, Y1 + 9), BOARDCOL
  LINE (X1 + 8, Y1)-(X1 + 8, Y1 + 9), BOARDCOL
RETURN

DRAWWALLOTLT:
REM 29
  LINE (X1, Y1 + 7)-(X1 + 2, Y1 + 9), BOARDCOL
RETURN

DRAWWALLOTRT:
REM 30
  LINE (X1 + 8, Y1 + 9)-(X1 + 11, Y1 + 7), BOARDCOL
  LINE (X1 + 11, Y1 + 9)-(X1 + 11, Y1 + 9), BOARDCOL
RETURN

DRAWWALLOTLUT:
REM 31
  LINE (X1 + 8, Y1 + 1)-(X1 + 10, Y1 + 3), BOARDCOL
  LINE (X1 + 10, Y1)-(X1 + 11, Y1 + 1), BOARDCOL
  LINE (X1 + 8, Y1)-(X1 + 8, Y1 + 1), BOARDCOL
  LINE (X1 + 11, Y1 + 3)-(X1 + 11, Y1 + 3), BOARDCOL
RETURN

DRAWWALLOTRUT:
REM 32
  LINE (X1 + 1, Y1 + 2)-(X1 + 2, Y1), BOARDCOL
  LINE (X1, Y1)-(X1, Y1), BOARDCOL
  LINE (X1, Y1 + 3)-(X1, Y1 + 3), BOARDCOL
RETURN

DRAWWALLGOTH:
REM 33
  LINE (X1, Y1 + 1)-(X1 + 11, Y1 + 1), BOARDCOL
  LINE (X1, Y1 + 3)-(X1 + 11, Y1 + 3), BOARDCOL
RETURN

DRAWWALLGTH:
REM 34
  LINE (X1, Y1 + 7)-(X1 + 11, Y1 + 7), BOARDCOL
  LINE (X1, Y1 + 9)-(X1 + 11, Y1 + 9), BOARDCOL
RETURN



'***************************************************************************
REM BOARD DATA - EACH DATA VALUE IS EXPLAINED ABOVE IN SUBROUTINES

DATA 30,17,17,17,17,17,17,17,17,19,17,17,17,17,17,17,17,17,29
DATA 28,2,2,2,2,2,2,2,2,8,2,2,2,2,2,2,2,2,18
DATA 28,1,3,4,2,3,7,4,2,6,2,3,7,4,2,3,4,1,18
DATA 28,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,18
DATA 28,2,3,4,2,5,2,3,7,9,7,4,2,5,2,3,4,2,18
DATA 28,2,2,2,2,8,2,2,2,8,2,2,2,8,2,2,2,2,18
DATA 31,27,27,23,2,12,7,4,0,6,0,3,7,11,2,24,27,27,32
DATA 0,0,0,28,2,8,0,0,0,0,0,0,0,8,2,18,0,0,0
DATA 17,17,17,26,2,6,0,24,27,33,27,23,0,6,2,25,17,17,17
DATA 0,0,0,0,2,0,0,25,17,34,17,26,0,0,2,0,0,0,0
DATA 27,27,27,23,2,5,0,0,0,0,0,0,0,5,2,24,27,27,27
DATA 30,17,17,26,2,6,0,3,7,9,7,4,0,6,2,25,17,17,29
DATA 28,2,2,2,2,2,2,2,2,8,2,2,2,2,2,2,2,2,18
DATA 28,2,3,13,2,3,7,4,2,6,2,3,7,4,2,14,4,2,18
DATA 28,1,2,8,2,2,2,2,2,0,2,2,2,2,2,8,2,1,18
DATA 22,4,2,6,2,5,2,3,7,9,7,4,2,5,2,6,2,3,21
DATA 28,2,2,2,2,8,2,2,2,8,2,2,2,8,2,2,2,2,18
DATA 28,2,3,7,7,10,7,4,2,6,2,3,7,10,7,7,4,2,18
DATA 28,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,18
DATA 31,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,27,32


Thanks everyone! I think I have it figured out now.

Just in case anyone wanted it, here is some very simple code that I started out with.

Graphics 640,480,0,2
SetBuffer BackBuffer()

packman=LoadImage("packman.bmp")

maze=LoadImage("maze.bmp")

x#=320
y#=240

While Not KeyDown(1)
Cls
DrawImage maze,0,0

If KeyDown(203)
	If Not ImagesCollide(packman,x-1,y,0,maze,0,0,0) Then x=x-1
EndIf
If KeyDown(205)
	If Not ImagesCollide(packman,x+1,y,0,maze,0,0,0) Then x=x+1
EndIf
If KeyDown(200)
	If Not ImagesCollide(packman,x,y-1,0,maze,0,0,0) Then y=y-1
EndIf
If KeyDown(208)
	If Not ImagesCollide(packman,x,y+1,0,maze,0,0,0) Then y=y+1
EndIf
DrawImage packman,x,y

Flip
Wend
End


Or, that could be rewritten as

; figure out where we want to move to
Local dest_x = x
Local dest_y = y
If KeyDown(203) Then dest_x = dest_x - 1
If KeyDown(205) Then dest_x = dest_x + 1
If KeyDown(200) Then dest_y = dest_y - 1
If KeyDown(208) Then dest_y = dest_y + 1

; attempt to move there
If Not ImagesCollide(packman,dest_x,dest_y,0,maze,0,0,0) Then
	x = dest_x
	y = dest_y
EndIf


Except that in 'true' Pacman, the pacman is always on the move unless it hits a wall...