Задание 17
Я тут немножечко подправил код.
Но в целом - отлично.
Запускаем и наслаждаемся тетрисом.
P.S.
Не забудь отрегулировать высоту консоли



Задание 16
Напиши свою реализацию методов left(), right(), up(), down() в классе Figure.
Подумай, что должны делать эти методы?



Задание 15
Напиши реализацию метода step в классе Tetris.
В методе надо переместить фигурку вниз на один шаг.
Если после перемещения положить фигурку на текущее место невозможно, то:
а) поднять ее обратно (up)
б) "приземлить" ее (landed)
в) удалить все "полные линии" в объекте field
г) создать новую фигурку взамен старой.



Задание 14
Напиши реализацию метода removeFullLines в классе Field
Надо
а) удалить все строки из матрицы, которые полностью заполнены (состоят из одних единиц)
б) сместить оставшиеся строки вниз
в) создать новые строки взамен отсутствующих.

ВАЖНО!
matrix[y][x] содержит элемент с координатами (x,y)
matrix[i] содержит i-ю строку
а) Мы можем удалить стоку:
matrix[i] = null

б)Скопировать [ссылку на] строку:
matrix[i+1] = matrix[i];

в)Создать новую строку:
matrix[i] = new int[width];



Задание 13
Теперь приступим к реализации созданных методов.
Напиши реализацию метода print в классе Field
а) Метод должен выводить на экран прямоугольник.
б) Высота прямоугольника равна height, ширина - width
в) Если данная клетка пустая - вывести точку, если не пустая - английский X

Подсказка:
if (matrix[y][x]==0) ...



Задание 12
В тетрисе мы управляем движением фигурки с помощью клавиатуры.
Тут есть 4 действия:
движение влево (кнопка влево)
движение вправо (кнопка влево)
поворот фигурки (цифра 5 на доп.клавиатуре справа)
падение вниз (пробел)

Мы будем обрабатывать ввод с клавиатуры в методе run() класса Tetris.

И тут у меня для тебя две новости: хорошая и плохая
Плохая новость состоит в том, что java не позволяет считать нажатые символы с клавиатуры,
пока пользователь не нажмет enter.
Не очень удобно, правда?

Хорошая новость состоит в том, я написал специальный класс (KeyboardObserver), который позволяет обойти это ограничение.
Так что ты можешь воспользоваться им.

Есть еще и отличная новость.
Ты до сих пор отлично справлялся, поэтому я помогу тебе немного.
Я написал за тебя методы:
а) createRandomFigure в FigureFactory
б) run в Tetris

Изучи их внимательно и переходи дальше.



Задание 11
Теперь создай класс FigureFactory.
С его помощью мы будем создавать фигуры различных форм.
Пока он будет содержать только один статический метод createRandomFigure:
public static Figure createRandomFigure(int x,int y)



Задание 10
Так же нам понадобятся методы для управления фигуркой.
Добавь в класс Figure методы:
left() - для движения фигурки влево.
right() - для движения фигурки вправо.
down() - для движения фигурки вниз.
up() - для движения фигурки вверх.
downMaximum() - падение фигурки в низ до дна.
rotate() - для поворота фигурки вокруг главной диагонали.
boolean isCurrentPositionAvailable() - проверка - может ли фигурка быть помещена в текущую позицию. Для теста захардкодь результат в true.
landed() - вызывается, когда фигурка достигла дна или уперлась в другую фигурку
Все ее занятые клетки теперь должны добавиться в Field.



Задание 9
Если ты обратил внимание, мы пишем программу "сверху вниз".
Сначала решили, какие классы нам нужны. Затем - какие методы.
А потом уже начнем писать код этих методов.
Таким образом мы разбиваем большую задачу на множество маленьких.
Когда код всех методов будет готов, останется только проверить - так ли все работает, как должно быть.
И если надо - внести некоторые изменения.



Задание 8
Теперь создадим костяк класса Figure.
Этот класс будет описывать падающую фигурку.

Нам понадобятся ее координаты и форма.
За координаты будут отвечать две переменные x и y.
За форму - матрица. Двумерный массив 3x3, состоящий из единиц и нулей.
Единицей мы обозначаем что клетка есть, нулем - что она пустая.

Добавь в класс Figure два поля поля: x типа int, y типа int.
Еще добавь двумерный массив: matrix(матрица) типа int[][].
Там же добавь getter'ы для созданных переменных.
Добавь конструктор с тремя параметрами x, y, matrix.



