diff --git a/app/src/main/java/tu_darmstadt/sudoku/controller/GameController.java b/app/src/main/java/tu_darmstadt/sudoku/controller/GameController.java index 61fc847..0724abe 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/controller/GameController.java +++ b/app/src/main/java/tu_darmstadt/sudoku/controller/GameController.java @@ -37,7 +37,7 @@ public class GameController implements IModelChangedListener { private int gameID = 0; private GameDifficulty difficulty; private CellConflictList errorList = new CellConflictList(); - private DoUndo doUndo; + private UndoRedoManager undoRedoManager; private int selectedValue; private LinkedList solvedListeners = new LinkedList<>(); private boolean notifiedOnSolvedListeners = false; @@ -74,13 +74,13 @@ public class GameController implements IModelChangedListener { } public void loadNewLevel(GameType type, GameDifficulty difficulty) { - SaveLoadLevelManager saveLoadLevelManager = SaveLoadLevelManager.getInstance(); + NewLevelManager newLevelManager = NewLevelManager.getInstance(); - int[] level = saveLoadLevelManager.loadLevel(type, difficulty); + int[] level = newLevelManager.loadLevel(type, difficulty); loadLevel(new GameInfoContainer(0, difficulty, type, level, null, null)); - saveLoadLevelManager.checkAndRestock(); + newLevelManager.checkAndRestock(); } public int getTime() { @@ -126,7 +126,7 @@ public class GameController implements IModelChangedListener { gameBoard.registerOnModelChangeListener(this); - doUndo = new DoUndo(gameBoard); + undoRedoManager = new UndoRedoManager(gameBoard); // call the solve function to get the solution of this board //qqWingController.solve(gameBoard); } @@ -232,7 +232,7 @@ public class GameController implements IModelChangedListener { } //gameID now has a value other than 0 and hopefully unique - SaveLoadGameStateController fm = new SaveLoadGameStateController(context, settings); + GameStateManager fm = new GameStateManager(context, settings); fm.saveGameState(this); } @@ -353,7 +353,7 @@ public class GameController implements IModelChangedListener { if(isValidCellSelected() && getSelectedValue() != value) { setValue(selectedRow, selectedCol, value); // add state to undo - doUndo.addState(gameBoard); + undoRedoManager.addState(gameBoard); } } @@ -362,7 +362,7 @@ public class GameController implements IModelChangedListener { if(isValidCellSelected() && getSelectedValue() != 0) { deleteValue(selectedRow, selectedCol); // add state to undo - doUndo.addState(gameBoard); + undoRedoManager.addState(gameBoard); } } @@ -371,7 +371,7 @@ public class GameController implements IModelChangedListener { if(isValidCellSelected()) { toggleNote(selectedRow, selectedCol, value); // add state to undo - doUndo.addState(gameBoard); + undoRedoManager.addState(gameBoard); } } @@ -474,18 +474,18 @@ public class GameController implements IModelChangedListener { } public void ReDo() { - updateGameBoard(doUndo.ReDo()); + updateGameBoard(undoRedoManager.ReDo()); } public void UnDo() { - updateGameBoard(doUndo.UnDo()); + updateGameBoard(undoRedoManager.UnDo()); } public boolean isRedoAvailable() { - return doUndo.isRedoAvailable(); + return undoRedoManager.isRedoAvailable(); } public boolean isUndoAvailable() { - return doUndo.isUnDoAvailable(); + return undoRedoManager.isUnDoAvailable(); } public void updateGameBoard(final GameBoard gameBoard) { diff --git a/app/src/main/java/tu_darmstadt/sudoku/controller/SaveLoadGameStateController.java b/app/src/main/java/tu_darmstadt/sudoku/controller/GameStateManager.java similarity index 97% rename from app/src/main/java/tu_darmstadt/sudoku/controller/SaveLoadGameStateController.java rename to app/src/main/java/tu_darmstadt/sudoku/controller/GameStateManager.java index 358bfd8..6b41d41 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/controller/SaveLoadGameStateController.java +++ b/app/src/main/java/tu_darmstadt/sudoku/controller/GameStateManager.java @@ -16,7 +16,7 @@ import tu_darmstadt.sudoku.controller.helper.GameInfoContainer; /** * Created by Chris on 16.11.2015. */ -public class SaveLoadGameStateController { +public class GameStateManager { Context context; private SharedPreferences settings; @@ -28,7 +28,7 @@ public class SaveLoadGameStateController { private static List list = new LinkedList<>(); - public SaveLoadGameStateController(Context context, SharedPreferences settings) { + public GameStateManager(Context context, SharedPreferences settings) { this.context = context; this.settings = settings; } diff --git a/app/src/main/java/tu_darmstadt/sudoku/controller/NewLevelManager.java b/app/src/main/java/tu_darmstadt/sudoku/controller/NewLevelManager.java new file mode 100644 index 0000000..0ad9e20 --- /dev/null +++ b/app/src/main/java/tu_darmstadt/sudoku/controller/NewLevelManager.java @@ -0,0 +1,281 @@ +package tu_darmstadt.sudoku.controller; + +import android.content.Context; +import android.content.SharedPreferences; +import android.os.AsyncTask; +import android.util.Log; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.util.LinkedList; +import java.util.List; + +import tu_darmstadt.sudoku.game.GameDifficulty; +import tu_darmstadt.sudoku.game.GameType; + +/** + * Created by Chris on 23.11.2015. + */ +public class NewLevelManager { + + Context context; + private SharedPreferences settings; + + private static NewLevelManager instance; + + private static String FILE_EXTENSION = ".txt"; + private static String LEVEL_PREFIX = "level_"; + private static String LEVELS_DIR = "level"; + private static File DIR; + + public static NewLevelManager getInstance() { + return instance; + } + public static NewLevelManager init(Context context, SharedPreferences settings) { + if(instance == null) { + instance = new NewLevelManager(context, settings); + } + return instance; + } + + private NewLevelManager(Context context, SharedPreferences settings) { + this.context = context; + this.settings = settings; + DIR = context.getDir(LEVELS_DIR, 0); + } + + public boolean isLevelLoadable(GameType type, GameDifficulty diff) { + for(File file : DIR.listFiles()) { + if (file.isFile()) { + String name = file.getName().substring(0, file.getName().lastIndexOf("_")); + + StringBuilder sb = new StringBuilder(); + sb.append(LEVEL_PREFIX); + sb.append(type.name()); + sb.append("_"); + sb.append(diff.name()); + + if(name.equals(sb.toString())) { + return true; + } + } + } + return false; + } + + public int[] loadLevel(GameType type, GameDifficulty diff) { + List result = new LinkedList<>(); + LinkedList availableFiles = new LinkedList<>(); + + // go through every file + for(File file : DIR.listFiles()) { + + // filter so we only work with actual files + if (file.isFile()) { + String name = file.getName().substring(0, file.getName().lastIndexOf("_")); + String number = file.getName().substring(file.getName().lastIndexOf("_")+1, file.getName().lastIndexOf(".")); + + StringBuilder sb = new StringBuilder(); + sb.append(LEVEL_PREFIX); + sb.append(type.name()); + sb.append("_"); + sb.append(diff.name()); + + // if file is a level for our gametype and difficulty .. load it + if(name.equals(sb.toString())) { + + // load file + byte[] bytes = new byte[(int)file.length()]; + try { + FileInputStream stream = new FileInputStream(file); + try { + stream.read(bytes); + } finally { + stream.close(); + } + } catch(IOException e) { + Log.e("File Manager", "Could not load game. IOException occured."); + } + + // start parsing + String gameString = new String(bytes); + + int[] puzzle = new int[type.getSize()*type.getSize()]; + + if(puzzle.length != gameString.length()) { + throw new IllegalArgumentException("Saved level is does not have the correct size."); + } + + for(int i = 0; i < gameString.length(); i++) { + puzzle[i] = Symbol.getValue(Symbol.SaveFormat, String.valueOf(gameString.charAt(i)))+1; + } + availableFiles.add(Integer.valueOf(number)); + result.add(puzzle); + } + } + } + + if(result.size() > 0) { + int chosen = availableFiles.get(0); + int[] resultPuzzle = result.get(0); + + StringBuilder sb = new StringBuilder(); + sb.append(LEVEL_PREFIX); + sb.append(type.name()); + sb.append("_"); + sb.append(diff.name()); + sb.append("_"); + sb.append(chosen); + sb.append(FILE_EXTENSION); + String filename = sb.toString(); + + // select and delete the file + File file = new File(DIR, filename); + file.delete(); + + // then return the puzzle to load it + return resultPuzzle; + } + + // TODO: make the UI wait. Or just generate a level now. + return null; + } + + public void checkAndRestock() { + new AsyncGenerationTask().execute(); + } + + private class AsyncGenerationTask extends AsyncTask { + @Override + protected void onPreExecute() { + super.onPreExecute(); + } + + @Override + protected String doInBackground(int[][] ... params) { + int preSaves = 5; + // init + final LinkedList gameTypes = GameType.getValidGameTypes(); + final LinkedList gameDifficulties = GameDifficulty.getValidDifficultyList(); + final LinkedList missing = new LinkedList<>(); + for(int i = 0; i < gameTypes.size(); i++) { + for(int j = 0; j < gameDifficulties.size(); j++) { + for(int k = 0; k < preSaves ; k++) { + int[] m = new int[preSaves]; + m[0] = i; // gametype + m[1] = j; // difficulty + m[2] = k; // preSaves Puzzles per difficulty and gametype + missing.add(m); + } + } + } + + LinkedList removeList = new LinkedList<>(); + // go through every file + for (File file : DIR.listFiles()) { + // filter so we only work with actual files + if (file.isFile()) { + String filename = file.getName(); + for(int i = 0; i < missing.size(); i++) { + StringBuilder sb = new StringBuilder(); + sb.append(LEVEL_PREFIX); + sb.append(gameTypes.get(missing.get(i)[0]).name()); + sb.append("_"); + sb.append(gameDifficulties.get(missing.get(i)[1]).name()); + sb.append("_"); + sb.append(missing.get(i)[2]); + sb.append(FILE_EXTENSION); + if(filename.equals(sb.toString())) { + removeList.add(missing.get(i)); + } + } + } + } + for(int[] i : removeList) { + missing.remove(i); + } + + int[][] missingArray = new int[missing.size()][3]; + missing.toArray(missingArray); + + // now generate all the missing puzzles. + int[] m; + while ((m = missing.poll()) != null) { + LinkedList deleteList = new LinkedList<>(); + final GameType gameType = gameTypes.get(m[0]); + final GameDifficulty gameDifficulty = gameDifficulties.get(m[1]); + + int[] missingNumbers = new int[preSaves]; + int c = 0; + missingNumbers[c++] = m[2]; + + for (int j = 0; j < missing.size(); j++) { + if (gameType == gameTypes.get(missing.get(j)[0]) + && gameDifficulty == gameDifficulties.get(missing.get(j)[1])) { + missingNumbers[c++] = missing.get(j)[2]; + deleteList.add(m); + } + } + + int amount = c; + QQWingController qqWingController = new QQWingController(); + LinkedList puzzleList = qqWingController.generateMultiple(gameType, gameDifficulty, amount); + + for (int p = 0; p < puzzleList.size(); p++) { + StringBuilder sb = new StringBuilder(); + sb.append(LEVEL_PREFIX); + sb.append(gameType.name()); + sb.append("_"); + sb.append(gameDifficulty.name()); + sb.append("_"); + sb.append(missingNumbers[p]); + sb.append(FILE_EXTENSION); + String filename = sb.toString(); + + + // create the file + File file = new File(DIR, filename); + + // convert the puzzle to a string + StringBuilder puzzleString = new StringBuilder(); + for (int digit : puzzleList.get(p)) { + if (digit == 0) { + puzzleString.append(0); + } else { + puzzleString.append(Symbol.getSymbol(Symbol.SaveFormat, digit - 1)); + } + } + + // save the file + try { + FileOutputStream stream = new FileOutputStream(file); + + try { + stream.write(puzzleString.toString().getBytes()); + } finally { + stream.close(); + } + } catch (IOException e) { + Log.e("File Manager", "Could not save game. IOException occured."); + } + } + for (int[] d : deleteList) { + missing.remove(d); + } + } + return null; + } + + @Override + protected void onProgressUpdate(Integer... values) { + } + + @Override + protected void onPostExecute(String result) { + } + } + +} + diff --git a/app/src/main/java/tu_darmstadt/sudoku/controller/DoUndo.java b/app/src/main/java/tu_darmstadt/sudoku/controller/UndoRedoManager.java similarity index 95% rename from app/src/main/java/tu_darmstadt/sudoku/controller/DoUndo.java rename to app/src/main/java/tu_darmstadt/sudoku/controller/UndoRedoManager.java index ee2df81..f0a7d4a 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/controller/DoUndo.java +++ b/app/src/main/java/tu_darmstadt/sudoku/controller/UndoRedoManager.java @@ -8,12 +8,12 @@ import tu_darmstadt.sudoku.game.GameBoard; /** * Created by Chris on 24.11.2015. */ -public class DoUndo { +public class UndoRedoManager { private int activeState; private LinkedList states = new LinkedList<>(); - public DoUndo(GameBoard initState) { + public UndoRedoManager(GameBoard initState) { // we get the base state and set it as active state. try { diff --git a/app/src/main/java/tu_darmstadt/sudoku/ui/GameActivity.java b/app/src/main/java/tu_darmstadt/sudoku/ui/GameActivity.java index 68a475e..74edbd9 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/ui/GameActivity.java +++ b/app/src/main/java/tu_darmstadt/sudoku/ui/GameActivity.java @@ -18,7 +18,7 @@ import android.widget.Toast; import java.util.List; -import tu_darmstadt.sudoku.controller.SaveLoadGameStateController; +import tu_darmstadt.sudoku.controller.GameStateManager; import tu_darmstadt.sudoku.controller.GameController; import tu_darmstadt.sudoku.controller.helper.GameInfoContainer; import tu_darmstadt.sudoku.game.GameDifficulty; @@ -75,10 +75,10 @@ public class GameActivity extends AppCompatActivity implements NavigationView.On gameController.registerGameSolvedListener(this); gameController.registerTimerListener(this); - List loadableGames = SaveLoadGameStateController.getLoadableGameList(); + List loadableGames = GameStateManager.getLoadableGameList(); if(loadLevel && loadableGames.size() > loadLevelID) { - // load level from SaveLoadGameStateController + // load level from GameStateManager gameController.loadLevel(loadableGames.get(loadLevelID)); } else { // load a new level diff --git a/app/src/main/java/tu_darmstadt/sudoku/ui/LoadGameActivity.java b/app/src/main/java/tu_darmstadt/sudoku/ui/LoadGameActivity.java index 05c3b9e..f1f4cdb 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/ui/LoadGameActivity.java +++ b/app/src/main/java/tu_darmstadt/sudoku/ui/LoadGameActivity.java @@ -29,7 +29,7 @@ import java.util.LinkedList; import java.util.List; import java.util.TimeZone; -import tu_darmstadt.sudoku.controller.SaveLoadGameStateController; +import tu_darmstadt.sudoku.controller.GameStateManager; import tu_darmstadt.sudoku.controller.helper.GameInfoContainer; import tu_darmstadt.sudoku.game.GameDifficulty; import tu_darmstadt.sudoku.ui.listener.IDeleteDialogFragmentListener; @@ -69,8 +69,8 @@ public class LoadGameActivity extends AppCompatActivity implements IDeleteDialog public void init() { - SaveLoadGameStateController saveLoadGameStateController = new SaveLoadGameStateController(this, settings); - loadableGameList = saveLoadGameStateController.loadGameStateInfo(); + GameStateManager gameStateManager = new GameStateManager(this, settings); + loadableGameList = gameStateManager.loadGameStateInfo(); AdapterView.OnItemClickListener clickListener = new AdapterView.OnItemClickListener() { @Override @@ -104,8 +104,8 @@ public class LoadGameActivity extends AppCompatActivity implements IDeleteDialog @Override public void onDialogPositiveClick(int position) { - SaveLoadGameStateController saveLoadGameStateController = new SaveLoadGameStateController(getApplicationContext(), settings); - saveLoadGameStateController.deleteGameStateFile(loadableGameList.get(position)); + GameStateManager gameStateManager = new GameStateManager(getApplicationContext(), settings); + gameStateManager.deleteGameStateFile(loadableGameList.get(position)); loadGameAdapter.delete(position); } diff --git a/app/src/main/java/tu_darmstadt/sudoku/ui/MainActivity.java b/app/src/main/java/tu_darmstadt/sudoku/ui/MainActivity.java index 2e709d5..bf10752 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/ui/MainActivity.java +++ b/app/src/main/java/tu_darmstadt/sudoku/ui/MainActivity.java @@ -24,8 +24,8 @@ import android.widget.Toast; import java.util.LinkedList; import java.util.List; -import tu_darmstadt.sudoku.controller.SaveLoadGameStateController; -import tu_darmstadt.sudoku.controller.SaveLoadLevelManager; +import tu_darmstadt.sudoku.controller.GameStateManager; +import tu_darmstadt.sudoku.controller.NewLevelManager; import tu_darmstadt.sudoku.controller.helper.GameInfoContainer; import tu_darmstadt.sudoku.game.GameDifficulty; import tu_darmstadt.sudoku.game.GameType; @@ -49,9 +49,9 @@ public class MainActivity extends AppCompatActivity { settings = PreferenceManager.getDefaultSharedPreferences(this); // check if we need to pre generate levels. - SaveLoadLevelManager.init(getApplicationContext(), settings); - SaveLoadLevelManager saveLoadLevelManager = SaveLoadLevelManager.getInstance(); - saveLoadLevelManager.checkAndRestock(); + NewLevelManager.init(getApplicationContext(), settings); + NewLevelManager newLevelManager = NewLevelManager.getInstance(); + newLevelManager.checkAndRestock(); setContentView(R.layout.activity_main_menu); @@ -133,8 +133,8 @@ public class MainActivity extends AppCompatActivity { int index = difficultyBar.getProgress()-1; GameDifficulty gameDifficulty = GameDifficulty.getValidDifficultyList().get(index < 0 ? 0 : index); - SaveLoadLevelManager saveLoadLevelManager = SaveLoadLevelManager.getInstance(); - if(saveLoadLevelManager.isLevelLoadable(gameType, gameDifficulty)) { + NewLevelManager newLevelManager = NewLevelManager.getInstance(); + if(newLevelManager.isLevelLoadable(gameType, gameDifficulty)) { // save current setting for later SharedPreferences.Editor editor = settings.edit(); editor.putString("lastChosenGameType", gameType.name()); @@ -146,7 +146,7 @@ public class MainActivity extends AppCompatActivity { i.putExtra("gameType", gameType); i.putExtra("gameDifficulty", gameDifficulty); } else { - saveLoadLevelManager.checkAndRestock(); + newLevelManager.checkAndRestock(); Toast t = Toast.makeText(getApplicationContext(), R.string.generating, Toast.LENGTH_SHORT); t.show(); return; @@ -170,7 +170,7 @@ public class MainActivity extends AppCompatActivity { private void refreshContinueButton() { // enable continue button if we have saved games. Button continueButton = (Button)findViewById(R.id.continueButton); - SaveLoadGameStateController fm = new SaveLoadGameStateController(getBaseContext(), settings); + GameStateManager fm = new GameStateManager(getBaseContext(), settings); List gic = fm.loadGameStateInfo(); if(gic.size() > 0) { continueButton.setEnabled(true);