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