#include #include #include #include #include #include bool Card::operator==(const Card &other) const { return other.rank == rank && other.suit == suit; } bool rank_higher(Rank a, Rank b) { // Return true if a is higher than b // Regard ACE as 14 if (a == ACE && b != ACE) { return true; } if (b != ACE && a > b) { return true; } return false; } bool can_beat(const Card &tobeat, const Card &beater, Suit trump) { if (!is_card(tobeat) || !is_card(beater)) { return false; } if (tobeat.suit == beater.suit) { return rank_higher(beater.rank, tobeat.rank); } if (beater.suit == trump) { return true; } return false; } bool is_card(const Card &card) { return card.suit != NOSUIT && card.rank != NORANK; } void add_to_deck(std::vector deck) { int rank; Card card; for (rank = ACE; rank <= KING; rank += 1) { card.rank = static_cast(rank); card.suit = HEARTS; deck.push_back(card); card.suit = DIAMONDS; deck.push_back(card); card.suit = CLUBS; deck.push_back(card); card.suit = SPADES; deck.push_back(card); } } void shuffle_deck(std::vector deck) { int i; int n = deck.size() Random rng; while (--n > 0) { int k = rng.random_in_range(0, n); Card temp = deck.at(n); deck.at(n) = deck.at(k); deck.at(k) = temp; } } /* Checking following things: - atleast two players with cards left */ bool game_ended(const GameState &gs) { int i; int count = 0; for (i = 0; i < gs.players.size(); i++) { if (gs.players.at(i).hand.size() != 0) { count += 1; } } return count < 2; } int next_turn(const GameState &gs) { int i; for (i = gs.turn + 1; i != gs.turn; i = (i + 1) % gs.players.size()) { if (gs.players.at(i).active) { return i; } } debug(CRITICAL, "No next turn possible!"); return gs.turn; } bool all_beaten(const std::vector &table) { int i; for (i = 0; i < table.size(); i++) { if (!is_card(table.at(i).beater)) { return false; } } return true; } void remove_card(std::vector &cards, Card card) { std::vector::iterator iter; for (iter = cards.begin(); iter < cards.end(); iter++) { if (*iter == card) { cards.erase(iter); return; } } debug(CRITICAL, "Card to remove not found on list"); } void fill_hands(GameState &gs) { int i, need; for (i = gs.turn + 1; i != gs.turn; i = (i + 1) % gs.players.size()) { if (!gs.players.at(i).active) { continue; } need = gs.cards_in_hand - gs.players.at(i).hand.size(); while (need > 0 && gs.deck.size() != 0) { gs.players.at(i).hand.push_back(gs.deck.back()); gs.deck.pop_back(); } } } void check_winners(GameState &gs) { int i; if (gs.deck.size() != 0) { return; } for (i = 0; i < gs.players.size(); i++) { if (gs.players.at(i).hand.size() == 0 && !gs.players.at(i).active) { gs.winners.push_back(i); gs.players.at(i).active = false; } } } bool may_add_card(const GameState &gs, const Card &card) { // Count of unbeaten cards int i; int count = 0; for (i = 0; i < gs.table.size(); i++) { if (! is_card(gs.table.at(i).beater)) { count += 1; } } if (count >= gs.players.at(gs.turn).hand.size()) { // Too many if we add one return false; } // Rank exists on table for (i = 0; i < gs.table.size(); i++) { if (gs.table.at(i).beater.rank == card.rank) return true; if (gs.table.at(i).tobeat.rank == card.rank) return true; } return false; } // Card to beat on table, not beaten already bool card_on_table(const std::vector &table, const Card &card) { int i; for (i = 0; i < table.size(); i++) { if (table.at(i).tobeat == card && !is_card(table.at(i).beater)) { return true; } } debug(INFO, "Card not on table"); return false; } // One rank only and none beaten bool one_rank_only(const std::vector &table, Rank rank) { int i; for (i = 0; i < table.size(); i++) { if (is_card(table.at(i).beater) || table.at(i).tobeat.rank != rank) { return false; } } return true; } /* For all but pickup: - Player must have owncard in hand For pickup: - Must be players turn, table may not be empty For hits: - If player's turn, table must be empty - If not player's turn: - there must be cards of the same rank already on table - there may not be more unbeaten cards on table than at beaters hand For beats: - Must be players turn, table may not be empty - Card to beat must be on table - Must be a beating combination For moves: - Must be players turn, table may not be empty - Table must contain cards of only one rank - No cards on table have been beaten - No more cards than next player has */ bool legal_move(const GameState &gs, const Move &move) { if (move.movetype != PICKUP && \ std::find(gs.players.at(move.player).hand.begin(), gs.players.at(move.player).hand.end(), move.owncard) == gs.players.at(move.player).hand.end()) { // Own card not in hand debug(INFO, "Card not in hand"); return false; } switch (move.movetype) { case PICKUP: return (gs.turn == move.player) && gs.table.size() != 0; case HIT: if (gs.turn == move.player) { return gs.table.size() == 0; } else { return may_add_card(gs, move.owncard); } case BEAT: return (gs.turn == move.player) && \ (gs.table.size() != 0) && \ card_on_table(gs.table, move.othercard) && \ can_beat(move.othercard, move.owncard, gs.trump); case MOVE: return (gs.turn == move.player) && \ (gs.table.size() != 0) && \ one_rank_only(gs.table, move.owncard.rank) && \ ((gs.table.size() + 1) <= \ gs.players.at(next_turn(gs)).hand.size()); default: debug(CRITICAL, "Invalid movetype!"); } return false; } void apply_move(GameState &gs, const Move &move) { int i = 0; TableCard tc; switch (move.movetype) { case PICKUP: for (i = 0; i < gs.table.size(); i++) { gs.players.at(move.player).hand.push_back(gs.table.at(i).tobeat); if (is_card(gs.table.at(i).beater)) gs.players.at(move.player).hand.push_back(gs.table.at(i).beater); } gs.table.clear(); fill_hands(gs); gs.turn = next_turn(gs); break; case HIT: tc.tobeat = move.owncard; tc.beater.suit = NOSUIT; tc.beater.rank = NORANK; gs.table.push_back(tc); if (gs.turn == move.player) { fill_hands(gs); gs.turn = next_turn(gs); } break; case BEAT: for (i = 0; i < gs.table.size(); i++) { if (gs.table.at(i).tobeat == move.othercard && !is_card(gs.table.at(i).beater)) { gs.table.at(i).beater = move.owncard; break; } } break; case MOVE: tc.tobeat = move.owncard; tc.beater.suit = NOSUIT; tc.beater.rank = NORANK; gs.table.push_back(tc); gs.turn = next_turn(gs); break; default: debug(CRITICAL, "Invalid movetype!"); } check_winners(gs); if (move.movetype != PICKUP) { remove_card(gs.players.at(move.player).hand, move.owncard); } }