项目实操关卡-人机PK

通过实战去成长,这体现在:能在学习初期就知道什么是关键知识,了解专业程序员一般是怎么思考和解决问题的,在一遍遍分析问题、拆解问题及解决问题的过程中真正地理解知识,并用这些知识来解决自己的问题。

一个项目一般是怎么完成的。更具体的说,程序员是如何思考和解决问题的呢?

我认为其中一个很重要的能力是【问题拆解】。问题拆解,指的是在做一件事或面对一个问题的时候,将其拆解成多个步骤或多个层次,逐步执行和解决问题,直至达到最终效果。

不过可能是出于长期编写代码的习惯,程序员会将问题拆得更细致一些,即拆到无法再拆为止

将完成一个项目的流程总结为以下三步
1.明确项目目标
2.分析过程,拆解项目
3.逐步执行,代码实现

明确项目目标

在互联网公司,一般情况下是由产品经理提出明确的项目需求,由程序员来实现,他们之间是“相爱相杀”的关系。:)

今天且让我扮演一下产品经理的角色。我们此次要实现的需求是:人机PK小游戏。具体效果请参照下面的示意动图。

简单来说,这个游戏中,会随机生成玩家和敌人的属性,同时互相攻击,直至一方血量小于零。

另外,这样的战斗会持续三局,采取三局两胜制,最后输出战斗结果,公布获胜方。

分析过程,拆解项目

“功能叠加、难度递增”这个角度考虑,将我们要实现的小游戏拆分成了三个版本

各版本程序功能描述

  • 版本1.0:规定双方角色属性,战斗时人为计算扣血量,并打印出战斗过程;
  • 版本2.0︰随机生成双方角色属性,自动计算扣血量,并优化显示战斗过程的代码;
  • 版本3.0:双方进行3局PK,每局判定胜负。三局两胜,判断最终结果.

当项目被清晰地拆解后,剩下的就是去逐步执行,也就是重复“执行→遇到问题→解决问题→继续执行”这个循环的过程。

逐步执行,代码实现

  • 版本1.0:自定属性,人工PK版本
  • 版本2.0:随机属性,自动PK
  • 版本3.0:打印战果,三局两胜

版本1.0:自定属性,人工PK

我们要做的主要有三步:1.规定并显示出玩家和敌人的属性 2.双方同时互相攻击,血量根据对方的攻击力扣除 3.若有一方血量小于等于0,游戏结束。

好,我们从第1步开始:设定【玩家】和【敌人】的属性,即【血量】和【攻击】。

print('【玩家】血量:100 攻击:50')  # 自定义玩家角色的血量和攻击
print('【敌人】血量:100 攻击:30')  # 自定义敌人角色的血量和攻击

第2步:手动计算攻击一次,双方各自所剩的血量。

print('你发起了攻击,【敌人】剩余血量50')  # 人工计算敌人血量:100-50=50
print('敌人向你发起了攻击,【玩家】剩余血量70')  # 人工计算玩家血量:100-30=70

第3步:继续做人工计算:算一算,玩家攻击2次敌人,敌人的血量就等于0了,这时候可以结束战斗,打印游戏结果。

print('你发起了攻击,【敌人】剩余血量0')  # 双方同时攻击,若血量出现小于等于0,游戏结束
print('敌人向你发起了攻击,【玩家】剩余血量40')print('敌人死翘翘了,你赢了!') # 打印结果

很简单吧!现在我们要做的,就是把这三段代码拼起来,然后我会加一些修饰视觉的换行符和分割线,让运行结果看得更清楚一点。

print('【玩家】\n血量:100\n攻击:50')  # 自定义玩家角色的血量和攻击,用换行符'\n'来优化视觉
print('------------------------')  # 辅助功能,起到视觉分割的作用,让代码的运行结果更清晰print('【敌人】\n血量:100\n攻击:30')
print('------------------------')print('你发起了攻击,【敌人】剩余血量50')  # 人工计算敌人血量:100-50=50
print('敌人向你发起了攻击,【玩家】剩余血量70')  # 人工计算玩家血量:100-30=70
print('------------------------')print('你发起了攻击,【敌人】剩余血量0')  # 双方同时攻击,若血量出现小于等于0,游戏结束
print('敌人向你发起了攻击,【玩家】剩余血量40')
print('-----------------------')print('敌人死翘翘了,你赢了!') # 打印结果

