@@ -15,16 +15,42 @@ import Life
15
15
type Tick = Word16
16
16
type HistorySize = Word8
17
17
type Projector a = World -> Tick -> a
18
- data Slice a = Slice { projection :: a , world :: ! World , tick :: ! Tick , slHistory :: [(Tick , World )], slUserWorlds :: [World ] }
18
+ type UserInputs = [World -> World ]
19
+
20
+ data Slice a = Slice { projection :: a , slHistory :: HistoricalRecord }
21
+ data HistoricalRecord = Evolution { hrTick :: Tick , hrInput :: UserInputs , hrPrevious :: HistoricalRecord }
22
+ | BaseState { hrTick :: Tick , hrInput :: UserInputs , baseWorld :: World }
19
23
20
24
instance Show (Slice a ) where
21
25
show slice = " World size " ++ (show $ size (world slice)) ++
22
26
" at tick " ++ (show $ tick slice) ++
23
- " with " ++ (show $ length (slHistory slice)) ++ " historical records"
27
+ " with " ++ (show $ hrLength (slHistory slice)) ++ " historical records"
24
28
25
29
maxCloseTickDifference :: Word16
26
30
maxCloseTickDifference = 11
27
31
32
+ tick :: Slice a -> Tick
33
+ tick = hrTick . slHistory
34
+
35
+ world :: Slice a -> World
36
+ world = hrWorld . slHistory
37
+
38
+ hrIsBase :: HistoricalRecord -> Bool
39
+ hrIsBase (BaseState _ _ _) = True
40
+ hrIsBase _ = False
41
+
42
+ hrLength :: HistoricalRecord -> Int
43
+ hrLength hr | hrIsBase hr = 1
44
+ | otherwise = 1 + hrLength (hrPrevious hr)
45
+
46
+ hrWorld :: HistoricalRecord -> World
47
+ hrWorld hr | null (hrInput hr) = hrWorld' hr
48
+ | otherwise = foldl1' merge worldsWithUserInput
49
+
50
+ where hrWorld' hr' | hrIsBase hr' = baseWorld hr'
51
+ | otherwise = evolve . hrWorld . hrPrevious $ hr'
52
+ worldsWithUserInput = map ($ hrWorld' hr) (hrInput hr)
53
+
28
54
farApart :: Tick -> Tick -> Bool
29
55
farApart tick1 tick2 = tick2 - tick1 > maxCloseTickDifference && tick1 - tick2 > maxCloseTickDifference
30
56
@@ -33,27 +59,36 @@ t1 `after` t2 | farApart t1 t2 = True
33
59
| otherwise = (t1 - t2) < (t2 - t1)
34
60
35
61
newSlice :: Projector a -> World -> Tick -> Slice a
36
- newSlice f w t = Slice { world = w, tick = t, projection = f w t, slHistory = [(t,w)], slUserWorlds = [] }
62
+ newSlice f w t = Slice { projection = f w t
63
+ , slHistory = BaseState { hrTick = t
64
+ , baseWorld = w
65
+ , hrInput= []
66
+ }
67
+ }
37
68
38
69
nextSlice :: Projector b -> Slice a -> Slice b
39
- nextSlice f s = let tick' = (tick s) + 1
40
- world' | (not . null ) (slUserWorlds s) = evolve (foldl1' merge $ slUserWorlds s)
41
- | otherwise = evolve (world s)
42
- in Slice { tick = tick'
43
- , projection = f world' tick'
44
- , world = world'
45
- , slHistory = (tick', world') : (slHistory s)
46
- , slUserWorlds = []
47
- }
70
+ nextSlice f s = let s' = Slice { projection = f (world s') (tick s')
71
+ , slHistory = Evolution { hrTick = tick s + 1
72
+ , hrInput= []
73
+ , hrPrevious = slHistory s
74
+ }
75
+ }
76
+ in s'
48
77
49
78
trimHistory :: HistorySize -> Slice a -> Slice a
50
- trimHistory n s = s { slHistory = genericTake n (slHistory s) }
79
+ trimHistory n s = s { slHistory = trimHistory' n (slHistory s) }
80
+ where trimHistory' _ base | hrIsBase base = base
81
+ trimHistory' 1 hr = BaseState { hrTick = hrTick hr
82
+ , hrInput = hrInput hr
83
+ , baseWorld = evolve . hrWorld . hrPrevious $ hr
84
+ }
85
+ trimHistory' n' hr = hr { hrPrevious = trimHistory' (n' - 1 ) (hrPrevious hr) }
51
86
52
87
addUserInput :: Tick -> (World -> World ) -> Slice a -> Maybe (Slice a )
53
- addUserInput inputTick f s = do worldAtTimeOfInput <- lookup inputTick (slHistory s)
54
- -- evaluating strictly so any exceptions will be raised while request is still being handled
55
- let ! userWorld = evolveUpTo (f worldAtTimeOfInput) inputTick (tick s)
56
- return $ s { slUserWorlds = userWorld : slUserWorlds s }
57
- where evolveUpTo w t t' | t == t' = w
58
- | otherwise = evolveUpTo (evolve w) (t + 1 ) t'
88
+ addUserInput inputTick f s = do newHistory <- addUserInput' (slHistory s)
89
+ return $ s { slHistory = newHistory }
90
+ where addUserInput' hr | hrTick hr == inputTick = return $ hr { hrInput = f : hrInput hr }
91
+ | hrIsBase hr = Nothing
92
+ | otherwise = do newPrevious <- addUserInput' (hrPrevious hr)
93
+ return $ hr { hrPrevious = newPrevious }
59
94
0 commit comments