Python17_项目实操关卡-人机PK
項目實操關(guān)卡-人機PK
通過實戰(zhàn)去成長,這體現(xiàn)在:能在學習初期就知道什么是關(guān)鍵知識,了解專業(yè)程序員一般是怎么思考和解決問題的,在一遍遍分析問題、拆解問題及解決問題的過程中真正地理解知識,并用這些知識來解決自己的問題。
一個項目一般是怎么完成的。更具體的說,程序員是如何思考和解決問題的呢?
我認為其中一個很重要的能力是【問題拆解】。問題拆解,指的是在做一件事或面對一個問題的時候,將其拆解成多個步驟或多個層次,逐步執(zhí)行和解決問題,直至達到最終效果。
不過可能是出于長期編寫代碼的習慣,程序員會將問題拆得更細致一些,即拆到無法再拆為止
將完成一個項目的流程總結(jié)為以下三步
1.明確項目目標
2.分析過程,拆解項目
3.逐步執(zhí)行,代碼實現(xiàn)
明確項目目標
在互聯(lián)網(wǎng)公司,一般情況下是由產(chǎn)品經(jīng)理提出明確的項目需求,由程序員來實現(xiàn),他們之間是“相愛相殺”的關(guān)系。:)
今天且讓我扮演一下產(chǎn)品經(jīng)理的角色。我們此次要實現(xiàn)的需求是:人機PK小游戲。具體效果請參照下面的示意動圖。
簡單來說,這個游戲中,會隨機生成玩家和敵人的屬性,同時互相攻擊,直至一方血量小于零。
另外,這樣的戰(zhàn)斗會持續(xù)三局,采取三局兩勝制,最后輸出戰(zhàn)斗結(jié)果,公布獲勝方。
分析過程,拆解項目
“功能疊加、難度遞增”這個角度考慮,將我們要實現(xiàn)的小游戲拆分成了三個版本
各版本程序功能描述
- 版本1.0:規(guī)定雙方角色屬性,戰(zhàn)斗時人為計算扣血量,并打印出戰(zhàn)斗過程;
- 版本2.0︰隨機生成雙方角色屬性,自動計算扣血量,并優(yōu)化顯示戰(zhàn)斗過程的代碼;
- 版本3.0:雙方進行3局PK,每局判定勝負。三局兩勝,判斷最終結(jié)果.
?
當項目被清晰地拆解后,剩下的就是去逐步執(zhí)行,也就是重復(fù)“執(zhí)行→遇到問題→解決問題→繼續(xù)執(zhí)行”這個循環(huán)的過程。
逐步執(zhí)行,代碼實現(xiàn)
- 版本1.0:自定屬性,人工PK版本
- 版本2.0:隨機屬性,自動PK
- 版本3.0:打印戰(zhàn)果,三局兩勝
版本1.0:自定屬性,人工PK
我們要做的主要有三步:1.規(guī)定并顯示出玩家和敵人的屬性 2.雙方同時互相攻擊,血量根據(jù)對方的攻擊力扣除 3.若有一方血量小于等于0,游戲結(jié)束。
好,我們從第1步開始:設(shè)定【玩家】和【敵人】的屬性,即【血量】和【攻擊】。
print('【玩家】血量:100 攻擊:50') # 自定義玩家角色的血量和攻擊 print('【敵人】血量:100 攻擊:30') # 自定義敵人角色的血量和攻擊第2步:手動計算攻擊一次,雙方各自所剩的血量。
print('你發(fā)起了攻擊,【敵人】剩余血量50') # 人工計算敵人血量:100-50=50 print('敵人向你發(fā)起了攻擊,【玩家】剩余血量70') # 人工計算玩家血量:100-30=70第3步:繼續(xù)做人工計算:算一算,玩家攻擊2次敵人,敵人的血量就等于0了,這時候可以結(jié)束戰(zhàn)斗,打印游戲結(jié)果。
print('你發(fā)起了攻擊,【敵人】剩余血量0') # 雙方同時攻擊,若血量出現(xiàn)小于等于0,游戲結(jié)束 print('敵人向你發(fā)起了攻擊,【玩家】剩余血量40')print('敵人死翹翹了,你贏了!') # 打印結(jié)果很簡單吧!現(xiàn)在我們要做的,就是把這三段代碼拼起來,然后我會加一些修飾視覺的換行符和分割線,讓運行結(jié)果看得更清楚一點。
print('【玩家】\n血量:100\n攻擊:50') # 自定義玩家角色的血量和攻擊,用換行符'\n'來優(yōu)化視覺 print('------------------------') # 輔助功能,起到視覺分割的作用,讓代碼的運行結(jié)果更清晰print('【敵人】\n血量:100\n攻擊:30') print('------------------------')print('你發(fā)起了攻擊,【敵人】剩余血量50') # 人工計算敵人血量:100-50=50 print('敵人向你發(fā)起了攻擊,【玩家】剩余血量70') # 人工計算玩家血量:100-30=70 print('------------------------')print('你發(fā)起了攻擊,【敵人】剩余血量0') # 雙方同時攻擊,若血量出現(xiàn)小于等于0,游戲結(jié)束 print('敵人向你發(fā)起了攻擊,【玩家】剩余血量40') print('-----------------------')print('敵人死翹翹了,你贏了!') # 打印結(jié)果結(jié)果
【玩家】 血量:100 攻擊:50 ------------------------ 【敵人】 血量:100 攻擊:30 ------------------------ 你發(fā)起了攻擊,【敵人】剩余血量50 敵人向你發(fā)起了攻擊,【玩家】剩余血量70 ------------------------ 你發(fā)起了攻擊,【敵人】剩余血量0 敵人向你發(fā)起了攻擊,【玩家】剩余血量40 ----------------------- 敵人死翹翹了,你贏了!唔...雖然看起來還有點兒意思,但所有信息一下子都蹦跶出來,一點都沒有體現(xiàn)游戲的進程感。
所以,為了讓打印出的東西能有時間間隔地依次出現(xiàn),我們需要設(shè)置一個類似“計時器”的東西。在Python里,我們需要用到兩行代碼來實現(xiàn):(敲黑板,很簡單的新知識)
import time #調(diào)用time模塊 time.sleep(secs) #使用time模塊下面的sleep()函數(shù),括號里填的是間隔的秒數(shù)(seconds,簡稱secs) #time.sleep(1.5)就表示停留1.5秒再運行后續(xù)代碼這里有個新名詞——模塊,它是Python里一個重要的概念
你可以把模塊想象成是一個裝著許多神奇函數(shù)的百寶箱,不過想要使用這個百寶箱里的函數(shù),得先用 import 模塊名 這樣一句代碼來打開它。
然后這里我們想使用time模塊里的sleep()函數(shù),也就是讓代碼運行結(jié)果不要一次性全部出現(xiàn),而是分批分批的出現(xiàn)。就要寫成time.sleep(secs)的形式。
如果我想設(shè)置成打印的信息間隔1.5秒出現(xiàn),代碼就可以這么寫:
import time #通常import語句會寫到代碼的開頭print('【玩家】\n血量:100\n攻擊:50') print('------------------------') time.sleep(1.5) #暫停1.5秒,再繼續(xù)運行后面的代碼print('【敵人】\n血量:100\n攻擊:30') print('------------------------') time.sleep(1.5) #同上print('你發(fā)起了攻擊,【敵人】剩余血量50') print('敵人向你發(fā)起了攻擊,【玩家】剩余血量70') print('------------------------') time.sleep(1.5)print('你發(fā)起了攻擊,【敵人】剩余血量0') print('敵人向你發(fā)起了攻擊,【玩家】剩余血量40') print('-----------------------') time.sleep(1.5)print('敵人死翹翹了,你贏了!')這個版本的代碼還有兩個明顯的缺陷:一是玩家和敵人的屬性(血量&攻擊)是我自己說了算,那勝負早已沒有懸念;二是戰(zhàn)斗過程中血量的變化要自己手動算,那要計算機有何用?
你放心,這些都是我們會在版本2.0解決的問題。
版本2.0:隨機屬性,自動PK
如前所述,這個階段,我們主要新增【隨機屬性】和【自動戰(zhàn)斗】兩個功能,畫成流程圖是這樣子的:
版本2.0流程圖
- 隨機生成玩家和敵人的屬性
- 顯示玩家和敵人的屬性
- PK過程展示自動攻擊扣血
Python 隨機生成數(shù)字
import random #調(diào)用random模塊 a = random.randint(1,100) # 隨機生成1-100范圍內(nèi)(含1和100)的一個整數(shù),并賦值給變量a print(a)方法步驟
1.定義兩個變量,來存儲玩家血量和玩家攻擊力的數(shù)值
2.血量是100-150的隨機數(shù),攻擊力是30-50的隨機數(shù)
3.將兩個變量打印出來
import random player_life = random.randint(100,150) #表示玩家血量 player_attack = random.randint(30,50) #表示玩家攻擊 print(player_life) print(player_attack)標準的變量名最好是用英文來表達含義,如果是多個單詞組成,需要用英文下劃線_來隔開。
對于取英文變量名,很多英語水平在高考即巔峰的同學會感到頭疼,這里我推薦大家一個網(wǎng)站:CODELF(https://unbug.github.io/codelf),輸入中文就可以看到別人是怎么命名的。
好,我們已經(jīng)知道如何生成隨機屬性,下面我們就要將屬性展示打印出來,請閱讀下列代碼,弄懂每一行的含義:
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的數(shù)據(jù)類型都是整數(shù),所以拼接時需要先用str()轉(zhuǎn)換 print('------------------------') time.sleep(1) #暫停一秒再執(zhí)行后續(xù)代碼 print('【敵人】\n'+'血量:'+str(enemy_life)+'\n攻擊:'+str(enemy_attack)) print('------------------------')那截至目前,我們已經(jīng)完成了隨機生成屬性和展示屬性,接下來我們就來實現(xiàn)"自動戰(zhàn)斗"。
因為現(xiàn)在雙方的血量和攻擊是隨機生成,不是固定的。所以我們不知道具體要戰(zhàn)斗多少回合才能分出勝負,也就是循環(huán)次數(shù)不明確,那自然要用while循環(huán)。
while (player_life >= 0) and (enemy_life >= 0): #and兩邊的條件分別用括號括起,是一種習慣,方便閱讀現(xiàn)在我們確定了執(zhí)行while循環(huán)的條件,接下來就是要填充循環(huán)內(nèi)部的內(nèi)容。
根據(jù)剛才的分析,我們希望循環(huán)的內(nèi)容是雙方互相攻擊,掉血的過程。
print('你發(fā)起了攻擊,【敵人】剩余血量xxx') print('敵人向你發(fā)起了攻擊,【玩家】剩余血量xxx') print('------------------------')其中【敵人】剩余血量=敵人當前血量-玩家攻擊,【玩家】剩余血量=玩家當前血量-敵人攻擊。
事實上我們之前已經(jīng)定義好了這四個變量,每一次互相傷害后,player_life(玩家血量)和enemy_life(敵人血量)都會被重新賦值,所以轉(zhuǎn)換為代碼邏輯就是:
player_life = player_life - enemy_attack enemy_life = enemy_life - player_attack #賦值語句的執(zhí)行順序是先計算等號右邊,再賦值給左邊的變量好,自動攻擊的基礎(chǔ)邏輯也已經(jīng)理清楚了。我們先合并一下這之前寫過的代碼。
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都是整數(shù)類型,所以拼接時需要先用str()轉(zhuǎn)換 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循環(huán)語句,讓雙方自動戰(zhàn)斗、扣血的過程循環(huán)起來
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('你發(fā)起了攻擊,【玩家】剩余血量'+str(player_life))#player_life是整數(shù),所以拼接時要先用str()轉(zhuǎn)換print('敵人向你發(fā)起了攻擊,【敵人】剩余血量'+str(enemy_life))print('------------------------')time.sleep(1.5)# 為了體現(xiàn)出戰(zhàn)斗回合,這里停頓1.5秒打印出每局結(jié)果,三局兩勝,并打印最終戰(zhàn)果的功能。這就是我們在版本3.0要增加的功能
版本3.0:打印戰(zhàn)果,三局兩勝
對比版本2.0,在版本3.0中,我們想要增加的功能是:
1.打印戰(zhàn)果:每局戰(zhàn)斗后,根據(jù)勝負平的結(jié)果打印出不同的提示;
2.三局兩勝:雙方戰(zhàn)斗三局,勝率高的為最終贏家。
- 游戲開始
- ?
- #循環(huán)三局
- 隨機生成屬性
- 顯示屬性
- PK過程展示
- 輸出單局結(jié)果
- ?
- 輸出三局兩勝結(jié)果
- 游戲結(jié)束
?
反復(fù)解釋新增功能,是因為這樣不斷地明確項目的階段性目標,可以讓自己持續(xù)專注地推進項目。
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('你發(fā)起了攻擊,【玩家】剩余血量'+str(player_life))print('敵人向你發(fā)起了攻擊,【敵人】剩余血量'+str(enemy_life))print('-----------------------')time.sleep(1.5)# 打印戰(zhàn)果 if player_life > 0 and enemy_life <= 0:print('敵人死翹翹了,你贏了') elif player_life <= 0 and enemy_life > 0:print('悲催,敵人把你干掉了!') else:print('哎呀,你和敵人同歸于盡了!')可以將其拆分成兩個部分:先來個三局,再判斷最終勝負。
首先我們來看,三局戰(zhàn)斗也是一個可以循環(huán)的結(jié)構(gòu),且循環(huán)次數(shù)是固定的,所以要用到for循環(huán)。
在這里我們可以使用for i in range( )的結(jié)構(gòu),我們先來回顧一下之前學過的range()函數(shù):
range(a,b,c)
a:計數(shù)從a開始。不填時,默認從0開始例如, range(1,5),表示從1開始生成
b:計數(shù)到b 結(jié)束,但不包括b例如, range(11),表示從0開始到11結(jié)束,不包括11
c:計數(shù)的間隔,不填時默認為1例如, range(0,30,5),表示計數(shù)的間隔5
?
嘗試把代碼打出來吧,讓戰(zhàn)斗循環(huán)三局。(先不用統(tǒng)計最后的結(jié)果)
給兩個提示:1.想清楚哪些代碼要嵌套到for循環(huán)里,即一局戰(zhàn)斗里包括什么信息。確定了for寫在哪里之后,一句戰(zhàn)斗包含的所有信息都要縮進;
2.細節(jié)也需要留意,如局與局之間要怎么區(qū)分開來(時間間隔&打印局數(shù)信息)
之后計算勝場數(shù)
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——————現(xià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('你發(fā)起了攻擊,【玩家】剩余血量'+str(player_life))print('敵人向你發(fā)起了攻擊,【敵人】剩余血量'+str(enemy_life))print('-----------------------')time.sleep(1.5)#打印最終戰(zhàn)果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('【最終結(jié)果:你贏了!】') elif enemy_victory > player_victory:print('【最終結(jié)果:你輸了!】') else: print('【最終結(jié)果:平局!】')格式化字符串
print('【玩家】\n'+'血量:'+str(player_life)+'\n攻擊:'+str(player_attack)) print('【敵人】\n'+'血量:'+str(enemy_life)+'\n攻擊:'+str(enemy_attack))我們在用+拼接字符串和變量的時候,常常需要考慮變量是什么類型的數(shù)據(jù),如果不是字符串類型,還先需要str()函數(shù)轉(zhuǎn)換。
并且一句話常常要拼接成好幾個部分,然后我們要考慮每一對引號' '的起始位置,好麻煩,相信你多少會有點體會。
所以,為了更方便地實現(xiàn)不同數(shù)據(jù)類型的拼接,用【格式符%】是更常用更便利的一種方式
我們可以把%想象成:圖書館里用來占位的一本書。先占一個位置,之后再填上實際的變量。舉個例子:下面這兩種寫法是相同的,請你著重研究下第二行的語法。
print('血量:'+str(player_life)+' 攻擊:'+str(player_attack)) print('血量:%s 攻擊:%s' % (player_life,player_attack))我們看到格式符%后面有一個字母s,這是一個類型碼,用來控制數(shù)據(jù)顯示的類型。%s就表示先占一個字符串類型的位置。
常見的類型碼
%d?? 整數(shù)
%f?? 浮點數(shù)
%s? 字符串
修改過后的代碼
import time import randomplayer_victory = 0 enemy_victory = 0for i in range(1,4):time.sleep(1.5)print(' \n——————現(xiàn)在是第 %s 局——————' % i)#對比之前:(' \n——————現(xià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('你發(fā)起了攻擊,【玩家】剩余血量%s' % player_life)print('敵人向你發(fā)起了攻擊,【敵人】的血量剩余%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【最終結(jié)果:你贏了!】') elif enemy_victory > player_victory:print('\n【最終結(jié)果:你輸了!】') else: print('\n【最終結(jié)果:平局!】')總結(jié)
以上是生活随笔為你收集整理的Python17_项目实操关卡-人机PK的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【对象存储】关于阿里云OSS踩坑记录
- 下一篇: 如何让EXCEL公式结果不显示#N/A、