155 lines
4 KiB
PHP
155 lines
4 KiB
PHP
|
<?php
|
||
|
$input_file = '../inputs/day16.txt';
|
||
|
if (file_exists($input_file)) {
|
||
|
$input = file_get_contents($input_file);
|
||
|
if ($input != null) {
|
||
|
$lines = array_map('trim', explode("\n", $input));
|
||
|
print 'Part 1 answer: ' . solve_part_one($lines) . "\n";
|
||
|
print 'Part 2 answer: ' . solve_part_two($lines) . "\n";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
function solve_part_one(array $lines): int
|
||
|
{
|
||
|
[$rules,, $nearby_tickets] = parse_lines($lines);
|
||
|
$invalid_values = [];
|
||
|
|
||
|
foreach ($nearby_tickets as $ticket) {
|
||
|
foreach ($ticket as $field) {
|
||
|
if (field_is_valid($rules, $field) == false)
|
||
|
$invalid_values[] = $field;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return array_sum($invalid_values);
|
||
|
}
|
||
|
|
||
|
function solve_part_two(array $lines): int
|
||
|
{
|
||
|
[$rules, $my_ticket, $nearby_tickets] = parse_lines($lines);
|
||
|
$invalid_tickets = get_invalid_tickets($nearby_tickets, $rules);
|
||
|
$valid_rules = get_valid_rules($rules, $my_ticket, $nearby_tickets, $invalid_tickets);
|
||
|
|
||
|
$accum = 1;
|
||
|
foreach ($valid_rules as $rule_name => $idx) {
|
||
|
if (str_starts_with($rule_name, "departure"))
|
||
|
$accum *= $my_ticket[$idx];
|
||
|
}
|
||
|
|
||
|
return $accum;
|
||
|
}
|
||
|
|
||
|
function get_valid_rules(array $rules, array $my_ticket, array $nearby_tickets, array $invalid_tickets): array
|
||
|
{
|
||
|
$possible_valid_rules = [];
|
||
|
|
||
|
foreach ($rules as $rule) {
|
||
|
for ($i = 0; $i < count($my_ticket); $i++) {
|
||
|
$rule_valid = true;
|
||
|
foreach ($nearby_tickets as $idx => $ticket) {
|
||
|
if (in_array($idx, $invalid_tickets) == false) {
|
||
|
$field = $ticket[$i];
|
||
|
if ((($field >= $rule[1][0] && $field <= $rule[1][1]) || ($field >= $rule[2][0] && $field <= $rule[2][1])) == false) {
|
||
|
$rule_valid = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if ($rule_valid == false)
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
if ($rule_valid) {
|
||
|
$possible_valid_rules[$i][] = $rule[0];
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return get_valid_rules_from_possibilities($possible_valid_rules);
|
||
|
}
|
||
|
|
||
|
function get_valid_rules_from_possibilities(array $possible_valid_rules): array
|
||
|
{
|
||
|
$valid_rules = [];
|
||
|
|
||
|
uasort($possible_valid_rules, fn ($x, $y) => count($x) > count($y) ? 1 : (count($x) < count($y) ? -1 : 0));
|
||
|
for ($idx = 0; $idx < count($possible_valid_rules); $idx++) {
|
||
|
$elem = $possible_valid_rules[array_keys($possible_valid_rules)[$idx]];
|
||
|
if (count($elem) == 1) {
|
||
|
$rule_name = $elem[array_keys($elem)[0]];
|
||
|
$valid_rules[$rule_name] = $idx;
|
||
|
$possible_valid_rules = array_map(fn ($arr) => array_diff($arr, $elem), $possible_valid_rules);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return $valid_rules;
|
||
|
}
|
||
|
|
||
|
function get_invalid_tickets(array $nearby_tickets, array $rules): array
|
||
|
{
|
||
|
$invalid_tickets = [];
|
||
|
foreach ($nearby_tickets as $idx => $ticket) {
|
||
|
$ticket_valid = true;
|
||
|
foreach ($ticket as $field) {
|
||
|
if (field_is_valid($rules, $field) == false)
|
||
|
$ticket_valid = false;
|
||
|
}
|
||
|
|
||
|
if ($ticket_valid == false)
|
||
|
$invalid_tickets[] = $idx;
|
||
|
}
|
||
|
|
||
|
return $invalid_tickets;
|
||
|
}
|
||
|
|
||
|
function field_is_valid(array $rules, int $field): bool
|
||
|
{
|
||
|
$valid = false;
|
||
|
foreach ($rules as $rule) {
|
||
|
if (($field >= $rule[1][0] && $field <= $rule[1][1]) || ($field >= $rule[2][0] && $field <= $rule[2][1])) {
|
||
|
$valid = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return $valid;
|
||
|
}
|
||
|
|
||
|
function parse_lines(array $lines): array
|
||
|
{
|
||
|
$section = 0;
|
||
|
$rules = [];
|
||
|
$my_ticket = [];
|
||
|
$nearby_tickets = [];
|
||
|
foreach ($lines as $line) {
|
||
|
if ($line === '') {
|
||
|
$section += 1;
|
||
|
} else {
|
||
|
if ($line != 'your ticket:' && $line != 'nearby tickets:') {
|
||
|
switch ($section) {
|
||
|
case 0:
|
||
|
$rules[] = parse_rule($line);
|
||
|
break;
|
||
|
case 1:
|
||
|
$my_ticket = array_map(fn ($x) => (int)$x, explode(',', $line));
|
||
|
break;
|
||
|
case 2:
|
||
|
$nearby_tickets[] = array_map(fn ($x) => (int)$x, explode(',', $line));
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return [$rules, $my_ticket, $nearby_tickets];
|
||
|
}
|
||
|
|
||
|
function parse_rule(string $line): array
|
||
|
{
|
||
|
[$name, $values] = array_map('trim', explode(':', $line));
|
||
|
[$min_range, $max_range] = array_map('trim', explode(' or ', $values));
|
||
|
[$min_min, $min_max] = array_map(fn ($x) => (int)$x, explode('-', $min_range));
|
||
|
[$max_min, $max_max] = array_map(fn ($x) => (int)$x, explode('-', $max_range));
|
||
|
|
||
|
return [$name, [$min_min, $min_max], [$max_min, $max_max]];
|
||
|
}
|