File indexing completed on 2024-05-12 07:58:31
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 "KBlocksAIPlayer.h" 0008 0009 #include "KBlocksAIEvaluation.h" 0010 #include "KBlocksAIFeature.h" 0011 #include "KBlocksAIPlannerExtend.h" 0012 #include "KBlocksAILog.h" 0013 0014 #include "kblocks_ai_debug.h" 0015 0016 #include <utility> 0017 #include <vector> 0018 0019 typedef std::pair<KBlocksPiece, double> Result; 0020 typedef std::vector<Result> Result_Sequence; 0021 0022 #define MAX_UTILITY // choose the goal with the highest utility 0023 //# PLANNER ###################################### 0024 #define TWO_PIECE_PLANNER 0025 //# SITUATION TREE ################################ 0026 #define SITUATION_MULTI_NARROW 0027 #define SITUATION_CRITIC_HEIGHT 0028 #define SITUATION_TERIS 0029 //#define SITUATION_SECTION_ANALYSIS 0030 //#define SITUATION_DEEP_NARROW 0031 //#define SITUATION_TALL_BLOCKS 0032 0033 //# EVALUATION FEATURE ############################ 0034 //#define DIFFERENCE_EVALUATION 0035 //#define SECOND_EVALUATION 0036 0037 //# EXTRA EVALUATION ############################# 0038 //#define SPECIAL_EVALUATION 0039 #ifdef SPECIAL_EVALUATION 0040 #define NBS_EVALUATION 0041 #endif 0042 0043 KBlocksAIPlayer::KBlocksAIPlayer(const string &name) 0044 { 0045 mAIName = name; 0046 0047 mAIStarted = false; 0048 mAIPaused = false; 0049 0050 mpAIField = nullptr; 0051 mpCurPiece = nullptr; 0052 mpNextPiece = nullptr; 0053 0054 mpPlanner = nullptr; 0055 mNextCount = 0; 0056 0057 mpEvaluatorFinal = nullptr; 0058 mpEvaluatorFirst = nullptr; 0059 mpEvaluatorPre = nullptr; 0060 0061 mBestPosition = 0; 0062 mBestRotation = 0; 0063 } 0064 0065 KBlocksAIPlayer::~KBlocksAIPlayer() 0066 { 0067 if (mAIStarted) { 0068 delete mpPlanner; 0069 delete mpNextPiece; 0070 delete mpCurPiece; 0071 delete mpAIField; 0072 } 0073 } 0074 0075 void KBlocksAIPlayer::startGame(SingleGameInterface *p) 0076 { 0077 if (mAIStarted) { 0078 return; 0079 } 0080 0081 mAIStarted = true; 0082 mpGame = p; 0083 0084 mpAIField = new KBlocksField(mpGame->getField()); 0085 mpCurPiece = new KBlocksPiece(mpGame->getPiece(0)); 0086 mpNextPiece = new KBlocksPiece(mpGame->getPiece(1)); 0087 0088 mpPlanner = new KBlocksAIPlannerExtend(mpAIField); 0089 } 0090 0091 void KBlocksAIPlayer::stopGame() 0092 { 0093 if (!mAIStarted) { 0094 return; 0095 } 0096 0097 delete mpPlanner; 0098 mpPlanner = nullptr; 0099 0100 delete mpNextPiece; 0101 mpNextPiece = nullptr; 0102 delete mpCurPiece; 0103 mpCurPiece = nullptr; 0104 delete mpAIField; 0105 mpAIField = nullptr; 0106 0107 mpGame = nullptr; 0108 mAIStarted = false; 0109 } 0110 0111 void KBlocksAIPlayer::pauseGame(bool flag) 0112 { 0113 mAIPaused = flag; 0114 } 0115 0116 void KBlocksAIPlayer::think(GamePlayer_ActionList *actionList) 0117 { 0118 if ((!mAIStarted) || (mAIPaused)) { 0119 return; 0120 } 0121 0122 // Phase I - State Update 0123 update(); 0124 // Phase II - Planning 0125 planning(); 0126 // Phase III - Situation Analysis 0127 situationAdaption(); 0128 // Phase IV - States Evaluation 0129 generateGoalAction(); 0130 0131 // set up action list 0132 if (mBestRotation > 0) { 0133 for (int i = 0; i < mBestRotation; i++) { 0134 actionList->push_back(PlayerAction_Rotate_CW); 0135 } 0136 } else { 0137 mBestRotation = -mBestRotation; 0138 for (int i = 0; i < mBestRotation; i++) { 0139 actionList->push_back(PlayerAction_Rotate_CCW); 0140 } 0141 } 0142 0143 if (mBestPosition > 0) { 0144 for (int i = 0; i < mBestPosition; i++) { 0145 actionList->push_back(PlayerAction_Move_Right); 0146 } 0147 } else { 0148 mBestPosition = -mBestPosition; 0149 for (int i = 0; i < mBestPosition; i++) { 0150 actionList->push_back(PlayerAction_Move_Left); 0151 } 0152 } 0153 0154 actionList->push_back(PlayerAction_Push_Down); 0155 } 0156 0157 string KBlocksAIPlayer::getName() 0158 { 0159 return mAIName; 0160 } 0161 0162 /****************************************************************** 0163 * THINK - DECIDE - CATCH 0164 *****************************************************************/ 0165 /* Phase I - State Update ----------------------------------*/ 0166 void KBlocksAIPlayer::update() 0167 { 0168 mpAIField->copy(mpGame->getField()); 0169 mpCurPiece->copy(mpGame->getPiece(0)); 0170 mpNextPiece->copy(mpGame->getPiece(1)); 0171 } 0172 0173 /* Phase II - Planning ----------------------------------*/ 0174 void KBlocksAIPlayer::planning() 0175 { 0176 if (!mpPlanner) { 0177 qCWarning(KBlocksAI) << "No planner set for AI evaluation!"; 0178 return; 0179 } 0180 #ifdef TWO_PIECE_PLANNER 0181 AIPlanner_PieceValue_Sequence mPieceSequence = AIPlanner_PieceValue_Sequence(0); 0182 mPieceSequence.push_back(KBlocks_PieceType_Detail(mpCurPiece->toValue())); 0183 mPieceSequence.push_back(KBlocks_PieceType_Detail(mpNextPiece->toValue())); 0184 KBlocksAIPlannerExtend *extendedPlanner = dynamic_cast<KBlocksAIPlannerExtend *>(mpPlanner); 0185 if (extendedPlanner != nullptr) { 0186 mNextCount = extendedPlanner->process(mPieceSequence); 0187 } 0188 #else 0189 KBlocks_PieceType_Detail type = KBlocks_PieceType_Detail(mpCurPiece->toValue()); 0190 mNextCount = mpPlanner->process(type); 0191 #endif 0192 } 0193 0194 /* Phase III - Situation Analysis -------------------*/ 0195 void KBlocksAIPlayer::situationAdaption() 0196 { 0197 #ifndef SPECIAL_EVALUATION 0198 if (false) { 0199 } 0200 #ifdef SITUATION_MULTI_NARROW 0201 else if (getDecisionFeature(DF_PEEKS_COUNT, mpAIField)) { 0202 mpEvaluatorFinal = WellsFillerEvaluation::instance(); 0203 } 0204 #endif 0205 #ifdef SITUATION_CRITIC_HEIGHT 0206 else if (getDecisionFeature(DF_HEIGHT_MAX, mpAIField)) { 0207 mpEvaluatorFinal = HeightKillerEvaluation::instance(); 0208 } 0209 #endif 0210 #ifdef SITUATION_SECTION_ANALYSIS 0211 else if (getDecisionFeature(DF_LAYER_SCAN, mpAIField)) { 0212 mpEvaluatorFinal = HalfBaseEvaluation::instance(); 0213 } 0214 #endif 0215 #ifdef SITUATION_DEEP_NARROW 0216 else if (getDecisionFeature(DF_DEEP_NARROW, mpAIField)) { 0217 mpEvaluatorFinal = DeepNarrowRemoverEvaluation::instance(); 0218 } 0219 #endif 0220 #ifdef SITUATION_TALL_BLOCKS 0221 else if (getDecisionFeature(DF_BLOCK_SCAN, mpAIField)) { 0222 mpEvaluatorFinal = BlockRemoverEvaluation::instance(); 0223 } 0224 #endif 0225 #ifdef SITUATION_TERIS 0226 else if (getDecisionFeature(DF_CREATING_TETRIS, mpAIField)) { 0227 if (getDecisionFeature(DF_REMOVE_TETRIS, mpAIField)) { 0228 mpEvaluatorFinal = TetrisEliminationEvaluation::instance(); 0229 } else { 0230 mpEvaluatorFinal = TetrisPreparingEvaluation::instance(); 0231 } 0232 } 0233 #endif 0234 else { 0235 mpEvaluatorFinal = BaseEvaluation::instance(); 0236 } 0237 0238 #ifdef DIFFERENCE_EVALUATION_ 0239 mpEvaluatorPre = mpEvaluatorFinal; 0240 #endif 0241 0242 #ifdef SECOND_EVALUATION 0243 mpEvaluatorFirst = BaseEvaluation::instance(); 0244 #endif 0245 0246 #else 0247 #ifdef NBS_EVALUATION 0248 mpEvaluatorFirst = 0; 0249 mpEvaluatorPre = 0; 0250 mpEvaluatorFinal = NBSEvaluation::instance(); 0251 #endif 0252 #endif 0253 //println(mpEvaluatorFinal->evaluationName()); 0254 } 0255 0256 /* Phase IV - States Evaluation -------------------*/ 0257 void KBlocksAIPlayer::generateGoalAction() 0258 { 0259 double current_value = 0; 0260 if (mpEvaluatorPre != nullptr) { 0261 current_value = mpEvaluatorPre->evaluate(mpAIField); 0262 } 0263 0264 double best_value = 0; 0265 Result_Sequence result = Result_Sequence(0); 0266 std::vector<int> best_goals = std::vector<int>(0); 0267 0268 mBestRotation = 0; 0269 mBestPosition = 0; 0270 0271 // FIRST STEP - ANALYZE FINAL BOARD_STATE 0272 if (mpEvaluatorFinal != nullptr) { 0273 bool firstValue = true; 0274 0275 for (int i = 0; i < mNextCount; i++) { 0276 KBlocksField *field = new KBlocksField(mpAIField->getWidth(), mpAIField->getHeight()); 0277 mpPlanner->getNextBoardStatus(i, field); 0278 0279 if (!field) { 0280 continue; 0281 } 0282 0283 KBlocksPiece *piece = new KBlocksPiece(); 0284 mpPlanner->getNextPieceState(i, piece); 0285 0286 double value = 0; 0287 #ifndef SPECIAL_EVALUATION 0288 value = mpEvaluatorFinal->evaluate(field) - current_value; 0289 #else 0290 #ifdef NBS_EVALUATION 0291 SpecialEvaluationInterface *evaluator = 0292 dynamic_cast<SpecialEvaluationInterface *>(mpEvaluatorFinal); 0293 evaluator->setCurrentPiece(piece); 0294 value = evaluator->evaluate(field); 0295 #endif 0296 #endif 0297 result.push_back(Result(KBlocksPiece(piece), value)); 0298 #ifdef MAX_UTILITY 0299 bool better = (best_value < value); 0300 #else 0301 bool better = (best_value > value); 0302 #endif 0303 if (better || firstValue) { 0304 best_goals.clear(); 0305 best_value = value; 0306 firstValue = false; 0307 } 0308 0309 // alternative found 0310 if (best_value == value) { 0311 best_goals.push_back(i); 0312 } 0313 0314 delete piece; 0315 if (field) { 0316 delete field; 0317 } 0318 } 0319 } 0320 0321 // NO SOLUTION FOUND 0322 if (best_goals.empty()) { 0323 return ; 0324 } 0325 0326 //SECOND STEP 0327 #ifdef TWO_PIECE_PLANNER 0328 #ifdef SECOND_EVALUATION 0329 if (mpEvaluatorFirst != 0) { 0330 std::vector<int> _best_goals = std::vector<int>(0); 0331 double _best_value = 0; 0332 double firstValue = true; 0333 0334 KBlocksAIPlannerExtend *_planner = (dynamic_cast<KBlocksAIPlannerExtend *>(mpPlanner)); 0335 0336 // double _current_value = current_value; 0337 for (int i = 0; i < best_goals.size(); i++) { 0338 int index = best_goals[i]; 0339 0340 KBlocksField *field = new KBlocksField(); 0341 0342 _planner->getNextBoardStatus(index, field, true); 0343 0344 if (!field) { 0345 continue; 0346 } 0347 0348 double value = mpEvaluatorFirst->evaluate(field); 0349 #ifdef MAX_UTILITY 0350 bool better = (_best_value < value); 0351 #else 0352 bool better = (_best_value > value); 0353 #endif 0354 if (better || firstValue) { 0355 _best_goals.clear(); 0356 _best_value = value; 0357 firstValue = false; 0358 better = true; 0359 } 0360 0361 // alternative found 0362 if (better || (_best_value == value)) { 0363 _best_goals.push_back(index); 0364 } 0365 0366 if (field) { 0367 delete field; 0368 } 0369 } 0370 best_goals = std::vector<int>(_best_goals); 0371 } 0372 #endif 0373 #endif 0374 0375 #ifdef INITLIST 0376 int goal_index = best_goals[0]; 0377 #else 0378 int goal_index = best_goals[rand() % best_goals.size()]; 0379 #endif 0380 0381 KBlocksPiece *goal_piece = &(result[goal_index].first); 0382 0383 mBestRotation = goal_piece->getRotation() - mpCurPiece->getRotation(); 0384 mBestPosition = goal_piece->getPosX() - mpCurPiece->getPosX(); 0385 }