Safe HaskellNone

Day16

Description

General - Main problem here is that the input file is difficult to parse. I broke it up into 3 files (-ranges, -your, -nearby) and parse these files seperately.

Part 1 - After that we just have to filter out the invalid codes from all codes (given by the nearby tickes).

Part 2 - This was ... difficult. No big algorithms involved. Just a lot of careful reading and the realisation that finding the colums is recursive (not unique, when you do it in one go). This was also difficult because you where able to make the testcases pass with a much simpler implementation and then it was hard to understand/see why it is not working for the puzzle input.

But first things first: Let's define data-structures/terminology ...

  • The input has three parts: the ranges of numbers that describe a valid field (or column; see below)
  • The/My ticket. Every number is a field. We need to find out how to associate these numbers with the ranges above
  • Nearby tickets to intersect with ranges to find out which field/column belongs to which range
  • You want to end up with a mapping of range-name to column-number

Now let's talk about the algorithm ...

  • This problem can be solved by building intersections of number sets (we only need any (not empty intersection) and all (full/complete intersection))
  • First we need to find all valid tickets. That's kinda easy. We just take the invalid fields from part1 and remove the tickets that have an invalid field in it
  • Now the real work starts
  • Next we need to realize that we need to look at the valid (nearby) tickets column by column (and need to find out which column fits/matches which range)
  • This should be easy, right? We just intersect the column with the ranges and if all the numbers in the column are in the range we have a match
  • This is when things get tricky. Because this is not the case. The match is not unique. Means a column will match with multiple ranges
  • BUT ... one of the columns will match with exactly one range. And that's the one you are looking for. You then have your first mapping of a range-name to a column-number
  • You then need to remove the range from the ranges and the column from the columns and do it again (recursively, until no more ranges/columns need to be mapped
  • When you are done with the mapping you can just lookup/select the 6 column-numbers, lookup the 6 numbers in your ticket and multiply them
  • Done
Synopsis

Documentation

type Description = String #

type Field = Int #

type Ranges = Map Description [Field] #

type Ticket = [Field] #

type Column = Int #

type Columns = Map Column [Field] #

data Notes #

The note that describes the ticket.

Constructors

Notes Ranges Ticket [Ticket] 

Instances

Instances details
Eq Notes # 
Instance details

Defined in Day16

Methods

(==) :: Notes -> Notes -> Bool

(/=) :: Notes -> Notes -> Bool

Show Notes # 
Instance details

Defined in Day16

Methods

showsPrec :: Int -> Notes -> ShowS

show :: Notes -> String

showList :: [Notes] -> ShowS

input :: String -> Notes #

Read the input file.

invalidFields :: Notes -> [Field] #

Returns the invalid fields.

part1 :: Notes -> Int #

Solve part1.

validTickets :: [Field] -> [Ticket] -> [Ticket] #

Returns the valid tickets.

check :: [Field] -> Columns -> [(Int, Bool)] #

Check for a given range, which cols are valid.

checks :: Ranges -> Columns -> [(Description, [(Column, Bool)])] #

Checks for all ranges, which cols are valid.

unique :: [(Description, [(Column, Bool)])] -> (Description, Column) #

Find the one col, that can be uniquely mapped to a range.

collect :: Ranges -> Columns -> Mappings -> Mappings #

(Recursively) Collect all of the col indexes.

solve :: String -> Notes -> [Int] #

Returns the fields for the word.

part2 :: Notes -> Int #

Solve part2.