码迷,mamicode.com
首页 > 其他好文 > 详细

(译)来自Percal25号行星的哥顿人

时间:2015-07-26 19:05:35      阅读:527      评论:0      收藏:0      [点我收藏+]

标签:

这是《Learn Python The Hard Way(笨方法学Python)》第43个练习的译文,我自己在看这篇文章的时候比较困难(原文链接:http://learnpythonthehardway.org/book/ex43.html),网上的中文版这一节貌似有大量的删减,于是我自己就大概翻译了一遍,并没有逐词翻译,但应该基本上保存了原文的意思。

 基础的面向对象分析与设计

我将会描述一个当你想使用Python时可以使用的方法,特别是在进行面向对象编程的时候。所给出的这个方法的意思是我将会给你一组步骤,你按顺序来做就好,但你也不能傻不拉几的用它来解决所有的问题。这仅仅是解决许多编程问题的一个好的开端,你也不能认为那些你要解决的问题只能用这一种方法。这种方法仅仅是你可以遵循的一个而已。

这些步骤是:
        1.把问题写出来或者画出来
        2.从第一步提取出关键的概念并研究他们
        3.为这些概念建立类和对象的映射
        4.用代码实现这些类并测试运行他们
        5.重复并改善他们

运用这些步骤的方法是“自顶向下”, 这意味着刚开始的时候是非常抽象和简单的一些想法,然后慢慢改善精炼,直到这些想法固定,然后将他们用代码来实现。
刚开始的时候我会仅仅写下这个问题,然后尽量去想任何与之相关的东西。我可能会去画一到两张图表,也可能是一些想法之间的关系,或者甚至给我自己写一些邮件来描述这些问题。这使我能够明白这个程序中的关键概念,也使我去探究我可能已经知道的一些东西。
之后我通过这些笔记、图示和一些描述,我得到了关键概念。做到这些有一个简单的方法:列出所有出现在你的笔记或是图示中的名词和动词,然后写出他们之间的关系。这给了我在下一步要用到的类、对象、函数的名字列表。我用这个列表去进一步研究还有哪些我需要但还未弄明白的东西。
当我有了这张概念列表,我就能得到这些概念的大致轮廓并知道他们作为类是如何相关联的。你通常会找到你这个列表中的名词并问:“这个名词像那另一个名字一样吗?也就是说,他们有共同的父类,应该怎么说?”一直这么做,直到你得到了一个类似树状图的类的层次关系。之后你会考虑你的那些动词,看看他们是否是每个类的函数并将他们放在你的树状图中。
当得到这些类的层次关系之后,我坐下来并且写一些仅仅包含类、他们的函数的基础架构代码。之后我写一个测试来运行这些代码并确保我写的这些类能够正确运行。有时我会先写一个测试代码,然后写一小段代码,然后一小段测试...直到我完成整个项目。
最后,我一遍遍的重复和改善使它尽可能的简洁,之后再去实现更多的功能。如果因为某个概念的特殊部分或是某个我没有预料到的问题困扰到我,我会坐下来重新开始搞定那个部分,然后再继续。
现在我会用这个方法为这部分的联系弄出来一个游戏引擎和一个游戏。

一个简单的游戏引擎分析

我想搞的这个游戏的名字叫做“来自percal 25号行星的哥顿人”,这是一个小型太空探险游戏。这仅仅是在我脑海中的一个概念,我要探寻出这个想法然后实现它。

把问题写或者画出来

我将会为这个游戏写一小段话:
“外星人入侵了我们的飞船,我们的英雄需要穿过迷宫般的房间打败他们,之后他要从行星下面的逃生舱逃脱。这个游戏更像Zork或者Adventure这类输出一些文字然后很有趣的就挂了的游戏。这个游戏需要包含一个引擎用来能够运行一个充满房间和场景的地图。每一个房间在玩家进入的时候都会有各自的描述,之后会告诉引擎运行下一个地图。”
此时我有了一个好主意并且知道了如何让游戏运行,所以现在我想描述每一个场景:
    死亡:
        这个场景是玩家挂了而且应该很有趣
    中轴路:
        这是起点,并且有一个哥顿人站在那里,他们要在继续之前很干掉他。
    激光武器军械库:
        这是英雄获得中子炸弹的地方,他要在到达逃生舱之前用中子炸弹炸掉飞船。它有一个数字键盘,英雄需要猜到它的号码。
    船:
        这是另一个与哥顿人战斗的场景,英雄在这里安装了炸弹。
    逃生舱:
        这是英雄逃脱的地方,但是首先腰猜到正确的逃生舱。
这时我可能画出了这些的地图,可能为每一个房间写了更多的描述,反正就是我所有脑子中想到的东西。

提取主要概念并且研究他们

现在我有了足够多的信息去提取一些名词然后去分析他们的类的层次关系。首先我列出了所有的名词:
Alien        外星人
Player        玩家
Ship        飞船
Maze        迷宫
Room        房间
Scene        场景
Gothon    哥顿人
Escape Pod 逃生舱
Planet        行星
Engine    引擎
Death        死亡
Central Corridor 中轴路
Laser Weapon Armory 激光武器军械库
The Bridge 桥
我也可能通过所有的动词去看哪些可能是好的函数名,但是我先暂时跳过。
这时你可能研究了每一个概念和你不知道的任何东西。例如,我可以玩这个游戏中的其中几类,明确了他们是如何工作的。我可以探索到飞船是如何设计的或是炸弹是如何工作的。可能我会去探索如何将游戏状态存入数据库这样的技术问题。当我完成了这些探索,我可能会重新回到第一步加入一些新的信息然后重写我的描述并提取新的概念。

为概念创造一个类的层次结构和对象映射

当我完成了上面的东西之后,我转而去弄一个类的层次结构,我会去问:“类似于其他的事情是什么?”我也会去问:“从一个词到另一个东西的基础主要是什么?”
我立刻意识到“房间”和“场景”是取决于我想做什么事的基础。我将会去为这个游戏挑选场景。之后我意识到所有像“中轴路”这样的特殊房间都仅仅是基础场景。我也意识到死亡也是一个基本场景,这是我明白了“场景”大于“房间”,我们可以有一个死亡场景,但是死亡房间看起来就有点奇怪了。“迷宫”和“地图”基本上相同,所以我将会更多的使用“地图”。我不想做一个战斗系统所以我将会忽略“外星人”和“玩家”但会先为以后保留他们。“行星”也就仅仅是一个场景。
搞定以上的之后我在我的编辑器中写出了一个类的层次结构:
* 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 逃生舱
注意我仅仅把“进入”放在“场景”下,因为我知道它(Scene 场景)下所有场景都会继承它然后重写。

写一些类的代码然后写一个测试去运行他们

一旦我拥有了这个树状的类层次和一些函数,我就会在我的编辑器中打开源文件并试着为他们编写代码。通常我仅仅是把树层次复制粘贴到源文件然后编辑到类中。这里有一个小例子,展示了它可能一开始的样子,在文件的结尾有一个简单的小测试。

 1 class Scene(object):
 2 
 3     def enter(self):
 4         pass
 5 
 6 
 7 class Engine(object):
 8 
 9     def __init__(self, scene_map):
10         pass
11 
12     def play(self):
13         pass
14 
15 class Death(Scene):
16 
17     def enter(self):
18         pass
19 
20 class CentralCorridor(Scene):
21 
22     def enter(self):
23         pass
24 
25 class LaserWeaponArmory(Scene):
26 
27     def enter(self):
28         pass
29 
30 class TheBridge(Scene):
31 
32     def enter(self):
33         pass
34 
35 class EscapePod(Scene):
36 
37     def enter(self):
38         pass
39 
40 
41 class Map(object):
42 
43     def __init__(self, start_scene):
44         pass
45 
46     def next_scene(self, scene_name):
47         pass
48 
49     def opening_scene(self):
50         pass
51 
52 
53 a_map = Map(central_corridor)
54 a_game = Engine(a_map)
55 a_game.play()


在这个文件中你可以看到,我简单地重复了我想要的层次结构并且在结尾写了一点代码来验证这个基本机构的正常运行。在接下来部分的练习中,你将会填写这个代码的其余部分,并且让它和这个游戏的描述一样运行起来。

重复并改善

这最后一步并不复杂,就是一个while循环。你永远不要做一次通过的操作。相反你应该回到整个循环过程再来一遍并且从你学到的下一步的信息中改善它。有时我会回到第三步然后意识到我需要再去回顾第一步和第二步的更多东西,所以我会停下现在所在进行的然后回到之前。有时我会突然得到一个灵感,我会立刻跳到末尾将我脑中的想法写出来,但之后我仍会回到前面做前面的步骤确保我覆盖了每一种可能性。
这种方法还有另外的一种思想,那就是你做的一些东西不仅能运用在一个层次上,你可以在当你遇到一个特殊的问题时运用到每一个层面。比方说我还不知道如何写Engine.play方法。我会停止然后去做整个过程去找出如何编写一个函数。

自顶向下 vs. 自下而上

这个方法被贴上“自顶向下”的标签是因为它是从最抽象的概念(顶层)然后过渡到实际的实现。我希望你从现在开始去分析问题的时候使用我在这本书中描述的方法,但你应该知道另一个解决问题的方法是也可以从编码开始,然后进一步的抽象概念。这另一种方法成为“自下而上”。做这些有下面的这几个步骤:
    1.搞定一个小问题,用编码实现并让它运行起来。
    2.精炼这些代码,让其包含类和自动化测试以使得其更加正式。
    3.提取你使用的关键概念并试着去研究他们。
    4.详细地描述出来真正做了什么。
    5.返回然后再精炼这些代码,可能要丢弃他们然后重新再来。
    6.重复,继续其他部分的问题。
如果你很坚定地在编程,我觉得这是一个更好的方法,这很自然的想到代码和问题之间的关系。这个方法当你知道全部问题的每一个部分的时候是非常好的,但你可能获取不到整个问题的全部信息。把他们分解成小块,然后用代码实现,能够帮助你慢慢的发现问题知道你解决他们。然而,请记住你的解决方案很可能曲折和怪异,这也就是为什么这个方法会包含返回并研究然后把你学到的东西真正都搞清楚。

“来自25号星球的哥顿人”代码

停!我接下来将会向你展示上面问题的最终解决方案,但我不希望你仅仅是看一看然后照着敲出来。我希望你能搞定我之前的那个粗糙的框架代码然后根据描述让它运行起来。当你自己可以解决之后再来看看我是怎么做的。
我不会直接堆出来所有的代码,相反我将会把这个最终文件ex43.py分解成各个部分然后去解释每一小段。

from sys import exit
from random import randint

 

这仅仅是这个游戏最基础的导入的一些东西,没什么特别的。

class Scene(object):

    def enter(self):
        print "This scene is not yet configured. 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.",
         "Your mom would be proud...if she were smarter.",
         "Such a luser.",
         "I have a small puppy that‘s better at this."
    ]

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