结果

【玩家】
血量:100
攻击:50
------------------------
【敌人】
血量:100
攻击:30
------------------------
你发起了攻击,【敌人】剩余血量50
敌人向你发起了攻击,【玩家】剩余血量70
------------------------
你发起了攻击,【敌人】剩余血量0
敌人向你发起了攻击,【玩家】剩余血量40
-----------------------
敌人死翘翘了,你赢了!

唔...虽然看起来还有点儿意思,但所有信息一下子都蹦跶出来,一点都没有体现游戏的进程感。

所以,为了让打印出的东西能有时间间隔地依次出现,我们需要设置一个类似“计时器”的东西。在Python里,我们需要用到两行代码来实现:(敲黑板,很简单的新知识)

import time   #调用time模块
time.sleep(secs)
#使用time模块下面的sleep()函数,括号里填的是间隔的秒数(seconds,简称secs)
#time.sleep(1.5)就表示停留1.5秒再运行后续代码

这里有个新名词——模块,它是Python里一个重要的概念

你可以把模块想象成是一个装着许多神奇函数的百宝箱,不过想要使用这个百宝箱里的函数,得先用 import 模块名 这样一句代码来打开它。

然后这里我们想使用time模块里的sleep()函数,也就是让代码运行结果不要一次性全部出现,而是分批分批的出现。就要写成time.sleep(secs)的形式。

如果我想设置成打印的信息间隔1.5秒出现,代码就可以这么写:

import time  #通常import语句会写到代码的开头print('【玩家】\n血量:100\n攻击:50')
print('------------------------')
time.sleep(1.5)
#暂停1.5秒,再继续运行后面的代码print('【敌人】\n血量:100\n攻击:30')
print('------------------------')
time.sleep(1.5)
#同上print('你发起了攻击,【敌人】剩余血量50')
print('敌人向你发起了攻击,【玩家】剩余血量70')
print('------------------------')
time.sleep(1.5)print('你发起了攻击,【敌人】剩余血量0')
print('敌人向你发起了攻击,【玩家】剩余血量40')
print('-----------------------')
time.sleep(1.5)print('敌人死翘翘了,你赢了!') 

这个版本的代码还有两个明显的缺陷:一是玩家和敌人的属性(血量&攻击)是我自己说了算,那胜负早已没有悬念;二是战斗过程中血量的变化要自己手动算,那要计算机有何用?

你放心,这些都是我们会在版本2.0解决的问题。

版本2.0:随机属性,自动PK

如前所述,这个阶段,我们主要新增【随机属性】和【自动战斗】两个功能,画成流程图是这样子的:

版本2.0流程图

  • 随机生成玩家和敌人的属性
  • 显示玩家和敌人的属性
  • PK过程展示自动攻击扣血

Python 随机生成数字

import random
#调用random模块
a = random.randint(1,100)
# 随机生成1-100范围内(含1和100)的一个整数,并赋值给变量a
print(a)

方法步骤

1.定义两个变量,来存储玩家血量和玩家攻击力的数值

2.血量是100-150的随机数,攻击力是30-50的随机数

3.将两个变量打印出来

import random
player_life = random.randint(100,150)
#表示玩家血量
player_attack = random.randint(30,50)
#表示玩家攻击
print(player_life)
print(player_attack)

标准的变量名最好是用英文来表达含义,如果是多个单词组成,需要用英文下划线_来隔开。

