File indexing completed on 2024-12-01 03:49:18
0001 /* 0002 This file is part of the KDE games lskat program 0003 SPDX-FileCopyrightText: 2006 Martin Heni <kde@heni-online.de> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "aiinput.h" 0009 0010 // Qt includes 0011 #include <QTimer> 0012 0013 // KF includes 0014 0015 // Local includes 0016 #include "lskatglobal.h" 0017 #include "player.h" 0018 0019 // Constructor for the engine 0020 AiInput::AiInput(EngineTwo *engine, QObject *parent) 0021 : AbstractInput(parent) 0022 { 0023 // Store engine 0024 mEngine = engine; 0025 } 0026 0027 // Allow or disallow input with this device 0028 void AiInput::setInputAllowed(bool allowed) 0029 { 0030 AbstractInput::setInputAllowed(allowed); 0031 if (allowed) QTimer::singleShot(1000, this, &AiInput::aiTurn); 0032 } 0033 0034 // Calculate and send out AI turn 0035 void AiInput::aiTurn() 0036 { 0037 // Turn was stopped meanwhile 0038 if (!mInputAllowed) return; 0039 0040 if (global_debug > 5) qCDebug(LSKAT_LOG) << "==================================================="; 0041 if (global_debug > 5) qCDebug(LSKAT_LOG) << "AI TURN START " << mInputAllowed; 0042 0043 // Check we are the right player 0044 if (mId != mEngine->currentPlayer()) 0045 { 0046 qCCritical(LSKAT_LOG) << "AI plays for wrong player"; 0047 return; 0048 } 0049 0050 // Retrieve game board 0051 AiInput::Board board = getBoardFromEngine(); 0052 AiInput::Move move; 0053 0054 if (global_debug > 0) qCDebug(LSKAT_LOG) << QLatin1String(""); 0055 0056 // Initiate move 0057 if (mEngine->currentMovePhase() == EngineTwo::FirstPlayerTurn) 0058 { 0059 if (global_debug > 5) qCDebug(LSKAT_LOG) << "Performing initial move " << mId; 0060 move = initiateMove(mId, board); 0061 } 0062 // Respond to move 0063 else 0064 { 0065 if (global_debug > 5) qCDebug(LSKAT_LOG) << "Performing answer move " << mId; 0066 move = answerMove(mId, board); 0067 } 0068 0069 // Send out move 0070 if (global_debug > 5) qCDebug(LSKAT_LOG) << "AI player " << mId << " moves to " << move.move; 0071 if (move.move >= 0) Q_EMIT signalPlayerInput(mId, mId, move.move); 0072 else qCCritical(LSKAT_LOG) << "Illegal AI Move???"; 0073 } 0074 0075 // Extract the current game board from the engine 0076 AiInput::Board AiInput::getBoardFromEngine() 0077 { 0078 Board b; 0079 int cnt = 0; 0080 // Reset array 0081 for (int i = 0; i < 32; i++) 0082 { 0083 b.playedCards[i] = -1; 0084 } 0085 0086 for (int i = 0; i < 2; i++) 0087 { 0088 Player *p = mEngine->player(i); 0089 for (int c = 0; c < 16; c++) 0090 { 0091 int card = p->getCard(c); 0092 b.cards[i][c] = card; 0093 b.points[i] = p->points(); 0094 } 0095 for (int c = 0; c < p->noOfMovesWon() * 2; c++) 0096 { 0097 int card = p->getWonCard(c); 0098 b.playedCards[cnt] = card; 0099 cnt++; 0100 } 0101 } 0102 0103 b.whoseTurn = mEngine->currentPlayer(); 0104 b.firstPlay = mEngine->currentMovePhase() == EngineTwo::FirstPlayerTurn; 0105 b.playedCard = mEngine->playedCard(0); 0106 b.trump = mEngine->trump(); 0107 b.analyze(); 0108 return b; 0109 } 0110 0111 // Game evaluation ratings 0112 // Point ratings 0113 const double RATING_SCHWARZ = 100000.0; 0114 const double RATING_SCHNEIDER = 70000.0; 0115 const double RATING_WON = 50000.0; 0116 const double RATING_REMIS = 20000.0; 0117 const double RATING_ONE_POINT = 500.0; 0118 // Tactical ratings 0119 const double RATING_AMOUNT_TRUMPCARD = 3.0 * RATING_ONE_POINT; 0120 const double RATING_AMOUNT_OPENCARD = 1.5 * RATING_ONE_POINT; 0121 const double RATING_AMOUNT_ACES = 3.0 * RATING_ONE_POINT; 0122 const double RATING_AMOUNT_TENS = 1.0 * RATING_ONE_POINT; 0123 const double RATING_AMOUNT_JACKS = 8.5 * RATING_ONE_POINT; 0124 0125 const double RATING_JACK_OF_CLUBS = 0.8 * RATING_ONE_POINT; 0126 const double RATING_JACK_OF_SPADE = 0.6 * RATING_ONE_POINT; 0127 const double RATING_JACK_OF_HEART = 0.4 * RATING_ONE_POINT; 0128 const double RATING_JACK_OF_DIAMOND = 0.2 * RATING_ONE_POINT; 0129 0130 // Evaluate the current game board and return a rating 0131 double AiInput::evaluteGame(int p, const AiInput::Board ¤t) 0132 { 0133 double rating = 0.0; 0134 0135 // ===== Points evaluation ===== 0136 // Check for won games of our side 0137 if (current.points[p] == 120) rating += RATING_SCHWARZ; 0138 else if (current.points[p] >= 90) rating += RATING_SCHNEIDER; 0139 else if (current.points[p] > 60) rating += RATING_WON; 0140 else if (current.points[p] == 60) rating += RATING_REMIS; 0141 0142 // Check for won games of other side 0143 if (current.points[1 - p] == 120) rating -= RATING_SCHWARZ; 0144 else if (current.points[1 - p] >= 90) rating -= RATING_SCHNEIDER; 0145 else if (current.points[1 - p] > 60) rating -= RATING_WON; 0146 else if (current.points[1 - p] == 60) rating -= RATING_REMIS; 0147 0148 // Evaluate points 0149 rating += (current.points[p] - current.points[1 - p]) * RATING_ONE_POINT; 0150 0151 // ===== Tactical evaluation ===== 0152 // Number of trumps (Stored under index Grand) 0153 int trump1 = current.amountOfSuite[p][int(Grand)]; 0154 int trump2 = current.amountOfSuite[1 - p][int(Grand)]; 0155 rating += (trump1 - trump2) * RATING_AMOUNT_TRUMPCARD; 0156 // Increase value of trumps for Grand 0157 if (current.trump == Grand) rating += (trump1 - trump2) * RATING_AMOUNT_TRUMPCARD; 0158 0159 // Missing suites 0160 //if (current.amountOfSuite[p][int(Club)] == 0 && (trump1 > trump2)) rating += RATING_GOOD_MISSING_SUITE; 0161 //if (current.amountOfSuite[p][int(Spade)] == 0 && (trump1 > trump2)) rating += RATING_GOOD_MISSING_SUITE; 0162 //if (current.amountOfSuite[p][int(Heart)] == 0 && (trump1 > trump2)) rating += RATING_GOOD_MISSING_SUITE; 0163 //if (current.amountOfSuite[p][int(Diamond)] == 0 && (trump1 > trump2)) rating += RATING_GOOD_MISSING_SUITE; 0164 0165 //if (current.amountOfSuite[1 - p][int(Club)] == 0 && (trump1 < trump2)) rating -= RATING_GOOD_MISSING_SUITE; 0166 //if (current.amountOfSuite[1 - p][int(Spade)] == 0 && (trump1 < trump2)) rating -= RATING_GOOD_MISSING_SUITE; 0167 //if (current.amountOfSuite[1 - p][int(Heart)] == 0 && (trump1 < trump2)) rating -= RATING_GOOD_MISSING_SUITE; 0168 //if (current.amountOfSuite[1 - p][int(Diamond)] == 0 && (trump1 < trump2)) rating -= RATING_GOOD_MISSING_SUITE; 0169 0170 // Number of open cards 0171 int amount1 = amountOfOpenCards(p, current); 0172 int amount2 = amountOfOpenCards(1 - p, current); 0173 rating += (amount1 - amount2) * RATING_AMOUNT_OPENCARD; 0174 0175 // Card types 0176 // Aces 0177 int amountAce1 = current.amountOfCardType[p][int(Ace)]; 0178 int amountAce2 = current.amountOfCardType[1 - p][int(Ace)]; 0179 rating += (amountAce1 - amountAce2) * RATING_AMOUNT_ACES; 0180 0181 // Tens 0182 int amountTen1 = current.amountOfCardType[p][int(Ten)]; 0183 int amountTen2 = current.amountOfCardType[1 - p][int(Ten)]; 0184 rating += (amountTen1 - amountTen2) * RATING_AMOUNT_TENS; 0185 0186 // Jacks 0187 int amountJack1 = current.amountOfCardType[p][int(Jack)]; 0188 int amountJack2 = current.amountOfCardType[1 - p][int(Jack)]; 0189 rating += (amountJack1 - amountJack2) * RATING_AMOUNT_JACKS; 0190 //qCDebug(LSKAT_LOG) << " Add rating(p=" << p << ") for jacks j1=" << amountJack1 <<" j2=" << amountJack2 << " has JC=" << findCard(current, Club, Jack); 0191 0192 if (findCard(current, Club, Jack) == p) rating += RATING_JACK_OF_CLUBS; 0193 if (findCard(current, Spade, Jack) == p) rating += RATING_JACK_OF_SPADE; 0194 if (findCard(current, Heart, Jack) == p) rating += RATING_JACK_OF_HEART; 0195 if (findCard(current, Diamond, Jack) == p) rating += RATING_JACK_OF_DIAMOND; 0196 0197 if (findCard(current, Club, Jack) == 1 - p) rating -= RATING_JACK_OF_CLUBS; 0198 if (findCard(current, Spade, Jack) == 1 - p) rating -= RATING_JACK_OF_SPADE; 0199 if (findCard(current, Heart, Jack) == 1 - p) rating -= RATING_JACK_OF_HEART; 0200 if (findCard(current, Diamond, Jack) == 1 - p) rating -= RATING_JACK_OF_DIAMOND; 0201 //qCDebug(LSKAT_LOG) << " Rating after jacks " << rating; 0202 0203 return rating; 0204 } 0205 0206 // Initiate a new move as first player 0207 AiInput::Move AiInput::initiateMove(int p, const AiInput::Board &board) 0208 { 0209 AiInput::Move maxMove; 0210 maxMove.move = -1; 0211 maxMove.value = -100.0 * RATING_SCHWARZ; // Absolute minimum score 0212 0213 // Loop all moves 0214 for (int m = 0; m < 8; m++) 0215 { 0216 AiInput::Board current(board); 0217 int card = current.takeCard(p, m); 0218 if (card < 0) continue; // Illegal move 0219 // Store move 0220 current.playedCard = card; 0221 current.whoseTurn = 1 - p; 0222 if (global_debug > 5) qCDebug(LSKAT_LOG) << "***** First mover try move on " << m << " (" << Deck::name(card) << ")"; 0223 AiInput::Move answer = answerMove(1 - p, current); 0224 // Negate answering moves value to get our rating 0225 double rating = -answer.value; 0226 if (global_debug > 5) qCDebug(LSKAT_LOG) << "First mover yields rating of " << rating; 0227 0228 rating += rulebaseFirstMover(p, card, board); 0229 if (global_debug > 5) qCDebug(LSKAT_LOG) << " rulesbase correction to " << rating; 0230 0231 // New best move? 0232 if (rating > maxMove.value) 0233 { 0234 maxMove.value = rating; 0235 maxMove.move = m; 0236 } 0237 } 0238 0239 return maxMove; 0240 } 0241 0242 // Answer a move as second player 0243 AiInput::Move AiInput::answerMove(int p, const AiInput::Board &board) 0244 { 0245 AiInput::Move maxMove; 0246 maxMove.move = -1; 0247 maxMove.value = -100.0 * RATING_SCHWARZ; // Absolute minimum score 0248 0249 // Loop all moves 0250 for (int m = 0; m < 8; m++) 0251 { 0252 AiInput::Board current(board); 0253 // qCDebug(LSKAT_LOG) << "CARD " << m << " is " 0254 // << Deck::name(current.cards[p][m]) << " on top of " 0255 // << Deck::name(current.cards[p][m + 8]); 0256 0257 int card = current.takeCard(p, m); 0258 if (card < 0) continue; // Illegal card 0259 0260 // Check validity of move 0261 if (!isLegalMove(current.playedCard, card, p, current)) continue; 0262 0263 // Check move winner 0264 int winner = EngineTwo::whoWonMove(current.playedCard, card, current.trump); 0265 if (global_debug > 5) 0266 qCDebug(LSKAT_LOG) << " Card" << m << " (" << Deck::name(card) << ") is valid " 0267 << "countering" << Deck::name(current.playedCard) << " with " 0268 << "winner (0:other, 1:we) " << winner; 0269 int deltaPoints = 0; 0270 deltaPoints += Deck::getCardValue(current.playedCard); 0271 deltaPoints += Deck::getCardValue(card); 0272 // The first mover won 0273 if (winner == 0) 0274 { 0275 current.points[1 - p] += deltaPoints; 0276 } 0277 // The second mover won (us) 0278 else 0279 { 0280 current.points[p] += deltaPoints; 0281 } 0282 0283 double rating = evaluteGame(p, current); 0284 rating += rulebaseAnswerMover(p, card, board); 0285 0286 if (global_debug > 5) 0287 qCDebug(LSKAT_LOG) << " Points after 2nd move " << m << " would be we: " 0288 << current.points[p] << " other: " << current.points[1 - p] 0289 << " rating is thus " << rating; 0290 // New best move? 0291 if (rating > maxMove.value) 0292 { 0293 maxMove.value = rating; 0294 maxMove.move = m; 0295 } 0296 } 0297 return maxMove; 0298 } 0299 0300 // Board copy constructor 0301 AiInput::Board::Board(const AiInput::Board &board) 0302 { 0303 for (int i = 0; i < 2; i++) 0304 { 0305 points[i] = board.points[i]; 0306 for (int j = 0; j < 16; j++) 0307 { 0308 cards[i][j] = board.cards[i][j]; 0309 } 0310 for (int j = 0; j < 5; j++) 0311 { 0312 amountOfSuite[i][j] = board.amountOfSuite[i][j]; 0313 } 0314 for (int j = 0; j < 8; j++) 0315 { 0316 amountOfCardType[i][j] = board.amountOfCardType[i][j]; 0317 } 0318 } 0319 for (int i = 0; i < 32; i++) 0320 { 0321 playedCards[i] = board.playedCards[i]; 0322 } 0323 playedCard = board.playedCard; 0324 whoseTurn = board.whoseTurn; 0325 firstPlay = board.firstPlay; 0326 trump = board.trump; 0327 } 0328 0329 // Retrieve card at given position for given player 0330 int AiInput::Board::card(int p, int pos) const 0331 { 0332 int card = cards[p][pos]; // 1st card 0333 if (card < 0) 0334 { 0335 card = cards[p][pos + 8]; // 2nd card 0336 } 0337 return card; 0338 } 0339 0340 // Check amount of cards at position, 0341 // i.e. card on top (2), bottom(1), or none at all (0) 0342 int AiInput::Board::cardsAtPos(int p, int pos) const 0343 { 0344 int card = cards[p][pos]; // 1st card 0345 if (card >= 0) return 2; 0346 card = cards[p][pos + 8]; // 2nd card 0347 if (card >= 0) return 1; 0348 return 0; 0349 } 0350 0351 // Retrieve card at given position for given player and 0352 // reset it (take it away) 0353 int AiInput::Board::takeCard(int p, int pos) 0354 { 0355 int card = cards[p][pos]; // 1st card 0356 cards[p][pos] = -1; // Can do in any case 0357 if (card < 0) 0358 { 0359 card = cards[p][pos + 8]; // 2nd card 0360 cards[p][pos + 8] = -1; // Can do in any case 0361 } 0362 else 0363 { 0364 cards[p][pos + 8] = -cards[p][pos + 8]; // Do not look underneath card 0365 } 0366 analyze(); 0367 return card; 0368 } 0369 0370 // Perform pre board analysis 0371 // + Count how many of each color 0372 void AiInput::Board::analyze() 0373 { 0374 // How many cards of given suite 0375 for (int pl = 0; pl < 2; pl++) 0376 { 0377 for (int i = 0; i < 5; i++) amountOfSuite[pl][i] = 0; 0378 for (int i = 0; i < 8; i++) amountOfCardType[pl][i] = 0; 0379 for (int i = 0; i < 8; i++) 0380 { 0381 int c = card(pl, i); 0382 if (c < 0) continue; 0383 Suite suite = Deck::getSuite(c); 0384 CardType type = Deck::getCardType(c); 0385 if (suite == trump || type == Jack) amountOfSuite[pl][Grand]++; 0386 else amountOfSuite[pl][int(suite)]++; 0387 amountOfCardType[pl][int(type)]++; 0388 } 0389 } 0390 } 0391 0392 // Internal return values for find card 0393 const int CARD_CURRENTLY_PLAYED = 2; 0394 const int CARD_PLAYED = 3; 0395 0396 // Check where a card is: 0397 // -1: unknown (still covered), 0: player 0, 1: player 1: 2: currently played, 2: already played 0398 int AiInput::findCard(const AiInput::Board ¤t, Suite sSuite, CardType sCardType) const 0399 { 0400 // Loop player's cards 0401 for (int p = 0; p < 2; p++) 0402 { 0403 for (int i = 0; i < 8; i++) 0404 { 0405 int card = current.card(p, i); 0406 if (card < 0) continue; 0407 Suite suite = Deck::getSuite(card); 0408 CardType type = Deck::getCardType(card); 0409 if (suite == sSuite && type == sCardType) 0410 { 0411 //qCDebug(LSKAT_LOG) << "Found" << Deck::name(sSuite, sCardType) << " at " << p << "," << i; 0412 return p; 0413 } 0414 } 0415 } 0416 0417 // Currently played card if any 0418 int card = current.playedCard; 0419 if (card >= 0) 0420 { 0421 Suite suite = Deck::getSuite(card); 0422 CardType type = Deck::getCardType(card); 0423 if (suite == sSuite && type == sCardType) 0424 { 0425 //qCDebug(LSKAT_LOG) << "Found" << Deck::name(sSuite, sCardType) << " as currently played card"; 0426 return CARD_CURRENTLY_PLAYED; 0427 } 0428 } 0429 0430 // Already played cards 0431 for (int i = 0; i < 32; i++) 0432 { 0433 int card = current.playedCards[i]; 0434 if (card < 0) continue; 0435 Suite suite = Deck::getSuite(card); 0436 CardType type = Deck::getCardType(card); 0437 if (suite == sSuite && type == sCardType) 0438 { 0439 //qCDebug(LSKAT_LOG) << "Found " << Deck::name(sSuite, sCardType) << " as played card " << i; 0440 return CARD_PLAYED; 0441 } 0442 } 0443 0444 return -1; 0445 } 0446 0447 // How many open cards has the given player 0448 int AiInput::amountOfOpenCards(int p, const AiInput::Board ¤t) const 0449 { 0450 int cnt = 0; 0451 for (int i = 0; i < 8; i++) 0452 { 0453 if (current.cardsAtPos(p, i) > 0) cnt++; 0454 } 0455 return cnt; 0456 } 0457 0458 int AiInput::amountOfWinningCards(int p, Suite sSuite, const AiInput::Board ¤t) const 0459 { 0460 int cnt = 0; 0461 for (int i = 0; i < 8; i++) 0462 { 0463 int card = current.card(p, i); 0464 if (card < 0) continue; 0465 Suite suite = Deck::getSuite(card); 0466 CardType type = Deck::getCardType(card); 0467 // Treat jacks as trump 0468 if (type == Jack) suite = Grand; 0469 // Check only right suite or trump cards 0470 if (sSuite != suite) continue; 0471 0472 // Would we win this card? 0473 if (wouldWinMove(p, card, current)) cnt++; 0474 } 0475 return cnt; 0476 } 0477 0478 // Check whether the given card would win a initial move 0479 bool AiInput::wouldWinMove(int p, int card, const AiInput::Board ¤t) const 0480 { 0481 Suite suite = Deck::getSuite(card); 0482 CardType type = Deck::getCardType(card); 0483 if (type == Jack) suite = current.trump; 0484 0485 // Check only right suite or trump cards 0486 if (suite != current.trump && 0487 current.amountOfSuite[1 - p][suite] == 0 && 0488 current.amountOfSuite[1 - p][Grand] > 0) 0489 { 0490 //qCDebug(LSKAT_LOG) << "Player" << (1 - p) << "can use trump against" << Deck::name(suite); 0491 return false; 0492 } 0493 0494 // Other player has suite..check cards 0495 for (int j = 0; j < 8; j++) 0496 { 0497 int ocard = current.card(1 - p, j); 0498 if (ocard < 0) continue; 0499 Suite osuite = Deck::getSuite(ocard); 0500 CardType otype = Deck::getCardType(ocard); 0501 if (otype == Jack) osuite = current.trump; 0502 0503 // Check only right suite or trump cards 0504 if (suite == osuite) 0505 { 0506 // 0 if card wins ocards, 1 otherwise 0507 int owinner = EngineTwo::whoWonMove(card, ocard, current.trump); 0508 if (owinner == 1) 0509 { 0510 //qCDebug(LSKAT_LOG) << "Player " << p << " looses " << Deck::name(card); 0511 return false; 0512 } 0513 } 0514 } 0515 //qCDebug(LSKAT_LOG) << "Player " << p << " wins " << Deck::name(card); 0516 return true; 0517 } 0518 0519 // Game evaluation ratings 0520 0521 // Try to save a free ten 0522 const double RULE_FREE_TEN = 20.0 * RATING_ONE_POINT; 0523 // Catch a free ten 0524 const double RULE_CATCH_TEN = 15.0 * RATING_ONE_POINT; 0525 // Use Ace to hunt free tens 0526 const double RULE_HUNTER_ACE = -20.0 * RATING_ONE_POINT; 0527 // Do not free Ace to possibly hunt free tens 0528 const double RULE_SUPPORT_ACE = -14.0 * RATING_ONE_POINT; 0529 // Protect possible free ten with minor card 0530 const double RULE_PROTECT_TEN = -14.0 * RATING_ONE_POINT; 0531 // Protect possible free ten with removing hunter Ace 0532 const double RULE_KILL_HUNTER_ACE = 25.0 * RATING_ONE_POINT; 0533 // Pull trumps 0534 const double RULE_PULL_TRUMP = 10.0 * RATING_ONE_POINT; 0535 // Save trumps 0536 const double RULE_SAVE_TRUMP = -10.0 * RATING_ONE_POINT; 0537 // Stay first mover 0538 const double RULE_FIRST_MOVER = 4.0 * RATING_ONE_POINT; 0539 0540 // Rule based override over board evaluation to tackle special scenarios 0541 double AiInput::rulebaseFirstMover(int p, int card, const AiInput::Board ¤t) const 0542 { 0543 double result = 0.0; 0544 Suite suite = Deck::getSuite(card); 0545 CardType type = Deck::getCardType(card); 0546 Suite altSuite = (suite == current.trump || type == Jack)?Grand:suite; 0547 0548 // Check whether we win the move 0549 if (wouldWinMove(p, card, current)) 0550 { 0551 if (global_debug > 1) qCDebug(LSKAT_LOG) << "TRIGGER RULE: Staying first mover " << Deck::name(card); 0552 result += RULE_FIRST_MOVER; 0553 } 0554 0555 // Protect free ten 0556 if (type == Ten && 0557 findCard(current, suite, Ace) != CARD_PLAYED && 0558 findCard(current, suite, Ace) != p && 0559 wouldWinMove(p, card, current)) 0560 { 0561 if (global_debug > 1) qCDebug(LSKAT_LOG) << "TRIGGER RULE: Saving Ten " << Deck::name(card); 0562 result += RULE_FREE_TEN; 0563 } 0564 0565 // Catch free ten 0566 if (type == Ace && 0567 (1 - p) == findCard(current, suite, Ten) && 0568 hasAmount(1 - p, altSuite, 1, 1, current) && 0569 wouldWinMove(p, card, current)) 0570 { 0571 if (global_debug > 1) qCDebug(LSKAT_LOG) << "TRIGGER RULE: Catching Ten with " << Deck::name(card); 0572 result += RULE_CATCH_TEN; 0573 } 0574 0575 // Saving Ace to try to catch free ten 0576 if (type == Ace && 0577 suite != current.trump && 0578 (1 - p) == findCard(current, suite, Ten) && 0579 hasAmount(1 - p, suite, 2, 3, current)) 0580 { 0581 if (global_debug > 1) qCDebug(LSKAT_LOG) << "TRIGGER RULE: Hunting Ten with " << Deck::name(card); 0582 result += RULE_HUNTER_ACE; 0583 } 0584 0585 // Saving additional cards for hunter Ace 0586 if (suite != current.trump && 0587 type != Jack && 0588 type != Ace && 0589 (1 - p) == findCard(current, suite, Ten) && 0590 (p) == findCard(current, suite, Ace) && 0591 hasAmount(1 - p, suite, 2, 3, current)) 0592 { 0593 if (global_debug > 1) qCDebug(LSKAT_LOG) << "TRIGGER RULE: Supporting Hunter ACE with " << Deck::name(card); 0594 if (hasAmount(1 - p, suite, 2, 2, current)) result += RULE_SUPPORT_ACE; 0595 else result += 0.75 * RULE_SUPPORT_ACE; 0596 } 0597 0598 // Saving additional cards for hunted Ten 0599 if (suite != current.trump && 0600 type != Jack && 0601 type != Ten && 0602 (1 - p) == findCard(current, suite, Ace) && 0603 (p) == findCard(current, suite, Ten) && 0604 hasAmount(p, suite, 2, 3, current) && 0605 hasAmount(1 - p, suite, 2, 11, current)) 0606 { 0607 if (global_debug > 1) qCDebug(LSKAT_LOG) << "TRIGGER RULE: Protecting hunted TEN with " << Deck::name(card); 0608 if (hasAmount(p, suite, 2, 2, current)) result += RULE_PROTECT_TEN; 0609 else result += 0.5 * RULE_PROTECT_TEN; 0610 } 0611 0612 // Killing hunter Ace 0613 if (suite != current.trump && 0614 type != Jack && 0615 type != Ten && 0616 (1 - p) == findCard(current, suite, Ace) && 0617 (p) == findCard(current, suite, Ten) && 0618 hasAmount(p, suite, 2, 11, current) && 0619 hasAmount(1 - p, suite, 1, 1, current)) 0620 { 0621 if (global_debug > 1) qCDebug(LSKAT_LOG) << "TRIGGER RULE: Killing hunter ACE with " << Deck::name(card); 0622 result += RULE_KILL_HUNTER_ACE; 0623 } 0624 0625 // Pull trumps 0626 if (altSuite == Grand) 0627 { 0628 int trumpWin1 = amountOfWinningCards(p, altSuite, current); 0629 //int trumpWin2 = amountOfWinningCards(1 - p, altSuite, current); 0630 int trump1 = current.amountOfSuite[p][int(altSuite)]; 0631 int trump2 = current.amountOfSuite[1 - p][int(altSuite)]; 0632 // Pull trump 0633 if (trumpWin1 >= trump2 && 0634 trump1 > trump2 && 0635 trump2 > 0 && 0636 wouldWinMove(p, card, current)) 0637 { 0638 if (global_debug > 1) qCDebug(LSKAT_LOG) << "TRIGGER RULE: Pull trump " << Deck::name(card); 0639 result += RULE_PULL_TRUMP; 0640 } 0641 0642 // Do not play trump if other party has none 0643 if (trump2 == 0) 0644 { 0645 if (global_debug > 1) qCDebug(LSKAT_LOG) << "TRIGGER RULE: Save trump " << Deck::name(card); 0646 if (trump1 == 1) result += RULE_SAVE_TRUMP; 0647 else if (trump1 == 2) result += 0.75 * RULE_SAVE_TRUMP; 0648 else result += 0.5 * RULE_SAVE_TRUMP; 0649 } 0650 } 0651 0652 return result; 0653 } 0654 0655 // Rule based override over board evaluation to tackle special scenarios 0656 double AiInput::rulebaseAnswerMover(int p, int card, const AiInput::Board ¤t) const 0657 { 0658 double result = 0.0; 0659 Suite suite = Deck::getSuite(card); 0660 CardType type = Deck::getCardType(card); 0661 0662 // Check whether we win the move 0663 if (wouldWinMove(p, card, current)) 0664 { 0665 if (global_debug > 1) qCDebug(LSKAT_LOG) << "TRIGGER ANSWER RULE: Becoming first mover" << Deck::name(card); 0666 result += RULE_FIRST_MOVER; 0667 } 0668 0669 // Saving Ace to try to catch free ten 0670 if (type == Ace && 0671 suite != current.trump && 0672 (1 - p) == findCard(current, suite, Ten) && 0673 hasAmount(1 - p, suite, 1, 2, current) && 0674 hasAmount(p, suite, 2, 3, current)) 0675 { 0676 if (global_debug > 1) qCDebug(LSKAT_LOG) << "TRIGGER ANSWER RULE: Hunting Ten with" << Deck::name(card); 0677 result += RULE_HUNTER_ACE; 0678 } 0679 0680 // Saving additional cards for hunter Ace 0681 if (suite != current.trump && 0682 type != Jack && 0683 type != Ace && 0684 (1 - p) == findCard(current, suite, Ten) && 0685 (p) == findCard(current, suite, Ace) && 0686 hasAmount(1 - p, suite, 1, 2, current)) 0687 { 0688 if (global_debug > 1) qCDebug(LSKAT_LOG) << "TRIGGER ANSWER RULE: Supporting Hunter ACE with" << Deck::name(card); 0689 if (hasAmount(1 - p, suite, 1, 1, current)) result += RULE_SUPPORT_ACE; 0690 else result += 0.75 * RULE_SUPPORT_ACE; 0691 } 0692 0693 return result; 0694 } 0695 0696 // Check whether the given player has between [min,max] cards of the given suite 0697 bool AiInput::hasAmount(int player, Suite suite, int min, int max, const AiInput::Board ¤t) const 0698 { 0699 if (current.amountOfSuite[player][int(suite)] >= min && 0700 current.amountOfSuite[player][int(suite)] <= max) 0701 { 0702 return true; 0703 } 0704 return false; 0705 } 0706 0707 // Check whether the two cards played are legal, supposed the given player is 0708 // the second one (as the first player always plays a legal card) 0709 bool AiInput::isLegalMove(int card1, int card2, int pl, const AiInput::Board ¤t) const 0710 { 0711 Suite suite1 = Deck::getSuite(card1); 0712 Suite suite2 = Deck::getSuite(card2); 0713 CardType type1 = Deck::getCardType(card1); 0714 CardType type2 = Deck::getCardType(card2); 0715 0716 // Force trump colour as Jacks count as Trump 0717 if (type1 == Jack) suite1 = current.trump; 0718 if (type2 == Jack) suite2 = current.trump; 0719 0720 // Same suite is always OK 0721 if (suite1 == suite2) return true; 0722 0723 // Search if current player has a card of the same colour but did not 0724 // play it (if it was played we checked already above) 0725 for (int i = 0; i < 8; i++) 0726 { 0727 int card = current.card(pl, i); 0728 // This card is not available anymore 0729 if (card < 0) continue; 0730 // if (type1 == Jack) qCDebug(LSKAT_LOG) << "Check card 2 " <<Deck::name(card); 0731 0732 // Ignore played card 0733 if (card == card2) continue; 0734 0735 // Analyze card 0736 Suite suite = Deck::getSuite(card); 0737 CardType type = Deck::getCardType(card); 0738 0739 // Force trump colour as Jacks count as Trump 0740 if (type == Jack) suite = current.trump; 0741 0742 // Check whether current card matches the first player card 0743 if (suite == suite1) 0744 { 0745 return false; 0746 } 0747 } 0748 // if (type1 == Jack) qCDebug(LSKAT_LOG) << "ALLOWED "; 0749 return true; 0750 } 0751 0752 #include "moc_aiinput.cpp"