FiveTech Support Forums

FiveWin / Harbour / xBase community
Board index FiveWin for Harbour/xHarbour PacMan by Benjamin Casarrubias
Posts: 44158
Joined: Thu Oct 06, 2005 05:47 PM
PacMan by Benjamin Casarrubias
Posted: Mon Nov 17, 2025 09:02 PM

EXE, full source code and resources:
https://github.com/FiveTechSoft/FWH_tools/blob/master/pacman.rar

// benjamin casarrubias moreno
// 15 de abril del 2015
// chino72vale@hotmail.com
#include "Fivewin.ch" 

MEMVAR xTeclas
MEMVAR nXPos, nYPos, xDonde, oSprite, nXposa, nYposa
MEMVAR aMatriz, Maxren, MaxCol, Omuro, xVidas
MEMVAR oComida, nComida, Van, aComidas, oFantasmas, fXpos, fYpos
MEMVAR xfdonde, XOTRA, XPOS, osay, csay, ofanta1, ofanta2, ofanta3, ofanta4, ofanta5, Ownd
FUNCTION MAIN() 

local oBrush, oTimer 
local i, y, n:=0, m:=0

PUBLIC xTeclas, xvidas
PUBLIC nXPos, nYPos 
PUBLIC Xdonde, oSprite, oMuro
PUBLIC aMatriz, Maxren, Maxcol, Omuro
PUBLIC oComida, nComida, Van, Acomidas
PUBLIC fXpos, fYpos, Ofantasmas
PUBLIC xFdonde, xotra, XPOS
PUBLIC OSAY, CSAY, OFANTA, OFANTA2, OFANTA3, OFANTA4, ofanta5, Ownd

             //12345678901234567890123456789
amatriz := { {"xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"},; 
             {"x  o  o o o xxxxx o o o  o  x"},;
             {"x xxx xxoxx ooooo xxoxx xxx x"},;
             {"xoxxxoxxoxxoxxxxxoxxoxxoxxxox"},;
             {"xooooooooooooooooooooooooooox"},;
             {"xoxxxoxxoxxxxxoxxxxxoxxoxxxox"},;
             {"xoooooxxoooooxoxoooooxxooooox"},;
             {"xoxxxoxxxxxxoxoxoxxxxxxoxxxox"},;
             {"xoxxxoxxoooooooooooooxxoxxxox"},;
             {"toooooxxoxxxxxoxxxxxoxxooooof"},;
             {"xoxxxoxxoxxxxxoxxxxxoxxoxxxox"},;
             {"xoxxxoxxoooooooooooooxxoxxxox"},;
             {"xoxxxoxxxxxxoxoxoxxxxxxoxxxox"},;
             {"xoooooxxoooooxoxoooooxxooooox"},;
             {"xoxxxoxxoxxxxxoxxxxxoxxoxoxox"},;
             {"xoxxxoooooooooooooooooooxoxox"},;
             {"xoooooxxxxoxxxoxxxxoxxxoxoxox"},;
             {"xoxxxoxxxxooooooooooxxxoxoxox"},;
             {"xooooooooooxxxxxxxxooooooooox"},;
             {"xxxxxxxxxxxxxxxxxxxxxxxxxxxxx"}}
             //65*37

nComida:=0
XTECLAS = 0
Van:=0
XPOS:=0
nXPos   = 450     // columna
nYPos   = 540     // rengloN
xdonde = 1
Maxren:=20
Maxcol:=29
xotra:=0
xfdonde:=2
// posicion inicial fantasma
fXpos:=5*30
fYpos:=15*30
xVidas:=0

aComidas:=array(MaxRen,MaxCol)
for i:=1 to maxren
  for y:= 1 to maxcol
    aComidas[i][y]=substr(amatriz[i][1],y,1)
    if substr(amatriz[i][1],y,1)="o"
       nComida++
    endif
  next y
next i

omuro:=array(maxren,maxcol)

