Compare commits
2 Commits
1b07a3b6ab
...
9e5e9eb91a
Author | SHA1 | Date | |
---|---|---|---|
9e5e9eb91a | |||
4b73c1b5e3 |
123
uebung06/src/sudoku/Field.java
Normal file
123
uebung06/src/sudoku/Field.java
Normal file
@@ -0,0 +1,123 @@
|
||||
package sudoku;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import static java.util.Collections.emptySet;
|
||||
|
||||
/**
|
||||
* A field of a Sudoku board.
|
||||
*/
|
||||
public class Field {
|
||||
private static final int DEP_LENGTH = 2 * Sudoku.SIZE + 2;
|
||||
public final Sudoku sudoku;
|
||||
public final int x;
|
||||
public final int y;
|
||||
private Value value;
|
||||
private List<Field> dependents;
|
||||
private final Set<Value> domain = EnumSet.allOf(Value.class);
|
||||
|
||||
/**
|
||||
* Creates a field of the specified Sudoku board at the specified coordinates.
|
||||
*
|
||||
* @param sudoku the Sudoku board
|
||||
* @param x x coordinate (0 <= x < 9)
|
||||
* @param y y coordinate (0 <= y < 9)
|
||||
*/
|
||||
public Field(Sudoku sudoku, int x, int y) {
|
||||
if (x < 0 || x >= Sudoku.SIZE || y < 0 || y >= Sudoku.SIZE)
|
||||
throw new IllegalArgumentException("Illegal coordinates " + x + ", " + y);
|
||||
this.sudoku = sudoku;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value of this field, or null if it is empty.
|
||||
*
|
||||
* @return the value of this field, or null if it is empty.
|
||||
*/
|
||||
public Value getValue() {
|
||||
return value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the value of this field
|
||||
*
|
||||
* @param v the value, or null if this field shall be empty.
|
||||
*/
|
||||
public void setValue(Value v) {
|
||||
value = v;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the value of this field so that it is empty.
|
||||
*/
|
||||
public void clearValue() {
|
||||
setValue(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks whether this field is empty.
|
||||
*
|
||||
* @return true iff this field is empty.
|
||||
*/
|
||||
public boolean isEmpty() {
|
||||
return value == null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this field. As a matter of fact,
|
||||
* its value is returned, or "." if it is empty.
|
||||
*
|
||||
* @return a string representation of this field.
|
||||
*/
|
||||
public String toString() {
|
||||
return isEmpty() ? "." : value.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the set of all values possible for this field that do not violate the Sudoku constraints,
|
||||
* if the field is empty. Otherwise, it returns the empty set. The returned set may be modified if
|
||||
* the field is empty.
|
||||
*
|
||||
* @return the domain of this field.
|
||||
*/
|
||||
public Set<Value> getDomain() {
|
||||
return isEmpty() ? domain : emptySet();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the list of all other fields of this Sudoku field whose values are constrained by the
|
||||
* value of this field.
|
||||
*
|
||||
* @return the list of all dependent fields.
|
||||
*/
|
||||
List<Field> getDependents() {
|
||||
if (dependents == null) {
|
||||
dependents = new ArrayList<>(DEP_LENGTH);
|
||||
|
||||
// fill the list with all fields in the given row and column
|
||||
for (int i = 0; i < Sudoku.SIZE; i++) {
|
||||
if (i != x)
|
||||
dependents.add(sudoku.field(i, y));
|
||||
if (i != y)
|
||||
dependents.add(sudoku.field(x, i));
|
||||
}
|
||||
|
||||
// determine the coordinates of the upper left corner of this field's block
|
||||
final int x0 = 3 * (x / 3);
|
||||
final int y0 = 3 * (y / 3);
|
||||
|
||||
// fill the list with the whole block
|
||||
for (int x1 = x0; x1 < x0 + 3; x1++)
|
||||
if (x1 != x)
|
||||
for (int y1 = y0; y1 < y0 + 3; y1++)
|
||||
if (y1 != y)
|
||||
dependents.add(sudoku.field(x1, y1));
|
||||
}
|
||||
return dependents;
|
||||
}
|
||||
}
|
206
uebung06/src/sudoku/Sudoku.java
Normal file
206
uebung06/src/sudoku/Sudoku.java
Normal file
@@ -0,0 +1,206 @@
|
||||
package sudoku;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* A Sudoku board as a matrix of {@linkplain sudoku.Field} instances.
|
||||
* <p>
|
||||
* Call {@linkplain #solve()} to solve it.
|
||||
*/
|
||||
public class Sudoku {
|
||||
/**
|
||||
* Edge size of the Sudoku board.
|
||||
*/
|
||||
public static final int SIZE = 9;
|
||||
private static final String LS = System.lineSeparator();
|
||||
private final Field[] board = new Field[SIZE * SIZE];
|
||||
|
||||
/**
|
||||
* Creates a Sudoku board with all of its fields being empty.
|
||||
*/
|
||||
public Sudoku() {
|
||||
for (int x = 0; x < SIZE; x++)
|
||||
for (int y = 0; y < SIZE; y++)
|
||||
board[SIZE * y + x] = new Field(this, x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the field at the specified coordinates
|
||||
*
|
||||
* @param x x coordinate (0 <= x < 9)
|
||||
* @param y y coordinate (0 <= x < 9)
|
||||
* @return the field at the specified coordinates
|
||||
*/
|
||||
public Field field(int x, int y) {
|
||||
return board[SIZE * y + x];
|
||||
}
|
||||
|
||||
/**
|
||||
* Bulk operation to set values of the fields.
|
||||
*
|
||||
* @param values a sequence of integers where 0 means empty
|
||||
*/
|
||||
public void initialize(int... values) {
|
||||
if (values.length != board.length)
|
||||
throw new IllegalArgumentException("Wrong Sudoku board with " + values.length + " values");
|
||||
for (int i = 0; i < values.length; i++)
|
||||
if (values[i] != 0)
|
||||
setValue(board[i], Value.of(values[i]));
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the specified value of the specified empty field
|
||||
*
|
||||
* @param field the empty field whose value is set
|
||||
* @param value the value of the field
|
||||
* @throws IllegalStateException if the field is not empty
|
||||
* @throws IllegalArgumentException if setting this value would violate the Sudoku constraints
|
||||
* @throws NullPointerException if the value is null
|
||||
*/
|
||||
public void setValue(Field field, Value value) {
|
||||
if (value == null)
|
||||
throw new NullPointerException("value is null");
|
||||
if (!field.isEmpty())
|
||||
throw new IllegalStateException("Value already set: " + field);
|
||||
if (!field.getDomain().contains(value))
|
||||
throw new IllegalArgumentException(value + " is not permitted");
|
||||
for (Field dependent : field.getDependents())
|
||||
dependent.getDomain().remove(value);
|
||||
field.setValue(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this board.
|
||||
*
|
||||
* @return a string representation of this board
|
||||
*/
|
||||
public String toString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
for (int y = 0; y < SIZE; y++) {
|
||||
if (y == 3 || y == 6) sb.append("---------+---------+---------").append(LS);
|
||||
for (int x = 0; x < SIZE; x++) {
|
||||
if (x == 3 || x == 6) sb.append("|");
|
||||
sb.append(" ").append(field(x, y)).append(" ");
|
||||
}
|
||||
sb.append(LS);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Looks for an empty field with minimal domain.
|
||||
*
|
||||
* @return a field with minimal domain, or null no field is empty
|
||||
*/
|
||||
private Field findBestCandidate() {
|
||||
Field best = null;
|
||||
int min = Integer.MAX_VALUE;
|
||||
for (Field f : board)
|
||||
if (f.isEmpty() && f.getDomain().size() < min) {
|
||||
best = f;
|
||||
min = best.getDomain().size();
|
||||
}
|
||||
return best;
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to solve the current board. The board is in the solved state if a solution is found,
|
||||
* and unmodified if no solution is found.
|
||||
*
|
||||
* @return true iff a solution has been found.
|
||||
*/
|
||||
public boolean solve() {
|
||||
final Field field = findBestCandidate();
|
||||
if (field == null)
|
||||
// A solution has been found
|
||||
return true;
|
||||
|
||||
// There are empty fields. Try all values of its domain
|
||||
final List<Field> revoke = new ArrayList<>();
|
||||
for (Value v : field.getDomain()) {
|
||||
field.setValue(v);
|
||||
boolean consistent = true;
|
||||
for (Field dependent : field.getDependents())
|
||||
if (dependent.getDomain().contains(v)) {
|
||||
if (dependent.getDomain().size() == 1) {
|
||||
// This is a dead end because field 'dependent'
|
||||
// would have an empty domain
|
||||
consistent = false;
|
||||
break;
|
||||
}
|
||||
revoke.add(dependent);
|
||||
dependent.getDomain().remove(v);
|
||||
}
|
||||
if (consistent && solve())
|
||||
return true;
|
||||
|
||||
// Choosing v as a value lead to a dead end.
|
||||
// Revoke all modifications
|
||||
field.clearValue();
|
||||
for (Field f : revoke)
|
||||
f.getDomain().add(v);
|
||||
revoke.clear();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public List<Sudoku> solveAll() {
|
||||
List<Sudoku> solutions = new ArrayList<>();
|
||||
solveAll(solutions);
|
||||
return solutions;
|
||||
}
|
||||
|
||||
private void solveAll(List<Sudoku> solutions) {
|
||||
final Field field = findBestCandidate();
|
||||
if (field == null) {
|
||||
// A solution has been found
|
||||
Sudoku copy = new Sudoku();
|
||||
copy.initialize(this.export());
|
||||
solutions.add(copy);
|
||||
return;
|
||||
}
|
||||
|
||||
// There are empty fields. Try all values of its domain
|
||||
final List<Field> revoke = new ArrayList<>();
|
||||
for (Value v : field.getDomain()) {
|
||||
field.setValue(v);
|
||||
boolean consistent = true;
|
||||
for (Field dependent : field.getDependents())
|
||||
if (dependent.getDomain().contains(v)) {
|
||||
if (dependent.getDomain().size() == 1) {
|
||||
// This is a dead end because field 'dependent'
|
||||
// would have an empty domain
|
||||
consistent = false;
|
||||
break;
|
||||
}
|
||||
revoke.add(dependent);
|
||||
dependent.getDomain().remove(v);
|
||||
}
|
||||
|
||||
if (consistent) solveAll(solutions);
|
||||
|
||||
// Revoke all modifications
|
||||
field.clearValue();
|
||||
for (Field f : revoke)
|
||||
f.getDomain().add(v);
|
||||
revoke.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private int[] export() {
|
||||
int[] values = new int[SIZE * SIZE];
|
||||
int idx = 0;
|
||||
while (idx < values.length) {
|
||||
Field f = board[idx];
|
||||
if (f != null) {
|
||||
Value value = f.getValue();
|
||||
if (value != null) values[idx] = value.getId();
|
||||
else values[idx] = 0;
|
||||
}
|
||||
else throw new IllegalStateException("at least one field is not set");
|
||||
++idx;
|
||||
}
|
||||
return values;
|
||||
}
|
||||
}
|
68
uebung06/src/sudoku/SudokuApp.java
Normal file
68
uebung06/src/sudoku/SudokuApp.java
Normal file
@@ -0,0 +1,68 @@
|
||||
package sudoku;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class SudokuApp {
|
||||
|
||||
public static void main(String[] args) {
|
||||
System.out.println("Sudoku Aufgabenblatt:");
|
||||
System.out.println();
|
||||
final Sudoku boardBlatt = new Sudoku();
|
||||
boardBlatt.initialize(4, 5, 0, 0, 0, 0, 2, 0, 0,
|
||||
6, 0, 0, 0, 2, 4, 8, 0, 0,
|
||||
8, 0, 0, 0, 6, 1, 3, 0, 0,
|
||||
0, 9, 0, 4, 0, 0, 0, 5, 0,
|
||||
0, 1, 0, 2, 0, 8, 0, 7, 0,
|
||||
0, 3, 0, 0, 0, 9, 0, 8, 0,
|
||||
0, 0, 7, 1, 4, 0, 0, 0, 8,
|
||||
0, 0, 2, 7, 9, 0, 0, 0, 6,
|
||||
0, 0, 5, 0, 0, 0, 0, 2, 1);
|
||||
printSolution(boardBlatt);
|
||||
|
||||
System.out.println("Sudoku Handzettel:");
|
||||
System.out.println();
|
||||
final Sudoku boardHandzettel = new Sudoku();
|
||||
boardHandzettel.initialize(7, 0, 0, 0, 2, 5, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 9, 1,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
4, 0, 9, 1, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 6, 0, 0, 2, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 3, 0,
|
||||
8, 2, 0, 0, 0, 0, 5, 0, 0,
|
||||
0, 0, 0, 0, 3, 0, 6, 0, 0,
|
||||
0, 0, 0, 9, 0, 0, 0, 0, 0);
|
||||
printSolution(boardHandzettel);
|
||||
|
||||
System.out.println("Sudoku mit 2 Lösungen:");
|
||||
System.out.println();
|
||||
final Sudoku boardWithTwoSolutions = new Sudoku();
|
||||
boardWithTwoSolutions.initialize(2, 9, 5, 7, 4, 3, 8, 6, 1,
|
||||
4, 3, 1, 8, 6, 5, 9, 0, 0,
|
||||
8, 7, 6, 1, 9, 2, 5, 4, 3,
|
||||
3, 8, 7, 4, 5, 9, 2, 1, 6,
|
||||
6, 1, 2, 3, 8, 7, 4, 9, 5,
|
||||
5, 4, 9, 2, 1, 6, 7, 3, 8,
|
||||
7, 6, 3, 5, 2, 4, 1, 8, 9,
|
||||
9, 2, 8, 6, 7, 1, 3, 5, 4,
|
||||
1, 5, 4, 9, 3, 8, 6, 0, 0);
|
||||
System.out.println(boardWithTwoSolutions);
|
||||
System.out.println("Solutions:");
|
||||
System.out.println();
|
||||
|
||||
List<Sudoku> solutions = boardWithTwoSolutions.solveAll();
|
||||
System.out.println(String.join(System.lineSeparator(), solutions.stream().map(Sudoku::toString).toList()));
|
||||
}
|
||||
|
||||
private static void printSolution(Sudoku sudoku) {
|
||||
System.out.println(sudoku);
|
||||
|
||||
System.out.println();
|
||||
if (sudoku.solve()) {
|
||||
System.out.println("Solution:");
|
||||
System.out.println();
|
||||
System.out.println(sudoku);
|
||||
}
|
||||
else
|
||||
System.out.println("No solution!");
|
||||
}
|
||||
}
|
41
uebung06/src/sudoku/Value.java
Normal file
41
uebung06/src/sudoku/Value.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package sudoku;
|
||||
|
||||
/**
|
||||
* The enumeration type of all possible values of a Sudoku field.
|
||||
*/
|
||||
public enum Value {
|
||||
ONE(1), TWO(2), THREE(3), FOUR(4), FIVE(5), SIX(6), SEVEN(7), EIGHT(8), NINE(9);
|
||||
|
||||
private final int id;
|
||||
|
||||
Value(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string representation of this value. As a matter of fact, its id is returned.
|
||||
*
|
||||
* @return a string representation of this value.
|
||||
*/
|
||||
@Override
|
||||
public String toString() {
|
||||
return String.valueOf(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the value with the specified id.
|
||||
*
|
||||
* @param id an id (1 <= id <= 9)
|
||||
* @return the value with the specified id.
|
||||
*/
|
||||
public static Value of(int id) {
|
||||
return values()[id - 1];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the id of this instance.
|
||||
*
|
||||
* @return the id of this instance.
|
||||
*/
|
||||
public int getId() {return id;}
|
||||
}
|
65
uebung06/test/sudoku/FieldTest.java
Normal file
65
uebung06/test/sudoku/FieldTest.java
Normal file
@@ -0,0 +1,65 @@
|
||||
package uebung06.test.sudoku;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import sudoku.*;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class FieldTest {
|
||||
private static final int X = 8;
|
||||
private static final int Y = 5;
|
||||
private static final int NUL = -1;
|
||||
private static final Value VAL = Value.EIGHT;
|
||||
private static Sudoku sudoku;
|
||||
private static Field field;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
sudoku = new Sudoku();
|
||||
field = new Field(sudoku, X, Y);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void illegalXLowerBoundFieldTest() {
|
||||
field = new Field(sudoku, NUL, Y);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void illegalXUpperBoundFieldTest() {
|
||||
field = new Field(sudoku, Sudoku.SIZE, Y);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void illegalYLowerBoundFieldTest() {
|
||||
field = new Field(sudoku, X, NUL);
|
||||
}
|
||||
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void illegalYUpperBoundFieldTest() {
|
||||
field = new Field(sudoku, X, Sudoku.SIZE);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void valueFieldTest() {
|
||||
field.setValue(VAL);
|
||||
assertEquals(field.getValue(), VAL);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void clearValueFieldTest() {
|
||||
field.setValue(VAL);
|
||||
field.clearValue();
|
||||
assertNull(field.getValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void isValueEmptyFieldTest() {
|
||||
field.setValue(VAL);
|
||||
field.clearValue();
|
||||
assertTrue(field.isEmpty());
|
||||
}
|
||||
}
|
129
uebung06/test/sudoku/SudokuAppTest.java
Normal file
129
uebung06/test/sudoku/SudokuAppTest.java
Normal file
@@ -0,0 +1,129 @@
|
||||
package uebung06.test.sudoku;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
import sudoku.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.stream.IntStream;
|
||||
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
public class SudokuAppTest {
|
||||
private static final int MAX = 81;
|
||||
private static final int MIN = 0;
|
||||
private static final int SUDOKU_SIZE = 9;
|
||||
private static Sudoku board;
|
||||
private static int[] init;
|
||||
private static int[] wrongSol;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
board = new Sudoku();
|
||||
init = IntStream.of(4, 5, 0, 0, 0, 0, 2, 0, 0,
|
||||
6, 0, 0, 0, 2, 4, 8, 0, 0,
|
||||
8, 0, 0, 0, 6, 1, 3, 0, 0,
|
||||
0, 9, 0, 4, 0, 0, 0, 5, 0,
|
||||
0, 1, 0, 2, 0, 8, 0, 7, 0,
|
||||
0, 3, 0, 0, 0, 9, 0, 8, 0,
|
||||
0, 0, 7, 1, 4, 0, 0, 0, 8,
|
||||
0, 0, 2, 7, 9, 0, 0, 0, 6,
|
||||
0, 0, 5, 0, 0, 0, 0, 2, 1).toArray();
|
||||
wrongSol = IntStream.of(4, 5, 1, 8, 7, 3, 2, 6, 9,
|
||||
6, 7, 3, 9, 2, 4, 8, 1, 5,
|
||||
8, 2, 9, 5, 6, 1, 3, 4, 7,
|
||||
2, 9, 8, 4, 1, 7, 6, 5, 3,
|
||||
5, 1, 6, 2, 3, 8, 9, 7, 4,
|
||||
7, 3, 4, 6, 5, 9, 1, 8, 2,
|
||||
3, 6, 7, 1, 4, 2, 5, 9, 8,
|
||||
1, 8, 2, 7, 9, 5, 4, 3, 6,
|
||||
9, 5, 4, 3, 8, 6, 7, 2, 1).toArray();
|
||||
}
|
||||
|
||||
//Testing first If-condition in Sudoku.init
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void illegalLowerBoundInitSudokuAppTest() {
|
||||
board.initialize(IntStream.range(MIN, MIN+1).toArray());
|
||||
}
|
||||
|
||||
//Testing first If-condition in Sudoku.init
|
||||
@Test(expected = IllegalArgumentException.class)
|
||||
public void illegalUpperBoundInitSudokuAppTest() {
|
||||
board.initialize(IntStream.range(MIN, MAX+1).toArray());
|
||||
}
|
||||
|
||||
//Testing ArrayIndex of Value
|
||||
@Test(expected = ArrayIndexOutOfBoundsException.class)
|
||||
public void illegalArrayIndexOutOfBoundInitSudokuAppTest() {
|
||||
board.initialize(IntStream.range(MIN, MAX).toArray());
|
||||
}
|
||||
|
||||
//Testing Sudoku.solve()
|
||||
@Test
|
||||
public void correctSolutionSudokuAppTest() {
|
||||
board.initialize(init);
|
||||
board.solve();
|
||||
|
||||
assertTrue(verifySudokuSol());
|
||||
}
|
||||
|
||||
//Just testing verifySudokuSol-Method
|
||||
@Test
|
||||
public void wrongSolutionSudokuAppTest() {
|
||||
try {
|
||||
board.initialize(wrongSol);
|
||||
} catch (final IllegalArgumentException e) {
|
||||
//do nothing, just handling throwing exceptions from Sudoku-class
|
||||
}
|
||||
|
||||
assertFalse(verifySudokuSol());
|
||||
}
|
||||
|
||||
public Boolean verifySudokuSol() {
|
||||
HashSet<Value> rows = new HashSet<>();
|
||||
HashSet<Value> cols = new HashSet<>();
|
||||
|
||||
HashMap<Integer, HashSet<Value>> blocks = new HashMap<>();
|
||||
IntStream.range(0, SUDOKU_SIZE)
|
||||
.forEach(block -> blocks.put(block, new HashSet<>())); //init Hashsets for every block
|
||||
|
||||
for (int y = 0; y < SUDOKU_SIZE; y++) {
|
||||
for (int x = 0; x < SUDOKU_SIZE; x++) {
|
||||
//check column
|
||||
Value val = board.field(x, y).getValue();
|
||||
if (!cols.add(val))
|
||||
return false;
|
||||
if (cols.size() == SUDOKU_SIZE)
|
||||
cols.clear();
|
||||
|
||||
//check row
|
||||
val = board.field(y, x).getValue();
|
||||
if (!rows.add(val))
|
||||
return false;
|
||||
if (rows.size() == SUDOKU_SIZE)
|
||||
rows.clear();
|
||||
|
||||
//check 3x3 block
|
||||
val = board.field(x, y).getValue();
|
||||
if (!blocks.get(getBlockIndex(x, y)).add(val))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getBlockIndex(int x, int y) {
|
||||
return getColIndex(x) + getRowIndex(y);
|
||||
}
|
||||
|
||||
public int getColIndex(int x) {
|
||||
return x / 3;
|
||||
}
|
||||
|
||||
public int getRowIndex(int y) {
|
||||
return (y/3)*3;
|
||||
}
|
||||
}
|
@@ -5,30 +5,30 @@ import oop.ch05.secured.SecuredContent;
|
||||
import oop.ch05.secured.SecurityClient;
|
||||
|
||||
public class AccountCard extends MensaCard {
|
||||
private final SecuredContent<String> account;
|
||||
private final SecuredContent<String> account;
|
||||
|
||||
public AccountCard(String key, String account, int password) {
|
||||
super(key, Color.white);
|
||||
this.account = new SecuredContent<>(password, account);
|
||||
}
|
||||
public AccountCard(String key, String account, int password) {
|
||||
super(key, Color.white);
|
||||
this.account = new SecuredContent<>(password, account);
|
||||
}
|
||||
|
||||
public String getAccount() {
|
||||
return account.getContent();
|
||||
}
|
||||
public String getAccount() {
|
||||
return account.getContent();
|
||||
}
|
||||
|
||||
public void setAccount(SecurityClient client, String account) throws AuthorizationException {
|
||||
if (account == null || account.trim().length() == 0)
|
||||
throw new IllegalArgumentException("Invalid account " + account);
|
||||
this.account.setContent(client, account);
|
||||
}
|
||||
public void setAccount(SecurityClient client, String account) throws AuthorizationException {
|
||||
if (account == null || account.trim().isEmpty())
|
||||
throw new IllegalArgumentException("Invalid account " + account);
|
||||
this.account.setContent(client, account);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " for account " + account.getContent();
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return super.toString() + " for account " + account.getContent();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void pass(CashPoint cashPoint) {
|
||||
cashPoint.charge(this);
|
||||
}
|
||||
@Override
|
||||
public void pass(CashPoint cashPoint) throws RejectedException, AuthorizationException {
|
||||
cashPoint.charge(this);
|
||||
}
|
||||
}
|
||||
|
51
uebung07/src/oop/ch05/mensa/AccountManagement.java
Normal file
51
uebung07/src/oop/ch05/mensa/AccountManagement.java
Normal file
@@ -0,0 +1,51 @@
|
||||
package oop.ch05.mensa;
|
||||
|
||||
import oop.ch05.secured.AuthorizationException;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.TreeMap;
|
||||
|
||||
public class AccountManagement {
|
||||
private final int password;
|
||||
public final String name;
|
||||
private final Random random = new Random();
|
||||
private final Map<String, Integer> accounts = new TreeMap<>();
|
||||
|
||||
public AccountManagement(int password, String name) {
|
||||
this.password = password;
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public void deposit(String account, int euros) {
|
||||
final int amount = accounts.getOrDefault(account, 0);
|
||||
accounts.put(account, amount + 100 * euros);
|
||||
}
|
||||
|
||||
public void pay(AccountCard card, int price, CashPoint cashPoint) throws RejectedException, AuthorizationException {
|
||||
final int amount = accounts.getOrDefault(card.getAccount(), 0);
|
||||
final int challenge = nextChallenge();
|
||||
if (cashPoint.challengeResponse(challenge) != requiredResponse(challenge))
|
||||
throw new AuthorizationException(cashPoint + " is not authorized to access accounts on " + name);
|
||||
if (amount < price)
|
||||
throw new RejectedException(card + " bounced");
|
||||
accounts.put(card.getAccount(), amount - price);
|
||||
}
|
||||
|
||||
public int getAmount(String account) {
|
||||
return accounts.getOrDefault(account, 0);
|
||||
}
|
||||
|
||||
private int nextChallenge() {
|
||||
return random.nextInt();
|
||||
}
|
||||
|
||||
private int requiredResponse(int challenge) {
|
||||
return challenge ^ password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Account Management " + name + " " + accounts;
|
||||
}
|
||||
}
|
@@ -4,67 +4,62 @@ import oop.ch05.secured.AuthorizationException;
|
||||
import oop.ch05.secured.SecurityClient;
|
||||
|
||||
public class CashPoint implements SecurityClient {
|
||||
private final int password;
|
||||
public final String name;
|
||||
private int counter;
|
||||
private int cents;
|
||||
private final int password;
|
||||
public final String name;
|
||||
private final AccountManagement accountMgmt;
|
||||
private int counter;
|
||||
private int cents;
|
||||
|
||||
public CashPoint(String name, int password) {
|
||||
this.name = name;
|
||||
this.password = password;
|
||||
this.counter = 0;
|
||||
this.cents = 0;
|
||||
}
|
||||
public CashPoint(String name, int password, AccountManagement accountMgmt) {
|
||||
this.name = name;
|
||||
this.password = password;
|
||||
this.accountMgmt = accountMgmt;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int challengeResponse(int challenge) {
|
||||
return challenge ^ password;
|
||||
}
|
||||
public int challengeResponse(int challenge) {
|
||||
return challenge ^ password;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Cash point " + name + " (" + getCounter() + " meals, " + getCents() + " cents charged)";
|
||||
}
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Cash point " + name + " (" + getCounter() + " meals, " + getCents() + " cents charged)";
|
||||
}
|
||||
|
||||
public int getCounter() {
|
||||
return counter;
|
||||
}
|
||||
public int getCounter() {
|
||||
return counter;
|
||||
}
|
||||
|
||||
public int getCents() {
|
||||
return cents;
|
||||
}
|
||||
public int getCents() {
|
||||
return cents;
|
||||
}
|
||||
|
||||
private int getPrice(Color color) {
|
||||
return switch (color) {
|
||||
case green -> 267;
|
||||
case blue -> 357;
|
||||
case white -> 495;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
private int getPrice(Color color) {
|
||||
return switch (color) {
|
||||
case green -> 267;
|
||||
case blue -> 357;
|
||||
case white -> 495;
|
||||
default -> 0;
|
||||
};
|
||||
}
|
||||
|
||||
void count(MensaCard card) {
|
||||
counter++;
|
||||
}
|
||||
void count(MensaCard card) {
|
||||
counter++;
|
||||
}
|
||||
|
||||
void charge(CashCard cashCard) throws AuthorizationException, RejectedException {
|
||||
final int price = getPrice(cashCard.color);
|
||||
if (cashCard.getBalance() < price)
|
||||
throw new RejectedException("insufficient payment");
|
||||
cashCard.charge(this, price);
|
||||
count(cashCard);
|
||||
cents += price;
|
||||
}
|
||||
void charge(CashCard cashCard) throws AuthorizationException, RejectedException {
|
||||
final int price = getPrice(cashCard.color);
|
||||
if (cashCard.getBalance() < price)
|
||||
throw new RejectedException("insufficient payment");
|
||||
cashCard.charge(this, price);
|
||||
count(cashCard);
|
||||
cents += price;
|
||||
}
|
||||
|
||||
void charge(AccountCard accountCard) {
|
||||
final int price = getPrice(accountCard.color);
|
||||
// zahlen methode
|
||||
/*
|
||||
|
||||
zahlen bein acmgmt
|
||||
|
||||
*/
|
||||
System.out.println("Charging " + price + " cents on account " + accountCard.getAccount());
|
||||
cents += price;
|
||||
}
|
||||
void charge(AccountCard accountCard) throws RejectedException, AuthorizationException {
|
||||
final int price = getPrice(accountCard.color);
|
||||
System.out.println("Charging " + price + " cents on account " + accountCard.getAccount());
|
||||
accountMgmt.pay(accountCard, price, this);
|
||||
count(accountCard);
|
||||
cents += price;
|
||||
}
|
||||
}
|
||||
|
@@ -4,10 +4,11 @@ import oop.ch05.secured.AuthorizationException;
|
||||
|
||||
public class MensaExample {
|
||||
public static void main(String[] args) {
|
||||
AccountManagement mgmt = new AccountManagement(4711, "Uni");
|
||||
VendingMachine vm1 = new VendingMachine("left", 4711);
|
||||
VendingMachine vm2 = new VendingMachine("right", 4711);
|
||||
VendingMachine tumVM = new VendingMachine("TUM Mensa", 3141);
|
||||
CashPoint unibwMensa = new CashPoint("UniBw Mensa", 4711);
|
||||
CashPoint unibwMensa = new CashPoint("UniBw Mensa", 4711, mgmt);
|
||||
|
||||
AccountCard conf = new AccountCard("conference", "33-1298", 42);
|
||||
MensaCard frankSmith = new CountCard("Frank Smith", Color.gray);
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package oop.ch05.generic.mensa;
|
||||
|
||||
import oop.ch05.generic.secured.AuthorizationException;
|
||||
import oop.ch05.secured.AuthorizationException;
|
||||
import oop.ch05.mensa.*;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
||||
|
Reference in New Issue
Block a user