All Downloads are FREE. Search and download functionalities are using the official Maven repository.

pacman.Executor Maven / Gradle / Ivy

The newest version!
package pacman;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import pacman.controllers.Controller;
import pacman.controllers.HumanController;
import pacman.controllers.MASController;
import pacman.game.Drawable;
import pacman.game.Game;
import pacman.game.GameView;
import pacman.game.comms.BasicMessenger;
import pacman.game.comms.Messenger;
import pacman.game.internal.POType;
import pacman.game.util.Stats;

import java.io.*;
import java.util.ArrayList;
import java.util.Random;
import java.util.function.Function;

import static pacman.game.Constants.*;

/**
 * This class may be used to execute the game in timed or un-timed modes, with or without
 * visuals. Competitors should implement their controllers in game.entries.ghosts and
 * game.entries.pacman respectively. The skeleton classes are already provided. The package
 * structure should not be changed (although you may create sub-packages in these packages).
 */
@SuppressWarnings("unused")
public class Executor {
    private final boolean pacmanPO;
    private final boolean ghostPO;
    private final boolean ghostsMessage;
    private final Messenger messenger;
    private final double scaleFactor;
    private final boolean setDaemon;
    private final boolean visuals;
    private final int tickLimit;
    private final int timeLimit;
    private final POType poType;
    private final int sightLimit;
    private final Random rnd = new Random();
    private final Function peek;
    private final Logger logger = LoggerFactory.getLogger(Executor.class);

    public static class Builder {
        private boolean pacmanPO = true;
        private boolean ghostPO = true;
        private boolean ghostsMessage = true;
        private Messenger messenger = new BasicMessenger();
        private double scaleFactor = 1.0d;
        private boolean setDaemon = false;
        private boolean visuals = false;
        private int tickLimit = 4000;
        private int timeLimit = 40;
        private POType poType = POType.LOS;
        private int sightLimit = 50;
        private Function peek = null;

        public Builder setPacmanPO(boolean po) {
            this.pacmanPO = po;
            return this;
        }

        public Builder setGhostPO(boolean po) {
            this.ghostPO = po;
            return this;
        }

        public Builder setGhostsMessage(boolean canMessage) {
            this.ghostsMessage = canMessage;
            if (canMessage) {
                messenger = new BasicMessenger();
            } else {
                messenger = null;
            }
            return this;
        }

        public Builder setMessenger(Messenger messenger) {
            this.ghostsMessage = true;
            this.messenger = messenger;
            return this;
        }

        public Builder setScaleFactor(double scaleFactor) {
            this.scaleFactor = scaleFactor;
            return this;
        }

        public Builder setGraphicsDaemon(boolean daemon) {
            this.setDaemon = daemon;
            return this;
        }

        public Builder setVisual(boolean visual) {
            this.visuals = visual;
            return this;
        }

        public Builder setTickLimit(int tickLimit) {
            this.tickLimit = tickLimit;
            return this;
        }

        public Builder setTimeLimit(int timeLimit) {
            this.timeLimit = timeLimit;
            return this;
        }

        public Builder setPOType(POType poType) {
            this.poType = poType;
            return this;
        }

        public Builder setSightLimit(int sightLimit) {
            this.sightLimit = sightLimit;
            return this;
        }

        public Builder setPeek(Function peek){
            this.peek = peek;
            return this;
        }

        public Executor build() {
            return new Executor(pacmanPO, ghostPO, ghostsMessage, messenger, scaleFactor, setDaemon, visuals, tickLimit, timeLimit, poType, sightLimit, peek);
        }
    }

    private Executor(
            boolean pacmanPO,
            boolean ghostPO,
            boolean ghostsMessage,
            Messenger messenger,
            double scaleFactor,
            boolean setDaemon,
            boolean visuals,
            int tickLimit,
            int timeLimit,
            POType poType,
            int sightLimit,
            Function peek
            ) {
        this.pacmanPO = pacmanPO;
        this.ghostPO = ghostPO;
        this.ghostsMessage = ghostsMessage;
        this.messenger = messenger;
        this.scaleFactor = scaleFactor;
        this.setDaemon = setDaemon;
        this.visuals = visuals;
        this.tickLimit = tickLimit;
        this.timeLimit = timeLimit;
        this.poType = poType;
        this.sightLimit = sightLimit;
        this.peek = peek;
    }

