項(xiàng)目介紹
我們這個(gè)項(xiàng)目是一個(gè)基于Python實(shí)現(xiàn)的推箱子小游戲,名叫Sokoban:
這個(gè)游戲的目的是讓玩家,也就是大寫的P
,推著箱子#
,填充用小寫的o
標(biāo)記的地面上的洞
項(xiàng)目規(guī)則
該版本的Sokoban的規(guī)則如下:
- 游戲在矩形的二維網(wǎng)格上舉行,其
原點(diǎn)(0,0)
位于左上方 - 網(wǎng)格上的每個(gè)單元格可以隨時(shí)包含以下內(nèi)容之一:
- 由大寫字母
P
表示的玩家 - 由空格字符
' '
表示的地磚 - 由哈希字符
#
表示的箱子 - 由星號(hào)字符
*
表示的墻 - 由小寫字符
o
表示的洞
- 由大寫字母
- 每個(gè)回合,玩家角色可以在網(wǎng)格上向上、向下、向左或向右移動(dòng)一個(gè)單位
- 玩家角色不能移動(dòng)到墻或洞中
- 每一回合,玩家角色都可以將箱子向他們?cè)噲D移動(dòng)的方向推一個(gè)單位,前提是玩家試圖移動(dòng)方向的箱子的下一個(gè)單元格是地磚或者是洞。如果不滿足此條件,玩家和箱子都不會(huì)移動(dòng)
- 如果玩家將箱子推入到洞中,洞和箱子都會(huì)消失,并留下一塊板磚。
- 如果玩家試圖離開屏幕邊緣或?qū)⑾渥油齐x屏幕邊緣,如果其他規(guī)則允許,玩家或箱子應(yīng)該出現(xiàn)在屏幕的對(duì)側(cè),就像屏幕的兩側(cè)是連接的一樣
項(xiàng)目接口文檔
在這個(gè)項(xiàng)目中你需要去實(shí)現(xiàn)含有以下方法的Sokoban類;
-
__init__(self,board)
:使用給定的board創(chuàng)建Sokoban實(shí)例,參數(shù)board是一個(gè)二維嵌套列表,也就是我們的游戲地圖 -
find_player(self)
:返回玩家角色在棋盤上的位置。行和列從0開始,原點(diǎn)(0,0)位于網(wǎng)格的左上角,例如在我們項(xiàng)目介紹中玩家的位置為 (0,0) -
is_complete(self)
:判斷游戲是否結(jié)束。如果地圖中沒有洞,則返回真,代表游戲結(jié)束,否則返回假 -
steps(self)
:返回玩家角色的移動(dòng)次數(shù)(也就是玩家的位置發(fā)生變化的時(shí)候) -
restart(self)
:將Sokoban實(shí)例進(jìn)行重置,重置為玩家開始游戲之前的狀態(tài) -
undo(self)
:撤銷玩家的上一次移動(dòng),使游戲狀態(tài)恢復(fù)到上一次移動(dòng)的時(shí)候,可以重復(fù)調(diào)用以撤銷多次移動(dòng)。如果撤銷被調(diào)用的次數(shù)超過玩家的移動(dòng)次數(shù),則棋盤應(yīng)保持其初始狀態(tài) -
move(self,direction)
:試圖將玩家移動(dòng)一個(gè)位置并且推動(dòng)玩家面前的箱子。方向參數(shù)是一個(gè)字符串,其值為w,a,s,d,分別表示向上,向左,向下和向右。只有當(dāng)玩家的位置發(fā)生更新的時(shí)候,才會(huì)計(jì)算移動(dòng)次數(shù)。如果玩家的位置沒有發(fā)生改變,則游戲的狀態(tài)不應(yīng)以任何方式改變 -
__str__(self)
:返回地圖的字符串表示形式。記住每行中的單元格要用空格分隔
項(xiàng)目實(shí)現(xiàn)過程
前置方法編寫
我們就按文檔中的接口一個(gè)個(gè)實(shí)現(xiàn)。先來看init方法,這個(gè)方法里面我們現(xiàn)在能想到的只有兩件事:
- 地圖初始化
- step游戲步數(shù)的初始化
def __init__(self,board):
self.board = board
self.step = 0
然后我們來實(shí)現(xiàn)str方法,在這里使用到的就是二維列表的遍歷,我們只需要要建立一個(gè)空字符串然后把列表中的元素往里面塞就行了:
def __str__(self):
show = ''
num = 0
for i in self.board:
num += 1
for j in i:
show += j + ' '
show += '\n'
return show[:-2]
我們每遍歷完一行就使用\n
來控制換行。我們最后對(duì)返回的字符串進(jìn)行了切片是因?yàn)椋何覀內(nèi)绻磺衅脑挘覀冊(cè)诖蛴∽詈笠恍械臅r(shí)候末尾也會(huì)有一個(gè)換行符,這個(gè)是沒有必要的!我們可以把它切去:
同樣使用了二維列表的遍歷的還有is_complete(self)方法,這個(gè)的思路也就是單純的遍歷地圖看是否還有洞口存在就行,實(shí)現(xiàn)較簡(jiǎn)單;
def is_complete(self):
for i in self.board:
for j in i :
if j == 'o':
return False
return True
接下來我們來實(shí)現(xiàn)find_player(self)方法,其核心思想仍然是二維列表的遍歷,這里我提供兩種實(shí)現(xiàn)方法:
實(shí)現(xiàn)方法1:
def find_player(self):
x = -1;y = -1
for j in self.board:
x += 1
for k in j:
y += 1
if k == 'P':
return (x,y)
y = -1
實(shí)現(xiàn)方法2:
def find_player(self):
for x in range(self.board_x()):
for y in range(self.board_x()):
if self.board[x][y] == 'P':
return (x,y)
move核心方法編寫
我們?cè)谒伎糾ove方法的編寫的時(shí)候,乍一想會(huì)發(fā)現(xiàn)有很多需要注意點(diǎn),有非常非常多的限制、判斷,可能想著想著一下子就迷失了方向。其實(shí)我們可以思考一下,我們每執(zhí)行一次move指令其實(shí)就進(jìn)行了兩個(gè)步驟:
- 判斷能否移動(dòng)
- 如果可以移動(dòng),則變動(dòng)玩家(可能還有箱子)的位置
也就是說我們現(xiàn)在將一個(gè)指令進(jìn)行了分解,讓他稍微具體了一些。換句話說我們想要實(shí)現(xiàn)move這個(gè)方法,只需要我們完成這兩個(gè)功能就可以了,這里我想了一下如果我們將這兩個(gè)功能都堆疊在move方法中,會(huì)顯得代碼非常的亂,而且涉及if的層層嵌套,會(huì)讓思維容易混亂。所以這里我們可以把判斷能否移動(dòng)這個(gè)功能抽象出來,令作為一個(gè)方法,我們把它命名為check
。將實(shí)際的移動(dòng)功能留在move方法中。
因?yàn)樯婕暗降乃膫€(gè)方向,其實(shí)我們知道一個(gè)方向怎么實(shí)現(xiàn)之后,其他的方向?qū)崿F(xiàn)也就是照葫蘆畫瓢,所以這里我只對(duì)check以及move方法中的w方向進(jìn)行講解,然后為了方便我們可以將地圖的長(zhǎng)度和寬度的獲取抽象成一個(gè)方法,方便后面的使用:
def board_x(self):
return len(self.board)
def board_y(self):
return len(self.board[0])
接下來我們開工!
check()
首先我們先拿到玩家的具體位置,直接調(diào)用find_player方法即可,其位置可以通過如下坐標(biāo)系來理解:
在w方向上來說,他的移動(dòng)有兩種情況:
- 情況一:w方向上與它相鄰的位置有箱子(也就是說玩家要推著箱子走)
- 情況二:只有玩家自己移動(dòng)
而情況一下面又有兩種情況:
-
在w方向上,箱子的前面一個(gè)是墻
- 這種情況下就不能移動(dòng)
- 這種情況下就不能移動(dòng)
-
箱子前面沒有墻
- 這種情況下可以移動(dòng)
- 這種情況下可以移動(dòng)
這里有人會(huì)說應(yīng)該還有一種情況箱子前面是洞口。這說明還沒有完全弄清我們單獨(dú)抽象出這個(gè)check方法的目的,我們的check方法只做一件事,那就是箱子或者箱子和人能不能移動(dòng)。而箱子前面是洞口這種情況屬于可以移動(dòng)的情況,不需要單拿出來討論。至于箱子與洞口的相消與地磚的填充不是我們check方法的功能,我們應(yīng)該把他們放到move方法中去處理。
考慮完這些情況之后我們還不能開始寫代碼,因?yàn)橛幸稽c(diǎn)我們不能忽略,那就是項(xiàng)目規(guī)則中的最后一條:
- 如果玩家試圖離開屏幕邊緣或?qū)⑾渥油齐x屏幕邊緣,如果其他規(guī)則允許,玩家或箱子應(yīng)該出現(xiàn)在屏幕的對(duì)側(cè),就像屏幕的兩側(cè)是連接的一樣
這個(gè)地方如果再去加加減減的,然后弄出一大堆情況非常麻煩且容易出現(xiàn)角標(biāo)越界等問題。我們其實(shí)可以想象一下,當(dāng)我們的玩家一直在w方向上前進(jìn)(假設(shè)整列沒有墻不會(huì)阻礙前進(jìn)),到達(dá)頂點(diǎn)之后,又從當(dāng)前列的下方出現(xiàn),這種情景有點(diǎn)類似于循環(huán)列表。我們可以借用取余的思想
,這樣就不需要進(jìn)行繁雜的討論,也可以避免角標(biāo)越界等錯(cuò)誤。
接下來我們來寫代碼:
def check(self,direction):
# 此時(shí)玩家的位置
x = self.find_player()[0]
y = self.find_player()[1]
if direction not in "wasd":
return -1
#每個(gè)方向上的處理
# 先考慮在你的移動(dòng)方向上沒有箱子的情況
# 再考慮在你的移動(dòng)方向上有箱子的情況
# 返回正整數(shù)代表可以移動(dòng) 返回1說明有箱子 返回0說明沒箱子 返回負(fù)數(shù)代表不能移動(dòng)
if direction == 'w':
# 代表方向上沒有箱子
if self.board[(x-1)%self.board_x()][y] != '#':
if self.board[(x-1)%self.board_x()][y] not in '*o':
return 0
else:
return -1
# 代表方向上有箱子
else:
if self.board[(x-2)%self.board_x()][y] not in '*':
return 1
else:
return -1
這里我們的返回值;
- 正整數(shù)代表可以移動(dòng)
- 0代表不需要推箱子,只有玩家移動(dòng)
- 1代表需要推箱子,玩家和箱子均需要移動(dòng)
- 負(fù)數(shù)代表不能移動(dòng)
其他方向同理
move
同樣還是先拿到玩家的坐標(biāo),然后調(diào)用check方法,如果check返回的是一個(gè)負(fù)數(shù)那么直接return不用處理。我們重點(diǎn)來討論如果返回的是正整數(shù)的時(shí)候的情況:
以w方向?yàn)槔M(jìn)行討論
- 如果check返回的是0(也就是說只有玩家移動(dòng)):
- 我們只需要將當(dāng)前玩家所處的方格以地磚替代,將前一個(gè)方格使用P替代
- 如果check返回的是非零整數(shù)(也就是說玩家和箱子都要移動(dòng)),這里再分為兩種情況:
- 箱子前面是洞
- 箱子和洞口相消,玩家前移
- 箱子前面是地磚
- 箱子和玩家均前移一個(gè)單位
- 箱子前面是洞
代碼如下:
def move(self,direction):
# 此時(shí)玩家的位置
x = self.find_player()[0]
y = self.find_player()[1]
ans = self.check(direction)
if direction == 'w':
if ans < 0:
return
else:
self.board[x][y] = ' '
if ans == 0:
self.board[(x-1)%self.board_x()][y] = 'P'
else:
if self.board[(x-2)%self.board_x()][y] == 'o':
self.board[(x-2)%self.board_x()][y] = ' '
self.board[(x-1)%self.board_x()][y] = 'P'
else:
self.board[(x-2)%self.board_x()][y] = '#'
self.board[(x-1)%self.board_x()][y] = 'P'
項(xiàng)目收尾
截至目前我們還有下面三個(gè)方法沒有實(shí)現(xiàn):
steps(self)
restart(self)
undo(self)
steps方法記錄玩家的移動(dòng)步數(shù),而在我們當(dāng)前項(xiàng)目中,所有的移動(dòng)操作都與move的方法有關(guān),我們可以直接在move方法中對(duì)step進(jìn)行計(jì)數(shù):
def steps(self):
return self.step
注意只有當(dāng)我們的check方法返回非負(fù)數(shù)的時(shí)候我們才會(huì)去計(jì)數(shù)。
接下來我們看看restart(self)和undo(self)方法,這兩個(gè)方法一個(gè)用來重新開始,一個(gè)用來回退。他們都有一個(gè)特點(diǎn)那就是狀態(tài)的回溯。那么我們就可以把他們用同一種思想處理,因?yàn)樗麄兊膮^(qū)別無非就是一個(gè)回溯到開頭狀態(tài),一個(gè)回溯到上一步的狀態(tài)。
具體的做法就是:只要玩家的位置(狀態(tài)) 發(fā)生了變化,我們就將變化前的地圖狀態(tài)進(jìn)行儲(chǔ)存。在代碼層面上來講就是將變化前的board存儲(chǔ)到一個(gè)專門的列表中,這里我們就把這個(gè)列表命名為history:
這里要非常注意,像下面這樣存入列表是不行:
self.history.append(self.board)
你最后會(huì)發(fā)現(xiàn)存入history中的所有元素都一樣并且與當(dāng)前的board是一致的。這是因?yàn)榱斜碓赑ython中是可變數(shù)據(jù)類型,即使發(fā)生變化其地址值不會(huì)發(fā)生改變。使用上面的方法我們存入history的一個(gè)個(gè)元素都有著同樣的地址值,也就是說它們是同一個(gè)對(duì)象。所以這里為了避免這種問題,我們應(yīng)該使用深拷貝,而普通的深拷貝對(duì)我們的多維嵌套列表是沒有用的。
這里我嘗試了網(wǎng)上最常見的幾種辦法都沒有用:
- 列表的copy方法
- list()方法
[:]
切片方法
我們可以使用Python內(nèi)置模塊copy中的deepcopy方法,代碼如下:
import copy
···
self.history.append(copy.deepcopy(self.board))
接下來我們只用在對(duì)應(yīng)的方法中從history里取出不同的狀態(tài)即可:
def restart(self):
self.board = self.history[0]
self.step = 0
self.history.clear();
def undo(self):
self.board = self.history[-1]
self.step -= 1
self.history.pop()
注意:
- 我們r(jià)estart之后要將history列表清空
- 在我們回退時(shí),除了返回history最后的元素,還要把它從列表中刪除,否則在二次或者多次回退時(shí)會(huì)出錯(cuò)
項(xiàng)目完善
我們?cè)诰帉懘a的時(shí)候其實(shí)還忽略了幾個(gè)點(diǎn):
- 如果撤銷被調(diào)用的次數(shù)超過玩家的移動(dòng)次數(shù),則棋盤應(yīng)保持其初始狀態(tài)
也就是說我們不能一直回退,按照我們的代碼,一直回退下去會(huì)出現(xiàn)以下兩種情況:
- history的列表長(zhǎng)度問題,會(huì)衍生出角標(biāo)出錯(cuò)
- 我們的step會(huì)變?yōu)樨?fù)數(shù)
改進(jìn):
def undo(self):
if self.step == 0:
return
self.board = self.history[-1]
self.step -= 1
self.history.pop()
- 如果我們一開始就restart,或者說連續(xù)多次restart也會(huì)報(bào)錯(cuò)
其本質(zhì)也是因?yàn)閔istory列表為空導(dǎo)致的角標(biāo)出錯(cuò)
改進(jìn):
def restart(self):
if len(self.history) == 0:
return
self.board = self.history[0]
self.step = 0
self.history.clear();
項(xiàng)目整體源碼
'''
推箱子
P代表玩家 o代表洞 #代表箱子 空字符代表地磚
項(xiàng)目要求:
1)二維網(wǎng)格的元原點(diǎn)位于左上方
2)每回合只能上下左右移動(dòng)一格
3)玩家不能移動(dòng)到墻或者洞中
4)只有當(dāng)玩家推動(dòng)箱子移動(dòng)的下一個(gè)單位是地磚或著洞的時(shí)候才能移動(dòng)成功 否則箱子不會(huì)移動(dòng)
5)箱子進(jìn)入洞中之后 洞和箱子都會(huì)消失 使用地磚進(jìn)行替代
6)如果離開屏幕邊緣 在規(guī)則允許的情況下(也就是第4條) 允許出現(xiàn)在對(duì)側(cè)
'''
import copy
class Sokoban:
def __init__(self,board):
self.board = board
self.step = 0
self.history = []
def __str__(self):
show = ''
num = 0
for i in self.board:
num += 1
for j in i:
show += j + ' '
show += '\n'
return show[:-2]
def find_player(self):
for x in range(self.board_x()):
for y in range(self.board_x()):
if self.board[x][y] == 'P':
return (x,y)
def is_complete(self):
for i in self.board:
for j in i :
if j == 'o':
return False
return True
def steps(self):
return self.step
def restart(self):
if len(self.history) == 0:
return
self.board = self.history[0]
self.step = 0
self.history.clear();
def undo(self):
if self.step == 0:
return
self.board = self.history[-1]
self.step -= 1
self.history.pop()
def move(self,direction):
# 此時(shí)玩家的位置
x = self.find_player()[0]
y = self.find_player()[1]
ans = self.check(direction)
if direction == 'w':
if ans < 0:
return
else:
self.step += 1
self.history.append(copy.deepcopy(self.board))
self.board[x][y] = ' '
if ans == 0:
self.board[(x-1)%self.board_x()][y] = 'P'
else:
if self.board[(x-2)%self.board_x()][y] == 'o':
self.board[(x-2)%self.board_x()][y] = ' '
self.board[(x-1)%self.board_x()][y] = 'P'
else:
self.board[(x-2)%self.board_x()][y] = '#'
self.board[(x-1)%self.board_x()][y] = 'P'
elif direction == 'a':
if ans < 0:
return
else:
self.step += 1
self.history.append(copy.deepcopy(self.board))
self.board[x][y] = ' '
if ans == 0:
self.board[x][(y - 1)%self.board_y()] = 'P'
else:
if self.board[x][(y - 2)%self.board_y()] == 'o':
self.board[x][(y - 2)%self.board_y()] = ' '
self.board[x][(y - 1)%self.board_y()] = 'P'
else:
self.board[x][(y - 2)%self.board_y()] = '#'
self.board[x][(y - 1)%self.board_y()] = 'P'
elif direction == 's':
if ans < 0:
return
else:
self.step += 1
self.history.append(copy.deepcopy(self.board))
self.board[x][y] = ' '
if ans == 0:
self.board[(x + 1)%self.board_x()][y] = 'P'
else:
if self.board[(x + 2)%self.board_x()][y] == 'o':
self.board[(x + 2)%self.board_x()][y] = ' '
self.board[(x + 1)%self.board_x()][y] = 'P'
else:
self.board[(x + 2)%self.board_x()][y] = '#'
self.board[(x + 1)%self.board_x()][y] = 'P'
elif direction == 'd':
if ans < 0:
return
else:
self.step += 1
self.history.append(copy.deepcopy(self.board))
self.board[x][y] = ' '
if ans == 0:
self.board[x][(y + 1)%self.board_y()] = 'P'
else:
if self.board[x][(y + 2)%self.board_y()] == 'o':
self.board[x][(y + 2)%self.board_y()] = ' '
self.board[x][(y + 1)%self.board_y()] = 'P'
else:
self.board[x][(y + 2)%self.board_y()] = '#'
self.board[x][(y + 1)%self.board_y()] = 'P'
def check(self,direction):
# 此時(shí)玩家的位置
x = self.find_player()[0]
y = self.find_player()[1]
if direction not in "wasd":
return -1
#每個(gè)方向上的處理
# 先考慮在你的移動(dòng)方向上沒有箱子的情況
# 再考慮在你的移動(dòng)方向上有箱子的情況
# 返回正整數(shù)代表可以移動(dòng) 返回1說明有箱子 返回0說明沒箱子 返回負(fù)數(shù)代表不能移動(dòng)
if direction == 'w':
# 代表方向上沒有箱子
if self.board[(x-1)%self.board_x()][y] != '#':
if self.board[(x-1)%self.board_x()][y] not in '*o':
return 0
else:
return -1
# 代表方向上有箱子
else:
if self.board[(x-2)%self.board_x()][y] not in '*':
return 1
else:
return -1
elif direction == 'a':
# 代表方向上沒有箱子
if self.board[x][(y - 1)%self.board_y()] != '#':
if self.board[x][(y - 1)%self.board_y()] not in '*o':
return 0
else:
return -1
# 代表方向上有箱子
else:
if self.board[x][(y - 2)%self.board_y()] not in '*':
return 1
else:
return -1
elif direction == 's':
# 代表方向上沒有箱子
if self.board[(x + 1)%self.board_x()][y] != '#':
if self.board[(x + 1)%self.board_x()][y] not in '*o':
return 0
else:
return -1
# 代表方向上有箱子
else:
if self.board[(x + 2)%self.board_x()][y] not in '*':
return 1
else:
return -1
elif direction == 'd':
# 代表方向上沒有箱子
if self.board[x][(y + 1)%self.board_y()] != '#':
if self.board[x][(y + 1)%self.board_y()] not in '*o':
return 0
else:
return -1
# 代表方向上有箱子
else:
if self.board[x][(y + 2)%self.board_y()] not in '*':
return 1
else:
return -1
def board_x(self):
return len(self.board)
def board_y(self):
return len(self.board[0])
# 豎著是x軸 橫著是y軸
board = [
['*', '*', ' ', '*', '*'],
['*', 'o', ' ', ' ', '*'],
['#', ' ', 'P', '#', 'o'],
['*', ' ', ' ', ' ', '*'],
['*', '*', ' ', '*', '*'],
]
game = Sokoban(board)
move = str()
print(game)
print(game.steps(), ':', game.is_complete())
while not game.is_complete():
move = input('move:')
if move == 'u':
game.undo()
elif move == 'r':
game.restart()
else:
game.move(move)
print(game)
print(game.steps(), ':', game.is_complete())
運(yùn)行效果;
直接cv在IDE中就可以玩,地圖可以自己自定義,記得箱子和洞口數(shù)量要一樣多否則永遠(yuǎn)過不了關(guān)。
項(xiàng)目缺陷分析
- 項(xiàng)目中使用的數(shù)據(jù)結(jié)構(gòu)較為單一,列表一用用到底。其實(shí)很多地方都可以用棧、隊(duì)列、鏈表等數(shù)據(jù)結(jié)構(gòu)進(jìn)行相關(guān)的優(yōu)化
- 有些地方的代碼些許冗雜,可以進(jìn)行語法上的優(yōu)化
- 因?yàn)榻涌谖臋n的束縛,其實(shí)有很多方法可以更加細(xì)化。例如move方法或者說check方法代碼邏輯還是有點(diǎn)多。有一些邏輯兩個(gè)方法可以共用,我們可以抽象出來新建一個(gè)方法。
- 既然在wasd四個(gè)方向上邏輯相似,那么我們是否可以考慮二次抽象,而不是將四種情況均放在check和move方法中。
項(xiàng)目收獲與反思
這個(gè)推箱子的小游戲是校內(nèi)老師布置的一次小作業(yè)。因?yàn)樽约浩綍r(shí)都是使用一些前端還有java后端方面的東西,python長(zhǎng)時(shí)間不用忘得差不多了。我寫的時(shí)候面向?qū)ο蟮姆矫娴恼Z法以及一些列表相關(guān)方法都是邊查文檔邊寫的。這就導(dǎo)致代碼方面不是非常的成熟健壯。
當(dāng)然收獲也非常的多,一個(gè)是復(fù)習(xí)了python,然后就是取余的思想。平時(shí)可能刷算法題的時(shí)候可能會(huì)遇見,開發(fā)的時(shí)候基本沒怎么用過,這個(gè)項(xiàng)目讓我見識(shí)到了取余在實(shí)際開發(fā)中發(fā)揮的作用:在優(yōu)化了代碼的同時(shí)還能減少出錯(cuò)。一開始沒想到取余的時(shí)候,一個(gè)個(gè)情況的分類討論簡(jiǎn)直讓人抓狂。
還有另外非常重要的一點(diǎn),就是寫代碼之前打草稿的必要性文章來源:http://www.zghlxwxcb.cn/news/detail-779296.html
其實(shí)在平時(shí)我們進(jìn)行不管是前端開發(fā)、后端開發(fā),更多的思考的是:用什么、怎么用,那種很嚴(yán)格的邏輯思考其實(shí)并不是很頻繁。這就導(dǎo)致一臺(tái)電腦一個(gè)文檔基本就可以解決問題。而涉及到算法或者說嚴(yán)格的情況分類與考慮這就需要我們打草稿整理思路再去寫代碼。直接一股腦地去寫代碼,或者說不打草稿非常的影響效率以及質(zhì)量。文章來源地址http://www.zghlxwxcb.cn/news/detail-779296.html
到了這里,關(guān)于手把手教你使用Python實(shí)現(xiàn)推箱子小游戲(附完整源碼)的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!