Python-练习 43. 面向对象的分析和设计基础

这个练习我想说一下当你想要用 Python 创建一个东西,尤其是面向对象编程的时候,过程是怎样的。
我说的“过程”指的是我会给出一些有序的步骤,但你也不用生搬硬套,因为它们也不一定适用每一个问题。它们只不过是为很多编程问题提供一个很好的开端,而不是解决这类问题的唯一方法,只是你可以参考的其中一种方法。

过程如下:
1. 把问题写或者划下来。
2. 提炼出关键概念,并进行研究。
3. 为这些概念创建一个类的层级和对象关系图。
4. 写下这些类的代码,并测试运行。
5. 重复和改进。

这是一种“自上而下”的方式,它从非常抽象、松散的想法开始,然后慢慢提炼,直到想法变得具体,可以通过代码来实现。

我会先从写下问题开始,尽可能地写下我所能想到的点。可能我还会画一两张图表、地图之类的,甚至会给我自己写一系列邮件来阐述这个问题。这样能让我针对这个问题把一些关键的概念表达出来,并且探索出关于该问题我可能已经掌握的东西。

然后我会过一遍这些笔记、图表以及描述,从其中抽象出关键概念。这里有一个小技巧:把你所写所画的东西里面所有的名词和动词列一个表出来,然后写下它们之间是如何相互关联的。这种方法让我得到了一个关于下一步的类、对象和函数名的列表。我拿着这个概念列表,研究其中我不明白的点,如果我需要的话,对其进行改进。

一旦我有了这个概念列表,我就创造了一个简单的概念框架,以及它们作为类是如何相互关联的。你可以经常列你的名词表,然后问自己“这个跟其他的概念名词类似吗?也就是说,它们有共同的父类吗?有的话应该叫什么?”重复这个过程直到你得到一个类的层级结构,可能就是一个简单的树状图或者示意图。然后把所有的动词挑出来,看看它们能不能作为每个类的函数名,然后把它们放到你的树状图里面。

等类的层级结构梳理清楚之后,我会坐下来,写一些基本的代码框架,只是一些类和它们的函数,没有其他东西。然后我会写一些测试代码,跑一下,看这些类有没有意义以及能不能正常运行。有时我会先写测试代码,有时候就是一小段测试,一小段代码,再一小段测试,以此类推,直到我把整个程序构建起来。

最后,我会重复这个过程,并且在运行的过程中不断精简,在添加更多应用之前让代码更简洁明了。如果我在某个特定环节因为一个概念或者我没有预料到的问题而卡壳,我会坐下来,只运行这一部分,直到把问题弄明白之后再继续。

我现在要通过一个游戏引擎和一个游戏练习来过一遍这个过程

一个简单的游戏引擎分析

