File indexing completed on 2024-04-14 04:02:24

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"