Created
October 21, 2018 20:50
-
-
Save Shtaba09/1de03be9b74485bb29dd153679ae2270 to your computer and use it in GitHub Desktop.
Консольная змейка
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.javarush.task.task23.task2312; | |
import javax.swing.*; | |
import java.awt.*; | |
import java.awt.event.FocusEvent; | |
import java.awt.event.FocusListener; | |
import java.awt.event.KeyEvent; | |
import java.awt.event.KeyListener; | |
import java.util.Queue; | |
import java.util.concurrent.ArrayBlockingQueue; | |
public class KeyboardObserver extends Thread { | |
private Queue<KeyEvent> keyEvents = new ArrayBlockingQueue<KeyEvent>(100); | |
static JFrame frame; | |
@Override | |
public void run() { | |
frame = new JFrame("KeyPress Tester"); | |
frame.setTitle("Transparent JFrame Demo"); | |
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); | |
frame.setUndecorated(false); | |
frame.setSize((Room.game.getWidth() * 10) + 17, (Room.game.getHeight() * 10) + 40); | |
//frame.setExtendedState(JFrame.MAXIMIZED_BOTH); | |
frame.setLayout(new GridBagLayout()); | |
//frame.setOpacity(0.0f); | |
//frame.setVisible(true); | |
frame.addFocusListener(new FocusListener() { | |
@Override | |
public void focusGained(FocusEvent e) { | |
//do nothing | |
} | |
@Override | |
public void focusLost(FocusEvent e) { | |
System.exit(0); | |
} | |
}); | |
frame.addKeyListener(new KeyListener() { | |
public void keyTyped(KeyEvent e) { | |
//do nothing | |
} | |
public void keyReleased(KeyEvent e) { | |
//do nothing | |
} | |
public void keyPressed(KeyEvent e) { | |
keyEvents.add(e); | |
} | |
}); | |
} | |
public boolean hasKeyEvents() { | |
return !keyEvents.isEmpty(); | |
} | |
public KeyEvent getEventFromTop() { | |
return keyEvents.poll(); | |
} | |
} | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.javarush.task.task23.task2312; | |
import javax.swing.*; | |
import java.awt.*; | |
import java.util.ArrayList; | |
public class Layer extends JPanel { | |
@Override | |
public void paintComponent(Graphics g) { | |
super.paintComponent(g); | |
g.setColor(java.awt.Color.GREEN); //Задаем цвет элементов зеленый | |
g.fillRect(Room.game.getWidth() * 10, 0, 10, (Room.game.getWidth() * 10) + 10); //Рисуем прямоугольник показывающий край поля справа | |
g.fillRect(0, Room.game.getHeight() * 10, (Room.game.getHeight() * 10) + 10, 10); //Рисуем прямоугольник показывающий край поля снизу | |
g.fillRect(Room.game.getMouse().getX()*10, Room.game.getMouse().getY()*10, 10, 10); //Рисуем прямоугольник показывающий мышь | |
ArrayList<SnakeSection> getsection = Room.game.getSnake().getSections(); //Получаем секции змейки | |
for (int i = 0; i < getsection.size(); i++) { | |
g.fillRect(getsection.get(i).getX()*10, getsection.get(i).getY()*10, 10, 10); //Рисуем по очереди секции змейки | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.javarush.task.task23.task2312; | |
public class Mouse { | |
private int x; | |
private int y; | |
public Mouse(int x, int y) { | |
this.x = x; | |
this.y = y; | |
} | |
public int getX() { | |
return x; | |
} | |
public int getY() { | |
return y; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.javarush.task.task23.task2312; | |
import java.awt.event.KeyEvent; | |
import java.util.ArrayList; | |
/** | |
* Основной класс программы. | |
*/ | |
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; | |
game = this; | |
} | |
public Snake getSnake() { | |
return snake; | |
} | |
public Mouse getMouse() { | |
return mouse; | |
} | |
public int getWidth() { | |
return width; | |
} | |
public int getHeight() { | |
return height; | |
} | |
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 void run() { | |
//Создаем объект "наблюдатель за клавиатурой" и стартуем его. | |
KeyboardObserver keyboardObserver = new KeyboardObserver(); | |
keyboardObserver.start(); | |
//пока змея жива | |
while (snake.isAlive()) { | |
//"наблюдатель" содержит события о нажатии клавиш? | |
if (keyboardObserver.hasKeyEvents()) { | |
KeyEvent event = keyboardObserver.getEventFromTop(); | |
//Если равно символу 'q' - выйти из игры. | |
if (event.getKeyChar() == 'q') return; | |
//Если "стрелка влево" - сдвинуть фигурку влево | |
if (event.getKeyCode() == KeyEvent.VK_LEFT) | |
snake.setDirection(SnakeDirection.LEFT); | |
//Если "стрелка вправо" - сдвинуть фигурку вправо | |
else if (event.getKeyCode() == KeyEvent.VK_RIGHT) | |
snake.setDirection(SnakeDirection.RIGHT); | |
//Если "стрелка вверх" - сдвинуть фигурку вверх | |
else if (event.getKeyCode() == KeyEvent.VK_UP) | |
snake.setDirection(SnakeDirection.UP); | |
//Если "стрелка вниз" - сдвинуть фигурку вниз | |
else if (event.getKeyCode() == KeyEvent.VK_DOWN) | |
snake.setDirection(SnakeDirection.DOWN); | |
} | |
snake.move(); //двигаем змею | |
print(); //отображаем текущее состояние игры | |
sleep(); //пауза между ходами | |
} | |
//Выводим сообщение "Game Over" | |
System.out.println("Game Over!"); | |
} | |
/** | |
* Выводим на экран текущее состояние игры | |
*/ | |
public void print() { | |
//Создаем массив, куда будем "рисовать" текущее состояние игры | |
int[][] matrix = new int[height][width]; | |
//Рисуем все кусочки змеи | |
ArrayList<SnakeSection> sections = new ArrayList<SnakeSection>(snake.getSections()); | |
for (SnakeSection snakeSection : sections) { | |
matrix[snakeSection.getY()][snakeSection.getX()] = 1; | |
} | |
//Рисуем голову змеи (4 - если змея мертвая) | |
matrix[snake.getY()][snake.getX()] = snake.isAlive() ? 2 : 4; | |
//Рисуем мышь | |
matrix[mouse.getY()][mouse.getX()] = 3; | |
//Выводим все это на экран | |
String[] symbols = {" . ", " x ", " X ", "^_^", "RIP"}; | |
for (int y = 0; y < height; y++) { | |
for (int x = 0; x < width; x++) { | |
System.out.print(symbols[matrix[y][x]]); | |
} | |
System.out.println(); | |
} | |
System.out.println(); | |
System.out.println(); | |
System.out.println(); | |
} | |
/** | |
* Метод вызывается, когда мышь съели | |
*/ | |
public void eatMouse() { | |
createMouse(); | |
} | |
/** | |
* Создает новую мышь | |
*/ | |
public void createMouse() { | |
int x = (int) (Math.random() * width); | |
int y = (int) (Math.random() * height); | |
mouse = new Mouse(x, y); | |
} | |
public static Room game; | |
public static void main(String[] args) { | |
game = new Room(20, 20, new Snake(10, 10)); | |
game.snake.setDirection(SnakeDirection.DOWN); | |
game.createMouse(); | |
game.run(); | |
} | |
private int initialDelay = 520; | |
private int delayStep = 20; | |
/** | |
* Программа делает паузу, длинна которой зависит от длинны змеи. | |
*/ | |
public void sleep() { | |
try { | |
int level = snake.getSections().size(); | |
int delay = level < 15 ? (initialDelay - delayStep * level) : 200; | |
Thread.sleep(delay); | |
} catch (InterruptedException e) { | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.javarush.task.task23.task2312; | |
import java.util.ArrayList; | |
/** | |
* Класс змея | |
*/ | |
public class Snake { | |
// Направление движения змеи | |
private SnakeDirection direction; | |
// Состояние - жива змея или нет. | |
private boolean isAlive; | |
// Список кусочков змеи. | |
private ArrayList<SnakeSection> sections; | |
public Snake(int x, int y) { | |
sections = new ArrayList<SnakeSection>(); | |
sections.add(new SnakeSection(x, y)); | |
isAlive = true; | |
} | |
public boolean isAlive() { | |
return isAlive; | |
} | |
public int getX() { | |
return sections.get(0).getX(); | |
} | |
public int getY() { | |
return sections.get(0).getY(); | |
} | |
public SnakeDirection getDirection() { | |
return direction; | |
} | |
public void setDirection(SnakeDirection direction) { | |
this.direction = direction; | |
} | |
public ArrayList<SnakeSection> getSections() { | |
return sections; | |
} | |
/** | |
* Метод перемещает змею на один ход. | |
* Направление перемещения задано переменной 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 = sections.get(0); | |
head = new SnakeSection(head.getX() + dx, head.getY() + dy); | |
// Проверяем - не вылезла ли голова за границу комнаты | |
checkBorders(head); | |
if (!isAlive) return; | |
// Проверяем - не пересекает ли змея саму себя | |
checkBody(head); | |
if (!isAlive) return; | |
// Проверяем - не съела ли змея мышь. | |
Mouse mouse = Room.game.getMouse(); | |
if (head.getX() == mouse.getX() && head.getY() == mouse.getY()) // съела | |
{ | |
sections.add(0, head); // Добавили новую голову | |
Room.game.eatMouse(); // Хвост не удаляем, но создаем новую мышь. | |
} else // просто движется | |
{ | |
sections.add(0, head); // добавили новую голову | |
sections.remove(sections.size() - 1); // удалили последний элемент с хвоста | |
} | |
} | |
/** | |
* Метод проверяет - находится ли новая голова в пределах комнаты | |
*/ | |
private void checkBorders(SnakeSection head) { | |
if ((head.getX() < 0 || head.getX() >= Room.game.getWidth()) || head.getY() < 0 || head.getY() >= Room.game.getHeight()) { | |
isAlive = false; | |
} | |
} | |
/** | |
* Метод проверяет - не совпадает ли голова с каким-нибудь участком тела змеи. | |
*/ | |
private void checkBody(SnakeSection head) { | |
if (sections.contains(head)) { | |
isAlive = false; | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.javarush.task.task23.task2312; | |
public enum SnakeDirection { | |
UP, | |
RIGHT, | |
DOWN, | |
LEFT | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.javarush.task.task23.task2312; | |
public class SnakeSection { | |
private int x; | |
private int y; | |
public SnakeSection(int x, int y) { | |
this.x = x; | |
this.y = y; | |
} | |
public int getX() { | |
return x; | |
} | |
public int getY() { | |
return y; | |
} | |
@Override | |
public boolean equals(Object o) { | |
if (this == o) return true; | |
if (o == null || getClass() != o.getClass()) return false; | |
SnakeSection that = (SnakeSection) o; | |
if (x != that.x) return false; | |
if (y != that.y) return false; | |
return true; | |
} | |
@Override | |
public int hashCode() { | |
int result = x; | |
result = 31 * result + y; | |
return result; | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
taskKey="com.javarush.task.task23.task2312.big18" | |
Змейка(18) | |
Отлично! | |
Запускаем и наслаждаемся змейкой. | |
Требования: | |
1. Змейка готова! | |
Змейка(17) | |
Теперь закончим класс Snake. | |
Змея состоит из кусочков. Давай каждый ход просто добавлять один кусочек со стороны головы, | |
а самый последний - удалять. Тогда получится, что змея ползет. | |
Добавлять кусочек нужно рядом с текущей головой (кусочком номер 0). | |
С какой стороны добавлять зависит от direction (UP, DOWN, LEFT, RIGHT). | |
Подсказка: | |
а) Как добавить кусочек змеи в начало списка sections? | |
sections.add(0, new_section); | |
б) А как удалить последний? | |
sections.remove(sections.size()-1); | |
Необходимо реализовать метод move(int dx, int dy): | |
a) проверить, не вылезла ли она за границу комнаты (если да, то змея умирает) | |
б) проверить, не совпадает ли она с уже существующими кусочками змеи (если да, то змея умирает) | |
в) добавить голову к змее (со стороны головы) и удалить последний кусочек из хвоста. | |
г) вызвать метод eatMouse у статического объекта game класса Room, если координаты мыши совпадают с координатами головы змеи. | |
д) если змея поймала мышь (координаты головы совпадают с координатами мыши), то удалять кусок из хвоста не надо. | |
Змейка(16) | |
Ничто не вечно.. Так и змея должна умирать, если она врезается в стену или саму себя. | |
Для определения, не пересекается ли змея сама с собой, можно сделать очень простую проверку: | |
содержит ли список sections "новую голову змеи". | |
Код для этого будет выглядеть примерно так: | |
if (sections.contains(head)) | |
При этом head должен быть еще не добавлен в список sections, иначе будет всегда true. | |
Но чтобы этот код работал, надо реализовать методы сравнения объектов (equals и hashCode) в классе SnakeSection. | |
Подсказка: | |
Используй Alt+Insert в Intellij IDEA для автоматической генерации методов equals и hashCode. | |
Задание: | |
а) реализуй методы equals и hashCode в классе SnakeSection. | |
б) реализуй метод checkBorders(SnakeSection head): если голова змеи за границами комнаты - змея умирает (isAlive = false) | |
в) реализуй метод checkBody(SnakeSection head): если голова змеи пересекается с ее телом - змея умирает (isAlive = false) | |
Змейка(15) | |
Теперь осталось допилить змею. | |
Вот что я предлагаю насчет движения змеи: | |
Змея состоит из кусочков. Давай каждый ход просто добавлять один кусочек со стороны головы, | |
а самый последний - удалять. Тогда получится, что змея ползет. | |
Давай добавим два метода move, один без параметров, а другой с двумя параметрами типа int. | |
В методе move без параметров необходимо: | |
а) прекратить движение если змея умерла(isAlive == false) | |
б) вызвать метод move(0, -1) если направление движения равно SnakeDirection.UP | |
в) вызвать метод move(1, 0) если направление движения равно SnakeDirection.RIGHT | |
г) вызвать метод move(0, 1) если направление движения равно SnakeDirection.DOWN | |
д) вызвать метод move(-1, 0) если направление движения равно SnakeDirection.LEFT | |
Метод move с параметрами int, int пока оставим пустым. | |
Змейка(14) | |
Теперь поработаем над методом print(). | |
Надо: | |
а) вывести на экран прямоугольник из точек размером width x height. | |
б) тело змеи отметить символом "x"-английское | |
в) голову змеи нарисовать символом "X"-английское. | |
Подсказка: | |
а) удобно сначала создать матрицу типа int[][] с размером (height x width) | |
б) затем пройтись по всем объектам и отрисовать их в матрицу. | |
Например, тело змеи - 1, голова змеи - 2, мышь - 3. | |
Змейка(13) | |
Предлагаю тебе в этот раз написать специальный метод sleep(), который будет делать паузу | |
в зависимости от длины змеи (количества элементов в sections). | |
Придумай какой-нибудь хитрый алгоритм. Чтобы на первом уровне пауза была 500 миллисекунд, | |
к 11 уровню постепенно уменьшилась до 300. А к 15 до 200. И дальше оставалась постоянной. | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment