File indexing completed on 2024-10-06 03:48:02
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 "engine_two.h" 0009 0010 // Qt includes 0011 #include <QTimer> 0012 0013 // KF includes 0014 #include <KLocalizedString> 0015 0016 // Local includes 0017 #include "lskatglobal.h" 0018 #include "display_two.h" 0019 0020 #define TIME_END_MOVE 1000 /* Wait this [ms] after end of move */ 0021 0022 // Constructor for the game document/engine 0023 EngineTwo::EngineTwo(QWidget *parent, Deck *deck, DisplayTwo *display) 0024 : AbstractEngine(parent) 0025 { 0026 mDisplay = display; 0027 mDeck = deck; 0028 mCurrentPlayer = 0; 0029 0030 connect(mDisplay, &DisplayTwo::dealingDone, this, &EngineTwo::gameLoopStart); 0031 } 0032 0033 // Initial part of the game loop. Prepare new move etc 0034 void EngineTwo::gameLoopStart() 0035 { 0036 if (global_debug > 1) qCDebug(LSKAT_LOG) << "GAME LOOP START"; 0037 if (!isGameRunning()) return; 0038 0039 // Switch to the current player 0040 activatePlayer(mCurrentPlayer); 0041 } 0042 0043 // Middle part of the game loop. Receive input of a player 0044 // Here: inputId == playerNumber 0045 void EngineTwo::playerInput(int inputId, int playerNumber, int cardNumber) 0046 { 0047 if (global_debug > 0) 0048 qCDebug(LSKAT_LOG) << "Engine got player input: card=" << cardNumber 0049 << "Player=" << playerNumber << "Id=" << inputId; 0050 if (playerNumber != mCurrentPlayer) 0051 { 0052 if (global_debug > 0) 0053 qCDebug(LSKAT_LOG) << "EngineTwo::playerInput: Input from wrong player"; 0054 return; 0055 } 0056 0057 Player *player = mPlayers[playerNumber]; 0058 int height = 0; 0059 int card = player->getCard(cardNumber); 0060 // Check whether top card is still available - if not try bottom one 0061 if (card < 0) 0062 { 0063 height = 1; 0064 card = player->getCard(cardNumber + 8 * height); 0065 } 0066 0067 // Check whether player still has this card 0068 if (card < 0) 0069 { 0070 if (global_debug > 0) 0071 qCDebug(LSKAT_LOG) << "EngineTwo::playerInput: Card" << cardNumber + 8 * height 0072 << "not available anymore "; 0073 return; 0074 } 0075 0076 // Remove this, Debug current card 0077 if (global_debug > 0) 0078 { 0079 Suite suite = Deck::getSuite(card); 0080 CardType type = Deck::getCardType(card); 0081 qCDebug(LSKAT_LOG) << "Gameloop " <<mCurrentPlayer <<" plays" << Deck::name(suite, type); 0082 } 0083 0084 // Check for legal move (first player always ok) 0085 if (mCurrentMovePhase == SecondPlayerTurn) 0086 { 0087 // Check (card of player 1), (card of player 2), (player 2) 0088 if (!isLegalMove(mCurrentMoveCards[FirstPlayerTurn], card, playerNumber)) 0089 { 0090 if (global_debug > 0) 0091 qCDebug(LSKAT_LOG) << "EngineTwo::playerInput: Card" << cardNumber + 8 * height 0092 << "is not a valid move "; 0093 return; 0094 } 0095 } 0096 0097 // Delete card from player 0098 player->deleteCard(cardNumber + 8 * height); 0099 0100 // Finish input from player 0101 player->stopTurn(); 0102 0103 // Play out card 0104 mDisplay->play(card, playerNumber, mCurrentMovePhase); 0105 0106 // Turn back card if top card is played 0107 if (height == 0) 0108 { 0109 int backcard = player->getCard(cardNumber + 8); 0110 mCurrentTurnCards[mCurrentMovePhase] = backcard; 0111 } 0112 else 0113 { 0114 mCurrentTurnCards[mCurrentMovePhase] = -1; 0115 } 0116 0117 // Store currently played card 0118 mCurrentMoveCards[mCurrentMovePhase] = card; 0119 0120 // Finish game loop 0121 if (mCurrentMovePhase == SecondPlayerTurn) 0122 { 0123 QTimer::singleShot(TIME_END_MOVE, this, &EngineTwo::gameLoopFinish); 0124 } 0125 else 0126 { 0127 QTimer::singleShot(0, this, &EngineTwo::gameLoopFinish); 0128 } 0129 } 0130 0131 // Final part of the game loop. Switch player etc. 0132 void EngineTwo::gameLoopFinish() 0133 { 0134 if (!isGameRunning()) return; 0135 0136 // If second move phase, remove cards 0137 if (mCurrentMovePhase == SecondPlayerTurn) 0138 { 0139 // Turn cards if available 0140 if (mCurrentTurnCards[FirstPlayerTurn] >= 0) 0141 mDisplay->turn(mCurrentTurnCards[FirstPlayerTurn]); 0142 if (mCurrentTurnCards[SecondPlayerTurn] >= 0) 0143 mDisplay->turn(mCurrentTurnCards[SecondPlayerTurn]); 0144 0145 // Switch the current player if second player one 0146 int winner = whoWonMove(mCurrentMoveCards[FirstPlayerTurn], 0147 mCurrentMoveCards[SecondPlayerTurn], 0148 mTrump); 0149 // The first mover won. This means to switch the current player back 0150 // to him. Otherwise the current player stays untouched, that is the 0151 // second player plays again. 0152 if (winner == 0) 0153 { 0154 mCurrentPlayer = 1 - mCurrentPlayer; 0155 } 0156 0157 // Move both cards away from play area. Move them to the winning mover's side 0158 Player *player = mPlayers[mCurrentPlayer]; 0159 mDisplay->remove(mCurrentPlayer, 0160 mCurrentMoveCards[FirstPlayerTurn], 0161 player->noOfMovesWon()); 0162 mDisplay->remove(mCurrentPlayer, 0163 mCurrentMoveCards[SecondPlayerTurn], 0164 player->noOfMovesWon() + 1); 0165 player->increaseMovesWon(); 0166 player->addWonCard(mCurrentMoveCards[FirstPlayerTurn]); 0167 player->addWonCard(mCurrentMoveCards[SecondPlayerTurn]); 0168 0169 if (global_debug > 0) 0170 { 0171 qCDebug(LSKAT_LOG) << "Winner = " << winner << "current =" << mCurrentPlayer; 0172 qCDebug(LSKAT_LOG) << " He has won" << player->noOfMovesWon() << "moves."; 0173 qCDebug(LSKAT_LOG) << " He has" << player->points() << "points."; 0174 } 0175 // Switch move phase (half moves) 0176 mCurrentMovePhase = FirstPlayerTurn; 0177 } 0178 // For the first part of a half move always swap players 0179 else if (mCurrentMovePhase == FirstPlayerTurn) 0180 { 0181 mCurrentPlayer = 1 - mCurrentPlayer; 0182 // Switch move phase (half moves) 0183 mCurrentMovePhase = SecondPlayerTurn; 0184 } 0185 0186 // Check whether the game is over 0187 if (gameOver()) 0188 { 0189 if (global_debug > 0) qCDebug(LSKAT_LOG) << "GAME OVER"; 0190 mGameStatus = Stopped; 0191 mDisplay->showMove(-1); 0192 int winner = evaluateGame(); 0193 Q_EMIT signalGameOver(winner); 0194 } 0195 // Game continues 0196 else 0197 { 0198 // Increase move number 0199 mMoveNumber++; 0200 0201 // Delayed call to game loop start 0202 QTimer::singleShot(0, this, &EngineTwo::gameLoopStart); 0203 } 0204 } 0205 0206 // Check whether the game is over 0207 bool EngineTwo::gameOver() 0208 { 0209 if (global_debug > 0) qCDebug(LSKAT_LOG) << "Move number in game over" << mMoveNumber; 0210 // Check number of moves. If all moves are done game is over. 0211 if (mMoveNumber >= 31) return true; 0212 return false; 0213 } 0214 0215 // Called after game ends..give points to players 0216 int EngineTwo::evaluateGame() 0217 { 0218 int winner = -1; 0219 Player *player1 = mPlayers[0]; 0220 Player *player2 = mPlayers[1]; 0221 0222 // Points in game 0223 int points1 = player1->points(); 0224 int points2 = player2->points(); 0225 0226 // Score awarded 0227 int score1 = 0; 0228 int score2 = 0; 0229 0230 QString text; 0231 0232 // Game was aborted 0233 if (points1 + points2 != 120) 0234 { 0235 text = i18n("Game was ended - no winner"); 0236 winner = -2; 0237 } 0238 // Drawn 0239 else if (points1 == points2) 0240 { 0241 // Add score 0242 score1 = 1; 0243 score2 = 1; 0244 text = i18n("Game is drawn"); 0245 winner = -1; 0246 } 0247 // First player won 0248 else if (points1 > points2) 0249 { 0250 text = i18n("Player 1 - %1 won ", player1->name()); 0251 0252 score1 = 2; 0253 player1->addWonGame(1); 0254 if (points1 > 90) 0255 { 0256 score1 += 1; // Schneider 0257 text = i18n("%1 won with over 90 points. Super!", player1->name()); 0258 } 0259 // Do not use 'else if' here! 0260 if (points1 >= 120) 0261 { 0262 score1 += 1; // Schwarz 0263 text = i18n("%1 won to nil. Congratulations!", player1->name()); 0264 } 0265 winner = 0; 0266 } 0267 // Second player won 0268 else 0269 { 0270 text = i18n("Player 2 - %1 won ", player2->name()); 0271 0272 score2 = 2; 0273 player2->addWonGame(1); 0274 if (points2 > 90) 0275 { 0276 score2 += 1; // Schneider 0277 text = i18n("%1 won with over 90 points. Super!", player2->name()); 0278 } 0279 // Do not use 'else if' here! 0280 if (points2 >= 120) 0281 { 0282 score2 += 1; // Schwarz 0283 text = i18n("%1 won to nil. Congratulations!", player2->name()); 0284 } 0285 winner = 1; 0286 } 0287 0288 // Add score to players 0289 player1->addScore(score1); 0290 player2->addScore(score2); 0291 0292 // Display game over data 0293 mDisplay->showText(text); 0294 mDisplay->showScore(0, score1); 0295 mDisplay->showScore(1, score2); 0296 0297 return winner; 0298 } 0299 0300 // Start a new game 0301 void EngineTwo::startGame(Suite trump, int startPlayer) 0302 { 0303 // Set the new trump 0304 mTrump = trump; 0305 0306 // Set game status to running 0307 mGameStatus = Running; 0308 0309 // Switch to the start player 0310 mCurrentPlayer = startPlayer; 0311 0312 // Set first player to move 0313 mCurrentMovePhase = FirstPlayerTurn; 0314 0315 // Game starts, reset moves 0316 mMoveNumber = 0; 0317 0318 // Loop players to deal cards 0319 for (int p = 0; p < 2; p++) 0320 { 0321 Player *player = mPlayers[p]; 0322 player->addGame(1); // Increase games of player 0323 mDisplay->deal(player, p); 0324 } 0325 0326 // Delayed call to game loop start via display signal 'dealingDone' 0327 // (see constructor) 0328 } 0329 0330 // Stop a game 0331 void EngineTwo::stopGame() 0332 { 0333 // Disable inputs for two players 0334 for (int i = 0; i < 2; i++) 0335 { 0336 mPlayers[i]->stopTurn(); 0337 } 0338 0339 mDisplay->showMove(-1); 0340 0341 if (isGameRunning()) 0342 { 0343 // Display game over data 0344 QString text = i18n("Game ended"); 0345 mDisplay->showText(text); 0346 0347 // Set game status to stopped 0348 mGameStatus = Stopped; 0349 Q_EMIT signalGameOver(-2); 0350 } 0351 } 0352 0353 // Switch the current player to the given player number. 0354 void EngineTwo::activatePlayer(int playerNumber) 0355 { 0356 // Disable inputs for two players 0357 for (int i = 0; i < 2; i++) 0358 { 0359 mPlayers[i]->stopTurn(); 0360 } 0361 // enable input of current player 0362 Player *player = mPlayers[playerNumber]; 0363 player->startTurn(); 0364 mDisplay->showMove(playerNumber); 0365 mCurrentPlayer = playerNumber; 0366 0367 Q_EMIT signalNextPlayer(player); 0368 } 0369 0370 // Check whether the two cards played are legal, supposed the 0371 // given player is the second one (as the first player always 0372 // plays a legal card) 0373 bool EngineTwo::isLegalMove(int card1, int card2, int playerNumber) 0374 { 0375 Suite suite1 = Deck::getSuite(card1); 0376 Suite suite2 = Deck::getSuite(card2); 0377 CardType type1 = Deck::getCardType(card1); 0378 CardType type2 = Deck::getCardType(card2); 0379 0380 // Force trump colour as Jacks count as Trump 0381 if (type1 == Jack) suite1 = mTrump; 0382 if (type2 == Jack) suite2 = mTrump; 0383 0384 // Same suite is always ok 0385 if (suite1 == suite2) return true; 0386 0387 // Search if current player has a card of the same colour 0388 // but didn't play it (if it was played we checked already 0389 // above) 0390 Player *p = player(playerNumber); 0391 bool validMove = true; 0392 for (int i = 0; i < 8; i++) 0393 { 0394 int card = p->getCard(i); 0395 // Ignore played card 0396 if (card == card2) continue; 0397 0398 // Check whether top card is still available - if not try bottom one 0399 if (card < 0) 0400 { 0401 card = p->getCard(i + 8); 0402 } 0403 // This card is not available anymore 0404 if (card < 0) continue; 0405 0406 // Analyze card 0407 Suite suite = Deck::getSuite(card); 0408 CardType type = Deck::getCardType(card); 0409 0410 // Force trump colour as Jacks count as Trump 0411 if (type == Jack) suite = mTrump; 0412 0413 // Check whether current card matches the first player card 0414 if (suite == suite1) 0415 { 0416 validMove = false; 0417 break; 0418 } 0419 } 0420 return validMove; 0421 } 0422 0423 // Check who won a move, the first or the second card. 0424 // The first card was played first and take precedence 0425 // when possible. The function returns 0 if the first 0426 // card won, 1 if the second card won. 0427 int EngineTwo::whoWonMove(int card1, int card2, Suite trump) 0428 { 0429 Suite suite1 = Deck::getSuite(card1); 0430 Suite suite2 = Deck::getSuite(card2); 0431 CardType type1 = Deck::getCardType(card1); 0432 CardType type2 = Deck::getCardType(card2); 0433 0434 // Two jacks 0435 if (type1 == Jack && type2 == Jack) 0436 { 0437 if (suite1 < suite2) return 0; 0438 else return 1; 0439 } 0440 // One Jack wins always 0441 if ((int)type1 == (int)Jack) return 0; 0442 if ((int)type2 == (int)Jack) return 1; 0443 0444 // Higher card wins if both have same suite 0445 if (suite1 == suite2) 0446 { 0447 // Check Ten because it is not in the right card 0448 // sequence. Ten is only beaten by Ace 0449 if (type1 == Ten) 0450 { 0451 if (type2 == Ace) return 1; 0452 else return 0; 0453 } 0454 if (type2 == Ten) 0455 { 0456 if (type1 == Ace) return 0; 0457 return 1; 0458 } 0459 0460 // Otherwise the higher card wins 0461 if ((int)card1 < (int)card2) return 0; 0462 return 1; 0463 } 0464 0465 if (global_debug > 0) 0466 { 0467 if (suite1 == trump) qCDebug(LSKAT_LOG) << "FIRST card wins TRUMP"; 0468 if (suite2 == trump) qCDebug(LSKAT_LOG) << "SECOND card wins TRUMP"; 0469 } 0470 0471 // If cards are not of the same suite a trump wins 0472 if (suite1 == trump) return 0; 0473 if (suite2 == trump) return 1; 0474 0475 // In all other cases the first card wins 0476 return 0; 0477 } 0478 0479 #include "moc_engine_two.cpp"