Jens Kadenbach 1年前
コミット
714980da64
8個のファイルの変更339行の追加4行の削除
  1. 12
    0
      aoc2022.cabal
  2. 3
    0
      package.yaml
  3. 55
    0
      ressources/day11-input
  4. 0
    3
      src/Day10.hs
  5. 101
    0
      src/Day11.hs
  6. 98
    0
      src/Day11/Parser.hs
  7. 2
    1
      src/Lib.hs
  8. 68
    0
      test/Day11Spec.hs

+ 12
- 0
aoc2022.cabal ファイルの表示

@@ -28,6 +28,8 @@ library
28 28
       Day1
29 29
       Day1.Internal
30 30
       Day10
31
+      Day11
32
+      Day11.Parser
31 33
       Day2
32 34
       Day2.Part1
33 35
       Day2.Part2
@@ -63,10 +65,13 @@ library
63 65
     , containers
64 66
     , heredoc
65 67
     , hspec
68
+    , hspec-megaparsec
66 69
     , lens
67 70
     , matrix
68 71
     , megaparsec
72
+    , mtl
69 73
     , parsec
74
+    , sort
70 75
     , split
71 76
     , text
72 77
     , transformers
@@ -87,10 +92,13 @@ executable aoc2022-exe
87 92
     , containers
88 93
     , heredoc
89 94
     , hspec
95
+    , hspec-megaparsec
90 96
     , lens
91 97
     , matrix
92 98
     , megaparsec
99
+    , mtl
93 100
     , parsec
101
+    , sort
94 102
     , split
95 103
     , text
96 104
     , transformers
@@ -102,6 +110,7 @@ test-suite aoc2022-test
102 110
   main-is: Spec.hs
103 111
   other-modules:
104 112
       Day10Spec
113
+      Day11Spec
105 114
       Day1Spec
106 115
       Day2Spec
107 116
       Day3Spec
@@ -122,10 +131,13 @@ test-suite aoc2022-test
122 131
     , containers
123 132
     , heredoc
124 133
     , hspec
134
+    , hspec-megaparsec
125 135
     , lens
126 136
     , matrix
127 137
     , megaparsec
138
+    , mtl
128 139
     , parsec
140
+    , sort
129 141
     , split
130 142
     , text
131 143
     , transformers

+ 3
- 0
package.yaml ファイルの表示

@@ -34,6 +34,9 @@ dependencies:
34 34
 - vector
35 35
 - megaparsec
36 36
 - transformers
37
+- hspec-megaparsec
38
+- mtl
39
+- sort
37 40
 
38 41
 ghc-options:
39 42
 - -Wall

+ 55
- 0
ressources/day11-input ファイルの表示

@@ -0,0 +1,55 @@
1
+Monkey 0:
2
+  Starting items: 52, 78, 79, 63, 51, 94
3
+  Operation: new = old * 13
4
+  Test: divisible by 5
5
+    If true: throw to monkey 1
6
+    If false: throw to monkey 6
7
+
8
+Monkey 1:
9
+  Starting items: 77, 94, 70, 83, 53
10
+  Operation: new = old + 3
11
+  Test: divisible by 7
12
+    If true: throw to monkey 5
13
+    If false: throw to monkey 3
14
+
15
+Monkey 2:
16
+  Starting items: 98, 50, 76
17
+  Operation: new = old * old
18
+  Test: divisible by 13
19
+    If true: throw to monkey 0
20
+    If false: throw to monkey 6
21
+
22
+Monkey 3:
23
+  Starting items: 92, 91, 61, 75, 99, 63, 84, 69
24
+  Operation: new = old + 5
25
+  Test: divisible by 11
26
+    If true: throw to monkey 5
27
+    If false: throw to monkey 7
28
+
29
+Monkey 4:
30
+  Starting items: 51, 53, 83, 52
31
+  Operation: new = old + 7
32
+  Test: divisible by 3
33
+    If true: throw to monkey 2
34
+    If false: throw to monkey 0
35
+
36
+Monkey 5:
37
+  Starting items: 76, 76
38
+  Operation: new = old + 4
39
+  Test: divisible by 2
40
+    If true: throw to monkey 4
41
+    If false: throw to monkey 7
42
+
43
+Monkey 6:
44
+  Starting items: 75, 59, 93, 69, 76, 96, 65
45
+  Operation: new = old * 19
46
+  Test: divisible by 17
47
+    If true: throw to monkey 1
48
+    If false: throw to monkey 3
49
+
50
+Monkey 7:
51
+  Starting items: 89
52
+  Operation: new = old + 2
53
+  Test: divisible by 19
54
+    If true: throw to monkey 2
55
+    If false: throw to monkey 4

