diff --git a/2021/README.md b/2021/README.md index 5df7fb0..f92b694 100644 --- a/2021/README.md +++ b/2021/README.md @@ -11,7 +11,7 @@ | 05 | ✓ | ✓ | [Nim] | | 06 | ✓ | ✓ | [Inko] | | 07 | ✓ | ✓ | [Swift] | -| 08 | ✓ | | [C#] | +| 08 | ✓ | ✓ | [C#] | | 09 | ✓ | ✓ | [C] | | 10 | ✓ | ✓ | [OCaml] | | 11 | | | | diff --git a/2021/day-08/common.cs b/2021/day-08/common.cs index 11865a0..6619082 100644 --- a/2021/day-08/common.cs +++ b/2021/day-08/common.cs @@ -43,4 +43,50 @@ namespace Common { } } } + + public static class Permutation { + public static IEnumerable Permutations(this IEnumerable input) { + return Permute(input, Enumerable.Empty()); + } + + private static IEnumerable Permute(IEnumerable memo, IEnumerable pfx) { + if (memo.Any()) { + return + from t in memo.Select((a, b) => (a, b)) + let next_memo = memo.Take(t.Item2).Concat(memo.Skip(t.Item2 + 1)).ToArray() + let next_pfx = pfx.Append(t.Item1) + from perms in Permute(next_memo, next_pfx) + select perms; + + } else { + return new[] { pfx.ToArray() }; + } + } + } + + public abstract class Option { + private Option() {} + + public abstract bool IsSome(); + + public bool GetValue(out T val) { + if (this is Some s) { + val = s.Value; + return true; + } + + val = default(T); + return false; + } + + public sealed class Some : Option { + public Some(T val) => Value = val; + public T Value { get; } + public override bool IsSome() => true; + } + + public sealed class None : Option { + public override bool IsSome() => false; + } + } } diff --git a/2021/day-08/part_two.cs b/2021/day-08/part_two.cs new file mode 100644 index 0000000..06fce16 --- /dev/null +++ b/2021/day-08/part_two.cs @@ -0,0 +1,137 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +using Common; +using static Common.SegmentDisplay; +using static Common.Permutation; + +class PartTwo { + private static readonly HashSet[] VALID_POSITIONS = { + new HashSet() {0, 1, 2, 4, 5, 6}, + new HashSet() {2, 5}, + new HashSet() {0, 2, 3, 4, 6}, + new HashSet() {0, 2, 3, 5, 6}, + new HashSet() {1, 2, 3, 5}, + new HashSet() {0, 1, 3, 5, 6}, + new HashSet() {0, 1, 3, 4, 5, 6}, + new HashSet() {0, 2, 5}, + new HashSet() {0, 1, 2, 3, 4, 5, 6}, + new HashSet() {0, 1, 2, 3, 5, 6}, + }; + + public static void Main(string[] args) { + var seg_display = new SegmentDisplay(args[0]); + + var output_sum = seg_display.Wirings.Sum(w => OutputNumber(w)); + + Console.WriteLine(output_sum); + } + + private static int OutputNumber(Wiring wiring) { + var inputs = wiring.Inputs.OrderBy(x => x.Count).ToList(); + var digit = 0; + + if (Analyze(inputs).GetValue(out var segments)) { + var digit_idx = wiring.Outputs.Count - 1; + + foreach (var output in wiring.Outputs) { + var segment_digit = GetOutputDigit(output, segments); + + digit += segment_digit * (int)Math.Pow(10, digit_idx); + + digit_idx--; + } + } + + return digit; + } + + private static int GetOutputDigit(HashSet output, Dictionary segments) { + var mapped_output = output.Select(v => segments[v]); + + switch (output.Count) { + case 2: return 1; + case 3: return 7; + case 4: return 4; + case 7: return 8; + case 5: + foreach (var n in new[]{2, 3, 5}) { + if (!VALID_POSITIONS[n].Except(mapped_output).Any()) return n; + } + break; + case 6: + foreach (var n in new[]{0, 6, 9}) { + if (!VALID_POSITIONS[n].Except(mapped_output).Any()) return n; + } + break; + default: + throw new Exception("Invalid output length"); + } + + throw new Exception("No valid mapping for output"); + } + + private static Option> Analyze(IEnumerable> inputs) { + return Analyze(inputs, new Dictionary()); + } + + private static Option> Analyze( + IEnumerable> inputs, + Dictionary segments + ) { + if (!inputs.Any()) return new Option>.Some(segments); + + var input = inputs.First(); + var segment_count = input.Count; + var unmapped_input = input.Except(segments.Keys); + var mapped_input = input.Intersect(segments.Keys).Select(v => segments[v]); + + switch (segment_count) { + case 2: return Analyze(1, unmapped_input, mapped_input, inputs, segments); + case 3: return Analyze(7, unmapped_input, mapped_input, inputs, segments); + case 4: return Analyze(4, unmapped_input, mapped_input, inputs, segments); + case 7: return new Option>.Some(segments); + case 5: + foreach (var n in new[]{2, 3, 5}) { + var retval = Analyze(n, unmapped_input.ToList(), mapped_input.ToList(), inputs, segments); + if (retval.IsSome()) return retval; + } + break; + case 6: + foreach (var n in new[]{0, 6, 9}) { + var retval = Analyze(n, unmapped_input.ToList(), mapped_input.ToList(), inputs, segments); + if (retval.IsSome()) return retval; + } + break; + default: + throw new Exception("Invalid input length"); + } + + return new Option>.None(); + } + + private static Option> Analyze( + int num, + IEnumerable unmapped_input, + IEnumerable mapped_input, + IEnumerable> inputs, + Dictionary segments + ) { + if (!mapped_input.Except(VALID_POSITIONS[num]).Any()) { + foreach (var perm in Permutations(unmapped_input)) { + var new_segments = new Dictionary(segments); + + foreach (var pair in perm.Zip(VALID_POSITIONS[num].Except(mapped_input), (a, b) => (a, b))) { + new_segments.Add(pair.Item1, pair.Item2); + } + + var retval = Analyze(inputs.Skip(1).ToList(), new_segments); + + if (retval.IsSome()) return retval; + } + } + + return new Option>.None(); + } +} diff --git a/README.md b/README.md index 6201c1a..e188ad2 100644 --- a/README.md +++ b/README.md @@ -8,4 +8,4 @@ - [2018](2018/README.md) (0% completed) - [2019](2019/README.md) (0% completed) - [2020](2020/README.md) (16% completed) -- [2021](2021/README.md) (38% completed) +- [2021](2021/README.md) (40% completed)