Проблема рекурсивной игры в питоне
Я создаю версию Minesweeper в Python и столкнулся с небольшой проблемой. В этом куске кода:
if winGame(mines):
printWorld(gameMap)
print 'You Win!'
answer = raw_input('Would you like to play again?')
if answer == 'y':
minesweeper()
else:
print 'Thanks for playing!'
break
Он снова вызывает функцию тральщика, которая снова начинает игру. Этот код находится внутри некоторого цикла True: вместе с остальным игровым кодом. Единственная проблема состоит в том, что если игра запускается снова, а затем выигрывает и говорит, что вы не хотите играть снова, это не нарушает цикл. Я думаю, что это как-то связано с тем, что я использую рекурсию для повторного вызова функции. Пока единственное, что работает, - это использование sys.exit(), но я бы предпочел иметь более законное решение, если это имеет смысл.
Вот полный код:
#Minesweeper Game
#difficulty levels
#make it look better text based
from random import *
import sys
gameMap = '''
#123456789#
1?????????1
2?????????2
3?????????3
4?????????4
#123456789#'''
row = 0
col = 0
coord = []
response = ''
numMines = 5
mines = []
answer = ''
#This runs the game
def minesweeper():
global gameMap
global row
global col
global coord
global mines
global response
global answer
#resets the gameboard after replaying.
gameMap = '''
#123456789#
1?????????1
2?????????2
3?????????3
4?????????4
#123456789#'''
#generates the mines
generateMines()
#converts the world into a list of lists. exception is for when playing again since
#it would try to convert it to a list of lists again
try:
initWorld()
except Exception, e:
pass
#main loop of the game.
while True:
#checks to see if you won the game
if winGame(mines):
printWorld(gameMap)
print 'You Win!'
answer = raw_input('Would you like to play again?')
if answer == 'y':
minesweeper()
else:
print 'Thanks for playing!'
break
#joins the list together so it can be printed
printWorld(gameMap)
print mines
#gets user input and converts it to an int, then adds coords to a list
getCoord()
#asks user what they want to do
clearOrFlag()
if response.lower() == 'c':
if isMine(mines):
answer = raw_input('Would you like to play again?')
if answer == 'y':
minesweeper()
else:
print 'Thanks for playing!'
break
else:
clearSpace(mines)
print '\n'
elif response.lower() == 'f':
flagSpace()
print '\n'
#randomly generates the mines and checks for duplicates
def generateMines():
global numMines
global mines
i = 0
mines = []
while i < numMines:
mines.append([randint(1,4),randint(1,9)])
i += 1
if checkDuplicateMines(mines):
generateMines()
#gets coordinates from the user
def getCoord():
global row
global col
global coord
global gameMap
row = 0
col = 0
coord = []
try:
row = raw_input('Enter an Row: ')
row = int(row)
col = raw_input('Enter a Column: ')
col = int(col)
except ValueError:
print 'Invalid Coordinates \n'
getCoord()
coord.append(row)
coord.append(col)
def isMine(mines):
global coord
global gameMap
for x in mines:
if coord == x:
showSolution(mines, gameMap)
return True
#asks user if they want to clear or flag a space
def clearOrFlag():
global response
response = raw_input("Clear (c), Flag/Unflag (f)")
#clears a space. if it's a mine, the player loses. If not it will write the
#number of surrounding mines to the space. Might break this up later.
def clearSpace(mines):
#checks to see if selected square is a ? (playable).
global gameMap
global row
global col
global coord
if gameMap[row][col] == '?' or gameMap[row][col] == 'F':
gameMap[row][col] = str(countMines(mines))
#flags a space, or unflags it if already flagged.
def flagSpace():
global gameMap
global row
global col
try:
if gameMap[row][col] == '?' :
gameMap[row][col] = 'F'
elif gameMap[row][col] == 'F':
gameMap[row][col] = '?'
except Exception, OutOfBounds:
print 'Invalid Coordinates \n'
#Prints the world
def printWorld(gameMap):
#just prints spaces to keep things tidy
print '\n' * 100
for row in gameMap:
print ' '.join(row)
print '\n'
print '\n'
#initializes the world so it can be printed
def initWorld():
global gameMap
# convert the gamemap into a list of lists
gameMap = gameMap.split('\n')
del gameMap[0]
for index in range(0, len(gameMap)):
gameMap[index] = list(gameMap[index])
#prints the gameBoard with all of the mines visible
def showSolution(mines, gameMap):
for x in mines:
gameMap[x[0]][x[1]] = 'M'
printWorld(gameMap)
print 'You Lose'
#counts the number of surrounding mines in a space
def countMines(mines):
global row
global col
count = 0
#theres probably a much better way to do this
for x in mines:
if [row+1,col] == x:
count += 1
if [row-1,col] == x:
count += 1
if [row,col+1] == x:
count += 1
if [row,col-1] == x:
count += 1
if [row+1,col+1] == x:
count += 1
if [row+1,col-1] == x:
count += 1
if [row-1,col+1] == x:
count += 1
if [row-1,col-1] == x:
count += 1
return count
#counts the number of flags on the board
def countFlags(mines):
global gameMap
numFlags = 0
for i in range (0, len(gameMap)):
for j in range (1, len(gameMap[0])-1):
if gameMap[i][j]=='F':
numFlags += 1
if numFlags == len(mines):
return True
else:
return False
#counts the number of mines flagged
def minesFlagged(mines):
global gameMap
count = 0
for x in mines:
if gameMap[x[0]][x[1]] == 'F':
count += 1
if count == numMines:
return True
else:
return False
#checks to see if there were duplicate mines generated
def checkDuplicateMines(mines):
mines.sort()
for x in range(0, len(mines)-1):
if mines[x] == mines[x+1]:
return True
x += 1
return False
#checks to see if player won the game
def winGame(mines):
if countFlags(mines):
if minesFlagged(mines):
return True
else:
return False
minesweeper()
2 ответа
Если вы хотите использовать рекурсию, верните вызов функции, а не просто вызывайте ее.
if winGame(mines):
printWorld(gameMap)
print 'You Win!'
answer = raw_input('Would you like to play again?')
if answer == 'y':
return minesweeper()
else:
print 'Thanks for playing!'
return
Таким образом, когда одна из ваших рекурсивных функций заканчивается, она возвращает None
на предыдущий, который снова возвращает None
к предыдущему и т. д. и т. д., пока не прозвенит последний return
который завершает весь цикл рекурсии.
Возможно, это не лучшее решение для этой проблемы (посмотрите на ответ MathieuW, в основном то же самое), но оно работает для любой ситуации и в основном используется в рекурсивных функциях.
Это нарушает цикл, если вы не отвечаете "у".
Но если вы играли в N игр, это только разорвет цикл N-й игры, и вы вернетесь в цикл вызова (N-1) -й функции.
Что касается фактического решения, я согласен с комментарием AshRj, который вы уже реализовали.
Вот еще одно решение, все еще использующее ваш предыдущий дизайн (но менее правильное), просто перемещающее разрыв.
if winGame(mines):
printWorld(gameMap)
print 'You Win!'
answer = raw_input('Would you like to play again?')
if answer == 'y':
minesweeper()
else:
print 'Thanks for playing!'
break