DEFINE WINDOW oWnd FROM 1,1 to 50,125 
@ nYpos, nxPos BITMAP oSprite FILE "recpac\pacman.bmp"  PIXEL  size 30,30 NOBORDER

@ fxPos, fYpos BITMAP ofantasmas FILE "recpac\fanta1.bmp"  PIXEL  size 30,30 NOBORDER

oFanta1:=TFANTASMON():NEW(270,300,,"recpac\fanta2.bmp")
ofanta2:=TFANTASMON():NEW(360,360,,"recpac\fanta3.bmp")
ofanta3:=TFANTASMON():NEW(360,360,,"recpac\fanta4.bmp")
ofanta4:=TFANTASMON():NEW(360,360,,"recpac\fanta5.bmp")
ofanta5:=TFANTASMON():NEW(360,360,,"recpac\fanta6.bmp")
n:=30  

// DIBUJANDO LA PANTALLA MAESTRA
for i:= 1 to maxren
   m:=30
   for y:= 1 to maxcol
      if aComidas[i][y]="x"
         @ n, m BITMAP omuro[i][y] size 30,30 of ownd ; 
                FILE "recpac\marcos.bmp" PIXEL NO BORDER
      ELSE
        if aComidas[i][y]="o"
             @ n, m BITMAP omuro[i][y] size 30,30 of ownd ; 
                FILE "recpac\comidita.bmp" PIXEL  NOBORDER
         ENDIF
      endif
      m:=m+30
  next y
  n:=n+30
 
next i
      
// TERMINA dibujo pantalla

oWnd:bKeyDown :={ | nKey | xteclas:=Teclas(nKey, oSprite)  }        
 
                 
oSprite:lTransparent = .t. 

ownd:binit := { || vueltacompleta() }

       
ACTIVATE WINDOW oWnd // CENTERED
 
RETURN NIL 


   


FUNCTION TERMINO()
LOCAL DEV:=.t.
LOCAL I, Y
FOR I:=1 TO MAXREN
  FOR Y:=1 TO MAXCOL
     if aComidas[i][y]="o"
        dev:=.f.
     endif
  next y
next i
return dev     
     


FUNCTION VUELTACOMPLETA()
LOCAL I
local xcol:=15, xren:=18

sonido_Inicial()
 
 
do while xdonde<>10


checapos()
ofanta1:movimiento_Checapos()
ofanta2:movimiento_Checapos()
ofanta3:movimiento_Checapos()
ofanta4:movimiento_Checapos()
ofanta5:movimiento_Checapos()
nXposa:=nXpos
nyPosa:=nYpos
IF XDONDE =1 // abajo
   nypos = nypos +30
   xren:=nypos/30
   xcol:=nxpos/30
   if aComidas[xren][xcol]="x"
      nYpos := nYpos -30
   endif
  oSprite:LoadBmp( "recpac\pacmana.bmp")
  oWnd:Refresh()
ENDIF

IF XDONDE =0  // arriba
   nypos = nypos -30
   xren:=nypos/30
   xcol:=nxpos/30
   if aComidas[xren][xcol]="x"
      nYpos := nYpos +30
   endif
   oSprite:LoadBmp( "recpac\pacmant.bmp")
   oWnd:Refresh()
   

ENDIF


IF XDONDE =2  // izquierda
   nXpos = nXpos -30
   xren:=nypos/30
   xcol:=nxpos/30
   if aComidas[xren][xcol]="x"
      nXpos := nXpos +30
   else
      if aComidas[xren][xcol]="t"
         nxpos:=870
      endif
      

   endif
   oSprite:LoadBmp( "recpac\pacmani.bmp")
   oWnd:Refresh()
   

ENDIF


IF XDONDE =3 // derecha
   nXpos:= nXpos +30
   xren:=nypos/30 
   xcol:=nxpos/30
   if aComidas[xren][xcol]="x"
      nXpos := nxpos -30
   else
      if aComidas[xren][xcol]="f"
         nxpos:=30
      endif
   endif
   

  oSprite:LoadBmp( "recpac\pacmand.bmp")
  oWnd:Refresh()
   