我要制作的这个游戏叫做“来自25号行星的哥顿人”(Gothons from Planet Percal #25),它是一个小型太空冒险游戏。因为我满脑子都是这个概念,我就去探索这个想法,然后思考如何把这个游戏做出来。

写或画出这个问题

我会写一小段关于这个游戏的文字:

“外星人入侵了一艘宇宙飞船,我们的英雄必须穿过迷宫般的房间打败他们,这样他才能逃到逃生舱去到下面的星球。游戏更像是 Zork 之类的文字冒险游戏,并且有着很有意思的死亡方式。这款游戏的引擎会运行一张满是房间或场景的地图。当玩家进入游戏时,每个房间都会打印自己的描述,然后告诉引擎下一步该运行地图中的哪个房间。”

这时我有了一个关于这个游戏以及它如何运行的好想法,所以现在我要描述一下每个场景:

死亡(Death):玩家死的时候,会非常有意思。

中央走廊(Central Corridor):这是起点,已经有一个哥顿人站在那里,在继续之前,玩家必须用一个笑话来击败他。

激光武器军械库(Laser Weapon Armory):这是英雄在到达逃生舱之前用中子弹炸毁飞船的地方。

这里有一个键盘,英雄必须猜出数字。

桥(The Bridge):另一个和哥顿人战斗的场景,英雄在这里放置了炸弹。

逃生舱(Escape Pod):英雄逃脱的地方,前提是他猜出正确的逃生舱。

到这一步我可能会画一幅映射图,或者为每个房间写更多的描述——反正就是当我探究这个问题的时候,任何我脑子里冒出的想法。

抽取关键概念并予以研究

我现在有足够的信息来提取其中的名词,并分析他们的类层级结构。首先,我会做一个所有名词的列
表:

Alien(外星人)
Player(玩家)
Ship(飞船)
Maze(迷宫)
Room(房间)
Scene(场景)
Gothon(哥特人)
Escape Pod(逃生舱)
Planet(行星)
Map(地图)
Engine(引擎)
Death(死亡)
Central Corridor(中央走廊)
Laser Weapon Armory(激光武器军械库)
The Bridge(桥)

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

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

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

我通过询问“什么与其他东西类似?”、“什么基本上就是另一个东西的另一个词?”来把我已经有的东西转换成类的层级结构。

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

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

Map
Engine
Scene

Death
Central Corridor
Laser Weapon Armory
The Bridge
Escape Pod

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

Map

next_scene
opening_scene

Engine

play

Scene

enter

Death
Central Corridor
Laser Weapon Armory
The Bridge
Escape Pod

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

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

一旦我有了这个类和函数的树状图,我在我的编辑器里面打开一个源文件,试着写它们的代码。通常我就是把树状图里的东西复制粘贴到源文件里,然后把它们编辑成类。下面是它们最开始的样子,文件最后放了一个小测试:

ex43_classes.py

class Scene(object):

    def enter(self):
        pass


class Engine(object):

    def __init__(self, scene_map):
        pass

    def play(self):
        pass

class Death(Scene):

    def enter(self):
        pass

class CentralCorridor(Scene):

    def enter(self):
        pass

class TheBridge(Scene):

    def enter(self):
        pass

class TheBridge(Scene):

    def enter(self):
        pass

class EscapePod(Scene):

    def enter(self):
        pass


class Map(object):

    def __init__(self, start_scene):
        pass

    def next_scene(self, scene_name):
        pass

    def opening_scene(self):
        pass


a_map = Map('central_corridor')
a_game = Engine(a_map)
a_game.play()

在这个文件中你可以看到,我只是复制了层级结构中我想要的东西,并在最后加上了一些测试代码来运行,看这个基本结构能不能成立。在这个练习后面的几部分,你会填上剩余的代码,让它像游戏描述中那样运行。

重复和改进

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

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

自上而下 vs 自下而上

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

1. 从问题中拿出一小部分,开始写简单的能运行的代码。
2. 然后用类和自动化测试来把代码改进地更正式一些。
3. 抽象出你所使用的关键概念,试着探究一下它们。
4. 针对正在发生的事情写一段描述。
5. 回过头去继续改进代码,也可能把之前写的删掉重新开始。
6. 转到这个问题的其他部分,然后重复以上步骤。

这个过程只有在你对编程已经比较熟练并且面对问题能够自然使用编程思维的情况下才会更好,同时,当你知道整个工程的一小部分、却对全局概念的信息掌握不全的时候,这个方法也很好用。**你可以将整个过程拆解成很多小块,然后边写代码边探索,这样可以帮助你一点一点地钻研这个问题,直到整个问题都得到解决。**但是,请记住,你的解决方案很可能会曲折而怪异,所以我才把回顾、研究以及基于你所学到的东西对代码进行改进和清理这些步骤加入到我的过程描述中。

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

接下来我要向你展示针对之前的问题我最终的解决方,但是我想让你直接跳进去开始敲代码,我希望你自己先基于描述粗略地写出代码框架,然后试着让它运行,一旦你有了你的解决方案,你再回来看我是怎么做的。

我会把最终的 ex43.py 拆成几个部分分别解释,而不是直接把所有代码一次全部给你。

from sys import exit
from random import randint
from textwrap import dedent

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


class Scene(object):

    def enter(self):
        print("This scene is not yet configured")
        print("Subclass it and implement enter().")
        exit(1)

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


class Engine(object):

	def __init__(self, scene_map):
		self.scene_map = scene_map

	def play(self):
		current_scene = self.scene_map.opening_scene()
		last_scene = self.scene_map.next_scene('finished')
	
		while current_scene != last_scene:
			next_scene_name = current_scene.enter()
			current_scene = self.scene_map.next_scene(next_scene_name)

		# be sure to print out the last scene
		current_scene.enter()

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


class Death(Scene):

    quips = [
        "You died. You kinda suck at this.",
        "Such a     luser.",
        "I have     a small puppy that's better at this.",
        "You're     worse than your Dad's jokes."
    ]

    def enter(self):
        print(Death.quips[randint(0, len(self.quips)-1)])
        exit(1)

第一个场景很反常地设置为了 Death,主要是想向你展示你可以写的最简单的场景。


 class CentralCorridor(Scene):
 
    def enter(self):
        print(dedent("""
            The Gothons of Planet Percal #25 have invaded your ship and
            destroyde your entire crew. You are the last surviving
            member and your last mission is to get the neutron destruct
            bomb from the Weapons Armory, put it in the bridge, and
            blow the ship up after getting into an escape pod.

            You're running down the central corridor to the Weapons
            Armory when a Gothon jumps out, red scaly skin, dark grimy
            teeth, and evil clown contume flowing around his hate
            filled body. He's blocking the door to the Armory and
            about to pull a weapon to blast you.
            """))

        action = input("> ")

        if action == "shoot!":
            print(dedent("""
                Quick on the draw you yank out your blaster and fire
                it at the Gothon. His clown costume is flowing and
                moving around his body, which throws off your aim.
                Your laser hits his costume but misses him entirely.
                This completely ruins his brand new costume his mother
                bought him, which makes him fly into an insane rage
                and blast you repeatedly in the face until you are
                dead. Then he eats you.
                """))
            return 'death'

        elif action == "dodge!":
            print(dedent("""
                Like a world class boxer you dodge, weave, slip and
                slide right as the Gothon's blaster cranks a laser
                past your head. In the middle of your artful dodge
                your foot slips and you bang your head on the metal
                wall and pass out. You wake up shortly after only to
                die as the Gothon stomps ton your head and eats you.
                """))
            return 'death'
    
        elif action  == "tell a joke":
            print(dedent("""
                Lucky for you they made you learn Gothon insults in
                the academy. You tell the one Gothon joke you know:
                Lbhe zbgure vf fb sng, jura fur fvgf nebhaq gur ubhfr,
                fur fvgf nebhaq gur ubhfr. The Gothon stops, tries 
                not to laugh, then busts out laughing and can't move.
                While he's laughing you run up and shoot him square in
                the head putting him down, then jump through the 
                Weapon Armory door.
                """))
            return 'laser_weapon_armory'

        else:
            print("DOES NOT COMPUTE!")
            return 'central_corridor'

然后创建中央走廊,这是游戏的开始。我把游戏的场景放在 Map 之前,是因为我需要在随后引用它们。你应该也看到了我在第4行用了 dedent 函数。稍后你可以尝试删除它,看看它会做什么。


 class LaserWeaponArmory(Scene):
 
    def enter(self):
        print(dedent("""
            You do a dive roll into the Weapon Armory, crouch and scan
            the room for more Gothons that might be hiding. It's dead
            quiet, too quiet. You stand up and run to the far side of
            the room and find the neutron bomb in its container.
            There's a keypad lock on the box and you need the code to
            get the bomb out. If you get the code wrong 10 times then 
            the lock closes forever and you can't get the bomb. The 
            code is 3 digits.
            """))
    
        code = f"{randint(1,9)}{randint(1,9)}{randint(1,9)}"
        guess = input("[keypad]> ")
        guesses = 0

        while guess != code and guesses < 10:
            print("BZZZZEDDD!")
            guesses += 1
            guess = input("[keypad]> ")

        if guess == code:
            print(dedent("""
                The container clicks open and the seal breaks, letting 
                gas out. You grab the neutron bomb and runa as fast as
                you can to the bridge where you must place it in the 
                right spot.
                """))
            return 'the_bridge'
        else:
            print(dedent("""
                The lock buzzes ont last time and then you hear a 
                sickening melting sound as the mechanism is fused
                together. You deide to sit there, and finally the
                Gothons blow up the ship from their ship and you die.
                """))
            return 'death'



class TheBridge(Scene):

    def enter(self):
        print(dedent("""
            You burst onto the Bridge with the netron destruct bomb
            under your arm and surprise 5 Gothons who are trying to
            take control of the ship. Eath of them has an even uglier
            clown costume than the last. They haven't pulled their
            weapons out yet, as they see the active bomb under your
            arm and don't want to set it off.
            """))
        
        action = input("> ")

        if action == "throw the bomb":
            print(dedent("""
                In a panic you throw the bomb at the group of Gothons
             and make a leap for the door. Right as you drop it a 
                Gothon shoots you right in the back killing you. As 
                you die you see another Gothon frantically try to 
                disarm the bomb. You die knowing they wil probably
                blow up when it gose off.
                """))
            return 'death'

        elif action == "slowly place the bomb":
            print(dedent("""
                You point your blaster at the bomb under your arm and 
                the Gothons put their hands up and start to sweat.
                You inch backward to the door, open it, and then
                carefully place the bomb on the floor, pointing your 
                blaster at it. You then jump back through the door,
                punch the close button and blast the lock so the
                Gothons can't get out. Now that the bomb is placed
                you run to the escape pod to get off this tin can.
                """))
            return 'eacape_pod'
        else:
            print("DOES NOT COMPUTE!")
            return "the_bridge"


class EscapePod(Scene):

    def enter(self):
        print(dedent("""
            You rush through the ship desperately trying to make it
            the escape pod before the whole ship explodes. It seems
            like hardly any Gothons are on the ship, so your run is
            clear of interference. You get to the chamber with the 
            escape pods, and now need to pick one to teke. Some of 
            them could be damaged but you don't have time to look.
            There's 5 pods, which one do you take?
            """))

        good_pod = randint(1,5)
        guess = input("[pod #]> ")


        if int(guess) != good_pod:
            print(dedent("""
                You jump into pod {guess} and hit the eject button.
                The pod escapes out into the void of space, then
                implodes as the hull ruptures, crushing your body into
                jam jelly.
                """))
            return 'death'
        else:
            print(dedent("""
                You jump into pod {guess} and hit the eject button.
                The pod easily slides out into space heading to the
                planet below. As it files to the planet, you look 
                back and see your ship implode then explode like a 
                bright star, taking out the Gothon ship at then same
                time. You won!
                """))

            return 'finished'

class Finished(Scene):

    def enter(self):
        print("You won! Good job.")
        return 'finished'

这是游戏的剩余场景,因为我知道我需要它们,并且已经想过它们之间如何流转,所以我能直接把代码
写出来。
顺便说一句,我不会把所有这些代码都输入进去。还记得我说过要循序渐进,一点一点来。现在我只给你们看最后的结果。


class Map(object):

    scenes = {
        'central_corridor': CentralCorridor(),
        'laser_weapon_armory': LaserWeaponArmory(),
        'the_bridge': TheBridge(),
        'escape_pod': EscapePod(),
        'death': Death(),
        'finished': Finished(),
    }
    
    def __init__(self, start_scene):
        self.start_scene = start_scene

    def next_scene(self, scene_name):
        val = Map.scenes.get(scene_name)
        return val

    def opening_scene(self):
        return self.next_scene(self.start_scene)

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


a_map = Map('central_corridor')
a_game = Engine(a_map)
a_game.play()

最后是我通过制作地图来运行游戏的代码,在调用 play 使游戏工作之前,我把地图交给了引擎。


运行结果

确保你理解了这个游戏,并且你先试图自己去解决它。如果你被难住了,你可以通过阅读我的代码来作弊,然后继续尝试自己解决它。

$ python3.6 ex43.py
The Gothons of Planet Percal #25 have invaded your ship and destroyed your entire crew. You are
You’re running down the central corridor to the Weapons Armory when a Gothon jumps out, red scal
> dodge!
Like a world class boxer you dodge, weave, slip and slide right as the Gothon’s blaster cranks a
You’re worse than your Dad’s jokes.

附加练习

1. 改变它!也许你不喜欢这个游戏,因为太暴力了,也可能你对科幻不感兴趣。先让游戏运行起来,然后把它变成你喜欢的样子。这是你的电脑,你可以让它做你想做的。

2. 我这段代码有一个bug。为什么门锁猜了11次?
因为没有十次的限制条件

3. 解释一下如何返回隔壁房间。
填写错误的信息

4. 在游戏中添加作弊代码,这样你就可以通过比较难的房间。我可以通过在一行写两个字来实现这一点

5. 回到我的描述和分析,然后尝试为这个英雄和他遇到的各种哥特人建立一个小的战斗系统。

6. 这实际上是“有限状态机”(finite state machine)的一个小版本。读读相关的内容,你可能看不懂,
但无论如何都要试一试。

ex43_cn.py(汉化版-_-)

from sys import exit
from random import randint
from textwrap import dedent

class Scene(object):

    def enter(self):
        print("尚未配置该场景")
        print("子类化并执行enter()")
        exit(1)

class Engine(object):#引擎

    def __init__(self, scene_map):
        self.scene_map = scene_map

    def play(self):
        current_scene = self.scene_map.opening_scene()
        last_scene = self.scene_map.next_scene('finished')

        while current_scene != last_scene:
            next_scene_name = current_scene.enter()
            current_scene = self.scene_map.next_scene(next_scene_name)

        # 确保打印最后场景
        current_scene.enter()

class Death(Scene):

    quips = [
        "你死了,你可太菜了!",
        "你这个失败者!",
        "我家狗玩的都比你好!",
        "你比你爸的笑话还糟!!"
    ]

    def enter(self):
        print(Death.quips[randint(0, len(self.quips)-1)])
        exit(1)

class CentralCorridor(Scene):

    def enter(self):
        print(dedent("""
            珀卡25号星球的哥特人入侵了你的飞船,你是最后一个幸存者,
            你的最后一个任务是从武器库中得到中子弹并将它放到桥上,然
            后进入逃生舱后炸毁飞船。

            当你正在通往武器库的中央走廊里奔跑时,一个哥特人跳了出来,
            红色的鳞状皮肤,深色的肮脏牙齿和邪恶的小丑打扮在充满仇恨
            的身体周围流动。他挡住了军械库的门,准备掏出武器炸死你。
            此时你该怎么办?
            1、拿枪射击
            2、溜了溜了
            3、给它讲个笑话
            """))

        action = int(input("你的选择>>>> "))

        print("你的选择是:", action)

        if action == 1:
            print(dedent("""
                快速取出爆能枪,然后射向到Gothon。
                他的小丑服装在他的身上四处流动,这使你无法瞄准。
                你的激光打中了他的服装,但完全没有伤到他。
                这完全毁了他母亲给他买的全新服装,这让他疯狂地大发雷霆,不断地对着你的脸爆炸,直到你死。然后他吃了你。
                """))
            return 'death'

        elif action == 2:
            print(dedent("""
                当Gothon的发射器向你的头顶发射激光时,你开始躲闪,就像世
                界一流的拳击手一样,你走位,走位,再走位。
                在不断的躲闪中,你的脚滑了一下,然后将头撞在金属墙上,然后昏倒了。
                你刚醒就死了,因为你的头被他吃掉了。
                """))
            return 'death'
    
        elif action  == 3:
            print(dedent("""
                幸运的是,它们使你找到了哥特人的弱点;
                你给他们讲了个笑话:
                ……%*6)(@&*(¥……Q&*DSF(^$&*q6#&)%$(()$&(W6$(Q^&*($(。。。。。
                他们停手了, 试图克制自己, 可是却笑出声来,动弹不得。
                当他一直在大笑的时候你溜走并一直射击它的头,直到他倒下,然后溜入武器仓库。              
                """))
            return 'laser_weapon_armory'

        else:
            print("DOES NOT COMPUTE!")
            return 'central_corridor'
        

class LaserWeaponArmory(Scene):

    def enter(self):
        print(dedent("""
            你潜入武器库,蹲下扫描房间,寻找更多可能隐藏的哥特人。
            太安静了,太安静了。
            你站起来,跑到房间的另一边,在它的容器里找到了中子弹。
            盒子上有个键盘锁,你需要密码才能取出炸弹。
            如果你把密码弄错10次,锁就会永远关上,你就拿不到炸弹。
            密码是3位数字。
            """))
    
        code = f"{randint(1,9)}{randint(1,9)}{randint(1,9)}"
        print("提示", code)
        guess = input("输入三位密码>>>> ")
        guesses = 0

        while guess != code and guesses < 10:
            print("输入错误")
            guesses += 1
            guess = input("输入三位密码>>>> ")

        if guess == code:
            print(dedent("""
                容器咔嗒一声打开,密封破裂,放出气体。
                你抓起中子弹,以最快的速度跑到桥上,你必须把它放在正确的位置。 
                """))
            return 'the_bridge'
        else:
            print(dedent("""
                锁上一次嗡嗡作响,然后你听到一个令人作呕的融化的声音,因为机制融合在一起。
                你坐在那里,最后哥特人在他们的船上被炸毁了,你死定了。
                """))
            return 'death'



class TheBridge(Scene):

    def enter(self):
        print(dedent("""
            你手臂下夹着自毁炸弹冲进舰桥,让5个试图控制飞船的哥特人大吃一惊。
             他们每个人的小丑服装都比上一个丑陋。 
             他们还没有拔出武器,因为他们看到启动了的炸弹在你的胳膊下,不想将其引爆。
             你可以选择:
             1、扔炸弹
             2、慢慢地放炸弹
            """))
        
        action = int(input("你的选择是>>>> "))

        if action == 1:
            print(dedent("""
                惊慌失措时,您将炸弹扔到了哥德岛上,向门口跳去. 
                就在你掉下去的时候,一个哥顿朝你的后背开枪杀了你。 
                当你死去时,你看到另一个哥特人疯狂地试图解除炸弹的武装  
                你死的时候知道他们可能会爆炸,当它消失。
                """))
            return 'death'

        elif action == 2:
            print(dedent("""
                你用爆能枪指着手臂下面的炸弹,哥特人举起手开始流汗-_-!!!。
                你向后一英寸到门,打开它,然后小心地把炸弹放在地板上,把你的爆能枪指向它。
                然后你从门里跳回来,按一下关门按钮,然后反锁,这样哥特人就不能出去了。
                既然炸弹放好了,你就跑去逃生舱,离开这个鬼地方。 
                """))

            return 'escape_pod'
        else:
            print("DOES NOT COMPUTE!")
            return "the_bridge"


class EscapePod(Scene):

    def enter(self):
        print(dedent("""
            你拼命地冲进飞船,想在整艘船爆炸前进入逃生舱。
            似乎船上几乎没有哥顿人,所以你的跑步没有干扰。
            你房间来到了逃生舱,现在需要挑一个乘坐。
            有些可能会损坏,但你没时间看。有5个逃生舱,你要哪一个?
            """))

        good_pod = randint(1,5)
        print("提示:", good_pod)
        guess = input("输入逃生舱编号(1-5)>>>> ")


        if int(guess) != good_pod:
            print(dedent("""
                你跳进{guess}号舱,按下弹出按钮。
                逃生舱逃逸到太空中,然后随着船体破裂而内爆,把你的身体压成果酱果冻。
                """))
            return 'death'
        else:
            print(dedent("""
                你跳进逃生舱{guess}并按下弹出按钮。
                太空舱很快滑入太空,向下面的行星前进。
                当它飞向行星时,你回首过去,看到你的飞船爆炸了,然后像一颗明亮的恒星一样,同时把哥顿飞船摧毁。你赢了!         
                """))

            return 'finished'

class Finished(Scene):

    def enter(self):
        print("你赢了!厉害啊!!!")
        return 'finished'

class Map(object):

    scenes = {
        'central_corridor': CentralCorridor(),#中央走廊
        'laser_weapon_armory': LaserWeaponArmory(),#激光武器库
        'the_bridge': TheBridge(),#桥
        'escape_pod': EscapePod(),#逃生舱
        'death': Death(),#死
        'finished': Finished(),#结束
    }
    
    def __init__(self, start_scene):
        self.start_scene = start_scene

    def next_scene(self, scene_name):
        val = Map.scenes.get(scene_name)
        return val

    def opening_scene(self):
        return self.next_scene(self.start_scene)

a_map = Map('central_corridor')
a_game = Engine(a_map)
a_game.play()

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值