にゃんこスイーパー


Python+Cursesで、にゃんこスイーパーを書いてみました。
Ubuntuのターミナルで動きます。
ファイルに、chmod +x nyankosweeper.pyとして、実行権を付けて動かして下さい。

$./nyankosweeper.py [number of cats]として、動かします。
[number of cats]は、省略できます。省略すると、猫の数は56個となります。

スペースキーで、開き、’ｚ’キーで、マークを付けます。カーソルは矢印キーで動かします。
’ｑ’キーで抜け出せられます。

pyinstallerで、コンパイルできます。コンパイルしなくとも、そもそも速度はあんまり問題ないですが。

```nyankosweeper.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 = "　１２３４５６７８９"
ac = "＃😺？"

def getkey():
    c = stdscr.getkey()
    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():
            printmines()
            putchar(0, 23, "You win. hit key")
            getkey()
            return(0)
        cursor(x, y)
        stdscr.refresh()
        c = getkey()
        if c in ('4','h',"KEY_LEFT"):
            x = x-(1 if x != 0 else 0)
        elif c in ( '6',"l","KEY_RIGHT"):
            x = x+(1 if x != xsize-1 else 0)
        elif c in ('8',"k","KEY_UP"):
            y = y-(1 if y != 0 else 0)
        elif c in ('2',"j","KEY_DOWN"):
            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, "Ｘ")
                putchar(0, 23, "You lose. 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 KeyboardInterrupt:
        pass
    else:
        return

def getv():
    argvs = sys.argv
    argc = len(argvs)
    m = 56

    if argc == 2:
        try:
            m = int(sys.argv[1])
            if m > 300:
                print('too many nyankos')
                exit(0)
        except ValueError:
            print('not a number')
            exit(0)
    return(m)

mines = getv()
locale.setlocale(locale.LC_ALL, '')
stdscr = curses.initscr()
stdscr.keypad(True)
curses.curs_set(0)
main()
curses.endwin()
del stdscr
```