Skip to content

Commit

Permalink
Code demonstrated yesterday
Browse files Browse the repository at this point in the history
  • Loading branch information
kohsuke committed Aug 27, 2021
1 parent 8c360cc commit f5fa811
Show file tree
Hide file tree
Showing 9 changed files with 230 additions and 22 deletions.
4 changes: 2 additions & 2 deletions pom.xml
Expand Up @@ -76,8 +76,8 @@
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
<source>10</source>
<target>10</target>
</configuration>
</plugin>
</plugins>
Expand Down
5 changes: 1 addition & 4 deletions src/main/java/com/creationline/sudoku/validator/App.java
Expand Up @@ -2,7 +2,6 @@

import java.io.IOException;
import java.io.InputStreamReader;
import java.util.Objects;

/**
* Hello world!
Expand All @@ -13,9 +12,7 @@ public static void main(String[] args) throws IOException {
try (var r = new InputStreamReader(System.in)) {
Board b = Board.read(r);

b.listConstraintGroup()
.map(Group::findInconsistency)
.filter(Objects::nonNull)
b.findInconsistencies()
.forEach(Inconsistency::report);
}
}
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/com/creationline/sudoku/validator/Block3x3Group.java
@@ -1,6 +1,7 @@
package com.creationline.sudoku.validator;

import java.util.ArrayList;
import java.util.Objects;
import java.util.stream.IntStream;

/**
Expand All @@ -12,6 +13,7 @@ class Block3x3Group extends Group {

public Block3x3Group(Board board, int x, int y) {
super(board);
assert x%3==0 && y%3==0;
this.x = x;
this.y = y;
}
Expand All @@ -28,4 +30,23 @@ public IntStream cells() {
}
return cells.stream().mapToInt(x -> x);
}

@Override
public String toString() {
return String.format("%s@%dx%d", getClass().getSimpleName(), x, y);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Block3x3Group that = (Block3x3Group) o;
return x == that.x &&
y == that.y;
}

@Override
public int hashCode() {
return Objects.hash(x, y);
}
}
56 changes: 53 additions & 3 deletions src/main/java/com/creationline/sudoku/validator/Board.java
Expand Up @@ -4,6 +4,9 @@
import java.io.IOException;
import java.io.Reader;
import java.util.Arrays;
import java.util.Objects;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.stream.Stream;

import static java.util.stream.IntStream.*;
Expand Down Expand Up @@ -32,23 +35,51 @@ public boolean isEmpty(int x, int y) {
}

private void rangeRange(int i) {
if (i<0 || i>=9)
if (i<0 || i>9)
throw new IllegalArgumentException("Value out of range: "+i);
}

public Stream<Group> listConstraintGroup() {
// TODO: range(0,9) x3 is redundant
return concat(
range(0,9).mapToObj(i -> new VerticalGroup(this,i)),
range(0,9).mapToObj(i -> new HorizontalGroup(this,i)),
range(0,9).mapToObj(i -> new Block3x3Group(this,i/3,i%3)));
range(0,9).mapToObj(i -> new VerticalGroup(this,i)),
range(0,9).mapToObj(i -> new Block3x3Group(this,(i/3)*3,(i%3)*3)));
}

@SafeVarargs
private static <T> Stream<T> concat(Stream<T>... items) {
return Arrays.stream(items).reduce(Stream.empty(), Stream::concat);
}

public Stream<Inconsistency> findInconsistencies() {
return listConstraintGroup()
.map(Group::findInconsistency)
.filter(Objects::nonNull);
}

public static Board read(String[] lines) {
if (lines.length!=9)
throw new IllegalArgumentException();

var board = new Board();
for (int y=0; y<9; y++) {
var line = lines[y].replace(" ",""); // allow input to have whitespaces
for (int x=0; x<9; x++) {
char digit = line.charAt(x);
if ('1'<=digit && digit<='9') {
board.set(x,y,digit-'0'); // a digit is present
} else
if (digit=='.') {
// empty cell, pass through
} else {
throw new IllegalArgumentException("Unexpected character: '"+digit+"'");
}
}
}
return board;
}

public static Board read(Reader r) throws IOException {
var board = new Board();
try (var br = new BufferedReader(r)) {
Expand All @@ -67,6 +98,25 @@ public static Board read(Reader r) throws IOException {
return board;
}

/**
* Swap X & Y axis
*/
public Board flipDiagonal() {
var that = new Board();
walk((x, y) -> {
that.set(y, x, this.get(x, y));
});
return that;
}

public void walk(BiConsumer<Integer,Integer> walker) {
for (int y=0; y<9; y++) {
for (int x=0; x<9; x++) {
walker.accept(x,y);
}
}
}

/**
* Constant representing an empty cell.
*/
Expand Down
3 changes: 3 additions & 0 deletions src/main/java/com/creationline/sudoku/validator/Group.java
Expand Up @@ -34,4 +34,7 @@ public final Inconsistency findInconsistency() {
}
return null;
}

