com.pseudosudostudios.jdd.activities.GameActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.pseudosudostudios.jdd.activities.GameActivity.java

Source

/*
 * Copyright (c) 2015. Ben O'Halloran/Pseudo Sudo Studios.
 * All rights reserved.
 * This work is licensed under a Creative Commons Attribution-NonCommercial-NoDerivs 3.0 Unported License.
 * Full license can be found here: http://creativecommons.org/licenses/by-nc-nd/3.0/deed.en_US
 */
package com.pseudosudostudios.jdd.activities;

import android.app.Activity;
import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.content.pm.ApplicationInfo;
import android.graphics.Point;
import android.os.Bundle;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.widget.Toast;

import com.pseudosudostudios.jdd.R;
import com.pseudosudostudios.jdd.utils.Difficulty;
import com.pseudosudostudios.jdd.views.Grid;
import com.pseudosudostudios.jdd.views.Tile;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;

/**
 * The {@link android.app.Activity} that the game
 */
public class GameActivity extends Activity {

    //Views
    public Grid bg = null;

    //Keys and messages
    public final static String winMenuOption = "Win";
    public final static String isReplay = "replay string";
    public static final String hintMessage = "Hint";

    //Bundle Keys
    public static final String onSaveBaseString = "onsaved";
    public static final String onSaveSolution = "saveSolu";
    public static final String onSaveCeterTile = "center tile int array";
    public static final String onSaveMoves = "on save moves";
    public static final String onSaveTime = "on save times";
    public static final String instructionString = "Instructions";
    public static final String jsonTileSize = "tile size";
    public static final String bundleGameColors = "numberOfColorsBundle";

    //Save Data Keys
    public static final String saveDataFileName = "saved_game.txt";
    public static final String northKey = "north";
    public static final String eastKey = "east";
    public static final String southKey = "south";
    public static final String westKey = "west";
    public static final String pointX = "x";
    public static final String pointY = "y";
    public static final String moveKey = "moves";
    public static final String timeKey = "time";
    public static final String arrayKey = "tilesArray";
    public static final String levelKey = "level key";
    public static final String numColorsKey = "number of colors";

    /**
     * Relaunch stuffs, when the user backs into the app from the win screen
     */
    public static final int LAUNCH_WIN = 314;
    public static final int RELAUNCH = 235;

    /**
     * Creates the view, loads from a rotation and from a relaunch
     */
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // Remove the ActionBar, same logic as the drama app
        super.onCreate(savedInstanceState);

        bg = new Grid(this);
        setContentView(bg);
        // if there was a savedInstanceGame, then load that one
        final SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
        boolean hasScreen = prefs.getBoolean(instructionString, false);
        if (!hasScreen && savedInstanceState == null) {
            showInstructions();
            onCreateStuff(savedInstanceState);
        } else if (savedInstanceState != null && savedInstanceState.containsKey(onSaveBaseString + 0 + 0))
            onCreateStuff(savedInstanceState);
        else
            loadGamePrompt(savedInstanceState);

