AOC2021/AOC2021/Models/Day8/SSDGenerator.cs
Alexander Sigler e381eb3d37
All checks were successful
continuous-integration/drone/push Build is passing
Added Day 9 code and input
2022-01-28 16:49:14 -08:00

331 lines
12 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
namespace AOC2021.Models.Day8
{
public class SSDGenerator
{
public static string ZERO = "abcefg";
public static string ONE = "cf";
public static string TWO = "acdeg";
public static string THREE = "acdfg";
public static string FOUR = "bcdf";
public static string FIVE = "abdfg";
public static string SIX = "abdefg";
public static string SEVEN = "acf";
public static string EIGHT = "abcdefg";
public static string NINE = "abcdfg";
private string _numbers;
private Dictionary<SSD, SSD?> _lookup;
private Dictionary<SSD, List<SSD>> _possibleLookup;
public SSDGenerator(string numbers)
{
_numbers = numbers;
_lookup = new Dictionary<SSD, SSD?>();
_possibleLookup = new Dictionary<SSD, List<SSD>>();
foreach (SSD ssd in Enum.GetValues(typeof(SSD)))
{
if (ssd != SSD._)
_lookup[ssd] = null;
_possibleLookup[ssd] = new List<SSD>();
}
}
public string ConvertToNumbers(string numbers)
{
var number = "";
foreach (var segments in numbers.Split(" "))
{
var constructedDigit = "";
foreach (var seg in segments)
{
foreach (var l in _lookup)
{
if (l.Value == GetProperCode(seg))
{
constructedDigit += GetProperCode(l.Key);
break;
}
}
}
number += ConvertToDigit(constructedDigit);
}
return number;
}
public void Generate()
{
var one = GetPossibleNumbers(ONE).First();
var seven = GetPossibleNumbers(SEVEN).First();
var AcharDiff = CharDiff(one, seven);
_lookup[SSD.A] = GetProperCode(AcharDiff); //We found the A value
_possibleLookup[SSD.C] = GetPossibleSSD(one); //Can also be #1 or #7 with AcharDiff as removed valued
_possibleLookup[SSD.F] = GetPossibleSSD(one); //Can also be #1 or #7 with AcharDiff as removed valued
//We know that #4 shares two of the same segments(C & F) as #1/#7. So we can remove possible values for that
var four = GetPossibleNumbers(FOUR).First();
//Remove all possible segments for C/F and assign what is left to B/D
_possibleLookup[SSD.B] = GetPossibleSSD(four, _possibleLookup[SSD.C].Select(x => GetProperCode(x)).ToArray());
_possibleLookup[SSD.D] = GetPossibleSSD(four, _possibleLookup[SSD.C].Select(x => GetProperCode(x)).ToArray());
//We have finished the unique digits. Now we move onto digits that have multiple possible values.
//Now we address the ones with 5 segments, #2, #3, #5. They all have the same possible numbers, so just use #2 as lookup
var twoThreeFivePossible = GetPossibleNumbers(TWO);
var simplifiedTwoThreeFive = twoThreeFivePossible.Select(x => RemoveKnownSSD(x)).ToArray();
var possibleValuesForSegmentDG = FindInCommon(simplifiedTwoThreeFive);
_possibleLookup[SSD.D].AddRange(GetPossibleSSD(possibleValuesForSegmentDG)); //Since D already has possible lookups, we add more
_possibleLookup[SSD.G] = GetPossibleSSD(possibleValuesForSegmentDG);
DeducePossibleLookups(); //Since we have overlapping possible values, we deduce the correct ones and simplify
//Now we address the ones with 6 segments, #0, #6, #9. They all have the same possible numbers, so just use #0 as lookup
var zeroSixNinePossible = GetPossibleNumbers(ZERO);
var simplifiedZeroSixNine = zeroSixNinePossible.Select(x => RemoveKnownSSD(x)).ToArray(); //Remove all known values
var possibleValuesForSegmentF = FindInCommon(simplifiedZeroSixNine); //F segment is in common with 0, 6, 9.
_lookup[SSD.F] = GetProperCode(possibleValuesForSegmentF[0]); //Its a single char since its the only one in common with all 3 digits
DeducePossibleLookups();
}
private void DeducePossibleLookups()
{
var reRunDeduce = false;
//Remove any possible lookup that have matched to lookup
foreach (var lookup in _lookup)
{
if (lookup.Value != null)
{
_possibleLookup[lookup.Key].Clear();
}
}
//Remove from Possible lookup any values that are already looked up.
foreach (var pL in _possibleLookup)
{
foreach (var lookup in _lookup.Where(x => x.Value != null))
{
pL.Value.Remove(lookup.Value.GetValueOrDefault());
}
}
//Duplicate possible value check (implies the duplicate value is correct one)
foreach (var pL in _possibleLookup)
{
var duplicateCount = pL.Value.GroupBy(x => x).Where(y => y.Count() > 1).Select(y => y.Key);
if (duplicateCount.Any()) //Means we have a duplicate entry for one of the possible and it means THAT is the correct value
{
_lookup[pL.Key] = duplicateCount.First();
pL.Value.Clear();
reRunDeduce = true;
}
}
//If there is only one possible value implies that it is the answer
foreach (var pL in _possibleLookup)
{
if (pL.Value.Count == 1)
{
_lookup[pL.Key] = pL.Value.First();
pL.Value.Clear();
reRunDeduce = true;
}
}
//Indicates 6 / 7 values are found, meaning the next unused value is it.
var lastOne = _lookup.Where(x => x.Value == null).Select(x => x.Key);
if (lastOne.Count() == 1)
{
var selectedValues = _lookup.Select(x => x.Value);
foreach (SSD ssd in Enum.GetValues(typeof(SSD)))
{
if (ssd == SSD._)
continue;
if (!selectedValues.Contains(ssd))
{
_lookup[lastOne.First()] = ssd;
break;
}
}
}
if (reRunDeduce)
DeducePossibleLookups();
}
private string[] GetPossibleNumbers(string searchingNumber)
{
var possibleNumbers = new List<string>();
foreach (var number in _numbers.Split(" "))
{
if (searchingNumber.Length == number.Trim().Length)
possibleNumbers.Add(String.Concat(number.OrderBy(c => c)));
}
return possibleNumbers.ToArray();
}
private string RemoveKnownSSD(string s)
{
var knownSSDs = _lookup.Select(x => x.Value);
var retVal = "";
foreach (var c in s)
{
if (!knownSSDs.Contains(GetProperCode(c)))
retVal += c;
}
return retVal;
}
private string FindInCommon(string[] values)
{
var retVal = "";
var charArrays = values.Select(x => x.ToCharArray()).ToArray();
var initial = charArrays[0];
foreach (var c in initial)
{
bool contains = true;
for (int i = 1; i < charArrays.Count(); i++)
{
if (!charArrays[i].Contains(c))
contains = false;
}
if (contains)
retVal += c;
}
return retVal;
}
private char CharDiff(string s1, string s2)
{
string diff = "";
var high = s1.Length > s2.Length ? s1 : s2;
var low = s1.Length <= s2.Length ? s1 : s2;
foreach (char c in high)
{
if (low.IndexOf(c) == -1)
diff += c;
}
if (diff.Length != 1)
throw new Exception($"Char Diff has value {diff} for {s1} and {s2}");
return diff[0];
}
private List<SSD> GetPossibleSSD(string value, params char[] removedChars)
{
var retVal = new List<SSD>();
foreach (var c in value)
{
if (!removedChars.Contains(c))
retVal.Add(GetProperCode(c));
}
return retVal;
}
public void PrintOutLookup()
{
foreach (var e in _lookup)
{
if (e.Key == SSD._)
continue;
Console.WriteLine($"{e.Key}: {e.Value}");
}
foreach (var e in _possibleLookup)
{
if (e.Key == SSD._)
continue;
Console.WriteLine($"P_{e.Key}: {string.Join(", ", e.Value)}");
}
}
public string GetProperCode(int x)
{
if (x == 0)
return ZERO;
if (x == 1)
return ONE;
if (x == 2)
return TWO;
if (x == 3)
return THREE;
if (x == 4)
return FOUR;
if (x == 5)
return FIVE;
if (x == 6)
return SIX;
if (x == 7)
return SEVEN;
if (x == 8)
return EIGHT;
if (x == 9)
return NINE;
return "";
}
public SSD GetProperCode(char c)
{
c = c.ToString().ToLower()[0];
if (c == 'a')
return SSD.A;
if (c == 'b')
return SSD.B;
if (c == 'c')
return SSD.C;
if (c == 'd')
return SSD.D;
if (c == 'e')
return SSD.E;
if (c == 'f')
return SSD.F;
if (c == 'g')
return SSD.G;
return SSD._;
}
public char GetProperCode(SSD ssd)
{
if (ssd == SSD.A)
return 'a';
if (ssd == SSD.B)
return 'b';
if (ssd == SSD.C)
return 'c';
if (ssd == SSD.D)
return 'd';
if (ssd == SSD.E)
return 'e';
if (ssd == SSD.F)
return 'f';
if (ssd == SSD.G)
return 'g';
return ' ';
}
private int ConvertToDigit(string s)
{
char[] characters = s.ToArray();
Array.Sort(characters);
s = new string(characters);
if (s.Equals(ZERO))
return 0;
if (s.Equals(ONE))
return 1;
if (s.Equals(TWO))
return 2;
if (s.Equals(THREE))
return 3;
if (s.Equals(FOUR))
return 4;
if (s.Equals(FIVE))
return 5;
if (s.Equals(SIX))
return 6;
if (s.Equals(SEVEN))
return 7;
if (s.Equals(EIGHT))
return 8;
if (s.Equals(NINE))
return 9;
return -1;
}
}
}