1
0
Fork 0
advent-of-code/2020/day16.php

154 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]];
}