我的第一个场景是一个怪异的Death(死亡)场景,它向你展示了你可以写的最简单的场景。

class CentralCorridor(Scene):

    def enter(self):
        print "The Gothons of Planet Percal #25 have invaded your ship and destroyed"
        print "your entire crew.  You are the last surviving member and your last"
        print "mission is to get the neutron destruct bomb from the Weapons Armory,"
        print "put it in the bridge, and blow the ship up after getting into an "
        print "escape pod."
        print "\n"
        print "You‘re running down the central corridor to the Weapons Armory when"
        print "a Gothon jumps out, red scaly skin, dark grimy teeth, and evil clown costume"
        print "flowing around his hate filled body.  He‘s blocking the door to the"
        print "Armory and about to pull a weapon to blast you."

        action = raw_input("> ")

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

        elif action == "dodge!":
            print "Like a world class boxer you dodge, weave, slip and slide right"
            print "as the Gothon‘s blaster cranks a laser past your head."
            print "In the middle of your artful dodge your foot slips and you"
            print "bang your head on the metal wall and pass out."
            print "You wake up shortly after only to die as the Gothon stomps on"
            print "your head and eats you."
            return death

        elif action == "tell a joke":
            print "Lucky for you they made you learn Gothon insults in the academy."
            print "You tell the one Gothon joke you know:"
            print "Lbhe zbgure vf fb sng, jura fur fvgf nebhaq gur ubhfr, fur fvgf nebhaq gur ubhfr."
            print "The Gothon stops, tries not to laugh, then busts out laughing and can‘t move."
            print "While he‘s laughing you run up and shoot him square in the head"
            print "putting him down, then jump through the Weapon Armory door."
            return laser_weapon_armory

        else:
            print "DOES NOT COMPUTE!"
            return central_corridor