Задание 7
Нам понадобится еще 4 метода в классе Field:
а) print() - объект будет отрисовывать на экран свое текущее состояние;
б) removeFullLines() - будет удалять из матрицы полностью заполненные строки и сдвигать вышележащие строки вниз;
в) Integer getValue(int x, int y) - возвращает значение которое находится в матрице с координатами x и y;
г) void setValue(int x, int y, int value) - устанавливает переданное значение в ячейку массива (матрицы) с координатами x, y.



Задание 6
Теперь перейдем к классу Field.
Он будет отвечать за хранение данных о текущих занятых и свободных клетках на поле игры.
Добавь в класс Field два поля поля: width (ширина) типа int, height(высота) типа int.
Так же нам понадобится матрица - двумерный массив: matrix(матрица) типа int[][];
Там же добавь getter'ы для созданных переменных.
Добавь конструктор с двумя параметрами width и height. И не забудь про матрицу.

ВАЖНО!
Двумерный массив можно представить как массив массивов или как прямоугольную матрицу.
При этом первой координатой в массиве у нас будет номер строки, а второй - столбца.
Другими словами ячейка с координатами x, y - это matrix[y][x].



Задание 5
Теперь нужно создать объект самого Тетриса.
Добавь в класс Tetris статическую переменную game. (тип - Tetris, видимость - public)

Затем в методе main создай объект типа Тетрис и сохрани его в эту переменную.
Затем добавь вызов метода run.

Должно получиться что-то типа такого:
game = new Tetris();
game.run();



Задание 4
Так же еще нам понадобится пара методов.
Добавь в класс Tetris методы run() и step():

run() будет отвечать за всю игру.
А step() - за один шаг в игре.



Задание 3
Отлично! Теперь добавь в Tetris две переменные: field типа Field  и figure типа Figure.
С их помощью мы будем хранить информацию о текущей фигурке и о состоянии дел на "поле клеток".
Так же добавь getter'ы для созданных переменных.



Задание 2
А еще нам понадобится метод main. Как же без него.
Добавь метод main в класс Tetris



Задание 1
Давай напишем игру Тетрис.
Наш Тетрис будет состоять из двух вещей: поля из клеток и фигурки, которая падает.
Поэтому для начала создай три класса: Field(поле с клетками), Figure(фигура) и сам Tetris.

 

package com.javarush.test.level22.lesson18.big01;

import java.awt.event.KeyEvent;

/**
 * Класс Tetris - содержит основной функционал игры.
 */
public class Tetris {

    public static Tetris game;
    private Field field;                //Поле с клетками
    private Figure figure;              //Фигурка
    private boolean isGameOver;         //Игра Окончена?

    public Tetris(int width, int height) {
        field = new Field(width, height);
        figure = null;
    }

    public static void main(String[] args) throws Exception {
        game = new Tetris(10, 20);
        game.run();
    }

    /**
     * Геттер переменной field.
     */
    public Field getField() {
        return field;
    }

    /**
     * Сеттер для field
     */
    public void setField(Field field) {
        this.field = field;
    }

    /**
     * Геттер переменной figure.
     */
    public Figure getFigure() {
        return figure;
    }

    /**
     * Сеттер для figure
     */
    public void setFigure(Figure figure) {
        this.figure = figure;
    }

    /**
     * Основной цикл программы.
     * Тут происходят все важные действия
     */
    public void run() throws Exception {
        //Создаем объект "наблюдатель за клавиатурой" и стартуем его.
        KeyboardObserver keyboardObserver = new KeyboardObserver();
        keyboardObserver.start();

        //выставляем начальное значение переменной "игра окончена" в ЛОЖЬ
        isGameOver = false;
        //создаем первую фигурку посередине сверху: x - половина ширины, y - 0.
        figure = FigureFactory.createRandomFigure(field.getWidth() / 2, 0);

        //пока игра не окончена
        while (!isGameOver) {
            //"наблюдатель" содержит события о нажатии клавиш?
            if (keyboardObserver.hasKeyEvents()) {
                //получить самое первое событие из очереди
                KeyEvent event = keyboardObserver.getEventFromTop();
                //Если равно символу 'q' - выйти из игры.
                if (event.getKeyChar() == 'q') return;
                //Если "стрелка влево" - сдвинуть фигурку влево
                if (event.getKeyCode() == KeyEvent.VK_LEFT)
                    figure.left();
                    //Если "стрелка вправо" - сдвинуть фигурку вправо
                else if (event.getKeyCode() == KeyEvent.VK_RIGHT)
                    figure.right();
                    //Если  код клавиши равен 12 ("цифра 5 на доп. клавиатуре") - повернуть фигурку
                else if (event.getKeyCode() == 12)
                    figure.rotate();
                    //Если "пробел" - фигурка падает вниз на максимум
                else if (event.getKeyCode() == KeyEvent.VK_SPACE)
                    figure.downMaximum();
            }

            step();             //делаем очередной шаг
            field.print();      //печатаем состояние "поля"
            Thread.sleep(300);  //пауза 300 миллисекунд - 1/3 секунды
        }

        //Выводим сообщение "Game Over"
        System.out.println("Game Over");
    }

