01020304050607080910111213141516171819202122232425

Advent of Code

2020/16

Ticket Translation

in C#

by encse

As you're walking to yet another connecting flight, you realize that one of the legs of your re-routed trip coming up is on a high-speed train. However, the train ticket you were given is in a language you don't understand. You should probably figure out what it says before you get to the train station after the next flight.

Unfortunately, you can't actually read the words on the ticket. You can, however, read the numbers, and so you figure out the fields these tickets must have and the valid ranges for values in those fields.

Read the full puzzle.

using System;
using System.Linq;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace AdventOfCode.Y2020.Day16;

record Field(string name, Func<int, bool> isValid);
record Problem(Field[] fields, int[][] tickets);

[ProblemName("Ticket Translation")]
class Solution : Solver {

    Field[] FieldCandidates(IEnumerable<Field> fields, params int[] values) =>
        fields.Where(field => values.All(field.isValid)).ToArray();

    public object PartOne(string input) {
        var problem = Parse(input);
        // add the values that cannot be associated with any of the fields
        return (
            from ticket in problem.tickets 
            from value in ticket 
            where !FieldCandidates(problem.fields, value).Any() 
            select value
        ).Sum();
    }

    public object PartTwo(string input) {

        var problem = Parse(input);
        // keep valid tickets only
        var tickets = (
            from ticket in problem.tickets 
            where ticket.All(value => FieldCandidates(problem.fields, value).Any()) 
            select ticket
        ).ToArray();

        // The problem is set up in a way that we can always find a column
        // that has just one field-candidate left.

        var fields = problem.fields.ToHashSet();
        var columns = Enumerable.Range(0, fields.Count).ToHashSet();

        var res = 1L;
        while (columns.Any()) {
            foreach (var column in columns) {
                var valuesInColumn = (from ticket in tickets select ticket[column]).ToArray();
                var candidates = FieldCandidates(fields, valuesInColumn);
                if (candidates.Length == 1) {
                    var field = candidates.Single();
                    fields.Remove(field);
                    columns.Remove(column);
                    if (field.name.StartsWith("departure")) {
                        res *= valuesInColumn.First();
                    }
                    break;
                }
            }
        }
        return res;
    }

    Problem Parse(string input) {
        int[] parseNumbers(string line) => (      
            from m in Regex.Matches(line, "\\d+") // take the consecutive ranges of digits
            select int.Parse(m.Value)             // convert them to numbers
        ).ToArray();

        var blocks = (
            from block in input.Split("\n\n")     // blocks are delimited by empty lines
            select block.Split("\n")              // convert them to lines
        ).ToArray();
        
        var fields = (                          
            from line in blocks.First()           // line <- ["departure location: 49-920 or 932-950", ...]
            let bounds = parseNumbers(line)       // bounds = [49, 920, 932, 950]
            select 
                new Field(
                    line.Split(":")[0],           // "departure location"
                    n => 
                        n >= bounds[0] && n <= bounds[1] || 
                        n >= bounds[2] && n <= bounds[3]
                )
        ).ToArray();

        var tickets = (                         
            from block in blocks.Skip(1)          // ticket information is in the second and third blocks 
            let numbers = block.Skip(1)           // skip "your ticket:" / "nearby tickets:"
            from line in numbers                  // line <- ["337,687,...", "223,323,...", ...]
            select parseNumbers(line)             // [337, 687, 607]
        ).ToArray();

        return new Problem(fields, tickets);
    }
}

Please ☆ my repo if you like it!

© 2025 Advent of Code is a registered trademark in the US Images provided by Bing image creator