game.py 59 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721
  1. """How we keep track of the state of the game.
  2. Copyright 2010-2015 Brandon Rhodes. Licensed as free software under the
  3. Apache License, Version 2.0 as detailed in the accompanying README.txt.
  4. """
  5. # Numeric comments scattered through this file refer to FORTRAN line
  6. # numbers, for those comparing this file and `advent.for`; so "#2012"
  7. # refers to FORTRAN line number 2012 (which you can find easily in the
  8. # FORTRAN using Emacs with an interactive search for newline-2012-tab,
  9. # that is typed C-s C-q C-j 2 0 1 2 C-i).
  10. import os
  11. import pickle
  12. import random
  13. import zlib
  14. from operator import attrgetter
  15. from .data import Data
  16. from .model import Room, Message, Dwarf, Pirate
  17. YESNO_ANSWERS = {'y': True, 'yes': True, 'n': False, 'no': False}
  18. class Game(Data):
  19. look_complaints = 3 # how many times to "SORRY, BUT I AM NOT ALLOWED..."
  20. full_description_period = 5 # how often we use a room's full description
  21. full_wests = 0 # how many times they have typed "west" instead of "w"
  22. dwarf_stage = 0 # DFLAG how active the dwarves are
  23. dwarves_killed = 0 # DKILL
  24. knife_location = None # KNFLOC
  25. foobar = -1 # FOOBAR turn number of most recent still-valid "fee"
  26. gave_up = False
  27. treasures_not_found = 0 # TALLY how many treasures have not yet been seen
  28. impossible_treasures = 0 # TALLY2 how many treasures can never be retrieved
  29. lamp_turns = 330
  30. warned_about_dim_lamp = False
  31. bonus = 0 # how they exited the final bonus round
  32. is_dead = False # whether we are currently dead
  33. deaths = 0 # how many times the player has died
  34. max_deaths = 3 # how many times the player can die
  35. turns = 0
  36. def __init__(self, seed=None):
  37. Data.__init__(self)
  38. self.output = ''
  39. self.yesno_callback = False
  40. self.yesno_casual = False # whether to insist they answer
  41. self.clock1 = 30 # counts down from finding last treasure
  42. self.clock2 = 50 # counts down until cave closes
  43. self.is_closing = False # is the cave closing?
  44. self.panic = False # they tried to leave during closing?
  45. self.is_closed = False # is the cave closed?
  46. self.is_done = False # caller can check for "game over"
  47. self.could_fall_in_pit = False # could the player fall into a pit?
  48. self.random_generator = random.Random()
  49. if seed is not None:
  50. self.random_generator.seed(seed)
  51. def random(self):
  52. return self.random_generator.random()
  53. def choice(self, seq):
  54. return self.random_generator.choice(seq)
  55. def write(self, more):
  56. """Append the Unicode representation of `s` to our output."""
  57. if more:
  58. self.output += str(more).upper()
  59. self.output += '\n'
  60. def write_message(self, n):
  61. self.write(self.messages[n])
  62. def yesno(self, s, yesno_callback, casual=False):
  63. """Ask a question and prepare to receive a yes-or-no answer."""
  64. self.write(s)
  65. self.yesno_callback = yesno_callback
  66. self.yesno_casual = casual
  67. # Properties of the cave.
  68. @property
  69. def is_dark(self):
  70. lamp = self.objects['lamp']
  71. if self.is_here(lamp) and lamp.prop:
  72. return False
  73. return self.loc.is_dark
  74. @property
  75. def inventory(self):
  76. return [ obj for obj in self.object_list if obj.is_toting ]
  77. @property
  78. def treasures(self):
  79. return [ obj for obj in self.object_list if obj.is_treasure ]
  80. @property
  81. def objects_here(self):
  82. return self.objects_at(self.loc)
  83. def objects_at(self, room):
  84. return [ obj for obj in self.object_list if room in obj.rooms ]
  85. def is_here(self, obj):
  86. if isinstance(obj, Dwarf):
  87. return self.loc is obj.room
  88. else:
  89. return obj.is_toting or (self.loc in obj.rooms)
  90. @property
  91. def is_finished(self):
  92. return (self.is_dead or self.is_done) and not self.yesno_callback
  93. # Game startup
  94. def start(self):
  95. """Start the game."""
  96. # For old-fashioned players, accept five-letter truncations like
  97. # "inven" instead of insisting on full words like "inventory".
  98. for key, value in list(self.vocabulary.items()):
  99. if isinstance(key, str) and len(key) > 5:
  100. self.vocabulary[key[:5]] = value
  101. # Set things going.
  102. self.chest_room = self.rooms[114]
  103. self.bottle.contents = self.water
  104. self.yesno(self.messages[65], self.start2) # want instructions?
  105. def start2(self, yes):
  106. """Display instructions if the user wants them."""
  107. if yes:
  108. self.write_message(1)
  109. self.hints[3].used = True
  110. self.lamp_turns = 1000
  111. self.oldloc2 = self.oldloc = self.loc = self.rooms[1]
  112. self.dwarves = [ Dwarf(self.rooms[n]) for n in (19, 27, 33, 44, 64) ]
  113. self.pirate = Pirate(self.chest_room)
  114. treasures = self.treasures
  115. self.treasures_not_found = len(treasures)
  116. for treasure in treasures:
  117. treasure.prop = -1
  118. self.describe_location()
  119. # Routines that handle the aftermath of "big" actions like movement.
  120. # Although these are called at the end of each `do_command()` cycle,
  121. # we place here at the top of `game.py` to mirror the order in the
  122. # advent.for file.
  123. def move_to(self, newloc=None): #2
  124. loc = self.loc
  125. if newloc is None:
  126. newloc = loc
  127. if self.is_closing and newloc.is_aboveground:
  128. self.write_message(130)
  129. newloc = loc # cancel move and put him back underground
  130. if not self.panic:
  131. self.clock2 = 15
  132. self.panic = True
  133. must_allow_move = ((newloc is loc) or (loc.is_forced)
  134. or (loc.is_forbidden_to_pirate))
  135. dwarf_blocking_the_way = any(
  136. dwarf.old_room is newloc and dwarf.has_seen_adventurer
  137. for dwarf in self.dwarves
  138. )
  139. if not must_allow_move and dwarf_blocking_the_way:
  140. newloc = loc # cancel move they were going to make
  141. self.write_message(2) # dwarf is blocking the way
  142. self.loc = loc = newloc #74
  143. # IF LOC.EQ.0 ?
  144. is_dwarf_area = not (loc.is_forced or loc.is_forbidden_to_pirate)
  145. if is_dwarf_area and self.dwarf_stage > 0:
  146. self.move_dwarves()
  147. else:
  148. if is_dwarf_area and loc.is_after_hall_of_mists:
  149. self.dwarf_stage = 1
  150. self.describe_location()
  151. def move_dwarves(self):
  152. #6000
  153. if self.dwarf_stage == 1:
  154. # 5% chance per turn of meeting first dwarf
  155. if self.loc.is_before_hall_of_mists or self.random() < .95:
  156. self.describe_location()
  157. return
  158. self.dwarf_stage = 2
  159. for i in range(2): # randomly remove 0, 1, or 2 dwarves
  160. if self.random() < .5:
  161. self.dwarves.remove(self.choice(self.dwarves))
  162. for dwarf in self.dwarves:
  163. if dwarf.room is self.loc: # move dwarf away from our loc
  164. dwarf.start_at(self.rooms[18])
  165. self.write_message(3) # dwarf throws axe and curses
  166. self.axe.drop(self.loc)
  167. self.describe_location()
  168. return
  169. #6010
  170. dwarf_count = dwarf_attacks = knife_wounds = 0
  171. for dwarf in self.dwarves + [ self.pirate ]:
  172. locations = { move.action for move in dwarf.room.travel_table
  173. if dwarf.can_move(move)
  174. and move.action is not dwarf.old_room
  175. and move.action is not dwarf.room }
  176. # Without stabilizing the order with a sort, the room chosen
  177. # would depend on how the Room addresses in memory happen to
  178. # order the rooms in the set() - and make it impossible to
  179. # test the game by setting the random number generator seed
  180. # and then playing through the game.
  181. locations = sorted(locations, key=attrgetter('n'))
  182. if locations:
  183. new_room = self.choice(locations)
  184. else:
  185. new_room = dwarf.old_room
  186. dwarf.old_room, dwarf.room = dwarf.room, new_room
  187. if self.loc in (dwarf.room, dwarf.old_room):
  188. dwarf.has_seen_adventurer = True
  189. elif self.loc.is_before_hall_of_mists:
  190. dwarf.has_seen_adventurer = False
  191. if not dwarf.has_seen_adventurer:
  192. continue
  193. dwarf.room = self.loc
  194. if dwarf.is_dwarf:
  195. dwarf_count += 1
  196. # A dwarf cannot walk and attack at the same time.
  197. if dwarf.room is dwarf.old_room:
  198. dwarf_attacks += 1
  199. self.knife_location = self.loc
  200. if self.random() < .095 * (self.dwarf_stage - 2):
  201. knife_wounds += 1
  202. else: # the pirate
  203. pirate = dwarf
  204. if self.loc is self.chest_room or self.chest.prop >= 0:
  205. continue # decide that the pirate is not really here
  206. treasures = [ t for t in self.treasures if t.is_toting ]
  207. if (self.platinum in treasures and self.loc.n in (100, 101)):
  208. treasures.remove(self.platinum)
  209. if not treasures:
  210. h = any( t for t in self.treasures if self.is_here(t) )
  211. one_treasure_left = (self.treasures_not_found ==
  212. self.impossible_treasures + 1)
  213. shiver_me_timbers = (
  214. one_treasure_left and not h and not(self.chest.rooms)
  215. and self.is_here(self.lamp) and self.lamp.prop == 1
  216. )
  217. if not shiver_me_timbers:
  218. if (pirate.old_room != pirate.room
  219. and self.random() < .2):
  220. self.write_message(127)
  221. continue # pragma: no cover
  222. self.write_message(186)
  223. self.chest.drop(self.chest_room)
  224. self.message.drop(self.rooms[140])
  225. else:
  226. #6022 I'll just take all this booty
  227. self.write_message(128)
  228. if not self.message.rooms:
  229. self.chest.drop(self.chest_room)
  230. self.message.drop(self.rooms[140])
  231. for treasure in treasures:
  232. treasure.drop(self.chest_room)
  233. #6024
  234. pirate.old_room = pirate.room = self.chest_room
  235. pirate.has_seen_adventurer = False # free to move
  236. # Report what has happened.
  237. if dwarf_count == 1:
  238. self.write_message(4)
  239. elif dwarf_count:
  240. self.write('There are {} threatening little dwarves in the'
  241. ' room with you.\n'.format(dwarf_count))
  242. if dwarf_attacks and self.dwarf_stage == 2:
  243. self.dwarf_stage = 3
  244. if dwarf_attacks == 1:
  245. self.write_message(5)
  246. k = 52
  247. elif dwarf_attacks:
  248. self.write('{} of them throw knives at you!\n'.format(dwarf_attacks))
  249. k = 6
  250. if not dwarf_attacks:
  251. pass
  252. elif not knife_wounds:
  253. self.write_message(k)
  254. else:
  255. if knife_wounds == 1:
  256. self.write_message(k + 1)
  257. else:
  258. self.write('{} of them get you!\n'.format(knife_wounds))
  259. self.oldloc2 = self.loc
  260. self.die()
  261. return
  262. self.describe_location()
  263. def describe_location(self): #2000
  264. loc = self.loc
  265. if loc.n == 0:
  266. self.die()
  267. could_fall = self.is_dark and self.could_fall_in_pit
  268. if could_fall and not loc.is_forced and self.random() < .35:
  269. self.die_here()
  270. return
  271. if self.bear.is_toting:
  272. self.write_message(141)
  273. if self.is_dark and not loc.is_forced:
  274. self.write_message(16)
  275. else:
  276. do_short = loc.times_described % self.full_description_period
  277. loc.times_described += 1
  278. if do_short and loc.short_description:
  279. self.write(loc.short_description)
  280. else:
  281. self.write(loc.long_description)
  282. if loc.is_forced:
  283. self.do_motion(self.vocabulary[2]) # dummy motion verb
  284. return
  285. if loc.n == 33 and self.random() < .25 and not self.is_closing:
  286. self.write_message(8)
  287. if not self.is_dark:
  288. for obj in self.objects_here:
  289. if obj is self.steps and self.gold.is_toting:
  290. continue
  291. if obj.prop < 0: # finding a treasure the first time
  292. if self.is_closed:
  293. continue
  294. obj.prop = 1 if obj in (self.rug, self.chain) else 0
  295. self.treasures_not_found -= 1
  296. if (self.treasures_not_found > 0 and
  297. self.treasures_not_found == self.impossible_treasures):
  298. self.lamp_turns = min(35, self.lamp_turns)
  299. if obj is self.steps and self.loc is self.steps.rooms[1]:
  300. prop = 1
  301. else:
  302. prop = obj.prop
  303. self.write(obj.messages[prop])
  304. self.finish_turn()
  305. def say_okay_and_finish(self, *ignored): #2009
  306. self.write_message(54)
  307. self.finish_turn()
  308. #2009 sets SPK="OK" then...
  309. #2010 sets SPK to K
  310. #2011 speaks SPK then...
  311. #2012 blanks VERB and OBJ and calls:
  312. def finish_turn(self, obj=None): #2600
  313. # Advance random number generator so each input affects future.
  314. self.random()
  315. # Check whether we should offer a hint.
  316. for hint in self.hints.values():
  317. if hint.turns_needed == 9999 or hint.used:
  318. continue
  319. if self.loc in hint.rooms:
  320. hint.turn_counter += 1
  321. if hint.turn_counter >= hint.turns_needed:
  322. if hint.n != 5: # hint 5 counter does not get reset
  323. hint.turn_counter = 0
  324. if self.should_offer_hint(hint, obj):
  325. hint.turn_counter = 0
  326. def callback(yes):
  327. if yes:
  328. self.write(hint.message)
  329. hint.used = True
  330. else:
  331. self.write_message(54)
  332. self.yesno(hint.question, callback)
  333. return
  334. else:
  335. hint.turn_counter = 0
  336. if self.is_closed:
  337. if self.oyster.prop < 0 and self.oyster.is_toting:
  338. self.write(self.oyster.messages[1])
  339. for obj in self.inventory:
  340. if obj.prop < 0:
  341. obj.prop = - 1 - obj.prop
  342. self.could_fall_in_pit = self.is_dark #2605
  343. if self.knife_location and self.knife_location is not self.loc:
  344. self.knife_location = None
  345. # The central do_command() method, that should be called over and
  346. # over again with words supplied by the user.
  347. def do_command(self, words):
  348. """Parse and act upon the command in the list of strings `words`."""
  349. self.output = ''
  350. self._do_command(words)
  351. return self.output
  352. def _do_command(self, words):
  353. if self.yesno_callback is not None:
  354. answer = YESNO_ANSWERS.get(words[0], None)
  355. if answer is None:
  356. if self.yesno_casual:
  357. self.yesno_callback = None
  358. else:
  359. self.write('Please answer the question.')
  360. return
  361. else:
  362. callback = self.yesno_callback
  363. self.yesno_callback = None
  364. callback(answer)
  365. return
  366. if self.is_dead:
  367. self.write('You have gotten yourself killed.')
  368. return
  369. #2608
  370. self.turns += 1
  371. if (self.treasures_not_found == 0
  372. and self.loc.n >= 15 and self.loc.n != 33):
  373. self.clock1 -= 1
  374. if self.clock1 == 0:
  375. self.start_closing_cave() # no "return", to do their command
  376. if self.clock1 < 0:
  377. self.clock2 -= 1
  378. if self.clock2 == 0:
  379. return self.close_cave() # "return", to cancel their command
  380. if self.lamp.prop == 1:
  381. self.lamp_turns -= 1
  382. if self.lamp_turns <= 30 and self.is_here(self.batteries) \
  383. and self.batteries.prop == 0 and self.is_here(self.lamp):
  384. #12000
  385. self.write_message(188)
  386. self.batteries.prop = 1
  387. if self.batteries.is_toting:
  388. self.batteries.drop(self.loc)
  389. self.lamp_turns += 2500
  390. self.warned_about_dim_lamp = False
  391. elif self.lamp_turns == 0:
  392. #12400
  393. self.lamp_turns = -1
  394. self.lamp.prop = 0
  395. if self.is_here(self.lamp):
  396. self.write_message(184)
  397. elif self.lamp_turns < 0 and self.loc.is_aboveground:
  398. #12600
  399. self.write_message(185)
  400. self.gave_up = True
  401. self.score_and_exit()
  402. return
  403. elif self.lamp_turns <= 30 and not self.warned_about_dim_lamp \
  404. and self.is_here(self.lamp):
  405. #12200
  406. self.warned_about_dim_lamp = True
  407. if self.batteries.prop == 1:
  408. self.write_message(189)
  409. elif not self.batteries.rooms:
  410. self.write_message(183)
  411. else:
  412. self.write_message(187)
  413. self.dispatch_command(words)
  414. def dispatch_command(self, words): #19999
  415. if not 1 <= len(words) <= 2:
  416. return self.dont_understand()
  417. if words[0] == 'save' and len(words) > 1:
  418. # Handle suspend separately, since filename can be anything,
  419. # and is not restricted to being a vocabulary word (and, in
  420. # fact, it can be an open file).
  421. return self.t_suspend(words[0], words[1])
  422. words = [ self.vocabulary.get(word) for word in words ]
  423. if None in words:
  424. return self.dont_understand()
  425. word1 = words[0]
  426. word2 = words[1] if len(words) == 2 else None
  427. if word1 == 'enter' and (word2 == 'stream' or word2 == 'water'):
  428. if self.loc.liquid is self.water:
  429. self.write_message(70)
  430. else:
  431. self.write_message(43)
  432. return self.finish_turn()
  433. if (word1 == 'enter' or word1 == 'walk') and word2:
  434. #2800 'enter house' becomes simply 'house' and so forth
  435. word1, word2 = word2, None
  436. if ((word1 == 'water' or word1 == 'oil') and
  437. (word2 == 'plant' or word2 == 'door') and
  438. self.is_here(self.referent(word2))):
  439. word1, word2 = self.vocabulary['pour'], word1
  440. if word1 == 'say':
  441. return self.t_say(word1, word2) if word2 else self.i_say(word1)
  442. kinds = (word1.kind, word2.kind if word2 else None)
  443. #2630
  444. if kinds == ('travel', None):
  445. if word1.text == 'west': #2610
  446. self.full_wests += 1
  447. if self.full_wests == 10:
  448. self.write_message(17)
  449. return self.do_motion(word1)
  450. if kinds == ('snappy_comeback', None):
  451. self.write_message(word1.n % 1000)
  452. return self.finish_turn()
  453. if kinds == ('noun', None):
  454. verb, noun = None, word1
  455. elif kinds == ('verb', None):
  456. verb, noun = word1, None
  457. elif kinds == ('verb', 'noun'):
  458. verb, noun = word1, word2
  459. elif kinds == ('noun', 'verb'):
  460. noun, verb = word1, word2
  461. else:
  462. return self.dont_understand()
  463. if not noun:
  464. obj = None
  465. else:
  466. obj = self.referent(noun)
  467. obj_here = self.is_here(obj)
  468. if not obj_here:
  469. if obj is self.grate:
  470. if self.loc.n in (1, 4, 7):
  471. return self.dispatch_command([ 'depression' ])
  472. elif 9 < self.loc.n < 15:
  473. return self.dispatch_command([ 'entrance' ])
  474. elif noun == 'dwarf':
  475. obj_here = any( d.room is self.loc for d in self.dwarves )
  476. elif obj is self.bottle.contents and self.is_here(self.bottle):
  477. obj_here = True
  478. elif obj is self.loc.liquid:
  479. obj_here = True
  480. elif (obj is self.plant and self.is_here(self.plant2)
  481. and self.plant2.prop != 0):
  482. obj = self.plant2
  483. obj_here = True
  484. elif obj is self.knife and self.knife_location is self.loc:
  485. self.knife_location = None
  486. self.write_message(116)
  487. return self.finish_turn()
  488. elif obj is self.rod and self.is_here(self.rod2):
  489. obj = self.rod2
  490. obj_here = True
  491. elif verb and (verb == 'find' or verb == 'inventory'):
  492. obj_here = True # lie; these verbs work for absent objects
  493. if not obj_here:
  494. return self.i_see_no(noun)
  495. if not verb:
  496. self.write('What do you want to do with the {}?\n'.format(
  497. noun.text))
  498. return self.finish_turn()
  499. verb_name = verb.synonyms[0].text
  500. if obj:
  501. method_name = 't_' + verb_name
  502. args = (verb, obj)
  503. else:
  504. method_name = 'i_' + verb_name
  505. args = (verb,)
  506. method = getattr(self, method_name)
  507. method(*args)
  508. def dont_understand(self):
  509. #3000 (a bit earlier than in the Fortran code)
  510. n = self.random()
  511. if n < 0.20: # 20% of the entire 1.0 range of random()
  512. self.write_message(61)
  513. elif n < 0.36: # 20% of the remaining 0.8 left
  514. self.write_message(13)
  515. else:
  516. self.write_message(60)
  517. self.finish_turn()
  518. def i_see_no(self, thing):
  519. self.write('I see no {} here.\n'.format(getattr(thing, 'text', thing)))
  520. self.finish_turn()
  521. # Motion.
  522. def do_motion(self, word): #8
  523. if word == 'null': #2
  524. self.move_to()
  525. return
  526. elif word == 'back': #20
  527. dest = self.oldloc2 if self.oldloc.is_forced else self.oldloc
  528. self.oldloc2, self.oldloc = self.oldloc, self.loc
  529. if dest is self.loc:
  530. self.write_message(91)
  531. self.move_to()
  532. return
  533. alt = None
  534. for move in self.loc.travel_table:
  535. if move.action is dest:
  536. word = move.verbs[0] # arbitrary verb going to `dest`
  537. break # Fall through, to attempt the move.
  538. elif (isinstance(move.action, Room)
  539. and move.action.is_forced
  540. and move.action.travel_table[0].action is dest):
  541. alt = move.verbs[0]
  542. else: # no direct route is available
  543. if alt is not None:
  544. word = alt # take a forced move if it's the only option
  545. else:
  546. self.write_message(140)
  547. self.move_to()
  548. return
  549. elif word == 'look': #30
  550. if self.look_complaints > 0:
  551. self.write_message(15)
  552. self.look_complaints -= 1
  553. self.loc.times_described = 0
  554. self.move_to()
  555. self.could_fall_in_pit = False
  556. return
  557. elif word == 'cave': #40
  558. self.write_message(57 if self.loc.is_aboveground else 58)
  559. self.move_to()
  560. return
  561. self.oldloc2, self.oldloc = self.oldloc, self.loc
  562. for move in self.loc.travel_table:
  563. if move.is_forced or word in move.verbs:
  564. c = move.condition
  565. if c[0] is None or c[0] == 'not_dwarf':
  566. allowed = True
  567. elif c[0] == '%':
  568. allowed = 100 * self.random() < c[1]
  569. elif c[0] == 'carrying':
  570. allowed = self.objects[c[1]].is_toting
  571. elif c[0] == 'carrying_or_in_room_with':
  572. allowed = self.is_here(self.objects[c[1]])
  573. elif c[0] == 'prop!=':
  574. allowed = self.objects[c[1]].prop != c[2]
  575. if not allowed:
  576. continue
  577. if isinstance(move.action, Room):
  578. self.move_to(move.action)
  579. return
  580. elif isinstance(move.action, Message):
  581. self.write(move.action)
  582. self.move_to()
  583. return
  584. elif move.action == 301: #30100
  585. inv = self.inventory
  586. if len(inv) != 0 and inv != [ self.emerald ]:
  587. self.write_message(117)
  588. self.move_to()
  589. elif self.loc.n == 100:
  590. self.move_to(self.rooms[99])
  591. else:
  592. self.move_to(self.rooms[100])
  593. return
  594. elif move.action == 302: #30200
  595. self.emerald.drop(self.loc)
  596. self.do_motion(word)
  597. return
  598. elif move.action == 303: #30300
  599. troll, troll2 = self.troll, self.troll2
  600. if troll.prop == 1:
  601. self.write(troll.messages[1])
  602. troll.prop = 0
  603. troll.rooms = list(troll.starting_rooms)
  604. troll2.destroy()
  605. self.move_to()
  606. return
  607. else:
  608. places = list(troll.starting_rooms)
  609. places.remove(self.loc)
  610. self.loc = places[0] # "the other side of the bridge"
  611. if troll.prop == 0:
  612. troll.prop = 1
  613. if not self.bear.is_toting:
  614. self.move_to()
  615. return
  616. self.write_message(162)
  617. self.chasm.prop = 1
  618. troll.prop = 2
  619. self.bear.drop(self.loc)
  620. self.bear.is_fixed = True
  621. self.bear.prop = 3
  622. if self.spices.prop < 0:
  623. self.impossible_treasures += 1
  624. self.oldloc2 = self.loc # refuse to strand belongings
  625. self.die()
  626. return
  627. #50
  628. n = word.n
  629. if 29 <= n <= 30 or 43 <= n <= 50:
  630. self.write_message(9)
  631. elif n in (7, 36, 37):
  632. self.write_message(10)
  633. elif n in (11, 19):
  634. self.write_message(11)
  635. elif n in (62, 65):
  636. self.write_message(42)
  637. elif n == 17:
  638. self.write_message(80)
  639. else:
  640. self.write_message(12)
  641. self.move_to()
  642. return
  643. # Death and reincarnation.
  644. def die_here(self): #90
  645. self.write_message(23)
  646. self.oldloc2 = self.loc
  647. self.die()
  648. def die(self): #99
  649. self.deaths += 1
  650. self.is_dead = True
  651. if self.is_closing:
  652. self.write_message(131)
  653. self.score_and_exit()
  654. return
  655. def callback(yes):
  656. if yes:
  657. self.write_message(80 + self.deaths * 2)
  658. if self.deaths < self.max_deaths:
  659. # do water and oil thing
  660. self.is_dead = False
  661. if self.lamp.is_toting:
  662. self.lamp.prop = 0
  663. for obj in self.inventory:
  664. if obj is self.lamp:
  665. obj.drop(self.rooms[1])
  666. else:
  667. obj.drop(self.oldloc2)
  668. self.loc = self.rooms[3]
  669. self.describe_location()
  670. return
  671. else:
  672. self.write_message(54)
  673. self.score_and_exit()
  674. self.yesno(self.messages[79 + self.deaths * 2], callback)
  675. # Verbs.
  676. def ask_verb_what(self, verb, *args): #8000
  677. self.write('{} What?\n'.format(verb.text))
  678. self.finish_turn()
  679. i_walk = ask_verb_what
  680. i_drop = ask_verb_what
  681. i_say = ask_verb_what
  682. i_nothing = say_okay_and_finish
  683. i_wave = ask_verb_what
  684. i_calm = ask_verb_what
  685. i_rub = ask_verb_what
  686. i_throw = ask_verb_what
  687. i_find = ask_verb_what
  688. i_feed = ask_verb_what
  689. i_break = ask_verb_what
  690. i_wake = ask_verb_what
  691. def write_default_message(self, verb, *args):
  692. self.write(verb.default_message)
  693. self.finish_turn()
  694. t_nothing = say_okay_and_finish
  695. t_calm = write_default_message
  696. t_quit = write_default_message
  697. t_score = write_default_message
  698. t_fee = write_default_message
  699. t_brief = write_default_message
  700. t_hours = write_default_message
  701. def i_carry(self, verb): #8010
  702. is_dwarf_here = any( dwarf.room == self.loc for dwarf in self.dwarves )
  703. objs = self.objects_here
  704. if len(objs) != 1 or is_dwarf_here:
  705. self.ask_verb_what(verb)
  706. else:
  707. self.t_carry(verb, objs[0])
  708. def t_carry(self, verb, obj): #9010
  709. if obj.is_toting:
  710. self.write(verb.default_message)
  711. self.finish_turn()
  712. return
  713. if obj.is_fixed or len(obj.rooms) > 1:
  714. if obj is self.plant and obj.prop <= 0:
  715. self.write_message(115)
  716. elif obj is self.bear and obj.prop == 1:
  717. self.write_message(169)
  718. elif obj is self.chain and self.chain.prop != 0:
  719. self.write_message(170)
  720. else:
  721. self.write_message(25)
  722. self.finish_turn()
  723. return
  724. if obj is self.water or obj is self.oil:
  725. if self.is_here(self.bottle) and self.bottle.contents is obj:
  726. # They want to carry the filled bottle.
  727. obj = self.bottle
  728. else:
  729. # They must mean they want to fill the bottle.
  730. if not self.bottle.is_toting:
  731. self.write_message(104)
  732. elif self.bottle.contents is not None:
  733. self.write_message(105)
  734. else:
  735. self.t_fill(verb, self.bottle) # hand off control to "fill"
  736. return
  737. self.finish_turn()
  738. return
  739. if len(self.inventory) >= 7:
  740. self.write_message(92)
  741. self.finish_turn()
  742. return
  743. if obj is self.bird and obj.prop == 0:
  744. if self.rod.is_toting:
  745. self.write_message(26)
  746. self.finish_turn(obj) # needs obj to decide to give hint
  747. return
  748. if not self.cage.is_toting:
  749. self.write_message(27)
  750. self.finish_turn()
  751. return
  752. self.bird.prop = 1
  753. if (obj is self.bird or obj is self.cage) and self.bird.prop != 0:
  754. self.bird.carry()
  755. self.cage.carry()
  756. else:
  757. obj.carry()
  758. if obj is self.bottle and self.bottle.contents is not None:
  759. self.bottle.contents.carry()
  760. self.say_okay_and_finish()
  761. def t_drop(self, verb, obj): #9020
  762. if obj is self.rod and not self.rod.is_toting and self.rod2.is_toting:
  763. obj = self.rod2
  764. if not obj.is_toting:
  765. self.write(verb.default_message)
  766. self.finish_turn()
  767. return
  768. bird, snake, dragon, bear, troll = self.bird, self.snake, self.dragon, \
  769. self.bear, self.troll
  770. if obj is bird and self.is_here(snake):
  771. self.write_message(30)
  772. if self.is_closed:
  773. self.wake_repository_dwarves()
  774. return
  775. snake.prop = 1
  776. snake.destroy()
  777. elif obj is self.coins and self.is_here(self.machine):
  778. obj.destroy()
  779. self.batteries.drop(self.loc)
  780. self.write(self.batteries.messages[0])
  781. self.finish_turn()
  782. return
  783. elif obj is bird and self.is_here(dragon) and dragon.prop == 0:
  784. self.write_message(154)
  785. bird.destroy()
  786. bird.prop = 0
  787. if snake.rooms:
  788. self.impossible_treasures += 1
  789. self.finish_turn()
  790. return
  791. elif obj is bear and self.is_here(troll):
  792. self.write_message(163)
  793. troll.destroy()
  794. self.troll2.rooms = list(self.troll.starting_rooms)
  795. troll.prop = 2
  796. elif obj is self.vase and self.loc is not self.rooms[96]:
  797. if self.pillow.is_at(self.loc):
  798. self.vase.prop = 0
  799. else:
  800. self.vase.prop = 2
  801. self.vase.is_fixed = True
  802. self.write(self.vase.messages[self.vase.prop + 1])
  803. else:
  804. self.write_message(54)
  805. #9021
  806. if obj is self.bottle.contents:
  807. obj = self.bottle
  808. if obj is self.bottle and self.bottle.contents:
  809. self.bottle.contents.hide()
  810. if obj is self.cage and self.bird.prop != 0:
  811. bird.drop(self.loc)
  812. elif obj is self.bird:
  813. obj.prop = 0
  814. obj.drop(self.loc)
  815. self.finish_turn()
  816. return
  817. def t_say(self, verb, word): #9030
  818. if word.n in (62, 65, 71, 2025):
  819. self.dispatch_command([ word.text ])
  820. else:
  821. self.write('Okay, "{}".'.format(word.text))
  822. self.finish_turn()
  823. def i_unlock(self, verb): #8040 Handles "unlock" case as well
  824. objs = (self.grate, self.door, self.oyster, self.clam, self.chain)
  825. objs = list(filter(self.is_here, objs))
  826. if len(objs) > 1:
  827. self.ask_verb_what(verb)
  828. elif len(objs) == 1:
  829. self.t_unlock(verb, objs[0])
  830. else:
  831. self.write_message(28)
  832. self.finish_turn()
  833. i_lock = i_unlock
  834. def t_unlock(self, verb, obj): #9040 Handles "lock" case as well
  835. if obj is self.clam or obj is self.oyster:
  836. #9046
  837. oy = 1 if (obj is self.oyster) else 0
  838. if verb == 'lock':
  839. self.write_message(61)
  840. elif not self.trident.is_toting:
  841. self.write_message(122 + oy)
  842. elif obj.is_toting:
  843. self.write_message(120 + oy)
  844. elif obj is self.oyster:
  845. self.write_message(125)
  846. else:
  847. self.write_message(124)
  848. self.clam.destroy()
  849. self.oyster.drop(self.loc)
  850. self.pearl.drop(self.rooms[105])
  851. elif obj is self.door:
  852. if obj.prop == 1:
  853. self.write_message(54)
  854. else:
  855. self.write_message(111)
  856. elif obj is self.cage:
  857. self.write_message(32)
  858. elif obj is self.keys:
  859. self.write_message(55)
  860. elif obj is self.grate or obj is self.chain:
  861. if not self.is_here(self.keys):
  862. self.write_message(31)
  863. elif obj is self.chain:
  864. #9048
  865. if verb == 'unlock':
  866. if self.chain.prop == 0:
  867. self.write_message(37)
  868. elif self.bear.prop == 0:
  869. self.write_message(41)
  870. else:
  871. self.chain.prop = 0
  872. self.chain.is_fixed = False
  873. if self.bear.prop != 3:
  874. self.bear.prop = 2
  875. self.bear.is_fixed = 2 - self.bear.prop
  876. self.write_message(171)
  877. else:
  878. #9049
  879. if self.loc not in self.chain.starting_rooms:
  880. self.write_message(173)
  881. elif self.chain.prop != 0:
  882. self.write_message(34)
  883. else:
  884. self.chain.prop = 2
  885. if self.chain.is_toting:
  886. self.chain.drop(self.loc)
  887. self.chain.is_fixed = True
  888. self.write_message(172)
  889. elif self.is_closing:
  890. if not self.panic:
  891. self.clock2 = 15
  892. self.panic = True
  893. self.write_message(130)
  894. else:
  895. #9043
  896. oldprop = obj.prop
  897. obj.prop = 0 if verb == 'lock' else 1
  898. self.write_message(34 + oldprop + 2 * obj.prop)
  899. else:
  900. self.write(verb.default_message)
  901. self.finish_turn()
  902. t_lock = t_unlock
  903. def t_light(self, verb, obj=None): #9070
  904. if not self.is_here(self.lamp):
  905. self.write(verb.default_message)
  906. elif self.lamp_turns <= 0:
  907. self.write_message(184)
  908. else:
  909. self.lamp.prop = 1
  910. self.write_message(39)
  911. if self.loc.is_dark:
  912. return self.describe_location()
  913. self.finish_turn()
  914. i_light = t_light
  915. def t_extinguish(self, verb, obj=None): #9080
  916. if not self.is_here(self.lamp):
  917. self.write(verb.default_message)
  918. else:
  919. self.lamp.prop = 0
  920. self.write_message(40)
  921. if self.loc.is_dark:
  922. self.write_message(16)
  923. self.finish_turn()
  924. i_extinguish = t_extinguish
  925. def t_wave(self, verb, obj): #9090
  926. fissure = self.fissure
  927. if (obj is self.rod and obj.is_toting and self.is_here(fissure)
  928. and not self.is_closing):
  929. fissure.prop = 0 if fissure.prop else 1
  930. self.write(fissure.messages[2 - fissure.prop])
  931. else:
  932. if obj.is_toting or (obj is self.rod and self.rod2.is_toting):
  933. self.write(verb.default_message)
  934. else:
  935. self.write_message(29)
  936. self.finish_turn()
  937. def i_attack(self, verb): #9120
  938. enemies = [ self.snake, self.dragon, self.troll, self.bear ]
  939. if self.dwarf_stage >= 2:
  940. enemies.extend(self.dwarves)
  941. dangers = list(filter(self.is_here, enemies))
  942. if len(dangers) > 1:
  943. return self.ask_verb_what(verb)
  944. if len(dangers) == 1:
  945. return self.t_attack(verb, dangers[0])
  946. targets = []
  947. if self.is_here(self.bird) and verb != 'throw':
  948. targets.append(self.bird)
  949. if self.is_here(self.clam) or self.is_here(self.oyster):
  950. targets.append(self.clam)
  951. if len(targets) > 1:
  952. return self.ask_verb_what(verb)
  953. elif len(targets) == 1:
  954. return self.t_attack(verb, targets[0])
  955. else:
  956. return self.t_attack(verb, None)
  957. def t_attack(self, verb, obj): #9124 (but control goes to 9120 first)
  958. if obj is self.bird:
  959. if self.is_closed:
  960. self.write_message(137)
  961. else:
  962. obj.destroy()
  963. obj.prop = 0
  964. if self.snake.rooms:
  965. self.impossible_treasures += 1
  966. self.write_message(45)
  967. elif obj is self.clam or obj is self.oyster:
  968. self.write_message(150)
  969. elif obj is self.snake:
  970. self.write_message(46)
  971. elif obj is self.dwarf:
  972. if self.is_closed:
  973. self.wake_repository_dwarves()
  974. return
  975. self.write_message(49)
  976. elif obj is self.dragon:
  977. if self.dragon.prop != 0:
  978. self.write_message(167)
  979. else:
  980. def callback(yes):
  981. self.write(obj.messages[1])
  982. obj.prop = 2
  983. obj.is_fixed = True
  984. oldroom1 = obj.rooms[0]
  985. oldroom2 = obj.rooms[1]
  986. newroom = self.rooms[ (oldroom1.n + oldroom2.n) // 2 ]
  987. obj.drop(newroom)
  988. self.rug.prop = 0
  989. self.rug.is_fixed = False
  990. self.rug.drop(newroom)
  991. for oldroom in (oldroom1, oldroom2):
  992. for o in self.objects_at(oldroom):
  993. o.drop(newroom)
  994. self.move_to(newroom)
  995. self.yesno(self.messages[49], callback, casual=True)
  996. return
  997. elif obj is self.troll:
  998. self.write_message(157)
  999. elif obj is self.bear:
  1000. self.write_message(165 + (self.bear.prop + 1) // 2)
  1001. else:
  1002. self.write_message(44)
  1003. self.finish_turn()
  1004. def i_pour(self, verb): #9130
  1005. if self.bottle.contents is None:
  1006. self.ask_verb_what(verb)
  1007. else:
  1008. self.t_pour(verb, self.bottle.contents)
  1009. def t_pour(self, verb, obj):
  1010. if obj is self.bottle:
  1011. return self.i_pour(verb)
  1012. if not obj.is_toting:
  1013. self.write(verb.default_message)
  1014. elif obj is not self.oil and obj is not self.water:
  1015. self.write_message(78)
  1016. else:
  1017. self.bottle.prop = 1
  1018. self.bottle.contents = None
  1019. obj.hide()
  1020. if self.is_here(self.plant):
  1021. if obj is not self.water:
  1022. self.write_message(112)
  1023. else:
  1024. self.write(self.plant.messages[self.plant.prop + 1])
  1025. self.plant.prop = (self.plant.prop + 2) % 6
  1026. self.plant2.prop = self.plant.prop // 2
  1027. return self.move_to()
  1028. elif self.is_here(self.door):
  1029. #9132
  1030. self.door.prop = 1 if obj is self.oil else 0
  1031. self.write_message(113 + self.door.prop)
  1032. else:
  1033. self.write_message(77)
  1034. return self.finish_turn()
  1035. def i_eat(self, verb): #8140
  1036. if self.is_here(self.food):
  1037. self.t_eat(verb, self.food)
  1038. else:
  1039. self.ask_verb_what(verb)
  1040. def t_eat(self, verb, obj): #9140
  1041. if obj is self.food:
  1042. #8142
  1043. self.food.destroy()
  1044. self.write_message(72)
  1045. elif obj in (self.bird, self.snake, self.clam, self.oyster,
  1046. self.dwarf, self.dragon, self.troll, self.bear):
  1047. self.write_message(71)
  1048. else:
  1049. self.write(verb.default_message)
  1050. self.finish_turn()
  1051. def i_drink(self, verb): #9150
  1052. if self.is_here(self.water) or self.loc.liquid is self.water:
  1053. self.t_drink(verb, self.water)
  1054. else:
  1055. self.ask_verb_what(verb)
  1056. def t_drink(self, verb, obj): #9150
  1057. if obj is not self.water:
  1058. self.write_message(110)
  1059. elif self.is_here(self.water):
  1060. self.bottle.prop = 1
  1061. self.bottle.contents = None
  1062. self.water.destroy()
  1063. self.write_message(74)
  1064. elif self.loc.liquid is self.water:
  1065. self.write(verb.default_message)
  1066. self.finish_turn()
  1067. def t_rub(self, verb, obj): #9160
  1068. if obj is self.lamp:
  1069. self.write(verb.default_message)
  1070. else:
  1071. self.write_message(71)
  1072. self.finish_turn()
  1073. def t_throw(self, verb, obj): #9170
  1074. if obj is self.rod and not self.rod.is_toting and self.rod2.is_toting:
  1075. obj = self.rod2
  1076. if not obj.is_toting:
  1077. self.write(verb.default_message)
  1078. self.finish_turn()
  1079. return
  1080. if obj.is_treasure and self.is_here(self.troll):
  1081. # Pay the troll toll
  1082. self.write_message(159)
  1083. obj.destroy()
  1084. self.troll.destroy()
  1085. self.troll2.rooms = list(self.troll.starting_rooms)
  1086. self.finish_turn()
  1087. return
  1088. if obj is self.food and self.is_here(self.bear):
  1089. self.t_feed(verb, self.bear)
  1090. return
  1091. if obj is not self.axe:
  1092. self.t_drop(verb, obj)
  1093. return
  1094. dwarves_here = [ d for d in self.dwarves if d.room is self.loc ]
  1095. if dwarves_here:
  1096. # 1/3rd chance that throwing the axe kills a dwarf
  1097. if self.choice((True, False, False)):
  1098. self.dwarves.remove(dwarves_here[0])
  1099. self.dwarves_killed += 1
  1100. if self.dwarves_killed == 1:
  1101. self.write_message(149)
  1102. else:
  1103. self.write_message(47)
  1104. else:
  1105. self.write_message(48) # Miss
  1106. self.axe.drop(self.loc)
  1107. self.do_motion(self.vocabulary['null'])
  1108. return
  1109. if self.is_here(self.dragon) and self.dragon.prop == 0:
  1110. self.write_message(152)
  1111. self.axe.drop(self.loc)
  1112. self.do_motion(self.vocabulary['null'])
  1113. return
  1114. if self.is_here(self.troll):
  1115. self.write_message(158)
  1116. self.axe.drop(self.loc)
  1117. self.do_motion(self.vocabulary['null'])
  1118. return
  1119. if self.is_here(self.bear) and self.bear.prop == 0:
  1120. self.write_message(164)
  1121. self.axe.drop(self.loc)
  1122. self.axe.is_fixed = True
  1123. self.axe.prop = 1
  1124. self.finish_turn()
  1125. return
  1126. self.t_attack(verb, None)
  1127. def i_quit(self, verb): #8180
  1128. def callback(yes):
  1129. self.write_message(54)
  1130. if yes:
  1131. self.score_and_exit()
  1132. self.yesno(self.messages[22], callback)
  1133. def t_find(self, verb, obj): #9190
  1134. if obj.is_toting:
  1135. self.write_message(24)
  1136. elif self.is_closed:
  1137. self.write_message(138)
  1138. elif (self.is_here(obj) or
  1139. obj is self.loc.liquid or
  1140. obj is self.dwarf and any(d.room is self.loc for d in self.dwarves)):
  1141. self.write_message(94)
  1142. else:
  1143. self.write(verb.default_message)
  1144. self.finish_turn()
  1145. t_inventory = t_find
  1146. def i_inventory(self, verb): #8200
  1147. first = True
  1148. objs = [ obj for obj in self.inventory if obj is not self.bear ]
  1149. for obj in objs:
  1150. if first:
  1151. self.write_message(99)
  1152. first = False
  1153. self.write(obj.inventory_message)
  1154. if self.bear.is_toting:
  1155. self.write_message(141)
  1156. if not objs:
  1157. self.write_message(98)
  1158. self.finish_turn()
  1159. def t_feed(self, verb, obj): #9210
  1160. if obj is self.bird:
  1161. self.write_message(100)
  1162. elif obj is self.troll:
  1163. self.write_message(182)
  1164. elif obj is self.dragon:
  1165. if self.dragon.prop != 0:
  1166. self.write_message(110)
  1167. else:
  1168. self.write_message(102)
  1169. elif obj is self.snake:
  1170. if self.is_closed or not self.is_here(self.bird):
  1171. self.write_message(102)
  1172. else:
  1173. self.write_message(101)
  1174. self.bird.destroy()
  1175. self.bird.prop = 0
  1176. self.impossible_treasures += 1
  1177. elif obj is self.dwarf:
  1178. if self.is_here(self.food):
  1179. self.write_message(103)
  1180. self.dwarf_stage += 1
  1181. else:
  1182. self.write(verb.default_message)
  1183. elif obj is self.bear:
  1184. if not self.is_here(self.food):
  1185. if self.bear.prop == 0:
  1186. self.write_message(102)
  1187. elif self.bear.prop == 3:
  1188. self.write_message(110)
  1189. else:
  1190. self.write(verb.default_message)
  1191. else:
  1192. self.food.destroy()
  1193. self.bear.prop = 1
  1194. self.axe.is_fixed = False
  1195. self.axe.prop = 0
  1196. self.write_message(168)
  1197. else:
  1198. self.write_message(14)
  1199. self.finish_turn()
  1200. def i_fill(self, verb): #9220
  1201. if self.is_here(self.bottle):
  1202. return self.t_fill(verb, self.bottle)
  1203. self.ask_verb_what(verb)
  1204. def t_fill(self, verb, obj):
  1205. if obj is self.bottle:
  1206. liquid = self.loc.liquid
  1207. if liquid is None:
  1208. self.write_message(106)
  1209. elif self.bottle.contents:
  1210. self.write_message(105)
  1211. else:
  1212. self.bottle.contents = liquid
  1213. if self.bottle.is_toting:
  1214. liquid.is_toting = True
  1215. if liquid is self.oil:
  1216. self.write_message(108)
  1217. else:
  1218. self.write_message(107)
  1219. elif obj is self.vase:
  1220. #9222
  1221. if self.vase.is_toting:
  1222. if self.loc.liquid is None:
  1223. self.write_message(144)
  1224. else:
  1225. self.write_message(145)
  1226. self.vase.drop(self.loc)
  1227. self.vase.prop = 2
  1228. self.vase.is_fixed = True
  1229. else:
  1230. self.write(verb.default_message)
  1231. else:
  1232. self.write(verb.default_message)
  1233. self.finish_turn()
  1234. def t_blast(self, verb, obj=None): #9230
  1235. if self.rod2.prop < 0 or not self.is_closed:
  1236. self.write(verb.default_message)
  1237. self.finish_turn()
  1238. return
  1239. if self.is_here(self.rod2):
  1240. self.bonus = 135
  1241. elif self.loc.n == 115:
  1242. self.bonus = 134
  1243. else:
  1244. self.bonus = 133
  1245. self.write_message(self.bonus)
  1246. self.score_and_exit()
  1247. i_blast = t_blast
  1248. def i_score(self, verb): #8240
  1249. score, max_score = self.compute_score(for_score_command=True)
  1250. self.write('If you were to quit now, you would score {}'
  1251. ' out of a possible {}.\n'.format(score, max_score))
  1252. def callback(yes):
  1253. self.write_message(54)
  1254. if yes:
  1255. self.score_and_exit()
  1256. self.yesno(self.messages[143], callback)
  1257. def i_fee(self, verb): #8250
  1258. for n in range(5):
  1259. if verb.synonyms[n].text == verb.text:
  1260. break # so that 0=fee, 1=fie, 2=foe, 3=foo, 4=fum
  1261. if n == 0:
  1262. self.foobar = self.turns
  1263. self.write_message(54)
  1264. elif n != self.turns - self.foobar:
  1265. self.write_message(151)
  1266. elif n < 3:
  1267. self.write_message(54)
  1268. else:
  1269. self.foobar = -1
  1270. eggs = self.eggs
  1271. start = eggs.starting_rooms[0]
  1272. if (eggs.is_at(start) or eggs.is_toting and self.loc is start):
  1273. self.write_message(54)
  1274. else:
  1275. troll = self.troll
  1276. if not eggs.rooms and not troll.rooms and not troll.prop:
  1277. self.troll.prop = 1
  1278. if self.loc is start:
  1279. self.write(eggs.messages[0])
  1280. elif self.is_here(eggs):
  1281. self.write(eggs.messages[1])
  1282. else:
  1283. self.write(eggs.messages[2])
  1284. eggs.rooms = list(eggs.starting_rooms)
  1285. eggs.is_toting = False
  1286. self.finish_turn()
  1287. def i_brief(self, verb): #8260
  1288. self.write_message(156)
  1289. self.full_description_period = 10000
  1290. self.look_complaints = 0
  1291. self.finish_turn()
  1292. def i_read(self, verb): #8270
  1293. if self.is_closed and self.oyster.is_toting:
  1294. return self.t_read(verb, self.oyster)
  1295. objs = (self.magazine, self.tablet, self.message)
  1296. objs = list(filter(self.is_here, objs))
  1297. if len(objs) != 1 or self.is_dark:
  1298. self.ask_verb_what(verb)
  1299. else:
  1300. self.t_read(verb, objs[0])
  1301. def t_read(self, verb, obj): #9270
  1302. if self.is_dark:
  1303. return self.i_see_no(obj.names[0])
  1304. elif (obj is self.oyster and not self.hints[2].used and
  1305. self.oyster.is_toting):
  1306. def callback(yes):
  1307. if yes:
  1308. self.hints[2].used = True
  1309. self.write_message(193)
  1310. else:
  1311. self.write_message(54)
  1312. self.yesno(self.messages[192], callback)
  1313. elif obj is self.oyster and self.hints[2].used:
  1314. self.write_message(194)
  1315. elif obj is self.message:
  1316. self.write_message(191)
  1317. elif obj is self.tablet:
  1318. self.write_message(196)
  1319. elif obj is self.magazine:
  1320. self.write_message(190)
  1321. else:
  1322. self.write(verb.default_message)
  1323. self.finish_turn()
  1324. def t_break(self, verb, obj): #9280
  1325. if obj is self.vase and self.vase.prop == 0:
  1326. self.write_message(198)
  1327. if self.vase.is_toting:
  1328. self.vase.drop(self.loc)
  1329. self.vase.prop = 2
  1330. self.vase.is_fixed = True
  1331. elif obj is self.mirror and self.is_closed:
  1332. self.write_message(197)
  1333. self.wake_repository_dwarves()
  1334. return
  1335. elif obj is self.mirror:
  1336. self.write_message(148)
  1337. else:
  1338. self.write(verb.default_message)
  1339. self.finish_turn()
  1340. def t_wake(self, verb, obj): #9290
  1341. if obj is self.dwarf and self.is_closed:
  1342. self.write_message(199)
  1343. self.wake_repository_dwarves()
  1344. else:
  1345. self.write(verb.default_message)
  1346. self.finish_turn()
  1347. def i_suspend(self, verb):
  1348. self.write('Provide "{}" with a filename or open file'.format(
  1349. verb.text))
  1350. self.finish_turn()
  1351. def t_suspend(self, verb, obj):
  1352. if isinstance(obj, str):
  1353. if os.path.exists(obj): # pragma: no cover
  1354. self.write('I refuse to overwrite an existing file.')
  1355. return
  1356. savefile = open(obj, 'wb')
  1357. else:
  1358. savefile = obj
  1359. r = self.random_generator # must replace live object with static state
  1360. self.random_state = r.getstate()
  1361. try:
  1362. del self.random_generator
  1363. savefile.write(zlib.compress(pickle.dumps(self), 9))
  1364. finally:
  1365. self.random_generator = r
  1366. if savefile is not obj:
  1367. savefile.close()
  1368. self.write('Game saved')
  1369. def i_hours(self, verb):
  1370. self.write('Open all day')
  1371. @classmethod
  1372. def resume(self, obj):
  1373. """Returns an Adventure game saved to the given file."""
  1374. if isinstance(obj, str):
  1375. savefile = open(obj, 'rb')
  1376. else:
  1377. savefile = obj
  1378. game = pickle.loads(zlib.decompress(savefile.read()))
  1379. if savefile is not obj:
  1380. savefile.close()
  1381. # Reinstate the random number generator.
  1382. game.random_generator = random.Random()
  1383. game.random_generator.setstate(game.random_state)
  1384. del game.random_state
  1385. return game
  1386. def should_offer_hint(self, hint, obj): #40000
  1387. if hint.n == 4: # cave
  1388. return self.grate.prop == 0 and not self.is_here(self.keys)
  1389. elif hint.n == 5: # bird
  1390. bird = self.bird
  1391. return self.is_here(bird) and self.rod.is_toting and obj is bird
  1392. elif hint.n == 6: # snake
  1393. return self.is_here(self.snake) and not self.is_here(self.bird)
  1394. elif hint.n == 7: # maze
  1395. return (not len(self.objects_here) and
  1396. not len(self.objects_at(self.oldloc)) and
  1397. not len(self.objects_at(self.oldloc2)) and
  1398. len(self.inventory) > 1)
  1399. elif hint.n == 8: # dark
  1400. return self.emerald.prop != 1 and self.platinum.prop != 1
  1401. elif hint.n == 9: # witt
  1402. return True
  1403. def start_closing_cave(self): #10000
  1404. self.grate.prop = 0
  1405. self.fissure.prop = 0
  1406. del self.dwarves[:]
  1407. self.troll.destroy()
  1408. self.troll2.rooms = list(self.troll.starting_rooms)
  1409. if self.bear.prop != 3:
  1410. self.bear.destroy()
  1411. for obj in self.chain, self.axe:
  1412. obj.prop = 0
  1413. obj.is_fixed = False
  1414. self.write_message(129)
  1415. self.clock1 = -1
  1416. self.is_closing = True
  1417. def close_cave(self): #11000
  1418. ne = self.rooms[115] # ne end of repository
  1419. sw = self.rooms[116]
  1420. for obj in (self.bottle, self.plant, self.oyster, self.lamp,
  1421. self.rod, self.dwarf):
  1422. obj.prop = -2 if obj is self.bottle else -1
  1423. obj.drop(ne)
  1424. self.loc = self.oldloc = self.oldloc2 = ne
  1425. for obj in (self.grate, self.snake, self.bird, self.cage,
  1426. self.rod2, self.pillow):
  1427. obj.prop = -2 if (obj is self.bird or obj is self.snake) else -1
  1428. obj.drop(sw)
  1429. self.mirror.rooms = [ne, sw]
  1430. self.mirror.is_fixed = 1
  1431. self.is_closed = True
  1432. for obj in self.inventory:
  1433. obj.is_toting = False
  1434. self.write_message(132)
  1435. self.move_to()
  1436. # TODO: 12000
  1437. # TODO: 12200
  1438. # TODO: 12400
  1439. # TODO: 12600
  1440. def wake_repository_dwarves(self): #19000
  1441. self.write_message(136)
  1442. self.score_and_exit()
  1443. def compute_score(self, for_score_command=False): #20000
  1444. score = maxscore = 2
  1445. for treasure in self.treasures:
  1446. # if ptext(0) is zero?
  1447. if treasure.n > self.chest.n:
  1448. value = 16
  1449. elif treasure is self.chest:
  1450. value = 14
  1451. else:
  1452. value = 12
  1453. maxscore += value
  1454. if treasure.prop >= 0:
  1455. score += 2
  1456. if treasure.rooms and treasure.rooms[0].n == 3 \
  1457. and treasure.prop == 0:
  1458. score += value - 2
  1459. maxscore += self.max_deaths * 10
  1460. score += (self.max_deaths - self.deaths) * 10
  1461. maxscore += 4
  1462. if not for_score_command and not self.gave_up:
  1463. score += 4
  1464. maxscore += 25
  1465. if self.dwarf_stage:
  1466. score += 25
  1467. maxscore += 25
  1468. if self.is_closing:
  1469. score += 25
  1470. maxscore += 45
  1471. if self.is_closed:
  1472. score += {0: 10, 135: 25, 134: 30, 133: 45}[self.bonus]
  1473. maxscore += 1
  1474. if 108 in (room.n for room in self.magazine.rooms):
  1475. score += 1
  1476. for hint in list(self.hints.values()):
  1477. if hint.used:
  1478. score -= hint.penalty
  1479. return score, maxscore
  1480. def score_and_exit(self):
  1481. score, maxscore = self.compute_score()
  1482. self.write('\nYou scored {} out of a possible {} using {} turns.'
  1483. .format(score, maxscore, self.turns))
  1484. for i, (minimum, text) in enumerate(self.class_messages):
  1485. if minimum >= score:
  1486. break
  1487. self.write('\n{}\n'.format(text))
  1488. if i < len(self.class_messages) - 1:
  1489. d = self.class_messages[i+1][0] + 1 - score
  1490. self.write('To achieve the next higher rating, you need'
  1491. ' {} more point{}\n'.format(d, 's' if d > 1 else ''))
  1492. else:
  1493. self.write('To achieve the next higher rating '
  1494. 'would be a neat trick!\n\nCongratulations!!\n')
  1495. self.is_done = True