From cc92454cc692f5abf6bf811403c8d801835991f0 Mon Sep 17 00:00:00 2001 From: Christopher Beckmann Date: Wed, 18 Nov 2015 03:54:33 +0100 Subject: [PATCH] Implemented the backtracking strategy for the Sudoku Solver. JUnit tests are running. Still need to add more logic strategies. --- app/src/main/AndroidManifest.xml | 19 +- .../sudoku/controller/GameController.java | 43 +++- .../sudoku/controller/Highscore.java | 8 + .../sudoku/controller/SaveLoadController.java | 2 +- .../sudoku/controller/Symbol.java | 16 +- .../helper}/GameInfoContainer.java | 13 +- .../tu_darmstadt/sudoku/game/GameBoard.java | 4 + .../sudoku/game/solver/ISolver.java | 2 +- .../sudoku/game/solver/Solver.java | 201 ++++++++++++++---- .../tu_darmstadt/sudoku/ui/AboutActivity.java | 3 + .../tu_darmstadt/sudoku/ui/GameActivity.java | 4 +- .../sudoku/ui/LoadGameActivity.java | 2 +- .../tu_darmstadt/sudoku/ui/MainActivity.java | 2 +- .../sudoku/ui/view/SudokuCellView.java | 4 +- .../sudoku/ui/view/SudokuKeyboardLayout.java | 3 +- app/src/main/res/layout/activity_about.xml | 1 + .../sudoku/controller/GameControllerTest.java | 2 +- .../sudoku/game/solver/SolverTest.java | 82 +++++-- 18 files changed, 307 insertions(+), 104 deletions(-) create mode 100644 app/src/main/java/tu_darmstadt/sudoku/controller/Highscore.java rename app/src/main/java/tu_darmstadt/sudoku/{game => controller/helper}/GameInfoContainer.java (90%) diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a9ba154..1fe669b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,6 +8,15 @@ android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme" > + + + + + + - - - - - - - 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 651edcb..ef73a91 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/controller/GameController.java +++ b/app/src/main/java/tu_darmstadt/sudoku/controller/GameController.java @@ -10,7 +10,7 @@ import tu_darmstadt.sudoku.game.CellConflict; import tu_darmstadt.sudoku.game.CellConflictList; import tu_darmstadt.sudoku.game.GameBoard; import tu_darmstadt.sudoku.game.GameCell; -import tu_darmstadt.sudoku.game.GameInfoContainer; +import tu_darmstadt.sudoku.controller.helper.GameInfoContainer; import tu_darmstadt.sudoku.game.GameType; import tu_darmstadt.sudoku.game.ICellAction; import tu_darmstadt.sudoku.game.solver.Solver; @@ -25,13 +25,14 @@ public class GameController { private int sectionHeight; private int sectionWidth; private GameBoard gameBoard; - private ISolver solver; + private Solver solver; private GameType gameType; private int selectedRow; private int selectedCol; private SharedPreferences settings; private int gameID = 0; private CellConflictList errorList = new CellConflictList(); + private int selectedValue; //private LinkedList listeners = new LinkedList<>(); // private Solver solver; @@ -139,7 +140,7 @@ public class GameController { settings = pref; } - public GameBoard solve(GameBoard gameBoard) { + public LinkedList solve() { switch(gameType) { case Default_9x9: case Default_6x6: @@ -150,8 +151,8 @@ public class GameController { throw new UnsupportedOperationException("No Solver for this GameType defined."); } - if(solver.solve()) { - return solver.getGameBoard(); + if(solver.solve(solver.getGameBoard())) { + return solver.getSolutions(); } return null; } @@ -228,6 +229,15 @@ public class GameController { return gameBoard.actionOnCells(ca,existing); } + public boolean checkIfBoardIsFilled() { + //if(gameBoard.getEmptyCellCount() == 0) { + // TODO: board is filled. check it for errors. + + //return true; + //} + return false; + } + public void saveGame(Context context) { if(settings == null) { return; @@ -312,15 +322,34 @@ public class GameController { return gameBoard.toString(); } + + + + /* + * Controls for the View. + * Select methods for Cells and Values. + * If a value is selected while a cell is selected the value is put into the cell. + * If no cell is selected while a value is being selected, the value gets selected, + * and every cell selection after that will automaticly put that value into the cell. + * + */ public int getSelectedRow() { return selectedRow; } - public int getSelectedCol() { return selectedCol; } + public int getSelectedValue() { + return selectedValue; + } public void selectCell(int row, int col) { + // TODO if there is a value selected + // TODO should we do this in here or rather in the view? + // we set the value directly + //if(selectedValue != 0) { + //} + if(selectedRow == row && selectedCol == col) { // if we select the same field 2ce -> deselect it selectedRow = -1; @@ -332,7 +361,7 @@ public class GameController { } } - public void setSelectedValue(int value) { + public void selectValue(int value) { if(isCellSelected()) setValue(selectedRow, selectedCol, value); } diff --git a/app/src/main/java/tu_darmstadt/sudoku/controller/Highscore.java b/app/src/main/java/tu_darmstadt/sudoku/controller/Highscore.java new file mode 100644 index 0000000..e948e69 --- /dev/null +++ b/app/src/main/java/tu_darmstadt/sudoku/controller/Highscore.java @@ -0,0 +1,8 @@ +package tu_darmstadt.sudoku.controller; + +/** + * Created by Chris on 17.11.2015. + */ +public class Highscore { + +} diff --git a/app/src/main/java/tu_darmstadt/sudoku/controller/SaveLoadController.java b/app/src/main/java/tu_darmstadt/sudoku/controller/SaveLoadController.java index ce1fc60..d558963 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/controller/SaveLoadController.java +++ b/app/src/main/java/tu_darmstadt/sudoku/controller/SaveLoadController.java @@ -11,7 +11,7 @@ import java.io.IOException; import java.util.LinkedList; import java.util.List; -import tu_darmstadt.sudoku.game.GameInfoContainer; +import tu_darmstadt.sudoku.controller.helper.GameInfoContainer; /** * Created by Chris on 16.11.2015. diff --git a/app/src/main/java/tu_darmstadt/sudoku/controller/Symbol.java b/app/src/main/java/tu_darmstadt/sudoku/controller/Symbol.java index 4478440..95b685e 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/controller/Symbol.java +++ b/app/src/main/java/tu_darmstadt/sudoku/controller/Symbol.java @@ -5,22 +5,24 @@ package tu_darmstadt.sudoku.controller; */ public enum Symbol { - Default(new char[] {'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N'}), - Fancy(new char[] {'♪', '♫', '☼', '♥', '♦', '♣', '♠', '•', '○', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N' }); + SaveFormat(new String[] {"1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "O", "P"}), + Default(new String[] {"1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N"}), + Roman(new String[] {"I", "II", "III", "IV", "V", "VI", "VII", "VIII", "IX", "X", "XI", "XII", "XIII", "XIV", "XV", "XVI", "XVII", "XVIII", "XIX", "XX", "XXI", "XXII"}), + Fancy(new String[] {"♪", "♫", "☼", "♥", "♦", "♣", "♠", "•", "○", "A", "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N" }); - private char[] map; + private String[] map; - Symbol(char[] map) { + Symbol(String[] map) { this.map = map; } public static String getSymbol(Symbol type, int value) { - return String.valueOf(type.map[value]); + return (type.map[value]); } - public static int getValue(Symbol type, char c) { + public static int getValue(Symbol type, String c) { for(int i = 0; i < type.map.length; i++) { - if(type.map[i] == c) return i; + if(type.map[i].equals(c)) return i; } return -1; } diff --git a/app/src/main/java/tu_darmstadt/sudoku/game/GameInfoContainer.java b/app/src/main/java/tu_darmstadt/sudoku/controller/helper/GameInfoContainer.java similarity index 90% rename from app/src/main/java/tu_darmstadt/sudoku/game/GameInfoContainer.java rename to app/src/main/java/tu_darmstadt/sudoku/controller/helper/GameInfoContainer.java index eb51b37..c6e6af0 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/game/GameInfoContainer.java +++ b/app/src/main/java/tu_darmstadt/sudoku/controller/helper/GameInfoContainer.java @@ -1,9 +1,12 @@ -package tu_darmstadt.sudoku.game; +package tu_darmstadt.sudoku.controller.helper; import android.util.Log; import tu_darmstadt.sudoku.controller.GameController; import tu_darmstadt.sudoku.controller.Symbol; +import tu_darmstadt.sudoku.game.GameCell; +import tu_darmstadt.sudoku.game.GameType; +import tu_darmstadt.sudoku.game.ICellAction; /** * Created by Chris on 17.11.2015. @@ -46,7 +49,7 @@ public class GameInfoContainer { } fixedValues = new int[s.length()]; for(int i = 0; i < s.length(); i++) { - fixedValues[i] = Symbol.getValue(Symbol.Default, s.charAt(i))+1; + fixedValues[i] = Symbol.getValue(Symbol.SaveFormat, String.valueOf(s.charAt(i)))+1; } } @@ -61,7 +64,7 @@ public class GameInfoContainer { } setValues = new int[s.length()]; for(int i = 0; i < s.length(); i++) { - setValues[i] = Symbol.getValue(Symbol.Default, s.charAt(i))+1; + setValues[i] = Symbol.getValue(Symbol.SaveFormat, String.valueOf(s.charAt(i)))+1; } } @@ -133,7 +136,7 @@ public class GameInfoContainer { @Override public StringBuilder action(GameCell gc, StringBuilder existing) { if (gc.isFixed()) { - existing.append(Symbol.getSymbol(Symbol.Default, gc.getValue() - 1)); + existing.append(Symbol.getSymbol(Symbol.SaveFormat, gc.getValue() - 1)); } else { existing.append(0); } @@ -151,7 +154,7 @@ public class GameInfoContainer { if (gc.isFixed() || gc.getValue() == 0) { existing.append(0); } else { - existing.append(Symbol.getSymbol(Symbol.Default, gc.getValue() - 1)); + existing.append(Symbol.getSymbol(Symbol.SaveFormat, gc.getValue() - 1)); } return existing; } diff --git a/app/src/main/java/tu_darmstadt/sudoku/game/GameBoard.java b/app/src/main/java/tu_darmstadt/sudoku/game/GameBoard.java index 7f7aea8..aed0726 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/game/GameBoard.java +++ b/app/src/main/java/tu_darmstadt/sudoku/game/GameBoard.java @@ -69,6 +69,10 @@ public class GameBoard implements Cloneable { } public LinkedList getRow(final int row) { + LinkedList result = new LinkedList(); + for(int i = 0; i < size; i++) { + + } return actionOnCells(new ICellAction>() { @Override public LinkedList action(GameCell gc, LinkedList existing) { diff --git a/app/src/main/java/tu_darmstadt/sudoku/game/solver/ISolver.java b/app/src/main/java/tu_darmstadt/sudoku/game/solver/ISolver.java index 116cebc..add7e75 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/game/solver/ISolver.java +++ b/app/src/main/java/tu_darmstadt/sudoku/game/solver/ISolver.java @@ -7,7 +7,7 @@ import tu_darmstadt.sudoku.game.GameBoard; */ public interface ISolver { - public boolean solve(); + public boolean solve(GameBoard gameBoard); public boolean calculateNextPossibleStep(); diff --git a/app/src/main/java/tu_darmstadt/sudoku/game/solver/Solver.java b/app/src/main/java/tu_darmstadt/sudoku/game/solver/Solver.java index 4cdf2d0..75aa43c 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/game/solver/Solver.java +++ b/app/src/main/java/tu_darmstadt/sudoku/game/solver/Solver.java @@ -1,7 +1,12 @@ package tu_darmstadt.sudoku.game.solver; -import java.util.LinkedList; +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; @@ -13,6 +18,7 @@ import tu_darmstadt.sudoku.game.ICellAction; public class Solver implements ISolver { private GameBoard gameBoard = null; + private LinkedList solutions = new LinkedList<>(); public Solver(GameBoard gf) { try { @@ -36,7 +42,7 @@ public class Solver implements ISolver { public void setNotes(GameBoard gameBoard) { for(int i = 0; i < gameBoard.getSize(); i++) { for(int j = 0; j < gameBoard.getSize(); j++) { - for(int k = 0; k < gameBoard.getSize(); k++) { + for(int k = 1; k <= gameBoard.getSize(); k++) { gameBoard.getCell(i,j).setNote(k); } } @@ -58,34 +64,92 @@ public class Solver implements ISolver { if(checked.contains(c.getValue())) { return true; } - checked.add(c.getValue()); + if(c.hasValue()) { + checked.add(c.getValue()); + } } return false; } - public boolean solve() { + public LinkedList getSolutions() { + return solutions; + } - if(gameBoard.isSolved(new LinkedList())) { + 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; } - checkSolvedCells(); + if(showPossibles(gameBoard)) + return solve(gameBoard); - if(showPossibles()) return solve(); + if(searchHiddenSingles(gameBoard)) + return solve(gameBoard); - if(searchHiddenSingles()) return solve(); + if(searchNakedPairsTriples(gameBoard)) + return solve(gameBoard); - if(searchNakedPairsTriples()) return solve(); + if(searchHiddenPairsTriples(gameBoard)) + return solve(gameBoard); - if(searchHiddenPairsTriples()) return solve(); + if(searchNakedQuads(gameBoard)) + return solve(gameBoard); - if(searchNakedQuads()) return solve(); + if(searchPointingPairs(gameBoard)) + return solve(gameBoard); - if(searchPointingPairs()) return solve(); + if(searchBoxLineReduction(gameBoard)) + return solve(gameBoard); - if(searchBoxLineReduction()) return solve(); - return false; + // 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); + + result = solve(gameBoardCopy); + + //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; } @Override @@ -98,7 +162,28 @@ public class Solver implements ISolver { return gameBoard; } - public boolean showPossibles() { + private boolean checkSolvedCells(final GameBoard gameBoard) { + return gameBoard.actionOnCells(new ICellAction() { + @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 list = new LinkedList(); for(int i = 0; i < gameBoard.getSize(); i++) { for(int j = 0; j < gameBoard.getSize(); j++) { @@ -108,19 +193,21 @@ public class Solver implements ISolver { list.addAll(gameBoard.getRow(i)); list.addAll(gameBoard.getColumn(j)); list.addAll(gameBoard.getSection(i,j)); - for(int k = 0; k < gameBoard.getSize(); k++) { - for(GameCell c : list) { - gc.deleteNote(c.getValue()); + 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 false; + return deletedSomething; } - private boolean searchHiddenSingles() { + private boolean searchHiddenSingles(final GameBoard gameBoard) { boolean foundHiddenSingles = false; LinkedList list = new LinkedList<>(); @@ -183,40 +270,68 @@ public class Solver implements ISolver { return foundHiddenSingles; } - public boolean searchNakedPairsTriples() { + + public boolean searchNakedPairsTriples(final GameBoard gameBoard) { return false; } - public boolean searchHiddenPairsTriples() { + public boolean searchHiddenPairsTriples(final GameBoard gameBoard) { return false; } - public boolean searchNakedQuads() { + public boolean searchNakedQuads(final GameBoard gameBoard) { return false; } - public boolean searchPointingPairs() { + public boolean searchPointingPairs(final GameBoard gameBoard) { return false; } - public boolean searchBoxLineReduction() { + public boolean searchBoxLineReduction(final GameBoard gameBoard) { return false; } - private boolean checkSolvedCells() { - return gameBoard.actionOnCells(new ICellAction() { - @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; - break; - } - } - gc.setValue(value); - existing = true; - } - return existing; - }}, 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 < minimumCount) { + minimumCount = count; + bestCandidate.set(candidate.x, candidate.y); + } + + count = countUnsolved(gameBoard.getColumn(i),candidate); + if(count < minimumCount) { + minimumCount = count; + bestCandidate.set(candidate.x, candidate.y); + } + + count = countUnsolved(gameBoard.getSection(i),candidate); + if(count < minimumCount) { + minimumCount = count; + bestCandidate.set(candidate.x, candidate.y); + } + + if(minimumCount == 2) { + return bestCandidate; + } + } + return bestCandidate; } + public int countUnsolved(final List list, Point p) { + int count = 0; + for(GameCell gc : list) { + if(!gc.hasValue()) { + count++; + p.set(gc.getRow(), gc.getCol()); + } + } + return count; + } + + + + } diff --git a/app/src/main/java/tu_darmstadt/sudoku/ui/AboutActivity.java b/app/src/main/java/tu_darmstadt/sudoku/ui/AboutActivity.java index 2d6320f..7258425 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/ui/AboutActivity.java +++ b/app/src/main/java/tu_darmstadt/sudoku/ui/AboutActivity.java @@ -1,10 +1,13 @@ package tu_darmstadt.sudoku.ui; +import android.content.Intent; import android.graphics.Color; import android.graphics.drawable.ColorDrawable; +import android.net.Uri; import android.support.v7.app.AppCompatActivity; import android.os.Bundle; import android.view.MenuItem; +import android.widget.TextView; import tu_darmstadt.sudoku.ui.view.R; 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 5f1a259..2a4a449 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/ui/GameActivity.java +++ b/app/src/main/java/tu_darmstadt/sudoku/ui/GameActivity.java @@ -17,7 +17,7 @@ import java.util.List; import tu_darmstadt.sudoku.controller.SaveLoadController; import tu_darmstadt.sudoku.controller.GameController; -import tu_darmstadt.sudoku.game.GameInfoContainer; +import tu_darmstadt.sudoku.controller.helper.GameInfoContainer; import tu_darmstadt.sudoku.game.GameType; import tu_darmstadt.sudoku.ui.view.R; import tu_darmstadt.sudoku.ui.view.SudokuFieldLayout; @@ -91,7 +91,7 @@ public class GameActivity extends AppCompatActivity implements NavigationView.On //set Special keys specialButtonLayout = (SudokuSpecialButtonLayout) findViewById(R.id.sudokuSpecialLayout); - specialButtonLayout.setButtons(p.x,gameController,keyboard); + specialButtonLayout.setButtons(p.x, gameController, keyboard); /* // DEBUG String debug = gameController.getFieldAsString(); 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 8c1a396..9a809aa 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/ui/LoadGameActivity.java +++ b/app/src/main/java/tu_darmstadt/sudoku/ui/LoadGameActivity.java @@ -18,7 +18,7 @@ import android.widget.TextView; import java.util.List; import tu_darmstadt.sudoku.controller.SaveLoadController; -import tu_darmstadt.sudoku.game.GameInfoContainer; +import tu_darmstadt.sudoku.controller.helper.GameInfoContainer; import tu_darmstadt.sudoku.ui.view.R; public class LoadGameActivity extends AppCompatActivity { 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 59af77f..decf4c4 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/ui/MainActivity.java +++ b/app/src/main/java/tu_darmstadt/sudoku/ui/MainActivity.java @@ -24,7 +24,7 @@ import android.widget.TextView; import java.util.List; import tu_darmstadt.sudoku.controller.SaveLoadController; -import tu_darmstadt.sudoku.game.GameInfoContainer; +import tu_darmstadt.sudoku.controller.helper.GameInfoContainer; import tu_darmstadt.sudoku.game.GameType; import tu_darmstadt.sudoku.ui.view.R; diff --git a/app/src/main/java/tu_darmstadt/sudoku/ui/view/SudokuCellView.java b/app/src/main/java/tu_darmstadt/sudoku/ui/view/SudokuCellView.java index 35f4e14..70694ce 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/ui/view/SudokuCellView.java +++ b/app/src/main/java/tu_darmstadt/sudoku/ui/view/SudokuCellView.java @@ -130,7 +130,7 @@ public class SudokuCellView extends View { p.setTextSize(mWidth / 4); p.setTextAlign(Paint.Align.RIGHT); // TODO settings: get SymbolEnum from settings - canvas.drawText(String.valueOf(Symbol.getSymbol(symbolsToUse, i)),(mWidth*1/12)*k,(mWidth*1/12)*j,p); + canvas.drawText(Symbol.getSymbol(symbolsToUse, i),(mWidth*1/12)*k,(mWidth*1/12)*j,p); /*canvas.drawText(String.valueOf(1), (mWidth * 1 / 12)*3, (mWidth* 1 / 12)*3, p); canvas.drawText(String.valueOf(2),(mWidth*1/12)*7, (mWidth* 1 / 12)*7,p ); canvas.drawText(String.valueOf(3),(mWidth*1/12)*11, (mWidth* 1 / 12)*11,p );*/ @@ -152,7 +152,7 @@ public class SudokuCellView extends View { p.setTextSize(Math.min(mHeight * 3 / 4, mHeight * 3 / 4)); p.setTextAlign(Paint.Align.CENTER); // TODO settings: get SymbolEnum from settings - canvas.drawText(String.valueOf(Symbol.getSymbol(symbolsToUse, mGameCell.getValue()-1)), mHeight / 2, mHeight / 2 + mHeight / 4, p); + canvas.drawText(Symbol.getSymbol(symbolsToUse, mGameCell.getValue()-1), mHeight / 2, mHeight / 2 + mHeight / 4, p); } public int getRow() { diff --git a/app/src/main/java/tu_darmstadt/sudoku/ui/view/SudokuKeyboardLayout.java b/app/src/main/java/tu_darmstadt/sudoku/ui/view/SudokuKeyboardLayout.java index 6703c44..2974d3f 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/ui/view/SudokuKeyboardLayout.java +++ b/app/src/main/java/tu_darmstadt/sudoku/ui/view/SudokuKeyboardLayout.java @@ -2,7 +2,6 @@ package tu_darmstadt.sudoku.ui.view; import android.content.Context; import android.graphics.Canvas; -import android.graphics.Paint; import android.util.AttributeSet; import android.view.Gravity; import android.view.View; @@ -32,7 +31,7 @@ public class SudokuKeyboardLayout extends GridLayout { if(notesEnabled) { gameController.toggleSelectedNote(btn.getValue()); } else { - gameController.setSelectedValue(btn.getValue()); + gameController.selectValue(btn.getValue()); } gameController.saveGame(getContext()); } diff --git a/app/src/main/res/layout/activity_about.xml b/app/src/main/res/layout/activity_about.xml index 150d1ef..fd8bbf9 100644 --- a/app/src/main/res/layout/activity_about.xml +++ b/app/src/main/res/layout/activity_about.xml @@ -82,6 +82,7 @@ android:text="@string/more_info"/> result = controller.solve(); + for(GameBoard gb : result) { + assertEquals("[GameBoard: \n" + + "\t[5 (0|0)] [8 (0|1)] [1 (0|2)] \t[9 (0|3)] [2 (0|4)] [6 (0|5)] \t[7 (0|6)] [3 (0|7)] [4 (0|8)] ]" + + "\t[2 (1|0)] [6 (1|1)] [7 (1|2)] \t[1 (1|3)] [3 (1|4)] [4 (1|5)] \t[9 (1|6)] [5 (1|7)] [8 (1|8)] ]" + + "\t[3 (2|0)] [9 (2|1)] [4 (2|2)] \t[7 (2|3)] [8 (2|4)] [5 (2|5)] \t[1 (2|6)] [2 (2|7)] [6 (2|8)] ]" + + "\t[9 (3|0)] [3 (3|1)] [5 (3|2)] \t[8 (3|3)] [4 (3|4)] [1 (3|5)] \t[6 (3|6)] [7 (3|7)] [2 (3|8)] ]" + + "\t[4 (4|0)] [1 (4|1)] [6 (4|2)] \t[2 (4|3)] [5 (4|4)] [7 (4|5)] \t[8 (4|6)] [9 (4|7)] [3 (4|8)] ]" + + "\t[8 (5|0)] [7 (5|1)] [2 (5|2)] \t[3 (5|3)] [6 (5|4)] [9 (5|5)] \t[5 (5|6)] [4 (5|7)] [1 (5|8)] ]" + + "\t[1 (6|0)] [5 (6|1)] [3 (6|2)] \t[6 (6|3)] [7 (6|4)] [2 (6|5)] \t[4 (6|6)] [8 (6|7)] [9 (6|8)] ]" + + "\t[6 (7|0)] [4 (7|1)] [8 (7|2)] \t[5 (7|3)] [9 (7|4)] [3 (7|5)] \t[2 (7|6)] [1 (7|7)] [7 (7|8)] ]" + + "\t[7 (8|0)] [2 (8|1)] [9 (8|2)] \t[4 (8|3)] [1 (8|4)] [8 (8|5)] \t[3 (8|6)] [6 (8|7)] [5 (8|8)] ]", + gb.toString()); + } + } + + @Test + public void solveTest2() { + controller.loadLevel(new GameInfoContainer(0, GameType.Default_9x9, + new int[]{0,0,0,0,4,1,0,0,0,0,6,0,0,0,0,2,0,0,0,0,0,0,0,0,0,0,0,3,2,0,6,0,0,0,0,0,0,0,0,0,5,0,0,4,1,7,0,0,0,0,0,0,0,0,0,0,0,2,0,0,3,0,0,0,4,8,0,0,0,0,0,0,5,0,1,0,0,0,0,0,0} + , null, null)); + + LinkedList result = controller.solve(); + + for(GameBoard gb : result) { + assertEquals("[GameBoard: \n" + + "\t[8 (0|0)] [7 (0|1)] [2 (0|2)] \t[9 (0|3)] [4 (0|4)] [1 (0|5)] \t[5 (0|6)] [6 (0|7)] [3 (0|8)] ]"+ + "\t[1 (1|0)] [6 (1|1)] [9 (1|2)] \t[5 (1|3)] [7 (1|4)] [3 (1|5)] \t[2 (1|6)] [8 (1|7)] [4 (1|8)] ]"+ + "\t[4 (2|0)] [5 (2|1)] [3 (2|2)] \t[8 (2|3)] [2 (2|4)] [6 (2|5)] \t[1 (2|6)] [9 (2|7)] [7 (2|8)] ]" + + "\t[3 (3|0)] [2 (3|1)] [4 (3|2)] \t[6 (3|3)] [1 (3|4)] [7 (3|5)] \t[8 (3|6)] [5 (3|7)] [9 (3|8)] ]" + + "\t[9 (4|0)] [8 (4|1)] [6 (4|2)] \t[3 (4|3)] [5 (4|4)] [2 (4|5)] \t[7 (4|6)] [4 (4|7)] [1 (4|8)] ]" + + "\t[7 (5|0)] [1 (5|1)] [5 (5|2)] \t[4 (5|3)] [9 (5|4)] [8 (5|5)] \t[6 (5|6)] [3 (5|7)] [2 (5|8)] ]" + + "\t[6 (6|0)] [9 (6|1)] [7 (6|2)] \t[2 (6|3)] [8 (6|4)] [4 (6|5)] \t[3 (6|6)] [1 (6|7)] [5 (6|8)] ]" + + "\t[2 (7|0)] [4 (7|1)] [8 (7|2)] \t[1 (7|3)] [3 (7|4)] [5 (7|5)] \t[9 (7|6)] [7 (7|7)] [6 (7|8)] ]" + + "\t[5 (8|0)] [3 (8|1)] [1 (8|2)] \t[7 (8|3)] [6 (8|4)] [9 (8|5)] \t[4 (8|6)] [2 (8|7)] [8 (8|8)] ]", + gb.toString()); + } } }