com.eightpuzzle.game.EightPuzzle.java Source code

Java tutorial

Introduction

Here is the source code for com.eightpuzzle.game.EightPuzzle.java

Source

package com.eightpuzzle.game;

/*******************************************************************************
 * Copyright 2011 See AUTHORS file.
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *   http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 ******************************************************************************/

import java.util.ArrayList;
import java.util.Deque;
import java.util.HashMap;
import java.util.List;

import com.badlogic.gdx.Application.ApplicationType;
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.files.FileHandle;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Pixmap;
import com.badlogic.gdx.graphics.Pixmap.Format;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator;
import com.badlogic.gdx.graphics.g2d.freetype.FreeTypeFontGenerator.FreeTypeFontParameter;
import com.badlogic.gdx.scenes.scene2d.Actor;
import com.badlogic.gdx.scenes.scene2d.InputEvent;
import com.badlogic.gdx.scenes.scene2d.Stage;
import com.badlogic.gdx.scenes.scene2d.Touchable;
import com.badlogic.gdx.scenes.scene2d.ui.Button;
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton;
import com.badlogic.gdx.scenes.scene2d.ui.ImageButton.ImageButtonStyle;
import com.badlogic.gdx.scenes.scene2d.ui.ImageTextButton;
import com.badlogic.gdx.scenes.scene2d.ui.Label;
import com.badlogic.gdx.scenes.scene2d.ui.Skin;
import com.badlogic.gdx.scenes.scene2d.ui.Table;
import com.badlogic.gdx.scenes.scene2d.ui.TextButton.TextButtonStyle;
import com.badlogic.gdx.scenes.scene2d.ui.VerticalGroup;
import com.badlogic.gdx.scenes.scene2d.ui.Window;
import com.badlogic.gdx.scenes.scene2d.ui.Window.WindowStyle;
import com.badlogic.gdx.scenes.scene2d.utils.ChangeListener;
import com.badlogic.gdx.scenes.scene2d.utils.ClickListener;
import com.badlogic.gdx.scenes.scene2d.utils.TextureRegionDrawable;
import com.badlogic.gdx.utils.Timer;
import com.badlogic.gdx.utils.viewport.ExtendViewport;
import com.eightpuzzle.solve.EightPuzzleSolver;
import com.eightpuzzle.solve.PuzzleNode;

public class EightPuzzle implements ApplicationListener {
    Skin skin;
    Stage stage;
    final Table table = new Table();
    private ImageTextButton newGameB;
    private ImageTextButton solveB;
    private final int GAME_WIDTH = 480;//1920;
    private final int GAME_HEIGHT = 800;//1200;
    private float aspectRatio;
    private SpriteBatch batch;
    private boolean isSolved;
    private BitmapFont gameFont;
    private BitmapFont solvedFont;

