zoukankan      html  css  js  c++  java
  • 练习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 抽取关键概念并予以研究

    我现在有足够的信息来提取其中的名词,并分析他们的类层级结构。首先,我会做一个所有名词的列表:
    • Alien(外星人)
    • Player(玩家)
    • Ship(飞船)
    • Maze(迷宫)
    • Room(房间)
    • Scene(场景)
    • Gothon(哥特人)
    • Escape Pod(逃生舱)
    • Planet(行星)
    • Map(地图)
    • Engine(引擎)
    • Death(死亡)
    • Central Corridor(中央走廊)
    • Laser Weapon Armory(激光武器军械库)
    • The Bridge(桥)

    我可能还会浏览一遍所有的动词,看它们适不适合作为函数名,但是我会先暂时跳过这一步。

           现在你可能也会研究一下每个概念以及任何你不明白的东西。比如,我会玩几个同类型的游戏,确保我知道它们是如何工作的。我可能还会研究船是如何设计的或者炸弹是怎么用的。还有一些技术性问题,比如如何把游戏状态储存在数据库中。当我完成这些研究,我可能会基于这些新信息从第一步开始,重新写我的描述,并做概念提取

    3 为这些概念创建类的层级结构和对象地图

            很快我就发现“房间”(“Room”)和“场景”(“Scene”)基本上是同一种东西,取决于我想用它们来做什么。在这个游戏中我选择用“场景”。然后我意识到所有特定的房间比如“中央走廊”其实就是“场景”。我还发现“死亡”(“Death”)也可以说是场景,这确认了我选择“场景”而不是“房间”的正确性,因为你可以说“死亡”是一种场景,但如果说它是一个“房间”就有点奇怪了。“迷宫”(“Maze”)和“地图”(“Map”)也基本上是同一种东西,我会选择用“地图”,因为我更常用它。我不想做一个战斗系统,所以我会暂时忽略“外星人”(“Alien”)和“玩家”(“Player”)这两个东西,先保存起来以备后用。“行星”(“Planet”)也可以是另一种场景,而不是其他特定的东西。

    经过上述思考过程,我开始创建一个看起来像这样的类的层级结构:

    然后我会浏览一遍,基于我描述里面的东西,想想看每个类下面需要些什么动作。例如,我从描述里知道,我需要一种方式来“运行”这个引擎,从地图“到达下一个场景”,到达“开场”,并“进入”一个场景,我会像这样把这些动作加上:

    注意我只把“enter”放在了“场景”下面,所有“场景”下面的东西都会继承这个动作,需要随后再重写。

    4 编写类代码并通过测试来运行

    一旦我有了这个类和函数的树状图,我在我的编辑器里面打开一个源文件,试着写它们的代码。通常我就是把树状图里的东西复制粘贴到源文件里,然后把它们编辑成类。下面是它们最开始的样子,文件最后放了一个小测试:
    ex43_classes.py
     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()
    ex43_classes.py
    在这个文件中你可以看到,我只是复制了层级结构中我想要的东西,并在最后加上了一些测试代码来运行,看这个基本结构能不能成立。在这个练习后面的几部分,你会填上剩余的代码,让它像游戏描述中那样运行。 

    5 重复和改进

           过程的最后一步准确来说不是一个步骤,而像是一个 while 循环。你不可能一次完成这个过程。相反,你会再次回顾整个过程,并根据你从后续步骤中学到的信息对其进行改进。有时我会进入第三步,然后意识到我需要再回到第一步和第二步,那我就会停下来,回到前面去做。有时我会灵光一闪,跳到最后,把脑子里的解决方案代码敲出来,然后再回过头来做前面的步骤,以确保我涵盖了所有可能的情况。

           在这个过程中你需要注意的另一个问题是,它不仅仅是你在一个单一层面上做的事,而是当你遇到一个特定的问题时,你可以在每个层面上做的事情。假设我不知道怎么写 Engine.play 这个方法。我可以停下来,把整个过程专注在这一个函数上来弄明白代码应该怎么写。

    三 自上而下VS自下而上

    这个过程通常被称为“自上而下”,因为它从最抽象的概念(上)开始,然后一直向下到实际的应用。我希望你能从现在开始分析这本书里遇到的问题时使用我刚才描述的这个过程,但是你应该知道编程中还有另一种解决问题的方式,那就是,从写代码开始,然后逐渐“上升”到抽象的概念,这种方式被称为“自下而上”。它的步骤大致如下: 

    1. 从问题中拿出一小部分,开始写简单的能运行的代码。
    2. 然后用类和自动化测试来把代码改进地更正式一些。
    3. 抽象出你所使用的关键概念,试着探究一下它们。
    4. 针对正在发生的事情写一段描述。
    5. 回过头去继续改进代码,也可能把之前写的删掉重新开始。
    6. 转到这个问题的其他部分,然后重复以上步骤。 
           这个过程只有在你对编程已经比较熟练并且面对问题能够自然使用编程思维的情况下才会更好,同时,当你知道整个工程的一小部分、却对全局概念的信息掌握不全的时候,这个方法也很好用。你可以将整个过程拆解成很多小块,然后边写代码边探索,这样可以帮助你一点一点地钻研这个问题,直到整个问题都得到解决。

    四  “来自25号行星的哥顿人”游戏代码

    1 代码讲解

    导入部分:

    1 from sys import exit
    2 from random import randint
    3 from textwrap import dedent
    导入相应的模块
    这是游戏所需库的基本引入。唯一的新东西是从 textwrap 模块导入 dedent 函数。这个函数将帮助我们使用 """ (三引号)字符串来编写我们的房间描述。它就是简单地从字符串的行首删除空白。如果没有这个函数,使用 """ 样式字符串就会失败,因为它们在屏幕上缩进的程度与Python代码相同。 

    基类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)
    基类Scene创建
    正如你在框架代码中看到的,我有一个基类 Scene,它具有所有场景都具有的公共功能。在这个简单的程序中,它们不会做太多的工作,只是向你演示如何创建基类。 

    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()
    Engine类
    我还有 Engine 类。你可以看到我已经在使用 Map.opening_scene 和 Map.next_scene 这两个方法了。因为我已经提前计划好了,所以可以在写出 Map 类之前就把这些方法写下来并用起来。 

    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类

    我的第一个场景很反常地设置为了 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'
    CentralCorridor类
    然后我创建了中央走廊,这是游戏的开始。我把游戏的场景放在 Map 之前,是因为我需要在随后引用它们。你应该也看到了我在第4行用了 dedent 函数。稍后你可以尝试删除它,看看它会做什么。

    剩余的游戏场景创建:

      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)
    Map类
    然后是我的 Map 类,你可以看到它把每个场景的名字存储在一个字典里,然后我用Map.scenes 引用那个字典。这也是为什么地图出现在场景之后的原因,因为字典必须引用场
    景,所以场景必须先存在。

    启动游戏部分/实例化/调用过程:

    1 a_map = Map('central_corridor')
    2 a_game = Engine(a_map)
    3 a_game.play()
    实例化/启动游戏
    最后是我通过制作地图来运行游戏的代码,在调用 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()
    “来自25号行星的哥顿人”游戏代码

    五 代码中涉及到的知识

     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}
    ")
     19         
     20     def play(self):                                            # 通过Engine类的对象a_game调用Engine类的方法play,开始游戏:
     21         # print(f"self.scene_map.start_scene = {self.scene_map.start_scene}
    ")
     22         current_scene = self.scene_map.opening_scene()         # 通过Map类的对象self.scene_map调用opening_scene(),得到返回值val(此时为类CentralCorridor()的对象);
     23         # print(f"current_scene1 = {current_scene}
    ")         # 通过将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}
    ")                # 通过将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}
    ")
     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}
    ")
     32             
     33         # be sure to print out the last scene
     34         current_scene.enter()
     35         # print(f"current_scene3 = {current_scene}
    ")
     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}
    ")
    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}
    ")
    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}
    ")
    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}
    ")
    257     
    258     def next_scene(self,scene_name):
    259         val = Map.scenes.get(scene_name)
    260         # print(f"val = {val}
    ")
    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}
    ")          # (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}
    ")         # (1)将Map类的对象a_map的地址作为参数传给Engine()类中构造方法的参数scene_map
    273 a_game.play()                           # 通过Engine类的对象a_game调用Engine类的方法play,开始游戏:
    “来自25号行星的哥顿人”游戏代码详解

    运行结果:

     1 PS E:6_AI-Study1_Python2_code_python2_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.
    运行结果
  • 相关阅读:
    ES5 ES6 作用域声明部分
    js 内建函数reduce
    $apply的使用与否
    得分-星星
    CSS3中translate、transform和translation的区别和联系
    vue 学习笔记
    -webkit-line-clamp 多行文字溢出...
    八位二进制数为什么表示范围(-128~~+127)理解
    vs2017_enterprise正式版离线安装包bt下载
    RSA密钥之C#格式与Java格式转换
  • 原文地址:https://www.cnblogs.com/luoxun/p/13434698.html
Copyright © 2011-2022 走看看