1 /***************************************************************************
2 * KBlocks, a falling blocks game by KDE *
3 * Copyright (C) 2010 University Freiburg <squall.leonhart.cai@gmail.com> *
4 * *
5 * This program is free software; you can redistribute it and/or modify *
6 * it under the terms of the GNU General Public License as published by *
7 * the Free Software Foundation; either version 2 of the License, or *
8 * (at your option) any later version. *
9 ***************************************************************************/
10 #include "KBlocksAIFeature.h"
11
12 #include <vector>
13
14 //#define ground_line 0
15 static int ground_line = 0;
16 static std::vector<int> board_signature = std::vector<int> (0);
17
18 /************************************************************************************
19 **** Util Function ***************************************************************
20 *************************************************************************************/
Abs(int a)21 int Abs(int a)
22 {
23 if (a < 0) {
24 return -a;
25 }
26 return a;
27 }
28
Abs(double a)29 double Abs(double a)
30 {
31 if (a < 0) {
32 return -a;
33 }
34 return a;
35 }
36
Min(int a,int b)37 int Min(int a, int b)
38 {
39 if (a > b) {
40 return b;
41 }
42 return a;
43 }
44
Max(int a,int b)45 int Max(int a, int b)
46 {
47 if (a < b) {
48 return b;
49 }
50 return a;
51 }
52
Min(double a,double b)53 double Min(double a, double b)
54 {
55 if (a > b) {
56 return b;
57 }
58 return a;
59 }
60
Max(double a,double b)61 double Max(double a, double b)
62 {
63 if (a < b) {
64 return b;
65 }
66 return a;
67 }
68 //----------------------------------------------------------------
69 // returns the number of row -last non-empty cell- plus one
column_max_height(int column,KBlocksField * field)70 int column_max_height(int column, KBlocksField *field)
71 {
72 int width = field->getWidth();
73 int height = field->getHeight();
74
75 if ((column < 0) || (column >= width)) {
76 //printf("WARNING: invalid column index %d",column);
77 }
78
79 for (int j = 0; j < height; j++) {
80 if (field->getCell(column, j)) {
81 return j;
82 }
83 }
84 return height;
85 }
86 //----------------------------------------------------------------
set_ground_line(int null_line)87 void set_ground_line(int null_line)
88 {
89 ground_line = null_line;
90 }
91 //----------------------------------------------------------------
update_board_signature(KBlocksField * field)92 void update_board_signature(KBlocksField *field)
93 {
94 int width = field->getWidth();
95 if (board_signature.empty()) {
96 board_signature = std::vector<int> (width);
97 }
98 for (int i = 0; i < width; i++) {
99 board_signature[i] = column_max_height(i, field);
100 }
101 }
102 /******************************************************************************
103 ******** Feature List ****************************************************
104 *******************************************************************************/
getFeature(const FeatureEnumeration id,KBlocksField * field)105 double getFeature(const FeatureEnumeration id, KBlocksField *field)
106 {
107 EvaluationInterface *e = nullptr;
108 switch (id) {
109 case FEATURE_BLOCKS_COUNT:
110 e = Evaluation_Blocks_Count::instance(); break;
111 case FEATURE_HOLES_COUNT:
112 e = Evaluation_Holes_Count::instance(); break;
113 case FEATURE_MAX_HEIGHT:
114 e = Evaluation_Max_Height::instance(); break;
115 case FEATURE_AVERAGE_HEIGHT:
116 e = Evaluation_Average_Height::instance(); break;
117 case FEATURE_AVERAGE_HEIGHT_DIFFERENT:
118 e = Evaluation_Average_Height_Difference::instance(); break;
119 case FEATURE_MAX_HEIGHT_DIFFERENT:
120 e = Evaluation_Max_Height_Difference::instance(); break;
121 case FEATURE_BLOCKS_OVER_HOLES_COUNT:
122 e = Evaluation_Blocks_Over_Holes_Count::instance(); break;
123 case FEATURE_KONTUR_COUNT:
124 e = Evaluation_Kontur_Count::instance(); break;
125 case FEATURE_MAX_KONTUR_LENGTH:
126 e = Evaluation_Max_Kontur_Length::instance(); break;
127 case FEATURE_NARROW_COUNT:
128 e = Evaluation_Narrow_Count::instance(); break;
129 case FEATURE_PREDICTION_COUNT:
130 e = Evaluation_Prediction_Count::instance(); break;
131 case FEATURE_CLOSED_HOLES_COUNT:
132 e = Evaluation_Closed_Holes_Count::instance(); break;
133 case FEATURE_WELLS_COUNT:
134 e = Evaluation_Wells_Count::instance(); break;
135 case FEATURE_WEIGHTED_BLOCKS_COUNT:
136 e = Evaluation_Weighted_Blocks_Count::instance(); break;
137 case FEATURE_ROW_TRANSITION_COUNT:
138 e = Evaluation_Row_Transition_Count::instance(); break;
139 case FEATURE_COLUMN_TRANSITION_COUNT:
140 e = Evaluation_Column_Transition_Count::instance(); break;
141 case FEATURE_MAX_WELL_DEPTH:
142 e = Evaluation_Max_Well_Depth::instance(); break;
143 default: ;
144 // printf("WARNING: Evaluation not found!");
145 }
146 if (e == nullptr) {
147 return 0;
148 }
149 return e->evaluate(field);
150 }
151
getFeatureName(const FeatureEnumeration id)152 const char *getFeatureName(const FeatureEnumeration id)
153 {
154 switch (id) {
155 case FEATURE_MAX_HEIGHT:
156 return "MAX_HEIGHT";
157 case FEATURE_HOLES_COUNT:
158 return "HOLES_COUNT";
159 case FEATURE_AVERAGE_HEIGHT:
160 return "AVERAGE_HEIGHT";
161 case FEATURE_AVERAGE_HEIGHT_DIFFERENT:
162 return "AVERAGE_HEIGHT_DIFFERENCE";
163 case FEATURE_MAX_HEIGHT_DIFFERENT:
164 return "MAX_HEIGHT_DIFFERENCE";
165 case FEATURE_BLOCKS_OVER_HOLES_COUNT:
166 return "BLOCKS_OVER_HOLES_COUNT";
167 case FEATURE_KONTUR_COUNT:
168 return "KONTUR_COUNT";
169 case FEATURE_MAX_KONTUR_LENGTH:
170 return "MAX_KONTUR_LENGTH";
171 case FEATURE_CLOSED_HOLES_COUNT:
172 return "CLOSED_HOLES_COUNT";
173 case FEATURE_WELLS_COUNT:
174 return "WELLS_COUNT";
175 case FEATURE_BLOCKS_COUNT:
176 return "BLOCKS_COUNT";
177 case FEATURE_WEIGHTED_BLOCKS_COUNT:
178 return "WEIGHTED_BLOCKS_COUNT";
179 case FEATURE_ROW_TRANSITION_COUNT:
180 return "ROW_TRANSITION_COUNT";
181 case FEATURE_COLUMN_TRANSITION_COUNT:
182 return "COLUMN_TRANSITION_COUNT";
183 case FEATURE_MAX_WELL_DEPTH:
184 return "MAX_WELL_DEPTH";
185 case FEATURE_NARROW_COUNT:
186 return "NARROW_COUNT";
187 case FEATURE_PREDICTION_COUNT:
188 return "PREDICTION_COUNT";
189 default: ;
190 // printf("WARNING: Evaluation not found!");
191 }
192 return "";
193 }
194 //----------------------------------------------------------------
getSpecialFeature(const SpecialFeatureEnumeration id,KBlocksField * newField,KBlocksField * oldField,KBlocksPiece * piece)195 double getSpecialFeature(const SpecialFeatureEnumeration id, KBlocksField *newField, KBlocksField *oldField, KBlocksPiece *piece)
196 {
197 SpecialEvaluationInterface *e = nullptr;
198 switch (id) {
199 case FEATURE_REMOVE_LINES:
200 e = Evaluation_Remove_Lines::instance(); break;
201 case FEATURE_LANDING_HEIGHT:
202 e = Evaluation_Landing_Height::instance(); break;
203 default:;
204 }
205
206 if (e == nullptr) {
207 return 0;
208 }
209
210 e->setCurrentPiece(piece);
211 e->setCurrentBoard(oldField);
212
213 return e->evaluate(newField);
214 }
215 /******************************************************************************
216 ******** Primitiv Function == Feature *********************************
217 *******************************************************************************/
218 Evaluation_Max_Height *Evaluation_Max_Height::_instance = nullptr;
evaluate(KBlocksField * field)219 double Evaluation_Max_Height::evaluate(KBlocksField *field)
220 {
221 int w = field->getWidth();
222 int max = 0;
223 for (int i = 0; i < w; i++) {
224 max = Max(max, board_signature[i]);
225 }
226 return max;
227 }
228 //----------------------------------------------------------------
229 Evaluation_Holes_Count *Evaluation_Holes_Count::_instance = nullptr;
evaluate(KBlocksField * field)230 double Evaluation_Holes_Count::evaluate(KBlocksField *field)
231 {
232 int w = field->getWidth();
233 int h = field->getHeight();
234 int count = 0;
235 for (int i = 0; i < w; i++) {
236 unsigned int maxheight = board_signature[i];
237 for (int j = maxheight + 1; j < (h - ground_line); j++) {
238 if (!field->getCell(i, j)) {
239 count++;
240 }
241 }
242 }
243 return count;
244 }
245 //----------------------------------------------------------------
246 Evaluation_Average_Height *Evaluation_Average_Height::_instance = nullptr;
evaluate(KBlocksField * field)247 double Evaluation_Average_Height::evaluate(KBlocksField *field)
248 {
249 int w = field->getWidth();
250 double average = 0;
251 for (int i = 0; i < w; i++) {
252 average += board_signature[i];
253 }
254 return average / w;
255 }
256 //----------------------------------------------------------------
257 Evaluation_Average_Height_Difference *Evaluation_Average_Height_Difference::_instance = nullptr;
evaluate(KBlocksField * field)258 double Evaluation_Average_Height_Difference::evaluate(KBlocksField *field)
259 {
260 int w = field->getWidth();
261 double average = 0;
262 int h0 = board_signature[0];
263 for (int i = 1; i < w; i++) {
264 int h1 = board_signature[i];
265 int dh = Abs(h1 - h0);
266 h0 = h1;
267 average += dh;
268 }
269 return average / (w - 1);
270 }
271 //----------------------------------------------------------------
272 Evaluation_Max_Height_Difference *Evaluation_Max_Height_Difference::_instance = nullptr;
evaluate(KBlocksField * field)273 double Evaluation_Max_Height_Difference::evaluate(KBlocksField *field)
274 {
275 int w = field->getWidth();
276 int max = 0;
277 int h0 = board_signature[0];
278 for (int i = 1; i < w; i++) {
279 int h1 = board_signature[i];
280 int dh = Abs(h1 - h0);
281 h0 = h1;
282 max = Max(max, dh);
283 }
284 return max;
285 }
286 //----------------------------------------------------------------
287 Evaluation_Kontur_Count *Evaluation_Kontur_Count::_instance = nullptr;
evaluate(KBlocksField * field)288 double Evaluation_Kontur_Count::evaluate(KBlocksField *field)
289 {
290 int w = field->getWidth();
291 int curH = board_signature[0];
292 double count = 0;
293 for (int i = 1; i < w; i++) {
294 int newH = board_signature[i];
295 if (newH == curH) {
296 count++;
297 }
298 curH = newH;
299 }
300 return count;
301 }
302 //----------------------------------------------------------------
303 Evaluation_Max_Kontur_Length *Evaluation_Max_Kontur_Length::_instance = nullptr;
evaluate(KBlocksField * field)304 double Evaluation_Max_Kontur_Length::evaluate(KBlocksField *field)
305 {
306 int w = field->getWidth();
307 int curH = board_signature[0];
308 double count = 0;
309 double max = 0;
310 for (int i = 1; i < w; i++) {
311 int newH = board_signature[i];
312 if (newH == curH) {
313 count++;
314 } else {
315 count = 0;
316 }
317 max = Max(max, count);
318 curH = newH;
319 }
320 return max;
321 }
322 //----------------------------------------------------------------
323 Evaluation_Closed_Holes_Count *Evaluation_Closed_Holes_Count::_instance = nullptr;
evaluate(KBlocksField * field)324 double Evaluation_Closed_Holes_Count::evaluate(KBlocksField *field)
325 {
326 int w = field->getWidth();
327 int h = field->getHeight();
328 int global_count = 0;
329 for (int col = 0; col < w; col++) {
330 int local_count = 0;
331 for (int row = board_signature[col]; row < (h - ground_line); row++) {
332 bool cell = field->getCell(col, row); // TODO : ?? Does not make sense...
333 if (cell) {
334 local_count++;
335 } else {
336 global_count += local_count;
337 local_count = 0;
338 }
339 }
340 global_count += local_count;
341 }
342 return global_count;
343 }
344 //----------------------------------------------------------------
345 Evaluation_Blocks_Count *Evaluation_Blocks_Count::_instance = nullptr;
evaluate(KBlocksField * field)346 double Evaluation_Blocks_Count::evaluate(KBlocksField *field)
347 {
348 int w = field->getWidth();
349 int h = field->getHeight();
350 int count = 0;
351 for (int j = 0; j < (h - ground_line); j++) {
352 for (int i = 0; i < w; i++) {
353 if (field->getCell(i, j)) {
354 count++;
355 }
356 }
357 }
358 return count;
359 }
360 //----------------------------------------------------------------
361 Evaluation_Blocks_Over_Holes_Count *Evaluation_Blocks_Over_Holes_Count::_instance = nullptr;
evaluate(KBlocksField * field)362 double Evaluation_Blocks_Over_Holes_Count::evaluate(KBlocksField *field)
363 {
364 int w = field->getWidth();
365 int h0 = field->getHeight();
366 int count = 0;
367 for (int col = 0; col < w; col++) {
368 int h1 = column_max_height(col, field);
369 for (int row = h1; row < (h0 - ground_line); row++) {
370 bool cell = field->getCell(col, row);
371 if (cell) {
372 count++;
373 } else {
374 break;
375 }
376 }
377 }
378 return count;
379 }
380 //----------------------------------------------------------------
381 Evaluation_Weighted_Blocks_Count *Evaluation_Weighted_Blocks_Count::_instance = nullptr;
evaluate(KBlocksField * field)382 double Evaluation_Weighted_Blocks_Count::evaluate(KBlocksField *field)
383 {
384 int w = field->getWidth();
385 int h = field->getHeight();
386 int count = 0;
387 for (int j = 0; j < (h - ground_line); j++) {
388 for (int i = 0; i < w; i++) {
389 if (field->getCell(i, j)) {
390 count += (h - ground_line - j); // TODO : Jet wrote (h - j) here, which I don't understand
391 }
392 }
393 }
394 return count;
395 }
396 //----------------------------------------------------------------
397 Evaluation_Row_Transition_Count *Evaluation_Row_Transition_Count::_instance = nullptr;
evaluate(KBlocksField * field)398 double Evaluation_Row_Transition_Count::evaluate(KBlocksField *field)
399 {
400 int w = field->getWidth();
401 int h = field->getHeight();
402 int count = 0;
403 for (int j = 0; j < (h - ground_line); j++) {
404 bool lastCell = true;
405 for (int i = 0; i < w; i++) {
406 bool cell = field->getCell(i, j);
407 if ((cell != lastCell) || ((i + 1) == w)) {
408 count++;
409 }
410 lastCell = cell;
411 }
412 }
413 return count;
414 }
415 //----------------------------------------------------------------
416 Evaluation_Column_Transition_Count *Evaluation_Column_Transition_Count::_instance = nullptr;
evaluate(KBlocksField * field)417 double Evaluation_Column_Transition_Count::evaluate(KBlocksField *field)
418 {
419 int w = field->getWidth();
420 int h = field->getHeight();
421 int count = 0;
422 for (int i = 0; i < w; i++) {
423 bool lastCell = true;
424 for (int j = 0; j < (h - ground_line); j++) {
425 bool cell = field->getCell(i, j);
426 if ((cell != lastCell) || ((j + 1) == (h - ground_line))) { // TODO : why (j+1)==(h - ground_line)??
427 count++;
428 }
429 lastCell = cell;
430 }
431 }
432 return count;
433 }
434 //----------------------------------------------------------------
435 Evaluation_Narrow_Count *Evaluation_Narrow_Count::_instance = nullptr;
evaluate(KBlocksField * field)436 double Evaluation_Narrow_Count::evaluate(KBlocksField *field)
437 {
438 int w = field->getWidth();
439 const int H = 5;
440 int lh = 0;
441 int ch = board_signature[0];
442 int rh = board_signature[1];
443 int count = 0;
444 if ((ch < rh) && (Abs(ch - rh) >= H)) {
445 count++;
446 }
447 for (int i = 2; i < w; i++) {
448 lh = ch;
449 ch = rh;
450 rh = column_max_height(i, field);
451 int ldh = Abs(ch - lh);
452 int rdh = Abs(ch - rh);
453 if ((ldh >= H) && (rdh >= H)) {
454 count++;
455 }
456 }
457 if ((ch > rh) && (Abs(ch - rh) >= H)) {
458 count++;
459 }
460 return count;
461 }
462 //----------------------------------------------------------------
463 Evaluation_Wells_Count *Evaluation_Wells_Count::_instance = nullptr;
evaluate(KBlocksField * field)464 double Evaluation_Wells_Count::evaluate(KBlocksField *field)
465 {
466 int w = field->getWidth();
467 int lh = 0;
468 int ch = board_signature[0];
469 int rh = board_signature[1];
470 int count = 0;
471 if (ch < rh) {
472 count += (rh - ch);
473 }
474 for (int i = 2; i < w; i++) {
475 lh = ch;
476 ch = rh;
477 rh = column_max_height(i, field);
478 if ((lh > ch) && (rh > ch)) {
479 count += Min(lh - ch, rh - ch);
480 }
481 }
482 if (ch > rh) {
483 count += (ch - rh);
484 }
485 return count;
486 }
487 //----------------------------------------------------------------
488 Evaluation_Max_Well_Depth *Evaluation_Max_Well_Depth::_instance = nullptr;
evaluate(KBlocksField * field)489 double Evaluation_Max_Well_Depth::evaluate(KBlocksField *field)
490 {
491 int w = field->getWidth();
492 int lh = 0;
493 int ch = board_signature[0];
494 int rh = board_signature[1];
495 int max = 0;
496 if (ch < rh) {
497 max = Max(max, rh - ch);
498 }
499 for (int i = 2; i < w; i++) {
500 lh = ch;
501 ch = rh;
502 rh = column_max_height(i, field);
503 if ((lh > ch) && (rh > ch)) {
504 max = Max(max, Min(lh - ch, rh - ch));
505 }
506 }
507 if (ch > rh) {
508 max = Max(max, ch - rh);
509 }
510 return max;
511 }
512 //----------------------------------------------------------------
513 Evaluation_Prediction_Count *Evaluation_Prediction_Count::_instance = nullptr;
evaluate(KBlocksField * field)514 double Evaluation_Prediction_Count::evaluate(KBlocksField *field)
515 {
516 int w = field->getWidth();
517 int count = 0;
518 KBlocksPiece piece;
519 int signatureCount = w;
520 int *signaturePiece = new int[w];
521 for (int type = 0; type < PieceType_Detail_Max_Count; type++) {
522 piece.fromValue(type);
523 int pw = piece.getWidth();
524 for (int px = 0; px < w - pw + 1; px++) {
525 signatureCount = piece.getSignature(signaturePiece);
526
527 bool matchedAll = true;
528 int matchedValue = board_signature[px] + signaturePiece[0];
529 for (int i = 0; i < signatureCount; i++) {
530 if (matchedValue != board_signature[px + i] + signaturePiece[i]) {
531 matchedAll = false;
532 break;
533 }
534 }
535 if (matchedAll) {
536 count++;
537 }
538 }
539 }
540 delete[] signaturePiece;
541 return count;
542 }
543 //# SPECIAL FEATURE #######################################
544 Evaluation_Landing_Height *Evaluation_Landing_Height::_instance = nullptr;
evaluate(KBlocksField * field)545 double Evaluation_Landing_Height::evaluate(KBlocksField *field)
546 {
547 if (mpPiece == nullptr) {
548 return 0;
549 }
550 return field->getHeight() - mpPiece->getPosY();
551 }
552 //----------------------------------------------------------------
553 Evaluation_Remove_Lines *Evaluation_Remove_Lines::_instance = nullptr;
evaluate(KBlocksField * field)554 double Evaluation_Remove_Lines::evaluate(KBlocksField *field)
555 {
556 if (mpField == nullptr) {
557 return 0;
558 }
559 int cblock = getFeature(FEATURE_BLOCKS_COUNT, mpField);
560 int nblock = getFeature(FEATURE_BLOCKS_COUNT, field);
561 if (cblock > nblock) {
562 return ((cblock - (nblock - 4)) / 10);
563 }
564 return 0;
565 }
566