+ 0
- 3
src/Day10.hs ファイルの表示

@@ -10,10 +10,7 @@ module Day10
10 10
 where
11 11
 
12 12
 import Control.Arrow ((>>>))
13
-import qualified Data.Matrix as M
14
-import Debug.Trace (trace)
15 13
 import Data.List.Split (chunksOf)
16
-import Data.List (intercalate)
17 14
 
18 15
 data Instruction = Addx Int | Noop
19 16
   deriving (Show, Eq)

+ 101
- 0
src/Day11.hs ファイルの表示

@@ -0,0 +1,101 @@
1
+{-# LANGUAGE OverloadedStrings #-}
2
+
3
+module Day11
4
+  ( runMonkeyMachine,
5
+    mostActiveMonkeys,
6
+    monkeyBusiness,
7
+    day11,
8
+    runPart1Machine,
9
+    runPart2Machine,
10
+    worryLimit,
11
+    divideBy3,
12
+  )
13
+where
14
+
15
+import Control.Monad.State.Strict (execState, get, modify', put)
16
+import qualified Control.Monad.State.Strict as St
17
+import Data.Foldable (forM_, toList)
18
+import Data.List (sortOn)
19
+import Data.Ord (Down (..))
20
+import Data.Sequence ((|>))
21
+import qualified Data.Sequence as S
22
+import Data.Text (pack)
23
+import Day11.Parser
24
+
25
+day11 :: IO ()
26
+day11 = do
27
+  input <- readFile "ressources/day11-input"
28
+  putStrLn "Day11"
29
+  let monkeys = (monkeyBusiness . mostActiveMonkeys . runPart1Machine . parseMonkeys) (pack input)
30
+  putStrLn ("Part 1 monkey business: " ++ show monkeys)
31
+  let monkeys' = (monkeyBusiness . mostActiveMonkeys . runPart2Machine . parseMonkeys) (pack input)
32
+  putStrLn ("Part 2 monkey business: " ++ show monkeys')
33
+
34
+type MonkeyState = St.State (S.Seq Monkey)
35
+
36
+runPart1Machine :: [Monkey] -> [Monkey]
37
+runPart1Machine = runMonkeyMachine divideBy3 20
38
+
39
+runPart2Machine :: [Monkey] -> [Monkey]
40
+runPart2Machine monkeys = runMonkeyMachine (worryLimit monkeys) 10000 monkeys
41
+
42
+worryLimit :: [Monkey] -> WorryReducer
43
+worryLimit monkeys = (`mod` limit)
44
+  where
45
+    limit = product $ map _test monkeys
46
+
47
+    
48
+runMonkeyMachine :: WorryReducer -> Int -> [Monkey] -> [Monkey]
49
+runMonkeyMachine eval i monkeys = toList (infiniteMachine !! i)
50
+  where
51
+    infiniteMachine = iterate go $ S.fromList monkeys
52
+    go :: S.Seq Monkey -> S.Seq Monkey
53
+    go m = execState go' m
54
+    go' :: MonkeyState ()
55
+    go' = mapM_ (doMonkey eval) [0 .. length monkeys - 1]
56
+
57
+mostActiveMonkeys :: [Monkey] -> [Monkey]
58
+mostActiveMonkeys = take 2 . sortOn (Down . _inspectionCount)
59
+
60
+monkeyBusiness :: [Monkey] -> Int
61
+monkeyBusiness = product . map _inspectionCount
62
+
63
+-- update a sequence of monkeys according to the moves of the monkey at position idx
64
+doMonkey :: WorryReducer -> Int -> MonkeyState ()
65
+doMonkey reducer idx = do
66
+  horde <- get
67
+  let m = horde `S.index` idx
68
+  let targets = fmap (evalMonkey reducer m) (_items m)
69
+  let m' = m {_items = S.empty, _inspectionCount = _inspectionCount m + S.length (_items m)}
70
+  put $ S.update idx m' horde
71
+  forM_ targets $ \movement ->
72
+    modify' (move movement)
73
+
74
+-- update other monkeys according to a list of calculated moves
75
+move :: (Int, Int) -> S.Seq Monkey -> S.Seq Monkey
76
+move (to, item) horde
77
+  | item < 0 = error ("Item worry level overflow: " ++ show (to, item))
78
+  | otherwise =
79
+    let m = horde `S.index` to
80
+        m' = m {_items = _items m |> item}
81
+     in S.update to m' horde
82
+
83
+type WorryReducer = Int -> Int
84
+
85
+divideBy3 :: WorryReducer
86
+divideBy3 = flip div 3
87
+
88
+evalMonkey :: WorryReducer -> Monkey -> Int -> (Int, Int)
89
+evalMonkey reducer monkey item = (target, newLevel)
90
+  where
91
+    afterOp = case _operation monkey of
92
+      Add Old -> item * 2
93
+      Add (Fixed x) -> item + x
94
+      Multiply Old -> item * item
95
+      Multiply (Fixed x) -> item * x
96
+    newLevel = reducer afterOp
97
+    isDivisible = newLevel `mod` _test monkey == 0
98
+    target =
99
+      if isDivisible
100
+        then _ifDivisible monkey
101
+        else _otherwise monkey

+ 98
- 0
src/Day11/Parser.hs ファイルの表示

@@ -0,0 +1,98 @@
1
+{-# LANGUAGE OverloadedStrings #-}
2
+module Day11.Parser (
3
+  parseMonkeys,
4
+  Monkey (..),
5
+  Operation (..),
6
+  Op (..),
7
+)
8
+ where
9
+
10
+import Text.Megaparsec
11
+import Text.Megaparsec.Char
12
+import Data.Text (Text)
13
+import Data.Void (Void)
14
+import Control.Monad (void)
15
+import qualified Data.Sequence as S
16
+
17
+type Parser = Parsec Void Text
18
+
19
+data Op = Fixed Int | Old
20
+  deriving (Show, Eq)
21
+
22
+data Operation = Add Op | Multiply Op
23
+  deriving (Show, Eq)
24
+
25
+data Monkey = Monkey
26
+  { _nr :: Int
27
+  , _items :: S.Seq Int
28
+  , _operation :: Operation
29
+  , _test :: Int
30
+  , _ifDivisible :: Int
31
+  , _otherwise :: Int
32
+  , _inspectionCount :: Int }
33
+  deriving (Show, Eq)
34
+
35
+parseMonkeys :: Text -> [Monkey]
36
+parseMonkeys input = case parse (monkey `sepBy` void eol) "" input of
37
+  Left err -> error (show err)
38
+  Right result -> result
39
+
40
+monkey :: Parser Monkey
41
+monkey = do
42
+  void (string "Monkey ")
43
+  nr <- many numberChar
44
+  void (char ':')
45
+  void eol
46
+  startingItems <- items
47
+  operation <- op
48
+  d <- divisor
49
+  ifDiv <- ifDivisible
50
+  ifOther <- ifOtherwise
51
+  return Monkey
52
+   { _nr=read nr
53
+   , _items=S.fromList startingItems
54
+   , _operation=operation
55
+   , _test=d
56
+   , _ifDivisible=ifDiv
57
+   , _otherwise=ifOther
58
+   , _inspectionCount=0 }
59
+
60
+ifDivisible :: Parser Int
61
+ifDivisible = do
62
+  x <- read <$> (string "    If true: throw to monkey " >> many numberChar)
63
+  void eol
64
+  return x
65
+
66
+ifOtherwise :: Parser Int
67
+ifOtherwise = do
68
+  x <- read <$> (string "    If false: throw to monkey " >> many numberChar)
69
+  void eol
70
+  return x
71
+
72
+divisor :: Parser Int
73
+divisor = do
74
+  d <- read <$> (string "  Test: divisible by " >> many numberChar)
75
+  void eol
76
+  return d
77
+
78
+items :: Parser [Int]
79
+items = do
80
+  void (string "  Starting items: ")
81
+  startingItems <- sepBy1 (many numberChar) (string ", ")
82
+  void eol
83
+  return (map read startingItems)
84
+
85
+op :: Parser Operation
86
+op = do
87
+  o <- void (string "  Operation: new = old ") >> addOp <|> mulOp
88
+  void eol
89
+  return o
90
+  where
91
+    old = string "old" >> return Old
92
+    num = Fixed . read <$> many numberChar
93
+    addOp = do
94
+      void (string "+ ")
95
+      Add <$> (old <|> num)
96
+    mulOp = do
97
+      void (string "* ")
98
+      Multiply <$> (old <|> num)

+ 2
- 1
src/Lib.hs ファイルの表示

@@ -12,9 +12,10 @@ import Day7 (day7)
12 12
 import Day8 (day8)
13 13
 import Day9 (day9)
14 14
 import Day10 (day10)
15
+import Day11 (day11)
15 16
 
16 17
 days :: [IO ()]
17
-days = [day1, day2, day3, day4, day5, day6, day7, day8, day9, day10]
18
+days = [day1, day2, day3, day4, day5, day6, day7, day8, day9, day10, day11]
18 19
 
19 20
 sep :: IO ()
20 21
 sep = putStrLn "---------"

+ 68
- 0
test/Day11Spec.hs ファイルの表示

@@ -0,0 +1,68 @@
1
+{-# LANGUAGE OverloadedStrings #-}
2
+{-# LANGUAGE QuasiQuotes #-}
3
+
4
+module Day11Spec (spec) where
5
+
6
+import Data.Foldable (toList)
7
+import qualified Data.Sequence as S
8
+import Data.Text (Text)
9
+import Day11
10
+import Day11.Parser
11
+import Test.Hspec
12
+import Text.Heredoc
13
+import Data.Int (Int64)
14
+
15
+testInput :: Text
16
+testInput =
17
+  [str|Monkey 0:
18
+                |  Starting items: 79, 98
19
+                |  Operation: new = old * 19
20
+                |  Test: divisible by 23
21
+                |    If true: throw to monkey 2
22
+                |    If false: throw to monkey 3
23
+                |
24
+                |Monkey 1:
25
+                |  Starting items: 54, 65, 75, 74
26
+                |  Operation: new = old + 6
27
+                |  Test: divisible by 19
28
+                |    If true: throw to monkey 2
29
+                |    If false: throw to monkey 0
30
+                |
31
+                |Monkey 2:
32
+                |  Starting items: 79, 60, 97
33
+                |  Operation: new = old * old
34
+                |  Test: divisible by 13
35
+                |    If true: throw to monkey 1
36
+                |    If false: throw to monkey 3
37
+                |
38
+                |Monkey 3:
39
+                |  Starting items: 74
40
+                |  Operation: new = old + 3
41
+                |  Test: divisible by 17
42
+                |    If true: throw to monkey 0
43
+                |    If false: throw to monkey 1
44
+                |]
45
+
46
+exampleMonkeys :: [Monkey]
47
+exampleMonkeys = parseMonkeys testInput
48
+
49
+spec :: Spec
50
+spec =
51
+  describe "Day11" $ do
52
+    describe "Part1" $ do
53
+      it "parses" $ do
54
+        head exampleMonkeys `shouldBe` Monkey 0 (S.fromList [79, 98]) (Multiply (Fixed 19)) 23 2 3 0
55
+        _items (exampleMonkeys !! 1) `shouldBe` S.fromList [54, 65, 75, 74]
56
+      it "runs part 1 monkey machine for 20 rounds" $ do
57
+        let monkeys = runMonkeyMachine divideBy3 20 exampleMonkeys
58
+        map _inspectionCount monkeys
59
+          `shouldBe` [101, 95, 7, 105]
60
+        map _inspectionCount (mostActiveMonkeys monkeys) `shouldBe` [105, 101]
61
+        map _nr (mostActiveMonkeys monkeys) `shouldBe` [3, 0]
62
+        monkeyBusiness (mostActiveMonkeys monkeys) `shouldBe` 10605
63
+
64
+      it "runs part 2 monkey machine for 10000 rounds" $ do
65
+        let monkeys = runMonkeyMachine (worryLimit exampleMonkeys) 10000 exampleMonkeys
66
+        map _inspectionCount monkeys
67
+          `shouldBe` [52166, 47830, 1938, 52013]
68
+        monkeyBusiness (mostActiveMonkeys monkeys) `shouldBe` 2713310158

読み込み中…
キャンセル
保存