1
0
Fork 0
mirror of https://gitlab.com/mfocko/CodeWars.git synced 2024-11-08 18:49:07 +01:00
CodeWars/4kyu/4_by_4_skyscrapers/solution.cs
Matej Focko fc899b0b02
chore: initial commit
Signed-off-by: Matej Focko <mfocko@redhat.com>
2021-12-28 16:19:58 +01:00

181 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;
}
}