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

de.hamstersimulator.objectsfirst.external.model.Hamster Maven / Gradle / Ivy

There is a newer version: 5.0.2
Show newest version
package de.hamstersimulator.objectsfirst.external.model;

import static de.hamstersimulator.objectsfirst.utils.Preconditions.checkArgument;
import static de.hamstersimulator.objectsfirst.utils.Preconditions.checkNotNull;
import static de.hamstersimulator.objectsfirst.utils.Preconditions.checkState;

import java.util.Collection;
import java.util.Optional;

import de.hamstersimulator.objectsfirst.datatypes.Direction;
import de.hamstersimulator.objectsfirst.datatypes.Location;
import de.hamstersimulator.objectsfirst.datatypes.LocationVector;
import de.hamstersimulator.objectsfirst.exceptions.FrontBlockedException;
import de.hamstersimulator.objectsfirst.exceptions.MouthEmptyException;
import de.hamstersimulator.objectsfirst.exceptions.NoGrainOnTileException;
import de.hamstersimulator.objectsfirst.internal.model.hamster.GameHamster;
import de.hamstersimulator.objectsfirst.internal.model.hamster.command.specification.InitHamsterCommandSpecification;
import de.hamstersimulator.objectsfirst.internal.model.hamster.command.specification.MoveCommandSpecification;
import de.hamstersimulator.objectsfirst.internal.model.hamster.command.specification.PickGrainCommandSpecification;
import de.hamstersimulator.objectsfirst.internal.model.hamster.command.specification.PutGrainCommandSpecification;
import de.hamstersimulator.objectsfirst.internal.model.hamster.command.specification.TurnLeftCommandSpecification;
import de.hamstersimulator.objectsfirst.internal.model.hamster.command.specification.WriteCommandSpecification;
import de.hamstersimulator.objectsfirst.internal.model.territory.Grain;
import de.hamstersimulator.objectsfirst.internal.model.territory.Tile;
import de.hamstersimulator.objectsfirst.internal.model.territory.TileContent;

/**
 * Class for Hamster in the Hamster Simulator. This class allows to initialize
 * Hamsters, command Hamsters through their territory and query their state.
 *
 * @author Steffen Becker
 *
 */
public class Hamster {

    /**
     * The game this hamster takes part in.
     */
    private HamsterGame game;

    /*@
     @ public model instance boolean isInitialized;
     @ private represents isInitialized = (game != null);
     @ public model instance int grainCount;
     @ private represents grainCount <- internalHamster.getGrainCount();
     @*/
    /**
     * The internal hamster object representing this hamster in the
     * simulator internal model.
     */
    private final GameHamster internalHamster;

    /**
     * Create a new Hamster object without initializing it.
     */
    public Hamster() {
        super();
        this.internalHamster = new GameHamster();
    }


    /*@
    @ requires territory != null;
    @ requires location != null;
    @ requires territory.isLocationInTerritory(location);
    @ requires ! territory.isBlockedByWall(location);
    @ requires newGrainCount >= 0;
    @ requires direction != null;
    @ ensures isInitialized;
    @ ensures grainCount == newGrainCount;
    @*/
    /**
     * Create and initialize a new hamster object with the given parameters.
     * @param territory The territory this hamster lives in.
     * @param location The location in the territory, where the hamster starts.
     * @param direction The direction in which the hamster looks initially.
     * @param newGrainCount The number of grain objects initially placed into the
     *        hamster's mouth.
     */
    public Hamster(final Territory territory, final Location location,
                   final Direction direction, final int newGrainCount) {
        this();
        init(territory, location, direction, newGrainCount);
    }

    /**
     * Internal constructor, used for the default hamster of a territory.
     * @param territory The territory for which the default hamster should be
     *                  initialized.
     */
    private Hamster(final Territory territory) {
        super();
        this.game = territory.getGame();
        this.internalHamster = territory.getInternalTerritory().getDefaultHamster();
        territory.registerHamster(this, this.internalHamster);
    }

