mirror of
https://gitlab.com/mfocko/CodeWars.git
synced 2024-11-09 11:09:07 +01:00
264 lines
7.2 KiB
Java
264 lines
7.2 KiB
Java
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.NoSuchElementException;
|
|
import java.util.stream.Stream;
|
|
import java.util.stream.StreamSupport;
|
|
|
|
public class SkyScrapers {
|
|
private static class Permutations<T extends Comparable<T>> implements Iterable<List<T>> {
|
|
public class PermutationsIterator implements Iterator<List<T>> {
|
|
private List<T> elements;
|
|
private boolean _hasNext;
|
|
private boolean firstIteration = true;
|
|
|
|
public PermutationsIterator(List<T> elements) {
|
|
this.elements = elements;
|
|
_hasNext = elements.size() > 0;
|
|
}
|
|
|
|
private void swap(int k, int l) {
|
|
T tmp = elements.get(k);
|
|
elements.set(k, elements.get(l));
|
|
elements.set(l, tmp);
|
|
}
|
|
|
|
public boolean hasNext() {
|
|
return _hasNext;
|
|
}
|
|
|
|
public List<T> next() {
|
|
if (!_hasNext) {
|
|
throw new NoSuchElementException("No more permutations are left");
|
|
}
|
|
|
|
var lastIteration = new ArrayList<T>(elements);
|
|
|
|
int k = 0, l = 0;
|
|
_hasNext = false;
|
|
for (int i = elements.size() - 1; i > 0; i--) {
|
|
if (elements.get(i).compareTo(elements.get(i - 1)) > 0) {
|
|
k = i - 1;
|
|
_hasNext = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
for (int i = elements.size() - 1; i > k; i--) {
|
|
if (elements.get(i).compareTo(elements.get(k)) > 0) {
|
|
l = i;
|
|
break;
|
|
}
|
|
}
|
|
|
|
swap(k, l);
|
|
Collections.reverse(elements.subList(k + 1, elements.size()));
|
|
|
|
return lastIteration;
|
|
}
|
|
}
|
|
|
|
private List<T> elements;
|
|
|
|
public Permutations(List<T> elements) {
|
|
this.elements = elements;
|
|
Collections.sort(this.elements);
|
|
}
|
|
|
|
public Iterator<List<T>> iterator() {
|
|
return new PermutationsIterator(elements);
|
|
}
|
|
|
|
public Stream<List<T>> stream() {
|
|
return StreamSupport.stream(this.spliterator(), false);
|
|
}
|
|
}
|
|
|
|
private static class PossibleConfigurations implements Iterable<List<Integer>> {
|
|
private List<Integer> initial;
|
|
private int clue;
|
|
|
|
private void generateInitial(int size) {
|
|
initial = new ArrayList<Integer>();
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
initial.add(i + 1);
|
|
}
|
|
}
|
|
|
|
public PossibleConfigurations(int size, int clue) {
|
|
generateInitial(size);
|
|
this.clue = clue;
|
|
}
|
|
|
|
public Iterator<List<Integer>> iterator() {
|
|
if (clue == 0) {
|
|
return (new Permutations(initial)).iterator();
|
|
}
|
|
|
|
return (new Permutations(initial))
|
|
.stream()
|
|
.filter(heights -> isValid((List<Integer>) heights, clue))
|
|
.iterator();
|
|
}
|
|
}
|
|
|
|
private static boolean isValid(List<Integer> heights, int clue) {
|
|
int canSee = 1;
|
|
int lastHeight = heights.get(0);
|
|
|
|
for (int i = 1; i < heights.size(); i++) {
|
|
int currentHeight = heights.get(i);
|
|
if (currentHeight > lastHeight) {
|
|
lastHeight = currentHeight;
|
|
canSee++;
|
|
}
|
|
}
|
|
|
|
return canSee == clue;
|
|
}
|
|
|
|
private static int getDx(int clueIndex) {
|
|
if (clueIndex >= 4 && clueIndex <= 7) {
|
|
return -1;
|
|
}
|
|
|
|
if (clueIndex >= 12) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private static int getDy(int clueIndex) {
|
|
if (clueIndex <= 3) {
|
|
return 1;
|
|
}
|
|
|
|
if (clueIndex >= 8 && clueIndex <= 11) {
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private static int getStartX(int clueIndex) {
|
|
if (clueIndex < 4) {
|
|
return clueIndex;
|
|
}
|
|
|
|
if (clueIndex <= 7) {
|
|
return 3;
|
|
}
|
|
|
|
if (clueIndex <= 11) {
|
|
return 3 - clueIndex % 4;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
private static int getStartY(int clueIndex) {
|
|
if (clueIndex < 4) {
|
|
return 0;
|
|
}
|
|
|
|
if (clueIndex <= 7) {
|
|
return clueIndex % 4;
|
|
}
|
|
|
|
if (clueIndex <= 11) {
|
|
return 3;
|
|
}
|
|
|
|
return 3 - clueIndex % 4;
|
|
}
|
|
|
|
private static List<Integer> backupHeights(int[][] heights, int clueIndex) {
|
|
List<Integer> backup = new ArrayList<Integer>();
|
|
|
|
int dx = getDx(clueIndex);
|
|
int dy = getDy(clueIndex);
|
|
|
|
for (
|
|
int x = getStartX(clueIndex), y = getStartY(clueIndex);
|
|
x >= 0 && x < 4 && y >= 0 && y < 4;
|
|
x += dx, y += dy
|
|
) {
|
|
backup.add(heights[y][x]);
|
|
}
|
|
|
|
return backup;
|
|
}
|
|
|
|
private static boolean emplaceHeights(
|
|
int[][] heights, int clueIndex, List<Integer> newHeights, boolean force
|
|
) {
|
|
int i = 0;
|
|
int dx = getDx(clueIndex);
|
|
int dy = getDy(clueIndex);
|
|
|
|
for (
|
|
int x = getStartX(clueIndex), y = getStartY(clueIndex);
|
|
x >= 0 && x < 4 && y >= 0 && y < 4;
|
|
x += dx, y += dy
|
|
) {
|
|
if (
|
|
!force && heights[y][x] != 0 && heights[y][x] != newHeights.get(i)
|
|
) {
|
|
return false;
|
|
}
|
|
|
|
heights[y][x] = newHeights.get(i++);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private static boolean solvePuzzle(
|
|
int[][] heights, int[] clues, int clueIndex, boolean ignoreZeroes
|
|
) {
|
|
while (clueIndex < 16 && ((ignoreZeroes && clues[clueIndex] == 0) || (!ignoreZeroes && clues[clueIndex] != 0))) {
|
|
clueIndex++;
|
|
}
|
|
|
|
if (clueIndex >= 16) {
|
|
return true;
|
|
}
|
|
|
|
// create copy of heights to ensure correct resetting
|
|
List<Integer> currentHeights = backupHeights(heights, clueIndex);
|
|
|
|
// iterate through the options
|
|
for (List<Integer> possibleHeights : new PossibleConfigurations(4, clues[clueIndex])) {
|
|
|
|
// emplace heights and if conflict occurs, reset and try next one
|
|
if (!emplaceHeights(heights, clueIndex, possibleHeights, false)) {
|
|
emplaceHeights(heights, clueIndex, currentHeights, true);
|
|
continue;
|
|
}
|
|
|
|
// if no conflict present, try filling out other clues
|
|
if (solvePuzzle(heights, clues, clueIndex + 1, ignoreZeroes)) {
|
|
return true;
|
|
}
|
|
|
|
// otherwise reset heights and try again
|
|
emplaceHeights(heights, clueIndex, currentHeights, true);
|
|
}
|
|
|
|
// if we got here, there is no feasible configuration of buildings
|
|
return false;
|
|
}
|
|
|
|
static int[][] solvePuzzle(int[] clues) {
|
|
var result = new int[4][4];
|
|
solvePuzzle(result, clues, 0, true);
|
|
|
|
// in case there are left zeroes
|
|
solvePuzzle(result, clues, 0, false);
|
|
|
|
return result;
|
|
}
|
|
}
|