ENDIF


if aComidas[xren][xcol]="o"
    omuro[xren][xcol]:end()
    aComidas[xren][xcol]:=" "
    van ++
    Sonido_Comiendo()
else
   // Sonido_sincomer()
     

endif  

// corrida del fantasmon wey 

ofanta1:Movimiento_Fantasma()
ofanta2:Movimiento_Fantasma()
ofanta3:Movimiento_Fantasma()
ofanta4:movimiento_Fantasma()
ofanta5:movimiento_Fantasma()

IF XFDONDE =1 // abajo
   fxpos = fxpos +30
   xren:=fxpos/30
   xcol:=fypos/30
   if aComidas[xren][xcol]="x"
      fxpos := fxpos -30
   endif
   

ENDIF

IF XFDONDE =0  // arriba
   fxpos = fxpos -30
   xren:=fxpos/30
   xcol:=fypos/30
   if aComidas[xren][xcol]="x"
      fxpos := fxpos +30
   endif
   

ENDIF

IF XFDONDE =2  // izquierda
   fypos = fypos -30
   xren:=fxpos/30
   xcol:=fypos/30
   if aComidas[xren][xcol]="x"
       fypos := fypos +30
   //   xfdonde:=1
   //   fxpos:=fxpos+30
      

   else
      if aComidas[xren][xcol]="t"
       //fypos := fypos +30
             fYpos:=840
      endif
      

   endif
   

ENDIF


IF XFDONDE =3 // derecha
   fypos:= fypos +30
   xren:=fxpos/30 
   xcol:=fypos/30
   if aComidas[xren][xcol]="x"
      fypos := fypos -30
   else
      if aComidas[xren][xcol]="f"
        // fypos := fypos -30
         fYpos:=60
      endif
   endif
   

ENDIF
  

// movimiento del fantasma

Mueve_Pacman()
Mueve_Fantasma()
ofanta1:Mover_Fantasma()
oFanta2:Mover_Fantasma()
ofanta3:Mover_Fantasma()
ofanta4:Mover_Fantasma()
ofanta5:Mover_Fantasma()

if (nYpos=fxpos .and. nXpos=fypos) .or. (nYposa=fXpos .and. nxposa=fypos)     // agarre del fantamas al pacman
   sonido_tecomio()
   syswait(1.9)
  

 //  ? "ey estoy en 3 son dos opciones ", xpos, xotra, xfdonde
   nXPos   = 450     // movemos al pacman alos valor de inicio we
   nYPos   = 540  
   

   fXpos:=5*30       // movemos al fantasmas al valor inicial
   fYpos:=15*30
   xfdonde:= 2
      

   xVidas++     // ya le dio en la madre los fantamas del caribe al pacman
   if xvidas=3
      Sonido_final()
      syswait(5)
      quit
   endif
    

endif   


IF XDONDE=10 .or. van=nComida  // vamanos para fuera
// "ey estoy en 3 son dos opciones ",xpos, xotra,  xfdonde
   Sonido_final()
   syswait(5)
   QUIT
ENDIF

enddo
?//finalizo bye"
Sonido_final()
syswait(5)


RETURN NIL

 

Function Mueve_Pacman()
   oSprite:Move( nYPos, nXPos, , , .T. ) 
   syswait(.15)
Return NIl

Function Mueve_fantasma()
   ofantasmas:Move( fxPos, fyPos, , , .T. ) 
Return NIl


Function Sonido_Termina()
   SndPlaySound( "recpac\cry.wav", 3 )
   syswait(.5)
Return NIL


Function Sonido_Comiendo()
   SndPlaySound( "recpac\comida.wav", 3 )
Return Nil