对于取英文变量名,很多英语水平在高考即巅峰的同学会感到头疼,这里我推荐大家一个网站:CODELF(https://unbug.github.io/codelf),输入中文就可以看到别人是怎么命名的。

好,我们已经知道如何生成随机属性,下面我们就要将属性展示打印出来,请阅读下列代码,弄懂每一行的含义:

import time
import random
#也可合并写成一行:import time,random# 生成随机属性
player_life = random.randint(100,150) # “player_life” 代表玩家血量
player_attack = random.randint(30,50) # “player_attack” 代表玩家攻击
enemy_life = random.randint(100,150) # “enemy_life” 代表敌人血量
enemy_attack = random.randint(30,50) # “enemy_attack” 代表敌人攻击# 展示双方角色的属性
print('【玩家】\n'+'血量:'+str(player_life)+'\n攻击:'+str(player_attack))
#player_life和player_attack的数据类型都是整数,所以拼接时需要先用str()转换
print('------------------------')
time.sleep(1)
#暂停一秒再执行后续代码
print('【敌人】\n'+'血量:'+str(enemy_life)+'\n攻击:'+str(enemy_attack))
print('------------------------')

那截至目前,我们已经完成了随机生成属性和展示属性,接下来我们就来实现"自动战斗"。

因为现在双方的血量和攻击是随机生成,不是固定的。所以我们不知道具体要战斗多少回合才能分出胜负,也就是循环次数不明确,那自然要用while循环。

while (player_life >= 0) and (enemy_life >= 0):
#and两边的条件分别用括号括起,是一种习惯,方便阅读

现在我们确定了执行while循环的条件,接下来就是要填充循环内部的内容。

根据刚才的分析,我们希望循环的内容是双方互相攻击,掉血的过程。

print('你发起了攻击,【敌人】剩余血量xxx')
print('敌人向你发起了攻击,【玩家】剩余血量xxx')
print('------------------------')

其中【敌人】剩余血量=敌人当前血量-玩家攻击,【玩家】剩余血量=玩家当前血量-敌人攻击。

事实上我们之前已经定义好了这四个变量,每一次互相伤害后,player_life(玩家血量)和enemy_life(敌人血量)都会被重新赋值,所以转换为代码逻辑就是:

player_life = player_life - enemy_attack
enemy_life = enemy_life - player_attack
#赋值语句的执行顺序是先计算等号右边,再赋值给左边的变量

好,自动攻击的基础逻辑也已经理清楚了。我们先合并一下这之前写过的代码。

import time,random# 生成随机属性
player_life = random.randint(100,150)
player_attack = random.randint(30,50)
enemy_life = random.randint(100,150)
enemy_attack = random.randint(30,50) # 展示双方角色的属性
print('【玩家】\n'+'血量:'+str(player_life)+'\n攻击:'+str(player_attack))
#player_life和player_attack都是整数类型,所以拼接时需要先用str()转换
print('------------------------')
time.sleep(1)
print('【敌人】\n'+'血量:'+str(enemy_life)+'\n攻击:'+str(enemy_attack))
print('------------------------')
time.sleep(1)while (player_life >0) and (enemy_life > 0):player_life = player_life - enemy_attack enemy_life = enemy_life - player_attack 

补充完成while循环语句,让双方自动战斗、扣血的过程循环起来

import time,randomplayer_life = random.randint(100,150)
player_attack = random.randint(30,50)
enemy_life = random.randint(100,150)
enemy_attack = random.randint(30,50) print('【玩家】\n'+'血量:'+str(player_life)+'\n攻击:'+str(player_attack))
print('------------------------')
time.sleep(1)
print('【敌人】\n'+'血量:'+str(enemy_life)+'\n攻击:'+str(enemy_attack))
print('------------------------')
time.sleep(1)while (player_life >0) and (enemy_life > 0):player_life = player_life - enemy_attack enemy_life = enemy_life - player_attack print('你发起了攻击,【玩家】剩余血量'+str(player_life))#player_life是整数,所以拼接时要先用str()转换print('敌人向你发起了攻击,【敌人】剩余血量'+str(enemy_life))print('------------------------')time.sleep(1.5)# 为了体现出战斗回合,这里停顿1.5秒    

打印出每局结果,三局两胜,并打印最终战果的功能。这就是我们在版本3.0要增加的功能

版本3.0:打印战果,三局两胜

对比版本2.0,在版本3.0中,我们想要增加的功能是:

1.打印战果:每局战斗后,根据胜负平的结果打印出不同的提示;

2.三局两胜:双方战斗三局,胜率高的为最终赢家。

  • 游戏开始
  • #循环三局
  • 随机生成属性
  • 显示属性
  • PK过程展示
  • 输出单局结果
  • 输出三局两胜结果
  • 游戏结束

反复解释新增功能,是因为这样不断地明确项目的阶段性目标,可以让自己持续专注地推进项目。

import time,random# 生成双方角色,并生成随机属性。
player_life = random.randint(100,150)
player_attack = random.randint(30,50)
enemy_life = random.randint(100,150)
enemy_attack = random.randint(30,50)# 展示双方角色的属性
print('【玩家】\n'+'血量:'+str(player_life)+'\n攻击:'+str(player_attack))
print('------------------------')
time.sleep(1)
print('【敌人】\n'+'血量:'+str(enemy_life)+'\n攻击:'+str(enemy_attack))
print('------------------------')
time.sleep(1)# 双方PK
while player_life > 0 and enemy_life > 0:player_life = player_life - enemy_attackenemy_life = enemy_life - player_attackprint('你发起了攻击,【玩家】剩余血量'+str(player_life))print('敌人向你发起了攻击,【敌人】剩余血量'+str(enemy_life))print('-----------------------')time.sleep(1.5)# 打印战果
if player_life > 0 and enemy_life <= 0:print('敌人死翘翘了,你赢了')
elif player_life <= 0 and enemy_life > 0:print('悲催,敌人把你干掉了!')
else:print('哎呀,你和敌人同归于尽了!')

可以将其拆分成两个部分:先来个三局,再判断最终胜负。

首先我们来看,三局战斗也是一个可以循环的结构,且循环次数是固定的,所以要用到for循环。

在这里我们可以使用for i in range( )的结构,我们先来回顾一下之前学过的range()函数:

range(a,b,c)
a:计数从a开始。不填时,默认从0开始例如, range(1,5),表示从1开始生成
b:计数到b 结束,但不包括b例如, range(11),表示从0开始到11结束,不包括11
c:计数的间隔,不填时默认为1例如, range(0,30,5),表示计数的间隔5

尝试把代码打出来吧,让战斗循环三局。(先不用统计最后的结果)

给两个提示:1.想清楚哪些代码要嵌套到for循环里,即一局战斗里包括什么信息。确定了for写在哪里之后,一句战斗包含的所有信息都要缩进;

2.细节也需要留意,如局与局之间要怎么区分开来(时间间隔&打印局数信息)

之后计算胜场数

player_victory = 0
enemy_victory = 0if player_life > 0 and enemy_life <= 0:player_victory += 1print('敌人死翘翘了,你赢了!')
elif player_life <= 0 and enemy_life > 0:enemy_victory  += 1print('悲催,敌人把你干掉了!')
else:print('哎呀,你和敌人同归于尽了!')

最终代码

import time,randomplayer_victory = 0
enemy_victory = 0for i in range(1,4):time.sleep(2)  # 让局与局之间有较明显的有时间间隔print(' \n——————现在是第'+str(i)+'局——————')  # 作为局的标记player_life = random.randint(100,150)player_attack = random.randint(30,50)enemy_life = random.randint(100,150)enemy_attack = random.randint(30,50)# 展示双方角色的属性print('【玩家】\n'+'血量:'+str(player_life)+'\n攻击:'+str(player_attack))print('------------------------')time.sleep(1)print('【敌人】\n'+'血量:'+str(enemy_life)+'\n攻击:'+str(enemy_attack))print('------------------------')time.sleep(1)# 双方PKwhile player_life > 0 and enemy_life > 0:player_life = player_life - enemy_attackenemy_life = enemy_life - player_attackprint('你发起了攻击,【玩家】剩余血量'+str(player_life))print('敌人向你发起了攻击,【敌人】剩余血量'+str(enemy_life))print('-----------------------')time.sleep(1.5)#打印最终战果if player_life > 0 and enemy_life <= 0:player_victory += 1print('敌人死翘翘了,你赢了!')elif player_life <= 0 and enemy_life > 0:enemy_victory += 1print('悲催,敌人把你干掉了!')else:print('哎呀,你和敌人同归于尽了!')if player_victory > enemy_victory :time.sleep(1)print('【最终结果:你赢了!】')
elif enemy_victory > player_victory:print('【最终结果:你输了!】')
else: print('【最终结果:平局!】')

格式化字符串

print('【玩家】\n'+'血量:'+str(player_life)+'\n攻击:'+str(player_attack))
print('【敌人】\n'+'血量:'+str(enemy_life)+'\n攻击:'+str(enemy_attack))

我们在用+拼接字符串和变量的时候,常常需要考虑变量是什么类型的数据,如果不是字符串类型,还先需要str()函数转换。

并且一句话常常要拼接成好几个部分,然后我们要考虑每一对引号' '的起始位置,好麻烦,相信你多少会有点体会。

所以,为了更方便地实现不同数据类型的拼接,用【格式符%】是更常用更便利的一种方式

我们可以把%想象成:图书馆里用来占位的一本书。先占一个位置,之后再填上实际的变量。举个例子:下面这两种写法是相同的,请你着重研究下第二行的语法。

print('血量:'+str(player_life)+' 攻击:'+str(player_attack))
print('血量:%s 攻击:%s' % (player_life,player_attack))

我们看到格式符%后面有一个字母s,这是一个类型码,用来控制数据显示的类型。%s就表示先占一个字符串类型的位置。

常见的类型码

%d   整数

%f   浮点数

%s  字符串

修改过后的代码

import time
import randomplayer_victory = 0
enemy_victory = 0for i in range(1,4):time.sleep(1.5)print('  \n——————现在是第 %s 局——————' % i)#对比之前:(' \n——————现在是第'+str(i)+'局——————')player_life = random.randint(100,150)player_attack = random.randint(30,50)enemy_life = random.randint(100,150)enemy_attack = random.randint(30,50)print('【玩家】\n血量:%s\n攻击:%s' % (player_life,player_attack))print('------------------------')time.sleep(1)print('【敌人】\n血量:%s\n攻击:%s' % (enemy_life,enemy_attack))print('-----------------------')time.sleep(1)while player_life > 0 and enemy_life > 0:player_life = player_life - enemy_attack enemy_life = enemy_life - player_attackprint('你发起了攻击,【玩家】剩余血量%s' % player_life)print('敌人向你发起了攻击,【敌人】的血量剩余%s' % enemy_life)print('-----------------------')time.sleep(1.2)if player_life > 0 and enemy_life <= 0:player_victory += 1print('敌人死翘翘了,你赢了!')elif player_life <= 0 and enemy_life > 0:enemy_victory += 1print('悲催,敌人把你干掉了!')else:print('哎呀,你和敌人同归于尽了!')if player_victory > enemy_victory :time.sleep(1)print('\n【最终结果:你赢了!】')
elif enemy_victory > player_victory:print('\n【最终结果:你输了!】')
else: print('\n【最终结果:平局!】')

Python17_项目实操关卡-人机PK相关推荐

  1. 章节十五:项目实操:PK小游戏(3)

    章节十五:项目实操:PK小游戏(3) 目录 章节十五:项目实操:PK小游戏(3) 1. 明确项目目标 2. 分析过程,拆解项目 3. 代码实现,逐步执行 3.1 定义Book类 3.2 类BookMa ...

  2. python 爬虫(项目实操)

    用BeautifulSoup库解析数据和提取数据,解析数据的方法是: 提取数据的方法是用find() 与find_all() 最后,我们把事情串起来,就变成下图中的模样.它所对应的,正是爬虫四步中的前 ...

  3. 【Python自查手册】之项目实操

    项目实操步骤 分析:明确项目目标 拆解(将一个问题拆解为多个步骤或者多种不同的层次,逐步解决和执行并最终达到效果)–分析过程拆到无法拆解为止 解决:代码实现,逐步执行 三局两胜制小游戏 # 敌我两PK ...

  4. 爬虫项目实操二、爬取“下厨房”网站的菜名、所需材料、和菜名所对应的详情页URL

    项目-爬取"下厨房"网站的菜名.所需材料.和菜名所对应的详情页URL. 它有一个固定栏目,叫做"本周最受欢迎",收集了当周最招人喜欢的菜谱.地址如下: http ...

  5. SpringBoot集成Mybatis项目实操

    本文为<从零打造项目>系列第三篇文章,首发于个人网站. <从零打造项目>系列文章 比MyBatis Generator更强大的代码生成器 SpringBoot项目基础设施搭建 ...

  6. 新项目实操分析,用付费视频进行流量变现

    我们知道,互联网的发展,使得网民越来越多,不仅上网的用户在增多,上网的时间也逐年增加. 所以很多人都想做互联网的生意,因为互联网有足够的关注度和巨大的流量.而且,互联网项目属于网上创业,硬件门槛相对较 ...

  7. 本地房产公众号项目实操分享

    ​本地房产公众号项目实操分享 房地产公众号,可以每天更新最新楼盘资讯.楼市预判.土拍信息等,文章可以不用全部原创,那我们可以从哪些地方寻找素材呢? 1.安居客;2.本地土拍网;3.本地规划网;4.房天 ...

  8. 拳王虚拟项目公社:闲鱼知乎引流售卖虚拟资源的虚拟副业项目实操

    我们知道,虚拟资源的优势就在于成本低,一部手机都可以储存,就拿影视资源来说,每个月都会有一些全新的电影电视剧上映,所以每一部影视剧上映都是自带流量的,那么我们如何借助这一些影视资源来进行一个引流呢? ...

  9. 拳王虚拟项目公社:闲鱼知乎引流售卖虚拟资源的虚拟兼职副业项目实操

    我们知道,虚拟资源的优势就在于成本低,一部手机都可以储存,就拿影视资源来说,每个月都会有一些全新的电影电视剧上映,所以每一部影视剧上映都是自带流量的,那么我们如何借助这一些影视资源来进行一个引流呢? ...

最新文章

  1. idea中设置指向源代码Scala
  2. linux日志系统分析:rsyslog、syslog、klog
  3. python输出print到txt-pycharm实现print输出保存到txt文件
  4. python界面设计实例qt_pyqt的最小示例qtreeview和qt设计
  5. sql python 教程_Python SQLAlchemy ORM教程(3)
  6. java反射 反射构造函数 报 wrong number of arguments 错误
  7. 打不开malloc和free函数
  8. ssh命令行使用明文密码连接远程服务器并执行命令
  9. 可视化——统计图表介绍
  10. HTML+CSS制作课程表
  11. 职高计算机专业毕业200字自我鉴定,职高计算机专业的自我鉴定
  12. Ubuntu日常使用命令记录
  13. 【OBS】OBS Studio 的安装、参数设置和录屏、摄像头使用教程
  14. NUC系列迷你电脑改装苹果网卡的又一神器方案
  15. xmanager 5 linux 6.5,工欲善其事必先利其器 —— Xmanager Enterprise 5 和 RealVNC 5/6 介绍...
  16. 中心极限定理及其应用
  17. 东辉职校计算机专业录取分数线,2016年上海东辉职校录取分数
  18. 如何裁剪图片,裁剪图片的几个方法
  19. python 忽略异常_关于python:如何正确地忽略异常
  20. nrf52 iic使用

热门文章

  1. csgo笔记本党设置4:3画面
  2. 网易互娱2017实习生招聘在线笔试(一)
  3. 很遗憾,这就是现实!35岁之后软件测试工程师靠什么养家?
  4. 上海域格CLM920_NC3模块连接oneNET平台
  5. Object.freeze()
  6. javascript 多行文本 function处理方法 及实践应用
  7. 易语言流程控制程序暂停开始继续
  8. Docker Compose搭建TDengine集群
  9. 手机共享电脑的proxy网络
  10. 2022年5月语音合成(TTS)和语音识别(ASR)论文月报