    @Override
    public void create() {
        gameFont = new BitmapFont();
        solvedFont = new BitmapFont();
        batch = new SpriteBatch();
        aspectRatio = (float) Gdx.graphics.getHeight() / (float) Gdx.graphics.getWidth();
        OrthographicCamera camera = new OrthographicCamera(GAME_WIDTH * aspectRatio, GAME_HEIGHT * aspectRatio);
        //camera.position.set(GAME_WIDTH/2, GAME_HEIGHT/2, 0)
        camera.setToOrtho(false, GAME_WIDTH * aspectRatio, GAME_HEIGHT * aspectRatio);
        //camera.setToOrtho(false,GAME_WIDTH,GAME_HEIGHT);
        stage = new Stage(new ExtendViewport(GAME_WIDTH * aspectRatio, GAME_HEIGHT * aspectRatio, camera));//Gdx.graphics.getWidth(), Gdx.graphics.getHeight(), camera));
        Gdx.input.setInputProcessor(stage);

        // A skin can be loaded via JSON or defined programmatically, either is fine. Using a skin is optional but strongly
        // recommended solely for the convenience of getting a texture, region, etc as a drawable, tinted drawable, etc.
        skin = new Skin();

        // Generate a 1x1 white texture and store it in the skin named "white".
        Pixmap pixmap = new Pixmap(1, 1, Format.RGBA8888);
        pixmap.setColor(Color.WHITE);
        pixmap.fill();
        skin.add("white", new Texture(pixmap));

        // Store the default libgdx font under the name "default".
        skin.add("default", new BitmapFont());

        // Configure a TextButtonStyle and name it "default". Skin resources are stored by type, so this doesn't overwrite the font.
        TextButtonStyle textButtonStyle = new TextButtonStyle();
        textButtonStyle.up = skin.newDrawable("white", Color.DARK_GRAY);
        textButtonStyle.down = skin.newDrawable("white", Color.DARK_GRAY);
        //textButtonStyle.checked = skin.newDrawable("white", Color.BLUE);
        textButtonStyle.over = skin.newDrawable("white", Color.LIGHT_GRAY);
        textButtonStyle.font = skin.getFont("default");
        skin.add("default", textButtonStyle);

        Label.LabelStyle labelSty = new Label.LabelStyle();
        labelSty.font = skin.getFont("default");
        //labelSty.fontColor = Color.GREEN;
        skin.add("default", labelSty);

        WindowStyle ws = new Window.WindowStyle();
        ws.titleFont = new BitmapFont();
        ws.background = skin.newDrawable("white", Color.BLACK);
        skin.add("default", ws);

        FreeTypeFontGenerator generator = new FreeTypeFontGenerator(Gdx.files.internal("fonts/Zig.ttf"));

        FreeTypeFontParameter parameter = new FreeTypeFontParameter();
        parameter.size = 25;
        gameFont = generator.generateFont(parameter);

        FreeTypeFontParameter parameter2 = new FreeTypeFontParameter();
        parameter2.size = 100;
        parameter2.color = Color.GREEN;
        solvedFont = generator.generateFont(parameter2);
        generator.dispose();

        FileHandle blue, n1, n2, n3, n4, n5, n6, n7, n8, solveUp, solveDown;
        if (Gdx.app.getType() == ApplicationType.Desktop) {
            blue = Gdx.files.internal("icons64px/blue-square.png");
            n1 = Gdx.files.internal("icons64px/Number-1-icon.png");
            n2 = Gdx.files.internal("icons64px/Number-2-icon.png");
            n3 = Gdx.files.internal("icons64px/Number-3-icon.png");
            n4 = Gdx.files.internal("icons64px/Number-4-icon.png");
            n5 = Gdx.files.internal("icons64px/Number-5-icon.png");
            n6 = Gdx.files.internal("icons64px/Number-6-icon.png");
            n7 = Gdx.files.internal("icons64px/Number-7-icon.png");
            n8 = Gdx.files.internal("icons64px/Number-8-icon.png");
            solveUp = blue;
            solveDown = blue;
        } else {
            blue = Gdx.files.internal("icons256px/blue-circle.png");
            n1 = Gdx.files.internal("icons256px/Number-1-icon.png");
            n2 = Gdx.files.internal("icons256px/Number-2-icon.png");
            n3 = Gdx.files.internal("icons256px/Number-3-icon.png");
            n4 = Gdx.files.internal("icons256px/Number-4-icon.png");
            n5 = Gdx.files.internal("icons256px/Number-5-icon.png");
            n6 = Gdx.files.internal("icons256px/Number-6-icon.png");
            n7 = Gdx.files.internal("icons256px/Number-7-icon.png");
            n8 = Gdx.files.internal("icons256px/Number-8-icon.png");
            solveUp = Gdx.files.internal("icons256px/Box_Green.png");
            solveDown = Gdx.files.internal("icons256px/rectangle_green.png");
        }
        Texture bSquare = new Texture(blue);
        Texture n1T = new Texture(n1);
        Texture n2T = new Texture(n2);
        Texture n3T = new Texture(n3);
        Texture n4T = new Texture(n4);
        Texture n5T = new Texture(n5);
        Texture n6T = new Texture(n6);
        Texture n7T = new Texture(n7);
        Texture n8T = new Texture(n8);

        TextureRegion bSquareReg = new TextureRegion(bSquare);
        TextureRegion n1Reg = new TextureRegion(n1T);
        TextureRegion n2Reg = new TextureRegion(n2T);
        TextureRegion n3Reg = new TextureRegion(n3T);
        TextureRegion n4Reg = new TextureRegion(n4T);
        TextureRegion n5Reg = new TextureRegion(n5T);
        TextureRegion n6Reg = new TextureRegion(n6T);
        TextureRegion n7Reg = new TextureRegion(n7T);
        TextureRegion n8Reg = new TextureRegion(n8T);

        TextureRegionDrawable solveDUp = new TextureRegionDrawable(new TextureRegion(new Texture(solveUp)));
        TextureRegionDrawable solveDDown = new TextureRegionDrawable(new TextureRegion(new Texture(solveDown)));
        ImageTextButton.ImageTextButtonStyle tbSty = new ImageTextButton.ImageTextButtonStyle(solveDUp, solveDDown,
                solveDUp, gameFont);
        skin.add("default", tbSty);

        ImageButtonStyle bSquareSty = new ImageButtonStyle();
        bSquareSty.imageUp = new TextureRegionDrawable(bSquareReg);
        bSquareSty.imageDown = new TextureRegionDrawable(bSquareReg);

        ImageButtonStyle n1Sty = new ImageButtonStyle();
        n1Sty.imageUp = new TextureRegionDrawable(n1Reg);
        n1Sty.imageDown = new TextureRegionDrawable(n1Reg);

        ImageButtonStyle n2Sty = new ImageButtonStyle();
        n2Sty.imageUp = new TextureRegionDrawable(n2Reg);
        n2Sty.imageDown = new TextureRegionDrawable(n2Reg);

        ImageButtonStyle n3Sty = new ImageButtonStyle();
        n3Sty.imageUp = new TextureRegionDrawable(n3Reg);
        n3Sty.imageDown = new TextureRegionDrawable(n3Reg);

        ImageButtonStyle n4Sty = new ImageButtonStyle();
        n4Sty.imageUp = new TextureRegionDrawable(n4Reg);
        n4Sty.imageDown = new TextureRegionDrawable(n4Reg);

        ImageButtonStyle n5Sty = new ImageButtonStyle();
        n5Sty.imageUp = new TextureRegionDrawable(n5Reg);
        n5Sty.imageDown = new TextureRegionDrawable(n5Reg);

        ImageButtonStyle n6Sty = new ImageButtonStyle();
        n6Sty.imageUp = new TextureRegionDrawable(n6Reg);
        n6Sty.imageDown = new TextureRegionDrawable(n6Reg);

        ImageButtonStyle n7Sty = new ImageButtonStyle();
        n7Sty.imageUp = new TextureRegionDrawable(n7Reg);
        n7Sty.imageDown = new TextureRegionDrawable(n7Reg);

        ImageButtonStyle n8Sty = new ImageButtonStyle();
        n8Sty.imageUp = new TextureRegionDrawable(n8Reg);
        n8Sty.imageDown = new TextureRegionDrawable(n8Reg);

        ImageButton b1 = new ImageButton(n1Sty);
        b1.addListener(new MyChangeListener(1));
        map.put(1, b1);

        ImageButton holeB = new ImageButton(bSquareSty);
        map.put(0, holeB);

        ImageButton ib = new ImageButton(n2Sty);
        ib.addListener(new MyChangeListener(2));
        map.put(2, ib);

        ImageButton b4 = new ImageButton(n3Sty);
        b4.addListener(new MyChangeListener(3));
        ImageButton b5 = new ImageButton(n4Sty);
        b5.addListener(new MyChangeListener(4));
        ImageButton b6 = new ImageButton(n5Sty);
        b6.addListener(new MyChangeListener(5));
        map.put(3, b4);
        map.put(4, b5);
        map.put(5, b6);

        ImageButton b7 = new ImageButton(n6Sty);
        b7.addListener(new MyChangeListener(6));
        ImageButton b8 = new ImageButton(n7Sty);
        b8.addListener(new MyChangeListener(7));
        ImageButton b9 = new ImageButton(n8Sty);
        b9.addListener(new MyChangeListener(8));
        map.put(6, b7);
        map.put(7, b8);
        map.put(8, b9);

        newGameB = new ImageTextButton("New Game", skin);
        newGameB.addListener(new NewGameListener());

        solveB = new ImageTextButton("Solve", skin);
        solveB.addListener(new MySolveListener());

        newGameBoard();

        //table.padTop(50);
        //table.padBottom(20);

        VerticalGroup vg = new VerticalGroup();
        //vg.padTop(50);
        vg.setFillParent(true);
        vg.addActor(newGameB);
        vg.addActor(table);
        vg.addActor(solveB);

        stage.addActor(vg);
    }