    /*
     * Commands
     */
    /*@
     @ requires territory != null;
     @ requires location != null;
     @ requires territory.isLocationInTerritory(location);
     @ requires ! territory.isBlockedByWall(location);
     @ requires newGrainCount >= 0;
     @ requires direction != null;
     @ ensures isInitialized;
     @ ensures grainCount == newGrainCount;
     @*/
    /**
     * Initialize a hamster object with the given parameters. Once a hamster
     * is initialized, the method must not be called again.
     * @param territory The territory this hamster lives in.
     * @param location The location in the territory, where the hamster starts.
     * @param direction The direction in which the hamster looks initially.
     * @param newGrainCount The number of grain objects initially placed into
     *                   the hamster's mouth
     */
    public void init(final Territory territory, final Location location,
                     final Direction direction, final int newGrainCount) {
        checkNotNull(territory);
        checkNotNull(location);
        checkNotNull(direction);
        checkArgument(newGrainCount >= 0);
        checkState(this.game == null, "Hamster is already initialized!");
        this.game = territory.getGame();
        this.game.processCommandSpecification(
                new InitHamsterCommandSpecification(
                        this.internalHamster,
                        territory.getInternalTerritory(),
                        location,
                        direction,
                        newGrainCount));
        territory.registerHamster(this, this.internalHamster);
    }


    /*@
     @ requires frontIsClear();
     @ requires isInitialized;
     @ ensures getDirection() == Direction.NORTH ==>
     @      \old(getLocation()).getRow() == getLocation().getRow() + 1 &&
     @      \old(getLocation()).getColumn() == getLocation().getColumn();
     @ ensures getDirection() == Direction.SOUTH ==>
     @      \old(getLocation()).getRow() == getLocation().getRow() - 1 &&
     @      \old(getLocation()).getColumn() == getLocation().getColumn();
     @ ensures getDirection() == Direction.EAST ==>
     @      \old(getLocation()).getRow() == getLocation().getRow() &&
     @      \old(getLocation()).getColumn() == getLocation().getColumn() - 1;
     @ ensures getDirection() == Direction.WEST ==>
     @      \old(getLocation()).getRow() == getLocation().getRow() &&
     @      \old(getLocation()).getColumn() == getLocation().getColumn() + 1;
     @*/
    /**
     * Move the hamster one step towards its looking direction.
     * @throws FrontBlockedException When the tile in front of the hamster is blocked
     */
    public void move() {
        this.game.processCommandSpecification(
                new MoveCommandSpecification(this.internalHamster));
    }

    /**
     * Changes the looking direction 90 degrees to the left, e.g.,
     * when looking north the hamster will look towards west afterwards.
     */
    public void turnLeft() {
        this.game.processCommandSpecification(
                new TurnLeftCommandSpecification(this.internalHamster));
    }

    /*@
     @ requires grainAvailable();
     @ requires isInitialized;
     @ ensures grainCount == \old(grainCount) + 1;
     @*/
    /**
     * Pick up a random grain from the tile on which the hamster is
     * currently.
     * @throws NoGrainOnTileException when no more grains are available on
     *         the hamster's tile.
     */
    public void pickGrain() {
        final Tile currentTile = this.internalHamster.getCurrentTile().get();
        final Grain grain = getRandomGrainFromTile(currentTile);
        if (grain == null) {
            throw new NoGrainOnTileException();
        }
        this.game.processCommandSpecification(
                new PickGrainCommandSpecification(this.internalHamster, grain));
    }

    /*@
    @ requires ! mouthEmpty();
    @ requires isInitialized;
    @ ensures grainCount == \old(grainCount) - 1;
    @*/
    /**
     * Drop a random grain object from the hamster's mouth.
     * @throws MouthEmptyException when the hamster does not carry any grain.
     */
    public void putGrain() {
        if (this.internalHamster.getGrainInMouth().isEmpty()) {
            throw new MouthEmptyException();
        }
        this.game.processCommandSpecification(
                new PutGrainCommandSpecification(
                        this.internalHamster,
                        this.internalHamster.getGrainInMouth().get(0)));
    }