    private static void writeStat(FileWriter writer, Stats stat, int i) throws IOException {
        writer.write(String.format("%s, %d, %f, %f, %f, %f, %d, %f, %f, %f, %d%n",
                stat.getDescription(),
                i,
                stat.getAverage(),
                stat.getSum(),
                stat.getSumsq(),
                stat.getStandardDeviation(),
                stat.getN(),
                stat.getMin(),
                stat.getMax(),
                stat.getStandardError(),
                stat.getMsTaken()));
    }

    //save file for replays
    public static void saveToFile(String data, String name, boolean append) {
        try (FileOutputStream outS = new FileOutputStream(name, append)) {
            PrintWriter pw = new PrintWriter(outS);

            pw.println(data);
            pw.flush();
            outS.close();

        } catch (IOException e) {
            System.out.println("Could not save data!");
        }
    }

    //load a replay
    private static ArrayList loadReplay(String fileName) {
        ArrayList replay = new ArrayList();

        try (BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(fileName)))) {
            String input = br.readLine();

            while (input != null) {
                if (!input.equals("")) {
                    replay.add(input);
                }

                input = br.readLine();
            }
        } catch (IOException ioe) {
            ioe.printStackTrace();
        }

        return replay;
    }

    /**
     * For running multiple games without visuals. This is useful to get a good idea of how well a controller plays
     * against a chosen opponent: the random nature of the game means that performance can vary from game to game.
     * Running many games and looking at the average score (and standard deviation/error) helps to get a better
     * idea of how well the controller is likely to do in the competition.
     *
     * @param pacManController The Pac-Man controller
     * @param ghostController  The Ghosts controller
     * @param trials           The number of trials to be executed
     * @param description      Description for the stats
     * @return Stats[] containing the scores in index 0 and the ticks in position 1
     */
    public Stats[] runExperiment(Controller pacManController, MASController ghostController, int trials, String description) {
        Stats stats = new Stats(description);
        Stats ticks = new Stats(description + " Ticks");
        MASController ghostControllerCopy = ghostController.copy(ghostPO);
        Game game;

        Long startTime = System.currentTimeMillis();
        for (int i = 0; i < trials; ) {
            try {
                game = setupGame();

                while (!game.gameOver()) {
                    if (tickLimit != -1 && tickLimit < game.getTotalTime()) {
                        break;
                    }
                    handlePeek(game);
                    game.advanceGame(
                            pacManController.getMove(getPacmanCopy(game), System.currentTimeMillis() + timeLimit),
                            ghostControllerCopy.getMove(game.copy(), System.currentTimeMillis() + timeLimit));
                }
                stats.add(game.getScore());
                ticks.add(game.getCurrentLevelTime());
                i++;
                System.out.println("Game finished: " + i + "   " + description);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        long timeTaken = System.currentTimeMillis() - startTime;
        stats.setMsTaken(timeTaken);
        ticks.setMsTaken(timeTaken);

        return new Stats[]{stats, ticks};
    }

    private Game setupGame() {
        return (this.ghostsMessage) ? new Game(rnd.nextLong(), 0, messenger.copy(), poType, sightLimit) : new Game(rnd.nextLong(), 0, null, poType, sightLimit);
    }

    private void handlePeek(Game game){
        if(peek != null) logger.info(peek.apply(game));
    }

    public Stats[] runExperimentTicks(Controller pacManController, MASController ghostController, int trials, String description) {
        Stats stats = new Stats(description);
        Stats ticks = new Stats(description);

        MASController ghostControllerCopy = ghostController.copy(ghostPO);
        Game game;

        Long startTime = System.currentTimeMillis();
        for (int i = 0; i < trials; i++) {
            game = setupGame();

            while (!game.gameOver()) {
                handlePeek(game);
                game.advanceGame(
                        pacManController.getMove(getPacmanCopy(game), System.currentTimeMillis() + timeLimit),
                        ghostControllerCopy.getMove(game.copy(), System.currentTimeMillis() + timeLimit));
            }
            stats.add(game.getScore());
            ticks.add(game.getTotalTime());
        }
        stats.setMsTaken(System.currentTimeMillis() - startTime);
        ticks.setMsTaken(System.currentTimeMillis() - startTime);

        return new Stats[]{stats, ticks};
    }

    /**
     * Run a game in asynchronous mode: the game waits until a move is returned. In order to slow thing down in case
     * the controllers return very quickly, a time limit can be used. If fasted gameplay is required, this delay
     * should be put as 0.
     *
     * @param pacManController The Pac-Man controller
     * @param ghostController  The Ghosts controller
     * @param delay            The delay between time-steps
     */
    public int runGame(Controller pacManController, MASController ghostController, int delay) {
        Game game = setupGame();

        GameView gv = (visuals) ? setupGameView(pacManController, game) : null;

        MASController ghostControllerCopy = ghostController.copy(ghostPO);

        while (!game.gameOver()) {
            if (tickLimit != -1 && tickLimit < game.getTotalTime()) {
                break;
            }
            handlePeek(game);
            game.advanceGame(
                    pacManController.getMove(getPacmanCopy(game), System.currentTimeMillis() + timeLimit),
                    ghostControllerCopy.getMove(game.copy(), System.currentTimeMillis() + timeLimit));

            try {
                Thread.sleep(delay);
            } catch (Exception e) {
            }

            if (visuals) {
                gv.repaint();
            }
        }
        System.out.println(game.getScore());
        return game.getScore();
    }

    private Game getPacmanCopy(Game game) {
        return game.copy((pacmanPO) ? Game.PACMAN : Game.CLONE);
    }

    private GameView setupGameView(Controller pacManController, Game game) {
        GameView gv;
        gv = new GameView(game, setDaemon);
        gv.setScaleFactor(scaleFactor);
        gv.showGame();
        if (pacManController instanceof HumanController) {
            gv.setFocusable(true);
            gv.requestFocus();
            gv.setPO(true);
            gv.addKeyListener(((HumanController) pacManController).getKeyboardInput());
        }

        if (pacManController instanceof Drawable) {
            gv.addDrawable((Drawable) pacManController);
        }
        return gv;
    }

    /**
     * Run the game with time limit (asynchronous mode).
     * Can be played with and without visual display of game states.
     *
     * @param pacManController The Pac-Man controller
     * @param ghostController  The Ghosts controller
     */
    public void runGameTimed(Controller pacManController, MASController ghostController) {
        Game game = setupGame();

        GameView gv = (visuals) ? setupGameView(pacManController, game) : null;
        MASController ghostControllerCopy = ghostController.copy(ghostPO);

        new Thread(pacManController).start();
        new Thread(ghostControllerCopy).start();

        while (!game.gameOver()) {
            if (tickLimit != -1 && tickLimit < game.getTotalTime()) {
                break;
            }
            handlePeek(game);
            pacManController.update(getPacmanCopy(game), System.currentTimeMillis() + DELAY);
            ghostControllerCopy.update(game.copy(), System.currentTimeMillis() + DELAY);

            try {
                Thread.sleep(DELAY);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            game.advanceGame(pacManController.getMove(), ghostControllerCopy.getMove());

            if (visuals) {
                gv.repaint();
            }
        }

        pacManController.terminate();
        ghostControllerCopy.terminate();
    }

    /**
     * Run the game in asynchronous mode but proceed as soon as both controllers replied. The time limit still applies so
     * so the game will proceed after 40ms regardless of whether the controllers managed to calculate a turn.
     *
     * @param pacManController The Pac-Man controller
     * @param ghostController  The Ghosts controller
     * @param fixedTime        Whether or not to wait until 40ms are up even if both controllers already responded
     * @param desc             the description for the stats
     * @return Stat score achieved by Ms. Pac-Man
     */
    public Stats runGameTimedSpeedOptimised(Controller pacManController, MASController ghostController, boolean fixedTime, String desc) {
        Game game = setupGame();

        GameView gv = (visuals) ? setupGameView(pacManController, game) : null;
        MASController ghostControllerCopy = ghostController.copy(ghostPO);
        Stats stats = new Stats(desc);

        new Thread(pacManController).start();
        new Thread(ghostControllerCopy).start();
        while (!game.gameOver()) {
            if (tickLimit != -1 && tickLimit < game.getTotalTime()) {
                break;
            }
            handlePeek(game);
            pacManController.update(getPacmanCopy(game), System.currentTimeMillis() + DELAY);
            ghostControllerCopy.update(game.copy(), System.currentTimeMillis() + DELAY);

            try {
                long waited = DELAY / INTERVAL_WAIT;

                for (int j = 0; j < DELAY / INTERVAL_WAIT; j++) {
                    Thread.sleep(INTERVAL_WAIT);

                    if (pacManController.hasComputed() && ghostControllerCopy.hasComputed()) {
                        waited = j;
                        break;
                    }
                }

                if (fixedTime) {
                    Thread.sleep(((DELAY / INTERVAL_WAIT) - waited) * INTERVAL_WAIT);
                }

                game.advanceGame(pacManController.getMove(), ghostControllerCopy.getMove());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (visuals) {
                gv.repaint();
            }
        }

        pacManController.terminate();
        ghostControllerCopy.terminate();
        stats.add(game.getScore());
        return stats;
    }

    /**
     * Run a game in asynchronous mode and recorded.
     *
     * @param pacManController The Pac-Man controller
     * @param ghostController  The Ghosts controller
     * @param fileName         The file name of the file that saves the replay
     * @return Stats the statistics for the run
     */
    public Stats runGameTimedRecorded(Controller pacManController, MASController ghostController, String fileName) {
        Stats stats = new Stats("");
        StringBuilder replay = new StringBuilder();

        Game game = setupGame();

        GameView gv = null;
        MASController ghostControllerCopy = ghostController.copy(ghostPO);

        if (visuals) {
            gv = new GameView(game, setDaemon);
            gv.setScaleFactor(scaleFactor);
            gv.showGame();

            if (pacManController instanceof HumanController) {
                gv.getFrame().addKeyListener(((HumanController) pacManController).getKeyboardInput());
            }

            if (pacManController instanceof Drawable) {
                gv.addDrawable((Drawable) pacManController);
            }
        }

        new Thread(pacManController).start();
        new Thread(ghostControllerCopy).start();

        while (!game.gameOver()) {
            if (tickLimit != -1 && tickLimit < game.getTotalTime()) {
                break;
            }
            handlePeek(game);
            pacManController.update(getPacmanCopy(game), System.currentTimeMillis() + DELAY);
            ghostControllerCopy.update(game.copy(), System.currentTimeMillis() + DELAY);

            try {
                Thread.sleep(DELAY);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            game.advanceGame(pacManController.getMove(), ghostControllerCopy.getMove());

            if (visuals) {
                gv.repaint();
            }

            replay.append(game.getGameState() + "\n");
        }
        stats.add(game.getScore());

        pacManController.terminate();
        ghostControllerCopy.terminate();

        saveToFile(replay.toString(), fileName, false);
        return stats;
    }

    /**
     * Replay a previously saved game.
     *
     * @param fileName The file name of the game to be played
     * @param visual   Indicates whether or not to use visuals
     */
    public void replayGame(String fileName, boolean visual) {
        ArrayList timeSteps = loadReplay(fileName);

        Game game = setupGame();

        GameView gv = null;

        if (visual) {
            gv = new GameView(game, setDaemon);
            gv.setScaleFactor(scaleFactor);
            gv.showGame();
        }

        for (int j = 0; j < timeSteps.size(); j++) {
            game.setGameState(timeSteps.get(j));

            try {
                Thread.sleep(DELAY);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            if (visual) {
                gv.repaint();
            }
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy