The problem
In a grid of 4 by 4 squares you wish to place a skyscraper in every sq. with just some clues:
- The peak of the skyscrapers is between 1 and 4
- No two skyscrapers in a row or column could have the identical variety of flooring
- A clue is the variety of skyscrapers which you can see in a row or column from the surface
- Increased skyscrapers block the view of decrease skyscrapers situated behind them
Are you able to write a program that may clear up this puzzle?
Instance:
To grasp how the puzzle works, that is an instance of a row with 2 clues. Seen from the left aspect there are 4 buildings seen whereas seen from the correct aspect only one:
There is just one means wherein the skyscrapers may be positioned. From left-to-right all 4 buildings have to be seen and no constructing could disguise behind one other constructing:
Instance of a 4 by 4 puzzle with the answer:
Process:
public static int[][] solvePuzzle (int[] clues)
- Go the clues in an array of 16 gadgets. This array accommodates the clues across the clock, index:
0 1 2 3 15 4 14 5 13 6 12 7 1110 9 8 - If no clue is out there, add worth `0`
- Every puzzle has just one attainable answer
- `SolvePuzzle()` returns matrix `int[][]`. The primary indexer is for the row, the second indexer for the column. (Python: returns 4-tuple of 4-tuples, Ruby: 4-Array of 4-Arrays)
The answer in Java code
Choice 1:
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
public class SkyScrapers {
personal last static Integer SIZE = 4;
static int[][] solvePuzzle (int[] clues) {
return new SkyScrapers().clear up(clues);
}
personal static class AbortException extends Exception { }
personal Desk desk;
personal int[][] clear up(int[] clues) {
desk = new Desk(clues);
attempt {
Place initPosition = desk.getInitPosition();
if (iter(initPosition)) return desk.desk;
else throw new RuntimeException("no answer");
} catch (AbortException e) { return desk.desk; }
}
personal boolean iter(Place place) {
Set<Integer> possibleValues = desk.getPossibleValuesForPosition(place);
for (Integer possibleValue : possibleValues) {
desk.setValueForPosition(place, possibleValue);
attempt {
Place nextPosition = desk.getNextPosition(place);
if(iter(nextPosition))
return true;
else
desk.setValueForPosition(place, 0);
} catch (AbortException e) { return true; }
}
return false;
}
static class Place {
Integer x;
Integer y;
Place(Integer x, Integer y) {
this.x = x;
this.y = y;
}
Integer getX() {
return x;
}
Integer getY() {
return y;
}
}
static class Desk {
int[][] desk;
Line[] horizontalLines = new Line[SIZE];
Line[] verticalLines = new Line[SIZE];
Desk(int[] clues) {
initTable();
initLinesWithClues(clues);
}
personal Integer getValueForPosition(int x, int y) {
return desk[y][x]; // inverted!
}
void setValueForPosition(Place place, int worth) {
int x = place.getX();
int y = place.getY();
setValueForPosition(x, y, worth);
verticalLines[x].line[y] = worth;
horizontalLines[y].line[x] = worth;
}
personal void setValueForPosition(int x, int y, int worth) {
desk[y][x] = worth; // inverted!
}
Place getInitPosition() throws AbortException {
Place place = new Place(0,0);
if (getValueForPosition(0,0) != 0) place = getNextPosition(place);
return place;
}
Place getNextPosition(Place place) throws AbortException {
int x = place.getX();
int y = place.getY();
do {
if (x == SIZE-1)
if (y == SIZE-1)
throw new AbortException();
else {
x = 0;
y++;
}
else
x++;
} whereas (getValueForPosition(x,y) != 0);
return new Place(x,y);
}
Set<Integer> getPossibleValuesForPosition(Place place) {
int x = place.getX();
int y = place.getY();
Set<Integer> possibleValues = new HashSet<>();
Set<Integer> possibleValuesForHorizontalLine = horizontalLines[y].getPossibleValuesForPosition(x);
Set<Integer> possibleValuesForVerticalLine = verticalLines[x].getPossibleValuesForPosition(y);
possibleValues.addAll(possibleValuesForHorizontalLine);
possibleValues.retainAll(possibleValuesForVerticalLine);
return possibleValues;
}
personal void initLinesWithClues(int[] clues) {
IntStream.vary(0, SIZE).forEach(x -> verticalLines[x] = new Line(getVerticalLine(x), clues[x], clues[3*SIZE-1-x]));
synchTableWithVerticalLines();
IntStream.vary(0, SIZE).forEach(y -> horizontalLines[y] = new Line(getHorizontalLine(y), clues[4*SIZE-1-y], clues[SIZE+y]));
synchTableWithHorizontalLines();
}
personal void synchTableWithHorizontalLines() {
for (int x = 0 ; x < SIZE ; x++)
for (int y = 0 ; y < SIZE ; y++)
setValueForPosition(x, y, horizontalLines[y].line[x]);
}
personal void synchTableWithVerticalLines() {
for (int x = 0 ; x < SIZE ; x++)
for (int y = 0 ; y < SIZE ; y++)
setValueForPosition(x, y, verticalLines[x].line[y]);
}
personal int[] getVerticalLine(int x) {
int[] line = new int[SIZE];
IntStream.vary(0, SIZE).forEach(y -> line[y] = getValueForPosition(x,y));
return line;
}
personal int[] getHorizontalLine(int y) {
int[] line = new int[SIZE];
IntStream.vary(0, SIZE).forEach(x -> line[x] = getValueForPosition(x,y));
return line;
}
personal void initTable() {
desk = new int[SIZE][SIZE];
IntStream.vary(0, SIZE).forEach(i -> Arrays.fill(desk[i], 0));
}
}
static class Line {
int[] line;
int topClue;
int bottomClue;
personal Set<Integer> allNonInitClues = IntStream.rangeClosed(2, SIZE-1).boxed().gather(Collectors.toSet());
Line(int[] line, int topClue, int bottomClue) {
this.line = line;
this.topClue = topClue;
this.bottomClue = bottomClue;
resolveInitClues();
}
personal void resolveInitClues() {
if (topClue == 1) line[0] = SIZE;
if (bottomClue == 1) line[SIZE-1] = SIZE;
if (topClue == SIZE) IntStream.vary(0, SIZE).forEach(i -> line[i] = i + 1);
if (bottomClue == SIZE) IntStream.vary(0, SIZE).forEach(i -> line[SIZE-1-i] = i + 1);
}
Set<Integer> getPossibleValuesForPosition(Integer positionInLine) {
if(line[positionInLine] != 0)
throw new IllegalArgumentException("Deadly Error : Making an attempt to find already found worth !!"
+ "n place = " + positionInLine + "n line : " + this);
Set<Integer> alreadyPlacedValues = getAlreadyPlacedValues(positionInLine);
Set<Integer> possibleValues = IntStream.rangeClosed(1, SIZE).boxed().gather(Collectors.toSet());
possibleValues.removeAll(alreadyPlacedValues);
possibleValues.removeIf(valueToTest -> !isCompatibleWithClues(valueToTest, positionInLine));
return possibleValues;
}
personal boolean isCompatibleWithClues(Integer valueToTest, int place) {
line[position] = valueToTest;
boolean consequence = true;
if(this.isComplete()) consequence = this.isCompatibleWithClues(); // double negation!
line[position] = 0;
return consequence;
}
personal boolean isComplete() {
return IntStream.vary(0, SIZE).allMatch(i -> line[i] != 0);
}
personal boolean isCompatibleWithClues()
(allNonInitClues.accommodates(bottomClue) && getNumberOfSkyCrapersOnLine(false) != bottomClue);
return !isNotCompatibleWithClues;
personal int getNumberOfSkyCrapersOnLine(boolean isAscending) {
int consequence = 0;
int maxValue = 0;
for (int i = 0 ; i < SIZE ; i++) {
int positionInLine = isAscending ? i : SIZE-1-i;
if (line[positionInLine] > maxValue) {
maxValue = line[positionInLine];
consequence++;
}
}
return consequence;
}
personal Set<Integer> getAlreadyPlacedValues(int positionInLine) {
Set<Integer> alreadyPlacedValues = new HashSet<Integer>();
for (int i = 0 ; i < SIZE ; i ++) {
if (i == positionInLine) proceed;
if (line[i] != 0) alreadyPlacedValues.add(line[i]);
}
return alreadyPlacedValues;
}
}
}
Choice 2:
import java.util.*;
public class SkyScrapers {
public static last int SIZE = 4;
public static last int[][] course = new int[][]{{0,1}, {1,0}, {0,-1}, {-1, 0}};
public static int[][][] square3d;
public static int row = 0, col = 0;
public static int rely;
static int[][] solvePuzzle (int[] clues) {
generateEmptySquare();
whereas(rely < SIZE * SIZE) {
for(int i = 0; i < SIZE * 4; i++) {
System.out.printf("i = %d, clue[i]=%d, r = %d, c = %dpercentn", i, clues[i], row, col);
cleanAllHintsInLine(i);
checkLineForOneHint(i);
useClue(i, clues[i]);
if((i + 1) % SIZE != 0) {
row += course[i / SIZE][0];
col += course[i / SIZE][1];
}
}
checkSquareForOneHint();
}
int[][] consequence = new int[SIZE][SIZE];
for(int i = 0; i < SIZE; i++) {
for(int j = 0; j < SIZE; j++) {
consequence[i][j] = square3d[i][j][0];
}
}
return consequence;
}
static void useClue(int i, int clue) {
swap(clue) {
case 1:
setSkyscraper(iRow(i, 0), iCol(i, 0), SIZE);
cleanHintLine(i, SIZE);
break;
case 2:
cleanHint(i, 0, SIZE);
if(getSkyscraper(i, SIZE - 1) == SIZE) {
setSkyscraper(iRow(i, 0), iCol(i, 0), SIZE - 1);
}
if(getSkyscraper(i, SIZE - 2) == SIZE) {
cleanHint(i, 1, SIZE - 1);
}
if(getSkyscraper(i, SIZE - 2) == SIZE && getSkyscraper(i, SIZE - 1) == SIZE - 1) {
setSkyscraper(iRow(i, 0), iCol(i, 0), 2);
setSkyscraper(iRow(i, 1), iCol(i, 1), 1);
}
break;
case 3:
cleanHint(i, 0, SIZE);
cleanHint(i, 1, SIZE);
cleanHint(i, 0, SIZE - 1);
if(getSkyscraper(i, SIZE - 1) == SIZE && getSkyscraper(i, SIZE - 2) == SIZE - 1) {
setSkyscraper(iRow(i, 0), iCol(i, 0), 2);
setSkyscraper(iRow(i, 1), iCol(i, 1), 1);
} else if (getSkyscraper(i, SIZE - 1) == SIZE - 1 && getSkyscraper(i, SIZE - 2) == SIZE) {
setSkyscraper(iRow(i, 0), iCol(i, 0), 1);
setSkyscraper(iRow(i, 1), iCol(i, 1), 2);
} else if (getSkyscraper(i, SIZE - 2) == SIZE && getSkyscraper(i, 0) == 2) {
setSkyscraper(iRow(i, 1), iCol(i, 1), 3);
}
break;
case SIZE:
for(int j = 0; j < SIZE; j++) {
setSkyscraper(iRow(i, j), iCol(i, j), j + 1);
}
break;
}
}
static void generateEmptySquare() {
square3d = new int[SIZE][SIZE][SIZE+1];
for(int r = 0; r < SIZE; r++) {
for(int c = 0; c < SIZE; c++) {
for(int d = 0; d < SIZE + 1; d++) {
square3d[r][c][d] = d;
}
}
}
rely = 0;
}
static void checkSquareForOneHint() {
for(int i = 0; i < SIZE; i++) {
for(int j = 0; j < SIZE; j++) {
checkIsOnlyOneHintAndSetIfYes(i, j);
}
}
}
static void checkLineForOneHint(int i) {
for(int h = 1; h <= SIZE; h++) {
int pos = 0;
int rely = 0;
for(int j = 0; j < SIZE; j++) {
if(square3d[iRow(i, j)][iCol(i, j)][h] > 0) {
rely++;
pos = j;
}
}
if(rely == 1) {
setSkyscraper(iRow(i, pos), iCol(i, pos), h);
}
}
}
static boolean checkIsOnlyOneHintAndSetIfYes(int row, int col) {
if(square3d[row][col][0] > 0) {
return true;
}
int num = 0;
int rely = 0;
for(int dig = 1; dig <= SIZE; dig++) {
if(square3d[row][col][dig] > 0) {
rely++;
num = dig;
}
}
if(rely == 1) {
setSkyscraper(row, col, num);
return true;
}
return false;
}
static void setSkyscraper(int row, int col, int worth) {
if(square3d[row][col][0] == 0) {
square3d[row][col][0] = worth;
for(int h = 0; h < SIZE; h++) {
square3d[row][col][h+1] = 0;
square3d[row][h][value] = 0;
square3d[h][col][value] = 0;
}
rely++;
}
}
static int getSkyscraper(int i, int j) {
return square3d[iRow(i, j)][iCol(i, j)][0];
}
static int iRow(int i, int j) {
return row + course[((i / SIZE) + 1) % SIZE][0] * j;
}
static int iCol(int i, int j) {
return col + course[((i / SIZE) + 1) % SIZE][1] * j;
}
static int howManySee(int i) {
int max = 0;
int rely = 0;
for(int j = 0; j < SIZE; j++) {
if(square3d[iRow(i, j)][iCol(i, j)][0] > max) {
max = square3d[iRow(i, j)][iCol(i, j)][0];
rely++;
}
}
return rely;
}
static void cleanAllHintsInLine(int i) {
for(int j=0; j < SIZE; j++) {
int v = getSkyscraper(i, j);
if(v > 0) {
cleanHintLine(i, v);
}
}
}
static void cleanHintLine(int i, int trace) {
for(int j = 0; j < SIZE; j++) {
cleanHint(i, j, trace);
}
}
static void cleanHint(int i, int j, int trace) {
square3d[iRow(i, j)][iCol(i, j)][hint] = 0;
}
}
Take a look at instances to validate our answer
import org.junit.Take a look at;
import static org.junit.Assert.assertEquals;
import org.junit.runners.JUnit4;
public class SolutionTest {
personal static int clues[][] = {
{ 2, 2, 1, 3,
2, 2, 3, 1,
1, 2, 2, 3,
3, 2, 1, 3 },
{ 0, 0, 1, 2,
0, 2, 0, 0,
0, 3, 0, 0,
0, 1, 0, 0 }
};
personal static int outcomes[][][] = {
{ { 1, 3, 4, 2 },
{ 4, 2, 1, 3 },
{ 3, 4, 2, 1 },
{ 2, 1, 3, 4 } },
{ { 2, 1, 4, 3 },
{ 3, 4, 1, 2 },
{ 4, 2, 3, 1 },
{ 1, 3, 2, 4 } }
};
@Take a look at
public void testSolvePuzzle1 () {
assertEquals (SkyScrapers.solvePuzzle (clues[0]), outcomes[0]);
}
@Take a look at
public void testSolvePuzzle2 () {
assertEquals (SkyScrapers.solvePuzzle (clues[1]), outcomes[1]);
}
}