-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathNonDeterministic.hs
122 lines (103 loc) · 4.1 KB
/
NonDeterministic.hs
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
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
{-
- NON-DETERMINISTIC TESTS FOR QUANTUMDRACULA
-}
module NonDeterministic where
import Game
import QuantumDracula
import Control.Monad.Trans.State.Lazy (evalStateT, execStateT, modify)
import Control.Monad (replicateM)
import Control.Monad.Trans.Class (lift)
import Data.Bifunctor (first)
import Data.List (sortBy)
import Data.Function (on)
import Data.Ord (Down(..))
main :: IO ()
main = do
putStrLn "Scenario 1."
putStrLn " Dracula can bite two players (1000 runs)."
putStrLn " Should do it EVERY the time."
scenario1 1000 >>= print
putStrLn ""
putStrLn "Scenario 2."
putStrLn " Total number of bites as aggressiveness increases (1000 runs per aggressiveness level)."
scenario2Summary >>= print
putStrLn ""
putStrLn "Scenario 3."
putStrLn " Do the best bite in Scenario 2. Ask if Dracula is present in the Cellar (1000 runs)."
putStrLn " Say no almost all of the time."
scenario3 1000 >>= print
putStrLn ""
putStrLn "Scenario 4."
putStrLn " Do the best bite in Scenario 2. Ask if Dracula is present in the South Hallway (1000 runs)."
putStrLn " Say no almost all of the time."
scenario4 1000 >>= print
putStrLn ""
-- Can bite two players, so always do it
scenario1 :: Int -> IO [(Room, Int)]
scenario1 = scenarioResults (draculaTurn st1) dist1
where
dist1 = [ Dungeon, Dining, Bathroom, Vent, Canal ]
st1 = GameState
{ sunlights = [ Sunlight {castTo = Alley, castFrom = Gallery} ]
, positions = Right <$> [ Gallery, Staircase, Tomb, Cellar ]
, lastInfo = 2
, lastBite = 2
, canBite = True
}
-- For me, it seems that the best bite is in SHall
scenario2 :: Int -> Int -> IO [(Room, Int)]
scenario2 lastBiteTurns = scenarioResults (draculaTurn st) dist
where
dist = [ Dungeon, Dining, Bathroom, Vent, Canal ]
st = GameState
{ sunlights = [ Sunlight {castTo = Alley, castFrom = Gallery} ]
, positions = Right <$> [ Gallery, Staircase, Tomb, SHall ]
, lastInfo = 2
, lastBite = lastBiteTurns
, canBite = True
}
-- ran this and got [391,624,726,818,854]
scenario2Summary :: IO [Int]
scenario2Summary = traverse (fmap (sum . map snd) . flip scenario2 1000) [1..5]
-- Turn immediately after doing a bite in SHall in scenario2. Dracula should
-- say False to presence in Cellar almost all the time
scenario3 :: Int -> IO [(Bool, Int)]
scenario3 = scenarioResults (pure <$> isPresent st Cellar) dist
where
dist = [ Cellar, SHall, Passage ]
st = GameState
{ sunlights = []
, positions = Left SHall : (Right <$> [ Gallery, Cellar, Tomb ])
, lastInfo = 1
, lastBite = 1
, canBite = True
}
-- Turn immediately after doing a bite in SHall in scenario2. Dracula should
-- say False to presence in SHall almost all the time
scenario4 :: Int -> IO [(Bool, Int)]
scenario4 = scenarioResults (pure <$> isPresent st SHall) dist
where
dist = [ Cellar, SHall, Passage ]
st = GameState
{ sunlights = []
, positions = Left SHall : (Right <$> [ Gallery, Cellar, Tomb ])
, lastInfo = 1
, lastBite = 1
, canBite = True
}
-- Run the ai `run` `n` times (with initial dracula state `dist`) and return a
-- collation of the results
scenarioResults :: Eq a => DraculaState [a] -> [Room] -> Int -> IO [(a, Int)]
scenarioResults run dist n = sortBy (compare `on` (Down . snd))
<$> execStateT (replicateM n singleScenario) []
where
singleScenario = lift (evalStateT run dist) >>= traverse update
-- update the count of tuples
update a = modify $ \st -> case splitWhen ((== a) . fst) st of
(seen, []) -> (a, 1) : seen
(seen, (a', count):rest) -> (a', count + 1) : seen ++ rest
-- fast computation of (takeWhile (not . p) xs, dropWhile (not . p) xs)
splitWhen _ [] = ([], [])
splitWhen p (x:xs)
| p x = ([], x:xs)
| otherwise = first (x:) $ splitWhen p xs