• ,

task35.task3513 задание 12 (игра 2048)

Приветствую. Не получается правильно реализовать функцию сэйва и вследствие этого функцию «отмена последнего действия».
Вот условие этого уровня + предыдущего:


2048 (12)

Ну что, попробуем наш алгоритм в действии? Осталось добавить сохранение игрового состояния в начало каждого метода движения, а также еще один кейс для обработки клавиши, которой будем выполнять отмену последнего хода. При сохранении текущего состояния в стек, обрати внимание на то, чтобы всегда сохранялось актуальное состояние и только однажды. Если ты послушал мой совет и реализовал методы right, up, down с помощью поворотов и вызова метода left, можешь использовать следующий подход:

В самом начале методов right, up, down вызываем метод saveState с gameTiles в качестве параметра.

В методе left организуем проверку того, вызывался ли уже метод saveState. За это у нас отвечает флаг isSaveNeeded, соответственно, если он равен true, выполняем сохранение. После выполнения сдвига влево устанавливаем флаг isSaveNeeded равным true. Также добавим в метод keyPressed класса Controller вызов метода rollback по нажатию на клавишу Z (код — KeyEvent.VK_Z).

2048 (11) Отличная работа! На этом этапе у нас уже есть полнофункциональное приложение, но ведь нет предела совершенству, давай еще поработаем. Если ты успел какое-то время поиграть в 2048, то заметил, что порой очень хочется иметь возможность отменить последний ход. Давай создадим в классе Model два стека, в одном будем хранить предыдущие состояния игрового поля, а в другом предыдущие счета. Назовем их previousStates и previousScores. Инициализировать можешь прямо в строке объявления или в конструкторе. Используй стандартную реализацию стека (java.util.Stack). Добавим boolean поле isSaveNeeded = true, оно нам понадобится в будущем.

Хранилище состояний у нас есть, теперь реализуем два метода для работы с ними.

Приватный метод saveState с одним параметром типа Tile[][] будет сохранять текущее игровое состояние и счет в стеки с помощью метода push и устанавливать флаг isSaveNeeded равным false.

Публичный метод rollback будет устанавливать текущее игровое состояние равным последнему находящемуся в стеках с помощью метода pop. Обрати внимание на то, что при сохранении массива gameTiles необходимо создать новый массив и заполнить его новыми объектами типа Tile перед сохранением в стек. В методе rollback достаточно просто выполнить присваивание (gameTiles = previousStates.pop()) и то же для счета, нет необходимости в глубоком копировании. Перед восстановлением игрового состояния с помощью метода rollback не забудь проверить что стеки не пусты, чтобы избежать возникновения исключения EmptyStackException.


Вот мой код.
public class Model {
    private static final int FIELD_WIDTH = 4;
    private Tile[][] gameTiles;
    int score,maxTile;
    private boolean isSaveNeeded = true;
    private Stack<Tile[][]> previousStates=new Stack<>();
    private Stack<Integer> previousScores=new Stack<>();



    public Model() {

        resetGameTiles();
    }
    public boolean canMove() {
        if(!getEmptyTiles().isEmpty())
            return true;
        for(int i = 0; i < gameTiles.length; i++) {
            for(int j = 1; j < gameTiles.length; j++) {
                if(gameTiles[i][j].value == gameTiles[i][j-1].value)
                    return true;
            }
        }
        for(int j = 0; j < gameTiles.length; j++) {
            for(int i = 1; i < gameTiles.length; i++) {
                if(gameTiles[i][j].value == gameTiles[i-1][j].value)
                    return true;
            }
        }
        return false;
    }

    public Tile[][] getGameTiles() {
        return gameTiles;
    }

    private ArrayList<Tile> getEmptyTiles(){
        ArrayList<Tile> emptyTiles=new ArrayList<>();
        for (int m = 0; m < FIELD_WIDTH ; m++) {
            for (int n = 0; n <FIELD_WIDTH ; n++) {
                if(gameTiles[m][n].value==0)emptyTiles.add(gameTiles[m][n]);
            }
        }
        return emptyTiles;
    }
    public void rotateToRight(){
        for (int k=0; k<FIELD_WIDTH/2; k++)
        {
            for (int j=k; j<FIELD_WIDTH-1-k; j++)
            {
                Tile tmp = gameTiles[k][j];
                gameTiles[k][j]  = gameTiles[j][FIELD_WIDTH-1-k];
                gameTiles[j][FIELD_WIDTH-1-k] = gameTiles[FIELD_WIDTH-1-k][FIELD_WIDTH-1-j];
                gameTiles[FIELD_WIDTH-1-k][FIELD_WIDTH-1-j] = gameTiles[FIELD_WIDTH-1-j][k];
                gameTiles[FIELD_WIDTH-1-j][k]  = tmp;
            }
        }

    }
    private void saveState(Tile[][] tiles){
        Tile[][] bufTiles=new Tile[FIELD_WIDTH][FIELD_WIDTH];
        for (int i = 0; i < tiles.length; i++) {
            for (int j = 0; j < tiles[i].length; j++) {
                bufTiles[i][j]=tiles[i][j];
                bufTiles[i][j].value=tiles[i][j].value;
            }
        }
        previousStates.push(bufTiles);
        previousScores.push(this.score);
        isSaveNeeded=false;
    }
    public void rollback(){
        if(!previousScores.isEmpty()&&!previousStates.isEmpty()) {
            gameTiles = previousStates.pop();
            score=previousScores.pop();

        }
    }

