tgmsbot/mscore.py

182 lines
6.4 KiB
Python
Raw Normal View History

2018-12-29 20:37:07 +08:00
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
import numpy as np
from random import randint
2018-12-30 15:24:45 +08:00
from copy import deepcopy
2018-12-29 20:37:07 +08:00
# 0 - 8: means 0-8 mines, not opened
# opened block = the value of not opened block + 10
# 9 is mine, not opened
# 19 is flagged mine
# 20 is stepped mine
# default:
# HEIGHT = 8
# WIDTH = 8
# MINES = 9
IS_MINE = 9
DEAD = 20
def check_params(height, width, mines):
2018-12-29 21:25:05 +08:00
if height <= 0 or width <= 0:
return (False, "地图太小!")
elif mines > height * width:
2018-12-29 20:37:07 +08:00
return (False, "放不下这么多雷嘛!")
elif mines == height * width:
return (False, "一点就爆炸,有意思吗?")
elif mines < 0:
return (False, "暂时还不会放负数颗雷呢。")
elif mines <= height * width:
return (True, "")
def get_row_col(width, index):
row = index // width
col = index - width * row
return (row, col)
def get_index(width, row_col):
(row, col) = row_col
index = width * row + col
return index
class Board():
def __init__(self, height, width, mines):
self.height = height
self.width = width
self.mines = mines
self.map = None
2018-12-30 15:24:45 +08:00
self.mmap = None
2018-12-29 20:37:07 +08:00
self.moves = list()
self.state = 0 # 0:not playing, 1:playing, 2:win, 3:dead
2019-01-15 00:57:48 +08:00
# statistics
self.__op = 0
self.__is = 0
self.__3bv = 0
2018-12-29 20:37:07 +08:00
def __gen_map(self, first_move):
height = self.height
width = self.width
mines = self.mines
if mines >= height * width:
return
elif mines < 0:
return
mines_1d = list(range(height * width))
# first_move should't be a mine
del mines_1d[get_index(width, first_move)]
mines_index = list()
self.map = np.zeros((height, width), dtype=np.int8)
for i in range(mines):
index = randint(1, len(mines_1d)) - 1
mines_index.append(mines_1d[index])
del mines_1d[index]
for mine_index in mines_index:
(row, col) = get_row_col(width, mine_index)
self.map[row][col] = IS_MINE
for row in range(height):
for col in range(width):
if self.map[row][col] != IS_MINE:
mine_count = 0
for nbr_value in self.__iter_neighbour(row, col, return_rc=False):
if nbr_value == IS_MINE:
mine_count += 1
self.map[row][col] = mine_count
2018-12-30 15:24:45 +08:00
self.mmap = deepcopy(self.map)
2018-12-29 20:37:07 +08:00
def __iter_neighbour(self, row, col, return_rc=True):
height = self.height
width = self.width
for i in [a - 1 for a in range(3)]:
for j in [b - 1 for b in range(3)]:
if (i != 0 or j != 0) and row + i >= 0 and row + i <= height - 1 and \
col + j >= 0 and col + j <= width - 1:
if return_rc:
yield (row + i, col + j)
else:
yield self.map[row + i][col + j]
def __do_i_win(self):
unopened = 0
mines_opened = 0
for x in np.nditer(self.map):
if x <= 8:
unopened += 1
elif x == 19:
mines_opened += 1
if mines_opened == self.mines:
return True
elif unopened == 0:
return True
else:
return False
def __open(self, row, col, automatic=False):
if self.state != 1:
return
if not automatic and self.map[row][col] == 9:
self.map[row][col] = DEAD
self.state = 3
elif self.map[row][col] == 0:
self.map[row][col] += 10 # open this block
# open other blocks
for nbr in self.__iter_neighbour(row, col):
self.__open(nbr[0], nbr[1], automatic=True)
elif self.map[row][col] >= 10:
# already opened
if automatic:
return
neighbour_mine_opened = 0
neighbour_unopened = 0
for neighbour in self.__iter_neighbour(row, col, return_rc=False):
if neighbour == 19:
neighbour_mine_opened += 1
if neighbour <= 9:
neighbour_unopened += 1
if (neighbour_mine_opened == self.map[row][col] - 10) or \
(neighbour_unopened == self.map[row][col] - 10 - neighbour_mine_opened):
for nbr in self.__iter_neighbour(row, col):
self.__open(nbr[0], nbr[1], automatic=True)
else:
self.map[row][col] += 10
2018-12-29 21:25:05 +08:00
if self.__do_i_win():
self.state = 2
2018-12-29 20:37:07 +08:00
def move(self, row_col):
if self.state == 0:
self.__gen_map(row_col)
self.state = 1
(row, col) = row_col
self.__open(row, col)
2018-12-30 17:07:00 +08:00
def gen_statistics(self):
2019-01-15 00:57:48 +08:00
if self.__op != 0:
return (self.__op, self.__is, self.__3bv)
2018-12-30 17:07:00 +08:00
self.__visited = np.zeros((self.height, self.width), dtype=np.int8)
def scan_open(row, col):
self.__visited[row][col] = 1
for nbr_rc in self.__iter_neighbour(row, col):
(nrow, ncol) = nbr_rc
if self.__visited[nrow][ncol] == 0:
nbr = self.mmap[nrow][ncol]
if nbr == 0:
scan_open(nrow, ncol)
elif nbr <= 8:
self.__visited[nrow][ncol] = 1
def scan_island(row, col):
self.__3bv += 1
self.__visited[row, col] = 1
for nbr_rc in self.__iter_neighbour(row, col):
(nrow, ncol) = nbr_rc
if self.__visited[nrow][ncol] == 0:
nbr = self.mmap[nrow][ncol]
if nbr >= 1 and nbr <= 8:
scan_island(nrow, ncol)
for row in range(self.height):
for col in range(self.width):
if self.__visited[row][col] == 0 and self.mmap[row][col] == 0:
self.__op += 1
self.__3bv += 1
scan_open(row, col)
for row in range(self.height):
for col in range(self.width):
cell = self.mmap[row][col]
if self.__visited[row][col] == 0 and cell >= 1 and cell <= 8:
self.__is += 1
scan_island(row, col)
return (self.__op, self.__is, self.__3bv)