Implemented the backtracking strategy for the Sudoku Solver.

JUnit tests are running. Still need to add more logic strategies.
This commit is contained in:
Christopher Beckmann 2015-11-18 03:54:33 +01:00
parent 6ad5832538
commit cc92454cc6
18 changed files with 307 additions and 104 deletions

View file

@ -8,6 +8,15 @@
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true" android:supportsRtl="true"
android:theme="@style/AppTheme" > android:theme="@style/AppTheme" >
<activity
android:name="tu_darmstadt.sudoku.ui.MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity <activity
android:name="tu_darmstadt.sudoku.ui.SettingsActivity" android:name="tu_darmstadt.sudoku.ui.SettingsActivity"
android:label="@string/title_activity_settings" android:label="@string/title_activity_settings"
@ -21,16 +30,6 @@
</activity> </activity>
<activity android:name="tu_darmstadt.sudoku.ui.AboutActivity" > <activity android:name="tu_darmstadt.sudoku.ui.AboutActivity" >
</activity> </activity>
<activity
android:name="tu_darmstadt.sudoku.ui.MainActivity"
android:label="@string/app_name"
android:theme="@style/AppTheme.NoActionBar" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name="tu_darmstadt.sudoku.ui.LoadGameActivity" > <activity android:name="tu_darmstadt.sudoku.ui.LoadGameActivity" >
</activity> </activity>
</application> </application>

View file