    public void step() {
        //опускам фигурку вниз
        figure.down();

        //если разместить фигурку на текущем месте невозможно
        if (!figure.isCurrentPositionAvailable()) {
            figure.up();                    //поднимаем обратно
            figure.landed();                //приземляем

            isGameOver = figure.getY() <= 1;//если фигурка приземлилась на самом верху - игра окончена

            field.removeFullLines();        //удаляем заполненные линии

            figure = FigureFactory.createRandomFigure(field.getWidth() / 2, 0); //создаем новую фигурку
        }
    }
}

 

package com.javarush.test.level22.lesson18.big01;

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);

    private JFrame frame;

    @Override
    public void run() {
        frame = new JFrame("KeyPress Tester");
        frame.setTitle("Transparent JFrame Demo");
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);

        frame.setUndecorated(true);
        frame.setSize(400, 400);
        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();
    }
}

 

package com.javarush.test.level22.lesson18.big01;


/**
 * Класс Figure описывает фигурку тетриса
 */
public class Figure {
    //метрица которая определяет форму фигурки: 1 - клетка не пустая, 0 - пустая
    private int[][] matrix;
    //координаты
    private int x;
    private int y;

    public Figure(int x, int y, int[][] matrix) {
        this.x = x;
        this.y = y;
        this.matrix = matrix;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    public int[][] getMatrix() {
        return matrix;
    }

    /**
     * Поворачаиваем фигурку.
     * Для простоты - просто вокруг главной диагонали.
     */
    public void rotate() {
        int[][] matrix2 = new int[3][3];

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                matrix2[i][j] = matrix[j][i];
            }
        }

        matrix = matrix2;
    }

    /**
     * Двигаем фигурку влево.
     * Проверяем не вылезла ли она за границу поля и/или не залезла ли на занятые клетки.
     */
    public void left() {
        x--;
        if (!isCurrentPositionAvailable())
            x++;
    }

    /**
     * Двигаем фигурку вправо.
     * Проверяем не вылезла ли она за границу поля и/или не залезла ли на занятые клетки.
     */
    public void right() {
        x++;
        if (!isCurrentPositionAvailable())
            x--;
    }

    /**
     * Двигаем фигурку вверх.
     * Используется, если фигурка залезла на занятые клетки.
     */
    public void up() {
        y--;
    }

    /**
     * Двигаем фигурку вниз.
     */
    public void down() {
        y++;
    }

    /**
     * Двигаем фигурку вниз до тех пор, пока не залезем на кого-нибудь.
     */
    public void downMaximum() {
        while (isCurrentPositionAvailable()) {
            y++;
        }

        y--;
    }

    /**
     * Проверяем - может ли фигурка находится на текущей позици:
     * а) не вылазиет ли она за границы поля
     * б) не залазиет ли она на занятые клетки
     */
    public boolean isCurrentPositionAvailable() {
        Field field = Tetris.game.getField();

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (matrix[i][j] == 1) {
                    if (y + i >= field.getHeight())
                        return false;

                    Integer value = field.getValue(x + j, y + i);
                    if (value == null || value == 1)
                        return false;
                }
            }
        }

        return true;
    }

    /**
     * Приземляем фигурку - добавляем все ее непустые клетки к клеткам поля.
     */
    public void landed() {
        Field field = Tetris.game.getField();

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (matrix[i][j] == 1)
                    field.setValue(x + j, y + i, 1);
            }
        }
    }
}

 

package com.javarush.test.level22.lesson18.big01;

