-
Notifications
You must be signed in to change notification settings - Fork 107
Expand file tree
/
Copy pathblock_controller_sample.py
More file actions
236 lines (207 loc) · 8.91 KB
/
block_controller_sample.py
File metadata and controls
236 lines (207 loc) · 8.91 KB
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
#!/usr/bin/python3
# -*- coding: utf-8 -*-
from datetime import datetime
import pprint
import copy
class Block_Controller(object):
# init parameter
board_backboard = 0
board_data_width = 0
board_data_height = 0
ShapeNone_index = 0
CurrentShape_class = 0
NextShape_class = 0
# GetNextMove is main function.
# input
# nextMove : nextMove structure which is empty.
# GameStatus : block/field/judge/debug information.
# in detail see the internal GameStatus data.
# output
# nextMove : nextMove structure which includes next shape position and the other.
def GetNextMove(self, nextMove, GameStatus):
t1 = datetime.now()
# print GameStatus
print("=================================================>")
pprint.pprint(GameStatus, width = 61, compact = True)
# get data from GameStatus
# current shape info
CurrentShapeDirectionRange = GameStatus["block_info"]["currentShape"]["direction_range"]
self.CurrentShape_class = GameStatus["block_info"]["currentShape"]["class"]
# next shape info
NextShapeDirectionRange = GameStatus["block_info"]["nextShape"]["direction_range"]
self.NextShape_class = GameStatus["block_info"]["nextShape"]["class"]
# current board info
self.board_backboard = GameStatus["field_info"]["backboard"]
# default board definition
self.board_data_width = GameStatus["field_info"]["width"]
self.board_data_height = GameStatus["field_info"]["height"]
self.ShapeNone_index = GameStatus["debug_info"]["shape_info"]["shapeNone"]["index"]
# search best nextMove -->
strategy = None
LatestEvalValue = -100000
# search with current block Shape
for direction0 in CurrentShapeDirectionRange:
# search with x range
x0Min, x0Max = self.getSearchXRange(self.CurrentShape_class, direction0)
for x0 in range(x0Min, x0Max):
# get board data, as if dropdown block
board = self.getBoard(self.board_backboard, self.CurrentShape_class, direction0, x0)
# evaluate board
EvalValue = self.calcEvaluationValueSample(board)
# update best move
if EvalValue > LatestEvalValue:
strategy = (direction0, x0, 1, 1)
LatestEvalValue = EvalValue
###test
###for direction1 in NextShapeDirectionRange:
### x1Min, x1Max = self.getSearchXRange(self.NextShape_class, direction1)
### for x1 in range(x1Min, x1Max):
### board2 = self.getBoard(board, self.NextShape_class, direction1, x1)
### EvalValue = self.calcEvaluationValueSample(board2)
### if EvalValue > LatestEvalValue:
### strategy = (direction0, x0, 1, 1)
### LatestEvalValue = EvalValue
# search best nextMove <--
print("===", datetime.now() - t1)
nextMove["strategy"]["direction"] = strategy[0]
nextMove["strategy"]["x"] = strategy[1]
nextMove["strategy"]["y_operation"] = strategy[2]
nextMove["strategy"]["y_moveblocknum"] = strategy[3]
print(nextMove)
print("###### SAMPLE CODE ######")
return nextMove
def getSearchXRange(self, Shape_class, direction):
#
# get x range from shape direction.
#
minX, maxX, _, _ = Shape_class.getBoundingOffsets(direction) # get shape x offsets[minX,maxX] as relative value.
xMin = -1 * minX
xMax = self.board_data_width - maxX
return xMin, xMax
def getShapeCoordArray(self, Shape_class, direction, x, y):
#
# get coordinate array by given shape.
#
coordArray = Shape_class.getCoords(direction, x, y) # get array from shape direction, x, y.
return coordArray
def getBoard(self, board_backboard, Shape_class, direction, x):
#
# get new board.
#
# copy backboard data to make new board.
# if not, original backboard data will be updated later.
board = copy.deepcopy(board_backboard)
_board = self.dropDown(board, Shape_class, direction, x)
return _board
def dropDown(self, board, Shape_class, direction, x):
#
# internal function of getBoard.
# -- drop down the shape on the board.
#
dy = self.board_data_height - 1
coordArray = self.getShapeCoordArray(Shape_class, direction, x, 0)
# update dy
for _x, _y in coordArray:
_yy = 0
while _yy + _y < self.board_data_height and (_yy + _y < 0 or board[(_y + _yy) * self.board_data_width + _x] == self.ShapeNone_index):
_yy += 1
_yy -= 1
if _yy < dy:
dy = _yy
# get new board
_board = self.dropDownWithDy(board, Shape_class, direction, x, dy)
return _board
def dropDownWithDy(self, board, Shape_class, direction, x, dy):
#
# internal function of dropDown.
#
_board = board
coordArray = self.getShapeCoordArray(Shape_class, direction, x, 0)
for _x, _y in coordArray:
_board[(_y + dy) * self.board_data_width + _x] = Shape_class.shape
return _board
def calcEvaluationValueSample(self, board):
#
# sample function of evaluate board.
#
width = self.board_data_width
height = self.board_data_height
# evaluation paramters
## lines to be removed
fullLines = 0
## number of holes or blocks in the line.
nHoles, nIsolatedBlocks = 0, 0
## absolute differencial value of MaxY
absDy = 0
## how blocks are accumlated
BlockMaxY = [0] * width
holeCandidates = [0] * width
holeConfirm = [0] * width
### check board
# each y line
for y in range(height - 1, 0, -1):
hasHole = False
hasBlock = False
# each x line
for x in range(width):
## check if hole or block..
if board[y * self.board_data_width + x] == self.ShapeNone_index:
# hole
hasHole = True
holeCandidates[x] += 1 # just candidates in each column..
else:
# block
hasBlock = True
BlockMaxY[x] = height - y # update blockMaxY
if holeCandidates[x] > 0:
holeConfirm[x] += holeCandidates[x] # update number of holes in target column..
holeCandidates[x] = 0 # reset
if holeConfirm[x] > 0:
nIsolatedBlocks += 1 # update number of isolated blocks
if hasBlock == True and hasHole == False:
# filled with block
fullLines += 1
elif hasBlock == True and hasHole == True:
# do nothing
pass
elif hasBlock == False:
# no block line (and ofcourse no hole)
pass
# nHoles
for x in holeConfirm:
nHoles += abs(x)
### absolute differencial value of MaxY
BlockMaxDy = []
for i in range(len(BlockMaxY) - 1):
val = BlockMaxY[i] - BlockMaxY[i+1]
BlockMaxDy += [val]
for x in BlockMaxDy:
absDy += abs(x)
#### maxDy
#maxDy = max(BlockMaxY) - min(BlockMaxY)
#### maxHeight
#maxHeight = max(BlockMaxY) - fullLines
## statistical data
#### stdY
#if len(BlockMaxY) <= 0:
# stdY = 0
#else:
# stdY = math.sqrt(sum([y ** 2 for y in BlockMaxY]) / len(BlockMaxY) - (sum(BlockMaxY) / len(BlockMaxY)) ** 2)
#### stdDY
#if len(BlockMaxDy) <= 0:
# stdDY = 0
#else:
# stdDY = math.sqrt(sum([y ** 2 for y in BlockMaxDy]) / len(BlockMaxDy) - (sum(BlockMaxDy) / len(BlockMaxDy)) ** 2)
# calc Evaluation Value
score = 0
score = score + fullLines * 10.0 # try to delete line
score = score - nHoles * 1.0 # try not to make hole
score = score - nIsolatedBlocks * 1.0 # try not to make isolated block
score = score - absDy * 1.0 # try to put block smoothly
#score = score - maxDy * 0.3 # maxDy
#score = score - maxHeight * 5 # maxHeight
#score = score - stdY * 1.0 # statistical data
#score = score - stdDY * 0.01 # statistical data
# print(score, fullLines, nHoles, nIsolatedBlocks, maxHeight, stdY, stdDY, absDy, BlockMaxY)
return score
BLOCK_CONTROLLER_SAMPLE = Block_Controller()