Задание 8
Настала очередь конструктора класса Snake.
Змея у нас будет поначалу состоять из одного кусочка - головы.
А что для него требуется?
Что необходимо передать в конструктор?
Координаты змеи, конечно же.

Надо:
а) Передать в конструктор координаты головы змеи (x и y)
б) создать в нем первый "кусочек змеи" (голову) и добавить его в коллекцию sections.
в) isAlive выставить в true
г) не забудь в конструкторе инициализировать переменную sections. В null не много-то и добавишь!
д) создать и реализовать метод int getX(). Метод должен вернуть координату Х головы змеи.
е) создать и реализовать метод int getY(). Метод должен вернуть координату Y головы змеи.
ё) еще добавить классу метод move()- он нам пригодится попозже.

 

package com.javarush.test.level23.lesson13.big01;

import java.util.ArrayList;

/**
 * Created by promoscow on 22.01.17.
 */
public class Snake {
    private ArrayList<SnakeSection> sections;
    private boolean isAlive;
    private SnakeDirection direction;

    public ArrayList<SnakeSection> getSections() {
        return sections;
    }

    public boolean isAlive() {
        return isAlive;
    }

    public SnakeDirection getDirection() {
        return direction;
    }

    public void setDirection(SnakeDirection direction) {
        this.direction = direction;
    }

    public int getX(int x) {
        return sections.get(0).getX();
    }

    public int getY(int y) {
        return sections.get(0).getY();
    }

    public Snake(int x, int y) {
        sections = new ArrayList<>();
        sections.add(new SnakeSection(x, y));
        isAlive = true;
    }

    public void move() {

    }
}
Задание 9
Еще остался самый главный класс - Room.
Что нам нужно для его описания?
Размеры комнаты (width и height) - раз.
Змея - два
Мышь - три.

Надо:
а) создать в классе Room переменные width & height типа int.
б) создать в классе Room переменную snake типа Snake.
в) создать в классе Room переменную mouse типа Snake. Шучу. Типа Mouse, конечно же 🙂
г) создать для них всех геттеры и сеттеры.
д) создать конструктор. Конструктор должен принимать три параметра: width, height и ... snake!

 

package com.javarush.test.level23.lesson13.big01;

/**
 * Created by promoscow on 22.01.17.
 */
public class Room {
    private int width;
    private int height;
    private Snake snake;
    private Mouse mouse;

    public Room(int width, int height, Snake snake) {
        this.width = width;
        this.height = height;
        this.snake = snake;
    }

    public void setWidth(int width) {

        this.width = width;
    }

    public void setHeight(int height) {
        this.height = height;
    }

    public void setSnake(Snake snake) {
        this.snake = snake;
    }

    public void setMouse(Mouse mouse) {
        this.mouse = mouse;
    }

    public int getWidth() {

        return width;
    }

    public int getHeight() {
        return height;
    }

    public Snake getSnake() {
        return snake;
    }

    public Mouse getMouse() {
        return mouse;
    }

    public static void main(String[] args) {

    }
}
Задание 10
Займемся методом main.
Для начала надо создать статическую переменную game типа Room. Доступ к переменной должен быть для всех классов.
Затем в методе main:
а) Создать змею - объект Snake
б) Создать комнату - объект типа Room и передать в него ширину, высоту и змею.
в) Установить змее direction равным SnakeDirection.DOWN
Чего еще не хватает? Собственно метода, в котором будет идти вся игровая логика.
И еще нужен метод, который бы отрисовывал все это на экране.
а) создать метод run()
б) создать метод print()

 

public static void main(String[] args) {
    game = new Room(10, 10, new Snake(5, 5));
    game.snake.setDirection(SnakeDirection.DOWN);
}
Задание 11
Теперь логика управления мышью.
С мышью у нас будут происходить две вещи.
Первая - змея съест мышь.
Вторая - появляется новая мышь в случайной точке комнаты.

Надо написать и реализовать метод createMouse() в классе Room.
В этом методе мы просто должны создавать новую мышь со случайными координатами в комнате.
Как получить случайные координаты?
Это ты уже должен был знать. На всякий случай даю подсказку:
int x = (int) (Math.random() * width);

Еще понадобится метод - eatMouse(), на случай, если мышь все-таки кто-то съест 🙂
Пока сложной логики в этом методе не будет - просто будем вызывать метод createMouse и все.

 

public void createMouse() {
    int x = (int) (Math.random() * width);
    int y = (int) (Math.random() * height);
    mouse = new Mouse(x, y);
}

public void eatMouse() {
    createMouse();
}
Задание 12
Добавить в метод main:
в) вызов метода createMouse().
Змея-то у нас есть, пусть и мышь будет
г) вызов метода run().
Без него ничего работать не будет. В нем вся основная логика.
Неплохо получилось, но я все-таки внесу пару правок.
Кстати, как насчет написать метод sleep?
Ты уже понял из предыдущих задач, что в методе run нужна пауза.
Но насколько я помню, скорость в змейке должна расти при росте ее длины.
Значит чем длиннее змея, тем выше скорость и меньше пауза.

 

