0 Comments

Here’s my video with my solutions for the Advent of Code challenge. As usual, let me know in the comments how I could have solved this better.

Here’s my heavy-handed C# and LINQ solution, (drawing on some methods from MoreLinq)

var input = File.ReadAllLines("day5.txt");

var vowels = new[] { 'a', 'e', 'i', 'o', 'u' };
var naughtyStrings = new[] { "ab", "cd", "pq", "xy" };
Predicate<string> hasThreeVowels =
    s => s.Where(c => vowels.Any(v => c == v))
            .Take(3)
            .Count() == 3;
Predicate<string> hasDoubleLetter =
    s => s.Pairwise((a, b) => a == b).Any(x => x);
Predicate<string> containsNaughtyString =
    s => naughtyStrings.Any(n => s.Contains(n));
Predicate<string> isNice =
    s => hasThreeVowels(s) && hasDoubleLetter(s) && !containsNaughtyString(s);

input
    .Where(s => isNice(s))
    .Count()
.Dump("a"); // a = 236

//"aabcdebccfaa"
Predicate<string> containsNonOverlappingPair = s => s
    .Select((c, n) => new { c, n })
    .Pairwise((a, b) => new
    {
        s = new string(new[] { a.c, b.c }),
        n = a.n
    })
    .GroupBy(p => p.s)
    .Where(g => g.Count() > 1
        && g.Any(v => v.n - g.First().n > 1))
    .Any();

Predicate<string> containsDuplicateSeparatedByOne = s => s
    .Select((c, n) => new { c, n })
    .GroupBy(p => p.c)
    .Where(g => g.Count() > 1 
    && g.Pairwise((a,b) => a.n + 2 == b.n).Any(c => c))
    .Any();

Predicate<string> isNiceB = s =>
    containsNonOverlappingPair(s) && containsDuplicateSeparatedByOne(s);
    

input
    .Where(s => isNiceB(s))
    .Count()
.Dump("b"); // b = 51

And here’s a slightly nicer version using Regex (credit to mermop)

var input = File.ReadAllLines("day5.txt");
var naughtyStrings = new[] { "ab", "cd", "pq", "xy" };
Predicate<string> hasThreeVowels = s => Regex.IsMatch(s, @"[aeiou].*[aeiou].*[aeiou]");
Predicate<string> hasDoubleLetter = s => Regex.IsMatch(s, @"(\w)\1+");
Predicate<string> containsNaughtyString = s => Regex.IsMatch(s, @"ab|cd|pq|xy");

Predicate<string> isNice =
    s => hasThreeVowels(s) && hasDoubleLetter(s) && !containsNaughtyString(s);

input
    .Where(s => isNice(s))
    .Count()
.Dump("a"); // a = 236


//"aabcdebccfaa"
Predicate<string> containsNonOverlappingPair = s=> Regex.IsMatch(s,@"(\w{2}).*\1+");
Predicate<string> containsDuplicateSeparatedByOne = s => Regex.IsMatch(s,@"(\w).\1");

Predicate<string> isNiceB = s =>
    containsNonOverlappingPair(s) && containsDuplicateSeparatedByOne(s);
    
input
    .Where(s => isNiceB(s))
    .Count()
.Dump("b"); // b = 51

And finally, the regex solution in F#:

let input = File.ReadAllLines("day5.txt")

let (=~) input pattern = Regex.IsMatch(input, pattern)
   
let hasThreeVowels s = s =~ @"[aeiou].*[aeiou].*[aeiou]"
let hasDoubleLetter s = s =~ @"(\w)\1+"
let containsNaughtyString s = s =~ @"ab|cd|pq|xy"

let isNice s = (hasThreeVowels s) && (hasDoubleLetter s) && (not (containsNaughtyString s))

input
    |> Seq.filter isNice
    |> Seq.length
    |> printf "a: %d"


//"aabcdebccfaa"
let containsNonOverlappingPair s = s =~ @"(\w{2}).*\1+"
let containsDuplicateSeparatedByOne s = s =~ @"(\w).\1"

let isNiceB s =
    (containsNonOverlappingPair s) && (containsDuplicateSeparatedByOne s)


input
    |> Seq.filter isNiceB
    |> Seq.length
    |> printf "b: %d"
   

Want to learn more about LINQ? Be sure to check out my Pluralsight course More Effective LINQ.
Vote on HN
comments powered by Disqus