    @Override
    public void render() {
        Gdx.gl.glClearColor(0.2f, 0.2f, 0.2f, 1);
        Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
        Gdx.gl.glViewport(0, 0, Gdx.graphics.getWidth(), Gdx.graphics.getHeight());
        stage.act(Math.min(Gdx.graphics.getDeltaTime(), 1 / 30f));
        stage.draw();
        if (isSolved) {
            batch.begin();
            solvedFont.draw(batch, "Solved!", GAME_WIDTH * aspectRatio / 2 - (GAME_WIDTH * aspectRatio / 4),
                    GAME_HEIGHT * aspectRatio / (float) 1.7);
            batch.end();
        }
    }

    private void newGameBoard() {
        state = EightPuzzleGenerator.newPuzzle();
        holeIndex = state.lastIndexOf(0);
        updateTable();
    }

    private void updateTable() {
        for (int i = 0; i < state.size(); i += 3) {
            table.add(map.get(state.get(i)));
            table.add(map.get(state.get(i + 1)));
            table.add(map.get(state.get(i + 2)));
            table.row().fillX();
        }
        isSolved = state.equals(PuzzleNode.GOAL);
    }

    @Override
    public void resize(int width, int height) {
        stage.getViewport().update(width, height, true);
    }

    @Override
    public void dispose() {
        stage.dispose();
        skin.dispose();
    }