// Make sure subtypes override this method
public abstract String toString();
}
@@ -1,20 +1,39 @@
package com.creationline.sudoku.validator;

import java.util.Objects;
import java.util.stream.IntStream;

/**
* @author Kohsuke Kawaguchi
*/
class HorizontalGroup extends Group {
final int x;
final int y;

public HorizontalGroup(Board board, int x) {
public HorizontalGroup(Board board, int y) {
super(board);
this.x = x;
this.y = y;
}

@Override
public IntStream cells() {
return IntStream.range(0,9).map(y -> board.get(x, y));
return IntStream.range(0,9).map(x -> board.get(x,y));
}

@Override
public String toString() {
return getClass().getSimpleName()+"@"+y;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
HorizontalGroup that = (HorizontalGroup) o;
return y == that.y;
}

@Override
public int hashCode() {
return Objects.hash(y);
}
}
21 changes: 21 additions & 0 deletions src/main/java/com/creationline/sudoku/validator/Inconsistency.java
@@ -1,5 +1,7 @@
package com.creationline.sudoku.validator;

import java.util.Objects;

/**
* Represents an inconsistency / constraint violation in Sudoku cells
*
Expand All @@ -18,4 +20,23 @@ public void report() {
// TODO
System.err.printf("%s is inconsistent%n", "");
}

@Override
public String toString() {
return String.format("Inconsistency[%s,%d]",group,digit);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Inconsistency that = (Inconsistency) o;
return digit == that.digit &&
group.equals(that.group);
}

@Override
public int hashCode() {
return Objects.hash(group, digit);
}
}
27 changes: 23 additions & 4 deletions src/main/java/com/creationline/sudoku/validator/VerticalGroup.java
@@ -1,20 +1,39 @@
package com.creationline.sudoku.validator;

import java.util.Objects;
import java.util.stream.IntStream;

/**
* @author Kohsuke Kawaguchi
*/
class VerticalGroup extends Group {
final int y;
final int x;

public VerticalGroup(Board board, int y) {
public VerticalGroup(Board board, int x) {
super(board);
this.y = y;
this.x = x;
}

@Override
public IntStream cells() {
return IntStream.range(0,9).map(x -> board.get(x,y));
return IntStream.range(0,9).map(y -> board.get(x, y));
}

@Override
public String toString() {
return getClass().getSimpleName()+"@"+x;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
VerticalGroup that = (VerticalGroup) o;
return x == that.x;
}

@Override
public int hashCode() {
return Objects.hash(x);
}
}
88 changes: 83 additions & 5 deletions src/test/java/com/creationline/sudoku/validator/AppTest.java
@@ -1,20 +1,98 @@
package com.creationline.sudoku.validator;

import static java.util.Collections.*;
import static java.util.stream.Collectors.*;
import static org.junit.Assert.assertTrue;

import org.junit.Assert;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

/**
* Unit test for simple App.
* What needs to be covered in these tests?
*
*
*/
public class AppTest
{
/**
* Rigorous Test :-)
* Simplest form of the test that should pass.
*/
@Test
public void testSanity() {
var b = Board.read(new String[]{
"53..7....",
"6..195...",
".98....6.",
"8...6...3",
"4..8.3..1",
"7...2...6",
".6....28.",
"...419..5",
"....8..79"
});

Assert.assertEquals(b.findInconsistencies().collect(toList()), emptyList());
}

/**
* Intent: make sure inconsistency in every row is separately checked.
*/
@Test
public void rowCheck() {
for (int y=0; y<9; y++) {
var b = board(
repeat(y,"........."),
".1. ... .1.",
repeat(9-y-1,"........."));

assertInconsistenciesOf(b,
new Inconsistency(new HorizontalGroup(b, y), 1));
}
}

/**
* Intent: make sure inconsistency in every column is separately checked.
*/
@Test
public void shouldAnswerWithTrue()
{
assertTrue( true );
public void columnCheck() {
for (int y=0; y<9; y++) {
var b = board(
repeat(y,"........."),
".1. ... .1.",
repeat(9-y-1,"........."));
b = b.flipDiagonal();

assertInconsistenciesOf(b,
new Inconsistency(new VerticalGroup(b, y), 1));
}
}

void assertInconsistenciesOf(Board b, Inconsistency... expected) {
Assert.assertEquals(
List.of(expected),
b.findInconsistencies().collect(toList())
);
}

Board board(Object... rows) {
var lines = new ArrayList<String>();
for (var row : rows) {
if (row instanceof String)
lines.add((String) row);
if (row instanceof String[])
lines.addAll(Arrays.asList((String[])row));
}
return Board.read(lines.toArray(new String[0]));
}

String[] repeat(int n, String line) {
var lines = new String[n];
for (int i=0; i<n; i++)
lines[i] = line;
return lines;
}
}

0 comments on commit f5fa811

Please sign in to comment.