@ -10,7 +10,7 @@ import tu_darmstadt.sudoku.game.CellConflict;
import tu_darmstadt.sudoku.game.CellConflictList; import tu_darmstadt.sudoku.game.CellConflictList;
import tu_darmstadt.sudoku.game.GameBoard; import tu_darmstadt.sudoku.game.GameBoard;
import tu_darmstadt.sudoku.game.GameCell; 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.GameType;
import tu_darmstadt.sudoku.game.ICellAction; import tu_darmstadt.sudoku.game.ICellAction;
import tu_darmstadt.sudoku.game.solver.Solver; import tu_darmstadt.sudoku.game.solver.Solver;
@ -25,13 +25,14 @@ public class GameController {
private int sectionHeight; private int sectionHeight;
private int sectionWidth; private int sectionWidth;
private GameBoard gameBoard; private GameBoard gameBoard;
private ISolver solver; private Solver solver;
private GameType gameType; private GameType gameType;
private int selectedRow; private int selectedRow;
private int selectedCol; private int selectedCol;
private SharedPreferences settings; private SharedPreferences settings;
private int gameID = 0; private int gameID = 0;
private CellConflictList errorList = new CellConflictList(); private CellConflictList errorList = new CellConflictList();
private int selectedValue;
//private LinkedList<IModelChangeListener> listeners = new LinkedList<>(); //private LinkedList<IModelChangeListener> listeners = new LinkedList<>();
// private Solver solver; // private Solver solver;
@ -139,7 +140,7 @@ public class GameController {
settings = pref; settings = pref;
} }
public GameBoard solve(GameBoard gameBoard) { public LinkedList<GameBoard> solve() {
switch(gameType) { switch(gameType) {
case Default_9x9: case Default_9x9:
case Default_6x6: case Default_6x6:
@ -150,8 +151,8 @@ public class GameController {
throw new UnsupportedOperationException("No Solver for this GameType defined."); throw new UnsupportedOperationException("No Solver for this GameType defined.");
} }
if(solver.solve()) { if(solver.solve(solver.getGameBoard())) {
return solver.getGameBoard(); return solver.getSolutions();
} }
return null; return null;
} }
@ -228,6 +229,15 @@ public class GameController {
return gameBoard.actionOnCells(ca,existing); 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) { public void saveGame(Context context) {
if(settings == null) { if(settings == null) {
return; return;
@ -312,15 +322,34 @@ public class GameController {
return gameBoard.toString(); 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() { public int getSelectedRow() {
return selectedRow; return selectedRow;
} }
public int getSelectedCol() { public int getSelectedCol() {
return selectedCol; return selectedCol;
} }
public int getSelectedValue() {
return selectedValue;
}
public void selectCell(int row, int col) { 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(selectedRow == row && selectedCol == col) {
// if we select the same field 2ce -> deselect it // if we select the same field 2ce -> deselect it
selectedRow = -1; 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); if(isCellSelected()) setValue(selectedRow, selectedCol, value);
} }

View file

@ -0,0 +1,8 @@
package tu_darmstadt.sudoku.controller;
/**
* Created by Chris on 17.11.2015.
*/
public class Highscore {
}

View file

@ -11,7 +11,7 @@ import java.io.IOException;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import tu_darmstadt.sudoku.game.GameInfoContainer; import tu_darmstadt.sudoku.controller.helper.GameInfoContainer;
/** /**
* Created by Chris on 16.11.2015. * Created by Chris on 16.11.2015.

View file

@ -5,22 +5,24 @@ package tu_darmstadt.sudoku.controller;
*/ */
public enum Symbol { 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'}), 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"}),
Fancy(new char[] {'♪', '♫', '☼', '♥', '♦', '♣', '♠', '•', '○', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', 'L', 'M', 'N' }); 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; this.map = map;
} }
public static String getSymbol(Symbol type, int value) { 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++) { 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; return -1;
} }

View file

@ -1,9 +1,12 @@
package tu_darmstadt.sudoku.game; package tu_darmstadt.sudoku.controller.helper;
import android.util.Log; import android.util.Log;
import tu_darmstadt.sudoku.controller.GameController; import tu_darmstadt.sudoku.controller.GameController;
import tu_darmstadt.sudoku.controller.Symbol; 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. * Created by Chris on 17.11.2015.
@ -46,7 +49,7 @@ public class GameInfoContainer {
} }
fixedValues = new int[s.length()]; fixedValues = new int[s.length()];
for(int i = 0; i < s.length(); i++) { 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()]; setValues = new int[s.length()];
for(int i = 0; i < s.length(); i++) { 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 @Override
public StringBuilder action(GameCell gc, StringBuilder existing) { public StringBuilder action(GameCell gc, StringBuilder existing) {
if (gc.isFixed()) { if (gc.isFixed()) {
existing.append(Symbol.getSymbol(Symbol.Default, gc.getValue() - 1)); existing.append(Symbol.getSymbol(Symbol.SaveFormat, gc.getValue() - 1));
} else { } else {
existing.append(0); existing.append(0);
} }
@ -151,7 +154,7 @@ public class GameInfoContainer {
if (gc.isFixed() || gc.getValue() == 0) { if (gc.isFixed() || gc.getValue() == 0) {
existing.append(0); existing.append(0);
} else { } else {
existing.append(Symbol.getSymbol(Symbol.Default, gc.getValue() - 1)); existing.append(Symbol.getSymbol(Symbol.SaveFormat, gc.getValue() - 1));
} }
return existing; return existing;
} }

View file

@ -69,6 +69,10 @@ public class GameBoard implements Cloneable {
} }
public LinkedList<GameCell> getRow(final int row) { public LinkedList<GameCell> getRow(final int row) {
LinkedList<GameCell> result = new LinkedList<GameCell>();
for(int i = 0; i < size; i++) {
}
return actionOnCells(new ICellAction<LinkedList<GameCell>>() { return actionOnCells(new ICellAction<LinkedList<GameCell>>() {
@Override @Override
public LinkedList<GameCell> action(GameCell gc, LinkedList<GameCell> existing) { public LinkedList<GameCell> action(GameCell gc, LinkedList<GameCell> existing) {

View file

@ -7,7 +7,7 @@ import tu_darmstadt.sudoku.game.GameBoard;
*/ */
public interface ISolver { public interface ISolver {
public boolean solve(); public boolean solve(GameBoard gameBoard);
public boolean calculateNextPossibleStep(); public boolean calculateNextPossibleStep();

View file

@ -1,7 +1,12 @@
package tu_darmstadt.sudoku.game.solver; 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.CellConflict;
import tu_darmstadt.sudoku.game.GameBoard; import tu_darmstadt.sudoku.game.GameBoard;
import tu_darmstadt.sudoku.game.GameCell; import tu_darmstadt.sudoku.game.GameCell;
@ -13,6 +18,7 @@ import tu_darmstadt.sudoku.game.ICellAction;
public class Solver implements ISolver { public class Solver implements ISolver {
private GameBoard gameBoard = null; private GameBoard gameBoard = null;
private LinkedList<GameBoard> solutions = new LinkedList<>();
public Solver(GameBoard gf) { public Solver(GameBoard gf) {
try { try {
@ -36,7 +42,7 @@ public class Solver implements ISolver {
public void setNotes(GameBoard gameBoard) { public void setNotes(GameBoard gameBoard) {
for(int i = 0; i < gameBoard.getSize(); i++) { for(int i = 0; i < gameBoard.getSize(); i++) {
for(int j = 0; j < gameBoard.getSize(); j++) { 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); gameBoard.getCell(i,j).setNote(k);
} }
} }
@ -58,34 +64,92 @@ public class Solver implements ISolver {
if(checked.contains(c.getValue())) { if(checked.contains(c.getValue())) {
return true; return true;
} }
checked.add(c.getValue()); if(c.hasValue()) {
checked.add(c.getValue());
}
} }
return false; return false;
} }
public boolean solve() { public LinkedList<GameBoard> getSolutions() {
return solutions;
}
if(gameBoard.isSolved(new LinkedList<CellConflict>())) { 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; 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 @Override
@ -98,7 +162,28 @@ public class Solver implements ISolver {
return gameBoard; return gameBoard;
} }
public boolean showPossibles() { 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>(); LinkedList<GameCell> list = new LinkedList<GameCell>();
for(int i = 0; i < gameBoard.getSize(); i++) { for(int i = 0; i < gameBoard.getSize(); i++) {
for(int j = 0; j < gameBoard.getSize(); j++) { 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.getRow(i));
list.addAll(gameBoard.getColumn(j)); list.addAll(gameBoard.getColumn(j));
list.addAll(gameBoard.getSection(i,j)); list.addAll(gameBoard.getSection(i,j));
for(int k = 0; k < gameBoard.getSize(); k++) { for(GameCell c : list) {
for(GameCell c : list) { for(int k = 0; k < gameBoard.getSize(); k++) {
gc.deleteNote(c.getValue()); 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; boolean foundHiddenSingles = false;
LinkedList<GameCell> list = new LinkedList<>(); LinkedList<GameCell> list = new LinkedList<>();
@ -183,40 +270,68 @@ public class Solver implements ISolver {
return foundHiddenSingles; return foundHiddenSingles;
} }
public boolean searchNakedPairsTriples() {
public boolean searchNakedPairsTriples(final GameBoard gameBoard) {
return false; return false;
} }
public boolean searchHiddenPairsTriples() { public boolean searchHiddenPairsTriples(final GameBoard gameBoard) {
return false; return false;
} }
public boolean searchNakedQuads() { public boolean searchNakedQuads(final GameBoard gameBoard) {
return false; return false;
} }
public boolean searchPointingPairs() { public boolean searchPointingPairs(final GameBoard gameBoard) {
return false; return false;
} }
public boolean searchBoxLineReduction() { public boolean searchBoxLineReduction(final GameBoard gameBoard) {
return false; return false;
} }
private boolean checkSolvedCells() { public Point getBestCandidate(GameBoard gameBoard) {
return gameBoard.actionOnCells(new ICellAction<Boolean>() { Point bestCandidate = new Point();
@Override int minimumCount = gameBoard.getSize();
public Boolean action(GameCell gc, Boolean existing) { int count = 0;
int value = -1; Point candidate = new Point();
if(!gc.hasValue() && gc.getNoteCount() == 1) { for(int i = 0; i < gameBoard.getSize(); i++) {
for(int i = 0; i < gameBoard.getSize(); i++) {
if(gc.getNotes()[i]) { count = countUnsolved(gameBoard.getRow(i),candidate);
value = i; if(count < minimumCount) {
break; minimumCount = count;
} bestCandidate.set(candidate.x, candidate.y);
} }
gc.setValue(value);
existing = true; count = countUnsolved(gameBoard.getColumn(i),candidate);
} if(count < minimumCount) {
return existing; minimumCount = count;
}}, false); 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<GameCell> list, Point p) {
int count = 0;
for(GameCell gc : list) {
if(!gc.hasValue()) {
count++;
p.set(gc.getRow(), gc.getCol());
}
}
return count;
}
} }

View file

@ -1,10 +1,13 @@
package tu_darmstadt.sudoku.ui; package tu_darmstadt.sudoku.ui;
import android.content.Intent;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.os.Bundle; import android.os.Bundle;
import android.view.MenuItem; import android.view.MenuItem;
import android.widget.TextView;
import tu_darmstadt.sudoku.ui.view.R; import tu_darmstadt.sudoku.ui.view.R;

View file

@ -17,7 +17,7 @@ import java.util.List;
import tu_darmstadt.sudoku.controller.SaveLoadController; import tu_darmstadt.sudoku.controller.SaveLoadController;
import tu_darmstadt.sudoku.controller.GameController; 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.game.GameType;
import tu_darmstadt.sudoku.ui.view.R; import tu_darmstadt.sudoku.ui.view.R;
import tu_darmstadt.sudoku.ui.view.SudokuFieldLayout; import tu_darmstadt.sudoku.ui.view.SudokuFieldLayout;
@ -91,7 +91,7 @@ public class GameActivity extends AppCompatActivity implements NavigationView.On
//set Special keys //set Special keys
specialButtonLayout = (SudokuSpecialButtonLayout) findViewById(R.id.sudokuSpecialLayout); specialButtonLayout = (SudokuSpecialButtonLayout) findViewById(R.id.sudokuSpecialLayout);
specialButtonLayout.setButtons(p.x,gameController,keyboard); specialButtonLayout.setButtons(p.x, gameController, keyboard);
/* /*
// DEBUG // DEBUG
String debug = gameController.getFieldAsString(); String debug = gameController.getFieldAsString();

View file

@ -18,7 +18,7 @@ import android.widget.TextView;
import java.util.List; import java.util.List;
import tu_darmstadt.sudoku.controller.SaveLoadController; 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; import tu_darmstadt.sudoku.ui.view.R;
public class LoadGameActivity extends AppCompatActivity { public class LoadGameActivity extends AppCompatActivity {

View file

@ -24,7 +24,7 @@ import android.widget.TextView;
import java.util.List; import java.util.List;
import tu_darmstadt.sudoku.controller.SaveLoadController; 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.game.GameType;
import tu_darmstadt.sudoku.ui.view.R; import tu_darmstadt.sudoku.ui.view.R;

View file

@ -130,7 +130,7 @@ public class SudokuCellView extends View {
p.setTextSize(mWidth / 4); p.setTextSize(mWidth / 4);
p.setTextAlign(Paint.Align.RIGHT); p.setTextAlign(Paint.Align.RIGHT);
// TODO settings: get SymbolEnum from settings // 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(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(2),(mWidth*1/12)*7, (mWidth* 1 / 12)*7,p );
canvas.drawText(String.valueOf(3),(mWidth*1/12)*11, (mWidth* 1 / 12)*11,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.setTextSize(Math.min(mHeight * 3 / 4, mHeight * 3 / 4));
p.setTextAlign(Paint.Align.CENTER); p.setTextAlign(Paint.Align.CENTER);
// TODO settings: get SymbolEnum from settings // 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() { public int getRow() {

View file

@ -2,7 +2,6 @@ package tu_darmstadt.sudoku.ui.view;
import android.content.Context; import android.content.Context;
import android.graphics.Canvas; import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.Gravity; import android.view.Gravity;
import android.view.View; import android.view.View;
@ -32,7 +31,7 @@ public class SudokuKeyboardLayout extends GridLayout {
if(notesEnabled) { if(notesEnabled) {
gameController.toggleSelectedNote(btn.getValue()); gameController.toggleSelectedNote(btn.getValue());
} else { } else {
gameController.setSelectedValue(btn.getValue()); gameController.selectValue(btn.getValue());
} }
gameController.saveGame(getContext()); gameController.saveGame(getContext());
} }

View file

@ -82,6 +82,7 @@
android:text="@string/more_info"/> android:text="@string/more_info"/>
<TextView <TextView
android:id="@+id/linkID"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text="@string/url" android:text="@string/url"

View file

@ -3,7 +3,7 @@ package tu_darmstadt.sudoku.controller;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import tu_darmstadt.sudoku.game.GameInfoContainer; import tu_darmstadt.sudoku.controller.helper.GameInfoContainer;
import tu_darmstadt.sudoku.game.GameType; import tu_darmstadt.sudoku.game.GameType;
import static org.junit.Assert.*; import static org.junit.Assert.*;

View file

@ -1,11 +1,19 @@
package tu_darmstadt.sudoku.game.solver; package tu_darmstadt.sudoku.game.solver;
import android.util.Log;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
import java.util.LinkedList;
import tu_darmstadt.sudoku.controller.GameController; import tu_darmstadt.sudoku.controller.GameController;
import tu_darmstadt.sudoku.controller.helper.GameInfoContainer;
import tu_darmstadt.sudoku.game.GameBoard;
import tu_darmstadt.sudoku.game.GameType; import tu_darmstadt.sudoku.game.GameType;
import static junit.framework.Assert.assertEquals;
/** /**
* Created by Chris on 12.11.2015. * Created by Chris on 12.11.2015.
*/ */
@ -15,31 +23,63 @@ public class SolverTest {
@Before @Before
public void init() { public void init() {
/*controller = new GameController(); controller = new GameController();
int[][] level = {{ 5, 0, 1, 9, 0, 0, 0, 0, 0 }, controller.loadLevel(new GameInfoContainer(0, GameType.Default_9x9,
{ 2, 0, 0, 0, 0, 4, 9, 5, 0 }, new int[]{5, 0, 1, 9, 0, 0, 0, 0, 0,
{ 3, 9, 0, 7, 0, 0, 0, 2, 6 }, 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));
{ 0, 3, 0, 0, 0, 1, 0, 7, 2 }, /*controller.loadLevel(new GameInfoContainer(3, GameType.Default_9x9,
{ 0, 0, 6, 0, 5, 7, 0, 0, 0 }, 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}
{ 0, 7, 2, 0, 0, 9, 0, 4, 1 }, , null, null));*/
{ 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 }};
controller.loadLevel(GameType.icon_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);*/
} }
//000041000060000200000000000320600000000050041700000000000200300048000000501000000
//501900000200004950390700026030001072006057000072009041000070409640000000700010305
@Test @Test
public void solveTest() { public void solveTest1() {
LinkedList<GameBoard> 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<GameBoard> 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());
}
} }
} }