WITH parsed AS (
SELECT regexp_split_to_table(input, '\n') AS line FROM aoc_1204
),
numbers AS (
SELECT split_part(line,':',1) AS card_id,
replace(split_part(line,':',2),'|','') AS nrs
FROM parsed
),
arr AS (
SELECT card_id,
nrs,
regexp_split_to_array(ltrim(rtrim(nrs)),'\s') AS nrs_arr
FROM numbers
),
winning AS (
SELECT card_id,
unnest(array_remove(nrs_arr,'')) nr,
ROW_NUMBER() OVER (PARTITION BY card_id) AS row_num
FROM arr
GROUP BY card_id, nr HAVING COUNT(*)>1
ORDER BY card_id
),
winning_points AS (
SELECT ROUND(EXP(SUM(LN(CASE WHEN row_num = 1 THEN row_num ELSE 2 END)))) AS points
FROM winning
GROUP BY card_id
)
SELECT SUM(points)
FROM winning_points;
WITH MUTUALLY RECURSIVE
lines(line string) AS (
SELECT
regexp_split_to_table(input, '\n') AS line
FROM
aoc_1204
),
cards(match string[]) AS (
SELECT
regexp_match(line, 'Card +(\d+): (.*)') AS match
FROM
lines
),
card_parts(card_id int, parts string[]) AS (
SELECT
match[1]::int AS card_id,
regexp_split_to_array(match[2], ' \| ') AS parts
FROM
cards
),
winners(card_id int, val int) AS (
SELECT
card_id,
regexp_split_to_table(trim(parts[1]), '\s+')::int AS val
FROM
card_parts
),
ours(card_id int, val int) AS (
SELECT
card_id,
regexp_split_to_table(trim(parts[2]), '\s+')::int AS val
FROM
card_parts
),
count_winning_numbers(card_id int, count int) AS (
SELECT
ours.card_id,
count(winners.val)::int AS count
FROM
ours LEFT OUTER JOIN winners ON (
ours.card_id = winners.card_id AND
ours.val = winners.val
)
GROUP BY ours.card_id
),
prizes(card_id int, prize_id int) AS (
SELECT
card_id,
prize_id
FROM
count_winning_numbers CROSS JOIN generate_series(card_id + 1, card_id + count) AS prize_id
UNION
SELECT
0 AS card_id,
ours.card_id AS prize_id
FROM
ours
),
multipliers(card_id int, multiplier int) AS (
SELECT
prizes.prize_id AS card_id,
SUM(coalesce(multipliers.multiplier, 1))::int AS multiplier
FROM
prizes left outer JOIN multipliers ON (
prizes.card_id = multipliers.card_id
)
GROUP BY prizes.prize_id
)
SELECT
SUM(multiplier) AS answer
FROM
multipliers;
-- Pre-supposes a view `input(input TEXT)` containing the string FROM AOC
WITH MUTUALLY RECURSIVE
-- PART 0
-- Parse the input as lines of text with line numbers.
lines(line TEXT) AS (
SELECT regexp_split_to_table(input, '\n')
FROM input
),
blocks(card TEXT, wins TEXT, have TEXT) AS (
SELECT
TRIM (regexp_split_to_array(line, '(:|\|)')[1]),
TRIM (regexp_split_to_array(line, '(:|\|)')[2]),
TRIM (regexp_split_to_array(line, '(:|\|)')[3])
FROM
lines
),
parsed(card INT, wins TEXT[], have TEXT[]) AS (
SELECT
regexp_match(card, '[0-9]+')[1]::INT,
regexp_split_to_array(wins, ' '),
regexp_split_to_array(have, ' ')
FROM blocks
),
-- PART 1
-- Count "have"s in "wins" for each row, exponentiate, sum.
matches(card INT, score BIGINT) AS (
SELECT card, (
SELECT COUNT(*)
FROM (
SELECT unnest(wins) w
INTERSECT
SELECT unnest(have) w
)
WHERE w != ''
)
FROM parsed
),
part1(part1 NUMERIC) AS (
SELECT SUM(pow(2, score - 1))::NUMERIC
FROM matches
WHERE score > 0
),
-- PART 2
-- Each card provides a copy of the next `score` cards.
-- This could be prefix sum if we want to be clever ...
expanded(card INT, score BIGINT) AS (
SELECT * FROM matches
UNION ALL
SELECT
matches.card,
matches.score
FROM
expanded,
matches,
generate_series(1, expanded.score) as step
WHERE
expanded.card + step = matches.card
),
part2(part2 BIGINT) AS ( SELECT COUNT(*) FROM expanded)
select * from part1, part2;
Day 4 was brought to you by: @chaas, @doy-materialize, @frankmcsherry, @morsapaes