summaryrefslogtreecommitdiff
path: root/14-1.hs
blob: 8044c8d6962d8e8897748bac24a9c4efe5c89d4b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
import Data.Maybe (fromMaybe)
import Text.Parsec
import Text.Parsec.Char

data Vec = Vec Int Int deriving Show

data Robot = Robot { pos :: Vec, vel :: Vec } deriving Show

data Conf = Conf { width :: Int, height :: Int}

doParse :: String -> [Robot]
doParse s = case parse inputp "" s of
  Left e   -> error $ show e
  Right xs -> xs

inputp :: Parsec String () [Robot]
inputp = sepBy robotp newline

robotp :: Parsec String () Robot
robotp = Robot
  <$> posp
  <*  char ' '
  <*> velp

posp :: Parsec String () Vec
posp = Vec
  <$  string "p="
  <*> number
  <*  char ','
  <*> number

velp :: Parsec String () Vec
velp = Vec
  <$  string "v="
  <*> number
  <*  char ','
  <*> number

number :: Parsec String () Int
number = ((read .) . (:)) . fromMaybe '0'
  <$> optionMaybe (char '-')
  <*> many1 digit

update :: Conf -> Robot -> Robot
update c (Robot (Vec x y) v@(Vec vx vy)) = wrap c $ Robot (Vec (x+vx) (y+vy)) v

wrap :: Conf -> Robot -> Robot
wrap c = wrapH c . wrapV c

wrapH :: Conf -> Robot -> Robot
wrapH c r  | tooLeft c r  = wrapRight c r
           | tooRight c r = wrapLeft c r
           | otherwise    = r
  where
    tooLeft _ (Robot (Vec x _) _) = x < 0
    tooRight (Conf w _) (Robot (Vec x _) _) = x >= w
    
    wrapRight (Conf w _) r@(Robot (Vec x y) _) = r { pos = Vec (x+w) y }
    wrapLeft (Conf w _) r@(Robot (Vec x y) _) = r { pos = Vec (x-w) y }

wrapV :: Conf -> Robot -> Robot
wrapV c r | tooHigh c r = wrapDown c r
          | tooLow  c r = wrapUp c r
          | otherwise   = r
  where
    tooHigh _ (Robot (Vec _ y) _) = y < 0
    tooLow (Conf _ h) (Robot (Vec _ y) _) = y >= h

    wrapDown (Conf _ h) r@(Robot (Vec x y) _) = r { pos = Vec x (y+h) }
    wrapUp (Conf _ h) r@(Robot (Vec x y) _) = r { pos = Vec x (y-h) }

within :: (Int,Int) -> (Int,Int) -> Robot -> Bool
within (x0,x1) (y0,y1) (Robot (Vec x y) _) =
  x >= x0 && x < x1 && y >= y0 && y < y1

score :: Conf -> [Robot] -> Int
score (Conf w h) rs =
  let leftHalf  = w `div` 2
      rightHalf = leftHalf + 1
      topHalf   = h `div` 2
      botHalf   = topHalf + 1
      q1 = filter (within (0,leftHalf) (0,topHalf)) rs
      q2 = filter (within (rightHalf,w) (0,topHalf)) rs
      q3 = filter (within (0,leftHalf) (botHalf,h)) rs
      q4 = filter (within (rightHalf,w) (botHalf,h)) rs
  in  product $ map length [q1,q2,q3,q4]

main :: IO ()
main = do
  input <- getContents
  let conf = Conf 101 103
  print
    . score conf
    . (!! 100)
    . iterate (map (update conf))
    . doParse
    $ input