Function Sonido_Inicial()
    SndPlaySound( "recpac\pacman_beginning\pacman_beginning.wav", 3 )
    syswait(4.0)
Return Nil

Function Sonido_rComiendo()
    SndPlaySound( "recpac\pacman_eatfruit\pacman_eatfruit.wav", 3 )
Return Nil

Function Sonido_Final()
    SndPlaySound( "recpac\pacman_intermission.wav", 3 )
Return Nil

Function Sonido_tecomio()
   SndPlaySound( "recpac\pacman_death\pacman_death.wav", 3 )
Return Nil

Function Sonido_finalizar()
   SndPlaySound( "recpac\pacman_death\pacman_death.wav", 3 )
Return Nil


Function teclas(xKey, oSprite)


IF xkey = 38  //ARRIBA
   XDONDE = 0
endif
if xkey = 40   // abajo
    XDONDE = 1
    

endif
if xkey = 37
   XDONDE = 2
endif
if xkey = 39
    XDONDE =3
endif

if xkey = 27
   xdonde=10
   

endif   
 
Return XKEY



Function  checapos()
local xren, xcol
//local xotra:=0
local xnpos :=0
   xren:=fxpos/30
   xcol:=fypos/30
xotra:=0  

XPOS:=0
if !acomidas[xren+1][xcol]="x"
   xpos++
   xotra:=xotra + 1
endif
if !acomidas[xren-1][xcol]="x"
   xpos++
   xotra:= xotra + 2
endif
if !acomidas[xren][xcol-1]="x"
   xpos++
   xotra:= xotra + 3
endif
if !acomidas[xren][xcol+1]="x"
   xpos++
   xotra:= xotra + 4 
endif

if xpos>2    // quire decir que ay que random para decidi a que lugar ir we  


   xfdonde:=nrandom(3)
  

else
   if xpos=2
      if xotra = 4  .or. xotra=6 .OR. XOTRA=5  //quire decir que esta en la esquina del  muro
         xfdonde:=nrandom(3)
     else
     

  endif
   endif
    


endif

Return NIL


CLASS TFANTASMON
   DATA   xfdonde INIT 2
   DATA   ny      INIT 0
   DATA   xModo   INIT 0
   DATA   xRen    INIT 0
   DATA   XcOL    INIT 0
   DATA   fXpos   INIT 0 
   DATA   FyPOS   INIT 0
   DATA   oFanta  

   METHOD New() 
   METHOD Mover_Fantasma()
   METHOD Movimiento_Fantasma()
   METHOD Movimiento_ChecaPos()
   METHOD Movimiento_Agarre()

ENDCLASS

METHOD New(fxPos, fyPos, ofanta, odibujo )  CLASS TFANTASMON

 @ fxPos, fYpos BITMAP ofanta FILE odibujo  PIXEL  size 30,30 NOBORDER  
 ::oFanta:=ofanta
 ::fxPos:=fxpos
 ::fYpos:=fYpos

RETURN SELF


METHOD Mover_Fantasma() CLASS TFANTASMON
   ::ofanta:Move( ::fxPos, ::fyPos, , , .T. ) 
   ::Movimiento_Agarre()
Return NIl


// agarre del fantamas
METHOD Movimiento_Agarre()   CLASS TFANTASMON
if (nYpos=::fxpos .and. nXpos=::fypos)  .or. (nYposa=::fXpos .and. nxposa=::fypos)  // agarre del fantamas al pacman
   sonido_tecomio()
   syswait(1.9)
   //  ? "ey estoy en 3 son dos opciones ", xpos, xotra, xfdonde
   nXPos   = 450     // movemos al pacman alos valor de inicio we
   nYPos   = 540  
   

   xVidas++     // ya le dio en la madre los fantamas del caribe al pacman
   if xvidas=3
      Sonido_final()
      syswait(5)
      quit
   endif
   

Endif
RETURN NIL


// corrida del fantasmon wey 
METHOD Movimiento_Fantasma()   CLASS TFANTASMON


