本文针对会基本python语法的读者。
转载请注明,有问题可以留言一起探讨。
文章中代码运行的环境为Windows下 Python 2.7.14
1、实现的简单效果
注: [-]:未翻转; [1] 周边的地雷数量; [*]地雷,[ ]周边没有地雷
2、需求的简单分析
(1)先在windows的附件里面把扫雷游戏打开,来多玩几盘
(2)整理整个游戏需求的流程图:
注:初级9行*9列~10个雷,中级16*16~40,高级16*30~99,自定义行列尚未实现
3、关键代码分析
前置:
将每一格封装成一个类:CGameCell
用m_CellList记录整个画面中每个类的对象
用mineList记录布雷的位置(所在的行,列)
(1)布雷:
思路:如在9*9~10的级别里,需要随机布置10个雷,这10个随机数需要在 [0 ~ 81)之间产生,之后在对每个随机数进行 r, c =divmod(index, self.m_ColBoard) 取模、取余的操作,模代表该雷所在的行,余代表该雷所在的列,再用mineList对所有的雷的位置进行记录。这样做的好处是,每次的布雷都是精准的,时间复杂度低。
(2)统计每格周边雷的数量:
思路:在mineList记录了雷的位置,以每个雷为中心,向其周边的八个方向(左上,正上,右上,正左,正右,左下,正下,右下)进行扩展(注意判断越界),判断该格是否为雷,非雷的话对该格记录的数据值+1。该做法比按照每格进行遍历,效率高出数量级.....
(3)非递归实现用户翻转指定格的效果,截图代码不完整,详细代码见后一章节:
思路:
新开两个数组一个是待检测列表(waitChecklist),一个是已检测数组(m_CellList);检查某个位置,如x,y时,先把它Point(x,y) append进待检查列表。然后循环while(待检测列表的长度>0){
count = round(point.x,point.y)
把point从待检测列表放到已经侧列表
如果count等于0,把point周围的八个point放进待检测列表
}
注意:如果已检测数组里面已经有那个point了,就不要把它加到待检测列表里面,不然会死循环,可参考广搜的原理和二叉树的非递归遍历。也可以用递归实现,但是公司一般不让用递归...
4、整体代码(首次用简书,代码粘贴进来有点奇怪)
# -*- coding: gbk -*-
# Created by zhoullin on 2018-03-27
import random
LEVEL_ONE = 1
LEVEL_TWO = 2
LEVEL_THREE = 3
MINE_VALUE = -1
EMPTY_VALUE = 0
UNCOVER_STATUS = 1
COVER_STATUS = 0
INPUT_GAME_LEVEL_ERROR = '输入游戏编号错误'
USER_SELECT_TIPS = ("1、初级:10个雷,9*9平铺网格\n"
"2、中级:40个雷,16*16平铺网格\n"
"3、高级:99个雷,16*30平铺网格")
LEVEL_DIC = {
1: (9, 9, 10),
2: (16, 16, 40),
3: (16, 30, 99)
}
def CheckInputIsCorrect(levelStr):
if len(levelStr) == 1 and levelStr[0].isdigit():
levelInt = int(levelStr[0])
if levelInt in (LEVEL_ONE, LEVEL_TWO, LEVEL_THREE):
return True
print INPUT_GAME_LEVEL_ERROR
return False
def GetUserSelectedLevel():
'''
获取用户选择的游戏级别
'''
print USER_SELECT_TIPS
while True:
levelStr = raw_input("请输入你要选择的游戏级别编号:").split()
if CheckInputIsCorrect(levelStr):
return int(levelStr[0])
return -1
class CGameCore(object):
def __init__(self, levelInt):
self.m_RowBoard, self.m_ColBoard, self.m_MineNum = LEVEL_DIC[levelInt]
self.m_TotalCell = self.m_RowBoard * self.m_ColBoard
self.m_CellList = [[CGameCell(r, c) for c in range(self.m_ColBoard)] for r in range(self.m_RowBoard)]
self.m_UncoverCount = 0
# for test
# print "self.m_CellList:",self.m_CellList
mineList = self.SetMinePosition()
self.UpdateMineBoardData(mineList)
def SetMinePosition(self):
if self.m_MineNum >= self.m_TotalCell: # 对mineNumber的参数是否合理进行判断
print "布雷数量过多,不符合游戏规则"
return
randomList = random.sample(range(self.m_TotalCell), self.m_MineNum) # 在[0,row*col]之间产生一个长度为mineNum的列表
mineList = []
if self.m_ColBoard == 0:
return mineList
for index in randomList:
r, c = divmod(index, self.m_ColBoard)
self.m_CellList[r][c].SetValue(MINE_VALUE)
mineList.append([r, c])
return mineList
def UpdateMineBoardData(self, mineList):
for mine in mineList:
row, col = mine
for i in range(row - 1, row + 2):
for j in range(col - 1, col + 2):
if self.CheckRowAndColIsCorrect(i, j):
self.m_CellList[i][j].UpdateValue()
#for test
#print mineList
def ShowBoard(self):
for r in range(0, self.m_RowBoard + 1):
for c in range(0, self.m_ColBoard + 1):
if r == 0 or c == 0:
self.showBoardHeader(r, c)
else:
self.m_CellList[r-1][c-1].PrintSelf()
print " "
print '当前游戏雷数量:', self.m_MineNum
def showBoardHeader(self, row, col):
if row == 0:
if col == 0:
print " ",
else:
if (col - 1) < 10: # 为了显示对齐
print ' %d ' % (col - 1),
else:
print ' %d' % (col - 1),
elif col == 0:
if (row - 1) < 10: # 为了显示对齐
print '%d ' % (row - 1),
else:
print (row - 1),
def UncoverPosition(self, row, col):
# 对用户指定的翻转格进行判断
if self.m_CellList[row][col].GetStatus() == UNCOVER_STATUS:
return 0
if self.m_CellList[row][col].GetValue() == MINE_VALUE:
return MINE_VALUE
self.m_CellList[row][col].SetStatus(UNCOVER_STATUS)
self.m_UncoverCount += 1
waitChecklist = []
waitChecklist.append((row, col))
while len(waitChecklist) > 0:
# for test
print "waitChecklist:",waitChecklist
tempRow, tempCol = waitChecklist[0]
if self.m_CellList[tempRow][tempCol].GetValue() == EMPTY_VALUE:
# for test
aroundList = self.GetAroundPosition(tempRow, tempCol)
for around in aroundList:
i, j = around
if (self.m_CellList[i][j].GetValue() == EMPTY_VALUE and (i, j) not in waitChecklist and self.m_CellList[i][j].GetStatus() == COVER_STATUS):
waitChecklist.append((i, j))
elif self.m_CellList[i][j].GetStatus() == COVER_STATUS:
self.m_CellList[i][j].SetStatus(UNCOVER_STATUS)
self.m_UncoverCount += 1
del waitChecklist[0]
print "m_UncoverCount:", self.m_UncoverCount
return 0
def GetAroundPosition(self, row, col):
aroundList = []
for i in range(max(0, row - 1), min(row + 2, self.m_RowBoard)):
for j in range(max(0, col - 1), min(col + 2, self.m_ColBoard)):
aroundList.append((i, j))
return aroundList
def ShowAllMineInBoard(self):
'''
显示所有的雷区
'''
print "显示所有的雷区"
for r in range(0, self.m_RowBoard + 1):
for c in range(0, self.m_ColBoard + 1):
if r == 0 or c == 0:
self.showBoardHeader(r, c)
else:
if self.m_CellList[r - 1][c - 1].GetValue() == MINE_VALUE:
self.m_CellList[r-1][c-1].SetStatus(UNCOVER_STATUS)
self.m_CellList[r-1][c-1].PrintSelf()
print " "
def CheckUncoverParameter(self,rowAndColStr):
if len(rowAndColStr) == 2 and rowAndColStr[0].isdigit() and rowAndColStr[1].isdigit():
if self.CheckRowAndColIsCorrect(int(rowAndColStr[0]), int(rowAndColStr[1])):
return True
return False
def CheckRowAndColIsCorrect(self, row, col):
if (0 <= row < self.m_RowBoard) and (0 <= col < self.m_ColBoard):
return True
return False
def MainLoop(self):
'''
正式进入游戏阶段
'''
while True:
rowAndColStr = raw_input("请输入你要翻转格子所在的行与列(中间用空格分离):").split()
if self.CheckUncoverParameter(rowAndColStr) is False:
print "输入的行或列错误!!!"
continue
result = self.UncoverPosition(int(rowAndColStr[0]), int(rowAndColStr[1]))
if result == MINE_VALUE:
self.ShowAllMineInBoard()
print '踩雷啦!!!游戏结束····'
break
else:
self.ShowBoard()
if (self.m_TotalCell - self.m_UncoverCount) == self.m_MineNum:
print "恭喜!恭喜!游戏通过啦!"
break
def Run(self):
self.ShowBoard()
self.MainLoop()
class CGameCell(object):
s_UncoverCount = 0
def __init__(self, row, column, value=0, uncover=COVER_STATUS):
self.m_Row = row
self.m_Column = column
self.m_Value = value
self.m_Uncover = uncover
def SetValue(self, value):
self.m_Value = value
def UpdateValue(self):
if self.m_Value != MINE_VALUE:
self.m_Value += 1
def GetValue(self):
return self.m_Value
def SetStatus(self, uncover):
self.m_Uncover = uncover
def GetStatus(self):
return self.m_Uncover
def PrintSelf(self):
if self.m_Uncover == COVER_STATUS:
print '[-]',
else:
if self.m_Value == 0:
print '[ ]',
elif self.m_Value == MINE_VALUE:
print '[*]',
else:
print '[%d]' % (self.m_Value),
if __name__ == "__main__":
levelInt = GetUserSelectedLevel()
gameExample = CGameCore(levelInt)
gameExample.Run()