-
Notifications
You must be signed in to change notification settings - Fork 1
/
Main.hs
297 lines (292 loc) · 12.3 KB
/
Main.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
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
module Main where
import Armor
import Definition
import Enemies
import Game
import Hero
import Parser
import Shoes
import Trees
import Weapons
import Control.Concurrent (threadDelay)
import Control.Monad (forM_, when)
import Control.Monad.RWS.Class (MonadState (put))
import Data.List
import Data.Maybe (isJust, isNothing, listToMaybe,
mapMaybe)
import System.Exit (exitSuccess)
import System.IO (hFlush, stdout)
import System.Posix.Internals (puts)
import qualified System.Random as SR
import Text.Read (readMaybe)
-- Main loop
repl :: IO ()
repl = do
putStrLn "Welcome to the game Infinite Blade!\n"
putStrLn "\t****************************************************"
putStrLn "\t******************** Background ********************"
putStrLn "\t****************************************************"
putStrLn "\nYou are at the entrance (root) of an ancient tree-like maze."
putStrLn "There are some enemies in the maze. Try to defeat them!"
putStrLn
"You need to find the exit of this ruined castle, defeat the deathless, and get the Infinity Blade to win the game."
putStrLn
"Do not forget to search for equipment to help you in your journey.\n"
putStrLn "\t****************************************************"
putStrLn "\t********************** Rules ***********************"
putStrLn "\t****************************************************"
putStrLn
"\nYou can go down to the children nodes, go up to the parent node, battle the enemy, search for equipment, "
putStrLn "check the current status of you and your enemy, or quit the game."
putStrLn
"You can only go down to a child node if the enemy in the current node is defeated."
putStrLn
"You can only search for equipment if the enemy in the current node is defeated."
putStrLn "You will win if you find the exit.\n"
putStrLn "\t****************************************************"
putStrLn "\t****************** Instructions ********************"
putStrLn "\t****************************************************"
putStrLn "\nYou can use the following commnads:"
putStrLn
"1. <go down to the first child> / <go down to child one>: go down to the child node with the given number. "
putStrLn "2. <go up>: go up to the parent node."
putStrLn "3. <battle>: battle the enemy in the current node."
putStrLn "4. <search>: search for equipment in the current node."
putStrLn "5. <check>: check the current status of you and your enemy."
putStrLn "6. <quit>: quit the game.\n"
putStrLn "\t****************************************************"
putStrLn "\n\nLet's start the game!\n\n"
let initialState = GameState sampleTree [1] initialHero sampleTree False
go initialState
where
go :: GameState -> IO ()
go state@(GameState pos path hero tree win) = do
case pos of
Leaf -> putStrLn "You see a leaf."
Node index name attrs children -> do
putStrLn "****************************************************"
putStrLn $ "You are in a room named " ++ name ++ "."
putStrLn "This is your current map:\n "
printVisitedTree tree index path
putStrLn ""
putStrLn "What's next?"
putStr "> "
hFlush stdout
line <- getLine
putStrLn "\n****************************************************"
-- case readMaybe line of
-- Just 1 -> handleCmd Go_Down state >>= go
-- Just 2 -> handleCmd Go_Up state >>= go
-- Just 3 -> handleCmd Battle state >>= go
-- Just 4 -> handleCmd Search state >>= go
-- Just 5 -> handleCmd Check state >>= go
-- Just 6 -> handleCmd Quit state >>= go
case parseInput parseCmd line of
Nothing -> do
putStrLn "I'm sorry, I do not understand."
go state
Just cmd -> handleCmd cmd state >>= go
handleCmd :: Cmd -> GameState -> IO GameState
handleCmd (Go_Down choice) state@(GameState pos path hero tree win) = do
case pos of
Node index _ attrs children -> do
if defeated attrs
then do
if null children
then do
putStrLn
"You are at a leaf node. You cannot go down any further!\n"
return state
else if choice > 0 && choice <= length children
then do
let nextNode = children !! (choice - 1)
case nextNode of
Node index_child _ attrs_child _ -> do
if exit attrs_child
then do
putStrLn
"Congratulations! You have found the exit and won the game!\n"
exitSuccess
else do
let newPath = path ++ [index_child]
newState =
state {currentPos = nextNode, path = newPath}
putStrLn
$ "You have entered the room: "
++ show choice
++ ".\n"
return newState
Leaf -> do
putStrLn "You have reached a leaf node.\n"
return state
else do
putStrLn "Invalid room number. Please try again.\n"
return state
else do
putStrLn
"This node is not defeated yet. You can only battle or go back to the parent!\n"
return state
Leaf -> do
putStrLn "You are at a leaf node. You cannot go down any further.\n"
return state
handleCmd Go_Up state@(GameState pos path hero tree win) = do
case findParent tree pos of
Nothing -> do
putStrLn "You are already at the root. You cannot go up any further.\n"
return state
Just parent -> do
case parent of
Node index _ _ _ -> do
let newPath = path ++ [index]
newState = state {currentPos = parent, path = newPath}
putStrLn "You climb up to the parent node.\n"
return newState
where
findParent :: GTree -> GTree -> Maybe GTree
findParent (Node n label attrs children) target =
if target `elem` children
then Just (Node n label attrs children)
else listToMaybe $ mapMaybe (`findParent` target) children
findParent Leaf _ = Nothing
handleCmd Battle state@(GameState pos path hero tree win) = do
case pos of
Node _ _ (NodeAttributes (Just enemy) _ _ _ False _) _ -> do
putStrLn "You have encountered an enemy! Prepare for battle."
gen <- SR.newStdGen
result <- fightEnemy hero enemy gen 1
case result of
Nothing -> do
putStrLn "You have been defeated. Game Over."
exitSuccess
Just newHero -> do
putStrLn "You have defeated the enemy!"
putStrLn "You can search and continue your journey.\n"
let newState = updateNodeAsDefeated pos state
return newState {hero}
Node _ _ (NodeAttributes (Just _) _ _ _ True _) _ -> do
putStrLn "This enemy has already been defeated."
return state
_ -> do
putStrLn "There is no enemy here to fight."
return state
where
updateNodeAsDefeated :: GTree -> GameState -> GameState
updateNodeAsDefeated targetNode state@(GameState pos path hero tree win) =
let newTree = markNodeAsDefeated targetNode tree
newPos =
if pos == targetNode
then updateNodeDefeatedFlag pos
else pos
in state {tree = newTree, currentPos = newPos}
markNodeAsDefeated :: GTree -> GTree -> GTree
markNodeAsDefeated targetNode (Node n label attrs children) =
if targetNode == Node n label attrs children
then Node n label (attrs {defeated = True}) children
else Node n label attrs (map (markNodeAsDefeated targetNode) children)
markNodeAsDefeated _ leaf = leaf
updateNodeDefeatedFlag :: GTree -> GTree
updateNodeDefeatedFlag (Node n label attrs children) =
Node n label (attrs {defeated = True}) children
updateNodeDefeatedFlag leaf = leaf
handleCmd Search state@(GameState pos path hero tree win) = do
case pos of
Node _ _ attrs _ -> do
if not (defeated attrs)
then do
putStrLn "You cannot search the room until you defeat the enemy."
return state
else do
putStrLn "Searching the room for equipment..."
let noEquipment =
isNothing (weapon attrs)
&& isNothing (armor attrs)
&& isNothing (shoes attrs)
if noEquipment
then do
putStrLn "Sorry, there are no equipments in this room."
return state
else do
putStrLn "You found some equipment!"
displayInfoEquipment hero
heroAfterWeapon <- checkAndEquipWeapon attrs hero
heroAfterArmor <- checkAndEquipArmor attrs heroAfterWeapon
heroAfterShoes <- checkAndEquipShoes attrs heroAfterArmor
putStrLn "Updated Hero Information:"
displayInfoEquipment heroAfterShoes
return state {hero = heroAfterShoes}
Leaf -> do
putStrLn "There is nothing to search for at a leaf."
return state
where
checkAndEquipWeapon :: NodeAttributes -> Hero -> IO Hero
checkAndEquipWeapon attrs hero =
case weapon attrs of
Just newWeapon -> do
putStrLn "You found a new weapon!"
displayInfoWeapon newWeapon
equipDecision <- askEquip "weapon"
return
$ if equipDecision
then hero {currentWeapon = newWeapon}
else hero
Nothing -> return hero
checkAndEquipArmor :: NodeAttributes -> Hero -> IO Hero
checkAndEquipArmor attrs hero =
case armor attrs of
Just newArmor -> do
putStrLn "You found new armor!"
displayInfoArmor newArmor
equipDecision <- askEquip "armor"
return
$ if equipDecision
then hero {currentArmor = newArmor}
else hero
Nothing -> return hero
checkAndEquipShoes :: NodeAttributes -> Hero -> IO Hero
checkAndEquipShoes attrs hero =
case shoes attrs of
Just newShoes -> do
putStrLn "You found new shoes!"
displayInfoShoe newShoes
equipDecision <- askEquip "shoes"
return
$ if equipDecision
then hero {currentShoes = newShoes}
else hero
Nothing -> return hero
askEquip :: String -> IO Bool
askEquip itemType = do
putStrLn $ "Do you want to equip the new " ++ itemType ++ "? (yes/no)"
decision <- getLine
return (decision == "yes" || decision == "Yes")
handleCmd Check state@(GameState pos path hero tree win) = do
putStrLn "Checking current status..."
displayInfoHero hero
putStrLn ""
case pos of
Node _ _ attrs _ ->
case enemy attrs of
Just enemy -> do
if defeated attrs
then putStrLn "This enemy has already been defeated."
else displayInfoEnemy enemy
Nothing -> putStrLn "There is no enemy here."
Leaf -> putStrLn "You are at a leaf node."
return state
handleCmd Quit state@(GameState pos path hero tree win) = do
putStrLn "Are you sure you want to quit? (y/n)"
putStr "> "
hFlush stdout
line <- getLine
case line of
"y" -> do
putStrLn "You have not finished the game. See you next time!"
exitSuccess
"n" -> do
putStrLn "You have chosen not to quit."
return state
_ -> do
putStrLn "Invalid input. You have chosen not to quit."
return state
main :: IO ()
main = repl