Done day 24 part 1
authorNeil Smith <neil.git@njae.me.uk>
Fri, 29 Nov 2019 10:34:49 +0000 (10:34 +0000)
committerNeil Smith <neil.git@njae.me.uk>
Fri, 29 Nov 2019 10:34:49 +0000 (10:34 +0000)
advent-of-code.cabal
data/advent24.txt [new file with mode: 0644]
data/advent24a.txt [new file with mode: 0644]
src/advent24/advent24.hs [new file with mode: 0644]
src/advent24/advent24iterate.hs [new file with mode: 0644]

index d3cd45cec865363afcce598c231cb49abcfbb726..b9a7c036faaa734c675ab2a559e01bc318f7fcf7 100644 (file)
@@ -281,3 +281,21 @@ executable advent23
                      , megaparsec 
                      , linear
                      , pqueue
+
+executable advent24
+  hs-source-dirs:      src/advent24
+  main-is:             advent24.hs
+  default-language:    Haskell2010
+  build-depends:       base >= 4.7 && < 5
+                     , containers
+                     , text
+                     , megaparsec 
+
+executable advent24iterate
+  hs-source-dirs:      src/advent24
+  main-is:             advent24iterate.hs
+  default-language:    Haskell2010
+  build-depends:       base >= 4.7 && < 5
+                     , containers
+                     , text
+                     , megaparsec 
diff --git a/data/advent24.txt b/data/advent24.txt
new file mode 100644 (file)
index 0000000..8e14c36
--- /dev/null
@@ -0,0 +1,23 @@
+Immune System:
+3609 units each with 2185 hit points (weak to cold, radiation) with an attack that does 5 slashing damage at initiative 20
+72 units each with 5294 hit points (weak to slashing; immune to radiation, cold) with an attack that does 639 fire damage at initiative 1
+4713 units each with 6987 hit points (weak to radiation) with an attack that does 12 slashing damage at initiative 2
+623 units each with 9745 hit points with an attack that does 137 cold damage at initiative 6
+1412 units each with 9165 hit points with an attack that does 52 bludgeoning damage at initiative 3
+2042 units each with 7230 hit points (immune to cold, radiation) with an attack that does 25 bludgeoning damage at initiative 15
+209 units each with 9954 hit points with an attack that does 384 cold damage at initiative 17
+33 units each with 6495 hit points (weak to fire) with an attack that does 1756 fire damage at initiative 7
+242 units each with 6650 hit points (immune to radiation, fire) with an attack that does 239 bludgeoning damage at initiative 12
+4701 units each with 7384 hit points (immune to cold) with an attack that does 14 fire damage at initiative 9
+
+Infection:
+4154 units each with 21287 hit points (immune to fire, slashing, cold, radiation) with an attack that does 9 fire damage at initiative 5
+2091 units each with 5531 hit points (immune to slashing) with an attack that does 5 fire damage at initiative 13
+2237 units each with 24000 hit points with an attack that does 20 fire damage at initiative 16
+149 units each with 31282 hit points (weak to radiation, cold) with an attack that does 329 radiation damage at initiative 8
+649 units each with 39642 hit points with an attack that does 108 cold damage at initiative 18
+108 units each with 35626 hit points (immune to radiation; weak to slashing) with an attack that does 519 cold damage at initiative 4
+1194 units each with 37567 hit points (weak to fire, radiation) with an attack that does 59 radiation damage at initiative 19
+2849 units each with 37603 hit points (immune to cold) with an attack that does 26 bludgeoning damage at initiative 10
+451 units each with 35892 hit points (weak to slashing; immune to cold) with an attack that does 133 fire damage at initiative 14
+3232 units each with 27332 hit points (weak to fire) with an attack that does 14 cold damage at initiative 11
diff --git a/data/advent24a.txt b/data/advent24a.txt
new file mode 100644 (file)
index 0000000..7a474c9
--- /dev/null
@@ -0,0 +1,7 @@
+Immune System:
+17 units each with 5390 hit points (weak to radiation, bludgeoning) with an attack that does 4507 fire damage at initiative 2
+989 units each with 1274 hit points (immune to fire; weak to bludgeoning, slashing) with an attack that does 25 slashing damage at initiative 3
+
+Infection:
+801 units each with 4706 hit points (weak to radiation) with an attack that does 116 bludgeoning damage at initiative 1
+4485 units each with 2961 hit points (immune to radiation; weak to fire, cold) with an attack that does 12 slashing damage at initiative 4
diff --git a/src/advent24/advent24.hs b/src/advent24/advent24.hs
new file mode 100644 (file)
index 0000000..45a0c51
--- /dev/null
@@ -0,0 +1,235 @@
+{-# LANGUAGE NegativeLiterals #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TypeFamilies #-}
+
+-- Box division approach taken from fizbin: 
+-- https://www.reddit.com/r/adventofcode/comments/a8s17l/2018_day_23_solutions/ecfmpy0/
+
+import Debug.Trace
+
+-- import Prelude hiding ((++))
+import Data.Text (Text)
+import qualified Data.Text as T
+import qualified Data.Text.IO as TIO
+
+import Data.Void (Void)
+import Text.Megaparsec hiding (State)
+import Text.Megaparsec.Char
+import qualified Text.Megaparsec.Char.Lexer as L
+import qualified Control.Applicative as CA
+
+import Data.List hiding (group)
+import Data.Function (on)
+import qualified Data.Map.Strict as M
+import Data.Map.Strict ((!))
+
+
+data Group = Group { _units :: Int
+                   , _hps :: Int
+                   , _modifiers :: [Modifier]
+                   , _damage :: Int
+                   , _damageType :: String
+                   , _initiative :: Int
+                   } deriving (Eq, Show)
+instance Ord Group where
+    g1 `compare` g2 = if (effectivePower g1) == (effectivePower g2)
+                      then (_initiative g1) `compare` (_initiative g2)
+                      else (effectivePower g1) `compare` (effectivePower g2)
+
+data Modifier = Weakness [String] | Immunity [String]  
+        deriving (Eq, Show)
+
+type Army = M.Map Int Group
+type Allocation = (Int, Int)
+data BattleOrder = Immune Int | Infection Int deriving (Eq, Show)
+
+
+main :: IO ()
+main = do 
+        text <- TIO.readFile "data/advent24.txt"
+        let armies = successfulParse text
+        print armies
+        print $ part1 (fst armies) (snd armies)
+
+effectivePower group = (_units group) * (_damage group)
+
+damageCaused attacker defender = 
+    if attackType `elem` immunities then 0
+    else if attackType `elem` weaknesses then (2 * effectivePower attacker)
+         else effectivePower attacker
+    where attackType = _damageType attacker
+          weaknesses = foldl' extractWeakness [] (_modifiers defender)
+          immunities = foldl' extractImmunity [] (_modifiers defender)
+
+extractWeakness currentWeaknesses (Weakness ws) = currentWeaknesses ++ ws
+extractWeakness currentWeaknesses (Immunity _ws) = currentWeaknesses
+
+extractImmunity currentImmunity (Weakness _ms) = currentImmunity
+extractImmunity currentImmunity (Immunity ms) = currentImmunity ++ ms
+
+applyDamage group damage = group { _units = unitsRemaining }
+    where unitsKilled = damage `div` (_hps group)
+          unitsRemaining = maximum [0, (_units group) - unitsKilled]
+
+
+keysByEffectivePower :: Army -> [Int]
+keysByEffectivePower army = reverse $ sortOn (\k -> effectivePower (army!k)) (M.keys army)
+
+allocateAttackers :: Army -> Army -> [Allocation]
+allocateAttackers attackers defenders = fst $ foldl' (allocateAttacker attackers defenders) ([], M.keys defenders) $ keysByEffectivePower attackers
+
+allocateAttacker :: Army -> Army -> ([Allocation], [Int]) -> Int -> ([Allocation], [Int])
+-- allocateAttacker attackers defenders allocated@(assignedTargets, availableTargets) attackerKey | trace ("Allocate " ++ show attackerKey ++ "\n" ++ show allocated ++ "\n" ++ show [(t, sortTarget t) | t <- targets]) False = undefined
+--     where targets = reverse $ sortOn sortTarget availableTargets
+--           sortTarget t = ( damageCaused (attackers!attackerKey) (defenders!t) 
+--                          , effectivePower (defenders!t) 
+--                          , _initiative (defenders!t)
+--                          )
+allocateAttacker attackers defenders (assignedTargets, availableTargets) attackerKey = 
+    if null viableTargets 
+    then (assignedTargets, availableTargets)
+    else (((attackerKey, target):assignedTargets), delete target availableTargets)
+    where attacker = attackers!attackerKey
+          viableTargets = filter (\t -> damageCaused attacker (defenders!t) > 0 ) availableTargets
+          target = head $ reverse $ sortOn sortTarget viableTargets
+          sortTarget t = ( damageCaused attacker (defenders!t) 
+                         , effectivePower (defenders!t) 
+                         , _initiative (defenders!t)
+                         )
+
+
+battleOrder :: Army -> Army -> [BattleOrder]
+battleOrder immuneArmy infectionArmy = mergeOrders immuneIds infectIds
+    where armyIds army = reverse $ sort [(_initiative (army!k), k) | k <- M.keys army]
+    -- $ sortOn (\k -> (_initiative (army!k), k)) $ M.keys army
+          immuneIds = armyIds immuneArmy
+          infectIds = armyIds infectionArmy
+
+mergeOrders :: [(Int, Int)] -> [(Int, Int)] -> [BattleOrder]
+mergeOrders [] ids = [ Infection k | (_, k) <- ids ]
+mergeOrders ids [] = [ Immune k | (_, k) <- ids ]
+mergeOrders ((i1, k1):id1s) ((i2, k2):id2s)
+    | i1 >= i2  = (Immune k1):(mergeOrders id1s ((i2, k2):id2s))
+    | otherwise = (Infection k2):(mergeOrders ((i1, k1):id1s) id2s)
+
+battleRound :: Army -> Army -> (Army, Army)
+battleRound immuneArmy infectionArmy | trace ("Round\n" ++ show immuneArmy ++ " " ++ show infectionArmy) False = undefined
+-- battleRound immuneArmy infectionArmy | trace (show (armyCount immuneArmy) ++ " " ++ show (armyCount infectionArmy)) False = undefined
+battleRound immuneArmy infectionArmy = (pruneArmy immuneArmy', pruneArmy infectionArmy')
+    where immuneAllocations = allocateAttackers immuneArmy infectionArmy
+          infectionAllocations = allocateAttackers infectionArmy immuneArmy
+          actionOrder = battleOrder immuneArmy infectionArmy
+          (immuneArmy', infectionArmy') =     foldl' (\ (a1, a2) order -> handleOrder order immuneAllocations infectionAllocations a1 a2) 
+                                                     (immuneArmy, infectionArmy)  actionOrder
+
+
+-- armyCount army = sum [_units g | g <- M.elems army ]
+
+handleOrder :: BattleOrder -> [Allocation] -> [Allocation] -> Army -> Army -> (Army, Army)
+handleOrder (Immune k) allocations _ attackArmy defendArmy = (attackArmy, defendArmy')
+    where defendArmy' = handleAttack k allocations attackArmy defendArmy
+handleOrder (Infection k) _ allocations defendArmy attackArmy = (defendArmy', attackArmy)
+    where defendArmy' = handleAttack k allocations attackArmy defendArmy
+
+handleAttack :: Int -> [Allocation] -> Army -> Army -> Army
+handleAttack attacker allocations attackArmy defendArmy = 
+    if not $ null attackersAllocations
+    then M.insert defender defendGroup' defendArmy
+    else defendArmy
+    where attackersAllocations = filter (\a -> attacker == fst a ) allocations
+          defender = snd $ head attackersAllocations
+          defendGroup = (defendArmy!defender)
+          damage = damageCaused (attackArmy!attacker) defendGroup
+          defendGroup' = applyDamage defendGroup damage
+
+battle :: Army -> Army -> Int
+battle immuneArmy infectionArmy = 
+    if battleOver immuneArmy infectionArmy 
+    then remainingUnitCount immuneArmy infectionArmy
+    else battle immuneArmy' infectionArmy'
+    where (immuneArmy', infectionArmy') = battleRound immuneArmy infectionArmy
+
+
+pruneArmy :: Army -> Army
+pruneArmy = M.filter (\g -> _units g > 0)
+
+battleOver :: Army -> Army -> Bool
+battleOver immuneArmy infectionArmy = (M.null immuneArmy) || (M.null infectionArmy)
+
+
+remainingUnitCount :: Army -> Army -> Int
+-- remainingUnitCount immuneArmy infectionArmy | trace ("End with\n" ++ show immuneArmy ++ " " ++ show infectionArmy) False = undefined
+remainingUnitCount immuneArmy infectionArmy = (unitCount immuneArmy) + (unitCount infectionArmy)
+    where unitCount army = sum $ [_units g | g <- M.elems army]
+
+
+part1 = battle
+
+
+type Parser = Parsec Void Text
+
+sc :: Parser ()
+sc = L.space (skipSome spaceChar) CA.empty CA.empty
+
+lexeme  = L.lexeme sc
+integer = lexeme L.decimal
+-- signedInteger = L.signed sc integer
+symb = L.symbol sc
+comma = symb ","
+semicolon = symb ";"
+openBracket = symb "("
+closeBracket = symb ")"
+
+immuneHeaderP = symb "Immune System:"
+infectionHeaderP = symb "Infection:"
+
+sizePaddingP = symb "units each with"
+hpPaddingP = symb "hit points"
+attackPaddingP = symb "with an attack that does"
+initiativePaddingP = symb "damage at initiative"
+weaknessPaddingP = symb "weak to"
+immunityPaddingP = symb "immune to"
+
+
+armiesP = (,) <$> immuneGroupsP <*> infectionGroupsP
+
+immuneGroupsP = immuneHeaderP *> many groupP
+infectionGroupsP = infectionHeaderP *> many groupP
+
+-- 72 units each with 5294 hit points (weak to slashing; immune to radiation, cold) with an attack that does 639 fire damage at initiative 1
+
+groupP = engroup <$> (integer <* sizePaddingP )
+                 <*> (integer <* hpPaddingP)
+                 <*> (attackModifierGroupP <* attackPaddingP )
+                 <*> integer
+                 <*> (damageTypeP <* initiativePaddingP)
+                 <*> integer
+    where engroup units hps aMods damage damageType initative = 
+                Group { _units = units
+                      , _hps = hps
+                      , _modifiers = aMods
+                      , _damage = damage
+                      , _damageType = damageType
+                      , _initiative = initative 
+                      }
+
+
+attackModifierGroupP = option [] ((openBracket `between` closeBracket) attackModifiersP)
+
+attackModifiersP = attackModifierP `sepBy` semicolon
+attackModifierP = weaknessP <|> immunityP
+weaknessP = Weakness <$> (weaknessPaddingP *> damageTypesP)
+immunityP = Immunity <$> (immunityPaddingP *> damageTypesP)
+
+damageTypeP = some letterChar <* sc
+damageTypesP = damageTypeP `sepBy` comma
+
+successfulParse :: Text -> (Army, Army)
+-- successfulParse _ = []
+successfulParse input = 
+        case parse armiesP "input" input of
+                Left  _error -> (M.empty, M.empty) -- TIO.putStr $ T.pack $ parseErrorPretty err
+                Right armies -> idTag armies
+        where idTag (immune, infect) = (tagArmy immune, tagArmy infect)
+              tagArmy army = M.fromList $ zip [1..] army
diff --git a/src/advent24/advent24iterate.hs b/src/advent24/advent24iterate.hs
new file mode 100644 (file)
index 0000000..ddd2782
--- /dev/null
@@ -0,0 +1,238 @@
+{-# LANGUAGE NegativeLiterals #-}
+{-# LANGUAGE FlexibleContexts #-}
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE TypeFamilies #-}
+
+-- Box division approach taken from fizbin: 
+-- https://www.reddit.com/r/adventofcode/comments/a8s17l/2018_day_23_solutions/ecfmpy0/
+
+import Debug.Trace
+
+-- import Prelude hiding ((++))
+import Data.Text (Text)
+import qualified Data.Text as T
+import qualified Data.Text.IO as TIO
+
+import Data.Void (Void)
+import Text.Megaparsec hiding (State)
+import Text.Megaparsec.Char
+import qualified Text.Megaparsec.Char.Lexer as L
+import qualified Control.Applicative as CA
+
+import Data.List hiding (group)
+import Data.Function (on)
+import qualified Data.Map.Strict as M
+import Data.Map.Strict ((!))
+
+
+data Group = Group { _units :: Int
+                   , _hps :: Int
+                   , _modifiers :: [Modifier]
+                   , _damage :: Int
+                   , _damageType :: String
+                   , _initiative :: Int
+                   } deriving (Eq, Show)
+instance Ord Group where
+    g1 `compare` g2 = if (effectivePower g1) == (effectivePower g2)
+                      then (_initiative g1) `compare` (_initiative g2)
+                      else (effectivePower g1) `compare` (effectivePower g2)
+
+data Modifier = Weakness [String] | Immunity [String]  
+        deriving (Eq, Show)
+
+type Army = M.Map Int Group
+type Allocation = (Int, Int)
+data BattleOrder = Immune Int | Infection Int deriving (Eq, Show)
+
+
+main :: IO ()
+main = do 
+        text <- TIO.readFile "data/advent24.txt"
+        let armies = successfulParse text
+        print armies
+        print $ part1 (fst armies) (snd armies)
+
+
+part1 = battle
+
+effectivePower :: Group -> Int
+effectivePower group = (_units group) * (_damage group)
+
+damageCaused :: Group -> Group -> Int
+damageCaused attacker defender = 
+    if attackType `elem` immunities then 0
+    else if attackType `elem` weaknesses then (2 * effectivePower attacker)
+         else effectivePower attacker
+    where attackType = _damageType attacker
+          weaknesses = foldl' extractWeakness [] (_modifiers defender)
+          immunities = foldl' extractImmunity [] (_modifiers defender)
+
+extractWeakness :: [String] -> Modifier -> [String]
+extractWeakness currentWeaknesses (Weakness ws) = currentWeaknesses ++ ws
+extractWeakness currentWeaknesses (Immunity _ws) = currentWeaknesses
+
+extractImmunity :: [String] -> Modifier -> [String]
+extractImmunity currentImmunity (Weakness _ms) = currentImmunity
+extractImmunity currentImmunity (Immunity ms) = currentImmunity ++ ms
+
+applyDamage :: Group -> Int -> Group
+applyDamage group damage = group { _units = unitsRemaining }
+    where unitsKilled = damage `div` (_hps group)
+          unitsRemaining = maximum [0, (_units group) - unitsKilled]
+
+
+keysByEffectivePower :: Army -> [Int]
+keysByEffectivePower army = reverse $ sortOn (\k -> effectivePower (army!k)) (M.keys army)
+
+allocateAttackers :: Army -> Army -> [Allocation]
+allocateAttackers attackers defenders = fst $ foldl' (allocateAttacker attackers defenders) ([], M.keys defenders) $ keysByEffectivePower attackers
+
+allocateAttacker :: Army -> Army -> ([Allocation], [Int]) -> Int -> ([Allocation], [Int])
+-- allocateAttacker attackers defenders allocated@(assignedTargets, availableTargets) attackerKey | trace ("Allocate " ++ show attackerKey ++ "\n" ++ show allocated ++ "\n" ++ show [(t, sortTarget t) | t <- targets]) False = undefined
+--     where targets = reverse $ sortOn sortTarget availableTargets
+--           sortTarget t = ( damageCaused (attackers!attackerKey) (defenders!t) 
+--                          , effectivePower (defenders!t) 
+--                          , _initiative (defenders!t)
+--                          )
+allocateAttacker attackers defenders (assignedTargets, availableTargets) attackerKey = 
+    if null viableTargets 
+    then (assignedTargets, availableTargets)
+    else (((attackerKey, target):assignedTargets), delete target availableTargets)
+    where attacker = attackers!attackerKey
+          viableTargets = filter (\t -> damageCaused attacker (defenders!t) > 0 ) availableTargets
+          target = head $ reverse $ sortOn sortTarget viableTargets
+          sortTarget t = ( damageCaused attacker (defenders!t) 
+                         , effectivePower (defenders!t) 
+                         , _initiative (defenders!t)
+                         )
+
+
+battleOrder :: Army -> Army -> [BattleOrder]
+battleOrder immuneArmy infectionArmy = mergeOrders immuneIds infectIds
+    where armyIds army = reverse $ sort [(_initiative (army!k), k) | k <- M.keys army]
+    -- $ sortOn (\k -> (_initiative (army!k), k)) $ M.keys army
+          immuneIds = armyIds immuneArmy
+          infectIds = armyIds infectionArmy
+
+mergeOrders :: [(Int, Int)] -> [(Int, Int)] -> [BattleOrder]
+mergeOrders [] ids = [ Infection k | (_, k) <- ids ]
+mergeOrders ids [] = [ Immune k | (_, k) <- ids ]
+mergeOrders ((i1, k1):id1s) ((i2, k2):id2s)
+    | i1 >= i2  = (Immune k1):(mergeOrders id1s ((i2, k2):id2s))
+    | otherwise = (Infection k2):(mergeOrders ((i1, k1):id1s) id2s)
+
+battleRound :: (Army, Army) -> (Army, Army)
+-- battleRound (immuneArmy, infectionArmy) | trace ("Round\n" ++ show immuneArmy ++ " " ++ show infectionArmy) False = undefined
+-- battleRound immuneArmy infectionArmy | trace (show (armyCount immuneArmy) ++ " " ++ show (armyCount infectionArmy)) False = undefined
+battleRound (immuneArmy, infectionArmy) = (pruneArmy immuneArmy', pruneArmy infectionArmy')
+    where immuneAllocations = allocateAttackers immuneArmy infectionArmy
+          infectionAllocations = allocateAttackers infectionArmy immuneArmy
+          actionOrder = battleOrder immuneArmy infectionArmy
+          (immuneArmy', infectionArmy') =     foldl' (\ (a1, a2) order -> handleOrder order immuneAllocations infectionAllocations a1 a2) 
+                                                     (immuneArmy, infectionArmy)  actionOrder
+
+
+-- armyCount army = sum [_units g | g <- M.elems army ]
+
+handleOrder :: BattleOrder -> [Allocation] -> [Allocation] -> Army -> Army -> (Army, Army)
+handleOrder (Immune k) allocations _ attackArmy defendArmy = (attackArmy, defendArmy')
+    where defendArmy' = handleAttack k allocations attackArmy defendArmy
+handleOrder (Infection k) _ allocations defendArmy attackArmy = (defendArmy', attackArmy)
+    where defendArmy' = handleAttack k allocations attackArmy defendArmy
+
+handleAttack :: Int -> [Allocation] -> Army -> Army -> Army
+handleAttack attacker allocations attackArmy defendArmy = 
+    if not $ null attackersAllocations
+    then M.insert defender defendGroup' defendArmy
+    else defendArmy
+    where attackersAllocations = filter (\a -> attacker == fst a ) allocations
+          defender = snd $ head attackersAllocations
+          defendGroup = (defendArmy!defender)
+          damage = damageCaused (attackArmy!attacker) defendGroup
+          defendGroup' = applyDamage defendGroup damage
+
+battle :: Army -> Army -> Int
+battle immuneArmy infectionArmy = uncurry remainingUnitCount endState
+    where endState = head $ dropWhile (not . uncurry battleOver) $ iterate battleRound (immuneArmy, infectionArmy)
+
+
+pruneArmy :: Army -> Army
+pruneArmy = M.filter (\g -> _units g > 0)
+
+battleOver :: Army -> Army -> Bool
+battleOver immuneArmy infectionArmy = (M.null immuneArmy) || (M.null infectionArmy)
+
+
+remainingUnitCount :: Army -> Army -> Int
+-- remainingUnitCount immuneArmy infectionArmy | trace ("End with\n" ++ show immuneArmy ++ " " ++ show infectionArmy) False = undefined
+remainingUnitCount immuneArmy infectionArmy = (unitCount immuneArmy) + (unitCount infectionArmy)
+    where unitCount army = sum $ [_units g | g <- M.elems army]
+
+
+
+type Parser = Parsec Void Text
+
+sc :: Parser ()
+sc = L.space (skipSome spaceChar) CA.empty CA.empty
+
+lexeme  = L.lexeme sc
+integer = lexeme L.decimal
+-- signedInteger = L.signed sc integer
+symb = L.symbol sc
+comma = symb ","
+semicolon = symb ";"
+openBracket = symb "("
+closeBracket = symb ")"
+
+immuneHeaderP = symb "Immune System:"
+infectionHeaderP = symb "Infection:"
+
+sizePaddingP = symb "units each with"
+hpPaddingP = symb "hit points"
+attackPaddingP = symb "with an attack that does"
+initiativePaddingP = symb "damage at initiative"
+weaknessPaddingP = symb "weak to"
+immunityPaddingP = symb "immune to"
+
+
+armiesP = (,) <$> immuneGroupsP <*> infectionGroupsP
+
+immuneGroupsP = immuneHeaderP *> many groupP
+infectionGroupsP = infectionHeaderP *> many groupP
+
+-- 72 units each with 5294 hit points (weak to slashing; immune to radiation, cold) with an attack that does 639 fire damage at initiative 1
+
+groupP = engroup <$> (integer <* sizePaddingP )
+                 <*> (integer <* hpPaddingP)
+                 <*> (attackModifierGroupP <* attackPaddingP )
+                 <*> integer
+                 <*> (damageTypeP <* initiativePaddingP)
+                 <*> integer
+    where engroup units hps aMods damage damageType initative = 
+                Group { _units = units
+                      , _hps = hps
+                      , _modifiers = aMods
+                      , _damage = damage
+                      , _damageType = damageType
+                      , _initiative = initative 
+                      }
+
+
+attackModifierGroupP = option [] ((openBracket `between` closeBracket) attackModifiersP)
+
+attackModifiersP = attackModifierP `sepBy` semicolon
+attackModifierP = weaknessP <|> immunityP
+weaknessP = Weakness <$> (weaknessPaddingP *> damageTypesP)
+immunityP = Immunity <$> (immunityPaddingP *> damageTypesP)
+
+damageTypeP = some letterChar <* sc
+damageTypesP = damageTypeP `sepBy` comma
+
+successfulParse :: Text -> (Army, Army)
+-- successfulParse _ = []
+successfulParse input = 
+        case parse armiesP "input" input of
+                Left  _error -> (M.empty, M.empty) -- TIO.putStr $ T.pack $ parseErrorPretty err
+                Right armies -> idTag armies
+        where idTag (immune, infect) = (tagArmy immune, tagArmy infect)
+              tagArmy army = M.fromList $ zip [1..] army