|
|
|
@ -0,0 +1,286 @@
|
|
|
|
|
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 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
|
|
|
|
|
|
|
|
|
|
PrintOutLookup();
|
|
|
|
|
//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();
|
|
|
|
|
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
|
|
|
|
|
PrintOutLookup();
|
|
|
|
|
DeducePossibleLookups();
|
|
|
|
|
PrintOutLookup();
|
|
|
|
|
foreach (var l in simplifiedZeroSixNine)
|
|
|
|
|
Console.WriteLine(l);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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 ' ';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|