IF ::XFDONDE =1 // abajo
   ::fxpos = ::fxpos +30
   ::xren:=::fxpos/30
   ::xcol:=::fypos/30
   if aComidas[::xren][::xcol]="x"
      ::fxpos := ::fxpos -30
   endif
   

   

ENDIF

IF ::XFDONDE =0  // arriba
   ::fxpos = ::fxpos -30
   ::xren:=::fxpos/30
   ::xcol:=::fypos/30
   if aComidas[::xren][::xcol]="x"
      ::fxpos := ::fxpos +30
   endif
   

ENDIF


IF ::XFDONDE =2  // izquierda
   ::fypos = ::fypos -30
   ::xren:=::fxpos/30
   ::xcol:=::fypos/30
   if aComidas[::xren][::xcol]="x"
       ::fypos := ::fypos +30
      

   else
      if aComidas[::xren][::xcol]="t"
       //fypos := fypos +30
             ::fYpos:=840
      endif
      

   endif
   

ENDIF


IF ::XFDONDE =3 // derecha
   ::fypos:= ::fypos +30
   ::xren:=::fxpos/30 
   ::xcol:=::fypos/30
   if aComidas[::xren][::xcol]="x"
      ::fypos := ::fypos -30
   else
      if aComidas[::xren][::xcol]="f"
        // fypos := fypos -30
         ::fYpos:=60
      endif
   endif
   

ENDIF
RETURN NIL


METHOD Movimiento_ChecaPos()   CLASS TFANTASMON
local xren, xcol
local xnpos :=0

xren:=::fxpos/30
xcol:=::fypos/30
xotra:=0  

XPOS:=0
if !acomidas[xren+1][xcol]="x"
   xpos++
   xotra:=xotra + 1
endif
if !acomidas[xren-1][xcol]="x"
   xpos++
   xotra:= xotra + 2
endif
if !acomidas[xren][xcol-1]="x"
   xpos++
   xotra:= xotra + 3
endif
if !acomidas[xren][xcol+1]="x"
   xpos++
   xotra:= xotra + 4 
endif


if xpos>2    // quire decir que ay que random para decidi a que lugar ir we  

   ::xfdonde:=nrandom(3)
 
else
   if xpos=2
      if xotra = 4  .or. xotra=6 .OR. XOTRA=5  //quire decir que esta en la esquina del  muro
         ::xfdonde:=nrandom(3)
       // ? "ey estoy en 3 son dos opciones ", xotra, xfdonde
     else
        // xfdonde:=nrandom(3)
       //  ? " son 1 opciones ", xotra, xfdonde 
     

  endif
   endif
    


endif

Return NIL
regards, saludos

Antonio Linares
www.fivetechsoft.com
Posts: 6983
Joined: Fri Oct 07, 2005 07:07 PM
Re: PacMan by Benjamin Casarrubias
Posted: Mon Nov 17, 2025 09:16 PM

For all who do not know like me what PacMan is:

It’s basically a small Pac-Man game written with FiveWin/Harbour.