    public void right(){
        saveState(this.gameTiles);
        rotateToRight();
        rotateToRight();
        left();
        rotateToRight();
        rotateToRight();
    }
    public void up(){
        saveState(this.gameTiles);
        rotateToRight();
        left();
        rotateToRight();
        rotateToRight();
        rotateToRight();
    }
    public void down(){
        saveState(this.gameTiles);
        rotateToRight();
        rotateToRight();
        rotateToRight();
        left();
        rotateToRight();
    }

    public void left(){
        if(isSaveNeeded)saveState(this.gameTiles);
        boolean isChanged = false;
        for (int i = 0; i < FIELD_WIDTH; i++) {
            if (compressTiles(gameTiles[i]) | mergeTiles(gameTiles[i])) {
                isChanged = true;
            }
        }
        isSaveNeeded=true;
        if (isChanged) addTile();
    }

    private void addTile(){
        ArrayList<Tile> emptyTiles=getEmptyTiles();
        if(!emptyTiles.isEmpty()) {
            Tile randomEmptyTile = emptyTiles.get((int) (emptyTiles.size() * Math.random()));
            randomEmptyTile.value = Math.random() < 0.9 ? 2 : 4;
        }
    }
    private boolean compressTiles(Tile[] tiles){
        boolean isChanged=false;
        for (int i = 1; i < tiles.length; i++) {
            for (int j = 1; j < tiles.length; j++) {
                if (tiles[j - 1].isEmpty() && !tiles[j].isEmpty()) {
                    tiles[j - 1] = tiles[j];
                    tiles[j] = new Tile();
                    isChanged=true;
                }
            }
        }
        return isChanged;
    }
    private boolean mergeTiles(Tile[] tiles){
        boolean isChanged=false;
        for (int i = 1; i < tiles.length; i++) {
            if ((tiles[i - 1].value == tiles[i].value) && !tiles[i - 1].isEmpty() && !tiles[i].isEmpty()) {

                tiles[i - 1].value *= 2;
                if(tiles[i-1].value>maxTile){
                    maxTile = tiles[i-1].value;
                }
                score += tiles[i - 1].value;
                tiles[i] = new Tile();
                isChanged=true;
                compressTiles(tiles);
            }
        }
        return isChanged;
    }


    public void resetGameTiles(){

        gameTiles=new Tile[FIELD_WIDTH][FIELD_WIDTH];
        for (int m = 0; m < FIELD_WIDTH ; m++) {
            for (int n = 0; n <FIELD_WIDTH ; n++) {
                gameTiles[m][n]=new Tile();
            }
        }
        score=0;
        maxTile=2;
        addTile();
        addTile();

    }
}

Игра 2048

Всем привет.
Увидел тут как-то консольную игрушку 2048 от timurnav . Поиграл. Интересно, но в консоли не удобно. Решил приделать графику, заодно потренироваться в JavaFX.
Вот что получилось.

Это в JavaFX8. В седьмой версии запустить jar не получилось из-за JavaFX. Но есть скрин из IDEA 14.1.2 с 7 SDK.

Может данный топик почитает кто-то с опытом программирования и напишет, как код, что можно изменить. Есть правда у меня такой минус — не люблю писать комментарии.
Вот ссылки на исходники: src.zip
Вот готовый jar: 2048_GUI.zip

Вот сделал новый jar, работает и в 7: 2048_GUI_fix1.zip

2048 на JAVA

Я тут между делом решил пару игр написать, начал с «крестики нолики», но немного подзавис на ней с изучением многопользовательности, пока изучал наткнулся на мысль, что могу сделать игру чтобы поиграть одному. самое яркое воспоминание это 2048, логика в ней довольно простая. по традиции решил начать писать ее для консоли.