Added Cell Selection and Highlighting of Connected Cells.

This commit is contained in:
Christopher Beckmann 2015-11-12 14:18:42 +01:00
parent a8486fe5a2
commit 8566f4341c
13 changed files with 303 additions and 102 deletions

View file

@ -10,6 +10,8 @@ import tu_darmstadt.sudoku.game.GameField;
import tu_darmstadt.sudoku.game.GameSettings;
import tu_darmstadt.sudoku.game.GameType;
import tu_darmstadt.sudoku.game.ICellAction;
import tu_darmstadt.sudoku.game.solver.Default9x9Solver;
import tu_darmstadt.sudoku.game.solver.ISolver;
/**
* Created by Chris on 06.11.2015.
@ -20,9 +22,13 @@ public class GameController {
private int sectionHeight;
private int sectionWidth;
private GameField gameField;
private ISolver solver;
private GameType gameType;
private int selectedRow;
private int selectedCol;
private CellConflictList errorList = new CellConflictList();
private GameSettings settings = new GameSettings();
private LinkedList<IModelChangeListener> listeners = new LinkedList<>();
//private LinkedList<IModelChangeListener> listeners = new LinkedList<>();
// private Default9x9Solver solver;
// private SudokuGenerator generator;
@ -32,16 +38,24 @@ public class GameController {
}
public GameController(GameType type) {
this.gameType = type;
setGameType(type);
gameField = new GameField(size, sectionHeight, sectionWidth);
setValue(0, 1, 8); setValue(0, 4, 2);
setValue(1, 1, 6); setValue(1, 2, 7);
setValue(2, 4, 8); setValue(2, 5, 5);
setValue(3, 4, 4); setValue(3, 6, 6);
setValue(4, 7, 9); setValue(4, 8, 3);
setValue(6, 0, 1); setValue(6, 1, 5);
setValue(7, 2, 8); setValue(7, 3, 5);
setValue(8, 2, 9); setValue(8, 3, 4);
}
private GameField solve(GameField gameField) {
switch(gameType) {
case Default_9x9:
solver = new Default9x9Solver(gameField);
break;
default:
throw new UnsupportedOperationException("No Solver for this GameType defined.");
}
if(solver.solve()) {
return solver.getGameField();
}
return null;
}
/*public boolean loadLevel(GameField level) {
@ -102,6 +116,19 @@ public class GameController {
}
}
public LinkedList<GameCell> getConnectedCells(int row, int col, boolean connectedSec, boolean connectedRow, boolean connectedCol) {
LinkedList<GameCell> list = new LinkedList<>();
if(connectedRow) list.addAll(gameField.getRow(row));
list.remove(gameField.getCell(row, col));
if(connectedCol) list.addAll(gameField.getColumn(col));
list.remove(gameField.getCell(row, col));
if(connectedSec) list.addAll(gameField.getSection(row, col));
list.remove(gameField.getCell(row, col));
return list;
}
public void deleteNotes(List<GameCell> updateList, int value) {
for(GameCell c : updateList) {
c.deleteNote(value);
@ -132,21 +159,15 @@ public class GameController {
}
public void resetLevel() {
gameField.actionOnCells(new ICellAction<Boolean>() {
@Override
public Boolean action(GameCell gc, Boolean existing) {
gc.reset();
return true;
}
}, true);
notifyListeners();
gameField.reset();
//notifyListeners();
}
public boolean deleteValue(int row, int col) {
GameCell c = gameField.getCell(row,col);
if(!c.isFixed()) {
c.setValue(0);
notifyListeners();
//notifyListeners();
return true;
}
return false;
@ -155,7 +176,7 @@ public class GameController {
public void setNote(int row, int col, int value) {
GameCell c = gameField.getCell(row,col);
c.setNote(value);
notifyListeners();
//notifyListeners();
}
public boolean[] getNotes(int row, int col) {
@ -166,13 +187,13 @@ public class GameController {
public void deleteNote(int row, int col, int value) {
GameCell c = gameField.getCell(row,col);
c.deleteNote(value);
notifyListeners();
//notifyListeners();
}
public void toggleNote(int row, int col, int value) {
GameCell c = gameField.getCell(row,col);
c.toggleNote(value);
notifyListeners();
//notifyListeners();
}
/** Debug only method
@ -183,12 +204,21 @@ public class GameController {
return gameField.toString();
}
public void registerListener(IModelChangeListener l) {
if(!listeners.contains(l)) {
listeners.add(l);
public void selectCell(int row, int col) {
this.selectedRow = row;
this.selectedCol = col;
}
public void setSelectedValue(int value) {
setValue(selectedRow, selectedCol, value);
}
// public void registerListener(IModelChangeListener l) {
// if(!listeners.contains(l)) {
// listeners.add(l);
// }
// }
public int getSectionHeight() {
return sectionHeight;
}
@ -197,10 +227,10 @@ public class GameController {
return sectionWidth;
}
public void notifyListeners() {
for(IModelChangeListener l : listeners) {
l.onModelChanged();
}
}
// public void notifyListeners() {
// for(IModelChangeListener l : listeners) {
// l.onModelChanged();
// }
// }
}

View file

@ -1,8 +0,0 @@
package tu_darmstadt.sudoku.controller;
/**
* Created by Chris on 11.11.2015.
*/
public interface IModelChangeListener {
public void onModelChanged();
}

