Well we got everything working for some generic ghost. Now let's add in three more ghosts and give them all names. We'll also make all four ghosts unique in their own way. The four ghosts are named Blinky, Pinky, Inky, and Clyde. We can name our ghosts whatever we want but we'll stick to history. The only visual difference between the ghosts is their color. Blinky is RED, Pinky is PINK, Inky is TEAL, and Clyde is ORANGE. Again, we can make them any color we want, but we'll stick to how they originally were more or less. We'll initially deal with the ghosts as a whole, then get more intimate and deal with each one individually.
In the constants.py file we'll start by simply adding a few more colors. These colors will represent the other three ghosts. We'll also add some name constants.
We are going to create a class for each ghost and we are going to put all of the classes in the ghosts.py file. Place these classes after the Ghost class since these classes will be inheriting from the Ghost class.
We'll start with Blinky since he's essentially the ghost we've been using all along. So his class is really simple.
Pinky is different in that his/her scatter goal is in the upper left corner and his/her chase goal is a bit more complicated. Unlike Blinky we don't just simply chase Pacman. We find out where Pacman is and then target 4 tiles ahead of him. That way it seems Pinky is trying to take him head on. If you combine both Blinky and Pinky then it seems as if they are working together to sandwich Pacman.
Inky's scatter goal is in the lower right corner of the maze. His chase goal is even more complicated that both Blinky's and Pinky's. In fact, Inky needs to know the position of Pacman and Blinky in order to calculate his chase goal.
What we do is find the position that's 2 tiles in front of Pacman, then we subtract Blinky's position and multiply the result by 2. We then add that result to Blinky's position. It's kind of hard to explain without a diagram, so try writing it out and you'll see how it works.
Clyde is interesting. First off his scatter goal is in the bottom left corner of the maze.
His chase goal changes depending on how close he is to Pacman. So we first need to determine how far away he is from Pacman. If he's less than 8 tiles away from Pacman, then retreats to his scatter goal. When he's far away from Pacman then he changes his mind and acts like Pinky.
It's better if we deal with the ghosts as a group rather than individually. It'll make it so we write less code which is always good. So we'll create a GhostGroup class in the ghosts.py file after all of the classes we just made.
We're going to store all of the ghost objects in a list. The __iter__ method allows us to loop through the ghost list in a convenient fashion. Using this we can say something like: "for ghost in self:" rather than "for ghost in self.ghosts:". Ok, so maybe it's not that convenient and not necessary, but it's good to know that it exists, right?
So in all of these methods we just loop through the ghosts list and perform the action to each ghost. Notice the updatePoints and resetPoints methods. So when Pacman eats a ghost he gets 200 points, when he eats the second ghost he gets 400 points, then 800 points, and finally 1600 points. Basically, the points a ghost is worth doubles every time he eats one. When Pacman eats a new power pellet those points reset back to 200. So to maximize your score you want to eat all of the ghosts for each 4 power pellets.
The first (of many) changes to make in the run.py file is to import the GhostGroup class instead of the Ghost class.
In the startGame method instead of creating self.ghost from the Ghost class we'll create self.ghosts from the GhostGroup class.
In the checkGhostEvents method, before we passed in the one ghost we had to Pacman to see if they were colliding. Now we'll have to pass in the ghosts object and Pacman will return the ghost he's colliding with instead of just returning True or False. If he isn't colliding with any ghosts, then he just returns None. We'll modify Pacman's method in a bit. Also notice the two cases of changing from self.ghost to just ghost.
Finally, in the update, checkPelletEvents, and render methods we simply change from using ghost to ghosts.