    private HashMap<Integer, Button> map = new HashMap<Integer, Button>();
    private List<Integer> state = new ArrayList<Integer>();
    private int holeIndex;

    public class MyChangeListener extends ChangeListener {
        private int value;

        public MyChangeListener(int val) {
            value = val;
        }

        @Override
        public void changed(ChangeEvent event, Actor actor) {
            int index = state.indexOf(value);
            boolean isLegalMove = false;
            if (holeIndex == index - 3 || holeIndex == index + 3)
                isLegalMove = true;
            else if ((index != 2 && index != 5) && holeIndex == index + 1)
                isLegalMove = true;
            else if ((index != 3 && index != 6) && holeIndex == index - 1)
                isLegalMove = true;

            if (isLegalMove) {
                int tmp = state.get(index);
                state.set(index, state.get(holeIndex));
                state.set(holeIndex, tmp);
                tmp = holeIndex;
                holeIndex = index;
                //value = tmp;
                updateTable();
            }
        }

    }

    public class MySolveListener extends ClickListener {
        @Override
        public void clicked(InputEvent a, float x, float y) {
            newGameB.setTouchable(Touchable.disabled);
            solveB.setTouchable(Touchable.disabled);
            Deque<List<Integer>> solutionStack = EightPuzzleSolver.solve(new PuzzleNode(null, state));
            int delaySec = 1;
            while (!solutionStack.isEmpty()) {
                final List<Integer> nextMove = solutionStack.removeFirst();
                Timer.Task t = new Timer.Task() {

                    @Override
                    public void run() {
                        state = nextMove;
                        if (state.equals(PuzzleNode.GOAL)) {
                            solveB.setTouchable(Touchable.enabled);
                            newGameB.setTouchable(Touchable.enabled);
                            holeIndex = 8;
                        }
                        updateTable();
                    }
                };
                new Timer().scheduleTask(t, delaySec++);
            }
        }
    }

    private class NewGameListener extends ClickListener {
        @Override
        public void clicked(InputEvent ie, float x, float y) {
            newGameBoard();
        }
    }

    @Override
    public void pause() {
        // TODO Auto-generated method stub

    }

    @Override
    public void resume() {
        // TODO Auto-generated method stub

    }
}