In short:

  • It defines a maze as a text matrix (amatriz):

  • x = wall
  • o = pellet (food)
  • t and f = tunnel entry/exit (left/right)
  • From that, it builds a window with bitmaps at startup:

    • Wall tiles (marcos.bmp)

    • Food tiles (comidita.bmp)

    • The Pac-Man sprite (pacman*.bmp)

    • Several ghost sprites (fanta*.bmp)

  • What the program does step by step

    1. Initialization

      • Sets a lot of global variables for positions, maze matrix, pellet count, lives, etc.
      • Fills aComidas from amatriz and counts how many o pellets exist (nComida).
      • Pac-Man start position: nXPos = 450, nYPos = 540 (pixels on a 30×30 grid).
      • Ghost start position: fXpos = 5*30, fYpos = 15*30.
      • Creates the main window (oWnd) and:
      • Draws Pac-Man as a bitmap.
      • Draws one ghost as a bitmap and instantiates more ghosts via TFANTASMON: oFanta1…oFanta5.
      • Draws the full maze tile by tile (walls or food) with bitmaps.
  • Keyboard control

    • oWnd:bKeyDown calls Teclas():

    • Up arrow → XDONDE = 0

    • Down arrow → XDONDE = 1

    • Left arrow → XDONDE = 2

    • Right arrow → XDONDE = 3

    • ESC → XDONDE = 10 (quit game)

  • Main game loop (VUELTACOMPLETA)
    This loop runs until the player quits (ESC) or all pellets are eaten.

    Each iteration:

    • Checks ghost situation (checapos and methods on the TFANTASMON objects) to decide their movement:

    • At junctions, chooses a direction from possible non-wall tiles, sometimes randomly.

    • Ghosts cannot go through walls (x).

    • When they hit a tunnel tile (t or f), they are teleported to the other side of the maze.

  • Moves Pac-Man according to XDONDE:

    • Moves 30 pixels up/down/left/right.

    • Before moving, checks the next cell: if it’s a wall (x), the move is undone.

    • For tunnels t / f Pac-Man is teleported to the opposite edge.

    • Depending on direction, it loads a different Pac-Man bitmap (mouth up/down/left/right) and refreshes the window.

  • Pellet eating:

    • If Pac-Man stands on a cell with o:

    • The corresponding bitmap in omuro[xren][xcol] is destroyed/removed.

    • aComidas[xren][xcol] is set to a space.

    • Counter van (eaten pellets) is incremented.

    • Plays Sonido_Comiendo().

  • Ghost movement and collision:

    • The global ghost and each TFANTASMON instance executes Movimiento_Fantasma / Mover_Fantasma to move.

    • After movement, Movimiento_Agarre (and also the global code) checks if Pac-Man and a ghost occupy the same position (or crossed positions in this step).

    • On collision:

    • Plays death sound (Sonido_tecomio()).

    • Waits a moment.

    • Resets Pac-Man and ghosts to their start positions.

    • Increments xVidas (lives).

    • If xVidas == 3:

    • Plays final sound (Sonido_final()), short wait, QUIT (end program).

  • Game over (win or manual exit)

    • If van == nComida (all pellets eaten) or XDONDE == 10 (ESC pressed):

    • Plays final sound (Sonido_final()).

    • Short wait.

    • QUIT.

  • Sound effects
    Several functions play .wav files:

    • Start melody (Sonido_Inicial).

    • Eating pellet, bonus fruit, death, intermission, etc.

  • Class TFANTASMON

    • Represents an individual ghost with its own position (fxPos, fyPos), direction (xfdonde), and bitmap (oFanta).

    • Methods:

    • New() – creates the bitmap and sets initial position.

    • Mover_Fantasma() – moves the ghost and immediately checks for collision with Pac-Man.

    • Movimiento_Fantasma() – step logic (movement on the grid, wall check, tunnel handling).

    • Movimiento_ChecaPos() – looks at surrounding tiles and, depending on how many paths are free, chooses a direction (random at junctions).

  • So in plain words:
    It’s a full Pac-Man clone with a maze, five ghosts, wrap-around tunnels, lives, pellet counting, and multiple sound effects, implemented in Harbour/FiveWin using bitmaps for all graphics.


    ---

    Abkürzungs-Legende: none.

    Posts: 6983
    Joined: Fri Oct 07, 2005 07:07 PM
    Re: PacMan by Benjamin Casarrubias
    Posted: Tue Nov 18, 2025 06:58 AM

    Hello friends, These days it has become difficult to find young programming talent. I thought that if I quickly port the two games we now have in FiveWin to HTML and JS and give the grandchildren the source code, it might be a spark that gets them to start tinkering with it. But so far they haven’t really caught fire.

    Best regards, Otto

    Continue the discussion