public static void main(String[] args) {
    game = new Room(10, 10, new Snake(5, 5));
    game.snake.setDirection(SnakeDirection.DOWN);
    game.createMouse();
    game.run();
}
Задание 13
Предлагаю тебе в этот раз написать специальный метод sleep().
Который будет делать паузу в зависимости от длины змеи (количества элементов в sections).
Придумай какой-нибудь хитрый алгоритм. Чтобы на первом уровне пауза была 500 миллисекунд,
а к 10 уровню постепенно уменьшилась до 300.
И ниже 200 не опускалась.

 

/**
 * Прогрмма делает паузу, длинна которой зависит от длинны змеи.
 */
public void sleep() {
    try {
        int pause = 500 - ((snake.getSections().size() - 1) * (200 / 9));
        if (pause < 200) pause = 200;
        Thread.sleep(pause);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}
Задание 14
Теперь поработаем над методом print().
Надо:
а) вывести на экран прямоугольник из точек размером width x height.
б) тело змеи отметить символом "x"-английское
в) голову змеи нарисовать символом X-английское.
Подсказка:
а) удобно сначала создать матрицу типа int[][] с размером (height x width)
б) затем пройтись по всем объектам и отрисовать их в матрицу.
Например, тело змеи - 1, голова змеи - 2, мышь - 3.

 

/**
 * Выводим на экран текущее состояние игры
 */
public void print() {
    //Создаем массив, куда будем "рисовать" текущее состояние игры
    //Рисуем все кусочки змеи
    //Рисуем мышь
    //Выводим все это на экран

    int[][] matrix = new int[height][width];
    matrix[snake.getSections().get(0).getY()][snake.getSections().get(0).getX()] = 2;
    for (int i = 1; i < snake.getSections().size(); i++) {
        matrix[snake.getSections().get(i).getY()][snake.getSections().get(i).getX()] = 1;
    }
    matrix[mouse.getY()][mouse.getX()] = 3;

    for (int i = 0; i < matrix.length; i++) {
        for (int j = 0; j < matrix[i].length; j++) {
            if (matrix[i][j] == 1) System.out.print(" x ");
            else if (matrix[i][j] == 2) System.out.print(" X ");
            else if (matrix[i][j] == 3) System.out.print(" * ");
            else if (matrix[i][j] == 0) System.out.print(" . ");
        }
    }
}
Задание 15
Теперь осталось допилить змею.
Вот что я предлагаю насчет движения змеи:
Змея состоит из кусочков. Давай каждый ход просто добавлять один кусочек со стороны головы,
а самый последний - удалять. Тогда получится, что змея ползет.

Добавлять кусочек нужно рядом с текущей головой (кусочком номер 0).
С какой стороны добавлять зависит от direction (UP, DOWN, LEFT, RIGHT).
Подсказка:
а) Как добавить кусочек змеи в начало списка sections?
sections.add(0, new_section);
б) А как удалить последний?
sections.remove(sections.size()-1);

В методе move надо:
а) сделать шаг в текущем направлении (определяется direction)
б) проверить, что если змея уперлась в стену, то умереть (isAlive = false)
в) проверить, что если змея уперлась себя, то умереть (isAlive = false)
г) проверить, если змея встретила мышь - то съесть ее.

 

    public void move() {
        if (direction == SnakeDirection.UP) {
            sections.add(0, new SnakeSection(sections.get(0).getY() - 1, sections.get(0).getX()));
        }
        else if (direction == SnakeDirection.DOWN) {
            sections.add(0, new SnakeSection(sections.get(0).getY() + 1, sections.get(0).getX()));
        }
        else if (direction == SnakeDirection.LEFT) {
            sections.add(0, new SnakeSection(sections.get(0).getY(), sections.get(0).getX() - 1));
        }
        else if (direction == SnakeDirection.RIGHT) {
            sections.add(0, new SnakeSection(sections.get(0).getY(), sections.get(0).getX() + 1));
        }

        sections.remove(sections.size()-1);

        if (sections.get(0).getY() > Room.game.getHeight() - 1
                || sections.get(0).getY() < 0
                || sections.get(0).getX() > Room.game.getWidth() - 1
                || sections.get(0).getX() < 0) isAlive = false;
        for (SnakeSection section : sections) {
            if (sections.get(0).getX() == section.getX()
                && sections.get(0).getY() == section.getY()) isAlive = false;
        }

        if (sections.get(0).getY() == Room.game.getMouse().getY()
                && sections.get(0).getX() == Room.game.getMouse().getX()) Room.game.eatMouse();
    }
}
Задание 16
Продолжаем работать над методом move()
Для определения, не пересекается ли змея сама с собой, можно сделать очень простую проверку:
содержит ли список sections "новую голову змеи".
Код для этого будет выглядеть примерно так:
if (sections.contains(head))
При этом head должен быть еще не добавлен в список sections, иначе будет всегда true.
Но чтобы этот код работал, надо реализовать методы сравнения объектов (equals & hashCode) в классе SnakeSection.