View file

@ -26,6 +26,16 @@ public class GameField implements Cloneable {
initCells(null);
}
public void reset() {
actionOnCells(new ICellAction<Boolean>() {
@Override
public Boolean action(GameCell gc, Boolean existing) {
gc.reset();
return true;
}
}, true);
}
public void initCells(int[][] level) {
// TODO: this is a placeholder, because we don't have real levels yet.
int[][] placeholder = {{ 5, 0, 1, 9, 0, 0, 0, 0, 0 },
@ -56,24 +66,28 @@ public class GameField implements Cloneable {
return field;
}
public LinkedList<GameCell> getRow(int row) {
LinkedList<GameCell> result = new LinkedList<GameCell>();
for(GameCell c : field[row]) {
result.add(c);
public LinkedList<GameCell> getRow(final int row) {
return actionOnCells(new ICellAction<LinkedList<GameCell>>() {
@Override
public LinkedList<GameCell> action(GameCell gc, LinkedList<GameCell> existing) {
if(gc.getRow() == row) {
existing.add(gc);
}
return result;
return existing;
}
}, new LinkedList<GameCell>());
}
public LinkedList<GameCell> getColumn(int col) {
LinkedList<GameCell> result = new LinkedList<GameCell>();
for(int i = 0; i < size ; i++) { // row
for(int j = 0 ; j < size ; j++) { // col
if(j == col) {
result.add(field[i][j]);
public LinkedList<GameCell> getColumn(final int col) {
return actionOnCells(new ICellAction<LinkedList<GameCell>>() {
@Override
public LinkedList<GameCell> action(GameCell gc, LinkedList<GameCell> existing) {
if(gc.getCol() == col) {
existing.add(gc);
}
return existing;
}
}
return result;
}, new LinkedList<GameCell>());
}
public LinkedList<GameCell> getSection(final int sec) {

View file

@ -4,9 +4,22 @@ package tu_darmstadt.sudoku.game;
* Created by Chris on 09.11.2015.
*/
public class GameSettings {
private boolean enableAutomaticNoteDeletion = true;
private static boolean enableAutomaticNoteDeletion = true;
private static boolean highlightConnectedRow = true;
private static boolean highlightConnectedColumn = true;
private static boolean highlightConnectedSection = true;
public boolean getEnableAutomaticNoteDeletion() {
public static boolean getEnableAutomaticNoteDeletion() {
return enableAutomaticNoteDeletion;
}
public static boolean getHighlightConnectedRow() {
return highlightConnectedRow;
}
public static boolean getHighlightConnectedColumn() {
return highlightConnectedColumn;
}
public static boolean getHighlightConnectedSection() {
return highlightConnectedSection;
}
}

View file

@ -1,6 +1,7 @@
package tu_darmstadt.sudoku.game.solver;
import java.util.LinkedList;
import java.util.List;
import tu_darmstadt.sudoku.game.CellConflict;
import tu_darmstadt.sudoku.game.GameCell;
@ -14,16 +15,42 @@ public class Default9x9Solver implements ISolver {
private GameField gameField = null;
Default9x9Solver(GameField gameField) {
public Default9x9Solver(GameField gf) {
try {
if(gameField == null) {
if(gf == null) {
throw new IllegalArgumentException("GameField may not be null.");
}
this.gameField = gameField.clone();
gameField = gf.clone();
} catch(CloneNotSupportedException e) {
throw new IllegalArgumentException("This GameField is not cloneable.", e);
}
gameField.reset();
if(!isSolvable(gameField)) {
throw new IllegalArgumentException("This GameField is not solveable.");
}
}
public boolean isSolvable(GameField gameField) {
for(int i = 0; i < gameField.getSize(); i++) {
if(hasErrors(gameField.getRow(i))) return false;
if(hasErrors(gameField.getColumn(i))) return false;
if(hasErrors(gameField.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;
}
checked.add(c.getValue());
}
return false;
}
public boolean solve() {
@ -53,6 +80,7 @@ public class Default9x9Solver implements ISolver {
@Override
public boolean calculateNextPossibleStep() {
return false;
}
@ -102,9 +130,8 @@ public class Default9x9Solver implements ISolver {
return gameField.actionOnCells(new ICellAction<Boolean>() {
@Override
public Boolean action(GameCell gc, Boolean existing) {
boolean oneNote = false;
int value = -1;
if(gc.getNoteCount() == 1) {
if(!gc.hasValue() && gc.getNoteCount() == 1) {
for(int i = 0; i < gameField.getSize(); i++) {
if(gc.getNotes()[i]) {
value = i;

View file

@ -15,6 +15,7 @@ import android.widget.Toast;
import java.util.jar.Attributes;
import tu_darmstadt.sudoku.game.GameCell;
import tu_darmstadt.sudoku.view.highlighting.CellHighlightTypes;
/**
* Created by TMZ_LToP on 10.11.2015.
@ -23,12 +24,13 @@ public class SudokuCellView extends View {
GameCell mGameCell;
int mWidth;
int mHeight;
int mSectionHeight;
int mSectionWidth;
int mRow;
int mCol;
boolean touched;
boolean selected;
CellHighlightTypes highlightType = CellHighlightTypes.Default;
public SudokuCellView(Context context) {
@ -43,51 +45,78 @@ public class SudokuCellView extends View {
this.selected = b;
}
public void setValues (int width, int sectionHeight, int sectionWidth, GameCell gameCell) {
public void setValues (int width, int height, int sectionHeight, int sectionWidth, GameCell gameCell) {
mSectionHeight = sectionHeight;
mSectionWidth = sectionWidth;
mGameCell = gameCell;
mWidth = width;
mHeight = height;
mRow = gameCell.getRow();
mCol = gameCell.getCol();
}
@Override
public void setHighlightType(CellHighlightTypes highlightType) {
this.highlightType = highlightType;
}
/*@Override
public boolean onTouchEvent(MotionEvent motionEvent) {
if(mGameCell == null) return false;
touched = true;
if(motionEvent.getAction() == motionEvent.ACTION_DOWN) {
highlightType = CellHighlightTypes.Selected;
}
return true;
}
}*/
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(mWidth, mWidth);
params.topMargin = mRow*mWidth;
// Set Layout
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(mWidth, mHeight);
params.topMargin = mRow*mHeight;
params.leftMargin = mCol*mWidth;
this.setLayoutParams(params);
if(mGameCell == null) {
return;
// Draw single Field
drawInfo(canvas);
}
drawBackground(canvas);
private void drawInfo(Canvas canvas) {
Paint p = new Paint();
switch(highlightType) {
case Default:
p.setColor(Color.WHITE);
break;
case Error:
p.setColor(Color.RED);
break;
case Selected:
p.setColor(Color.GREEN);
break;
case Connected:
p.setColor(Color.argb(55, 255, 255, 0));
break;
case Highlighted:
p.setColor(Color.YELLOW);
break;
default:
p.setColor(Color.WHITE);
}
drawBackground(canvas, 3, 3, mWidth - 3, mHeight - 3, p);
// if there is no mGameCell .. we can not retrieve the information to draw
if(mGameCell != null) {
drawValue(canvas);
}
public void drawBackground(Canvas canvas) {
Paint p = new Paint();
p.setColor(Color.WHITE);
RectF rect = new RectF(3, 3, mWidth-3, mWidth-3);
canvas.drawRect(rect, p);
if(touched) {
p.setColor(Color.GREEN);
RectF rectTouched = new RectF(3, 3, mWidth-3, mWidth-3);
canvas.drawRect(rectTouched, p);
}
public void drawBackground(Canvas canvas, int left, int top, int right, int bottom, Paint p) {
RectF rect = new RectF(left, top, right, bottom);
canvas.drawRect(rect, p);
}
public void drawValue(Canvas canvas) {
@ -98,18 +127,15 @@ public class SudokuCellView extends View {
p.setTypeface(Typeface.DEFAULT_BOLD);
}
p.setAntiAlias(true);
p.setTextSize(Math.min(mWidth * 3 / 4, mWidth * 3 / 4));
p.setTextSize(Math.min(mHeight * 3 / 4, mHeight * 3 / 4));
p.setTextAlign(Paint.Align.CENTER);
canvas.drawText(String.valueOf(mGameCell.getValue()), mWidth/2, mWidth/2 + mWidth/4, p);
}
public int getColumn() {
return mCol;
canvas.drawText(String.valueOf(mGameCell.getValue()), mHeight/2, mHeight/2 + mHeight/4, p);
}
public int getRow() {
return mRow;
}
public int getCol() {
return mCol;
}
}

View file

@ -5,10 +5,16 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.RelativeLayout;
import java.util.List;
import tu_darmstadt.sudoku.controller.GameController;
import tu_darmstadt.sudoku.game.GameCell;
import tu_darmstadt.sudoku.game.GameSettings;
import tu_darmstadt.sudoku.view.highlighting.CellHighlightTypes;
/**
* Created by Timm Lippert on 11.11.2015.
@ -19,6 +25,7 @@ public class SudokuFieldLayout extends RelativeLayout {
private int sectionHeight;
private int sectionWidth;
private int gameCellWidth;
private int gameCellHeight;
public SudokuCellView [][] gamecells;
AttributeSet attrs;
@ -34,13 +41,34 @@ public class SudokuFieldLayout extends RelativeLayout {
gameController = gc;
gamecells = new SudokuCellView[gc.getSize()][gc.getSize()];
OnClickListener listener = new OnClickListener() {
OnTouchListener listener = new OnTouchListener() {
@Override
public void onClick(View v) {
public boolean onTouch(View v, MotionEvent event) {
if(v instanceof SudokuCellView) {
SudokuCellView view = (SudokuCellView) v;
SudokuCellView scv = ((SudokuCellView) v);
int row = scv.getRow();
int col = scv.getCol();
// Reset everything
for(int i = 0; i < gameController.getSize(); i++) {
for(int j = 0; j < gameController.getSize(); j++) {
gamecells[i][j].setHighlightType(CellHighlightTypes.Default);
}
}
// Set connected Fields
for(GameCell c : gameController.getConnectedCells(row,col, GameSettings.getHighlightConnectedRow(), GameSettings.getHighlightConnectedColumn(), GameSettings.getHighlightConnectedSection())) {
gamecells[c.getRow()][c.getCol()].setHighlightType(CellHighlightTypes.Connected);
}
// Select touched Cell
gameController.selectCell(row, col);
scv.setHighlightType(CellHighlightTypes.Selected);
}
return false;
}
};
sectionHeight = gameController.getSectionHeight();
@ -49,6 +77,7 @@ public class SudokuFieldLayout extends RelativeLayout {
for (int i = 0; i < gameController.getSize(); i++) {
for (int j = 0; j < gameController.getSize(); j++) {
gamecells[i][j] = new SudokuCellView(getContext(), attrs);
gamecells[i][j].setOnTouchListener(listener);
addView(gamecells[i][j]);
}
}
@ -58,19 +87,22 @@ public class SudokuFieldLayout extends RelativeLayout {
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(gameController == null) return;
Paint p = new Paint();
p.setColor(Color.BLACK);
p.setStrokeWidth(2);
// TODO: Draw Borders
//canvas.drawLine(0, 0, 0, getHeight(), p);
for(int i = 0; i <= (gameController.getSize()/sectionWidth); i++) {
for(int j = -2; j < 2; j++) {
canvas.drawLine((i * getWidth() / sectionWidth) + j, 0, (i * getWidth() / sectionWidth) + j, getHeight(), p);
}
}
for(int i = 0; i <= (gameController.getSize()/sectionHeight); i++) {
canvas.drawLine(0, i*getHeight() / sectionHeight, getHeight(), i*getHeight() / sectionHeight, p);
for(int j = -2; j < 2; j++) {
canvas.drawLine(0, (i * getHeight() / sectionHeight) + j, getHeight(), (i * getHeight() / sectionHeight) + j, p);
}
}
}
@ -80,10 +112,11 @@ public class SudokuFieldLayout extends RelativeLayout {
if(changed && gameController != null) {
gameCellWidth = (Math.min(r-l, b-t)) / gameController.getSize();
gameCellHeight = (Math.min(r-l, b-t)) / gameController.getSize();
for (int i = 0; i < gameController.getSize(); i++) {
for (int j = 0; j < gameController.getSize(); j++) {
gamecells[i][j].setValues(gameCellWidth, sectionHeight, sectionWidth, gameController.getGameCell(i, j));
gamecells[i][j].setValues(gameCellWidth, gameCellHeight, sectionHeight, sectionWidth, gameController.getGameCell(i, j));
}
}
}

View file

@ -0,0 +1,12 @@
package tu_darmstadt.sudoku.view.highlighting;
/**
* Created by Chris on 12.11.2015.
*/
public enum CellHighlightTypes {
Default,
Selected,
Error,
Connected,
Highlighted // Same Numbers are not connected but might be highlighted.
}

View file

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
@ -13,5 +14,6 @@
android:id="@+id/sudokuLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:clickable="true"
android:gravity="center"/>
</RelativeLayout>

View file

@ -1,7 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="title_activity_game_view">Sudoku</string>
<!-- Strings related to Menu -->
<string name="new_game">Neues Spiel</string>
<string name="settings">Einstellungen</string>
<string name="highscore">Bestenliste</string>
<string name="mainmenu">Hauptmenü</string>
<string name="group">Gruppe</string>
<string name="help">Hilfe</string>
<string name="about">Über</string>
<!-- Strings related to Settings -->
<string name="title_activity_settings">Einstellungen</string>
<string name="action_settings">Einstellungen</string>
<!-- Strings related to Highlight -->
<string name="pref_highlighting_selection">Auswahl</string>
<string name="pref_highlighting_selection_values">Werte</string>
</resources>

View file

@ -1,9 +1,9 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="fab_margin">16dp</dimen>
<dimen name="activity_horizontal_margin">13dp</dimen>
<dimen name="activity_vertical_margin">13dp</dimen>
<dimen name="fab_margin">13dp</dimen>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="nav_header_vertical_spacing">16dp</dimen>
<dimen name="nav_header_vertical_spacing">13dp</dimen>
<dimen name="nav_header_height">160dp</dimen>
</resources>

View file

@ -1,6 +1,5 @@
<resources>
<string name="app_name">Sudoku</string>
<string name="action_settings">Settings</string>
<string name="title_activity_game_view">Sudoku</string>
<!-- Strings related to Menu -->
@ -14,9 +13,11 @@
<string name="navigation_drawer_open">Open navigation drawer</string>
<string name="navigation_drawer_close">Close navigation drawer</string>
<string name="title_activity_settings">Settings</string>
<!-- Strings related to Settings -->
<string name="title_activity_settings">Settings</string>
<string name="action_settings">Settings</string>
<!-- Strings related to Highlight -->
<string name="pref_highlighting_selection">Selection</string>

View file

@ -0,0 +1,35 @@
package tu_darmstadt.sudoku.game.solver;
import org.junit.Before;
import org.junit.Test;
import tu_darmstadt.sudoku.controller.GameController;
/**
* Created by Chris on 12.11.2015.
*/
public class SolverTest {
GameController controller;
@Before
public void init() {
controller = new GameController();
int[][] level = {{ 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 }};
}
@Test
public void solveTest() {
}
}