This commit is contained in:
Jonathan Chan 2018-12-24 18:10:18 -08:00
parent 3d3559bb38
commit 270ef6c664
4 changed files with 163 additions and 4 deletions

View File

@ -35,5 +35,5 @@ Now located in this repository's wiki.
| 21 | ~ 1 |
| 22 | 23 |
| 23 | 128 |
| 24 | |
| 24 | ~ 2 |
| 25 | |

View File

@ -1,5 +1,5 @@
-- Some possibly-useful GHC extensions:
{-# LANGUAGE BangPatterns, TupleSections, ScopedTypeVariables, GADTs, GeneralizedNewtypeDeriving #-}
{-# LANGUAGE BangPatterns, ViewPatterns, TupleSections, ScopedTypeVariables, GADTs, GeneralizedNewtypeDeriving #-}
module Main where

23
input/24.txt Normal file
View File

@ -0,0 +1,23 @@
Immune System:
5711 units each with 6662 hit points (immune to fire; weak to slashing) with an attack that does 9 bludgeoning damage at initiative 14
2108 units each with 8185 hit points (weak to radiation, bludgeoning) with an attack that does 36 slashing damage at initiative 13
1590 units each with 3940 hit points with an attack that does 24 cold damage at initiative 5
2546 units each with 6960 hit points with an attack that does 25 slashing damage at initiative 2
1084 units each with 3450 hit points (immune to bludgeoning) with an attack that does 27 slashing damage at initiative 11
265 units each with 8223 hit points (immune to radiation, bludgeoning, cold) with an attack that does 259 cold damage at initiative 12
6792 units each with 6242 hit points (immune to slashing; weak to bludgeoning, radiation) with an attack that does 9 slashing damage at initiative 18
3336 units each with 12681 hit points (weak to slashing) with an attack that does 28 fire damage at initiative 6
752 units each with 5272 hit points (immune to slashing; weak to bludgeoning, radiation) with an attack that does 69 radiation damage at initiative 4
96 units each with 7266 hit points (immune to fire) with an attack that does 738 bludgeoning damage at initiative 8
Infection:
1492 units each with 47899 hit points (weak to fire, slashing; immune to cold) with an attack that does 56 bludgeoning damage at initiative 15
3065 units each with 39751 hit points (weak to bludgeoning, slashing) with an attack that does 20 slashing damage at initiative 1
7971 units each with 35542 hit points (weak to bludgeoning, radiation) with an attack that does 8 bludgeoning damage at initiative 10
585 units each with 5936 hit points (weak to cold; immune to fire) with an attack that does 17 slashing damage at initiative 17
2449 units each with 37159 hit points (immune to cold) with an attack that does 22 cold damage at initiative 7
8897 units each with 6420 hit points (immune to bludgeoning, slashing, fire; weak to radiation) with an attack that does 1 bludgeoning damage at initiative 19
329 units each with 31704 hit points (weak to fire; immune to cold, radiation) with an attack that does 179 bludgeoning damage at initiative 16
6961 units each with 11069 hit points (weak to fire) with an attack that does 2 radiation damage at initiative 20
2837 units each with 29483 hit points (weak to cold) with an attack that does 20 bludgeoning damage at initiative 9
8714 units each with 7890 hit points with an attack that does 1 cold damage at initiative 3

View File

@ -1,6 +1,142 @@
{-# LANGUAGE ViewPatterns #-}
module Day24 (main) where
import Data.Foldable (foldl')
import Data.List (maximumBy, sort, sortOn, delete, find)
import Data.Ord (comparing)
import Data.Maybe (fromMaybe)
type Pairs = ([Group], [Group], [(Group, Int)])
data Group = Group {
number :: Int,
army :: Army,
units :: Int,
hitPoints :: Int, -- of each unit
immunities :: [AttackType],
weaknesses :: [AttackType],
attackType :: AttackType,
attackDamage :: Int,
initiative :: Int
} deriving (Eq, Ord, Show)
data Army = Immune | Infection deriving (Eq, Ord, Show)
data AttackType = Fire | Slashing | Radiation | Bludgeoning | Cold deriving (Eq, Ord, Show)
demoImmuneSystem :: [Group]
demoImmuneSystem = [
Group 1 Immune 17 5390 [] [Radiation, Bludgeoning] Fire 4507 2,
Group 2 Immune 989 1274 [Fire] [Bludgeoning, Slashing] Slashing 25 3
]
demoInfection :: [Group]
demoInfection = [
Group 1 Infection 801 4706 [] [Radiation] Bludgeoning 116 1,
Group 2 Infection 4485 2961 [Radiation] [Fire, Cold] Slashing 12 4
]
initImmuneSystem :: [Group]
initImmuneSystem = [
Group 1 Immune 5711 6662 [Fire] [Slashing] Bludgeoning 9 14,
Group 2 Immune 2108 8185 [] [Radiation, Bludgeoning] Slashing 36 13,
Group 3 Immune 1590 3940 [] [] Cold 24 5,
Group 4 Immune 2546 6960 [] [] Slashing 25 2,
Group 5 Immune 1084 3450 [Bludgeoning] [] Slashing 27 11,
Group 6 Immune 265 8223 [Radiation, Bludgeoning, Cold] [] Cold 259 12,
Group 7 Immune 6792 6242 [Slashing] [Bludgeoning, Radiation] Slashing 9 18,
Group 8 Immune 3336 12681 [] [Slashing] Fire 28 6,
Group 9 Immune 752 5272 [Slashing] [Bludgeoning, Radiation] Radiation 69 4,
Group 10 Immune 96 7266 [Fire] [] Bludgeoning 738 8
]
initInfection :: [Group]
initInfection = [
Group 1 Infection 1492 47899 [Cold] [Fire, Slashing] Bludgeoning 56 15,
Group 2 Infection 3065 39751 [] [Bludgeoning, Slashing] Slashing 20 1,
Group 3 Infection 7971 35542 [] [Bludgeoning, Radiation] Bludgeoning 8 10,
Group 4 Infection 585 5936 [Fire] [Cold] Slashing 17 17,
Group 5 Infection 2449 37159 [Cold] [] Cold 22 7,
Group 6 Infection 8897 6420 [Bludgeoning, Slashing, Fire] [Radiation] Bludgeoning 1 19,
Group 7 Infection 329 31704 [Cold, Radiation] [Fire] Bludgeoning 179 16,
Group 8 Infection 6961 11069 [] [Fire] Radiation 2 20,
Group 9 Infection 2837 29483 [] [Cold] Bludgeoning 20 9,
Group 10 Infection 8714 7890 [] [] Cold 1 3
]
effectivePower :: Group -> Int
effectivePower g = units g * attackDamage g
-- damage :: attacking group -> defending group -> damage dealt
damage :: Group -> Group -> Int
damage a d
| attackType a `elem` immunities d = 0
| attackType a `elem` weaknesses d = 2 * effectivePower a
| otherwise = effectivePower a
-- chooseTarget :: attacking group -> target groups -> target group
chooseTarget :: Group -> [Group] -> Maybe Group
chooseTarget a groups =
let target = maximumBy (comparing (\t -> (damage a t, effectivePower t, initiative t))) groups
in if damage a target == 0 then Nothing else Just target
-- pair :: (immune system groups, infection groups, (attacking group, defending number)) -> attacking group -> (remaining immunes, remaining infections, new pairs)
pair :: Pairs -> Group -> Pairs
pair paired@(_, [], _) group@(army -> Immune) = paired
pair paired@([], _, _) group@(army -> Infection) = paired
pair paired@(immune, infection, pairs) group@(army -> Immune) =
case chooseTarget group infection of
Just target -> (immune, delete target infection, (group, number target):pairs)
Nothing -> paired
pair paired@(immune, infection, pairs) group@(army -> Infection) =
case chooseTarget group immune of
Just target -> (delete target immune, infection, (group, number target):pairs)
Nothing -> paired
-- attack :: (immune system groups, infection groups) -> (attacking group, defending number) -> remaining (immunes, infections)
attack :: ([Group], [Group]) -> (Group, Int) -> ([Group], [Group])
attack groups@(immune, infection) (Group { number = n, army = Immune }, i) =
fromMaybe groups $ do
a <- find ((== n) . number) immune
d <- find ((== i) . number) infection
let unitsLeft = (units d) - (damage a d) `div` (hitPoints d)
infectionRest = delete d infection
Just $ if unitsLeft > 0 then (immune, d { units = unitsLeft } : infectionRest) else (immune, infectionRest)
attack groups@(immune, infection) (Group { number = n, army = Infection }, i) =
fromMaybe groups $ do
a <- find ((== n) . number) infection
d <- find ((== i) . number) immune
let unitsLeft = (units d) - (damage a d) `div` (hitPoints d)
immuneRest = delete d immune
Just $ if unitsLeft > 0 then (d { units = unitsLeft } : immuneRest, infection) else (immuneRest, infection)
-- fight :: (immune system groups, infection groups) before fight -> (immune, infection) after
fight :: ([Group], [Group]) -> ([Group], [Group])
fight (immune, infection) =
let chooseOrder = reverse . sortOn (\g -> (effectivePower g, initiative g)) $ immune ++ infection
(_, _, pairs) = foldl' pair (immune, infection, []) chooseOrder
attackOrder = reverse . sortOn (initiative . fst) $ pairs
in foldl' attack (immune, infection) attackOrder
-- getOutcome :: (immune system groups, infection groups) -> (winning army, remaining units or -1 if stalemate)
getOutcome :: ([Group], [Group]) -> (Army, Int)
getOutcome (immune, []) = (Immune, sum $ map units immune)
getOutcome ([], infection) = (Infection, sum $ map units infection)
getOutcome ii@(immune, infection) =
let ii'@(immune', infection') = fight ii
in if sort immune' == sort immune && sort infection' == sort infection
then (Infection, -1) -- stalemate
else getOutcome ii'
part1 :: ([Group], [Group]) -> Int
part1 = snd . getOutcome
part2 :: ([Group], [Group]) -> Int
part2 ii@(immune, infection) =
let (army, n) = getOutcome ii
in if army == Immune then n else part2 (boost 1 immune, infection)
where boost n = map (\g -> g { attackDamage = n + attackDamage g })
main :: IO ()
main = do
input <- readFile "input/24.txt"
print input
print $ part1 (initImmuneSystem, initInfection)
print $ part2 (initImmuneSystem, initInfection)