{-# Language QuasiQuotes #-}
{-|
Module      : Main
Description : Day 13 solution
Copyright   : (c) Eric Mertens, 2023
License     : ISC
Maintainer  : emertens@gmail.com

<https://adventofcode.com/2023/day/13>

>>> :{
:main +
"#.##..##.
..#.##.#.
##......#
##......#
..#.##.#.
..##..##.
#.#.##.#.\n
#...##..#
#....#..#
..##..###
#####.##.
#####.##.
..##..###
#....#..#
"
:}
405
400

-}
module Main (main) where

import Advent (format)
import Data.List (tails, transpose)

-- |
--
-- >>> :main
-- 28895
-- 31603
main :: IO ()
IO ()
main =
 do input <- [format|2023 13 (%s%n)*&%n|]
    print (sum (map (solver 0) input))
    print (sum (map (solver 1) input))

findReflection :: Int -> [String] -> [Int]
findReflection :: Int -> [[Char]] -> [Int]
findReflection Int
differences [[Char]]
xs =
  [ Int
i
  | (Int
i, [[Char]]
l, [[Char]]
r) <- [Int] -> [[[Char]]] -> [[[Char]]] -> [(Int, [[Char]], [[Char]])]
forall a b c. [a] -> [b] -> [c] -> [(a, b, c)]
zip3 [Int
0..] ([[Char]] -> [[[Char]]]
forall a. [a] -> [[a]]
inits' [[Char]]
xs) ([[Char]] -> [[[Char]]]
forall a. [a] -> [[a]]
tails [[Char]]
xs)
  , Bool -> Bool
not ([[Char]] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [[Char]]
l), Bool -> Bool
not ([[Char]] -> Bool
forall a. [a] -> Bool
forall (t :: * -> *) a. Foldable t => t a -> Bool
null [[Char]]
r)
  , let diff :: a -> a -> a
diff a
x a
y = if a
x a -> a -> Bool
forall a. Eq a => a -> a -> Bool
== a
y then a
0 else a
1
  , Int
differences Int -> Int -> Bool
forall a. Eq a => a -> a -> Bool
== ([Char] -> [Char] -> Int) -> [[Char]] -> [[Char]] -> Int
forall c a b. Num c => (a -> b -> c) -> [a] -> [b] -> c
sum2 ((Char -> Char -> Int) -> [Char] -> [Char] -> Int
forall c a b. Num c => (a -> b -> c) -> [a] -> [b] -> c
sum2 Char -> Char -> Int
forall {a} {a}. (Eq a, Num a) => a -> a -> a
diff) [[Char]]
l [[Char]]
r
  ]

solver :: Int -> [String] -> Int
solver :: Int -> [[Char]] -> Int
solver Int
n [[Char]]
xs =
  [Int] -> Int
forall a. HasCallStack => [a] -> a
head (Int -> [[Char]] -> [Int]
findReflection Int
n ([[Char]] -> [[Char]]
forall a. [[a]] -> [[a]]
transpose [[Char]]
xs) [Int] -> [Int] -> [Int]
forall a. [a] -> [a] -> [a]
++ (Int -> Int) -> [Int] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map (Int
100 Int -> Int -> Int
forall a. Num a => a -> a -> a
*) (Int -> [[Char]] -> [Int]
findReflection Int
n [[Char]]
xs))

-- | Like inits, but the prefixes are built up in reverse
-- >>> inits' [1,2,3]
-- [[],[1],[2,1],[3,2,1]]
inits' :: [a] -> [[a]]
inits' :: forall a. [a] -> [[a]]
inits' = ([a] -> a -> [a]) -> [a] -> [a] -> [[a]]
forall b a. (b -> a -> b) -> b -> [a] -> [b]
scanl ((a -> [a] -> [a]) -> [a] -> a -> [a]
forall a b c. (a -> b -> c) -> b -> a -> c
flip (:)) []

-- | Kind of a generalized dot-product. Trims off longer list.
-- >>> sum2 (*) [2,3] [10,100,1000]
-- 320
sum2 :: Num c => (a -> b -> c) -> [a] -> [b] -> c
sum2 :: forall c a b. Num c => (a -> b -> c) -> [a] -> [b] -> c
sum2 a -> b -> c
f [a]
xs [b]
ys = [c] -> c
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum ((a -> b -> c) -> [a] -> [b] -> [c]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith a -> b -> c
f [a]
xs [b]
ys)