Posted in:

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 LINQ Best Practices.

Comments

Comment by Sehnsucht

Seems like my previous comment isn't there anymore (not sure why ? maybe too many edit or a mistake from me)

let common predicate =
System.IO.File.ReadAllLines "day05.txt"
|> Array.filter predicate
|> Array.length
// could be Array.sumBy (with 1 or 0 instead of true false in part1 and part2)

let (|Matches|) pattern =
let rgx = Regex (pattern, RegexOptions.Compiled)
fun input -> [for m in rgx.Matches input -> [for g in m.Groups -> g.Value]]

let part1 () = common (function
Matches "(?:[aeiou].*){3}" [_]
& Matches "(.)\1" (_ :: _)
& Matches "ab|cd|pq|xy" [] -> true
| _ -> false)

let part2 () = common (function
Matches "(..).*\1" (_ :: _)
& Matches "(.).\1" (_ :: _) -> true
| _ -> false)

Sehnsucht
Comment by Mark Heath

yes, I saw your first comment come through, but it disappeared again. Nice solution, and as I said, I need to start using Active Patterns more myself

Mark Heath