// AI revision 2 //This version is fully commented #include "common.h" #include "pacman.h" #include Maze * Pacman::maze = NULL; const int Pacman::NUM_PERSONALITIES = 8; extern double drand48(); bool Pacman::move () { /*This function determines what function to call for which pacman personality*/ switch (personality) { case Straighty: return straighty (); case Psycho: return psycho (); case Fraidy: return fraidy (); case DotEater: return dotEater(); case DotHater: return dotHater(); case WallFollowerLeft: return wallFollowerLeft(); case WallFollowerRight: return wallFollowerRight(); case WallFollowerRandom: return wallFollowerRandom(); } } bool Pacman::deathCheck () { /*This function determines whether a pacman is dead*/ if (maze->ghostAt(x, y)) { die(); return true; } return false; } void Pacman::newGame () { /*This function initializes a new game*/ lastDirection=randomDirection(); consec = 0; alive = true; Location* temp = maze->getPacmanStart(); x = temp->x; y = temp->y; } void Pacman::newGeneration () { /*This function initializes a number of randomly selected pacman personalities before each generation*/ newPersonality(); } bool Pacman::isAlive () { /*This function determines if a pacman is alive*/ return alive; } //Pacman AI bool Pacman::dotEater() { /*Basically, the pacman seeks out the closest dot. If there are no dots in any direction, the pacman will continue in one direction until there are. If it arises that the number of squares to the closest dot is the same in two or more directions, the routine will first check the number of dots in the directions (favoring higher numbers), then, if those are the same, it will move randomly between the same directions.*/ if (ghostInSight()) { fraidy(); if (fraidy()) return true; else return false; } if (initCheck() == 4) { die(); return false; } int small = initCheck(); int chance [4]; int i; if (maze->squaresToDot(0,x,y) == Maze::NUM_SQUARES && maze->squaresToDot(1,x,y) == Maze::NUM_SQUARES && maze->squaresToDot(2,x,y) == Maze::NUM_SQUARES && maze->squaresToDot(3,x,y) == Maze::NUM_SQUARES && directionOK(lastDirection)) { makeMove(lastDirection); return true; } randomArray(chance); for (i = small + 1; i <= 3; i++) if ((maze->squaresToDot(i,x,y) < maze->squaresToDot(small,x,y) || maze->squaresToDot(i,x,y) == maze->squaresToDot(small,x,y) && maze->dotsInDir(i,x,y) > maze->dotsInDir(small,x,y) || maze->squaresToDot(i,x,y) == maze->squaresToDot(small,x,y) && chance[small] <= chance[i]) && directionOK(i))/*I think this funky parenthesis stuff is legal*/ small = i; makeMove(small); return true; } bool Pacman::dotHater() { /*This routine is basically the opposite of the dotEater, the main difference being that if there are no dots in any direction, it moves randomly.*/ if (ghostInSight()) { return fraidy(); } if (initCheck() == 4) { die(); return false; } int big = initCheck(); int chance [4]; int i; randomArray(chance); for (i = big + 1; i <= 3; i++) if ((maze->squaresToDot(i,x,y) > maze->squaresToDot(big,x,y) || maze->squaresToDot(i,x,y) == maze->squaresToDot(big,x,y) && chance[big] <= chance[i]) && directionOK(i)) big = i; makeMove(big); return true; } bool Pacman::psycho() { if (initCheck() == 4) { die(); return false; } int directionToMove = initCheck(); int chance [4]; int i; if (ghostInSight == false && directionOK(lastDirection)) { makeMove(lastDirection); return true; } randomArray(chance); if (psychoOK(0) == false && psychoOK(1) == false && psychoOK(2) == false && psychoOK(3) == false) { for (i = directionToMove + 1; i <= 3; i++) if (directionOK(i) && chance[i] > chance[directionToMove]) directionToMove = i; makeMove(directionToMove); return true; } for (i = directionToMove + 1; i <= 3; i++) if ((maze->squaresToGhost(i,x,y) < maze->squaresToGhost(directionToMove,x,y) || maze->squaresToGhost(i,x,y) == maze->squaresToGhost(directionToMove,x,y) && maze->ghostsInDir(i,x,y) < maze->ghostsInDir(directionToMove,x,y) || maze->squaresToGhost(i,x,y) == maze->squaresToGhost(directionToMove,x,y) && chance[directionToMove] <= chance[i]) && psychoOK(i)) directionToMove = i; makeMove(directionToMove); return true; } bool Pacman::fraidy() { /*This routine is similar to dotHater except it avoids ghosts instead of dots.*/ if (initCheck() == 4) { die(); return false; } int directionToMove = initCheck(); int chance [4]; int i; randomArray(chance); for (i = directionToMove + 1; i <= 3; i++) if ((maze->squaresToGhost(i,x,y) > maze->squaresToGhost(directionToMove,x,y) || maze->squaresToGhost(i,x,y) == maze->squaresToGhost(directionToMove,x,y) && maze->ghostsInDir(i,x,y) < maze->ghostsInDir(directionToMove,x,y) || maze->squaresToGhost(i,x,y) == maze->squaresToGhost(directionToMove,x,y) && chance[directionToMove] <= chance[i]) && directionOK(i)) directionToMove = i; makeMove(directionToMove); return true; } bool Pacman::straighty() { /*This routine continues in one direction until it hits a wall. It then turns in the direction with the most squares. This is based on Ryan's pseudocode.*/ if (ghostInSight()) { return fraidy(); } if (initCheck() == 4) { die(); return false; } if (directionOK(lastDirection)) { makeMove (lastDirection); return true; } else if (maze->squaresToWall(nextDir(lastDirection),x,y) > maze->squaresToWall(prevDir(lastDirection),x,y)) { makeMove(nextDir(lastDirection)); return true; } else { makeMove(prevDir(lastDirection)); return true; } } bool Pacman::wallFollowerRight() { /*This is Ryan's routine. It follows the right side of walls.*/ if (ghostInSight()) { return fraidy(); } if (initCheck() == 4) { die(); return false; } if (directionOK(nextDir(lastDirection))) { makeMove(nextDir(lastDirection)); return true; } else if (directionOK(lastDirection)) { makeMove(lastDirection); return true; } else if (directionOK(prevDir(lastDirection))) { makeMove(prevDir(lastDirection)); return true; } else { makeMove(backDir(lastDirection)); return true; } } bool Pacman::wallFollowerLeft() { /*This routine folows the left side of walls.*/ if (ghostInSight()) { return fraidy(); } if (initCheck() == 4) { die(); return false; } if (directionOK(prevDir(lastDirection))) { makeMove(lastDirection); return true; } else if (directionOK(nextDir(lastDirection))) { makeMove(nextDir(lastDirection)); return true; } else { makeMove(backDir(lastDirection)); return true; } } bool Pacman::wallFollowerRandom() { /*My version randomly picks the direction to turn at junctions*/ if (ghostInSight()) { return fraidy(); } if (initCheck() == 4) { die(); return false; } int chance; chance = int(drand48() * 2); if (chance == 0 && wallFollowerRight()) { return wallFollowerRight(); } else if (chance == 1 && wallFollowerLeft()) { return wallFollowerLeft(); } else { die(); return false; } } void Pacman::newPersonality () { /*This chooses the random pacman personality*/ personality = Personality(int(drand48()) * NUM_PERSONALITIES); } void Pacman::die () { /*This function kills the pacman*/ alive = false; } bool Pacman::directionOK(int dir) { /*This function returns true or false depending on whether the direction is legal*/ return maze->pacmanMoveOK(x + xChange(dir), y + yChange(dir)); } void Pacman::makeMove(int dir) { /*This function allows the pacmen to move*/ if (lastDirection == dir) { consec++; } else { lastDirection = dir; consec = 1; } int ox = x, oy = y; maze->movePacman(ox, oy, x += xChange(dir), y += yChange(dir)); } //Pacman AI functions bool Pacman::psychoOK(int direction) { //This function is a special directionOK for psycho. if (maze->squaresToGhost(direction,x,y) <= 2 || directionOK(direction) == false) return false; else return true; } int Pacman::initCheck() { /*This function returns the value of the first direction with directionOK. It is also used to determine if the pacman has any legal moves. If it doesn't have any legal moves, it returns 4.*/ for(int i = 0; i <= 3; i++) if (directionOK(i)) return i; return 4; } bool Pacman::ghostInSight() { /*This function returns true if there is a ghost within sight of the pacman and false if there are no ghosts in sight.*/ if (maze->squaresToGhost(0,x,y) == Maze::NUM_SQUARES && maze->squaresToGhost(1,x,y) == Maze::NUM_SQUARES && maze->squaresToGhost(2,x,y) == Maze::NUM_SQUARES && maze->squaresToGhost(3,x,y) == Maze::NUM_SQUARES) return false; else return true; } void Pacman::randomArray(int chance[4]) { /*This function stores an array of 4 random numbers*/ for (int i = 0; i <= 3; i++) chance[i] = int(drand48() * 1000); }