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 }