在那之后我创造了CentralCorridor(中轴路),这是游戏的开端。我在Map之前搞定了场景是因为我之后需要将他们联系起来。

class LaserWeaponArmory(Scene):

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

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

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



class TheBridge(Scene):

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

        action = raw_input("> ")

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

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


class EscapePod(Scene):

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

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


        if int(guess) != good_pod:
            print "You jump into pod %s and hit the eject button." % guess
            print "The pod escapes out into the void of space, then"
            print "implodes as the hull ruptures, crushing your body"
            print "into jam jelly."
            return death
        else:
            print "You jump into pod %s and hit the eject button." % guess
            print "The pod easily slides out into space heading to"
            print "the planet below.  As it flies to the planet, you look"
            print "back and see your ship implode then explode like a"
            print "bright star, taking out the Gothon ship at the same"
            print "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()

最后我搞定了全部的代码,把Map(地图)交给Engine(引擎),然后调用play将游戏运行起来。

你应该看到的结果:

确保你理解了这个游戏,并且你应该首先自己尝试。如果你被难住,稍微瞄一眼我的代码,然后继续尝试自己搞定。

当我运行我的游戏的时候就像这样:

$ python ex43.py
The Gothons of Planet Percal #25 have invaded your ship and destroyed
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 costume
flowing around his hate filled body.  He‘s blocking the door to the
Armory and about to pull a weapon to blast you.
>  dodge!
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 on
your head and eats you.
Your mom would be proud...if she were smarter.

加分练习

1.改变它!可能你恨这个游戏。可以更暴力一点,但不能像科幻小说一样。让这个游戏可以运行,然后改变成你喜欢的任何样子。这是你的电脑,想怎么做就怎么做。

2.我的这个代码有bug。为什么门锁可以猜11次?

3.解释一下返回下一个房间的工作原理

4.添加个作弊代码,让你能够轻易通过困难的房间。我写两个词在一行上就能搞定了。

5.返回去看我的描述和分析,然后试着去为英雄造一个战斗系统,让他遭遇各种哥顿人

6.这实际上是一个小版本的”有限状态机“。去阅读一下。他们可能没有意义,但是请尝试一下。

常见问题

我怎样为自己的游戏编个故事?

你可以就像给你的朋友讲一个故事一样。或者从你喜欢书或者电影里找一些场景也是可以的。

(译)来自Percal25号行星的哥顿人

标签:

原文地址:http://www.cnblogs.com/lzl7/p/4677322.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!