Undo/Redo Feature added. States are not saved through sessions. They build up for every play session you start.
This commit is contained in:
parent
d2bed7b2a5
commit
31144449c7
15 changed files with 193 additions and 617 deletions
77
app/src/main/java/tu_darmstadt/sudoku/controller/DoUndo.java
Normal file
77
app/src/main/java/tu_darmstadt/sudoku/controller/DoUndo.java
Normal file
|
@ -0,0 +1,77 @@
|
|||
package tu_darmstadt.sudoku.controller;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import tu_darmstadt.sudoku.game.GameBoard;
|
||||
|
||||
/**
|
||||
* Created by Chris on 24.11.2015.
|
||||
*/
|
||||
public class DoUndo {
|
||||
|
||||
private int activeState;
|
||||
private LinkedList<GameBoard> states = new LinkedList<>();
|
||||
|
||||
public DoUndo(GameBoard initState) {
|
||||
// we get the base state and set it as active state.
|
||||
try {
|
||||
|
||||
states.addLast(initState.clone());
|
||||
activeState = 0;
|
||||
|
||||
} catch (CloneNotSupportedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isUnDoAvailable() {
|
||||
return activeState > 0 && states.size() > 1;
|
||||
}
|
||||
|
||||
public boolean isRedoAvailable() {
|
||||
return activeState < states.size()-1;
|
||||
}
|
||||
|
||||
public GameBoard UnDo() {
|
||||
if(isUnDoAvailable()) {
|
||||
return states.get(--activeState);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public GameBoard ReDo() {
|
||||
if(isRedoAvailable()) {
|
||||
return states.get(++activeState);
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void addState(GameBoard gameBoard) {
|
||||
|
||||
LinkedList<GameBoard> deleteList = new LinkedList<>();
|
||||
|
||||
for(int i = 0; i < states.size(); i++) {
|
||||
if (i > activeState) { // 3 states // state 1 is active // means 0,[1],2
|
||||
// delete rest of the list // i > activeState // i > 1 // so i = 2 will be deleted // 0 can not be deleted
|
||||
deleteList.add(states.get(i));
|
||||
}
|
||||
}
|
||||
for(GameBoard g : deleteList) {
|
||||
states.removeLastOccurrence(g);
|
||||
}
|
||||
|
||||
// then append the current state
|
||||
try {
|
||||
|
||||
GameBoard board = gameBoard.clone();
|
||||
states.addLast(board);
|
||||
activeState = states.size()-1;
|
||||
|
||||
} catch (CloneNotSupportedException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -8,7 +8,6 @@ import java.util.List;
|
|||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
|
||||
import tu_darmstadt.sudoku.controller.solver.Solver;
|
||||
import tu_darmstadt.sudoku.game.CellConflict;
|
||||
import tu_darmstadt.sudoku.game.CellConflictList;
|
||||
import tu_darmstadt.sudoku.game.GameBoard;
|
||||
|
@ -17,9 +16,9 @@ import tu_darmstadt.sudoku.controller.helper.GameInfoContainer;
|
|||
import tu_darmstadt.sudoku.game.GameDifficulty;
|
||||
import tu_darmstadt.sudoku.game.GameType;
|
||||
import tu_darmstadt.sudoku.game.ICellAction;
|
||||
import tu_darmstadt.sudoku.game.listeners.IGameSolvedListener;
|
||||
import tu_darmstadt.sudoku.game.listeners.IModelChangedListener;
|
||||
import tu_darmstadt.sudoku.game.listeners.ITimerListener;
|
||||
import tu_darmstadt.sudoku.game.listener.IGameSolvedListener;
|
||||
import tu_darmstadt.sudoku.game.listener.IModelChangedListener;
|
||||
import tu_darmstadt.sudoku.game.listener.ITimerListener;
|
||||
|
||||
/**
|
||||
* Created by Chris on 06.11.2015.
|
||||
|
@ -30,9 +29,7 @@ public class GameController implements IModelChangedListener {
|
|||
private int sectionHeight;
|
||||
private int sectionWidth;
|
||||
private GameBoard gameBoard;
|
||||
private Solver solver;
|
||||
private int[] solution;
|
||||
private LinkedList<GameBoard> solvedBoards = new LinkedList<>();
|
||||
private GameType gameType;
|
||||
private int selectedRow;
|
||||
private int selectedCol;
|
||||
|
@ -40,6 +37,7 @@ public class GameController implements IModelChangedListener {
|
|||
private int gameID = 0;
|
||||
private GameDifficulty difficulty;
|
||||
private CellConflictList errorList = new CellConflictList();
|
||||
private DoUndo doUndo;
|
||||
private int selectedValue;
|
||||
private LinkedList<IGameSolvedListener> solvedListeners = new LinkedList<>();
|
||||
private boolean notifiedOnSolvedListeners = false;
|
||||
|
@ -76,59 +74,13 @@ public class GameController implements IModelChangedListener {
|
|||
}
|
||||
|
||||
public void loadNewLevel(GameType type, GameDifficulty difficulty) {
|
||||
//Generator generator = new Generator(type, gameDifficulty);
|
||||
//GameBoard randomBoard = generator.getGameBoard();
|
||||
|
||||
SaveLoadLevelManager saveLoadLevelManager = SaveLoadLevelManager.getInstance();
|
||||
|
||||
int[] level = saveLoadLevelManager.loadLevel(type, difficulty);
|
||||
|
||||
loadLevel(new GameInfoContainer(0, difficulty, type, level, null, null));
|
||||
|
||||
saveLoadLevelManager.checkAndRestock();
|
||||
|
||||
// TODO call methods to generate level.
|
||||
//int[] generated = qqWingController.generate(type, difficulty);
|
||||
//loadLevel(new GameInfoContainer(0, difficulty, type, generated, null, null));
|
||||
|
||||
/* switch(type) {
|
||||
case Default_6x6:
|
||||
loadLevel(new GameInfoContainer(1, GameDifficulty.Easy, GameType.Default_6x6,
|
||||
new int[]{1,0,0,0,0,6,
|
||||
4,0,6,1,0,0,
|
||||
0,0,2,3,0,5,
|
||||
0,4,0,0,1,0,
|
||||
0,6,0,2,0,0,
|
||||
0,3,0,5,0,1}, null,null));
|
||||
break;
|
||||
case Default_12x12:
|
||||
loadLevel(new GameInfoContainer(2, GameDifficulty.Hard, GameType.Default_12x12,
|
||||
new int[] {0, 2, 1, 0, 0, 6, 0, 0, 0, 8, 9, 0,
|
||||
10, 0,12, 0, 0, 2, 1,11, 0, 0, 0, 6,
|
||||
6, 0, 0, 4, 0,12, 0, 0, 0, 0, 2, 1,
|
||||
0, 0, 0, 5, 0, 0, 0, 4,11,10, 0, 0,
|
||||
0,10, 0, 1, 0, 0, 6, 0, 0, 0, 0, 0,
|
||||
0, 7, 0, 0,11, 0, 0, 0, 0,12, 8, 9,
|
||||
2, 1,11, 0, 0, 0, 0, 7, 0, 0, 6, 0,
|
||||
0, 0, 0, 0, 0, 5, 0, 0, 4, 0,10, 0,
|
||||
0, 0, 7, 3, 9, 0, 0, 0, 1, 0, 0, 0,
|
||||
1, 5, 0, 0, 0, 0, 4, 0,10, 0, 0,11,
|
||||
9, 0, 0, 0, 1,10, 2, 0, 0, 6, 0, 7,
|
||||
0, 6,10, 0, 0, 0, 8, 0, 0, 1,12, 0}
|
||||
,null, null));
|
||||
break;
|
||||
case Default_9x9:
|
||||
case Unspecified:
|
||||
default:
|
||||
loadLevel(new GameInfoContainer(3, GameDifficulty.Moderate, GameType.Default_9x9,
|
||||
new int[]{5, 0, 1, 9, 0, 0, 0, 0, 0,
|
||||
2, 0, 0, 0, 0, 4, 9, 5, 0,
|
||||
3, 9, 0, 7, 0, 0, 0, 2, 6,
|
||||
0, 3, 0, 0, 0, 1, 0, 7, 2,
|
||||
0, 0, 6, 0, 5, 7, 0, 0, 0,
|
||||
0, 7, 2, 0, 0, 9, 0, 4, 1,
|
||||
0, 0, 0, 0, 7, 0, 4, 0, 9,
|
||||
6, 4, 0, 0, 0, 0, 0, 0, 0,
|
||||
7, 0, 0, 0, 1, 0, 3, 0, 5}
|
||||
, null, null));
|
||||
}*/
|
||||
}
|
||||
|
||||
public int getTime() {
|
||||
|
@ -174,6 +126,7 @@ public class GameController implements IModelChangedListener {
|
|||
|
||||
gameBoard.registerOnModelChangeListener(this);
|
||||
|
||||
doUndo = new DoUndo(gameBoard);
|
||||
// call the solve function to get the solution of this board
|
||||
//qqWingController.solve(gameBoard);
|
||||
}
|
||||
|
@ -188,25 +141,8 @@ public class GameController implements IModelChangedListener {
|
|||
solution = qqWingController.solve(gameBoard);
|
||||
}
|
||||
return solution;
|
||||
|
||||
// if(solvedBoards.size() == 0) {
|
||||
//
|
||||
// solver = new Solver(gameBoard);
|
||||
//
|
||||
// if (solver.solve(solver.getGameBoard())) {
|
||||
// solvedBoards.addAll(solver.getSolutions());
|
||||
// return solvedBoards;
|
||||
// }
|
||||
// }
|
||||
// return solvedBoards;
|
||||
}
|
||||
|
||||
/*public boolean loadLevel(GameBoard level) {
|
||||
if(GameBoard.isValid(level)) {
|
||||
gameBoard = level;
|
||||
}
|
||||
}*/
|
||||
|
||||
private void setGameType(GameType type) {
|
||||
this.gameType = type;
|
||||
this.size = type.getSize();
|
||||
|
@ -328,6 +264,7 @@ public class GameController implements IModelChangedListener {
|
|||
if(!c.isFixed()) {
|
||||
c.setValue(0);
|
||||
//notifyListeners();
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
@ -358,6 +295,9 @@ public class GameController implements IModelChangedListener {
|
|||
|
||||
public void toggleNote(int row, int col, int value) {
|
||||
GameCell c = gameBoard.getCell(row,col);
|
||||
if(c.hasValue()) {
|
||||
c.setValue(0);
|
||||
}
|
||||
c.toggleNote(value);
|
||||
//notifyListeners();
|
||||
}
|
||||
|
@ -410,15 +350,29 @@ public class GameController implements IModelChangedListener {
|
|||
}
|
||||
|
||||
public void selectValue(int value) {
|
||||
if(isValidCellSelected()) setValue(selectedRow, selectedCol, value);
|
||||
if(isValidCellSelected() && getSelectedValue() != value) {
|
||||
setValue(selectedRow, selectedCol, value);
|
||||
// add state to undo
|
||||
doUndo.addState(gameBoard);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void deleteSelectedValue() {
|
||||
if(isValidCellSelected()) deleteValue(selectedRow, selectedCol);
|
||||
if(isValidCellSelected() && getSelectedValue() != 0) {
|
||||
deleteValue(selectedRow, selectedCol);
|
||||
// add state to undo
|
||||
doUndo.addState(gameBoard);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void toggleSelectedNote(int value) {
|
||||
if(isValidCellSelected()) toggleNote(selectedRow, selectedCol, value);
|
||||
if(isValidCellSelected()) {
|
||||
toggleNote(selectedRow, selectedCol, value);
|
||||
// add state to undo
|
||||
doUndo.addState(gameBoard);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isValidCellSelected() {
|
||||
|
@ -450,7 +404,6 @@ public class GameController implements IModelChangedListener {
|
|||
//TODO disable controls and play animation in view. onSolved method is called.
|
||||
}
|
||||
} else {
|
||||
//notifiedOnSolvedListeners = false;
|
||||
|
||||
// TODO: errorList now holds all the errors
|
||||
// TODO: display errors .. notify some view?
|
||||
|
@ -520,4 +473,48 @@ public class GameController implements IModelChangedListener {
|
|||
}
|
||||
}
|
||||
|
||||
public void ReDo() {
|
||||
updateGameBoard(doUndo.ReDo());
|
||||
}
|
||||
|
||||
public void UnDo() {
|
||||
updateGameBoard(doUndo.UnDo());
|
||||
}
|
||||
|
||||
public boolean isRedoAvailable() {
|
||||
return doUndo.isRedoAvailable();
|
||||
}
|
||||
public boolean isUndoAvailable() {
|
||||
return doUndo.isUnDoAvailable();
|
||||
}
|
||||
|
||||
public void updateGameBoard(final GameBoard gameBoard) {
|
||||
if(gameBoard == null) {
|
||||
return;
|
||||
}
|
||||
for(int i = 0; i < gameBoard.getSize(); i++) {
|
||||
for(int j = 0; j < gameBoard.getSize(); j++) {
|
||||
GameCell other_c = gameBoard.getCell(i,j);
|
||||
GameCell this_c = this.gameBoard.getCell(i,j);
|
||||
if(other_c.isFixed()) {
|
||||
continue;
|
||||
}
|
||||
if(other_c.hasValue()) {
|
||||
this_c.setValue(other_c.getValue());
|
||||
} else {
|
||||
this_c.setValue(0);
|
||||
for(int k = 0; k < gameBoard.getSize(); k++) {
|
||||
if(other_c.getNotes()[k]) {
|
||||
this_c.setNote(k+1);
|
||||
} else {
|
||||
this_c.deleteNote(k+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,164 +0,0 @@
|
|||
package tu_darmstadt.sudoku.controller.generator;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.support.v7.util.SortedList;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
|
||||
import tu_darmstadt.sudoku.controller.solver.Solver;
|
||||
import tu_darmstadt.sudoku.game.GameBoard;
|
||||
import tu_darmstadt.sudoku.game.GameCell;
|
||||
import tu_darmstadt.sudoku.game.GameDifficulty;
|
||||
import tu_darmstadt.sudoku.game.GameType;
|
||||
import tu_darmstadt.sudoku.game.ICellAction;
|
||||
|
||||
/**
|
||||
* Created by Chris on 19.11.2015.
|
||||
*/
|
||||
public class Generator {
|
||||
|
||||
private Random random;
|
||||
private int size;
|
||||
private int sectionHeight;
|
||||
private int sectionWidth;
|
||||
private GameBoard gameBoard;
|
||||
|
||||
public Generator(GameType type, GameDifficulty difficulty) {
|
||||
this.size = type.getSize();
|
||||
this.sectionHeight = type.getSectionHeight();
|
||||
this.sectionWidth = type.getSectionWidth();
|
||||
this.gameBoard = new GameBoard(type);
|
||||
gameBoard.initCells(new int[size*size]);
|
||||
setNotes(gameBoard);
|
||||
|
||||
// generate a random valid board.
|
||||
gameBoard = generate();
|
||||
|
||||
// produce a level out of it.
|
||||
gameBoard = createLevel(difficulty);
|
||||
}
|
||||
|
||||
public GameBoard createLevel(GameDifficulty difficulty) {
|
||||
return gameBoard;
|
||||
}
|
||||
|
||||
public GameBoard getGameBoard() {
|
||||
return gameBoard;
|
||||
}
|
||||
|
||||
|
||||
public GameBoard generate() {
|
||||
random = new Random();
|
||||
|
||||
GameBoard workingBoard = gameBoard;
|
||||
GameBoard board = gameBoard;
|
||||
|
||||
/*GameCell chosen = gameBoard.getCell(random.nextInt(9), random.nextInt(9));
|
||||
while(chosen.getNoteCount() <= 2) {
|
||||
chosen = gameBoard.getCell(random.nextInt(9), random.nextInt(9));
|
||||
}*/
|
||||
|
||||
while(workingBoard != null && !workingBoard.isFilled()) {
|
||||
|
||||
// clone board
|
||||
try {
|
||||
workingBoard = board.clone();
|
||||
} catch (CloneNotSupportedException e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
|
||||
// get all candidates
|
||||
List<GameCell> candidates = getListOfCandidates(workingBoard);
|
||||
// choose one of them
|
||||
GameCell chosen = candidates.get(random.nextInt(candidates.size()));
|
||||
// get all possible values
|
||||
List<Integer> possibleValues = getPossibleValues(chosen);
|
||||
// set a random value of that pool
|
||||
chosen.setValue(possibleValues.get(random.nextInt(possibleValues.size())));
|
||||
|
||||
deleteConnectedValues(workingBoard, chosen);
|
||||
|
||||
Solver solver = new Solver(workingBoard);
|
||||
if (solver.solve(solver.getGameBoard())) {
|
||||
List<GameBoard> solutions = solver.getSolutions();
|
||||
switch(solutions.size()) {
|
||||
case 0: // if we get no solution .. revert change
|
||||
continue;
|
||||
case 1: // if we get 1 solution we are done and return the solution
|
||||
return solutions.get(0);
|
||||
default: // if we get more than 1 solution .. keep setting numbers
|
||||
board = workingBoard;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
private boolean deleteConnectedValues(final GameBoard gameBoard, final GameCell cell) {
|
||||
return gameBoard.actionOnCells(new ICellAction<Boolean>() {
|
||||
@Override
|
||||
public Boolean action(GameCell gc, Boolean existing) {
|
||||
if (!gc.hasValue() && !gc.equals(cell)) {
|
||||
if (gc.getRow() == cell.getRow()
|
||||
|| gc.getCol() == cell.getCol()
|
||||
|| (int) (Math.floor(gc.getRow() / sectionHeight) * sectionHeight + Math.floor(gc.getCol() / sectionWidth)) ==
|
||||
(int) (Math.floor(cell.getRow() / sectionHeight) * sectionHeight + Math.floor(cell.getCol() / sectionWidth))) {
|
||||
gc.deleteNote(cell.getValue());
|
||||
if(gc.getNoteCount() == 0) {
|
||||
existing = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return existing;
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
private List<Integer> getPossibleValues(GameCell c) {
|
||||
List<Integer> result = new LinkedList<>();
|
||||
for(int i = 0; i < size; i++) {
|
||||
if(c.getNotes()[i]) {
|
||||
result.add(i+1);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private List<GameCell> getListOfCandidates(GameBoard gameBoard) {
|
||||
final ArrayList<GameCell> candidates = new ArrayList<>();
|
||||
|
||||
gameBoard.actionOnCells(new ICellAction<ArrayList<GameCell>>() {
|
||||
@Override
|
||||
public ArrayList<GameCell> action(GameCell gc, ArrayList<GameCell> existing) {
|
||||
if(gc.getNoteCount() > 1) {
|
||||
existing.add(gc);
|
||||
}
|
||||
return existing;
|
||||
}
|
||||
}, candidates);
|
||||
|
||||
return candidates;
|
||||
}
|
||||
|
||||
private void setNotes(GameBoard gameBoard) {
|
||||
for(int i = 0; i < gameBoard.getSize(); i++) {
|
||||
for(int j = 0; j < gameBoard.getSize(); j++) {
|
||||
for(int k = 1; k <= gameBoard.getSize(); k++) {
|
||||
gameBoard.getCell(i,j).setNote(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,352 +0,0 @@
|
|||
package tu_darmstadt.sudoku.controller.solver;
|
||||
|
||||
import android.graphics.Point;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import tu_darmstadt.sudoku.controller.helper.GameInfoContainer;
|
||||
import tu_darmstadt.sudoku.game.CellConflict;
|
||||
import tu_darmstadt.sudoku.game.GameBoard;
|
||||
import tu_darmstadt.sudoku.game.GameCell;
|
||||
import tu_darmstadt.sudoku.game.ICellAction;
|
||||
|
||||
/**
|
||||
* Created by Chris on 10.11.2015.
|
||||
*/
|
||||
public class Solver {
|
||||
|
||||
private GameBoard gameBoard = null;
|
||||
private LinkedList<GameBoard> solutions = new LinkedList<>();
|
||||
|
||||
public Solver(GameBoard gf) {
|
||||
try {
|
||||
if(gf == null) {
|
||||
throw new IllegalArgumentException("GameBoard may not be null.");
|
||||
}
|
||||
|
||||
gameBoard = gf.clone();
|
||||
} catch(CloneNotSupportedException e) {
|
||||
throw new IllegalArgumentException("This GameBoard is not cloneable.", e);
|
||||
}
|
||||
|
||||
gameBoard.reset();
|
||||
if(!isSolvable(gameBoard)) {
|
||||
throw new IllegalArgumentException("This GameBoard is not solveable.");
|
||||
}
|
||||
|
||||
setNotes(gameBoard);
|
||||
}
|
||||
|
||||
public void setNotes(GameBoard gameBoard) {
|
||||
for(int i = 0; i < gameBoard.getSize(); i++) {
|
||||
for(int j = 0; j < gameBoard.getSize(); j++) {
|
||||
for(int k = 1; k <= gameBoard.getSize(); k++) {
|
||||
gameBoard.getCell(i,j).setNote(k);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSolvable(GameBoard gameBoard) {
|
||||
for(int i = 0; i < gameBoard.getSize(); i++) {
|
||||
if(hasErrors(gameBoard.getRow(i))) return false;
|
||||
if(hasErrors(gameBoard.getColumn(i))) return false;
|
||||
if(hasErrors(gameBoard.getSection(i))) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean hasErrors(LinkedList<GameCell> list) {
|
||||
LinkedList<Integer> checked = new LinkedList<Integer>();
|
||||
for(GameCell c : list) {
|
||||
if(checked.contains(c.getValue())) {
|
||||
return true;
|
||||
}
|
||||
if(c.hasValue()) {
|
||||
checked.add(c.getValue());
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public LinkedList<GameBoard> getSolutions() {
|
||||
return solutions;
|
||||
}
|
||||
|
||||
public boolean isDone(GameBoard gameBoard) {
|
||||
for(int i = 0; i < gameBoard.getSize(); i++) {
|
||||
for(int j = 0; j < gameBoard.getSize(); j++) {
|
||||
if(!gameBoard.getCell(i,j).hasValue()) return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public boolean solve(final GameBoard gameBoard) {
|
||||
|
||||
checkSolvedCells(gameBoard);
|
||||
|
||||
//String string = gameBoard.toString();
|
||||
if(isDone(gameBoard)) {
|
||||
solutions.add(gameBoard);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(showPossibles(gameBoard))
|
||||
return solve(gameBoard);
|
||||
|
||||
if(isImpossible(gameBoard))
|
||||
return false;
|
||||
|
||||
if(searchHiddenSingles(gameBoard))
|
||||
return solve(gameBoard);
|
||||
|
||||
if(searchNakedPairsTriples(gameBoard))
|
||||
return solve(gameBoard);
|
||||
|
||||
if(searchHiddenPairsTriples(gameBoard))
|
||||
return solve(gameBoard);
|
||||
|
||||
if(searchNakedQuads(gameBoard))
|
||||
return solve(gameBoard);
|
||||
|
||||
if(searchPointingPairs(gameBoard))
|
||||
return solve(gameBoard);
|
||||
|
||||
if(searchBoxLineReduction(gameBoard))
|
||||
return solve(gameBoard);
|
||||
|
||||
|
||||
// if every defined strategy fails.. we have to guess
|
||||
// get the best candidate
|
||||
Point p = getBestCandidate(gameBoard);
|
||||
|
||||
// then we test every possible value for that candidate, but we do it on a cloned gameBoard
|
||||
boolean result = false;
|
||||
for(int i = 0; i < gameBoard.getSize(); i++) {
|
||||
GameCell gc = gameBoard.getCell(p.x,p.y);
|
||||
try {
|
||||
if(gc.getNotes()[i]) {
|
||||
GameBoard gameBoardCopy = gameBoard.clone();
|
||||
|
||||
GameCell copyGC = gameBoardCopy.getCell(p.x, p.y);
|
||||
|
||||
copyGC.setValue(i+1);
|
||||
|
||||
result = solve(gameBoardCopy);
|
||||
|
||||
if(solutions.size() > 1) {
|
||||
// don't search for more than 1 solution
|
||||
return result;
|
||||
}
|
||||
|
||||
//if (result) {
|
||||
// stop after we found 1 solution
|
||||
//return true;
|
||||
|
||||
// or keep going to find multiple solutions
|
||||
//}
|
||||
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
} catch(CloneNotSupportedException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public boolean isImpossible(GameBoard gameBoard) {
|
||||
return gameBoard.actionOnCells(new ICellAction<Boolean>() {
|
||||
@Override
|
||||
public Boolean action(GameCell gc, Boolean existing) {
|
||||
return (gc.getNoteCount() == 0 && !gc.hasValue()) ? true : existing;
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
public boolean calculateNextPossibleStep() {
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public GameBoard getGameBoard() {
|
||||
return gameBoard;
|
||||
}
|
||||
|
||||
private boolean checkSolvedCells(final GameBoard gameBoard) {
|
||||
return gameBoard.actionOnCells(new ICellAction<Boolean>() {
|
||||
@Override
|
||||
public Boolean action(GameCell gc, Boolean existing) {
|
||||
int value = -1;
|
||||
if(!gc.hasValue() && gc.getNoteCount() == 1) {
|
||||
for(int i = 0; i < gameBoard.getSize(); i++) {
|
||||
if(gc.getNotes()[i]) {
|
||||
value = i+1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
gc.setValue(value);
|
||||
existing = true;
|
||||
}
|
||||
return existing;
|
||||
}}, false);
|
||||
}
|
||||
|
||||
|
||||
public boolean showPossibles(final GameBoard gameBoard) {
|
||||
boolean deletedSomething = false;
|
||||
LinkedList<GameCell> list = new LinkedList<GameCell>();
|
||||
for(int i = 0; i < gameBoard.getSize(); i++) {
|
||||
for(int j = 0; j < gameBoard.getSize(); j++) {
|
||||
GameCell gc = gameBoard.getCell(i,j);
|
||||
if(!gc.hasValue()) {
|
||||
list.clear();
|
||||
list.addAll(gameBoard.getRow(i));
|
||||
list.addAll(gameBoard.getColumn(j));
|
||||
list.addAll(gameBoard.getSection(i,j));
|
||||
for(GameCell c : list) {
|
||||
for(int k = 0; k < gameBoard.getSize(); k++) {
|
||||
if(gc.getNotes()[k] && c.hasValue() && !c.equals(gc) && k+1 == c.getValue()) {
|
||||
gc.deleteNote(c.getValue());
|
||||
deletedSomething = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return deletedSomething;
|
||||
}
|
||||
|
||||
private boolean searchHiddenSingles(final GameBoard gameBoard) {
|
||||
boolean foundHiddenSingles = false;
|
||||
|
||||
LinkedList<GameCell> list = new LinkedList<>();
|
||||
|
||||
for(int i = 0; i < gameBoard.getSize()*3; i++) {
|
||||
|
||||
int index = i % gameBoard.getSize();
|
||||
int listSelector = (int)Math.floor(i / gameBoard.getSize());
|
||||
|
||||
if(listSelector == 0) list = gameBoard.getRow(index);
|
||||
if(listSelector == 1) list = gameBoard.getColumn(index);
|
||||
if(listSelector == 2) list = gameBoard.getSection(index);
|
||||
|
||||
LinkedList<Integer[]> possibles = new LinkedList<>();
|
||||
LinkedList<Integer> notPossibles = new LinkedList<Integer>();
|
||||
|
||||
for(GameCell c : list) { // check all gameCells in Row
|
||||
|
||||
if(c.hasValue()) {
|
||||
notPossibles.add(c.getValue());
|
||||
continue;
|
||||
}
|
||||
|
||||
for(int k = 0; k < gameBoard.getSize(); k++) { // check all nodes
|
||||
if(c.getNotes()[k]) { // if k note active
|
||||
for(int p = 0; p < possibles.size(); p++) { // check possible list
|
||||
if(possibles.get(p)[2] == k+1) { // is value in possible list?
|
||||
// value already exists in possible list
|
||||
// add to impossibles
|
||||
if(!notPossibles.contains(k+1)) {
|
||||
notPossibles.add(k + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
if(!notPossibles.contains(k+1)){ // if k node is possible
|
||||
possibles.add(new Integer[] {c.getRow(), c.getCol(), k+1});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// we checked a section/row/column
|
||||
// now set the remaining possibles that are not impossible
|
||||
boolean possible = true;
|
||||
for(int p = 0; p < possibles.size(); p++) {
|
||||
possible = true;
|
||||
for(int np = 0; np < notPossibles.size(); np++) {
|
||||
if(possibles.get(p)[2] == notPossibles.get(np)) {
|
||||
// found that current possible is impossible
|
||||
possible = false;
|
||||
}
|
||||
}
|
||||
// if this is still possible then SET IT :D YAY
|
||||
if(possible) {
|
||||
gameBoard.getCell(possibles.get(p)[0],possibles.get(p)[1]).setValue(possibles.get(p)[2]);
|
||||
foundHiddenSingles = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return foundHiddenSingles;
|
||||
}
|
||||
|
||||
|
||||
public boolean searchNakedPairsTriples(final GameBoard gameBoard) {
|
||||
return false;
|
||||
}
|
||||
public boolean searchHiddenPairsTriples(final GameBoard gameBoard) {
|
||||
return false;
|
||||
}
|
||||
public boolean searchNakedQuads(final GameBoard gameBoard) {
|
||||
return false;
|
||||
}
|
||||
public boolean searchPointingPairs(final GameBoard gameBoard) {
|
||||
return false;
|
||||
}
|
||||
public boolean searchBoxLineReduction(final GameBoard gameBoard) {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Point getBestCandidate(GameBoard gameBoard) {
|
||||
Point bestCandidate = new Point();
|
||||
int minimumCount = gameBoard.getSize();
|
||||
int count = 0;
|
||||
Point candidate = new Point();
|
||||
for(int i = 0; i < gameBoard.getSize(); i++) {
|
||||
|
||||
count = countUnsolved(gameBoard.getRow(i),candidate);
|
||||
if(count != 0 && count < minimumCount) {
|
||||
minimumCount = count;
|
||||
bestCandidate.set(candidate.x, candidate.y);
|
||||
}
|
||||
|
||||
count = countUnsolved(gameBoard.getColumn(i),candidate);
|
||||
if(count != 0 && count < minimumCount) {
|
||||
minimumCount = count;
|
||||
bestCandidate.set(candidate.x, candidate.y);
|
||||
}
|
||||
|
||||
count = countUnsolved(gameBoard.getSection(i),candidate);
|
||||
if(count != 0 && count < minimumCount) {
|
||||
minimumCount = count;
|
||||
bestCandidate.set(candidate.x, candidate.y);
|
||||
}
|
||||
|
||||
if(minimumCount == 2) {
|
||||
return bestCandidate;
|
||||
}
|
||||
}
|
||||
return bestCandidate;
|
||||
}
|
||||
|
||||
public int countUnsolved(final List<GameCell> list, Point p) {
|
||||
int count = 0;
|
||||
for(GameCell gc : list) {
|
||||
if(!gc.hasValue()) {
|
||||
count++;
|
||||
p.set(gc.getRow(), gc.getCol());
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
|
@ -3,7 +3,7 @@ package tu_darmstadt.sudoku.game;
|
|||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import tu_darmstadt.sudoku.game.listeners.IModelChangedListener;
|
||||
import tu_darmstadt.sudoku.game.listener.IModelChangedListener;
|
||||
|
||||
/**
|
||||
* Created by Christopher Beckmann on 06.11.2015.
|
||||
|
|
|
@ -4,7 +4,7 @@ import java.util.Arrays;
|
|||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import tu_darmstadt.sudoku.game.listeners.IModelChangedListener;
|
||||
import tu_darmstadt.sudoku.game.listener.IModelChangedListener;
|
||||
|
||||
/**
|
||||
* Created by Chris on 06.11.2015.
|
||||
|
@ -174,8 +174,9 @@ public class GameCell implements Cloneable {
|
|||
@Override
|
||||
public GameCell clone() throws CloneNotSupportedException {
|
||||
GameCell clone = (GameCell) super.clone();
|
||||
clone.modelChangedListeners = new LinkedList<IModelChangedListener>();
|
||||
// keep listeners .. so we can just replace the board and still have the listeners
|
||||
clone.notes = (notes == null) ? null : Arrays.copyOf(notes, notes.length);
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
package tu_darmstadt.sudoku.game.listener;
|
||||
|
||||
/**
|
||||
* Created by Chris on 19.11.2015.
|
||||
*/
|
||||
public interface IGameSolvedListener {
|
||||
public void onSolved();
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package tu_darmstadt.sudoku.game.listeners;
|
||||
package tu_darmstadt.sudoku.game.listener;
|
||||
|
||||
import tu_darmstadt.sudoku.game.GameCell;
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
package tu_darmstadt.sudoku.game.listeners;
|
||||
package tu_darmstadt.sudoku.game.listener;
|
||||
|
||||
/**
|
||||
* Created by TMZ_LToP on 20.11.2015.
|
|
@ -1,11 +0,0 @@
|
|||
package tu_darmstadt.sudoku.game.listeners;
|
||||
|
||||
import tu_darmstadt.sudoku.controller.GameController;
|
||||
import tu_darmstadt.sudoku.game.GameBoard;
|
||||
|
||||
/**
|
||||
* Created by Chris on 19.11.2015.
|
||||
*/
|
||||
public interface IGameSolvedListener {
|
||||
public void onSolved();
|
||||
}
|
|
@ -23,8 +23,8 @@ import tu_darmstadt.sudoku.controller.GameController;
|
|||
import tu_darmstadt.sudoku.controller.helper.GameInfoContainer;
|
||||
import tu_darmstadt.sudoku.game.GameDifficulty;
|
||||
import tu_darmstadt.sudoku.game.GameType;
|
||||
import tu_darmstadt.sudoku.game.listeners.IGameSolvedListener;
|
||||
import tu_darmstadt.sudoku.game.listeners.ITimerListener;
|
||||
import tu_darmstadt.sudoku.game.listener.IGameSolvedListener;
|
||||
import tu_darmstadt.sudoku.game.listener.ITimerListener;
|
||||
import tu_darmstadt.sudoku.ui.view.R;
|
||||
import tu_darmstadt.sudoku.ui.view.SudokuFieldLayout;
|
||||
import tu_darmstadt.sudoku.ui.view.SudokuKeyboardLayout;
|
||||
|
|
|
@ -13,10 +13,10 @@ import android.preference.PreferenceManager;
|
|||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.ArrayAdapter;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ListView;
|
||||
|
@ -32,6 +32,7 @@ import java.util.TimeZone;
|
|||
import tu_darmstadt.sudoku.controller.SaveLoadGameStateController;
|
||||
import tu_darmstadt.sudoku.controller.helper.GameInfoContainer;
|
||||
import tu_darmstadt.sudoku.game.GameDifficulty;
|
||||
import tu_darmstadt.sudoku.ui.listener.IDeleteDialogFragmentListener;
|
||||
import tu_darmstadt.sudoku.ui.view.R;
|
||||
|
||||
public class LoadGameActivity extends AppCompatActivity implements IDeleteDialogFragmentListener {
|
||||
|
@ -46,11 +47,26 @@ public class LoadGameActivity extends AppCompatActivity implements IDeleteDialog
|
|||
|
||||
setContentView(R.layout.activity_load_game);
|
||||
|
||||
android.support.v7.app.ActionBar actionBar = getSupportActionBar();
|
||||
actionBar.setTitle(R.string.menu_continue_game);
|
||||
actionBar.setDisplayHomeAsUpEnabled(true);
|
||||
|
||||
settings = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
// Respond to the action bar's Up/Home button
|
||||
case android.R.id.home:
|
||||
finish();
|
||||
return true;
|
||||
}
|
||||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
public void init() {
|
||||
|
||||
SaveLoadGameStateController saveLoadGameStateController = new SaveLoadGameStateController(this, settings);
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
package tu_darmstadt.sudoku.ui;
|
||||
package tu_darmstadt.sudoku.ui.listener;
|
||||
|
||||
import android.app.DialogFragment;
|
||||
|
|
@ -42,15 +42,17 @@ public class SudokuSpecialButtonLayout extends LinearLayout {
|
|||
keyboard.toggleNotesEnabled();
|
||||
break;
|
||||
case Do:
|
||||
// TODO: not implemented
|
||||
gameController.ReDo();
|
||||
gameController.saveGame(getContext());
|
||||
break;
|
||||
case Undo:
|
||||
// TODO: not implemented
|
||||
gameController.UnDo();
|
||||
gameController.saveGame(getContext());
|
||||
break;
|
||||
case Hint:
|
||||
if(gameController.isValidCellSelected()) {
|
||||
int[] solved = gameController.solve();
|
||||
// TODO test every placed value so far
|
||||
// TODO test every placed value so far? Or just reveal current selected?
|
||||
|
||||
// and reveal the selected value.
|
||||
gameController.selectValue(solved[row * gameController.getSize() + col]);
|
||||
|
@ -79,7 +81,7 @@ public class SudokuSpecialButtonLayout extends LinearLayout {
|
|||
fixedButtons = new SudokuButton[fixedButtonsCount];
|
||||
LayoutParams p;
|
||||
int i = 0;
|
||||
ArrayList<SudokuButtonType> type = (ArrayList<SudokuButtonType>) SudokuButtonType.getSpecialButtons();
|
||||
//ArrayList<SudokuButtonType> type = (ArrayList<SudokuButtonType>) SudokuButtonType.getSpecialButtons();
|
||||
for (SudokuButtonType t : SudokuButtonType.getSpecialButtons()){
|
||||
fixedButtons[i] = new SudokuButton(getContext(),null);
|
||||
p = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT,1);
|
||||
|
|
|
@ -67,4 +67,6 @@
|
|||
<string name="loadgame_delete_confirm">Delete</string>
|
||||
<string name="loadgame_delete_cancel">Cancel</string>
|
||||
|
||||
|
||||
|
||||
</resources>
|
||||
|
|
Loading…
Reference in a new issue