It’s already great you made it through if you’re new to Elixir!
And it’s a also great and fun way to learn
defmodule Day04 do
import Enum, only: [filter: 2, chunk_by: 2, any?: 2, sort: 1]
def run(cnd), do: 124_075..580_769 |> filter(&valid?(Integer.digits(&1), cnd)) |> length()
defp valid?(ds, cnd), do: chunk_by(ds, & &1) |> any?(&cnd.(length(&1))) and sort(ds) == ds
end
IO.inspect(Day04.run(&(&1 >= 2)), label: "part 1")
IO.inspect(Day04.run(&(&1 == 2)), label: "part 2")
My take: https://github.com/egze/aoc/blob/master/lib/aoc/y2019/d4.ex
Used some Guards and Flow. Compared to Enum
, Flow
was 2.5 times faster on my 2,6 GHz Quad-Core Intel Core i7.
That is really impressive
My solution: https://github.com/mkasztelnik/elixir-adventofcode-2019/blob/master/lib/advent_of_code/day_04.ex
My solution:
defmodule Day4 do
def valid?(num) when is_number(num) do
num
|> Integer.digits()
|> valid?()
end
def valid?([a,b,c,d,e,f]) do
a <= b && b <= c && c <= d && d <= e && e <= f &&
((a == b && b != c) ||
(a != b && b == c && c != d) ||
(b != c && c == d && d != e) ||
(c != d && d == e && d != f) ||
(d != e && e == f))
end
end
Since the input range was so small I just used a range and Stream.filter
to check if the input was valid or not then counted up the results. Link to the repo.
A video where I misread the specs (again!) and lose at least 15 minutes of your time developing crap we won’t need. Also I have some Common Test stuff in there as a short demo, and thought of optimizations after the fact: https://gist.github.com/ferd/5d8c2997bb2b834af302e4856681d91f
It is very unelegant and I am not a happy camper! At least it handles arbitrary password lengths!
I thought this was MY solution when I clicked that link, we had almost the same naming also
I finally found some time to sit down and do todays task:
I do not quite like it, but I rarely really enjoy those “password validation” tasks that happen every year again.
Today much more simple, here’s my solution.
@bossek: really liked your super compact and clean solution!
@ferd: thanks for making these great videos!
I am really humbled by some of your cool uses of Enum.chunk_every
…
I did really like using with
instead of a bunch of &&
… it may be slower, (I don’t know!) but it still short-circuits.
defmodule Advent2019.Day4 do
@moduledoc "https://adventofcode.com/2019/day/4"
@behaviour Advent
def setup do
387638..919123
end
@doc """
iex> has_double([1, 1, 1, 1, 1, 1])
true
iex> has_double([1, 2, 3, 4, 5, 6])
false
iex> has_double([1, 2, 3, 3, 5, 6])
true
"""
def has_double([a, b, c, d, e, f]) do
a == b || b == c || c == d || d == e || e == f
end
@doc """
iex> no_triplets([1, 1, 1, 1, 1, 1])
false
iex> no_triplets([1, 2, 3, 4, 5, 6])
false
iex> no_triplets([1, 2, 3, 3, 5, 6])
true
"""
def no_triplets([a, b, c, d, e, f]) do
a == b && !Enum.any?([c, d, e, f], & &1 == a) ||
b == c && !Enum.any?([a, d, e, f], & &1 == b) ||
c == d && !Enum.any?([a, b, e, f], & &1 == c) ||
d == e && !Enum.any?([a, b, c, f], & &1 == d) ||
e == f && !Enum.any?([a, b, c, d], & &1 == e)
end
@doc """
- It is a six-digit number.
- The value is within the range given in your puzzle input.
- Two adjacent digits are the same (like 22 in 122345).
- Going from left to right, the digits never decrease; they only ever
increase or stay the same (like 111123 or 135679).
iex> valid_password?(111111, &has_double/1)
true
iex> valid_password?(223450, &has_double/1)
false
iex> valid_password?(123789, &has_double/1)
false
"""
def valid_password?(number, func) do
[a, b, c, d, e, f] = list_of_integers =
for div <- [1, 10, 100, 1_000, 10_000, 100_000] do
number
|> Integer.floor_div(div)
|> Integer.mod(10)
end
|> Enum.reverse
with true <- a <= b,
true <- b <= c,
true <- c <= d,
true <- d <= e,
true <- e <= f,
true <- func.(list_of_integers)
do
true
else
false -> false
end
end
@doc """
How many different passwords within the range given in your puzzle input meet
these criteria?
"""
def p1(range) do
range
|> Stream.filter(fn(num) -> valid_password?(num, &has_double/1) end)
|> Enum.count
end
def p2(range) do
range
|> Stream.filter(fn(num) -> valid_password?(num, &no_triplets/1) end)
|> Enum.count
end
end
Nice solutions everyone! Mine is kind of straight-forward but still way faster than my Ruby solution.
defmodule Day4 do
def part1, do: run(fn repeated_count -> Enum.any?(repeated_count, &(&1 > 1)) end)
def part2, do: run(fn repeated_count -> Enum.any?(repeated_count, &(&1 == 2)) end)
def run(filter) do
372_037..905_157
|> Stream.filter(&is_not_decreasing/1)
|> Stream.filter(&(count_repeating_digits(&1) |> Map.values() |> filter.()))
|> Enum.count()
end
defp is_not_decreasing(n), do: ind(-1, Integer.digits(n))
defp ind(last, [head|tail]) when last <= head, do: ind(head, tail)
defp ind(_last, []), do: true
defp ind(_last, _list), do: false
def count_repeating_digits(n), do: crd(%{}, -1, Integer.digits(n))
def crd(map, last, [head|tail]) when last == head, do: crd(Map.update(map, head, 2, &(&1 + 1)), head, tail)
def crd(map, _last, [head|tail]), do: crd(map, head, tail)
def crd(map, _last, []), do: map
end
Day4.part1() |> IO.puts()
Day4.part2() |> IO.puts()
A friend and I came up with this solution that only uses regexes in awk to work:
seq $START $STOP | awk '/([^0]|^)00([^0]+|$)|([^1]|^)11([^1]+|$)|([^2]|^)22([^2]+|$)|([^3]|^)33([^3]+|$)|([^4]|^)44([^4]+|$)|([^5]|^)55([^5]+|$)|([^6]|^)66([^6]+|$)|([^7]|^)77([^7]+|$)|([^8]|^)88([^8]+|$)|([^9]|^)99([^9]+|$)/ && !/[1-9]0|[2-9][01]|[3-9][0-2]|[4-9][0-3]|[5-9][0-4]|[6-9][0-5]|[7-9][0-6]|[89][0-7]|9[0-8]/ {print $0}' | wc -l
It’s about 3x slower than an actual code solution, but it’s surprisingly fast, and I believe it works on any input string 4 digits or longer.
I decided to my solution and do unholy things with for comprehensions
defmodule Day4 do
def part_1(range) do
for n <- range,
[a, b, c, d, e, f] = digits = Integer.digits(n),
# has a pair
digits |> Enum.chunk_every(2, 1) |> Enum.any?(&match?([x, x], &1)),
# never decrease
a <= b && b <= c && c <= d && d <= e && e <= f,
reduce: 0,
do: (a -> a + 1)
end
def part_2(range) do
for n <- range,
[a, b, c, d, e, f] = digits = Integer.digits(n),
# never decrease
a <= b && b <= c && c <= d && d <= e && e <= f,
# at least an exact pair
digits |> Enum.chunk_by(& &1) |> Enum.any?(&match?([x, x], &1)),
reduce: 0,
do: (a -> a + 1)
end
end
Upvote for the match?/2
. I didn’t know that.
This has been my favorite problem so far. I solved it with a regex.
My solution is at https://github.com/simon-wolf/advent-of-code-2019/tree/master/day_04
I really enjoyed this one and I went for pattern matching as the central logic but I also took the opportunity to allow my validation functions to be daisy-chained in a pipeline.
My solution for day 4 in Gleam is here
The most common mistake I make when writing Gleam is typing true
or false
instead of True
or False
when pattern matching . This compiles just fine but silently fails because e.g. when
false
is first, it always matches and binds the value to first
edit: this is not a big problem because Erlang warns about unused variables, but in my case these were buried between other Erlang warnings
edit2: Gleam may eventually have exhaustiveness checks, it’s being researched: https://github.com/gleam-lang/gleam/issues/34
I went for a quick and dirty approach, using just a for comprehension and some Enum functions:
defmodule PasswordDigger do
@moduledoc """
Documentation for PasswordDigger.
A function to calc all possible combinations
given a set of rules:
- each digit is equal or higher than previous
- six digits
- at least one pair (two same digits)
- range: 109165-576723
"""
def part1 do
items =
for a <- 1..5,
b <- a..9,
c <- b..9,
d <- c..9,
e <- d..9,
f <- e..9,
Integer.undigits([a, b, c, d, e, f]) < 576_723,
Enum.uniq([a, b, c, d, e, f]) != [a, b, c, d, e, f],
do: Integer.undigits([a, b, c, d, e, f])
IO.puts("Number of possible passwords is: #{Enum.count(items)}")
end
def part2 do
large_group_filter = fn password ->
password
|> Enum.group_by(& &1)
|> Enum.any?(fn {_k, vs} -> Enum.count(vs) == 2 end)
end
items =
for a <- 1..5,
b <- a..9,
c <- b..9,
d <- c..9,
e <- d..9,
f <- e..9,
Integer.undigits([a, b, c, d, e, f]) < 576_723,
Enum.uniq([a, b, c, d, e, f]) != [a, b, c, d, e, f],
large_group_filter.([a, b, c, d, e, f]),
do: Integer.undigits([a, b, c, d, e, f])
IO.puts("Number of possible passwords is: #{Enum.count(items)}")
end
end