练习43--面向对象的分析和设计基础
一 设计方法和技巧
1 设计过程
- 1. 把问题写或者划下来。
- 2. 提炼出关键概念,并进行研究。
- 3. 为这些概念创建一个类的层级和对象关系图。
- 4. 写下这些类的代码,并测试运行。
- 5. 重复和改进。
2 详细设计技巧
二 一个简单的游戏引擎分析——“来自25号行星的哥顿人”(Gothons from Planet Percal #25)
1 写或画出这个问题
(1)我会写一小段关于这个游戏的文字——游戏是干什么的,类似于需求分析吧,或者最后的实现效果:
“外星人入侵了一艘宇宙飞船,我们的英雄必须穿过迷宫般的房间打败他们,这样他才能逃到逃生舱去到下面的星球。游戏更像是 Zork 之类的文字冒险游戏,并且有着很有意思的死亡方式。这款游戏的引擎会运行一张满是房间或场景的地图。当玩家进入游戏时,每个房间都会打印自己的描述,然后告诉引擎下一步该运行地图中的哪个房间。”
(2)这时我有了一个关于这个游戏以及它如何运行的好想法,所以现在我要描述一下每个场景:——将需求分解,逐步完成
- 死亡(Death):玩家死的时候,会非常有意思。
- 中央走廊(Central Corridor):这是起点,已经有一个哥顿人站在那里,在继续之前,玩家必须用一个笑话来击败他。
- 房间(Room):不同的房间存在不同的陷阱,成功避过这些陷阱的话就会更加接近目的地。
- 激光武器军械械库(Laser Weapon Armory):这是英雄在到达逃生舱之前用中子弹炸毁飞船的地方。这里有一个键盘,英雄必须猜出数字。
- 桥(The Bridge):另一个和哥顿人战斗的场景,英雄在这里放置了炸弹。
- 逃生舱(Escape Pod):英雄逃脱的地方,前提是他猜出正确的逃生舱。
2 抽取关键概念并予以研究
我可能还会浏览一遍所有的动词,看它们适不适合作为函数名,但是我会先暂时跳过这一步。
现在你可能也会研究一下每个概念以及任何你不明白的东西。比如,我会玩几个同类型的游戏,确保我知道它们是如何工作的。我可能还会研究船是如何设计的或者炸弹是怎么用的。还有一些技术性问题,比如如何把游戏状态储存在数据库中。当我完成这些研究,我可能会基于这些新信息从第一步开始,重新写我的描述,并做概念提取。
3 为这些概念创建类的层级结构和对象地图
很快我就发现“房间”(“Room”)和“场景”(“Scene”)基本上是同一种东西,取决于我想用它们来做什么。在这个游戏中我选择用“场景”。然后我意识到所有特定的房间比如“中央走廊”其实就是“场景”。我还发现“死亡”(“Death”)也可以说是场景,这确认了我选择“场景”而不是“房间”的正确性,因为你可以说“死亡”是一种场景,但如果说它是一个“房间”就有点奇怪了。“迷宫”(“Maze”)和“地图”(“Map”)也基本上是同一种东西,我会选择用“地图”,因为我更常用它。我不想做一个战斗系统,所以我会暂时忽略“外星人”(“Alien”)和“玩家”(“Player”)这两个东西,先保存起来以备后用。“行星”(“Planet”)也可以是另一种场景,而不是其他特定的东西。
4 编写类代码并通过测试来运行
1 class Scene(object): 2 3 def enter(self): 4 pass 5 6 class Engine(object): 7 8 def __init__(self, scene_map): 9 pass 10 11 def play(self): 12 pass 13 14 class Death(Scene): 15 16 def enter(self): 17 pass 18 19 class CentralCorridor(Scene): 20 21 def enter(self): 22 pass 23 24 class LaserWeaponArmory(Scene): 25 26 def enter(self): 27 pass 28 29 class TheBridge(Scene): 30 31 def enter(self): 32 pass 33 34 class EscapePod(Scene): 35 36 def enter(self): 37 pass 38 39 class Map(object): 40 41 def __init__(self, start_scene): 42 pass 43 44 def next_scene(self, scene_name): 45 pass 46 47 def opening_scene(self): 48 pass 49 50 51 a_map = Map('central_corridor') 52 a_game = Engine(a_map) 53 a_game.play()
5 重复和改进
过程的最后一步准确来说不是一个步骤,而像是一个 while 循环。你不可能一次完成这个过程。相反,你会再次回顾整个过程,并根据你从后续步骤中学到的信息对其进行改进。有时我会进入第三步,然后意识到我需要再回到第一步和第二步,那我就会停下来,回到前面去做。有时我会灵光一闪,跳到最后,把脑子里的解决方案代码敲出来,然后再回过头来做前面的步骤,以确保我涵盖了所有可能的情况。
在这个过程中你需要注意的另一个问题是,它不仅仅是你在一个单一层面上做的事,而是当你遇到一个特定的问题时,你可以在每个层面上做的事情。假设我不知道怎么写 Engine.play 这个方法。我可以停下来,把整个过程专注在这一个函数上来弄明白代码应该怎么写。
三 自上而下VS自下而上
这个过程通常被称为“自上而下”,因为它从最抽象的概念(上)开始,然后一直向下到实际的应用。我希望你能从现在开始分析这本书里遇到的问题时使用我刚才描述的这个过程,但是你应该知道编程中还有另一种解决问题的方式,那就是,从写代码开始,然后逐渐“上升”到抽象的概念,这种方式被称为“自下而上”。它的步骤大致如下:
四 “来自25号行星的哥顿人”游戏代码
1 代码讲解
导入部分:
1 from sys import exit 2 from random import randint 3 from textwrap import dedent
基类Scene创建部分:
1 class Scene(object): 2 3 def enter(self): 4 print("This scene is not yet configured.") 5 print("Subclass it and implement enter().") 6 exit(1)
Engine类创建部分:
1 class Engine(object): 2 3 def __init__(self,scene_map): 4 self.scene_map = scene_map 5 6 def play(self): 7 current_scene = self.scene_map.opening_scene() 8 last_scene = self.scene_map.next_scene('finished') 9 10 while current_scene != last_scene: 11 next_scene_name = current_scene.enter() 12 current_scene = self.scene_map.next_scene(next_scene_name) 13 14 # be sure to print out the last scene 15 current_scene.enter()
Death类创建部分:
1 class Death(Scene): 2 3 quips = [ 4 "You died.You kinda suck at this.", 5 "Your Mom would be proud...if she ware smarter.", 6 "Such a luser.", 7 "I have a small puppy that's better at this.", 8 "You're worse than your Dad's jokes." 9 ] 10 11 def enter(self): 12 print(Death.quips[randint(0,len(self.quips)-1)]) 13 exit(1)
我的第一个场景很反常地设置为了 Death,主要是想向你展示你可以写的最简单的场景。
CentralCorridor中央走廊部分的创建:
1 class CentralCorridor(Scene): 2 3 def enter(self): 4 print(dedent(""" 5 The Gothons of Planet Percal #25 have invaded your ship and 6 destroyed your entire crew.You are the last surviving 7 member and your last mission is to get the neutron destruct 8 bomb from the weapons Armory,put it in the bridge,and 9 blow the ship up after getting into an escape pod. 10 11 You're running down the central corridor to the Weapons 12 Armory when a Gothon jumps out,red scaly skin,dark grimy 13 teeth,and evil clown costume flowing around his hate 14 filled body.He's blocking the door to the Armory and 15 about to pull a weapon to blast you. 16 """)) 17 18 action = input("> ") 19 20 if action == "shoot!": 21 print(dedent(""" 22 Quick on the draw you yank out your blaster and fire 23 it an the Gothon. His clown costume is flowing and 24 moving around his body,which throws off your aim. 25 Your laser hits his costume but misses him entirely. 26 This completely ruins his brand new costume his mother 27 bought him,which makes him fly into an insame rage 28 and blast you repeatedly in the face until you are 29 dead.Then he eats you. 30 """)) 31 return 'death' 32 33 elif action == "dodge!": 34 print(dedent(""" 35 Like a word class boxer you dodge,weave,slip and 36 slide right as the Gothon's blaster cranks a laser 37 past your head.In the middle of your artful dodge 38 your foot slips and you bang your head on the metal 39 wall and pass out.You wake up shortly after only to 40 die as the Gothon stomps on your head and eats you. 41 """)) 42 return 'death' 43 44 elif action == "tell a joke": 45 print(dedent(""" 46 Lucky for you they made you learn Gothon insults in 47 the academy. You tell the one Gothon joke you know: 48 Lbhe zbgure vf fb sng,jura fur fvgf nebhaq gur ubhfr, 49 fur fvgf nebhaq gur ubfhr.The Gothon stops,tries 50 not to laugh, then busts out laughing and can't move. 51 While he's laughing you run up and shoot him square in 52 the head putting him down,then jump through the 53 Weapon Armory door. 54 """)) 55 return 'laser_weapon_armory' 56 57 else: 58 print("DOES NOT COMPUTE!") 59 return 'central_corridor'
剩余的游戏场景创建:
1 class LaserWeaponArmory(Scene): 2 3 def enter(self): 4 print(dedent(""" 5 You do a dive roll into the Weapon Armory, crouch and scan 6 the room for more Gothons that might be hiding. It's dead 7 quiet, too quiet. You stand up and run to the far side of 8 the room and find the neutron bomb in its container. 9 There's a keypad lock on the box and you need the code to 10 get the bomb out. If you get the code wrong 10 times then 11 the lock closes forever and you can't get the bomb. The 12 code is 3 digits. 13 """)) 14 15 code = f"{randint(1,9)}{randint(1,9)}{randint(1,9)}" 16 guess = input("[keypad]> ") 17 guesses = 0 18 19 while guess != code and guesses < 10: 20 print("BZZZZEDDD!") 21 guesses += 1 22 guess = input("[keypad]> ") 23 24 if guess == code: 25 print(dedent(""" 26 The container clicks open and the seal breaks, letting 27 gas out. You grab the neutron bomb and run as fast as 28 you can to the bridge where you must place it in the 29 right spot. 30 """)) 31 return 'the_bridge' 32 else: 33 print(dedent(""" 34 The lock buzzes one last time and then you hear a 35 sickening melting sound as the mechanism is fused 36 together. You decide to sit there, and finally the 37 Gothons blow up the ship from their ship and you die. 38 """)) 39 return 'death' 40 41 class TheBridge(Scene): 42 43 def enter(self): 44 print(dedent(""" 45 You burst onto the Bridge with the netron destruct bomb 46 under your arm and surprise 5 Gothons who are trying to 47 take control of the ship. Each of them has an even uglier 48 clown costume than the last. They haven't pulled their 49 weapons out yet, as they see the active bomb under your 50 arm and don't want to set it off. 51 """)) 52 53 action = input("> ") 54 55 if action == "throw the bomb": 56 print(dedent(""" 57 In a panic you throw the bomb at the group of Gothons 58 and make a leap for the door. Right as you drop it a 59 Gothon shoots you right in the back killing you. As 60 you die you see another Gothon frantically try to 61 disarm the bomb. You die knowing they will probably 62 blow up when it goes off. 63 """)) 64 return 'death' 65 66 elif action == "slowly place the bomb": 67 print(dedent(""" 68 You point your blaster at the bomb under your arm and 69 the Gothons put their hands up and start to sweat. 70 You inch backward to the door, open it, and then 71 carefully place the bomb on the floor, pointing your 72 blaster at it. You then jump back through the door, 73 punch the close button and blast the lock so the 74 Gothons can't get out. Now that the bomb is placed 75 you run to the escape pod to get off this tin can. 76 """)) 77 return 'escape_pod' 78 79 else: 80 print("DOES NOT COMPUTE!") 81 return 'the_bridge' 82 83 class EscapedPod(Scene): 84 85 def enter(self): 86 print(dedent(""" 87 You rush through the ship desperately trying to make it 88 the escape pod before the whole ship explodes. It seems 89 like hardly any Gothons are on the ship, so your run is 90 clear of interference. You get to the chamber with the 91 escape pods, and now need to pick one to take. Some of 92 them could be damaged but you don't have time to look. 93 There's 5 pods, which one do you take? 94 """)) 95 96 good_pod = randint(1,5) 97 guess = input("[pod #]> ") 98 99 if int(guess) != good_pod: 100 print(dedent(""" 101 You jump into pod {guess} and hit the eject button. 102 The pod escapes out into the void of space, then 103 implodes as the hull ruptures, crushing your body into 104 jam jelly. 105 """)) 106 return 'death' 107 else: 108 print(dedent(""" 109 You jump into pod {guess} and hit the eject button. 110 The pod easily slides out into space heading to the 111 planet below. As it flies to the planet, you look 112 back and see your ship implode then explode like a 113 bright star, taking out the Gothon ship at the same 114 time. You won! 115 """)) 116 return 'finished' 117 118 class Finished(Scene): 119 120 def enter(self): 121 print("You won! Good job.") 122 return 'finished'
Map地图类创建部分:
1 class Map(object): 2 3 scenes = { 4 'central_corridor':CentralCorridor(), 5 'laser_weapon_armory':LaserWeaponArmory(), 6 'the_bridge':TheBridge(), 7 'escape_pod':EscapedPod(), 8 'death':Death(), 9 'finished':Finished(), 10 } 11 12 def __init__(self,start_scene): 13 self.start_scene = start_scene 14 15 def next_scene(self,scene_name): 16 val = Map.scenes.get(scene_name) 17 return val 18 19 def opening_scene(self): 20 return self.next_scene(self.start_scene)
启动游戏部分/实例化/调用过程:
1 a_map = Map('central_corridor') 2 a_game = Engine(a_map) 3 a_game.play()
ex43.py完整代码:
1 from sys import exit 2 from random import randint 3 from textwrap import dedent 4 5 class Scene(object): 6 7 def enter(self): 8 print("This scene is not yet configured.") 9 print("Subclass it and implement enter().") 10 exit(1) 11 12 class Engine(object): 13 14 def __init__(self,scene_map): 15 self.scene_map = scene_map 16 17 def play(self): 18 current_scene = self.scene_map.opening_scene() 19 last_scene = self.scene_map.next_scene('finished') 20 21 while current_scene != last_scene: 22 next_scene_name = current_scene.enter() 23 current_scene = self.scene_map.next_scene(next_scene_name) 24 25 # be sure to print out the last scene 26 current_scene.enter() 27 28 class Death(Scene): 29 30 quips = [ 31 "You died.You kinda suck at this.", 32 "Your Mom would be proud...if she ware smarter.", 33 "Such a luser.", 34 "I have a small puppy that's better at this.", 35 "You're worse than your Dad's jokes." 36 ] 37 38 def enter(self): 39 print(Death.quips[randint(0,len(self.quips)-1)]) 40 exit(1) 41 42 class CentralCorridor(Scene): 43 44 def enter(self): 45 print(dedent(""" 46 The Gothons of Planet Percal #25 have invaded your ship and 47 destroyed your entire crew.You are the last surviving 48 member and your last mission is to get the neutron destruct 49 bomb from the weapons Armory,put it in the bridge,and 50 blow the ship up after getting into an escape pod. 51 52 You're running down the central corridor to the Weapons 53 Armory when a Gothon jumps out,red scaly skin,dark grimy 54 teeth,and evil clown costume flowing around his hate 55 filled body.He's blocking the door to the Armory and 56 about to pull a weapon to blast you. 57 """)) 58 59 action = input("> ") 60 61 if action == "shoot!": 62 print(dedent(""" 63 Quick on the draw you yank out your blaster and fire 64 it an the Gothon. His clown costume is flowing and 65 moving around his body,which throws off your aim. 66 Your laser hits his costume but misses him entirely. 67 This completely ruins his brand new costume his mother 68 bought him,which makes him fly into an insame rage 69 and blast you repeatedly in the face until you are 70 dead.Then he eats you. 71 """)) 72 return 'death' 73 74 elif action == "dodge!": 75 print(dedent(""" 76 Like a word class boxer you dodge,weave,slip and 77 slide right as the Gothon's blaster cranks a laser 78 past your head.In the middle of your artful dodge 79 your foot slips and you bang your head on the metal 80 wall and pass out.You wake up shortly after only to 81 die as the Gothon stomps on your head and eats you. 82 """)) 83 return 'death' 84 85 elif action == "tell a joke": 86 print(dedent(""" 87 Lucky for you they made you learn Gothon insults in 88 the academy. You tell the one Gothon joke you know: 89 Lbhe zbgure vf fb sng,jura fur fvgf nebhaq gur ubhfr, 90 fur fvgf nebhaq gur ubfhr.The Gothon stops,tries 91 not to laugh, then busts out laughing and can't move. 92 While he's laughing you run up and shoot him square in 93 the head putting him down,then jump through the 94 Weapon Armory door. 95 """)) 96 return 'laser_weapon_armory' 97 98 else: 99 print("DOES NOT COMPUTE!") 100 return 'central_corridor' 101 102 class LaserWeaponArmory(Scene): 103 104 def enter(self): 105 print(dedent(""" 106 You do a dive roll into the Weapon Armory, crouch and scan 107 the room for more Gothons that might be hiding. It's dead 108 quiet, too quiet. You stand up and run to the far side of 109 the room and find the neutron bomb in its container. 110 There's a keypad lock on the box and you need the code to 111 get the bomb out. If you get the code wrong 10 times then 112 the lock closes forever and you can't get the bomb. The 113 code is 3 digits. 114 """)) 115 116 code = f"{randint(1,9)}{randint(1,9)}{randint(1,9)}" 117 guess = input("[keypad]> ") 118 guesses = 0 119 120 while guess != code and guesses < 10: 121 print("BZZZZEDDD!") 122 guesses += 1 123 guess = input("[keypad]> ") 124 125 if guess == code: 126 print(dedent(""" 127 The container clicks open and the seal breaks, letting 128 gas out. You grab the neutron bomb and run as fast as 129 you can to the bridge where you must place it in the 130 right spot. 131 """)) 132 return 'the_bridge' 133 else: 134 print(dedent(""" 135 The lock buzzes one last time and then you hear a 136 sickening melting sound as the mechanism is fused 137 together. You decide to sit there, and finally the 138 Gothons blow up the ship from their ship and you die. 139 """)) 140 return 'death' 141 142 class TheBridge(Scene): 143 144 def enter(self): 145 print(dedent(""" 146 You burst onto the Bridge with the netron destruct bomb 147 under your arm and surprise 5 Gothons who are trying to 148 take control of the ship. Each of them has an even uglier 149 clown costume than the last. They haven't pulled their 150 weapons out yet, as they see the active bomb under your 151 arm and don't want to set it off. 152 """)) 153 154 action = input("> ") 155 156 if action == "throw the bomb": 157 print(dedent(""" 158 In a panic you throw the bomb at the group of Gothons 159 and make a leap for the door. Right as you drop it a 160 Gothon shoots you right in the back killing you. As 161 you die you see another Gothon frantically try to 162 disarm the bomb. You die knowing they will probably 163 blow up when it goes off. 164 """)) 165 return 'death' 166 167 elif action == "slowly place the bomb": 168 print(dedent(""" 169 You point your blaster at the bomb under your arm and 170 the Gothons put their hands up and start to sweat. 171 You inch backward to the door, open it, and then 172 carefully place the bomb on the floor, pointing your 173 blaster at it. You then jump back through the door, 174 punch the close button and blast the lock so the 175 Gothons can't get out. Now that the bomb is placed 176 you run to the escape pod to get off this tin can. 177 """)) 178 return 'escape_pod' 179 180 else: 181 print("DOES NOT COMPUTE!") 182 return 'the_bridge' 183 184 class EscapedPod(Scene): 185 186 def enter(self): 187 print(dedent(""" 188 You rush through the ship desperately trying to make it 189 the escape pod before the whole ship explodes. It seems 190 like hardly any Gothons are on the ship, so your run is 191 clear of interference. You get to the chamber with the 192 escape pods, and now need to pick one to take. Some of 193 them could be damaged but you don't have time to look. 194 There's 5 pods, which one do you take? 195 """)) 196 197 good_pod = randint(1,5) 198 guess = input("[pod #]> ") 199 200 if int(guess) != good_pod: 201 print(dedent(""" 202 You jump into pod {guess} and hit the eject button. 203 The pod escapes out into the void of space, then 204 implodes as the hull ruptures, crushing your body into 205 jam jelly. 206 """)) 207 return 'death' 208 else: 209 print(dedent(""" 210 You jump into pod {guess} and hit the eject button. 211 The pod easily slides out into space heading to the 212 planet below. As it flies to the planet, you look 213 back and see your ship implode then explode like a 214 bright star, taking out the Gothon ship at the same 215 time. You won! 216 """)) 217 return 'finished' 218 219 class Finished(Scene): 220 221 def enter(self): 222 print("You won! Good job.") 223 return 'finished' 224 225 class Map(object): 226 227 scenes = { 228 'central_corridor':CentralCorridor(), 229 'laser_weapon_armory':LaserWeaponArmory(), 230 'the_bridge':TheBridge(), 231 'escape_pod':EscapedPod(), 232 'death':Death(), 233 'finished':Finished(), 234 } 235 236 def __init__(self,start_scene): 237 self.start_scene = start_scene 238 239 def next_scene(self,scene_name): 240 val = Map.scenes.get(scene_name) 241 return val 242 243 def opening_scene(self): 244 return self.next_scene(self.start_scene) 245 246 a_map = Map('central_corridor') 247 a_game = Engine(a_map) 248 a_game.play()
五 代码中涉及到的知识
1 sys.exit(n)
功能:执行到主程序末尾,解释器自动退出,但是如果需要中途退出程序,可以调用sys.exit函数,带有一个可选的整数参数返回给调用它的程序,表示你可以在主程序中捕获对sys.exit的调用。(0是正常退出,其他为异常)
有关sys模块的其它内容可参考:https://www.cnblogs.com/cherishry/p/5725184.html
2 textwrap.dedent()函数
功能:去除缩进
有关textwrap模块的其它内容,可参考:https://www.cnblogs.com/wj5633/p/6931187.html
3 代码详解
1 from sys import exit # 导入exit()方法,用来控制程序的结束时间 2 from random import randint # 导入randint()方法产生随机整数 3 from textwrap import dedent # 导入dedent()方法去除缩进 4 5 # 定义场景基类 6 class Scene(object): 7 8 def enter(self): 9 print("This scene is not yet configured.") 10 print("Subclass it and implement enter().") 11 exit(1) 12 13 # 程序运行的主体部分Engine类 14 class Engine(object): 15 16 def __init__(self,scene_map): # 在创建Engine类的对象a_game的过程中,已经将Map类的对象a_map的值传给了参数scene_map 17 self.scene_map = scene_map # 将a_map的值传给self.scene_map,相当于创建了一个新的Map类的对象 18 # print(f"self.scene_map = {self.scene_map}\n") 19 20 def play(self): # 通过Engine类的对象a_game调用Engine类的方法play,开始游戏: 21 # print(f"self.scene_map.start_scene = {self.scene_map.start_scene}\n") 22 current_scene = self.scene_map.opening_scene() # 通过Map类的对象self.scene_map调用opening_scene(),得到返回值val(此时为类CentralCorridor()的对象); 23 # print(f"current_scene1 = {current_scene}\n") # 通过将val的值赋给current_scene把它初始化为类CentralCorridor()的对象 24 last_scene = self.scene_map.next_scene('finished') # 通过Map类的对象self.scene_map调用next_scene()方法,得到返回值val(此时为类Finished()的对象) 25 # print(f"last_scene = {last_scene}\n") # 通过将val的值赋给last_scene把它初始化为类Finished()的对象 26 27 while current_scene != last_scene: 28 next_scene_name = current_scene.enter() # 通过类CentralCorridor()的对象current_scene调用类CentralCorridor()中的方法enter(),并将函数返回值赋给next_scene_name变量 29 # print(f"next_scene_name = {next_scene_name}\n") 30 current_scene = self.scene_map.next_scene(next_scene_name)# 通过Map类的对象self.scene_map调用Map类中的方法,将current_scene初始化为新的场景类的对象 31 # print(f"current_scene2 = {current_scene}\n") 32 33 # be sure to print out the last scene 34 current_scene.enter() 35 # print(f"current_scene3 = {current_scene}\n") 36 37 # 只要出错,就会触发Death()类 38 class Death(Scene): 39 40 quips = [ 41 "You died.You kinda suck at this.", 42 "Your Mom would be proud...if she ware smarter.", 43 "Such a luser.", 44 "I have a small puppy that's better at this.", 45 "You're worse than your Dad's jokes." 46 ] 47 48 def enter(self): 49 print(Death.quips[randint(0,len(self.quips)-1)]) # 通过生成随机数的方式任意返回列表quips中的一个元素 50 exit(1) 51 52 # 程序运行的初始场景,通过a_map = Map('central_corridor')语句第一次进入 53 class CentralCorridor(Scene): 54 55 def enter(self): 56 print(dedent(""" 57 The Gothons of Planet Percal #25 have invaded your ship and 58 destroyed your entire crew.You are the last surviving 59 member and your last mission is to get the neutron destruct 60 bomb from the weapons Armory,put it in the bridge,and 61 blow the ship up after getting into an escape pod. 62 63 You're running down the central corridor to the Weapons 64 Armory when a Gothon jumps out,red scaly skin,dark grimy 65 teeth,and evil clown costume flowing around his hate 66 filled body.He's blocking the door to the Armory and 67 about to pull a weapon to blast you. 68 """)) 69 70 action = input("> ") # 根据用户的输入,执行不同的语句,返回不同的结果 71 72 if action == "shoot!": 73 print(dedent(""" 74 Quick on the draw you yank out your blaster and fire 75 it an the Gothon. His clown costume is flowing and 76 moving around his body,which throws off your aim. 77 Your laser hits his costume but misses him entirely. 78 This completely ruins his brand new costume his mother 79 bought him,which makes him fly into an insame rage 80 and blast you repeatedly in the face until you are 81 dead.Then he eats you. 82 """)) 83 return 'death' # 如果用户输入的操作为"shoot!",将'death'字符串变量返回给a_map 84 85 elif action == "dodge!": 86 print(dedent(""" 87 Like a word class boxer you dodge,weave,slip and 88 slide right as the Gothon's blaster cranks a laser 89 past your head.In the middle of your artful dodge 90 your foot slips and you bang your head on the metal 91 wall and pass out.You wake up shortly after only to 92 die as the Gothon stomps on your head and eats you. 93 """)) 94 return 'death' # 如果用户输入的操作为"dodge!",将'death'字符串变量返回给a_map 95 96 elif action == "tell a joke": 97 print(dedent(""" 98 Lucky for you they made you learn Gothon insults in 99 the academy. You tell the one Gothon joke you know: 100 Lbhe zbgure vf fb sng,jura fur fvgf nebhaq gur ubhfr, 101 fur fvgf nebhaq gur ubfhr.The Gothon stops,tries 102 not to laugh, then busts out laughing and can't move. 103 While he's laughing you run up and shoot him square in 104 the head putting him down,then jump through the 105 Weapon Armory door. 106 """)) 107 # print(f"a_map2 = {a_map}\n") 108 return 'laser_weapon_armory' # 如果用户输入的操作为"tell a joke",将'laser_weapon_armory'字符串变量返回给a_map 109 110 else: 111 print("DOES NOT COMPUTE!") 112 return 'central_corridor' # 其它情况,返回'central_corridor',重新匹配 113 114 # LaserWeaponArmory()场景只有在用户动作为"tell a joke"的情况下才会触发 115 class LaserWeaponArmory(Scene): # 通过Engine类中的current_scene = self.scene_map.opening_scene()语句进入该场景 116 117 def enter(self): 118 print(dedent(""" 119 You do a dive roll into the Weapon Armory, crouch and scan 120 the room for more Gothons that might be hiding. It's dead 121 quiet, too quiet. You stand up and run to the far side of 122 the room and find the neutron bomb in its container. 123 There's a keypad lock on the box and you need the code to 124 get the bomb out. If you get the code wrong 10 times then 125 the lock closes forever and you can't get the bomb. The 126 code is 3 digits. 127 """)) 128 129 code = f"{randint(1,9)}{randint(1,9)}{randint(1,9)}" 130 print(f"code = {code}\n") 131 guess = input("[keypad]> ") # 用户输入不同的keypad,猜谜 132 guesses = 0 133 134 while guess != code and guesses < 10: # 保证用户有10次猜谜机会,否则执行else语句 135 print("BZZZZEDDD!") 136 guesses += 1 137 guess = input("[keypad]> ") 138 139 if guess == code: # 如果用户猜中了,返回字符串'the_bridge'给对象current_scene 140 print(dedent(""" 141 The container clicks open and the seal breaks, letting 142 gas out. You grab the neutron bomb and run as fast as 143 you can to the bridge where you must place it in the 144 right spot. 145 """)) 146 return 'the_bridge' 147 else: 148 print(dedent(""" 149 The lock buzzes one last time and then you hear a 150 sickening melting sound as the mechanism is fused 151 together. You decide to sit there, and finally the 152 Gothons blow up the ship from their ship and you die. 153 """)) 154 return 'death' # 用户猜了10次还是没有猜中,返回字符串'death'给对象current_scene 155 156 # TheBridge()场景只有在用户成功进入LaserWeaponArmory()场景并且在猜谜游戏中猜中才会触发 157 class TheBridge(Scene): 158 159 def enter(self): 160 print(dedent(""" 161 You burst onto the Bridge with the netron destruct bomb 162 under your arm and surprise 5 Gothons who are trying to 163 take control of the ship. Each of them has an even uglier 164 clown costume than the last. They haven't pulled their 165 weapons out yet, as they see the active bomb under your 166 arm and don't want to set it off. 167 """)) 168 169 action = input("> ") 170 171 if action == "throw the bomb": 172 print(dedent(""" 173 In a panic you throw the bomb at the group of Gothons 174 and make a leap for the door. Right as you drop it a 175 Gothon shoots you right in the back killing you. As 176 you die you see another Gothon frantically try to 177 disarm the bomb. You die knowing they will probably 178 blow up when it goes off. 179 """)) 180 return 'death' 181 182 elif action == "slowly place the bomb": 183 print(dedent(""" 184 You point your blaster at the bomb under your arm and 185 the Gothons put their hands up and start to sweat. 186 You inch backward to the door, open it, and then 187 carefully place the bomb on the floor, pointing your 188 blaster at it. You then jump back through the door, 189 punch the close button and blast the lock so the 190 Gothons can't get out. Now that the bomb is placed 191 you run to the escape pod to get off this tin can. 192 """)) 193 return 'escape_pod' 194 195 else: 196 print("DOES NOT COMPUTE!") 197 return 'the_bridge' 198 199 # EscapedPod()场景只有在用户成功触发TheBridge()场景并且输入动作为"slowly place the bomb"的情况下才会触发 200 class EscapedPod(Scene): 201 202 def enter(self): 203 print(dedent(""" 204 You rush through the ship desperately trying to make it 205 the escape pod before the whole ship explodes. It seems 206 like hardly any Gothons are on the ship, so your run is 207 clear of interference. You get to the chamber with the 208 escape pods, and now need to pick one to take. Some of 209 them could be damaged but you don't have time to look. 210 There's 5 pods, which one do you take? 211 """)) 212 213 good_pod = randint(1,5) 214 print(f"good_pod = {good_pod}\n") 215 guess = input("[pod #]> ") 216 217 if int(guess) != good_pod: 218 print(dedent(""" 219 You jump into pod {guess} and hit the eject button. 220 The pod escapes out into the void of space, then 221 implodes as the hull ruptures, crushing your body into 222 jam jelly. 223 """)) 224 return 'death' 225 else: 226 print(dedent(""" 227 You jump into pod {guess} and hit the eject button. 228 The pod easily slides out into space heading to the 229 planet below. As it flies to the planet, you look 230 back and see your ship implode then explode like a 231 bright star, taking out the Gothon ship at the same 232 time. You won! 233 """)) 234 return 'finished' 235 236 # Finished()场景的触发条件:用户选择流程:"tell a joke"——猜谜成功(是否等于code变量的值)——"slowly place the bomb"——再次猜谜成功(是否等于good_pod变量的值) 237 class Finished(Scene): 238 239 def enter(self): 240 print("You won! Good job.") 241 return 'finished' 242 243 class Map(object): 244 245 scenes = { 246 'central_corridor':CentralCorridor(), 247 'laser_weapon_armory':LaserWeaponArmory(), 248 'the_bridge':TheBridge(), 249 'escape_pod':EscapedPod(), 250 'death':Death(), 251 'finished':Finished(), 252 } 253 254 def __init__(self,start_scene): 255 self.start_scene = start_scene 256 # print(f"self.start_scene = {self.start_scene}\n") 257 258 def next_scene(self,scene_name): 259 val = Map.scenes.get(scene_name) 260 # print(f"val = {val}\n") 261 return val 262 263 def opening_scene(self): 264 return self.next_scene(self.start_scene) 265 266 a_map = Map('central_corridor') # a_map是Map类的一个对象,此时完成的操作是: 267 # print(f"a_map1 = {a_map}\n") # (1)将'central_corridor'传给参数start_scene,并将其赋值给self.start_scene变量 268 # (2)然后通过opening_scene()方法调用next_scene()方法,通过变量self.start_scene将'central_corridor'传给参数scene_name 269 # (3)在next_scene方法内部,通过调用Map类的字典变量scenes的内置方法get,得到键'central_corridor' 270 # 对应的值CentralCorridor(),然后将val初始化为类CentralCorridor()的对象 271 a_game = Engine(a_map) # a_game是Engine()类的一个对象,此时完成的操作是: 272 # print(f"a_game = {a_game}\n") # (1)将Map类的对象a_map的地址作为参数传给Engine()类中构造方法的参数scene_map 273 a_game.play() # 通过Engine类的对象a_game调用Engine类的方法play,开始游戏:
运行结果:
1 PS E:\6_AI-Study\1_Python\2_code_python\02_LearnPythonTheHardWay> python ex43.py 2 3 The Gothons of Planet Percal #25 have invaded your ship and 4 destroyed your entire crew.You are the last surviving 5 member and your last mission is to get the neutron destruct 6 bomb from the weapons Armory,put it in the bridge,and 7 blow the ship up after getting into an escape pod. 8 9 You're running down the central corridor to the Weapons 10 Armory when a Gothon jumps out,red scaly skin,dark grimy 11 teeth,and evil clown costume flowing around his hate 12 filled body.He's blocking the door to the Armory and 13 about to pull a weapon to blast you. 14 15 > tell a joke 16 17 Lucky for you they made you learn Gothon insults in 18 the academy. You tell the one Gothon joke you know: 19 Lbhe zbgure vf fb sng,jura fur fvgf nebhaq gur ubhfr, 20 fur fvgf nebhaq gur ubfhr.The Gothon stops,tries 21 not to laugh, then busts out laughing and can't move. 22 While he's laughing you run up and shoot him square in 23 the head putting him down,then jump through the 24 Weapon Armory door. 25 26 27 You do a dive roll into the Weapon Armory, crouch and scan 28 the room for more Gothons that might be hiding. It's dead 29 quiet, too quiet. You stand up and run to the far side of 30 the room and find the neutron bomb in its container. 31 There's a keypad lock on the box and you need the code to 32 get the bomb out. If you get the code wrong 10 times then 33 the lock closes forever and you can't get the bomb. The 34 code is 3 digits. 35 36 code = 839 37 38 [keypad]> 839 39 40 The container clicks open and the seal breaks, letting 41 gas out. You grab the neutron bomb and run as fast as 42 you can to the bridge where you must place it in the 43 right spot. 44 45 46 You burst onto the Bridge with the netron destruct bomb 47 under your arm and surprise 5 Gothons who are trying to 48 take control of the ship. Each of them has an even uglier 49 clown costume than the last. They haven't pulled their 50 weapons out yet, as they see the active bomb under your 51 arm and don't want to set it off. 52 53 > slowly place the bomb 54 55 You point your blaster at the bomb under your arm and 56 the Gothons put their hands up and start to sweat. 57 You inch backward to the door, open it, and then 58 carefully place the bomb on the floor, pointing your 59 blaster at it. You then jump back through the door, 60 punch the close button and blast the lock so the 61 Gothons can't get out. Now that the bomb is placed 62 you run to the escape pod to get off this tin can. 63 64 65 You rush through the ship desperately trying to make it 66 the escape pod before the whole ship explodes. It seems 67 like hardly any Gothons are on the ship, so your run is 68 clear of interference. You get to the chamber with the 69 escape pods, and now need to pick one to take. Some of 70 them could be damaged but you don't have time to look. 71 There's 5 pods, which one do you take? 72 73 good_pod = 4 74 75 [pod #]> 4 76 77 You jump into pod {guess} and hit the eject button. 78 The pod easily slides out into space heading to the 79 planet below. As it flies to the planet, you look 80 back and see your ship implode then explode like a 81 bright star, taking out the Gothon ship at the same 82 time. You won! 83 84 You won! Good job.