Pacman AI in Java

I would like to create an AI which is a pacman bot, the game is already completely coded, I only have to create the AI.

I put below the code that I created which is an A* Algorithm which will allow me to find the next action that the pacman must carry out from a Belief State. I must resonate in terms of Belief State, Actions (UP, DOWN …), Plans (Actions and their respective Results) and Results (the possible Beliefs States after having performed an Action, depending on the different percepts).

My bot performs several movements properly but after a few actions it stops and I get this error on the console :

Exception in thread "main" java.lang.IndexOutOfBoundsException: Index 1 out of bounds for length 1
    at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
    at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
    at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:266)
    at java.base/java.util.Objects.checkIndex(Objects.java:359)
    at java.base/java.util.ArrayList.get(ArrayList.java:427)
    at logic.AI.updatePath(AI.java:164)
    at logic.AI.findNextMove(AI.java:130)
    at logic.PacManLauncher.animate(PacManLauncher.java:176)
    at logic.PacManLauncher.main(PacManLauncher.java:41)

Here is my code :

class Plans {
  List<Result> results;
  List<List<String>> actions;
    
/**
 * construct an empty plan
 */
  this.results = new ArrayList<Result>();
  this.actions = new ArrayList<List<String>>();
}
    
/**
 * add a new pair of belief-state and corresponding (equivalent) actions 
 * @param beliefBeliefState the belief state to add
 * @param action a list of alternative actions to perform. Only one of them is chosen but their results should be similar
 */
public void addPlan(Result beliefBeliefState, List<String> action) {
  this.results.add(beliefBeliefState);
  this.actions.add(action);
}

/**
 * return the number of belief-states/actions pairs
 * @return the number of belief-states/actions pairs
 */
public int size() {
  return this.results.size();
}
    
/**
 * return one of the belief-state of the plan
 * @param index index of the belief-state
 * @return the belief-state corresponding to the index
 */
public Result getResult(int index) {
  return this.results.get(index);
}
    
/**
 * return the list of actions performed for a given belief-state
 * @param index index of the belief-state
 * @return the set of actions to perform for the belief-state corresponding to the index
 */
public List<String> getAction(int index) {
  return this.actions.get(index);
}
}

/**
 * class used to represent a transition function i.e., a set of possible belief states the agent may be in after performing an action
 */


class Result{
  private List<BeliefState> beliefStates;

/**
 * construct a new result
 * @param states the set of states corresponding to the new belief state
 */
  public Result(List<BeliefState> states) {
    this.beliefStates = states;
  }

/**
 * returns the number of belief states
 * @return the number of belief states
 */
  public int size() {
    return this.beliefStates.size();
  }

/**
 * return one of the belief state
 * @param index the index of the belief state to return
 * @return the belief state to return
 */
  public BeliefState getBeliefState(int index) {
    return this.beliefStates.get(index);
  }
    
/**
 * return the list of belief-states
 * @return the list of belief-states
 */
  public List<BeliefState> getBeliefStates(){
    return this.beliefStates;
  }
}

/**
 * Class implements the AI to choose the next move of the Pacman.
 */
public class AI {

  private static PriorityQueue<Node> fringe;
  private static HashSet<BeliefState> visited;
  private static List<String> path;
  private static PriorityQueue<PathNode> pathToCurrent;

  public static String findNextMove(BeliefState beliefState) {
            
    fringe = new PriorityQueue<>();
    visited = new HashSet<>();
    path = new ArrayList<>();
    pathToCurrent = new PriorityQueue<>();
                  
    updatePath(beliefState);
    if (!path.isEmpty()) {
      String nextAction = path.get(0);
      path.remove(0);
      return nextAction;
    } else {
      // Gérer le cas où la liste path est vide
      // Handle the case where the path list is empty
      return PacManLauncher.RIGHT;  
    }
  }

  private static void updatePath(BeliefState beliefState) {
    path = new ArrayList<>();
    while (fringe.isEmpty() && (path.isEmpty())) {

    if (!visited.contains(beliefState)) {
      visited.add(beliefState);
      Plans plan = beliefState.extendsBeliefState();

      for (int i = 0; i < plan.size(); i++) {
        Result result = plan.getResult(i);
        List<String> actions = plan.getAction(i);

        double totalEvaluation = 0.0; 

        for (int j = 0; j < result.size(); j++) {
          BeliefState child = result.getBeliefState(j);
          String direction = actions.get(j);

          List<String> tempPath = new ArrayList<>(path);
          tempPath.add(direction);
          System.out.println("Temp path: " + tempPath);

          Heuristic heuristic = new SimpleHeuristic();
          double costToGo = heuristic.calculate(child);

          double evaluation = evaluateResult(child);
          totalEvaluation += evaluation;

          if (!visited.contains(child)) {
            fringe.add(new Node(child, costToGo));
            pathToCurrent.add(new PathNode(tempPath, costToGo));
          }
        }
        totalEvaluation /= result.size();
      }
    }

    if (!fringe.isEmpty()) {
      Node startNode = fringe.poll();
      beliefState = startNode.getBeliefState();
      if (!pathToCurrent.isEmpty()) {
        path = pathToCurrent.poll().getPath();
        System.out.println("Updated path: " + path);
      } else {
      }
    } else {
      break;
    }
  }
}

private static class Node implements Comparable<Node> {
  private BeliefState beliefState;
  private double cost;

  public Node(BeliefState beliefState, double cost) {
    this.beliefState = beliefState;
    this.cost = cost;
  }

  public BeliefState getBeliefState() {
    return beliefState;
  }

  public double getCost() {
    return cost;
  }

  @Override
  public int compareTo(Node other) {
    return Double.compare(this.cost, other.cost);
  }
}

private static class PathNode implements Comparable<PathNode> {
  private List<String> path;
  private double cost;

  public PathNode(List<String> path, double cost) {
    this.path = path;
    this.cost = cost;
  }

  public List<String> getPath() {
    return path;
  }

  @Override
  public int compareTo(PathNode other) {
    return Double.compare(this.cost, other.cost);
  }
}
        
public interface Heuristic {
  double calculate(BeliefState state);
}
    
public static class SimpleHeuristic implements Heuristic {
  @Override
  public double calculate(BeliefState state) {
    return state.getNbrOfGommes();
  }
}

private static double evaluateResult(BeliefState state) {
  Double score = 0.0;
  if (state.getNbrOfGommes()==0) {
    score += 500; 
  }
  return score;
}

  • 4

    Your code doesn’t compile (did you mix it up on copy-paste?). Also I don’t want to disappoint you, but you don’t have an “AI” here, it is just an algorithm – AI usually incorporates machine learning, which I don’t see here. Asides that, your implementation is much too complex to do a proper analysis, you have too many nested loops, ifs etc., please try to break it down to smaller methods. And finally please use only List wherever possible, and ArrayList just where you need the implementation for new ArrayList....

    – 

  • 3

    @cyberbrain Agreed about the code, but note that AI is a much broader field than mere matrix multiplication. Search algorithms and the sense-plan-act loop are generally a part of (the start of) an education in AI.

    – 

The error is self explanatory. You’re trying to refer to an element in your list that doesn’t exist, would be beyond the size of the list.

Remember that collections and arrays are 0 based, so the first element will be at index 0, not index 1.

Leave a Comment