diff --git a/.idea/vcs.xml b/.idea/vcs.xml index 6564d52..94a25f7 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,6 +1,6 @@ - + \ No newline at end of file diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 2948c5e..c429233 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -8,15 +8,6 @@ android:label="@string/app_name" android:supportsRtl="true" android:theme="@style/AppTheme" > - listeners = new LinkedList<>(); -// private SudokuSolver solver; +// private Default9x9Solver solver; // private SudokuGenerator generator; public GameController() { @@ -36,6 +40,8 @@ public class GameController { } }*/ + + public void setValue(int row, int col, int value) { GameCell cell = gameField.getCell(row, col); if (!cell.isFixed() && isValidNumber(value)) { @@ -47,13 +53,14 @@ public class GameController { updateList.addAll(gameField.getRow(cell.getRow())); updateList.addAll(gameField.getColumn(cell.getCol())); updateList.addAll(gameField.getSection(cell.getRow(), cell.getCol())); - deleteNote(updateList, value); + deleteNotes(updateList, value); } - } } - public void deleteNote(List updateList, int value) { + + + public void deleteNotes(List updateList, int value) { for(GameCell c : updateList) { c.deleteNote(value); } @@ -125,6 +132,50 @@ public class GameController { return errorList; } + public void resetLevel() { + gameField.actionOnCells(new ICellAction() { + @Override + public Boolean action(GameCell gc, Boolean existing) { + gc.reset(); + return true; + } + }, true); + notifyListeners(); + } + + public boolean deleteValue(int row, int col) { + GameCell c = gameField.getCell(row,col); + if(!c.isFixed()) { + c.setValue(0); + notifyListeners(); + return true; + } + return false; + } + + public void setNote(int row, int col, int value) { + GameCell c = gameField.getCell(row,col); + c.setNote(value); + notifyListeners(); + } + + public boolean[] getNotes(int row, int col) { + GameCell c = gameField.getCell(row,col); + return c.getNotes().clone(); + } + + public void deleteNote(int row, int col, int value) { + GameCell c = gameField.getCell(row,col); + c.deleteNote(value); + notifyListeners(); + } + + public void toggleNote(int row, int col, int value) { + GameCell c = gameField.getCell(row,col); + c.toggleNote(value); + notifyListeners(); + } + /** Debug only method * * @return the Field represented as a String @@ -132,4 +183,17 @@ public class GameController { public String getFieldAsString() { return gameField.toString(); } + + public void registerListener(IModelChangeListener l) { + if(!listeners.contains(l)) { + listeners.add(l); + } + } + + public void notifyListeners() { + for(IModelChangeListener l : listeners) { + l.onModelChanged(); + } + } + } diff --git a/app/src/main/java/tu_darmstadt/sudoku/controller/IModelChangeListener.java b/app/src/main/java/tu_darmstadt/sudoku/controller/IModelChangeListener.java new file mode 100644 index 0000000..f551e96 --- /dev/null +++ b/app/src/main/java/tu_darmstadt/sudoku/controller/IModelChangeListener.java @@ -0,0 +1,8 @@ +package tu_darmstadt.sudoku.controller; + +/** + * Created by Chris on 11.11.2015. + */ +public interface IModelChangeListener { + public void onModelChanged(); +} diff --git a/app/src/main/java/tu_darmstadt/sudoku/game/GameCell.java b/app/src/main/java/tu_darmstadt/sudoku/game/GameCell.java index 202bf8b..3593041 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/game/GameCell.java +++ b/app/src/main/java/tu_darmstadt/sudoku/game/GameCell.java @@ -1,14 +1,17 @@ package tu_darmstadt.sudoku.game; +import java.util.Arrays; + /** * Created by Chris on 06.11.2015. */ -public class GameCell { +public class GameCell implements Cloneable { private int row = 0; private int col = 0; private int value = 0; private boolean fixed = false; + private int noteCount = 0; private boolean notes[]; private int size = 0; @@ -59,18 +62,28 @@ public class GameCell { * @param val the value to be toggled. */ public void toggleNote(int val) { - if(!isFixed()) + if(!isFixed()) { + noteCount = notes[val - 1] ? noteCount - 1 : noteCount + 1; notes[val - 1] = !notes[val - 1]; + } } public void setNote(int val) { - if(!isFixed()) + if(!isFixed()) { + noteCount = notes[val - 1] ? noteCount : noteCount + 1; notes[val - 1] = true; + } } public void deleteNote(int val) { - if(!isFixed()) + if(!isFixed()) { + noteCount = notes[val - 1] ? noteCount - 1 : noteCount; notes[val - 1] = false; + } + } + + public int getNoteCount() { + return noteCount; } public boolean[] getNotes() { @@ -81,6 +94,7 @@ public class GameCell { * Clear the notes array (set everything to false). */ public void deleteNotes() { + noteCount = 0; notes = new boolean[size]; } @@ -134,4 +148,19 @@ public class GameCell { return sb.toString(); } + + public Boolean reset() { + if(isFixed()) { + return false; + } + setValue(0); + return true; + } + + @Override + public GameCell clone() throws CloneNotSupportedException { + GameCell clone = (GameCell) super.clone(); + clone.notes = (notes == null) ? null : Arrays.copyOf(notes, notes.length); + return clone; + } } diff --git a/app/src/main/java/tu_darmstadt/sudoku/game/GameField.java b/app/src/main/java/tu_darmstadt/sudoku/game/GameField.java index bf3b09d..865f016 100644 --- a/app/src/main/java/tu_darmstadt/sudoku/game/GameField.java +++ b/app/src/main/java/tu_darmstadt/sudoku/game/GameField.java @@ -1,11 +1,12 @@ package tu_darmstadt.sudoku.game; +import java.util.Arrays; import java.util.LinkedList; /** * Created by Christopher Beckmann on 06.11.2015. */ -public class GameField { +public class GameField implements Cloneable { //private int id; private int sectionHeight; @@ -49,6 +50,9 @@ public class GameField { break; case Unspecified: default: + this.size = 1; + this.sectionHeight = 1; + this.sectionWidth = 1; throw new IllegalArgumentException("GameType can not be unspecified."); } } @@ -90,19 +94,15 @@ public class GameField { return result; } - public LinkedList getSection(int sec) { - LinkedList result = new LinkedList(); - for(int i = 0; i < size ; i++) { // row - for(int j = 0 ; j < size ; j++) { // col - if((int)(Math.floor(i/sectionHeight)*sectionHeight + Math.floor(j/sectionWidth)) == sec) { - result.add(field[i][j]); + public LinkedList getSection(final int sec) { + return actionOnCells(new ICellAction>() { + @Override + public LinkedList action(GameCell gc, LinkedList existing) { + if((int)(Math.floor(gc.getRow()/sectionHeight)*sectionHeight + Math.floor(gc.getCol()/sectionWidth)) == sec) { + existing.add(gc); } - if(result.size() >= sectionHeight*sectionWidth) { - break; - } - } - } - return result; + return existing; + }}, new LinkedList()); } public LinkedList getSection(int row, int col) { @@ -114,16 +114,40 @@ public class GameField { return size; } + public T actionOnCells(ICellAction ca, T existing) { + for(int i = 0; i < field.length; i++) { + for(int j = 0; j < field[i].length; j++) { + existing = ca.action(field[i][j], existing); + } + } + return existing; + } + + @Override + public GameField clone() throws CloneNotSupportedException { + GameField clone = (GameField) super.clone(); + + GameCell[][] cloneField = new GameCell[size][size]; + for(int i = 0; i < size; i++) { + for(int j = 0; j < size; j++) { + cloneField[i][j] = field[i][j].clone(); + } + } + clone.field = cloneField; + + return clone; + } + @Override public String toString() { StringBuilder sb = new StringBuilder(); - sb.append("GameField: "); sb.append("\n"); + sb.append("[GameField: \n"); - for(int i = 0; i < size; i++) { + for (int i = 0; i < size; i++) { - for(int j = 0; j < size; j++) { - if(j % sectionWidth == 0) { + for (int j = 0; j < size; j++) { + if (j % sectionWidth == 0) { sb.append("\t"); } @@ -131,7 +155,7 @@ public class GameField { sb.append(" "); } - sb.append("\n"); + sb.append("]"); } return sb.toString(); } diff --git a/app/src/main/java/tu_darmstadt/sudoku/game/ICellAction.java b/app/src/main/java/tu_darmstadt/sudoku/game/ICellAction.java new file mode 100644 index 0000000..f2ee966 --- /dev/null +++ b/app/src/main/java/tu_darmstadt/sudoku/game/ICellAction.java @@ -0,0 +1,8 @@ +package tu_darmstadt.sudoku.game; + +/** + * Created by Chris on 10.11.2015. + */ +public interface ICellAction { + T action(GameCell gc, T existing); +} diff --git a/app/src/main/java/tu_darmstadt/sudoku/game/solver/Default9x9Solver.java b/app/src/main/java/tu_darmstadt/sudoku/game/solver/Default9x9Solver.java new file mode 100644 index 0000000..f6f77db --- /dev/null +++ b/app/src/main/java/tu_darmstadt/sudoku/game/solver/Default9x9Solver.java @@ -0,0 +1,89 @@ +package tu_darmstadt.sudoku.game.solver; + +import tu_darmstadt.sudoku.game.GameCell; +import tu_darmstadt.sudoku.game.GameField; +import tu_darmstadt.sudoku.game.ICellAction; + +/** + * Created by Chris on 10.11.2015. + */ +public class Default9x9Solver implements ISolver { + + private GameField gameField = null; + + Default9x9Solver(GameField gameField) { + try { + if(gameField == null) { + throw new IllegalArgumentException("GameField may not be null."); + } + + this.gameField = gameField.clone(); + } catch(CloneNotSupportedException e) { + throw new IllegalArgumentException("This GameField is not cloneable.", e); + } + } + + public boolean solve() { + + checkSolvedCells(); + /* + if(showPossibles()) return solve(); + + if(searchHiddenSingles()) return solve(); + + if(searchNakedPairsTriples()) return solve(); + + if(searchHiddenPairsTriples()) return solve(); + + if(searchNakedQuads()) return solve(); + + if(searchPointingPairs()) return solve(); + + if(searchBoxLineReduction()) return solve(); + + selectBestCell(); + */ + + + + + + + return true; + } + + @Override + public boolean calculateNextPossibleStep() { + return false; + } + + public GameField getGameField() { + return gameField; + } + + private boolean checkSolvedCells() { + return gameField.actionOnCells(new ICellAction() { + @Override + public Boolean action(GameCell gc, Boolean existing) { + boolean oneNote = false; + int value = -1; + if(gc.getNoteCount() == 1) { + for(int i = 0; i < gameField.getSize(); i++) { + if(gc.getNotes()[i]) { + value = i; + break; + } + } + gc.setValue(value); + existing = true; + } + return existing; + }}, false); + } + + private boolean searchHiddenSingles() { + return false; + } + + +} 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 new file mode 100644 index 0000000..5d12da9 --- /dev/null +++ b/app/src/main/java/tu_darmstadt/sudoku/game/solver/ISolver.java @@ -0,0 +1,16 @@ +package tu_darmstadt.sudoku.game.solver; + +import tu_darmstadt.sudoku.game.GameField; + +/** + * Created by Chris on 11.11.2015. + */ +public interface ISolver { + + public boolean solve(); + + public boolean calculateNextPossibleStep(); + + public GameField getGameField(); + +} diff --git a/app/src/main/java/tu_darmstadt/sudoku/view/MainMenu.java b/app/src/main/java/tu_darmstadt/sudoku/view/MainMenu.java deleted file mode 100644 index d016c10..0000000 --- a/app/src/main/java/tu_darmstadt/sudoku/view/MainMenu.java +++ /dev/null @@ -1,52 +0,0 @@ -package tu_darmstadt.sudoku.view; - -import android.os.Bundle; -import android.support.design.widget.FloatingActionButton; -import android.support.design.widget.Snackbar; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; -import android.view.View; -import android.view.Menu; -import android.view.MenuItem; - -public class MainMenu extends AppCompatActivity { - - @Override - protected void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - setContentView(R.layout.activity_main_menu); - //Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar); - //setSupportActionBar(toolbar); - - FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab); - fab.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View view) { - Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG) - .setAction("Action", null).show(); - } - }); - } - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - // Inflate the menu; this adds items to the action bar if it is present. - getMenuInflater().inflate(R.menu.menu_main_menu, menu); - return true; - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - // Handle action bar item clicks here. The action bar will - // automatically handle clicks on the Home/Up button, so long - // as you specify a parent activity in AndroidManifest.xml. - int id = item.getItemId(); - - //noinspection SimplifiableIfStatement - if (id == R.id.action_settings) { - return true; - } - - return super.onOptionsItemSelected(item); - } -} diff --git a/app/src/main/res/layout/activity_main_menu.xml b/app/src/main/res/layout/activity_main_menu.xml deleted file mode 100644 index 7697f78..0000000 --- a/app/src/main/res/layout/activity_main_menu.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - - - - - - - diff --git a/app/src/main/res/layout/content_main_menu.xml b/app/src/main/res/layout/content_main_menu.xml deleted file mode 100644 index 7f0cbd7..0000000 --- a/app/src/main/res/layout/content_main_menu.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - diff --git a/app/src/main/res/menu/menu_main_menu.xml b/app/src/main/res/menu/menu_main_menu.xml deleted file mode 100644 index 2b4455a..0000000 --- a/app/src/main/res/menu/menu_main_menu.xml +++ /dev/null @@ -1,6 +0,0 @@ - - - diff --git a/app/src/test/java/tu_darmstadt/sudoku/controller/GameControllerTest.java b/app/src/test/java/tu_darmstadt/sudoku/controller/GameControllerTest.java index df32634..3d76f4a 100644 --- a/app/src/test/java/tu_darmstadt/sudoku/controller/GameControllerTest.java +++ b/app/src/test/java/tu_darmstadt/sudoku/controller/GameControllerTest.java @@ -1,11 +1,8 @@ package tu_darmstadt.sudoku.controller; import org.junit.Before; -import org.junit.BeforeClass; import org.junit.Test; -import tu_darmstadt.sudoku.game.GameType; - import static org.junit.Assert.*; /** @@ -153,4 +150,45 @@ public class GameControllerTest { assertEquals(2, controller.getErrorList().size()); assertEquals(result, controller.getErrorList().toString()); } + + @Test + public void deleteTest() { + controller.setValue(1, 2, 5); + assertEquals(5, controller.getValue(1, 2)); + controller.deleteValue(1, 2); + assertEquals(0, controller.getValue(1, 2)); + } + + @Test + public void createNoteTest() { + controller.setNote(1, 2, 5); + controller.setNote(1, 2, 9); + + boolean[] result = {false, false, false, false, true, false, false, false, true}; + + assertArrayEquals(result, controller.getNotes(1, 2)); + } + + @Test + public void deleteNoteTest() { + controller.setNote(1, 2, 5); + controller.setNote(1, 2, 9); + controller.deleteNote(1, 2, 5); + + boolean[] result = {false, false, false, false, false, false, false, false, true}; + + assertArrayEquals(result, controller.getNotes(1, 2)); + } + + @Test + public void toggleNoteTest() { + controller.toggleNote(1,2,5); + controller.toggleNote(1,2,9); + controller.toggleNote(1,2,5); + controller.toggleNote(1,2,4); + + boolean[] result = {false, false, false, true, false, false, false, false, true}; + + assertArrayEquals(result, controller.getNotes(1, 2)); + } }