File indexing completed on 2024-04-28 04:01:56
0001 /*************************************************************************** 0002 * KBlocks, a falling blocks game by KDE * 0003 * SPDX-FileCopyrightText: 2010 University Freiburg <squall.leonhart.cai@gmail.com> * 0004 * * 0005 * SPDX-License-Identifier: GPL-2.0-or-later 0006 ***************************************************************************/ 0007 #include "KBlocksAIFeature.h" 0008 0009 #include <vector> 0010 0011 //#define ground_line 0 0012 static int ground_line = 0; 0013 static std::vector<int> board_signature = std::vector<int> (0); 0014 0015 /************************************************************************************ 0016 **** Util Function *************************************************************** 0017 *************************************************************************************/ 0018 int Abs(int a) 0019 { 0020 if (a < 0) { 0021 return -a; 0022 } 0023 return a; 0024 } 0025 0026 double Abs(double a) 0027 { 0028 if (a < 0) { 0029 return -a; 0030 } 0031 return a; 0032 } 0033 0034 int Min(int a, int b) 0035 { 0036 if (a > b) { 0037 return b; 0038 } 0039 return a; 0040 } 0041 0042 int Max(int a, int b) 0043 { 0044 if (a < b) { 0045 return b; 0046 } 0047 return a; 0048 } 0049 0050 double Min(double a, double b) 0051 { 0052 if (a > b) { 0053 return b; 0054 } 0055 return a; 0056 } 0057 0058 double Max(double a, double b) 0059 { 0060 if (a < b) { 0061 return b; 0062 } 0063 return a; 0064 } 0065 //---------------------------------------------------------------- 0066 // returns the number of row -last non-empty cell- plus one 0067 int column_max_height(int column, KBlocksField *field) 0068 { 0069 int width = field->getWidth(); 0070 int height = field->getHeight(); 0071 0072 if ((column < 0) || (column >= width)) { 0073 //printf("WARNING: invalid column index %d",column); 0074 } 0075 0076 for (int j = 0; j < height; j++) { 0077 if (field->getCell(column, j)) { 0078 return j; 0079 } 0080 } 0081 return height; 0082 } 0083 //---------------------------------------------------------------- 0084 void set_ground_line(int null_line) 0085 { 0086 ground_line = null_line; 0087 } 0088 //---------------------------------------------------------------- 0089 void update_board_signature(KBlocksField *field) 0090 { 0091 int width = field->getWidth(); 0092 if (board_signature.empty()) { 0093 board_signature = std::vector<int> (width); 0094 } 0095 for (int i = 0; i < width; i++) { 0096 board_signature[i] = column_max_height(i, field); 0097 } 0098 } 0099 /****************************************************************************** 0100 ******** Feature List **************************************************** 0101 *******************************************************************************/ 0102 double getFeature(const FeatureEnumeration id, KBlocksField *field) 0103 { 0104 EvaluationInterface *e = nullptr; 0105 switch (id) { 0106 case FEATURE_BLOCKS_COUNT: 0107 e = Evaluation_Blocks_Count::instance(); break; 0108 case FEATURE_HOLES_COUNT: 0109 e = Evaluation_Holes_Count::instance(); break; 0110 case FEATURE_MAX_HEIGHT: 0111 e = Evaluation_Max_Height::instance(); break; 0112 case FEATURE_AVERAGE_HEIGHT: 0113 e = Evaluation_Average_Height::instance(); break; 0114 case FEATURE_AVERAGE_HEIGHT_DIFFERENT: 0115 e = Evaluation_Average_Height_Difference::instance(); break; 0116 case FEATURE_MAX_HEIGHT_DIFFERENT: 0117 e = Evaluation_Max_Height_Difference::instance(); break; 0118 case FEATURE_BLOCKS_OVER_HOLES_COUNT: 0119 e = Evaluation_Blocks_Over_Holes_Count::instance(); break; 0120 case FEATURE_KONTUR_COUNT: 0121 e = Evaluation_Kontur_Count::instance(); break; 0122 case FEATURE_MAX_KONTUR_LENGTH: 0123 e = Evaluation_Max_Kontur_Length::instance(); break; 0124 case FEATURE_NARROW_COUNT: 0125 e = Evaluation_Narrow_Count::instance(); break; 0126 case FEATURE_PREDICTION_COUNT: 0127 e = Evaluation_Prediction_Count::instance(); break; 0128 case FEATURE_CLOSED_HOLES_COUNT: 0129 e = Evaluation_Closed_Holes_Count::instance(); break; 0130 case FEATURE_WELLS_COUNT: 0131 e = Evaluation_Wells_Count::instance(); break; 0132 case FEATURE_WEIGHTED_BLOCKS_COUNT: 0133 e = Evaluation_Weighted_Blocks_Count::instance(); break; 0134 case FEATURE_ROW_TRANSITION_COUNT: 0135 e = Evaluation_Row_Transition_Count::instance(); break; 0136 case FEATURE_COLUMN_TRANSITION_COUNT: 0137 e = Evaluation_Column_Transition_Count::instance(); break; 0138 case FEATURE_MAX_WELL_DEPTH: 0139 e = Evaluation_Max_Well_Depth::instance(); break; 0140 default: ; 0141 // printf("WARNING: Evaluation not found!"); 0142 } 0143 if (e == nullptr) { 0144 return 0; 0145 } 0146 return e->evaluate(field); 0147 } 0148 0149 const char *getFeatureName(const FeatureEnumeration id) 0150 { 0151 switch (id) { 0152 case FEATURE_MAX_HEIGHT: 0153 return "MAX_HEIGHT"; 0154 case FEATURE_HOLES_COUNT: 0155 return "HOLES_COUNT"; 0156 case FEATURE_AVERAGE_HEIGHT: 0157 return "AVERAGE_HEIGHT"; 0158 case FEATURE_AVERAGE_HEIGHT_DIFFERENT: 0159 return "AVERAGE_HEIGHT_DIFFERENCE"; 0160 case FEATURE_MAX_HEIGHT_DIFFERENT: 0161 return "MAX_HEIGHT_DIFFERENCE"; 0162 case FEATURE_BLOCKS_OVER_HOLES_COUNT: 0163 return "BLOCKS_OVER_HOLES_COUNT"; 0164 case FEATURE_KONTUR_COUNT: 0165 return "KONTUR_COUNT"; 0166 case FEATURE_MAX_KONTUR_LENGTH: 0167 return "MAX_KONTUR_LENGTH"; 0168 case FEATURE_CLOSED_HOLES_COUNT: 0169 return "CLOSED_HOLES_COUNT"; 0170 case FEATURE_WELLS_COUNT: 0171 return "WELLS_COUNT"; 0172 case FEATURE_BLOCKS_COUNT: 0173 return "BLOCKS_COUNT"; 0174 case FEATURE_WEIGHTED_BLOCKS_COUNT: 0175 return "WEIGHTED_BLOCKS_COUNT"; 0176 case FEATURE_ROW_TRANSITION_COUNT: 0177 return "ROW_TRANSITION_COUNT"; 0178 case FEATURE_COLUMN_TRANSITION_COUNT: 0179 return "COLUMN_TRANSITION_COUNT"; 0180 case FEATURE_MAX_WELL_DEPTH: 0181 return "MAX_WELL_DEPTH"; 0182 case FEATURE_NARROW_COUNT: 0183 return "NARROW_COUNT"; 0184 case FEATURE_PREDICTION_COUNT: 0185 return "PREDICTION_COUNT"; 0186 default: ; 0187 // printf("WARNING: Evaluation not found!"); 0188 } 0189 return ""; 0190 } 0191 //---------------------------------------------------------------- 0192 double getSpecialFeature(const SpecialFeatureEnumeration id, KBlocksField *newField, KBlocksField *oldField, KBlocksPiece *piece) 0193 { 0194 SpecialEvaluationInterface *e = nullptr; 0195 switch (id) { 0196 case FEATURE_REMOVE_LINES: 0197 e = Evaluation_Remove_Lines::instance(); break; 0198 case FEATURE_LANDING_HEIGHT: 0199 e = Evaluation_Landing_Height::instance(); break; 0200 default:; 0201 } 0202 0203 if (e == nullptr) { 0204 return 0; 0205 } 0206 0207 e->setCurrentPiece(piece); 0208 e->setCurrentBoard(oldField); 0209 0210 return e->evaluate(newField); 0211 } 0212 /****************************************************************************** 0213 ******** Primitiv Function == Feature ********************************* 0214 *******************************************************************************/ 0215 Evaluation_Max_Height *Evaluation_Max_Height::_instance = nullptr; 0216 double Evaluation_Max_Height::evaluate(KBlocksField *field) 0217 { 0218 int w = field->getWidth(); 0219 int max = 0; 0220 for (int i = 0; i < w; i++) { 0221 max = Max(max, board_signature[i]); 0222 } 0223 return max; 0224 } 0225 //---------------------------------------------------------------- 0226 Evaluation_Holes_Count *Evaluation_Holes_Count::_instance = nullptr; 0227 double Evaluation_Holes_Count::evaluate(KBlocksField *field) 0228 { 0229 int w = field->getWidth(); 0230 int h = field->getHeight(); 0231 int count = 0; 0232 for (int i = 0; i < w; i++) { 0233 unsigned int maxheight = board_signature[i]; 0234 for (int j = maxheight + 1; j < (h - ground_line); j++) { 0235 if (!field->getCell(i, j)) { 0236 count++; 0237 } 0238 } 0239 } 0240 return count; 0241 } 0242 //---------------------------------------------------------------- 0243 Evaluation_Average_Height *Evaluation_Average_Height::_instance = nullptr; 0244 double Evaluation_Average_Height::evaluate(KBlocksField *field) 0245 { 0246 int w = field->getWidth(); 0247 double average = 0; 0248 for (int i = 0; i < w; i++) { 0249 average += board_signature[i]; 0250 } 0251 return average / w; 0252 } 0253 //---------------------------------------------------------------- 0254 Evaluation_Average_Height_Difference *Evaluation_Average_Height_Difference::_instance = nullptr; 0255 double Evaluation_Average_Height_Difference::evaluate(KBlocksField *field) 0256 { 0257 int w = field->getWidth(); 0258 double average = 0; 0259 int h0 = board_signature[0]; 0260 for (int i = 1; i < w; i++) { 0261 int h1 = board_signature[i]; 0262 int dh = Abs(h1 - h0); 0263 h0 = h1; 0264 average += dh; 0265 } 0266 return average / (w - 1); 0267 } 0268 //---------------------------------------------------------------- 0269 Evaluation_Max_Height_Difference *Evaluation_Max_Height_Difference::_instance = nullptr; 0270 double Evaluation_Max_Height_Difference::evaluate(KBlocksField *field) 0271 { 0272 int w = field->getWidth(); 0273 int max = 0; 0274 int h0 = board_signature[0]; 0275 for (int i = 1; i < w; i++) { 0276 int h1 = board_signature[i]; 0277 int dh = Abs(h1 - h0); 0278 h0 = h1; 0279 max = Max(max, dh); 0280 } 0281 return max; 0282 } 0283 //---------------------------------------------------------------- 0284 Evaluation_Kontur_Count *Evaluation_Kontur_Count::_instance = nullptr; 0285 double Evaluation_Kontur_Count::evaluate(KBlocksField *field) 0286 { 0287 int w = field->getWidth(); 0288 int curH = board_signature[0]; 0289 double count = 0; 0290 for (int i = 1; i < w; i++) { 0291 int newH = board_signature[i]; 0292 if (newH == curH) { 0293 count++; 0294 } 0295 curH = newH; 0296 } 0297 return count; 0298 } 0299 //---------------------------------------------------------------- 0300 Evaluation_Max_Kontur_Length *Evaluation_Max_Kontur_Length::_instance = nullptr; 0301 double Evaluation_Max_Kontur_Length::evaluate(KBlocksField *field) 0302 { 0303 int w = field->getWidth(); 0304 int curH = board_signature[0]; 0305 double count = 0; 0306 double max = 0; 0307 for (int i = 1; i < w; i++) { 0308 int newH = board_signature[i]; 0309 if (newH == curH) { 0310 count++; 0311 } else { 0312 count = 0; 0313 } 0314 max = Max(max, count); 0315 curH = newH; 0316 } 0317 return max; 0318 } 0319 //---------------------------------------------------------------- 0320 Evaluation_Closed_Holes_Count *Evaluation_Closed_Holes_Count::_instance = nullptr; 0321 double Evaluation_Closed_Holes_Count::evaluate(KBlocksField *field) 0322 { 0323 int w = field->getWidth(); 0324 int h = field->getHeight(); 0325 int global_count = 0; 0326 for (int col = 0; col < w; col++) { 0327 int local_count = 0; 0328 for (int row = board_signature[col]; row < (h - ground_line); row++) { 0329 bool cell = field->getCell(col, row); // TODO : ?? Does not make sense... 0330 if (cell) { 0331 local_count++; 0332 } else { 0333 global_count += local_count; 0334 local_count = 0; 0335 } 0336 } 0337 global_count += local_count; 0338 } 0339 return global_count; 0340 } 0341 //---------------------------------------------------------------- 0342 Evaluation_Blocks_Count *Evaluation_Blocks_Count::_instance = nullptr; 0343 double Evaluation_Blocks_Count::evaluate(KBlocksField *field) 0344 { 0345 int w = field->getWidth(); 0346 int h = field->getHeight(); 0347 int count = 0; 0348 for (int j = 0; j < (h - ground_line); j++) { 0349 for (int i = 0; i < w; i++) { 0350 if (field->getCell(i, j)) { 0351 count++; 0352 } 0353 } 0354 } 0355 return count; 0356 } 0357 //---------------------------------------------------------------- 0358 Evaluation_Blocks_Over_Holes_Count *Evaluation_Blocks_Over_Holes_Count::_instance = nullptr; 0359 double Evaluation_Blocks_Over_Holes_Count::evaluate(KBlocksField *field) 0360 { 0361 int w = field->getWidth(); 0362 int h0 = field->getHeight(); 0363 int count = 0; 0364 for (int col = 0; col < w; col++) { 0365 int h1 = column_max_height(col, field); 0366 for (int row = h1; row < (h0 - ground_line); row++) { 0367 bool cell = field->getCell(col, row); 0368 if (cell) { 0369 count++; 0370 } else { 0371 break; 0372 } 0373 } 0374 } 0375 return count; 0376 } 0377 //---------------------------------------------------------------- 0378 Evaluation_Weighted_Blocks_Count *Evaluation_Weighted_Blocks_Count::_instance = nullptr; 0379 double Evaluation_Weighted_Blocks_Count::evaluate(KBlocksField *field) 0380 { 0381 int w = field->getWidth(); 0382 int h = field->getHeight(); 0383 int count = 0; 0384 for (int j = 0; j < (h - ground_line); j++) { 0385 for (int i = 0; i < w; i++) { 0386 if (field->getCell(i, j)) { 0387 count += (h - ground_line - j); // TODO : Jet wrote (h - j) here, which I don't understand 0388 } 0389 } 0390 } 0391 return count; 0392 } 0393 //---------------------------------------------------------------- 0394 Evaluation_Row_Transition_Count *Evaluation_Row_Transition_Count::_instance = nullptr; 0395 double Evaluation_Row_Transition_Count::evaluate(KBlocksField *field) 0396 { 0397 int w = field->getWidth(); 0398 int h = field->getHeight(); 0399 int count = 0; 0400 for (int j = 0; j < (h - ground_line); j++) { 0401 bool lastCell = true; 0402 for (int i = 0; i < w; i++) { 0403 bool cell = field->getCell(i, j); 0404 if ((cell != lastCell) || ((i + 1) == w)) { 0405 count++; 0406 } 0407 lastCell = cell; 0408 } 0409 } 0410 return count; 0411 } 0412 //---------------------------------------------------------------- 0413 Evaluation_Column_Transition_Count *Evaluation_Column_Transition_Count::_instance = nullptr; 0414 double Evaluation_Column_Transition_Count::evaluate(KBlocksField *field) 0415 { 0416 int w = field->getWidth(); 0417 int h = field->getHeight(); 0418 int count = 0; 0419 for (int i = 0; i < w; i++) { 0420 bool lastCell = true; 0421 for (int j = 0; j < (h - ground_line); j++) { 0422 bool cell = field->getCell(i, j); 0423 if ((cell != lastCell) || ((j + 1) == (h - ground_line))) { // TODO : why (j+1)==(h - ground_line)?? 0424 count++; 0425 } 0426 lastCell = cell; 0427 } 0428 } 0429 return count; 0430 } 0431 //---------------------------------------------------------------- 0432 Evaluation_Narrow_Count *Evaluation_Narrow_Count::_instance = nullptr; 0433 double Evaluation_Narrow_Count::evaluate(KBlocksField *field) 0434 { 0435 int w = field->getWidth(); 0436 const int H = 5; 0437 int lh = 0; 0438 int ch = board_signature[0]; 0439 int rh = board_signature[1]; 0440 int count = 0; 0441 if ((ch < rh) && (Abs(ch - rh) >= H)) { 0442 count++; 0443 } 0444 for (int i = 2; i < w; i++) { 0445 lh = ch; 0446 ch = rh; 0447 rh = column_max_height(i, field); 0448 int ldh = Abs(ch - lh); 0449 int rdh = Abs(ch - rh); 0450 if ((ldh >= H) && (rdh >= H)) { 0451 count++; 0452 } 0453 } 0454 if ((ch > rh) && (Abs(ch - rh) >= H)) { 0455 count++; 0456 } 0457 return count; 0458 } 0459 //---------------------------------------------------------------- 0460 Evaluation_Wells_Count *Evaluation_Wells_Count::_instance = nullptr; 0461 double Evaluation_Wells_Count::evaluate(KBlocksField *field) 0462 { 0463 int w = field->getWidth(); 0464 int lh = 0; 0465 int ch = board_signature[0]; 0466 int rh = board_signature[1]; 0467 int count = 0; 0468 if (ch < rh) { 0469 count += (rh - ch); 0470 } 0471 for (int i = 2; i < w; i++) { 0472 lh = ch; 0473 ch = rh; 0474 rh = column_max_height(i, field); 0475 if ((lh > ch) && (rh > ch)) { 0476 count += Min(lh - ch, rh - ch); 0477 } 0478 } 0479 if (ch > rh) { 0480 count += (ch - rh); 0481 } 0482 return count; 0483 } 0484 //---------------------------------------------------------------- 0485 Evaluation_Max_Well_Depth *Evaluation_Max_Well_Depth::_instance = nullptr; 0486 double Evaluation_Max_Well_Depth::evaluate(KBlocksField *field) 0487 { 0488 int w = field->getWidth(); 0489 int lh = 0; 0490 int ch = board_signature[0]; 0491 int rh = board_signature[1]; 0492 int max = 0; 0493 if (ch < rh) { 0494 max = Max(max, rh - ch); 0495 } 0496 for (int i = 2; i < w; i++) { 0497 lh = ch; 0498 ch = rh; 0499 rh = column_max_height(i, field); 0500 if ((lh > ch) && (rh > ch)) { 0501 max = Max(max, Min(lh - ch, rh - ch)); 0502 } 0503 } 0504 if (ch > rh) { 0505 max = Max(max, ch - rh); 0506 } 0507 return max; 0508 } 0509 //---------------------------------------------------------------- 0510 Evaluation_Prediction_Count *Evaluation_Prediction_Count::_instance = nullptr; 0511 double Evaluation_Prediction_Count::evaluate(KBlocksField *field) 0512 { 0513 int w = field->getWidth(); 0514 int count = 0; 0515 KBlocksPiece piece; 0516 int signatureCount = w; 0517 int *signaturePiece = new int[w]; 0518 for (int type = 0; type < PieceType_Detail_Max_Count; type++) { 0519 piece.fromValue(type); 0520 int pw = piece.getWidth(); 0521 for (int px = 0; px < w - pw + 1; px++) { 0522 signatureCount = piece.getSignature(signaturePiece); 0523 0524 bool matchedAll = true; 0525 int matchedValue = board_signature[px] + signaturePiece[0]; 0526 for (int i = 0; i < signatureCount; i++) { 0527 if (matchedValue != board_signature[px + i] + signaturePiece[i]) { 0528 matchedAll = false; 0529 break; 0530 } 0531 } 0532 if (matchedAll) { 0533 count++; 0534 } 0535 } 0536 } 0537 delete[] signaturePiece; 0538 return count; 0539 } 0540 //# SPECIAL FEATURE ####################################### 0541 Evaluation_Landing_Height *Evaluation_Landing_Height::_instance = nullptr; 0542 double Evaluation_Landing_Height::evaluate(KBlocksField *field) 0543 { 0544 if (mpPiece == nullptr) { 0545 return 0; 0546 } 0547 return field->getHeight() - mpPiece->getPosY(); 0548 } 0549 //---------------------------------------------------------------- 0550 Evaluation_Remove_Lines *Evaluation_Remove_Lines::_instance = nullptr; 0551 double Evaluation_Remove_Lines::evaluate(KBlocksField *field) 0552 { 0553 if (mpField == nullptr) { 0554 return 0; 0555 } 0556 int cblock = getFeature(FEATURE_BLOCKS_COUNT, mpField); 0557 int nblock = getFeature(FEATURE_BLOCKS_COUNT, field); 0558 if (cblock > nblock) { 0559 return ((cblock - (nblock - 4)) / 10); 0560 } 0561 return 0; 0562 }