Java, 330 Points
Before I get ridiculed for such a high score let me clarify that I attempted to try and solve this in a different kind of way knowing it probably wouldn't be quite as optimal as some of the better answers here. I was more or less curious if I could get close which to my surprise I didn't realize just how much worse it would turn out. Here is the run down of what my approach was here:
Develop an algo for solving a Sudoku puzzle.
Develop a scrambling algo that can still be solvable. It does this somewhat randomly while removing clues that can be trivially determined before hand. I could get to about 22 clues reliably before it took far too long.
Once scrambled, the puzzle could be represented by a triplet of single digit integers for each clue, in my case 22 triplets of 3. I thought if I could combine these into a single 66 digit number then base95 encode this then I have something that can be easily decoded.
The encoded string ended up being longer than I hoped at generally around 33 characters long. At which point I tried an alternative way than using Java BigInteger where I created a large number from an 81 bit mask representing the 81 cells of a grid where 1 means a clue exists for this cell. I then combined that bitmask to 4 bit representations of each cell value in sequential order, rounded up to bytes and found that I roughly got the same encoded string length after base95 encoded.
So basically I am posting my code in case anybody was interested in a different approach that didn't work out so well.
Class Puzz
public class Puzz {
enum By {
Row, Column, Block
}
static final List<Integer> NUMBERS = Arrays.asList(new Integer[] { 1, 2, 3,
4, 5, 6, 7, 8, 9 });
List<Square> entries = new ArrayList<Square>();
HashMap<Integer, List<Square>> squaresByRow = new HashMap<Integer, List<Square>>();
HashMap<Integer, List<Square>> squaresByColumn = new HashMap<Integer, List<Square>>();
HashMap<Integer, List<Square>> squaresByBlock = new HashMap<Integer, List<Square>>();
public Puzz(int[][] data) {
// Create squares put them in squares by row hashtable
for (int r = 0; r < 9; r++) {
List<Square> squaresInRow = new ArrayList<Square>();
for (int c = 0; c < 9; c++) {
Square square = new Square(r, c, data[r][c], this);
entries.add(square);
squaresInRow.add(square);
}
squaresByRow.put(r, squaresInRow);
}
// Put squares in column hash table
for (int c = 0; c < 9; c++) {
List<Square> squaresInColumn = new ArrayList<Square>();
for (int r = 0; r < 9; r++) {
squaresInColumn.add(squaresByRow.get(r).get(c));
}
squaresByColumn.put(c, squaresInColumn);
}
// Put squares in block hash table
for (int i = 1; i < 10; i++) {
squaresByBlock.put(i, new ArrayList<Square>());
}
for (int r = 0; r < 9; r++) {
for (int c = 0; c < 9; c++) {
int block = getBlock(r, c);
squaresByBlock.get(block).add(get(r, c));
}
}
// Discover the possibilities
updatePossibilities();
}
public void updatePossibilities() {
for (int r = 0; r < 9; r++) {
for (int c = 0; c < 9; c++) {
Square theSquare = get(r, c);
if (theSquare.value != 0) {
theSquare.possibilities.removeAll(NUMBERS);
continue;
} else {
theSquare.possibilities.addAll(NUMBERS);
}
int block = getBlock(r, c);
HashSet<Square> squares = new HashSet<Square>();
squares.addAll(squaresByRow.get(r));
squares.addAll(squaresByColumn.get(c));
squares.addAll(squaresByBlock.get(block));
for (Square s : squares) {
if (s == theSquare)
continue;
theSquare.possibilities.remove(s.value);
}
}
}
}
public int getValue(int row, int column) {
return squaresByRow.get(row).get(column).value;
}
public Square get(int row, int column) {
return squaresByRow.get(row).get(column);
}
public boolean set(int row, int column, int value) {
if (value == 0) {
squaresByRow.get(row).get(column).value = 0;
updatePossibilities();
return true;
}
if (isValid(row, column, value)) {
squaresByRow.get(row).get(column).value = value;
updatePossibilities();
return true;
} else {
return false;
}
}
public boolean isValidSubset(By subset, int row, int column, int value) {
List<Dubs> dubss = new ArrayList<Dubs>();
List<Trips> tripss = new ArrayList<Trips>();
Square theSquare = get(row, column);
int block = getBlock(row, column);
List<Square> squares = new ArrayList<Square>();
switch (subset) {
case Row:
squares.addAll(squaresByRow.get(row));
break;
case Column:
squares.addAll(squaresByColumn.get(column));
break;
default:
squares.addAll(squaresByBlock.get(block));
break;
}
for (Square r : squares) {
if (r == theSquare)
continue;
// if any of the impacted squares have this value then it is not a
// valid value
if (r.value == value)
return false;
if (r.possibilities.size() == 3) {
List<Integer> poss = new ArrayList<Integer>(r.possibilities);
tripss.add(new Trips(poss.get(0), poss.get(1), poss.get(2),
r.row, r.col));
}
if (r.possibilities.size() == 2) {
List<Integer> poss = new ArrayList<Integer>(r.possibilities);
dubss.add(new Dubs(poss.get(0), poss.get(1), r.row, r.col));
}
}
// Find the trips and rule out the value if a triplet exists in squares
List<Trips> tripsCopy = new ArrayList<Trips>(tripss);
for (Trips trips : tripsCopy) {
int countOfOccurrences = 0;
for (Trips tr : tripss) {
if (tr.equals(trips) && !(tr.row == row && tr.col == column))
countOfOccurrences++;
}
for (Dubs dubs : dubss) {
if (trips.containedWithin(dubs)
&& !(dubs.row == row && dubs.col == column))
countOfOccurrences++;
}
if (countOfOccurrences == 3 && trips.containedWithin(value))
return false;
}
// Find the dubs and rule out the value if a double exists in squares
List<Dubs> dubsCopy = new ArrayList<Dubs>(dubss);
for (Dubs dubs : dubsCopy) {
int countOfOccurrences = 0;
for (Dubs du : dubss) {
// Count occurrences of Dubs that are not the tested square
if (du.equals(dubs) && !(du.row == row && du.col == column))
countOfOccurrences++;
}
if (countOfOccurrences == 2 && dubs.containedWithin(value))
return false;
}
return true;
}
public boolean isValid(int row, int column, int value) {
return isValidSubset(By.Row, row, column, value)
&& isValidSubset(By.Column, row, column, value)
&& isValidSubset(By.Block, row, column, value);
}
public int getBlock(int row, int column) {
int blockRow = (int) Math.floor(row / 3);
int columnRow = (int) Math.floor(column / 3) + 1;
return (blockRow * 3) + columnRow;
}
public Puzz solve(Puzz arg, boolean top) throws Exception {
// Make an original copy of the array
Puzz p = (Puzz) arg.clone();
for (int i = 1; i < 10; i++) {
for (Square s : p.squaresByBlock.get(i)) {
if (s.value == 0) {
for (Integer number : NUMBERS) {
if (p.set(s.row, s.col, number)) {
// System.out.println(p);
Puzz solved = solve(p, false);
if (solved != null)
return solved;
}
}
// no numbers fit here, return null and backtrack
p.set(s.row, s.col, 0);
return null;
}
}
}
// Check for remaining 0's
for (Square s : p.entries) {
if (s.value == 0)
return null;
}
return p;
}
public Puzz scramble(int clues) throws Exception {
Puzz p = (Puzz) clone();
Random rand = new Random();
int removed = 0;
//Remove the last row, it is a freebie
int toRemove = 81 - clues - 15;
for (int c = 0; c < 9; c++) {
p.set(8, c, 0);
}
p.set(0, 0, 0);
p.set(0, 3, 0);
p.set(0, 6, 0);
p.set(3, 0, 0);
p.set(3, 3, 0);
p.set(3, 6, 0);
// Keeping track of this because randomly removing squares can potentially create an
// unsolvable situation
HashSet<Square> alreadyTried = new HashSet<Square>();
while (removed < toRemove) {
if (alreadyTried.size() >= ((toRemove + clues) - removed)) {
// Start over
removed = 0;
alreadyTried = new HashSet<Square>();
p = (Puzz)clone();
for (int c = 0; c < 9; c++) {
p.set(8, c, 0);
}
p.set(0, 0, 0);
p.set(0, 3, 0);
p.set(0, 6, 0);
p.set(3, 0, 0);
p.set(3, 3, 0);
p.set(3, 6, 0);
}
int randX = rand.nextInt((7) + 1);
int randY = rand.nextInt((8) + 1);
int existingValue = p.getValue(randX, randY);
if (existingValue != 0) {
p.set(randX, randY, 0);
// confirm it is still solvable after removing this item
Puzz psol = solve(p, true);
if (psol != null && psol.equals(this)) {
removed++;
alreadyTried = new HashSet<Square>();
System.out.println("Clues Remaining: " + (81 - 15 - removed));
} else {
// otherwise set it back to what it was and try again
p.set(randX, randY, existingValue);
Square s = new Square(randX, randY, existingValue, p);
alreadyTried.add(s);
}
}
}
p.updatePossibilities();
return p;
}
public static String encode(Puzz p) { // Remove all zero'ed items
StringBuffer sb = new StringBuffer();
for (int i = 0; i < 9; i++) {
for (Square s : p.squaresByRow.get(i)) {
if (s.value == 0)
continue;
sb.append(s.row).append(s.col).append(s.value);
}
}
// number mod 95 gives lowest digit, subtract that from original number
BigInteger num = new BigInteger(sb.toString());
byte[] numBytes = num.toByteArray();
StringBuffer retVal = new StringBuffer();
while (num.compareTo(BigInteger.ZERO) > 0) {
int modu = num.mod(new BigInteger("95")).intValue();
retVal.append((char) (modu + 32));
num = num.subtract(new BigInteger("" + modu));
num = num.divide(new BigInteger("95"));
}
return retVal.toString();
}
@Override
public boolean equals(Object arg0) {
if (arg0 == null || !(arg0 instanceof Puzz))
return false;
Puzz p = (Puzz) arg0;
for (int r = 0; r < 9; r++) {
for (int c = 0; c < 9; c++) {
int val1 = getValue(r, c);
int val2 = p.getValue(r, c);
if (val1 != val2)
return false;
}
}
return true;
}
@Override
protected Object clone() throws CloneNotSupportedException {
int[][] data = new int[9][9];
for (Square square : entries) {
data[square.row][square.col] = square.value;
}
return new Puzz(data);
}
@Override
public String toString() {
if (entries == null)
return "";
StringBuffer sb = new StringBuffer();
for (int r = 0; r < 9; r++) {
for (int c = 0; c < 9; c++) {
sb.append(getValue(r, c)).append(' ');
}
sb.append('\n');
}
return sb.toString();
}
}
class Square {
public Square(int row, int col, Puzz p) {
this.row = row;
this.col = col;
this.p = p;
}
public Square(int row, int col, int value, Puzz p) {
this(row, col, p);
this.value = value;
}
int row;
int col;
int value;
HashSet<Integer> possibilities = new HashSet<Integer>(Puzz.NUMBERS);
Puzz p;
@Override
protected Object clone() throws CloneNotSupportedException {
Square s = new Square(row, col, value, p);
s.possibilities = new HashSet<Integer>();
for (Integer val : possibilities) {
s.possibilities.add(new Integer(val));
}
return s;
}
@Override
public boolean equals(Object obj) {
if (!(obj instanceof Square))
return false;
Square s = (Square) obj;
return row == s.row && col == s.col && value == s.value
&& p.equals(s.p);
}
@Override
public int hashCode() {
return row ^ col ^ value ^ p.hashCode();
}
}
class Dubs {
int p1;
int p2;
int row, col;
public Dubs(int p1, int p2) {
this.p1 = p1;
this.p2 = p2;
}
public Dubs(int p1, int p2, int row, int col) {
this(p1, p2);
this.row = row;
this.col = col;
}
public boolean containedWithin(int value) {
return (p1 == value || p2 == value);
}
@Override
public boolean equals(Object arg0) {
if (!(arg0 instanceof Dubs))
return false;
Dubs d = (Dubs) arg0;
return (this.p1 == d.p1 || this.p1 == d.p2)
&& (this.p2 == d.p1 || this.p2 == d.p2);
}
}
class Trips {
int p1;
int p2;
int p3;
int row, col;
public Trips(int p1, int p2) {
this.p1 = p1;
this.p2 = p2;
}
public Trips(int p1, int p2, int p3) {
this(p1, p2);
this.p3 = p3;
}
public Trips(int p1, int p2, int p3, int row, int col) {
this(p1, p2, p3);
this.row = row;
this.col = col;
}
public boolean containedWithin(int value) {
return (p1 == value || p2 == value || p3 == value);
}
public boolean containedWithin(Dubs d) {
return (d.p1 == p1 || d.p1 == p2 || d.p1 == p3)
&& (d.p2 == p1 || d.p2 == p2 || d.p2 == p3);
}
public boolean equals(Object arg0) {
if (!(arg0 instanceof Trips))
return false;
Trips t = (Trips) arg0;
return (this.p1 == t.p1 || this.p1 == t.p2 || this.p1 == t.p3)
&& (this.p2 == t.p1 || this.p2 == t.p2 || this.p2 == t.p3)
&& (this.p3 == t.p1 || this.p3 == t.p2 || this.p3 == t.p3);
}
}
My Test Case
public class TestCompression extends TestCase {
public static int[][] test1 = new int[][] {
new int[] { 9, 7, 3, 5, 8, 1, 4, 2, 6 },
new int[] { 5, 2, 6, 4, 7, 3, 1, 9, 8 },
new int[] { 1, 8, 4, 2, 9, 6, 7, 5, 3 },
new int[] { 2, 4, 7, 8, 6, 5, 3, 1, 9 },
new int[] { 3, 9, 8, 1, 2, 4, 6, 7, 5 },
new int[] { 6, 5, 1, 7, 3, 9, 8, 4, 2 },
new int[] { 8, 1, 9, 3, 4, 2, 5, 6, 7 },
new int[] { 7, 6, 5, 9, 1, 8, 2, 3, 4 },
new int[] { 4, 3, 2, 6, 5, 7, 9, 8, 1 } };
public static int[][] test2 = new int[][] {
new int[] { 7, 2, 4, 8, 6, 5, 1, 9, 3 },
new int[] { 1, 6, 9, 2, 4, 3, 8, 7, 5 },
new int[] { 3, 8, 5, 1, 9, 7, 2, 4, 6 },
new int[] { 8, 9, 6, 7, 2, 4, 3, 5, 1 },
new int[] { 2, 7, 3, 9, 5, 1, 6, 8, 4 },
new int[] { 4, 5, 1, 3, 8, 6, 9, 2, 7 },
new int[] { 5, 4, 2, 6, 3, 9, 7, 1, 8 },
new int[] { 6, 1, 8, 5, 7, 2, 4, 3, 9 },
new int[] { 9, 3, 7, 4, 1, 8, 5, 6, 2 } };
public static int[][] test3 = new int[][] {
new int[] { 1, 5, 7, 6, 8, 2, 3, 4, 9 },
new int[] { 4, 3, 2, 5, 1, 9, 6, 8, 7 },
new int[] { 6, 9, 8, 3, 4, 7, 2, 5, 1 },
new int[] { 8, 2, 5, 4, 7, 6, 1, 9, 3 },
new int[] { 7, 1, 3, 9, 2, 8, 4, 6, 5 },
new int[] { 9, 6, 4, 1, 3, 5, 7, 2, 8 },
new int[] { 5, 4, 1, 2, 9, 3, 8, 7, 6 },
new int[] { 2, 8, 9, 7, 6, 1, 5, 3, 4 },
new int[] { 3, 7, 6, 8, 5, 4, 9, 1, 2 } };
public static int[][] test4 = new int[][] {
new int[] { 8, 3, 5, 4, 1, 6, 9, 2, 7 },
new int[] { 2, 9, 6, 8, 5, 7, 4, 3, 1 },
new int[] { 4, 1, 7, 2, 9, 3, 6, 5, 8 },
new int[] { 5, 6, 9, 1, 3, 4, 7, 8, 2 },
new int[] { 1, 2, 3, 6, 7, 8, 5, 4, 9 },
new int[] { 7, 4, 8, 5, 2, 9, 1, 6, 3 },
new int[] { 6, 5, 2, 7, 8, 1, 3, 9, 4 },
new int[] { 9, 8, 1, 3, 4, 5, 2, 7, 6 },
new int[] { 3, 7, 4, 9, 6, 2, 8, 1, 5 } };
public static int[][] test5 = new int[][] {
new int[] { 6, 2, 8, 4, 5, 1, 7, 9, 3 },
new int[] { 5, 9, 4, 7, 3, 2, 6, 8, 1 },
new int[] { 7, 1, 3, 6, 8, 9, 5, 4, 2 },
new int[] { 2, 4, 7, 3, 1, 5, 8, 6, 9 },
new int[] { 9, 6, 1, 8, 2, 7, 3, 5, 4 },
new int[] { 3, 8, 5, 9, 6, 4, 2, 1, 7 },
new int[] { 1, 5, 6, 2, 4, 3, 9, 7, 8 },
new int[] { 4, 3, 9, 5, 7, 8, 1, 2, 6 },
new int[] { 8, 7, 2, 1, 9, 6, 4, 3, 5 } };
public static int[][] test6 = new int[][] {
new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 },
new int[] { 4, 5, 6, 7, 8, 9, 1, 2, 3 },
new int[] { 7, 8, 9, 1, 2, 3, 4, 5, 6 },
new int[] { 2, 1, 4, 3, 6, 5, 8, 9, 7 },
new int[] { 3, 6, 5, 8, 9, 7, 2, 1, 4 },
new int[] { 8, 9, 7, 2, 1, 4, 3, 6, 5 },
new int[] { 5, 3, 1, 6, 4, 8, 9, 7, 2 },
new int[] { 6, 4, 8, 9, 7, 2, 5, 3, 1 },
new int[] { 9, 7, 2, 5, 3, 1, 6, 4, 8 } };
public static int[][] test7 = new int[][] {
new int[] { 1, 4, 5, 7, 9, 2, 8, 3, 6 },
new int[] { 3, 7, 6, 5, 8, 4, 1, 9, 2 },
new int[] { 2, 9, 8, 3, 6, 1, 7, 5, 4 },
new int[] { 7, 3, 1, 9, 2, 8, 6, 4, 5 },
new int[] { 8, 5, 9, 6, 4, 7, 3, 2, 1 },
new int[] { 4, 6, 2, 1, 3, 5, 9, 8, 7 },
new int[] { 6, 2, 4, 8, 7, 3, 5, 1, 9 },
new int[] { 5, 8, 7, 4, 1, 9, 2, 6, 3 },
new int[] { 9, 1, 3, 2, 5, 6, 4, 7, 8 } };
public static int[][] test8 = new int[][] {
new int[] { 5, 2, 7, 4, 1, 6, 9, 3, 8 },
new int[] { 8, 6, 4, 3, 2, 9, 1, 5, 7 },
new int[] { 1, 3, 9, 5, 7, 8, 6, 4, 2 },
new int[] { 2, 9, 1, 8, 5, 4, 3, 7, 6 },
new int[] { 3, 4, 8, 6, 9, 7, 5, 2, 1 },
new int[] { 6, 7, 5, 1, 3, 2, 4, 8, 9 },
new int[] { 7, 1, 2, 9, 4, 5, 8, 6, 3 },
new int[] { 4, 8, 3, 2, 6, 1, 7, 9, 5 },
new int[] { 9, 5, 6, 7, 8, 3, 2, 1, 4 } };
public static int[][] test9 = new int[][] {
new int[] { 2, 4, 6, 7, 1, 3, 9, 8, 5 },
new int[] { 1, 8, 5, 4, 9, 6, 7, 3, 2 },
new int[] { 9, 3, 7, 8, 2, 5, 1, 4, 6 },
new int[] { 6, 7, 8, 5, 4, 2, 3, 9, 1 },
new int[] { 4, 9, 3, 1, 6, 8, 2, 5, 7 },
new int[] { 5, 1, 2, 3, 7, 9, 4, 6, 8 },
new int[] { 8, 2, 4, 9, 5, 7, 6, 1, 3 },
new int[] { 7, 5, 9, 6, 3, 1, 8, 2, 4 },
new int[] { 3, 6, 1, 2, 8, 4, 5, 7, 9 } };
public static int[][] test10 = new int[][] {
new int[] { 8, 6, 1, 2, 9, 4, 5, 7, 3 },
new int[] { 4, 7, 5, 3, 1, 8, 6, 9, 2 },
new int[] { 3, 9, 2, 5, 6, 7, 8, 1, 4 },
new int[] { 2, 3, 6, 4, 5, 9, 7, 8, 1 },
new int[] { 1, 5, 4, 7, 8, 3, 2, 6, 9 },
new int[] { 9, 8, 7, 6, 2, 1, 3, 4, 5 },
new int[] { 5, 2, 9, 1, 7, 6, 4, 3, 8 },
new int[] { 6, 4, 8, 9, 3, 2, 1, 5, 7 },
new int[] { 7, 1, 3, 8, 4, 5, 9, 2, 6 } };
@Test
public void test2() throws Exception {
int encodedLength = 0;
Puzz expected = new Puzz(test1);
Puzz test = (Puzz) expected.clone();
long start = System.currentTimeMillis();
test = test.scramble(22);
long duration = System.currentTimeMillis() - start;
System.out.println("Duration of scramble for 22 clue puzzle: " + duration);
System.out.println("Scrambled");
System.out.println(test);
String encoded = Puzz.encode(test);
System.out.println("Encoded Length with BigInteger: " + encoded.length());
encodedLength += encoded.length();
expected = new Puzz(test2);
test = (Puzz) expected.clone();
start = System.currentTimeMillis();
test = test.scramble(22);
duration = System.currentTimeMillis() - start;
System.out.println("Duration of scramble for 22 clue puzzle: " + duration);
System.out.println("Scrambled");
System.out.println(test);
encoded = Puzz.encode(test);
System.out.println("Encoded Length with BigInteger: " + encoded.length());
encodedLength += encoded.length();
expected = new Puzz(test3);
test = (Puzz) expected.clone();
start = System.currentTimeMillis();
test = test.scramble(22);
duration = System.currentTimeMillis() - start;
System.out.println("Duration of scramble for 22 clue puzzle: " + duration);
System.out.println("Scrambled");
System.out.println(test);
encoded = Puzz.encode(test);
System.out.println("Encoded Length with BigInteger: " + encoded.length());
encodedLength += encoded.length();
expected = new Puzz(test4);
test = (Puzz) expected.clone();
start = System.currentTimeMillis();
test = test.scramble(22);
duration = System.currentTimeMillis() - start;
System.out.println("Duration of scramble for 22 clue puzzle: " + duration);
System.out.println("Scrambled");
System.out.println(test);
encoded = Puzz.encode(test);
System.out.println("Encoded Length with BigInteger: " + encoded.length());
encodedLength += encoded.length();
expected = new Puzz(test5);
test = (Puzz) expected.clone();
start = System.currentTimeMillis();
test = test.scramble(22);
duration = System.currentTimeMillis() - start;
System.out.println("Duration of scramble for 22 clue puzzle: " + duration);
System.out.println("Scrambled");
System.out.println(test);
encoded = Puzz.encode(test);
System.out.println("Encoded Length with BigInteger: " + encoded.length());
encodedLength += encoded.length();
expected = new Puzz(test6);
test = (Puzz) expected.clone();
start = System.currentTimeMillis();
test = test.scramble(22);
duration = System.currentTimeMillis() - start;
System.out.println("Duration of scramble for 22 clue puzzle: " + duration);
System.out.println("Scrambled");
System.out.println(test);
encoded = Puzz.encode(test);
System.out.println("Encoded Length with BigInteger: " + encoded.length());
encodedLength += encoded.length();
expected = new Puzz(test7);
test = (Puzz) expected.clone();
start = System.currentTimeMillis();
test = test.scramble(22);
duration = System.currentTimeMillis() - start;
System.out.println("Duration of scramble for 22 clue puzzle: " + duration);
System.out.println("Scrambled");
System.out.println(test);
encoded = Puzz.encode(test);
System.out.println("Encoded Length with BigInteger: " + encoded.length());
encodedLength += encoded.length();
expected = new Puzz(test8);
test = (Puzz) expected.clone();
start = System.currentTimeMillis();
test = test.scramble(22);
duration = System.currentTimeMillis() - start;
System.out.println("Duration of scramble for 22 clue puzzle: " + duration);
System.out.println("Scrambled");
System.out.println(test);
encoded = Puzz.encode(test);
System.out.println("Encoded Length with BigInteger: " + encoded.length());
encodedLength += encoded.length();
expected = new Puzz(test9);
test = (Puzz) expected.clone();
start = System.currentTimeMillis();
test = test.scramble(22);
duration = System.currentTimeMillis() - start;
System.out.println("Duration of scramble for 22 clue puzzle: " + duration);
System.out.println("Scrambled");
System.out.println(test);
encoded = Puzz.encode(test);
System.out.println("Encoded Length with BigInteger: " + encoded.length());
encodedLength += encoded.length();
expected = new Puzz(test10);
test = (Puzz) expected.clone();
start = System.currentTimeMillis();
test = test.scramble(22);
duration = System.currentTimeMillis() - start;
System.out.println("Duration of scramble for 22 clue puzzle: " + duration);
System.out.println("Scrambled");
System.out.println(test);
encoded = Puzz.encode(test);
encodedLength += encoded.length();
System.out.println("Final Result: " + encodedLength);
}
}
Test Output
Duration of scramble for 22 clue puzzle: 427614
Scrambled
0 0 3 0 0 0 0 0 6
0 2 0 0 0 0 0 9 0
0 0 0 0 9 6 7 5 0
0 4 0 0 0 5 0 1 0
0 0 0 1 0 0 0 0 0
0 5 0 0 0 0 8 4 0
0 0 0 3 0 0 5 0 7
7 0 0 9 0 8 0 3 0
0 0 0 0 0 0 0 0 0
Building encoded string: U5[XZ+C6Bgf)}O."gDE)`\)kNv7*6}1w+
Encoded Length with BigInteger: 33
Duration of scramble for 22 clue puzzle: 167739
Scrambled
0 2 4 0 0 0 0 0 0
1 6 0 0 4 0 8 0 5
0 0 5 0 9 7 2 0 0
0 0 0 0 2 4 0 0 1
0 0 3 9 0 0 0 0 0
0 0 0 0 0 0 0 0 7
0 4 0 0 0 0 0 0 8
0 1 0 5 0 0 0 3 0
0 0 0 0 0 0 0 0 0
Building encoded string: 7\c^oE}`H6@P.&E)Zu\t>B"k}Vf<[0a3&
Encoded Length with BigInteger: 33
Duration of scramble for 22 clue puzzle: 136364
Scrambled
0 0 7 0 8 0 0 0 0
0 3 2 0 0 9 6 0 0
0 0 0 0 0 0 2 5 0
0 2 0 0 0 6 0 0 0
0 0 0 9 0 0 0 0 0
0 0 4 1 0 5 7 2 0
5 0 1 0 0 0 0 7 0
2 8 9 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0
Building encoded string: [S#bHlTDwS,&w,moQ{WN}Z9!{1C>.vN{-
Encoded Length with BigInteger: 33
Duration of scramble for 22 clue puzzle: 392150
Scrambled
0 0 0 0 0 6 0 0 0
0 9 0 0 0 0 0 0 1
4 0 0 0 0 3 6 0 8
0 0 0 0 0 0 0 8 0
0 0 3 0 7 8 0 0 9
7 0 0 0 0 0 0 0 3
6 0 2 0 0 0 0 9 0
9 0 1 3 4 0 2 0 0
0 0 0 0 0 0 0 0 0
Building encoded string: T-yKJ2<d)Dj~[~>]334*9YpxM<JQNf2|<
Encoded Length with BigInteger: 33
Duration of scramble for 22 clue puzzle: 169355
Scrambled
0 0 0 0 0 1 0 0 0
0 9 4 7 0 0 0 8 0
0 1 3 0 0 0 5 0 2
0 0 0 0 0 0 0 0 9
0 0 0 0 2 7 3 5 4
0 8 0 0 0 0 0 1 0
0 0 0 0 4 0 9 0 8
0 0 0 5 0 0 0 0 6
0 0 0 0 0 0 0 0 0
Building encoded string: 5@.=FmOKws7jl5*hWMQqqou\lv'e^Q}D:
Encoded Length with BigInteger: 33
Duration of scramble for 22 clue puzzle: 786
Scrambled
0 2 3 0 0 6 0 0 0
0 5 0 7 0 0 1 2 3
0 8 0 0 2 0 0 0 0
0 0 0 0 0 5 0 0 7
0 6 5 8 0 0 0 0 0
0 0 7 0 0 4 3 0 0
0 3 0 0 4 0 0 0 2
0 0 0 0 0 2 0 0 0
0 0 0 0 0 0 0 0 0
Building encoded string: wY%(O9tOSDZu-PBaFl^.f0xH7C~e)=\3&
Encoded Length with BigInteger: 33
Duration of scramble for 22 clue puzzle: 826530
Scrambled
0 0 0 0 9 0 0 0 0
0 0 0 0 0 0 0 0 0
0 9 0 3 0 1 7 0 0
0 3 0 0 0 8 0 4 5
0 0 9 0 0 7 3 0 0
0 0 2 0 3 0 0 8 0
6 0 0 0 0 0 0 0 9
5 0 0 4 1 0 2 0 3
0 0 0 0 0 0 0 0 0
Building encoded string: K|>.Aa?,8e&NRL;*ut=+Iqk8E$@&-zlF9
Encoded Length with BigInteger: 33
Duration of scramble for 22 clue puzzle: 4834
Scrambled
0 2 0 0 1 0 0 3 8
8 6 0 3 0 0 1 0 0
0 0 0 0 0 8 6 0 2
0 0 0 0 0 0 0 7 0
0 0 8 0 0 0 0 0 0
0 0 0 0 3 0 0 0 0
0 0 2 0 0 5 8 0 3
4 0 0 0 0 1 7 9 0
0 0 0 0 0 0 0 0 0
Building encoded string: GOS0!r=&HR5PZ|ezy>*l7 HWU`wIN7Q4&
Encoded Length with BigInteger: 33
Duration of scramble for 22 clue puzzle: 42126
Scrambled
0 0 0 0 0 3 0 0 5
0 0 5 4 0 0 0 3 2
9 0 0 8 0 0 0 0 0
0 0 0 0 0 2 0 0 0
0 0 0 0 6 8 2 0 7
5 1 0 0 7 0 0 0 8
8 0 0 0 5 0 0 1 0
7 0 0 0 0 0 0 0 4
0 0 0 0 0 0 0 0 0
Building encoded string: [4#9D_?I1.!h];Y_2!iqLyngbBJ&k)FF;
Encoded Length with BigInteger: 33
Duration of scramble for 22 clue puzzle: 156182
Scrambled
0 6 0 0 0 0 0 7 0
4 0 5 3 1 0 0 0 2
0 0 0 0 6 0 0 0 0
0 3 0 0 0 9 0 8 1
0 0 0 0 0 0 0 0 0
0 0 7 0 0 1 0 4 5
5 0 9 0 0 0 0 0 8
6 0 0 0 3 2 0 0 0
0 0 0 0 0 0 0 0 0
Building encoded string: r+a;I%hGj4YCA-pXz+n=ioRL:agzH'K<(
Encoded Length with BigInteger: 33
Final Result: 330
for inspiration: http://cstheory.stackexchange.com/questions/8151/what-is-the-minimum-number-of-bits-required-to-store-a-sudoku-puzzle/8163#8163
– ratchet freak – 2014-12-12T08:38:26.903Which bytes are allowed should be more clearly stated: anything (0-255), ASCII (0-127) or printable ASCII (32-126), etc. – feersum – 2014-11-15T00:39:26.620
@MartinBüttner You're right. I will add more test cases. – kukac67 – 2014-11-15T00:39:45.050
@feersum Printable ASCII (so, 32-126). I will update the rules. – kukac67 – 2014-11-15T00:43:04.130
5
Also, there should be a time limit, to prevent a solution that enumerates every board configuration and checks whether it is one of the 6670903752021072936960 possible solved Sudoku grids.
– feersum – 2014-11-15T00:44:00.633@feersum I didn't want to impose a time-limit, though I suppose I should. However, I don't want to do a hard cut-off, just prevent brute-forcing. – kukac67 – 2014-11-15T00:45:51.137
@feersum let's assume the usual "submitters should be able to test their submissions before submitting them" – John Dvorak – 2014-11-15T00:47:19.623
@JanDvorak I haven't heard about this one...and often see untestable programs. – feersum – 2014-11-15T00:48:40.493
3You may want to change the scoring. As it stands there is nothing stopping me from hardcoding the test cases to 1-char codes and just use 81-char codes for everything else – TwiNight – 2014-11-15T05:11:16.080
4@TwiNight apart from it being a standard loophole, you mean? – John Dvorak – 2014-11-15T05:42:27.177
4Despite my answer below, I think the best way to solve this would be to write a sudoku solver, then remove the maximum number of digits from the grid such that the puzzle is still soluble (that should be all but four or five numbers). Then compress that. The decompressor also contains the solver. – abligh – 2014-11-15T13:36:16.190
@abligh Yes. A Sudoku to be solved is a compressed Sudoku solution. 95 characters for the compression is too generous to non-Sudoku solutions, so those have been run with. They'll be cut to pieces later. The question gives a strong hint. Also, I haven't tried, but even with the number solutions, why not replace the 9 by zero before making the big numbers? – Bill Woodger – 2014-11-15T16:15:31.787
@BillWoodger most of the solutions (incl mine) subtract one from each digit so they are in fact 0..8 (base 9). – abligh – 2014-11-15T17:10:01.293
@JanDvorak Declaring it to be a standard loophole won't matter much since there is really no way to draw a line between the obvious hardcoding and then simply writing code which is only optimized for the test cases. It would be much better to use the standard solution and score according to the sum of decompression code and compressed data. (And here it is important that it is the decompression code and not the compression code, which is included in the scoring rule.) – kasperd – 2014-11-16T01:24:41.313
@kasperd that would be too big of a change at this point, however. Might be good enough for a separate challenge, though – John Dvorak – 2014-11-16T02:11:43.000
4@kasperd it is indeed difficult to draw the line (see the
fudge
subroutine in my second answer that gains 12 points). A fairer test would be to require that (a) the test solutions work, (b) score on 1,000 randomly generated Sudoku grids and divide the answer by 100. I believe the best one can do with random data is about 110, based on 10 x log-base-95(6670903752021072936960) – abligh – 2014-11-16T11:52:39.390Is there a deadline for submitting answers? When will the winner be declared? I have a plan, but didn't get to implementing it yet. Well, if the one with the hardcoded test examples counts, the race is run. My goal is the best generic solution. :) – Reto Koradi – 2014-11-18T08:16:44.463
@RetoKoradi There's still time :) At least 3 more days, most likely more. – kukac67 – 2014-11-19T01:17:02.910
There is another fundamental issue with this challenge. Most, if not all of the solutions are taking advantage of information beyond the allowed byte values 32..126. In particular, they depend on knowing the length of the string or file. If there are multiple boards in a file, you would need a delimiter to end each board, which would increase the number of characters per board by one. Or you would need to use some other scheme to encode the end of the board in the compressed data. – Mark Adler – 2014-11-22T07:47:06.083
Another more subtle issue with this challenge is the 6th test case. It is easy to see that this is a very special board, in the sense that the lowest values are selected for each cell, except for one, assuming some ordering of filling in those cells and ordering of the digits. That selects for solutions that code in that order, which can provide a very small integer for that board, and a so a single character compression. This can be seen in Jan Dvorak's and averykhoo's solutions, both of which code that board to ")". So the winning solutions are fudged, even if not intentionally. – Mark Adler – 2014-11-22T09:46:43.007