        scoreNoTime = 0;
        scorePrev = 0;

    }

    //Re-init a saved game from rotation or makes a new game if needed
    private void onCreateStuff(Bundle state) {
        // this is the re-load after rotating
        if (!loadFromBundle(state)) {
            bg.makeNewGame();
        }
    }

    private boolean loadFromBundle(Bundle state) {
        if (state == null)
            return false;
        Log.d("onCreateStuff", "Using bundle");
        Log.d("Loading", "Reading from bundle");
        // Reload the game after a screen rotation
        Tile.initPaints();
        Tile[][] loadedTiles = new Tile[3][3];
        Tile[][] loadedSol = new Tile[3][3];
        for (int r = 0; r < bg.tiles.length; r++)
            for (int c = 0; c < bg.tiles[r].length; c++) {
                loadedTiles[r][c] = new Tile(this, state.getIntArray(onSaveBaseString + r + c));
                int[] arr = state.getIntArray(onSaveSolution + r + c);
                if (arr != null)
                    loadedSol[r][c] = new Tile(this, arr);
            }
        int moves = state.getInt(onSaveMoves, 0);
        long time = state.getLong(onSaveTime, 0L);
        bg.setTileArray(loadedTiles, loadedSol, moves, time, state.getInt(jsonTileSize, 0));
        bg.setDifficulty(findDifficulty(state.getString(onSaveBaseString)));
        Grid.numberOfColors = state.getInt(bundleGameColors);
        bg.setCenterTile(new Tile(this, state.getIntArray(onSaveCeterTile)));
        Tile.initPaints();
        bg.invalidate();
        return true;
    }

    /**
     * This handles the initial display and writing the boolean to the shared prefs
     */
    private void showInstructions() {
        if (bg.isReadyToPlay())
            return;
        final SharedPreferences prefs = getPreferences(Context.MODE_PRIVATE);
        boolean skipInstructions = prefs.getBoolean(instructionString, false);

        if (!skipInstructions) {
            Builder d = new Builder(this);
            d.setCancelable(false).setMessage(R.string.instructions).setTitle(R.string.instructions_title)
                    .setCancelable(false)
                    .setPositiveButton("Show next time", new DialogInterface.OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Editor edit = prefs.edit();
                            edit.putBoolean(instructionString, false);
                            edit.commit();
                        }
                    }).setNegativeButton("Don't show again", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Editor edit = prefs.edit();
                            edit.putBoolean(instructionString, true);
                            edit.commit();
                        }
                    }).show();
        }
    }

    /**
     * Prompts the user to see if they want to load a saved game
     */
    private void loadGamePrompt(final Bundle saved) {
        File f = new File(getFilesDir(), saveDataFileName);
        if (!f.exists()) {
            onCreateStuff(saved);
            bg.setTileSize();
            bg.setCenters();
            bg.invalidate();
            return;
        } else if (!isSavedGameSolved()) {
            Builder d = new Builder(this);

            d.setCancelable(false).setMessage(R.string.load_game_prompt).setTitle(R.string.load_game_title)
                    .setPositiveButton(R.string.load_game_load, new DialogInterface.OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Log.d("Load", "Calling loadGame");
                            loadGame(saved);
                            bg.invalidate();
                        }
                    }).setNegativeButton(R.string.load_game_new, new DialogInterface.OnClickListener() {

                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            onCreateStuff(null);
                        }
                    });
            d.show();
        } else {
            onCreateStuff(null);
        }
    }

    @Override
    protected void onPause() {
        super.onPause();
        saveToFile();
    }

    public void saveToFile() {
        if (bg == null)
            return;
        Log.i("Save game", "Saving game");
        File outFile = null;
        try {
            JSONObject obj = bg.toJSONObject();
            outFile = new File(getFilesDir(), saveDataFileName);
            if (!outFile.exists())
                outFile.createNewFile();
            BufferedWriter writer = new BufferedWriter(new FileWriter(outFile));
            writer.write(obj.toString());
            writer.close();
            Log.i("Save game", "Game Saved");
        } catch (JSONException e) {
            Log.i("Save game", "Error saving game-JSON");
            e.printStackTrace();
        } catch (IOException e) {
            Log.i("Save game", "Error saving game - writing");
            e.printStackTrace();
        } catch (NullPointerException e) {
            if (outFile != null)
                outFile.delete();
        }
    }

    private boolean isSavedGameSolved() {
        return true;
        /*
         * File dataFile = new File(getFilesDir(), saveDataFileName); try {
        * BufferedReader read = new BufferedReader(new FileReader(dataFile));
        * String input = ""; while (read.ready()) input += read.readLine();
        * read.close(); JSONObject master = new JSONObject(input); JSONArray
        * tilesFromFile = master.getJSONArray(arrayKey); Tile[][] loadedTile =
        * new Tile[3][3]; int jsonIndex = 0; for (int row = 0; row <
        * loadedTile.length; row++) { for (int col = 0; col <
        * loadedTile[row].length; col++) { JSONObject currTile = tilesFromFile
        * .getJSONObject(jsonIndex); Point center = new
        * Point(currTile.getInt(pointX), currTile.getInt(pointY)); int north =
        * currTile.getInt(northKey); int east = currTile.getInt(eastKey); int
        * south = currTile.getInt(southKey); int west =
        * currTile.getInt(westKey); loadedTile[row][col] = new Tile(this,
        * center, north, east, south, west);
        *
        * jsonIndex++; } } jsonIndex = 0; return
        * bg.isPuzzleSolved(loadedTile); } catch (Exception e) {
        * e.printStackTrace(); return false; }
        */
    }

    private void loadGame(Bundle saved) {
        Log.i("Load game", "Loading game");
        if (bg == null)
            bg = new Grid(this);
        File dataFile = new File(getFilesDir(), saveDataFileName);
        try {
            BufferedReader read = new BufferedReader(new FileReader(dataFile));
            String input = "";
            while (read.ready())
                input += read.readLine();
            read.close();

            JSONObject master = new JSONObject(input);
            JSONArray tilesFromFile = master.getJSONArray(arrayKey);
            Tile[][] loadedTile = new Tile[3][3];
            Tile[][] solution = new Tile[3][3];
            int jsonIndex = 0;
            for (int row = 0; row < loadedTile.length; row++) {
                for (int col = 0; col < loadedTile[row].length; col++) {
                    JSONObject currTile = tilesFromFile.getJSONObject(jsonIndex);
                    Point center = new Point(currTile.getInt(pointX), currTile.getInt(pointY));
                    int north = currTile.getInt(northKey);
                    int east = currTile.getInt(eastKey);
                    int south = currTile.getInt(southKey);
                    int west = currTile.getInt(westKey);
                    loadedTile[row][col] = new Tile(this, center, north, east, south, west);

                    jsonIndex++;
                }
            }
            jsonIndex = 0;
            JSONArray solJSon = master.getJSONArray(onSaveSolution);
            for (int row = 0; row < solution.length; row++) {
                for (int col = 0; col < solution[row].length; col++) {
                    JSONObject currTile = solJSon.getJSONObject(jsonIndex);
                    Point center = new Point(currTile.getInt(pointX), currTile.getInt(pointY));
                    int north = currTile.getInt(northKey);
                    int east = currTile.getInt(eastKey);
                    int south = currTile.getInt(southKey);
                    int west = currTile.getInt(westKey);
                    solution[row][col] = new Tile(this, center, north, east, south, west);
                    jsonIndex++;
                }
            }
            bg.moves = master.getInt(moveKey);
            bg.setTileArray(loadedTile, solution, master.getInt(moveKey), master.getLong(onSaveTime),
                    master.getInt(jsonTileSize));
            bg.setDifficulty(master.getString(levelKey));
            Grid.numberOfColors = master.getInt(numColorsKey);
            Tile.initPaints();
            Log.i("Load game", "Game Loaded");
        } catch (Exception e) {
            Log.i("Load game", "Error loading game");
            if (dataFile != null)
                dataFile.delete();
            e.printStackTrace();
            showInstructions();
        }
        bg.invalidate();
    }

    public static Difficulty findDifficulty(String string) {
        if (string.equals(Difficulty.EASY.toString()))
            return Difficulty.EASY;
        else if (string.equals(Difficulty.MEDIUM.toString()))
            return Difficulty.MEDIUM;
        return Difficulty.HARD;
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_game_activity, menu);
        if (!isUserDebuggable())
            menu.removeItem(R.id.action_win);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.action_new_game:
            startNewGame();
            return true;
        case R.id.action_instructions:
            startActivity(new Intent(this, Instructions.class));
            return true;
        case R.id.action_win:
            win(bg.moves);
            return true;
        default:
            Log.w("Unkown menu item", item.toString());
            return false;
        }
    }

    private static long lastBackTime = -1;

    @Override
    public void onBackPressed() {
        if (bg != null) {
            if (lastBackTime == -1) {
                lastBackTime = System.currentTimeMillis();
                Toast.makeText(this, R.string.back_press_toast, Toast.LENGTH_SHORT).show();
            } else if ((System.currentTimeMillis() - lastBackTime) / 1000L <= 5) {
                lastBackTime = -1;
                bg.makeNewGame();
            }
        } else
            super.onBackPressed();
    }

    public boolean isUserDebuggable() {
        return (0 != (getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE));
    }

    @Override
    protected void onSaveInstanceState(Bundle outState) {
        if (bg.tiles == null || bg.tiles[2][2] == null || bg == null) {
            super.onSaveInstanceState(outState);
            return;
        }
        for (int r = 0; r < bg.tiles.length; r++)
            for (int c = 0; c < bg.tiles[r].length; c++) {
                outState.putIntArray(onSaveBaseString + r + c, bg.tiles[r][c].toIntArray());
                if (bg.sol1 != null && bg.sol1[r][c] != null)
                    outState.putIntArray(onSaveSolution + r + c, bg.sol1[r][c].toIntArray());
            }
        try {
            outState.putIntArray(onSaveCeterTile, bg.getCenterTile().toIntArray());
        } catch (NullPointerException e) {
            outState.putIntArray(onSaveCeterTile, bg.tiles[1][1].toIntArray());
        }
        outState.putInt(bundleGameColors, Grid.numberOfColors);
        outState.putInt(jsonTileSize, Grid.tileSize);
        outState.putString(onSaveBaseString, bg.getDifficulty().toString());
        outState.putInt(bundleGameColors, Grid.numberOfColors);
        outState.putLong(onSaveTime, bg.getTimeSinceStart());
    }

    /**
     * Launches the win screen flow, including dealing with Facebook
     */
    public void win(int moves) {
        new File(getFilesDir(), saveDataFileName).delete();
        Log.i("Win", "Time: " + bg.getTimeSinceStart());
        Bundle bundle = new Bundle();
        bundle.putInt(WinActivity.moveKey, moves);
        bundle.putString(WinActivity.levelKey, bg.getDifficulty().toString());
        bundle.putLong(WinActivity.timeKey, ((System.currentTimeMillis() - bg.startTime)));
        startActivityForResult(new Intent(this, WinActivity.class).putExtras(bundle), LAUNCH_WIN);
    }

    /**
     * This is the dumb method that creates a new game
     */
    public void startNewGame() {
        bg.makeNewGame();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        Log.d("Result code", resultCode + "");
        if (requestCode == LAUNCH_WIN) {
            startNewGame();
        }
    }

    /**
     * @return the score
     * @param time
     * the DELTA of the times
     * @param moves
     * the number of moves made to solve the puzzle
     */
    static int scorePrev = 0;
    static int scoreNoTime = 0;
    static final int scoreMatchMult = 20000;

    public int calcScoreBenMod(int moves, long time, int numColors, String level, int matches) {
        double num = levelScoreMod(level) * (200D / moves) + (30 * 60 * 100D / (time + 5));
        double denom = 1 + Math.pow(7 - numColors, 2);
        return (int) Math.abs((matches * (num / denom)));
    }

    public int benScoreCalc(int moves, long time) {
        int fromColors = 2500 / (Math.abs(7 - Grid.numberOfColors) + 1);
        int fromLevel = 100 * levelScoreMod(bg.getDifficulty());
        int fromParams = (int) (time / 1000) + moves * 10;

        int score = fromColors + fromLevel - fromParams + 5000;
        if (score < 0)
            return 0;
        else
            return score;
    }

    public int levelScoreMod(String level) {
        return levelScoreMod(Difficulty.valueOf(level));
    }

    public int levelScoreMod(Difficulty d) {
        if (d == Difficulty.HARD)
            return 16;
        else if (d == Difficulty.MEDIUM)
            return 4;
        else
            return 1;
    }
}