最終版,增加了時間顯示、雷數顯示和結束測驗等內容,優化了細節。
import numpy as np
import random
import datetime
random.seed()
ISMINEFLAG=0X10
OPENEDFLAG=0X20
MARKEDFLAG=0X40
ISMMAKFLAG=0X50 #ISMINEFLAG and MARKEDFLAG
ISOPMAFLAG=0X70 #ISMINEFLAG and OPENEDFLAG and MARKEDFLAG
AROUNDFLAG=0X0F
def makemine(dim):
"""Make a mine ndarray,default size is 10*10,default mines is 50
Mine-array item value type is np.byte in 8 digits, details as follows:
7------0
XXXXXXXX
0-3: surround mines(0-8 is valid)
4: mine flag. 1 if mine, otherwise 0
5: opened flag. 1 means opened.
6: marked flag. 1 means marked
7: reserved
"""
if dim[2]>=dim[0]*dim[1]:
print("Provided mines are more than mine-array items!")
return None
try:
a=np.zeros((dim[0],dim[1]),dtype=np.byte)
except :
print("Mine-array construct failed!")
return None
for mm in random.sample(range(dim[0]*dim[1]),dim[2]): #mark all rand mines
x,y=divmod(mm,dim[1])
a[x,y]|=ISMINEFLAG
for x in range(dim[0]):
for y in range(dim[1]):
v=0
for i in range(x-1,x+2):
for j in range(y-1,y+2):
if 0<=i<dim[0] and 0<=j<dim[1] and not(all([i==x,j==y])):
v+=(a[i,j]&ISMINEFLAG)>>4
a[x,y]+=v
return a
def testbit(bit:np.byte,n:int):
if n>=8 or n<0:
return None
return bool(bit&(1<<n))
def action(arr,act): #x,y,action 1 is mine, 2 is marked,3 is double click,return marked number
x,y,a=act
if (a not in range(1,4)) or (x not in range(arr.shape[0])) or (y not in range(arr.shape[1])):
print("\nAct prarmetier({},{},{})is wrong! \n".format(x,y,a))
return 0
if a==2: #mark or unmark
if not testbit(arr[x,y],5):
arr[x,y]^=MARKEDFLAG
return 1 if bool(arr[x,y]&MARKEDFLAG) else -1
elif a==1:
if testbit(arr[x,y],5): # Has Opened
return 0
if testbit(arr[x,y],4):
print("\n({},{}) has a mine!".format(x,y))
else:
openmine(arr,x,y)
elif a==3:
if testbit(arr[x,y],5): #opened item can be doubl-clicked
for i in range(x-1,x+2):
for j in range(y-1,y+2):
if 0<=i<arr.shape[0] and 0<=j<arr.shape[1] and not(all([i==x,j==y])):
if not bool(arr[i,j]&ISOPMAFLAG):
arr[i,j]|=OPENEDFLAG
if (arr[i,j]&AROUNDFLAG)==0: #continue mine after doubleclick and found surround mines is 0
openmine(arr,i,j)
return 0
def openmine(arr,x,y):
if (arr[x,y]&AROUNDFLAG)==0:
for i in range(x-1,x+2):
for j in range(y-1,y+2):
if 0<=i<arr.shape[0] and 0<=j<arr.shape[1] :
if not testbit(arr[i,j],5): # has not opened
arr[i,j]|=OPENEDFLAG
openmine(arr,i,j)
else:
arr[x,y]|=OPENEDFLAG
return
def show(arr):
print("\n ",end="")
for i in range(0,arr.shape[1],2):
dd=3 if i<10 else 2
print(i,end=" "*dd)
print()
print("-"*(2*(arr.shape[1]+1)+1))
for x in range(arr.shape[0]):
print(str(x).rjust(2),end="|")
for y in range(arr.shape[1]):
if testbit(arr[x,y],5): #has opened
print(arr[x,y]&AROUNDFLAG,end=" ")
else:
if testbit(arr[x,y],6): #has marked
print(r"?",end=" ")
else:
print(r"*",end=" ")
print()
return
def testover(arr):
for z in arr.flat:
if bool(z&ISMINEFLAG):
if (z&ISMMAKFLAG)!=ISMMAKFLAG:
return False
else:
if not bool(z&OPENEDFLAG):
return False
else:
return True
if __name__=="__main__":
try:
xs,ys,mines=map(int,input(r"Input xs,ys,mines.SPACE seperates nums. Ctrl+C exit):").split())
if xs<0 or ys<0 or mines>xs*ys:
raise ValueError
except ValueError:
print("\nInput xs,ys,mines error! Use default(10,10,50)\n")
xs,ys,mines=(10,10,50)
a=makemine((xs,ys,mines))
show(a); marks=0; remains=-1
t1=datetime.datetime.now()
while True:
try:
x,y,act=map(int,input(r"Input x,y,act. act=1 or 2 or 3,means mine,mark,doubleclick.SPACE seperates three nums. Ctrl+C exit.->").split())
marks+=action(a,(x,y,act)); remains=mines-marks
t=datetime.datetime.now()-t1
mm,ss=divmod(t.seconds,60); hh,mm=divmod(mm,60)
print("\nArray sizes:{}*{},Total: {}mines,Marked: {}mines,Remained: {}mines, Used time: {}:{}:{}".format(xs,ys,mines,marks,remains,hh,mm,ss),end="")
show(a)
print()
except ValueError:
print("\nInput x,y,act error!\n")
if remains==0:
if testover(a):
print("Congratulation.Game is over!\n")
break
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/21482.html
