-
Notifications
You must be signed in to change notification settings - Fork 3
/
field.py
295 lines (247 loc) · 10.5 KB
/
field.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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
import pygame
import numpy as np
import copy
from config import BLOCK_SIZE
class Field:
""" テトリスの盤面全体を管理するクラス
盤面の個々のブロックを
・9 = 壁
・-1 = 空き
・0-6 = 埋まっているブロック(値はブロック色)
で表現する
"""
EMPTY_LINE = [9, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 9]
FLOOR_LINE = [9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9]
def __init__(self):
""" イニシャライザ
self.tile が盤面を表す2次元配列
高さは20マス
"""
self.tiles = [self.FLOOR_LINE]
for _ in range(20):
self.tiles.insert(0, copy.deepcopy(self.EMPTY_LINE))
def get_tile(self, x, y):
""" 指定のタイルのブロック状況を返す
:param x: x座標
:param y: y座標
:return: 与えられた座標にセットされている盤面の値
"""
return self.tiles[y][x]
def set_blocks(self, blocks):
""" ブロックを盤面に確定反映する
:param blocks:
:return:
"""
for block in blocks:
self.tiles[block.y][block.x] = block.c
def line_erase(self):
""" 埋まった行があればそれを消す関数
:return: 消去された行数
"""
erase = []
for y in range(len(self.tiles)-1): # 全ての行について捜査する
flag = True
for x in range(len(self.tiles[0])): # 横一列ブロックがすべて埋まっているか調べる
if self.tiles[y][x] == -1:
flag = False
if flag: # flag = True である場合には埋まっているので消去リストに追加
erase.append(y)
# 埋まったラインがあった場合
for i in erase:
self.tiles.pop(i) # そのラインを取り除く
self.tiles.insert(0, copy.deepcopy(self.EMPTY_LINE)) # 最上部に新たなラインを追加する
return len(erase)
def draw(self, screen, colors):
""" 画面に盤面を描画する
:param screen: pygame の Screen オブジェクト
:param colors: pygame の Surface (描画用の色ブロック)
:return:
"""
for y in range(len(self.tiles)):
for x in range(len(self.tiles[0])):
if self.tiles[y][x] == 9:
pygame.draw.rect(screen, (80, 80, 80), (x*BLOCK_SIZE, y*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE), 0)
elif self.tiles[y][x] == -1:
pygame.draw.rect(screen, (80, 80, 80), (x*BLOCK_SIZE, y*BLOCK_SIZE, BLOCK_SIZE, BLOCK_SIZE), 1)
else:
screen.blit(colors[self.tiles[y][x]], (BLOCK_SIZE*x, BLOCK_SIZE*y, BLOCK_SIZE, BLOCK_SIZE))
def get_bit_field(self, candidate_blocks=None):
""" 盤面の評価用の-1, 1 の2値の盤面値を返す関数
:param candidate_blocks: 現在の盤面に特定のテトリミノが次に配置されたと仮定した状況で応答する
:return: 盤面の2次元配列(埋まっている:1, 空いている:0). (壁は含めない)
"""
bit_field = [[0 if self.tiles[y][x] == -1 else 1 for x in range(1, len(self.tiles[0])-1)] for y in range(0, len(self.tiles)-1)]
if candidate_blocks:
for block in candidate_blocks:
bit_field[block.y][block.x-1] = 1
return bit_field
def get_field_score(self, candidate_blocks=None):
""" 盤面の評価値を返す
:param candidate_blocks: 現在の盤面に特定のテトリミノが次に配置されたと仮定した状況で評価する
:return: 9つの1次元配列 (Numpy array型)
https://ichi.pro/identeki-arugorizumu-de-tetorisu-gb-no-sekai-kiroku-o-yaburu-118795294920347
戻り配列詳細:
0: 高さ総合計
1: 穴数合計
2: 少なくとも1つ以上穴のある列数
3: 行遷移数
4: 列遷移数
5: 凸凹数
6: ブロックが1つも無い列数
7: 最大井戸高さ
8: 消去行数
"""
area = np.array(self.get_bit_field(candidate_blocks))
peaks = self.get_peaks(area)
highest_peak = np.max(peaks)
agg_height = np.sum(peaks)
holes = self.get_holes(peaks, area)
n_holes = np.sum(holes)
n_cols_with_holes = np.count_nonzero(np.array(holes) > 0)
row_transitions = self.get_row_transition(area, highest_peak)
col_transitions = self.get_col_transition(area, peaks)
bumpiness = self.get_bumpiness(peaks)
num_pits = np.count_nonzero(np.count_nonzero(area, axis=0) == 0)
wells = self.get_wells(peaks)
max_wells = np.max(wells)
cleard = area.min(axis=1).sum()
return np.array([agg_height, n_holes, n_cols_with_holes,
row_transitions, col_transitions, bumpiness, num_pits, max_wells, cleard])
@staticmethod
def get_peaks(area):
"""高さ計算
:param area: 盤面
:return: 各列での最大のブロックの高さ
"""
peaks = np.array([])
for col in range(area.shape[1]):
if 1 in area[:, col]:
p = area.shape[0] - np.argmax(area[:, col], axis=0)
peaks = np.append(peaks, p)
else:
peaks = np.append(peaks, 0)
return peaks
@staticmethod
def get_holes(peaks, area):
""" 穴数計算
:param peaks: get_peaksで得られた値(各列での最大の高さ)
:param area: 盤面
:return: 各列での穴(空白)の数
"""
holes = []
for col in range(area.shape[1]):
start = -peaks[col]
if start == 0:
holes.append(0)
else:
holes.append(np.count_nonzero(area[int(start):, col] == 0))
return holes
@staticmethod
def get_row_transition(area, highest_peak):
"""行遷移数
:param area: 盤面
:param highest_peak: 最大のブロック高さ
:return: 各行の占有タイルから非占有タイルへの遷移の合計数
"""
s = 0
for row in range(int(area.shape[0] - highest_peak), area.shape[0]):
for col in range(1, area.shape[1]):
if area[row, col] != area[row, col - 1]:
s += 1
return s
@staticmethod
def get_col_transition(area, peaks):
"""列遷移数
:param area: 盤面
:param peaks: get_peaksで得られた値(各列での最大の高さ)
:return: 各列の占有タイルから非占有タイルへの遷移の合計数
"""
s = 0
for col in range(area.shape[1]):
if peaks[col] <= 1:
continue
for row in range(int(area.shape[0] - peaks[col]), area.shape[0] - 1):
if area[row, col] != area[row + 1, col]:
s += 1
return s
@staticmethod
def get_bumpiness(peaks):
""" 凸凹値
:param peaks: get_peaksで得られた値(各列での最大の高さ)
:return: 隣り合う列間の絶対的な高さの差の合計
"""
s = 0
for i in range(9):
s += np.abs(peaks[i] - peaks[i + 1])
return s
@staticmethod
def get_wells(peaks):
""" 最も深い井戸
:param peaks: get_peaksで得られた値(各列での最大の高さ)
:return: 各列で最も深い井戸
"""
wells = []
for i in range(len(peaks)):
if i == 0:
w = peaks[1] - peaks[0]
w = w if w > 0 else 0
wells.append(w)
elif i == len(peaks) - 1:
w = peaks[-2] - peaks[-1]
w = w if w > 0 else 0
wells.append(w)
else:
w1 = peaks[i - 1] - peaks[i]
w2 = peaks[i + 1] - peaks[i]
w1 = w1 if w1 > 0 else 0
w2 = w2 if w2 > 0 else 0
w = w1 if w1 >= w2 else w2
wells.append(w)
return wells
if __name__ == "__main__":
import pprint
fields = Field()
fields.tiles[16] = [9, -1, -1, -1, -1, -1, -1, -1, 1, -1, -1, 9]
fields.tiles[17] = [9, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 9]
fields.tiles[18] = [9, -1, 1, 1, 1, -1, 1, -1, 1, -1, -1, 9]
fields.tiles[19] = [9, 1, 1, 1, 1, 1, 1, 1, 1, -1, -1, 9]
print(fields.get_field_score())
fields = Field()
fields.tiles[16] = [9, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 9]
fields.tiles[17] = [9, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 9]
fields.tiles[18] = [9, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 9]
fields.tiles[19] = [9, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 9]
print(fields.get_field_score())
fields = Field()
fields.tiles[16] = [9, -1, -1, -1, 1, -1, -1, -1, -1, -1, -1, 9]
fields.tiles[17] = [9, -1, -1, -1, 1, -1, -1, -1, 1, 1, 1, 9]
fields.tiles[18] = [9, -1, 1, 1, 1, 1, 1, 1, 1, -1, 1, 9]
fields.tiles[19] = [9, 1, 1, -1, 1, 1, 1, 1, -1, -1, 1, 9]
print(fields.get_field_score())
fields = Field()
fields.tiles[16] = [9, -1, -1, -1, -1, -1, -1, -1, -1, 1, 1, 9]
fields.tiles[17] = [9, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 9]
fields.tiles[18] = [9, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 9]
fields.tiles[19] = [9, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 9]
print(fields.get_field_score())
fields = Field()
fields.tiles[15] = [9, -1, -1, -1, -1, -1, -1, 1, -1, -1, -1, 9]
fields.tiles[16] = [9, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, 9]
fields.tiles[17] = [9, -1, 1, 1, -1, 1, -1, 1, -1, -1, 1, 9]
fields.tiles[18] = [9, -1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9]
fields.tiles[19] = [9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, -1, 9]
print(fields.get_field_score())
fields = Field()
fields.tiles[16] = [9, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 9]
fields.tiles[17] = [9, -1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 9]
fields.tiles[18] = [9, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 9]
fields.tiles[19] = [9, 1, 1, -1, 1, 1, 1, 1, -1, -1, -1, 9]
print(fields.get_field_score())
fields = Field()
fields.tiles[16] = [9, -1, -1, -1, -1, 1, 1, 1, -1, -1, -1, 9]
fields.tiles[17] = [9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9]
fields.tiles[18] = [9, 1, 1, 1, 1, 1, 1, 1, -1, -1, -1, 9]
fields.tiles[19] = [9, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 9]
print(fields.get_field_score())
fields = Field()
print(fields.get_field_score())