Python+Cursesで、minesweeperを書いてみました。
ubuntuのターミナルで動きます。
ファイルに、chmod +x minesweeper.py
として、実行権を付けて動かして下さい。
$./minesweeper.py [number of mines]
として、動かします。
[number of mines]は、省略できます。省略すると、機雷の数は56個となります。
スペースキーで、開き、’z’キーで、マークを付けます。カーソルは矢印キーで動かします。
’q’キーで抜け出せられます。
pyinstallerで、コンパイルできます。コンパイルしなくとも、そもそも速度はあんまり問題ないですが。
ターミナルのプロファイルはデフォルトにしておいて下さい。
minesweeper.py
#!/usr/bin/python3
import curses
import random
import locale
import select
import sys
xsize = 40
ysize = 23
vvram = [[0 for i in range(ysize)] for j in range(xsize)]
mask = [["#" for i in range(ysize)] for j in range(xsize)]
itod = " 123456789"
ac = "#@?"
def getkey():
c = stdscr.getkey()
if c == 'KEY_UP':
c = '8'
elif c == 'KEY_DOWN':
c = '2'
elif c == 'KEY_RIGHT':
c = '6'
elif c == 'KEY_LEFT':
c = '4'
return c
def putchar(x, y, a):
stdscr.addstr(y, x*2, a, curses.A_REVERSE)
def putcharr(x, y, a):
stdscr.addstr(y, x*2, a)
def putmines(n):
for i in range(n):
while(True):
(x, y) = (random.randint(0, xsize-1), random.randint(0, ysize-1))
if (vvram[x][y] == 0):
vvram[x][y] = '*'
break
#
def calccell(x, y):
if (vvram[x][y] == 0):
cnt = 0
for dx in range(-1, 2):
for dy in range(-1, 2):
if x+dx >= 0 and y+dy >= 0 and x+dx < xsize and y+dy < ysize:
cnt += 1 if vvram[x+dx][y+dy] == '*' else 0
vvram[x][y] = cnt
return(cnt)
def reveal(x, y):
if mask[x][y] != ' ':
mask[x][y] = ' '
if vvram[x][y] == 0:
for dx in range(-1, 2):
for dy in range(-1, 2):
if x+dx >= 0 and y+dy >= 0 and x+dx < xsize and y+dy < ysize:
reveal(x+dx, y+dy)
def cursor(x, y):
putcharr(x, y, retchar(x, y))
def printmines():
for x in range(xsize):
for y in range(ysize):
c = vvram[x][y]
if c == '*':
putchar(x, y, "*")
elif (c >= 0 and c <= 9):
putchar(x, y, itod[c])
def retchar(x, y):
m = mask[x][y]
if m in ac:
return(m)
else:
c = vvram[x][y]
if c >= 0 and c <= 9:
return(itod[c])
def drawchar(x, y):
putchar(x, y, retchar(x, y))
def drawscreen():
for x in range(xsize):
for y in range(ysize):
drawchar(x, y)
def calcfield():
for x in range(xsize):
for y in range(ysize):
calccell(x, y)
def endcheck():
c = 0
for x in range(xsize):
for y in range(ysize):
c += 1 if mask[x][y] in ac else 0
return(c == mines)
def mainloop():
(x, y) = (xsize//2, ysize//2)
while True:
drawscreen()
if endcheck():
putchar(0, 23, "You win. Game Over. hit key")
getkey()
return(0)
cursor(x, y)
stdscr.refresh()
c = getkey()
if c == '4':
x = x-(1 if x != 0 else 0)
elif c == '6':
x = x+(1 if x != xsize-1 else 0)
elif c == '8':
y = y-(1 if y != 0 else 0)
elif c == '2':
y = y+(1 if y != ysize-1 else 0)
elif (c == 'z' or c=='m') and mask[x][y] != ' ':
mask[x][y] = ac[(ac.index(mask[x][y])+1) % 3]
elif c == ' ':
if vvram[x][y] == '*':
printmines()
putchar(x, y, "X")
putchar(0, 23, "You lose. Game Over. hit key")
getkey()
return(-1)
reveal(x, y)
elif c == 'q':
return(1)
def game():
putmines(mines)
calcfield()
mainloop()
return
def main():
try:
game()
except (Exception, KeyboardInterrupt) as e:
curses.endwin()
raise e
else:
curses.endwin()
argvs = sys.argv
argc = len(argvs)
if argc == 2:
mines = int(argvs[1])
if mines > 300:
print("too many mines")
exit(1)
else:
mines = 56
locale.setlocale(locale.LC_ALL, '')
stdscr = curses.initscr()
stdscr.keypad(True)
curses.curs_set(0)
main()
exit(0)