Day 2: Password Philosophy
Puzzle Description
Here's another Haxe day, but today's Haxe code was rewritten from the ground up because the original was very confusing.
Solution Summary
- Parse input into a
List
of policy entries -
Count entries where password is valid
- For
part1
, this is counting the number of characters in the password that match the character in the entry. - For
part2
, this is an xor between 2 positions based on whether they match the entry's character.
- For
Part 1
Let's first define what a PolicyEntry
is:
case class PolicyEntry(left: Int, right: Int, of: Char, password: String)
typedef PolicyEntry = {
var min: Int;
var max: Int;
var char: String;
var password: String;
};
Let's then parse our input:
def parse(str: String): List[PolicyEntry] =
str.linesIterator.map:
case s"$lower-$upper $char: $password" =>
assert(char.length == 1)
PolicyEntry(lower.toInt, upper.toInt, char.head, password.trim())
.toList
As always, Haxe really sucks at handling newlines properly. We need to bring in StringTools
to trim the string.
using StringTools;
// ...
static public function parse(str: String): Array<PolicyEntry> {
var policies: Array<PolicyEntry> = [];
for (line in str.trim().split("\n")) {
final reg = ~/(\d+)-(\d+) ([a-z]): ([a-z]+)/;
final goodLine = line.trim();
reg.match(goodLine);
final entry: PolicyEntry = {
min: Std.parseInt(reg.matched(0)),
max: Std.parseInt(reg.matched(1)),
char: reg.matched(3),
password: reg.matched(4)
};
policies.push(entry);
}
return policies;
}
Then we can do part 1, which is just a simple count
:
Range is helpful here and saves us having to write out the comparison manually.
def part1(input: List[PolicyEntry]): Int =
input.count: entry =>
(entry.left to entry.right).contains(entry.password.count(_ == entry.of))
Haxe doesn't really have a count function, but it has filter
, so filter
into length
is acceptable here.
For the main loop though I just use a mutable variable.
Haxe String
also doesn't even have a filter
function, so I have to convert it to an Array<String>
with
split("")
.
static public function part1(policies: Array<PolicyEntry>): Int {
var count = 0;
for (entry in policies) {
final size = entry.password.split("").filter((it) -> it == entry.char).length;
if (size >= entry.min && size <= entry.max) {
count += 1;
}
}
return count;
}
That's part 1.
Part 2
Part 2 is easy, we just replace our test with an xor test.
Scala lets you use the ^
operator on Booleans directly:
def part2(input: List[PolicyEntry]): Int =
input.count: entry =>
val l = entry.password(entry.left - 1) == entry.of
val r = entry.password(entry.right - 1) == entry.of
l ^ r
Haxe doesn't actually have a dedicated Char
class, so I'm just comparing strings here.
The closest is charCodeAt
which returns an Int
.
!=
is the same as an xor.
static public function part2(policies: Array<PolicyEntry>): Int {
var count = 0;
for (entry in policies) {
final l = entry.password.charAt(entry.min - 1) == entry.char;
final r = entry.password.charAt(entry.max - 1) == entry.char;
// xor
if (l != r) {
count += 1;
}
}
return count;
}
Final Code
case class PolicyEntry(left: Int, right: Int, of: Char, password: String)
def parse(str: String): List[PolicyEntry] =
str.linesIterator.map:
case s"$lower-$upper $char: $password" =>
assert(char.length == 1)
PolicyEntry(lower.toInt, upper.toInt, char.head, password.trim())
.toList
def part1(input: List[PolicyEntry]): Int =
input.count: entry =>
(entry.left to entry.right).contains(entry.password.count(_ == entry.of))
def part2(input: List[PolicyEntry]): Int =
input.count: entry =>
val l = entry.password(entry.left - 1) == entry.of
val r = entry.password(entry.right - 1) == entry.of
l ^ r
using StringTools;
typedef PolicyEntry = {
var min: Int;
var max: Int;
var char: String;
var password: String;
};
class Day02 {
static public function parse(str: String): Array<PolicyEntry> {
var policies: Array<PolicyEntry> = [];
for (line in str.trim().split("\n")) {
final reg = ~/(\d+)-(\d+) ([a-z]): ([a-z]+)/;
final goodLine = line.trim();
reg.match(goodLine);
final entry: PolicyEntry = {
min: Std.parseInt(reg.matched(0)),
max: Std.parseInt(reg.matched(1)),
char: reg.matched(3),
password: reg.matched(4)
};
policies.push(entry);
}
return policies;
}
static public function part1(policies: Array<PolicyEntry>): Int {
var count = 0;
for (entry in policies) {
final size = entry.password.split("")
.filter((it) -> it == entry.char).length;
if (size >= entry.min && size <= entry.max) {
count += 1;
}
}
return count;
}
static public function part2(policies: Array<PolicyEntry>): Int {
var count = 0;
for (entry in policies) {
final l = entry.password.charAt(entry.min - 1) == entry.char;
final r = entry.password.charAt(entry.max - 1) == entry.char;
// xor
if (l != r) {
count += 1;
}
}
return count;
}
}
Benchmark
Part 1
Mean |
Error |
|
---|---|---|
JVM |
3.531 ms |
+/- 0.547 ms |
JS |
7.097 ms |
+/- 0.233 ms |
Native |
1.245 ms |
+/- 0.050 ms |
Part 2
Mean |
Error |
|
---|---|---|
JVM |
3.569 ms |
+/- 0.542 ms |
JS |
7.085 ms |
+/- 1.443 ms |
Native |
1.155 ms |
+/- 0.032 ms |