lyxell.dev

Advent of Code 2024 in SQLite: Day 2

On Day 2 the input was a list of integer sequences, like this:

16 19 21 24 21
15 18 19 22 24 25 25
80 81 83 84 87 89 93
6 7 8 9 10 13 18
60 62 61 64 66 67
76 79 81 84 82 80

In the first part of Day 2 the task was to find the rows where:

And then report the number of rows that met these conditions.

I used a regex to parse the input.

create table T (row text);
.import 02_input.txt T
.load ./regex0.so

-- P contains the problem input
--
-- r is the row index
-- c is the column index
-- v is the value at (r, c)
with P(r, c, v) as (
	select
		T.rowid,
		M.rowid,
		cast(M.match as integer)
	from
		T,
		regex_find_all("\d+", T.row) as M
)

Then I used the window function lag to get a table containing the adjacent items.

-- L contains all adjacent pairs
-- of values for each row
--
-- r is the row
-- v1 is the first value of the pair
-- v2 is the second value of the pair
L(r, v1, v2) as (
	select
		r,
		v,
		lag(v) over (partition by r order by c)
	from P
)

Yielding a result like this:


 r  pred  succ 

 1  16         
 1  19    16   
 1  21    19   
 1  24    21   
 1  21    24   
 2  15         
 2  18    15   
 2  19    18   
 2  22    19   

In Postgres we would have reached for bool_and to aggregate the result now, but SQLite does not have any functions for aggregating booleans. So instead we need to use sum and exploit the fact that booleans can be coerced into (0, 1).

select count(*)
from (
    select r
    from L
    group by r
    having
        sum(abs(pred - succ) > 3) = 0
        and (sum(pred <= succ) = 0 or sum(pred >= succ) = 0)
)

In Part 2 it was allowed to discard any value from the row. That is, if the input was 1 2 3 1 6, we were allowed to discard the second 1 if that made the row meet the criteria. Just like in Part 1 we should then report how many rows meet the criteria.

The solution for Part 2 was very similar to the solution for Part 1, but I added an additional join in the L-table to discard columns.

-- D contains all the columns indices
-- that we will discard
--
-- dr is the row index
-- dc is the column index to discard
D(dr, dc) as (
	select r, c from P
),

-- L contains all adjacent pairs
-- of values partitioned by row and
-- the index of the discarded column
--
-- r is the row index
-- dc is the discarded column
-- pred is the first value of the pair
-- succ is the second value of the pair
L(r, dc, pred, succ) as (
	select
		r,
		dc,
		v,
		lag(v) over (partition by r, dc order by c)
	from P
	join D on r = dr
	where c != dc
)

select count(distinct r)
from (
    select r
    from L
    group by r, dc
    having
        sum(abs(pred - succ) > 3) = 0
        and (sum(pred <= succ) = 0 or sum(pred >= succ) = 0)
)

The runnable scripts can be found on GitHub.