    /**
     * Read a number from the hamster simulator UI for further use.
     * @param message The message used to prompt for the number.
     * @return Number read from the user.
     */
    public int readNumber(final String message) {
        return this.game.readInteger(message);
    }

    /**
     * Read a string from the hamster simulator UI for further use.
     * @param message The message used to prompt for the string.
     * @return String read from the user.
     */
    public String readString(final String message) {
        return this.game.readString(message);
    }

    /**
     * Writes a new string for this hamster to the game log. The message
     * will be displayed in a way that it can be associated to this hamster
     * object.
     * @param text The string to write to the log.
     */
    public void write(final String text) {
        this.game.processCommandSpecification(
                new WriteCommandSpecification(this.internalHamster, text));
    }

    /*
     * Queries
     */
    /**
     * Checks the front of the hamster.
     * @return true if the front of the hamster is clear, i.e., the hamster
     *              could execute a move command successfully.
     */
    public /*@ pure @*/ boolean frontIsClear() {
        checkState(this.internalHamster.getCurrentTile().isPresent());
        final LocationVector movementVector = this.internalHamster.getDirection().getMovementVector();
        final Tile currentTile = this.internalHamster.getCurrentTile().get();
        if (currentTile.getLocation().getRow() + movementVector.getDeltaRow() < 0
            || currentTile.getLocation().getColumn() + movementVector.getDeltaColumn() < 0) {
            return false;
        }

        final Location potentialNewLocation = currentTile.getLocation().translate(movementVector);

        if (!currentTile.getTerritory().isLocationInTerritory(potentialNewLocation)) {
            return false;
        }

        return !currentTile.getTerritory().getTileAt(potentialNewLocation).isBlocked();
    }

    /**
     * Checks the hamster's current tile for grain.
     * @return true if there are grain objects available on the hamster's current tile,
     *              i.e., a pickGrain command could execute successfully.
     */
    public /*@ pure @*/ boolean grainAvailable() {
        final Optional tile = this.internalHamster.getCurrentTile();
        checkArgument(tile.isPresent());
        return tile.get().getGrainCount() > 0;
    }

    /**
     * Checks whether the hamster's mouth is empty or whether it
     * has grains in its mouth.
     * @return true when there are no grain objects left in the hamster's mouth,
     *              i.e., when a putGrain command can not be executed.
     */
    public /*@ pure @*/ boolean mouthEmpty() {
        return this.internalHamster.getGrainInMouth().isEmpty();
    }

    /**
     * Get the current hamster location.
     * @return The current hamster's location in the territory.
     */
    public /*@ pure @*/ Location getLocation() {
        return this.internalHamster.getCurrentTile().get().getLocation();
    }

    /**
     * Get the current hamster looking direction.
     * @return The current hamster's looking direction.
     */
    public /*@ pure @*/ Direction getDirection() {
        return this.internalHamster.getDirection();
    }

    /** Internal factory object used to create a hamster object from the default
     * hamster of the given territory.
     * @param territory The territory for whose default hamster a game hamster should
     *                  be created.
     * @return The hamster object for the territories default hamster.
     */
    static Hamster fromInternalDefaultHamster(final Territory territory) {
        return new Hamster(territory);
    }

    /** Select a random grain object from the given tile.
     * @param tile The tile to check for grains.
     * @return A random grain object or null if there are no grains on the tile.
     */
    private Grain getRandomGrainFromTile(final Tile tile) {
        final Collection content = tile.getContent();
        for (final TileContent c : content) {
            if (c instanceof Grain) {
                return (Grain) c;
            }
        }
        return null;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy