An implementation of PacMan, using JavaScript
Play online here: https://mickyginger.github.io/pacman/
- HTML5
- CSS3
- ES6
- Lodash
I decided to make a generic Character
class which knew only how to move and whether a move was valid. I then created a Pacman
class which inherited Character
where I added a keydown
event listener so the user could move PacMan.
I then created a Ghost
class which also inherited from Character
. This class includes all the logic to make the ghosts move.
Both characters move around the grid by swapping classes on div
elements. The whole grid is an array of divs, and the movement is calculated using the index of the div
in the array. I used a 28 x 28 grid. To find the div
that the character wants to move to I used the following formula:
- NORTH: character index - width
- SOUTH: character index + width
- EAST: character index + 1
- WEST: character index - 1
I prevented characters walking through walls, I checked for the class of wall
on the div
before making the move.
The ghost movement is split into three modes:
- Hunt mode: The ghost is moving toward PacMan
- Flee mode: The ghost is moving away from PacMan
- Nest mode: The ghost is moving toward its start position
Essentially they are all using the same functionality. In hunt mode the x and y coordinate of the ghost and PacMan compared and a random move is made that would bring the ghost closer to PacMan's location.
In flee mode the same logic is used, except this time a random move is made that would take the ghost farther away from PacMan
Finally in nest mode the random move is made that would bring the ghost closer to its home square.
To determine whether the ghost's move is closer or further away from its destination I wrote a simple isCloser
method, which took the current index, and the destination index.
getCoords(index) {
return [index % this.gridWidth, Math.floor(index / this.gridWidth)]
}
isCloser(index, point) {
const [pointX, pointY] = this.getCoords(point)
const [posX, posY] = this.getCoords(this.position)
const [nextX, nextY] = this.getCoords(index)
return Math.abs(nextX - pointX) > Math.abs(posX - pointX) ||
Math.abs(nextY - pointY) > Math.abs(posY - pointY)
}
The most challenging problem was the ghost movement. In earlier iterations of the game ghosts would get stuck in the corners or move backward and forward on the same two squares.
Since the path that the ghosts can move along is only one square wide, often the ghost can only make a choice of north/south or east/west. When a ghost reaches a junction it might have two or three options.
In the end the solution to both problems was a simple one: do not allow the ghost to double back on itself. This way the ghost will never get stuck in a corner, nor will it move back and forth between two squares.
Please fork the repo and make a pull request.
If you'd rather just take the code and develop it please credit me and drop me a link to your repo, I'd love to take a look!