Advent of Code Day 5 Solved in C# and F#
December 9. 2015 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
Seems like my previous comment isn't there anymore (not sure why ? maybe too many edit or a mistake from me)
Sehnsuchtyes, 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