-
Notifications
You must be signed in to change notification settings - Fork 0
/
mine_sweeper.py
105 lines (78 loc) · 2.8 KB
/
mine_sweeper.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
from itertools import product, repeat
from random import shuffle
from typing import List
from ecsfp import Scene
SIZE = 9, 9
MINE_NUM = 9
class TagMeta(type):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self._instance = super().__call__()
def __call__(self):
return self._instance
class Reveal(metaclass=TagMeta): pass
class MineNum(int):
__slots__ = ()
GameMap = List[List[int]]
def get_around_index(x: int, y: int):
for ax, ay in product(range(max(0, x-1), min(x+2, SIZE[0])), range(max(0, y-1), min(y+2, SIZE[1]))):
if x == ax and y == ay:
continue
yield ax, ay
def new_game_map() -> GameMap:
game_map = [*repeat(-1, MINE_NUM), *repeat(0, SIZE[0]*SIZE[1] - MINE_NUM)]
shuffle(game_map)
game_map = [list(row) for row in zip(*repeat(iter(game_map), SIZE[1]))]
for x, y in product(range(SIZE[0]), range(SIZE[1])):
if game_map[x][y] == -1:
for ax, ay in get_around_index(x, y):
if game_map[ax][ay] != -1:
game_map[ax][ay] += 1
return game_map
def init_game(scene: Scene) -> GameMap:
game_map = new_game_map()
for x, y in product(range(SIZE[0]), range(SIZE[1])):
game_map[x][y] = scene.add_entity(MineNum(game_map[x][y]))
return game_map
def update_str(scene: Scene):
reveals = scene.match_entities(MineNum, Reveal)
hidens = scene.match_entities(MineNum) - reveals
for r_ent in reveals:
num = scene.get_component(r_ent, MineNum)
scene.add_component(r_ent, ' ' if num == 0 else str(num))
for h_ent in hidens:
scene.add_component(h_ent, '*')
def reveal(scene: Scene, game_map: GameMap, x: int, y: int):
to_reveal = {(x, y)}
while to_reveal:
x, y = to_reveal.pop()
ent = game_map[x][y]
if scene.has_component(ent, Reveal):
continue
if scene.get_component(ent, MineNum) == 0:
to_reveal.update(get_around_index(x, y))
scene.add_component(ent, Reveal())
def is_over(scene: Scene) -> bool:
reveals = scene.match_entities(MineNum, Reveal)
for r_ent in reveals:
if scene.get_component(r_ent, MineNum) == -1:
return True
if len(reveals) == SIZE[0] * SIZE[1] - MINE_NUM:
return True
return False
scene = Scene()
game_map = init_game(scene)
while True:
update_str(scene)
print('\n X', *range(SIZE[0]), sep=' ')
print('Y')
for row, line in enumerate(zip(*game_map)):
print(f'{row} ', *map(scene.get_component, line, repeat(str)))
if is_over(scene):
print("Game Over!")
break
x = int(input('X: '))
y = int(input('Y: '))
if x < 0 or x > SIZE[0] or y < 0 or y > SIZE[1]:
continue
reveal(scene, game_map, int(x), int(y))