8b21ab556a
FossilOrigin-Name: 54a80479319c1ce4c27fdae1169b09159fb624f6d10c5224a338273ec1ea160a
143 lines
4.1 KiB
Forth
143 lines
4.1 KiB
Forth
# Day 2: Password Philosophy
|
|
|
|
Your flight departs in a few days from the coastal
|
|
airport; the easiest way down to the coast from
|
|
here is via toboggan.
|
|
|
|
The shopkeeper at the North Pole Toboggan Rental
|
|
Shop is having a bad day. "Something's wrong with
|
|
our computers; we can't log in!" You ask if you can
|
|
take a look.
|
|
|
|
Their password database seems to be a little
|
|
corrupted: some of the passwords wouldn't have been
|
|
allowed by the Official Toboggan Corporate Policy
|
|
that was in effect when they were chosen.
|
|
|
|
To try to debug the problem, they have created a
|
|
list (your puzzle input) of passwords (according
|
|
to the corrupted database) and the corporate
|
|
policy when that password was set.
|
|
|
|
For example, suppose you have the following list:
|
|
|
|
1-3 a: abcde
|
|
1-3 b: cdefg
|
|
2-9 c: ccccccccc
|
|
|
|
Each line gives the password policy and then the
|
|
password. The password policy indicates the lowest
|
|
and highest number of times a given letter must
|
|
appear for the password to be valid. For example,
|
|
`1-3 a` means that the password must contain `a` at
|
|
least 1 time and at most 3 times.
|
|
|
|
In the above example, 2 passwords are valid. The
|
|
middle password, cdefg, is not; it contains no
|
|
instances of b, but needs at least 1. The first and
|
|
third passwords are valid: they contain one a or
|
|
nine c, both within the limits of their respective
|
|
policies.
|
|
|
|
How many passwords are valid according to their
|
|
policies?
|
|
|
|
----
|
|
|
|
This is an easy problem. Just a little parsing and reduction.
|
|
|
|
`range` is defined to parse the limits of the character. This
|
|
just means splitting the string and converting the pieces to
|
|
a number.
|
|
|
|
The `reduce` word is slighly more complex. It takes the
|
|
password and the character and constructs a combinator for
|
|
use in filtering out other characters.
|
|
|
|
The remaining characters will be counted and compared to
|
|
the range.
|
|
|
|
Basically, an input line like:
|
|
|
|
1-3 a: abcde
|
|
|
|
Becomes:
|
|
|
|
'abcde [ $a eq? ] s:filter s:length #1 #3 n:between?
|
|
|
|
At this point, I just add up the flags (-1 for TRUE), and
|
|
use `n:abs` to get the final count.
|
|
|
|
~~~
|
|
:range
|
|
#0 a:fetch $- s:tokenize [ s:to-number ] a:for-each ;
|
|
|
|
:reduce
|
|
[ #2 a:fetch ] [ #1 a:fetch fetch &eq? curry ] bi s:filter ;
|
|
|
|
:process (na-n)
|
|
[ reduce s:length ] [ range ] bi n:between? + ;
|
|
|
|
#0 'input-day-2 [ ASCII:SPACE s:tokenize process ] file:for-each-line
|
|
n:abs
|
|
~~~
|
|
|
|
----
|
|
|
|
# Part 2
|
|
|
|
While it appears you validated the passwords
|
|
correctly, they don't seem to be what the Official
|
|
Toboggan Corporate Authentication System is expecting.
|
|
|
|
The shopkeeper suddenly realizes that he just
|
|
accidentally explained the password policy rules from
|
|
his old job at the sled rental place down the street!
|
|
The Official Toboggan Corporate Policy actually works
|
|
a little differently.
|
|
|
|
Each policy actually describes two positions in the
|
|
password, where 1 means the first character, 2 means
|
|
the second character, and so on. (Be careful; Toboggan
|
|
Corporate Policies have no concept of "index zero"!)
|
|
Exactly one of these positions must contain the given
|
|
letter. Other occurrences of the letter are irrelevant
|
|
for the purposes of policy enforcement.
|
|
|
|
Given the same example list from above:
|
|
|
|
1-3 a: abcde is valid: position 1 contains a and position 3 does not.
|
|
1-3 b: cdefg is invalid: neither position 1 nor position 3 contains b.
|
|
2-9 c: ccccccccc is invalid: both position 2 and position 9 contain c.
|
|
|
|
How many passwords are valid according to the new
|
|
interpretation of the policies?
|
|
|
|
----
|
|
|
|
So in this variation, I need to check two positions per
|
|
password.
|
|
|
|
Being lazy here, I'm using `reorder` a couple of times
|
|
to restructure the stack. The only thing to remember is
|
|
that indexing is one based, so the word that extracts
|
|
the positions needs to decrement to adjust for Retro's
|
|
zero based indexing.
|
|
|
|
~~~
|
|
:grab-characters
|
|
'abc 'abac reorder + fetch [ + fetch ] dip ;
|
|
|
|
:check
|
|
'abc 'abac reorder eq? [ eq? ] dip xor + ;
|
|
|
|
:positions
|
|
#0 a:fetch $- s:tokenize [ s:to-number n:dec ] a:for-each ;
|
|
|
|
:process (na-n)
|
|
[ #1 a:fetch fetch ] [ #2 a:fetch ] [ positions ] tri
|
|
grab-characters check ;
|
|
|
|
#0 'input-day-2 [ ASCII:SPACE s:tokenize process ] file:for-each-line
|
|
n:abs
|
|
~~~
|