mirror of
https://gitlab.com/mfocko/CodeWars.git
synced 2024-11-22 08:33:48 +01:00
182 lines
5.1 KiB
C#
182 lines
5.1 KiB
C#
|
using System;
|
||
|
using System.Collections.Generic;
|
||
|
using System.Linq;
|
||
|
|
||
|
public class Skyscrapers {
|
||
|
private static IEnumerable<List<T>> Permutations<T>(List<T> initial)
|
||
|
where T: IComparable<T> {
|
||
|
yield return initial;
|
||
|
|
||
|
var hasNext = initial.Count > 1;
|
||
|
while (hasNext) {
|
||
|
var k = 0;
|
||
|
var l = 0;
|
||
|
hasNext = false;
|
||
|
|
||
|
for (var i = initial.Count - 1; i > 0; i--) {
|
||
|
if (initial[i].CompareTo(initial[i - 1]) > 0) {
|
||
|
k = i - 1;
|
||
|
hasNext = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
for (var i = initial.Count - 1; i > k; i--) {
|
||
|
if (initial[i].CompareTo(initial[k]) > 0) {
|
||
|
l = i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
(initial[k], initial[l]) = (initial[l], initial[k]);
|
||
|
initial.Reverse(k + 1, initial.Count - k - 1);
|
||
|
|
||
|
if (hasNext) {
|
||
|
yield return initial;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static bool IsValid(List<int> heights, int clue) {
|
||
|
var canSee = 1;
|
||
|
var lastHeight = heights[0];
|
||
|
|
||
|
for (var i = 1; i < heights.Count; i++) {
|
||
|
var currentHeight = heights[i];
|
||
|
if (currentHeight > lastHeight) {
|
||
|
lastHeight = currentHeight;
|
||
|
canSee++;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return canSee == clue;
|
||
|
}
|
||
|
|
||
|
private static IEnumerable<List<int>> PossibleHeights(int size, int clue) {
|
||
|
var initial = new List<int>();
|
||
|
for (var i = 0; i < size; i++) {
|
||
|
initial.Add(i + 1);
|
||
|
}
|
||
|
|
||
|
if (clue == 0) {
|
||
|
return Permutations(initial);
|
||
|
}
|
||
|
|
||
|
return Permutations(initial).Where(heights => IsValid(heights, clue));
|
||
|
}
|
||
|
|
||
|
private static int Size {
|
||
|
get => 4;
|
||
|
}
|
||
|
|
||
|
private static (int, int) GetDiffs(int clueIndex) {
|
||
|
if (clueIndex < Size) {
|
||
|
return (0, 1);
|
||
|
} else if (clueIndex < 2 * Size) {
|
||
|
return (-1, 0);
|
||
|
} else if (clueIndex < 3 * Size) {
|
||
|
return (0, -1);
|
||
|
}
|
||
|
return (1, 0);
|
||
|
}
|
||
|
|
||
|
private static (int, int) GetStarts(int clueIndex) {
|
||
|
if (clueIndex < Size) {
|
||
|
return (clueIndex, 0);
|
||
|
} else if (clueIndex < 2 * Size) {
|
||
|
return (Size - 1, clueIndex % Size);
|
||
|
} else if (clueIndex < 3 * Size) {
|
||
|
return (Size - 1 - clueIndex % Size, Size - 1);
|
||
|
}
|
||
|
return (0, Size - 1 - clueIndex % Size);
|
||
|
}
|
||
|
|
||
|
private static List<int> BackupHeights(int[][] heights, int clueIndex) {
|
||
|
var backup = new List<int>();
|
||
|
|
||
|
var (dx, dy) = GetDiffs(clueIndex);
|
||
|
for (
|
||
|
var (x, y) = GetStarts(clueIndex);
|
||
|
x >= 0 && x < Size && y >= 0 && y < Size;
|
||
|
x += dx, y += dy
|
||
|
) {
|
||
|
backup.Add(heights[y][x]);
|
||
|
}
|
||
|
|
||
|
return backup;
|
||
|
}
|
||
|
|
||
|
private static bool EmplaceHeights(
|
||
|
int[][] heights, int clueIndex, List<int> newHeights, bool force
|
||
|
) {
|
||
|
int i = 0;
|
||
|
|
||
|
var (dx, dy) = GetDiffs(clueIndex);
|
||
|
for (
|
||
|
var (x, y) = GetStarts(clueIndex);
|
||
|
x >= 0 && x < Size && y >= 0 && y < Size;
|
||
|
x += dx, y += dy
|
||
|
) {
|
||
|
if (
|
||
|
!force && heights[y][x] != 0 && heights[y][x] != newHeights[i]
|
||
|
) {
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
heights[y][x] = newHeights[i++];
|
||
|
}
|
||
|
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
private static bool SolvePuzzle(
|
||
|
int[][] heights, int[] clues, int clueIndex, bool ignoreZeroes
|
||
|
) {
|
||
|
while (clueIndex < 4 * Size && (
|
||
|
(ignoreZeroes && clues[clueIndex] == 0) || (!ignoreZeroes && clues[clueIndex] != 0)
|
||
|
)) {
|
||
|
clueIndex++;
|
||
|
}
|
||
|
|
||
|
if (clueIndex >= 4 * Size) {
|
||
|
return true;
|
||
|
}
|
||
|
|
||
|
// create copy of heights to ensure correct resetting
|
||
|
var currentHeights = BackupHeights(heights, clueIndex);
|
||
|
|
||
|
// iterate through the options
|
||
|
foreach (var possibleHeights in PossibleHeights(Size, 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;
|
||
|
}
|
||
|
|
||
|
public static int[][] SolvePuzzle(int[] clues) {
|
||
|
var result = new int[Size][];
|
||
|
for (var i = 0; i < Size; i++) {
|
||
|
result[i] = new int[Size];
|
||
|
}
|
||
|
SolvePuzzle(result, clues, 0, true);
|
||
|
|
||
|
// in case there are left zeroes
|
||
|
SolvePuzzle(result, clues, 0, false);
|
||
|
|
||
|
return result;
|
||
|
}
|
||
|
}
|