图片主题是:那些年我们追过的武侠美女。
有点小激动,小时候的零花钱全用在买武侠的贴纸上了,六毛钱一张,当年可是巨款呀!别说是牺牲糖葫芦爆米花,就是勒紧裤腰带也得买呀!因为能带来巨大的精神愉悦感,我有两个专门贴贴纸的本子,没事就翻着看,要不是体育总不及格,一定会立下仗剑走天涯的远大理想的。小时候我可不喜欢什么迪斯尼公主,我最想当的是侠女十三妹,黄杏秀是我心中最美的人了。其它记不得了,就记得她用轻功从天而降,拿着暗器biu biu biu刺杀谁啦的场景,简直帅呆了!听说还有少年看完少林寺真跑道嵩山少林要去学武功,我还惭愧很久呐,看看人家这执行力!!!是长大后才知道武侠和江湖不只是鲜衣怒马、除恶扬善和“事了拂衣去,深藏功与名”,更有风餐露宿人心险恶利来利往和你死我活的丑陋与不堪。
------------扯淡完毕分割线--------------
不知爱读武侠小说的朋友记得古龙陆小凤系列里,西门吹雪和叶孤城在紫禁之巅那场巅峰对决不?还有射雕英雄传和神雕侠侣里北丐洪七公和西毒欧阳锋从年轻打到老,斗了一辈子,最后在华山之巅相拥大笑而亡。最后我们就用类和对象模拟绝世高手的巅峰对决,既可以向经典致敬,又可以用实际应用来体会面向对象的程序开发。
武林高手的巅峰对决,就从“武林高手”和“巅峰对决”这两个关键字入手,建立两个类,或者说建立两个模版:武林高手类和巅峰对决类。有了模版,就可以生成实例。“月圆之夜,紫禁之巅”这场对决可以表述成:叶孤城和西门吹雪是“武林高手”类生成的两个实例(对象),而紫禁之巅则是“巅峰对决”类的一个发生在紫禁城,在叶孤城和西门吹雪两个剑客之间展开的一个实例。
先来看武林高手(Warriors)这个类,可以从两个角度来分析:
1、 特征(属性): 姓名、武功高强(有多高?)、最大攻击力、最大防守力
武功高强、压箱底的绝招的杀伤力和最有效抵挡的力道这些都是描述性的词语,必须量化才能用计算语言表达出来。我们用能量值来表达武功,武功越高能量值越大;压箱底的绝招的杀伤力用最大攻击力的数值表达;最有效抵挡的力道用最大防守力的数值表达。
2、 行为能力(方法函数):
进攻:每次出手攻击的力道不是恒定的,跟策略、状态、形势都有关系。为了简单,我们让这个力度随机产生的,进攻值=最大攻击力 *(0和1间随机产生的数值)。为了减少互殴的回合,随机数加0.5,就算是可能某次进攻值超过了最大攻击力,用超常发挥也可以解释得通,所以进攻值=最大攻击力 *(0和1间随机产生的数值)
防守:同进攻,防守值=最大防守力 * (0和1间随机产生的数值 + 0.5)
再来看巅峰对决(Battles)这个类:
1、 属性:地点、对决双方(高手1、高手2)
2、 方法函数:
比武开始launchAttack():两位选手开始过招。实际情况千变万化。可能是一个佛山无影脚连环踢,对方连连后退毫无还手之力;也可能是一方开始攻势凌厉,却后劲不足,另一方以守为攻,后面却越战越勇.......确定不可能的一种情况就是:我打你一拳,你挡一下,然后我停住等你来打我,这样一来一往轮流做庄。但是,鉴于水平有限,这里只能按轮流进攻来处理。所以这个函数这么写:
(1) 过招的“来”(画面:西门吹雪攻,叶孤城守)
(2) 过招的“回”(画面:叶孤城攻,西门吹雪守)
(3) 重复以上过程(不停地有来有回),直到一方战败
实现过招fight(进攻方,防守方):首先fight()应该是个static method,它跟“巅峰对决”这个类有逻辑上的关系,但每次过招又不属于整个类或专属于具体对象。过招拆成可表达的动作就是:
(1) 进攻方.进攻()
(2) 防守方.防守()
(3) 计算对防守方的伤害值(进攻方的攻击力-防守方的防守力)
(4) 计算防守方的功力(能量值)还剩下多少
(5) 汇报战况(谁向谁进攻,攻击力多少,被攻击方能量值还剩多少)
(6) 如果防守方战斗力下降到0或0以下,宣布败北(Game over),否则继续战斗
两个类建好后,接下来的事情就好办了,来看“天外飞仙”叶孤城和剑神西门吹雪月圆之夜于紫禁之巅这场精彩对决。首先生成两个武林高手类的对象snow和city:
snow:姓名-西门吹雪、功夫-50、最大攻击力-20、最大防守力-10,即
snow = Warriors(‘西门吹雪’, 50, 20, 10)
city:姓名-叶孤城、功夫-50、最大攻击力-20、最大防守力-10,即
city = Warriors(‘叶孤城’, 50, 20, 10)
再生成个“比武”的实例:紫禁之巅 forbiddenCity = Battles(‘紫禁城’, snow, city ), 然后战斗开始:forbiddenCity.lauchAttack()
北丐洪七公和西毒欧阳锋那场华山之巅的对决也是先生成武林高手类的对象北丐和西毒:
north = Warriors('洪七公', 100, 40, 15)
west= Warriors('欧阳锋', 100, 40, 15)
再生成华山之巅的“比武”的实例:
mountainTop = Battles('华山之巅', north, west)
战斗开始……
mountainTop.launchAttack()
# ---------- 尖峰对决 ----------
import random # 需要random.random()产生0~1之间的随机数
import math # 需要math.ceil()取整小数
class Warriors: # 武林高手类
def __init__(self, name='武术爱好者', energyValue=0, attkMax=0, defendMax=0): # 初始化函数的形参给了缺省值
self.name = name # 大侠也许报上姓名
self.energyValue = energyValue # 武功高低
self.defendMax = defendMax
# 为了化解对方绝招所能耗费的最大内力(最大防守值)
self.attkMax =attkMax # 最杀招攻击力(最大攻击力)
def attack(self): # 出招
attkAmt = self.attkMax * (random.random() + 0.5)
# 计算产生的破坏力,0~1之间的随机数乘以最大攻击力,为了加大每次的攻击力(不然一次战斗要打很久),随机数加了个0.5,理论上是用0.5~1.5的随机数与最大攻击力。超过最大攻击力的进攻认为是超常发挥。
return attkAmt # 返回量化了的攻击力,实数float类型
def defend(self): # 防守
blockAmt = self.defendMax * (random.random() + 0.5)
# 计算防守力,方法同攻击力的计算方法
return blockAmt # 返回量化了的防守力,实数float类型
class Battles: # 巅峰对决类
def __init__(self, location, warrior1, warrior2):
self.location = location # 决斗发生地点
self.warrior1 = warrior1 # 参加决斗的一方
self.warrior2 = warrior2 # 参加决斗的另一方
print('\n\n{}:{} vs {}'.format(self.location, self.warrior1.name, self.warrior2.name)) # 示意决斗开始
def launchAttack(self): # 决斗开始
while True: # 水平有限,大战的形式只能设定为:我打你一拳,然后不动手等着你打我一拳;你打完我才来第二拳,这期间你不继续进攻,而是等着我打第完二拳……如此循环,直到一方战败
# 高手1攻击,高手2防守
if self.fight(self.warrior1, self.warrior2):
print('比武结束') # 高手2战败,退出循环,宣布比武结束
break
# 高手1攻击,高手2防守
if self.fight(self.warrior2, self.warrior1):
print('比武结束') # 高手1战败,退出循环,宣布比武结束
break
@staticmethod # 标识静态方法函数
def fight(attacker, defender): # 慢镜头每一招,用代码描述发生了什么
attacker.attack() # 攻击方进攻,返回攻击力
defender.defend() # 防守防防守,返回防守力
defend2warriorB = math.ceil(attacker.attack() - defender.defend()) # 计算对防守方的伤害值,攻击力-防守力,调用math.ceil()是为了方便看把实数转成整数,往上靠,如4.7转成5,4.2也转成5
defender.energyValue = defender.energyValue - defend2warriorB # 计算防守方还剩下几分功力(能量值)
print('\n{} 对 {} 发起攻击, 伤害值为{} '.format(
attacker.name, defender.name, defend2warriorB))
print('{} 功力降为 {} '.format(attacker.name, defender.energyValue))
# 公布这次攻击的详情:进攻方、防守方、伤害值、防守方还剩下的能量值
if defender.energyValue < 0: # 如果防守方能量值小于0
return True # 胜负已分,退出fight()的while循环
print('\n{} 战败, {} 登顶武林至尊'.format(defender.name, attacker.name)) # 宣布比赛结果
else:
return False # 防守方能量值没降为0,还可再战,战斗继续
snow = Warriors('西门吹雪', 50, 20, 10)
# 生成剑神西门吹雪实例,武功高强(能量值50),最大攻击力20,最大防守力10
city= Warriors('叶孤城', 50, 20, 10)
# 生成剑外飞仙叶孤城实例,武功与西门吹雪势均力敌(能量值50),最大攻击力20,最大防守力10
forbiddenCity = Battles('紫禁之巅', snow, city)
# 生成决战类的实例:紫禁之巅这一战,决战地点:紫禁城,决战双方:叶孤城vs西门吹雪
forbiddenCity.launchAttack() # 吃瓜群众坐好,战斗开始了
north = Warriors('洪七公', 100, 40, 15)
# 生成北丐洪七公实例,一代宗师(能量值100),最大攻击力40,最大防守力15
west= Warriors('欧阳锋', 100, 40, 15)
# 生成西毒欧阳锋实例,第一大反派(能量值100),最大攻击力40,最大防守力15
mountainTop = Battles('华山之巅', north, west)
# 生成决战类的实例:华山之巅这一战,决战地点:华山之巅,决战双方:洪七公vs欧阳锋
mountainTop.launchAttack() # 吃瓜群众坐好,虽然华山顶很冷,因为战斗开始了
运行结果:
紫禁之巅:西门吹雪 vs 叶孤城 西门吹雪 对 叶孤城 发起攻击, 伤害值为19 西门吹雪 功力降为 31 叶孤城 对 西门吹雪 发起攻击, 伤害值为19 叶孤城 功力降为 31 西门吹雪 对 叶孤城 发起攻击, 伤害值为13 西门吹雪 功力降为 18 叶孤城 对 西门吹雪 发起攻击, 伤害值为17 叶孤城 功力降为 14 西门吹雪 对 叶孤城 发起攻击, 伤害值为0 西门吹雪 功力降为 18 叶孤城 对 西门吹雪 发起攻击, 伤害值为5 叶孤城 功力降为 9 西门吹雪 对 叶孤城 发起攻击, 伤害值为16 西门吹雪 功力降为 2 叶孤城 对 西门吹雪 发起攻击, 伤害值为0 叶孤城 功力降为 9 西门吹雪 对 叶孤城 发起攻击, 伤害值为6 西门吹雪 功力降为 -4 叶孤城 战败, 西门吹雪 登顶武林至尊 比武结束 |
华山之巅:洪七公 vs 欧阳锋 洪七公 对 欧阳锋 发起攻击, 伤害值为21 洪七公 功力降为 79 欧阳锋 对 洪七公 发起攻击, 伤害值为31 欧阳锋 功力降为 69 洪七公 对 欧阳锋 发起攻击, 伤害值为18 洪七公 功力降为 61 欧阳锋 对 洪七公 发起攻击, 伤害值为15 欧阳锋 功力降为 54 洪七公 对 欧阳锋 发起攻击, 伤害值为36 洪七公 功力降为 25 欧阳锋 对 洪七公 发起攻击, 伤害值为11 欧阳锋 功力降为 43 洪七公 对 欧阳锋 发起攻击, 伤害值为23 洪七公 功力降为 2 欧阳锋 对 洪七公 发起攻击, 伤害值为7 欧阳锋 功力降为 36 洪七公 对 欧阳锋 发起攻击, 伤害值为12 洪七公 功力降为 -10 欧阳锋 战败, 洪七公 登顶武林至尊 比武结束 |
属性的设置和提取没有用装饰器处理,有兴趣的朋友可以自行以完善。
即将推出的Python ABC教程对PythonABC视频内容进行了梳理,修正了发现的错误、对代码做了些许优化、替换掉视频中的英文注释、替换掉国内不能访问的资源……敬请关注,谢谢
欢迎访问PythonABC网站:pythonabc.org