Beispiel #1
0
bool Engine::playCurrentRound()
{
#define CHECK_QUIT \
if (mQuit.get()) { \
    return false; \
}

    lock();
    if (!mCurrentRoundIndex) {
        mCurrentRoundIndex = &mRoundIndex;
        // prepare round data
        // pick current player as first attacker
        mAttackers.push_back(mCurrentPlayer);
        // if there was no deal yet (very first round) - do not consider cards while picking next players
        std::map<const PlayerId*, CardSet>* cards = *mCurrentRoundIndex ? &mPlayersCards : NULL;
        // pick next player as defender
        mDefender = Rules::pickNext(mGeneratedIds, mCurrentPlayer, cards);
        // gather rest players as additional attackers
        const PlayerId* attacker = mDefender;
        while((attacker = Rules::pickNext(mGeneratedIds, attacker, cards)) != mCurrentPlayer) {
            if (attacker) {
                mAttackers.push_back(attacker);
            }
        }
        unlock();
        std::for_each(mGameObservers.begin(), mGameObservers.end(), RoundStartNotification(mAttackers, mDefender, mRoundIndex));
        // deal cards
        dealCards();
        lock();
        mMaxAttackCards = Rules::maxAttackCards(mPlayersCards[mDefender].size());
        assert(mMaxAttackCards);
    }

    CardSet& defenderCards = mPlayersCards[mDefender];

    if (!mCurrentRoundAttackerId) {
        mCurrentRoundAttackerId = mAttackers[0];
        mPassedCounter = 0;
    }

    Player& defender = *mPlayers[mDefender];

    unlock();

    for (;;) {

        if (mTableCards.attackCards().size() == mMaxAttackCards) {
            // defender has no more cards - defend succeeded
            break;
        }

        const Card* attackCardPtr;

        if (mPickAttackCardFromTable) {
            assert(!mTableCards.attackCards().empty());
            attackCardPtr = &*(mTableCards.attackCards().end() - 1);
        } else {
            CardSet attackCards = Rules::getAttackCards(mTableCards.all(), mPlayersCards[mCurrentRoundAttackerId]);

            Player& currentAttacker = *mPlayers[mCurrentRoundAttackerId];

            if (mTableCards.empty()) {
                attackCardPtr = attackCards.empty() ? NULL : &currentAttacker.attack(mDefender, attackCards);
            } else {
                // ask for pitch even with empty attackCards - expected NULL attack card pointer
                attackCardPtr = currentAttacker.pitch(mDefender, attackCards);
            }

            // check if quit requested and only after that transfer move to defender
            CHECK_QUIT;

            if (attackCards.empty() || !attackCardPtr) {
                lock();
                // player skipped the move - pick next attacker
                mCurrentRoundAttackerId = Rules::pickNext(mAttackers, mCurrentRoundAttackerId, &mPlayersCards);
                // if more than one attacker and we have first attacker again - reset pass counter
                if (mAttackers.size() > 1 && mCurrentRoundAttackerId == mAttackers[0]) {
                    mPassedCounter = 0;
                }
                mPassedCounter++;
                unlock();

                if (mPassedCounter == mAttackers.size()) {
                    // all attackers "passed" - round ended
                    break;
                }
                continue;
            }

            assert(attackCardPtr);

            if(!findByPtr(attackCards, attackCardPtr)) {
                // invalid card returned - the card is not from attackCards
                assert(!attackCards.empty());
                // take any card
                attackCardPtr = &*attackCards.begin();
            }

            Card attackCard = *attackCardPtr;

            lock();
            mTableCards.addAttackCard(attackCard);
            mPlayersCards[mCurrentRoundAttackerId].erase(attackCard);
            unlock();
            CHECK_QUIT;
            std::for_each(mGameObservers.begin(), mGameObservers.end(), CardsDroppedNotification(mCurrentRoundAttackerId, attackCard));
            // the card is removed from the `attackCards` and is added to `mTableCards`, so update its pointer
            attackCardPtr = &*std::find(mTableCards.attackCards().begin(), mTableCards.attackCards().end(), attackCard);
        }

        if (mDefendFailed) {
            continue;
        }

        CardSet defendCards = Rules::getDefendCards(*attackCardPtr, defenderCards, mDeck->trumpSuit());

        const Card* defendCardPtr = defender.defend(mCurrentRoundAttackerId, *attackCardPtr, defendCards);

        bool noCardsToDefend = defendCards.empty();
        bool userGrabbedCards = !defendCardPtr;
        bool invalidDefendCard = !findByPtr(defendCards, defendCardPtr);

        lock();
        mPickAttackCardFromTable = false;
        unlock();

        if(noCardsToDefend || userGrabbedCards || invalidDefendCard) {
            // defend failed
            lock();
            mDefendFailed = true;
            unlock();
        } else {
            lock();
            mTableCards.addDefendCard(*defendCardPtr);
            defenderCards.erase(*defendCardPtr);
            unlock();
            CHECK_QUIT;
            std::for_each(mGameObservers.begin(), mGameObservers.end(), CardsDroppedNotification(mDefender, *defendCardPtr));
        }
    }

    if (mDefendFailed) {
        lock();
        defenderCards.insert(mTableCards.all().begin(), mTableCards.all().end());
        defender.cardsUpdated(defenderCards);
        unlock();
        CHECK_QUIT;
        std::for_each(mGameObservers.begin(), mGameObservers.end(), CardsReceivedNotification(mDefender, mTableCards.all()));
    } else {
        std::for_each(mGameObservers.begin(), mGameObservers.end(), CardsGoneNotification(mTableCards.all()));
    }

    // cleanup
    lock();
    mCurrentRoundAttackerId = NULL;
    mMaxAttackCards = 0;
    mCurrentRoundIndex = NULL;
    unlock();
    CHECK_QUIT;

    std::for_each(mGameObservers.begin(), mGameObservers.end(), RoundEndNotification(mRoundIndex));

    return !mDefendFailed;
}