Задание:
а) реализовать методы  equals &amp; hashCode в классе SnakeSection.

Сигнатура методов:
public boolean equals(Object o)
public int hashCode()

Подсказка:
Используй Alt+Insert в Intellij IDEA
В классе Snake:
б) в методе move(int dx, int dy) создать голову(кусочек змеи) с правильными координатами. Вызвать метод checkBody() и checkBorders()
в) реализовать метод checkBody: если голова змеи пересекается с ее телом (любым из кусочков) - змея умирает (isAlive = false)
/**
 * Метод перемещает змею на один ход.
 * Направление перемещения задано переменной direction.
 */
public void move() {
    if (!isAlive) return;

    if (direction == SnakeDirection.UP)
        move(0, -1);
    else if (direction == SnakeDirection.RIGHT)
        move(1, 0);
    else if (direction == SnakeDirection.DOWN)
        move(0, 1);
    else if (direction == SnakeDirection.LEFT)
        move(-1, 0);
}

/**
 * Метод перемещает змею в соседнюю клетку.
 * Кординаты клетки заданы относительно текущей головы с помощью переменных (dx, dy).
 */
private void move(int dx, int dy) {
    //Создаем новую голову - новый "кусочек змеи".
    //Проверяем - не вылезла ли голова за границу комнаты
    //Проверяем - не пересекает ли змея  саму себя
    //Проверяем - не съела ли змея мышь.
    //Двигаем змею.

    SnakeSection head = new SnakeSection(dx, dy);
    checkBody(head);
    checkBorders(head);
}

/**
 * Метод проверяет - находится ли новая голова в пределах комнаты
 */
private void checkBorders(SnakeSection head) {
    if (head.getY() > Room.game.getHeight() - 1
            || head.getY() < 0
            || head.getX() > Room.game.getWidth() - 1
            || head.getX() < 0) isAlive = false;
}

/**
 * Метод проверяет - не совпадает ли голова с каким-нибудь участком тела змеи.
 */
private void checkBody(SnakeSection head) {
    if (sections.contains(head)) isAlive = false;
}
Задание 17
Теперь закончим класс Shake
Надо:
а) реализовать метод checkBorders: если голова змеи за границами комнаты - змея умирает (isAlive = false)

Реализовать метод move(int dx, int dy):
б) проверить, не вылезла ли она за границу комнаты (если да, то змея умирает)
в) проверить, не совпадает ли она с уже существующими кусочками змеи (если да, то змея умирает)
г) добавить голову к змее (со стороны головы) и удалить последний кусочек из хвоста.
д) если змея поймала мышь (координаты головы совпадают с координатами мыши), то удалять кусок из хвоста не надо.

 

/**
 * Метод перемещает змею на один ход.
 * Направление перемещения задано переменной direction.
 */
public void move() {
    if (!isAlive) return;

    if (direction == SnakeDirection.UP)
        move(0, -1);
    else if (direction == SnakeDirection.RIGHT)
        move(1, 0);
    else if (direction == SnakeDirection.DOWN)
        move(0, 1);
    else if (direction == SnakeDirection.LEFT)
        move(-1, 0);
}

/**
 * Метод перемещает змею в соседнюю клетку.
 * Кординаты клетки заданы относительно текущей головы с помощью переменных (dx, dy).
 */
private void move(int dx, int dy) {
    //Создаем новую голову - новый "кусочек змеи".
    //Проверяем - не вылезла ли голова за границу комнаты
    //Проверяем - не пересекает ли змея  саму себя
    //Проверяем - не съела ли змея мышь.
    //Двигаем змею.

    SnakeSection head = new SnakeSection(sections.get(0).getX() + dx, sections.get(0).getY() + dy);    //создаём новую секцию head
    checkBody(head);    //проверяем, не втыкается ли она в себя
    checkBorders(head);    //проверяем, не втыкается ли она в борт

    sections.add(0, head);

    if (head.getY() == Room.game.getMouse().getY()
            && head.getX() == Room.game.getMouse().getX()) {
        Room.game.eatMouse();
    } else sections.remove(sections.size()-1);
}

/**
 * Метод проверяет - находится ли новая голова в пределах комнаты
 */
private void checkBorders(SnakeSection head) {
    if (head.getY() >= Room.game.getHeight()
            || head.getY() < 0
            || head.getX() >= Room.game.getWidth()
            || head.getX() < 0) isAlive = false;
}

/**
 * Метод проверяет - не совпадает ли голова с каким-нибудь участком тела змеи.
 */
private void checkBody(SnakeSection head) {
    if (sections.contains(head)) isAlive = false;
}