/**
 * Клсс FigureFactory отвечает за создание объектов-фигурок.
 */
public class FigureFactory {
    /**
     * Набор из шести шаблонов для фигурок
     */
    public static final int[][][] BRICKS = {{
            {1, 1, 0},                          //   X X
            {0, 1, 1},                          //     X X
            {0, 0, 0}}, {                       //

            {1, 0, 0},                          //   X
            {1, 1, 0},                          //   X X
            {0, 1, 0}}, {                       //     X

            {0, 1, 0},                          //   X
            {0, 1, 0},                          //   X
            {0, 0, 0}}, {                       //   X

            {1, 1, 0},                          //   X X
            {1, 1, 0},                          //   X X
            {0, 0, 0}}, {                       //

            {1, 1, 1},                          //   X X X
            {0, 1, 0},                          //     X
            {0, 0, 0}}, {                       //

            {1, 1, 1},                          //   X X X
            {1, 1, 1},                          //   X X X
            {0, 0, 0}}                          //
    };

    /**
     * Метод выбирает случайный шаблон и создает с ним новую фигурку.
     */
    public static Figure createRandomFigure(int x, int y) {
        int index = (int) (Math.random() * 6);
        return new Figure(x, y, BRICKS[index]);
    }
}

 

package com.javarush.test.level22.lesson18.big01;

import java.util.ArrayList;

/**
 * Класс Field описывает "поле клеток" игры Тетрис
 */
public class Field {
    //ширина и высота
    private int width;
    private int height;

    //матрица поля: 1 - клетка занята, 0 - свободна
    private int[][] matrix;

    public Field(int width, int height) {
        this.width = width;
        this.height = height;
        matrix = new int[height][width];
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }

    public int[][] getMatrix() {
        return matrix;
    }

    /**
     * Метод возвращает значение, которое содержится в матрице с координатами (x,y)
     * Если координаты за пределами матрицы, метод возвращает null.
     */
    public Integer getValue(int x, int y) {
        if (x >= 0 && x < width && y >= 0 && y < height)
            return matrix[y][x];

        return null;
    }

    /**
     * Метод устанавливает переданное значение(value) в ячейку матрицы с координатами (x,y)
     */
    public void setValue(int x, int y, int value) {
        if (x >= 0 && x < width && y >= 0 && y < height)
            matrix[y][x] = value;
    }

    /**
     * Метод печатает на экран текущее содержание матрицы
     */
    public void print() {
        //Создаем массив, куда будем "рисовать" текущее состояние игры
        int[][] canvas = new int[height][width];

        //Копируем "матрицу поля" в массив
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                canvas[i][j] = matrix[i][j];
            }
        }

        //Копируем фигурку в массив, только непустые клетки
        int left = Tetris.game.getFigure().getX();
        int top = Tetris.game.getFigure().getY();
        int[][] brickMatrix = Tetris.game.getFigure().getMatrix();

        for (int i = 0; i < 3; i++) {
            for (int j = 0; j < 3; j++) {
                if (top + i >= height || left + j >= width) continue;
                if (brickMatrix[i][j] == 1)
                    canvas[top + i][left + j] = 2;
            }
        }


        //Выводим "нарисованное" на экран, но начинаем с "границы кадра".
        System.out.println("---------------------------------------------------------------------------\n");

        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                int index = canvas[i][j];
                if (index == 0)
                    System.out.print(" . ");
                else if (index == 1)
                    System.out.print(" X ");
                else if (index == 2)
                    System.out.print(" X ");
                else
                    System.out.print("???");
            }
            System.out.println();
        }


        System.out.println();
        System.out.println();
    }

    /**
     * Удаляем заполненные линии
     */
    public void removeFullLines() {
        //Создаем список для хранения линий
        ArrayList<int[]> lines = new ArrayList<int[]>();

        //Копируем все непустые линии в список.
        for (int i = 0; i < height; i++) {
            //подсчитываем количество единиц в строке - просто суммируем все ее значения
            int count = 0;
            for (int j = 0; j < width; j++) {
                count += matrix[i][j];
            }

            //Если сумма строки не равно ее ширине - добавляем в список
            if (count != width)
                lines.add(matrix[i]);
        }

        //Добавляем недостающие строки в начало списка.
        while (lines.size() < height) {
            lines.add(0, new int[width]);
        }

        //Преобразуем список обратно в матрицу
        matrix = lines.toArray(new int[height][width]);
    }
}