Snake.java

package me.schawe.multijsnake.snake;

import me.schawe.multijsnake.snake.ai.Autopilot;

import java.util.*;

public class Snake {
    private Coordinate head;
    private Move headDirection;
    private Move lastHeadDirection;
    private ArrayDeque<Coordinate> tail;
    private int length;
    private final SnakeId id;
    private boolean dead;
    private String name;
    private final Autopilot autopilot;

    public Coordinate getHead() {
        return head;
    }

    public Move getHeadDirection() {
        return headDirection;
    }

    public Move getLastHeadDirection() {
        return lastHeadDirection;
    }

    public boolean isDead() {
        return dead;
    }

    public int getLength() {
        return length;
    }

    public SnakeId getId() {
        return id;
    }

    public int getIdx() {
        return id.getIdx();
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public ArrayDeque<Coordinate> getTail() {
        return tail;
    }

    public List<Coordinate> getTailAsList() {
        return new ArrayList<>(getTail());
    }

    public Snake(SnakeId id, Coordinate start, Move direction, Autopilot autopilot) {
        lastHeadDirection = direction;
        headDirection = direction;
        head = start;
        tail = new ArrayDeque<>();
        length = 2;
        this.id = id;
        dead = false;
        name = Optional.ofNullable(autopilot).map(Autopilot::generateName).orElseGet(() -> "Anon " + (id.getIdx() + 1));
        this.autopilot = autopilot;
    }

    public Snake(SnakeId id, Coordinate start){
        this(id, start, new Random());
    }

    public Snake(SnakeId id, Coordinate start, Move dir){
        this(id, start, dir, null);
    }

    public Snake(SnakeId id, Coordinate start, Random random){
        this(id, start, Move.random(random), null);
    }

    public Snake(SnakeId id, Coordinate start, Random random, Autopilot autopilot){
        this(id, start, Move.random(random), autopilot);
    }

    public void reset(Coordinate start) {
        Move dir = Move.random();
        lastHeadDirection = dir;
        headDirection = dir;
        head = start;
        tail = new ArrayDeque<>();
        length = 2;
        dead = false;
    }

    public void kill() {
        dead = true;
    }

    public Optional<Autopilot> ai() {
        return Optional.ofNullable(autopilot);
    }

    public void incrementLength() {
        this.length += 1;
    }

    public void setHead(Coordinate head) {
        this.head = head;
    }

    public void setHeadDirection(Move headDirection) {
        this.headDirection = headDirection;
    }

    public void setLastHeadDirection(Move lastHeadDirection) {
        this.lastHeadDirection = lastHeadDirection;
    }

    public void turnRelative(MoveRelative rmove) {
        if(this.isDead()) {
            return;
        }

        headDirection = rmove.toMove(this.getLastHeadDirection());
    }

    public void turnAbsolute(Move direction) {
        if(this.isDead()) {
            return;
        }

        headDirection = direction;
    }

    public Coordinate step() {
        Coordinate offset = getHeadDirection().toCoord();
        lastHeadDirection = headDirection;

        tail.add(head.copy());

        while (tail.size() >= length + 1) {
            tail.remove();
        }

        head = head.add(offset);

        return head;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        Snake snake = (Snake) o;
        return id.equals(snake.id);
    }

    @Override
    public int hashCode() {
        return Objects.hash(id);
    }
}