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

<https://adventofcode.com/2015/day/15>

We have a list of ingredients with different properties and a scoring
function. We have to combine those ingredients to maximize the score.

-}
module Main where

import Advent.Format (format)
import Data.List (transpose)

main :: IO ()
IO ()
main =
 do [([Char], [([Char], Integer)])]
input <- [format|2015 15 (%s: (%s %ld)&(, )%n)*|]
    let stats :: [[Integer]]
stats = (([Char], [([Char], Integer)]) -> [Integer])
-> [([Char], [([Char], Integer)])] -> [[Integer]]
forall a b. (a -> b) -> [a] -> [b]
map ((([Char], Integer) -> Integer) -> [([Char], Integer)] -> [Integer]
forall a b. (a -> b) -> [a] -> [b]
map ([Char], Integer) -> Integer
forall a b. (a, b) -> b
snd ([([Char], Integer)] -> [Integer])
-> (([Char], [([Char], Integer)]) -> [([Char], Integer)])
-> ([Char], [([Char], Integer)])
-> [Integer]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. ([Char], [([Char], Integer)]) -> [([Char], Integer)]
forall a b. (a, b) -> b
snd) [([Char], [([Char], Integer)])]
input
        n :: Integer
n = Int -> Integer
forall a b. (Integral a, Num b) => a -> b
fromIntegral ([([Char], [([Char], Integer)])] -> Int
forall a. [a] -> Int
forall (t :: * -> *) a. Foldable t => t a -> Int
length [([Char], [([Char], Integer)])]
input)
        possibilities :: [[Integer]]
possibilities = [[Integer]] -> [Integer] -> [Integer]
computeStats [[Integer]]
stats ([Integer] -> [Integer]) -> [[Integer]] -> [[Integer]]
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Integer -> Integer -> [[Integer]]
divisions Integer
n Integer
100

    Integer -> IO ()
forall a. Show a => a -> IO ()
print ([Integer] -> Integer
forall a. Ord a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum (([Integer] -> Integer) -> [[Integer]] -> [Integer]
forall a b. (a -> b) -> [a] -> [b]
map [Integer] -> Integer
score [[Integer]]
possibilities))
    Integer -> IO ()
forall a. Show a => a -> IO ()
print ([Integer] -> Integer
forall a. Ord a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Ord a) => t a -> a
maximum [[Integer] -> Integer
score [Integer]
meal | [Integer]
meal <- [[Integer]]
possibilities, [Integer] -> Integer
forall a. HasCallStack => [a] -> a
last [Integer]
meal Integer -> Integer -> Bool
forall a. Eq a => a -> a -> Bool
== Integer
500])

score ::
  [Integer] {- ^ properties list, calories are last -} ->
  Integer   {- ^ score for recipe                   -}
score :: [Integer] -> Integer
score = [Integer] -> Integer
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
product ([Integer] -> Integer)
-> ([Integer] -> [Integer]) -> [Integer] -> Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Integer] -> [Integer]
forall a. HasCallStack => [a] -> [a]
init

computeStats ::
  [[Integer]] {- ^ properties for all ingredients -} ->
  [Integer]   {- ^ divisions                      -} ->
  [Integer]   {- ^ cumulative properties          -}
computeStats :: [[Integer]] -> [Integer] -> [Integer]
computeStats [[Integer]]
props [Integer]
divs
  = ([Integer] -> Integer) -> [[Integer]] -> [Integer]
forall a b. (a -> b) -> [a] -> [b]
map (Integer -> Integer -> Integer
forall a. Ord a => a -> a -> a
max Integer
0 (Integer -> Integer)
-> ([Integer] -> Integer) -> [Integer] -> Integer
forall b c a. (b -> c) -> (a -> b) -> a -> c
. [Integer] -> Integer
forall a. Num a => [a] -> a
forall (t :: * -> *) a. (Foldable t, Num a) => t a -> a
sum)              -- compute sum of each property
  ([[Integer]] -> [Integer]) -> [[Integer]] -> [Integer]
forall a b. (a -> b) -> a -> b
$ [[Integer]] -> [[Integer]]
forall a. [[a]] -> [[a]]
transpose                      -- compute lists of each property
  ([[Integer]] -> [[Integer]]) -> [[Integer]] -> [[Integer]]
forall a b. (a -> b) -> a -> b
$ (Integer -> [Integer] -> [Integer])
-> [Integer] -> [[Integer]] -> [[Integer]]
forall a b c. (a -> b -> c) -> [a] -> [b] -> [c]
zipWith ((Integer -> Integer) -> [Integer] -> [Integer]
forall a b. (a -> b) -> [a] -> [b]
map ((Integer -> Integer) -> [Integer] -> [Integer])
-> (Integer -> Integer -> Integer)
-> Integer
-> [Integer]
-> [Integer]
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
(*)) [Integer]
divs [[Integer]]
props -- scale up properties by ingredient

divisions ::
  Integer     {- ^ number of divisions -} ->
  Integer     {- ^ amount to divide    -} ->
  [[Integer]] {- ^ all possible divisions -}
divisions :: Integer -> Integer -> [[Integer]]
divisions Integer
1   Integer
n = [[Integer
n]]
divisions Integer
cnt Integer
n =
  do Integer
x  <- [Integer
1..Integer
nInteger -> Integer -> Integer
forall a. Num a => a -> a -> a
-Integer
cntInteger -> Integer -> Integer
forall a. Num a => a -> a -> a
+Integer
1]
     [Integer]
xs <- Integer -> Integer -> [[Integer]]
divisions (Integer
cnt Integer -> Integer -> Integer
forall a. Num a => a -> a -> a
- Integer
1) (Integer
nInteger -> Integer -> Integer
forall a. Num a => a -> a -> a
-Integer
x)
     [Integer] -> [[Integer]]
forall a. a -> [a]
forall (m :: * -> *) a. Monad m => a -> m a
return (Integer
xInteger -> [Integer] -> [Integer]
forall a. a -> [a] -> [a]
:[Integer]
xs)