斗地主前1500行(c++11及以上)
2025-07-18 23:00:55
发布于:福建
#include <iostream>
#include <vector>
#include <algorithm>
#include <ctime>
#include <cstdlib>
#include <map>
#include <set>
#include <cctype>
#include <sstream>
#include <iomanip>
#include <cmath>
#include <queue>
#include <windows.h>
#include <unordered_map>
#include <unordered_set>
#include <functional>
#include <random>
#include <chrono>
using namespace std;
// 扑克牌类
class Card {
public:
enum Suit { HEART, DIAMOND, CLUB, SPADE, JOKER };
enum Rank { THREE = 3, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN, JACK, QUEEN, KING, ACE, TWO, L_JOKER, B_JOKER };
Suit suit;
Rank rank;
int id; // 添加唯一标识符
Card() : suit(HEART), rank(THREE), id(-1) {}
Card(Suit s, Rank r, int i) : suit(s), rank(r), id(i) {}
bool operator<(const Card& other) const {
if (rank != other.rank) return rank < other.rank;
return suit < other.suit;
}
bool operator==(const Card& other) const {
return id == other.id; // 使用ID比较
}
string toString() const {
static const string suits[] = {"红心", "方块", "黑桃", "梅花", "Joker"};
if (suit == JOKER) {
if (rank == L_JOKER) return "小王"; // 小王
if (rank == B_JOKER) return "大王"; // 大王
return "Joker";
}
string rankStr;
switch(rank) {
case THREE: rankStr = "3"; break;
case FOUR: rankStr = "4"; break;
case FIVE: rankStr = "5"; break;
case SIX: rankStr = "6"; break;
case SEVEN: rankStr = "7"; break;
case EIGHT: rankStr = "8"; break;
case NINE: rankStr = "9"; break;
case TEN: rankStr = "10"; break;
case JACK: rankStr = "J"; break;
case QUEEN: rankStr = "Q"; break;
case KING: rankStr = "K"; break;
case ACE: rankStr = "A"; break;
case TWO: rankStr = "2"; break;
default: rankStr = "?";
}
return suits[suit] + rankStr;
}
};
// 牌型枚举
enum class CardType {
ILLEGAL, // 非法牌型
SINGLE, // 单张
PAIR, // 对子
TRIPLET, // 三张
TRIPLET_WITH_SINGLE, // 三带一
TRIPLET_WITH_PAIR, // 三带二
STRAIGHT, // 顺子
STRAIGHT_PAIRS, // 连对
BOMB, // 炸弹
ROCKET, // 火箭
FOUR_WITH_TWO, // 四带二
PLANE, // 飞机(三顺)
PLANE_WITH_SINGLES, // 飞机带单
PLANE_WITH_PAIRS // 飞机带对
};
// 牌组信息
struct CardGroup {
CardType type;
vector<Card> cards;
int mainRank; // 主牌点数(用于比较)
int length = 0; // 牌型长度(顺子/连对/飞机)
int player = -1; // 出牌玩家
CardGroup() : type(CardType::ILLEGAL), mainRank(0) {}
};
// 记牌器类
class CardTracker {
private:
map<int, vector<Card::Rank>> playerKnownCards;
map<Card::Rank, int> publicPlayedCards;
vector<Card> initialDeck;
bool enabled = false;
public:
void setInitialDeck(const vector<Card>& deck) {
initialDeck = deck;
}
void enable(bool status) {
enabled = status;
}
void updatePlayedCards(int player, const vector<Card>& cards) {
for (const Card& card : cards) {
publicPlayedCards[card.rank]++;
playerKnownCards[player].push_back(card.rank);
}
}
double getKeyCardProbability(Card::Rank rank, int player) const {
int totalCount = (rank == Card::L_JOKER || rank == Card::B_JOKER) ? 1 : 4;
auto it = publicPlayedCards.find(rank);
int played = (it != publicPlayedCards.end()) ? it->second : 0;
auto playerIt = playerKnownCards.find(player);
if (playerIt != playerKnownCards.end()) {
const vector<Card::Rank>& knownCards = playerIt->second;
if (find(knownCards.begin(), knownCards.end(), rank) != knownCards.end()) {
return 0.0;
}
}
return max(0.0, (totalCount - played) / static_cast<double>(totalCount));
}
Card::Rank detectOpponentWeakness(int player) const {
for (int r = Card::THREE; r <= Card::TEN; r++) {
if (getKeyCardProbability(static_cast<Card::Rank>(r), player) > 0.8) {
return static_cast<Card::Rank>(r);
}
}
return Card::B_JOKER;
}
void displayRemainingCards(const vector<Card>& playerHand) const {
if (!enabled) return;
vector<Card> remainingCards = initialDeck;
for (const Card& card : playerHand) {
auto it = find(remainingCards.begin(), remainingCards.end(), card);
if (it != remainingCards.end()) {
remainingCards.erase(it);
}
}
for (const auto& entry : publicPlayedCards) {
Card::Rank rank = entry.first;
int count = entry.second;
for (int i = 0; i < count; i++) {
auto it = find_if(remainingCards.begin(), remainingCards.end(),
[rank](const Card& c) { return c.rank == rank; });
if (it != remainingCards.end()) {
remainingCards.erase(it);
}
}
}
map<Card::Rank, int> remainingCount;
for (const Card& card : remainingCards) {
remainingCount[card.rank]++;
}
cout << "\n=== 记牌器 (剩余牌) ===\n";
bool hasCards = false;
// 显示普通牌(3到2)
for (int r = Card::THREE; r <= Card::TWO; r++) {
Card::Rank rank = static_cast<Card::Rank>(r);
int count = 0;
if (remainingCount.find(rank) != remainingCount.end()) {
count = remainingCount[rank];
}
cout << cardRankToString(rank) << ": " << count << "张 ";
hasCards = true;
if ((r - Card::THREE + 1) % 8 == 0) cout << endl;
}
// 显示特殊牌(小王、大王)
if (remainingCount.find(Card::L_JOKER) != remainingCount.end() ||
remainingCount.find(Card::B_JOKER) != remainingCount.end()) {
hasCards = true;
int smallCount = (remainingCount.find(Card::L_JOKER) != remainingCount.end()) ? remainingCount[Card::L_JOKER] : 0;
int bigCount = (remainingCount.find(Card::B_JOKER) != remainingCount.end()) ? remainingCount[Card::B_JOKER] : 0;
cout << "\n小王: " << smallCount << "张 ";
cout << "大王: " << bigCount << "张 ";
}
if (!hasCards) {
cout << "无剩余牌";
}
cout << "\n=======================\n";
}
void reset() {
playerKnownCards.clear();
publicPlayedCards.clear();
}
private:
string cardRankToString(Card::Rank rank) const {
switch(rank) {
case Card::THREE: return "3";
case Card::FOUR: return "4";
case Card::FIVE: return "5";
case Card::SIX: return "6";
case Card::SEVEN: return "7";
case Card::EIGHT: return "8";
case Card::NINE: return "9";
case Card::TEN: return "10";
case Card::JACK: return "J";
case Card::QUEEN: return "Q";
case Card::KING: return "K";
case Card::ACE: return "A";
case Card::TWO: return "2";
default: return "?";
}
}
};
// 游戏状态类
class GameState {
public:
int landlordPosition = -1;
int lastWinner = -1;
int roundCount = 0;
bool isLandlordThreatened = false;
int bidScore = 0;
int lastPlayedPlayer = -1;
map<int, int> playerCardCount;
int landlordRemainingCards = 20;
bool isEndgame = false;
void checkEndgame() {
int minCards = min(min(playerCardCount[0], playerCardCount[1]), playerCardCount[2]);
isEndgame = (minCards <= 5);
}
};
// 玩家基类
class Player {
public:
vector<Card> hand;
string name;
bool isLandlord = false;
int position;
CardTracker cardTracker;
Player(const string& n, int pos) : name(n), position(pos) {}
virtual CardGroup playCard(const CardGroup& lastGroup, const GameState& state) = 0;
virtual int callLandlord(int currentMaxBid) = 0;
void updateTracker(int player, const vector<Card>& cards) {
cardTracker.updatePlayedCards(player, cards);
}
void sortHand() {
sort(hand.begin(), hand.end(), [](const Card& a, const Card& b) {
return a.rank > b.rank;
});
}
void printHand() const {
cout << name << "的手牌 (" << hand.size() << "张):\n";
for (int i = 0; i < hand.size(); i++) {
cout << "[" << i+1 << "]" << hand[i].toString() << " ";
if ((i+1) % 10 == 0) cout << endl;
}
cout << endl;
}
bool canPlayCard() const {
return !hand.empty();
}
void removeCards(const vector<Card>& cardsToRemove) {
for (const Card& card : cardsToRemove) {
auto it = find_if(hand.begin(), hand.end(), [&](const Card& c) {
return c.id == card.id;
});
if (it != hand.end()) {
hand.erase(it);
}
}
sortHand();
}
void addCards(const vector<Card>& cardsToAdd) {
hand.insert(hand.end(), cardsToAdd.begin(), cardsToAdd.end());
sortHand();
}
virtual double calculateHandStrength() const {
double strength = 0;
for (const Card& card : hand) {
if (card.rank >= Card::ACE) {
strength += 1.5;
} else if (card.rank >= Card::TEN) {
strength += 1.2;
} else {
strength += 1.0;
}
}
if (hasBomb()) strength += 3.0;
if (hasRocket()) strength += 4.0;
int straightLength = findLongestStraight();
if (straightLength >= 5) strength += straightLength * 0.5;
int pairCount = countPairs();
strength += pairCount * 0.3;
int tripletCount = countTriplets();
strength += tripletCount * 0.5;
return strength;
}
map<Card::Rank, int> getRankCount() const {
map<Card::Rank, int> rankCount;
for (const Card& card : hand) {
rankCount[card.rank]++;
}
return rankCount;
}
int getRankCountSafe(Card::Rank rank) const {
auto rankCount = getRankCount();
auto it = rankCount.find(rank);
return (it != rankCount.end()) ? it->second : 0;
}
vector<Card::Rank> getDistinctRanks() const {
set<Card::Rank> ranks;
for (const Card& card : hand) {
if (card.suit != Card::JOKER) {
ranks.insert(card.rank);
}
}
return vector<Card::Rank>(ranks.begin(), ranks.end());
}
void displayTracker() const {
cardTracker.displayRemainingCards(hand);
}
// 判断是否为大牌(A、2、王)
bool isHighValueCard(Card::Rank rank) const {
return rank >= Card::ACE || rank == Card::TWO;
}
// 判断是否为关键牌(2、王)
bool isCriticalCard(Card::Rank rank) const {
return rank == Card::TWO || rank == Card::L_JOKER || rank == Card::B_JOKER;
}
// 判断是否为小牌(3-9)
bool isLowValueCard(Card::Rank rank) const {
return rank >= Card::THREE && rank <= Card::NINE;
}
protected:
bool hasBomb() const {
auto rankCount = getRankCount();
for (const auto& entry : rankCount) {
if (entry.second == 4) {
return true;
}
}
return false;
}
bool hasRocket() const {
bool smallJoker = false, bigJoker = false;
for (const Card& card : hand) {
if (card.suit == Card::JOKER) {
if (card.rank == Card::L_JOKER) smallJoker = true;
if (card.rank == Card::B_JOKER) bigJoker = true;
}
}
return smallJoker && bigJoker;
}
int findLongestStraight() const {
vector<int> ranks;
for (const Card& card : hand) {
if (card.suit != Card::JOKER && card.rank < Card::TWO) {
ranks.push_back(card.rank);
}
}
if (ranks.empty()) return 0;
sort(ranks.begin(), ranks.end());
ranks.erase(unique(ranks.begin(), ranks.end()), ranks.end());
int maxLength = 1;
int currentLength = 1;
for (int i = 1; i < ranks.size(); i++) {
if (ranks[i] == ranks[i-1] + 1) {
currentLength++;
if (currentLength > maxLength) {
maxLength = currentLength;
}
} else {
currentLength = 1;
}
}
return maxLength;
}
int countPairs() const {
auto rankCount = getRankCount();
int pairCount = 0;
for (const auto& entry : rankCount) {
if (entry.second >= 2) {
pairCount++;
}
}
return pairCount;
}
int countTriplets() const {
auto rankCount = getRankCount();
int tripletCount = 0;
for (const auto& entry : rankCount) {
if (entry.second >= 3) {
tripletCount++;
}
}
return tripletCount;
}
bool isContinuousSequence(const vector<Card::Rank>& ranks, int minLength) const {
if (ranks.size() < minLength) return false;
for (int i = 1; i < ranks.size(); i++) {
if (ranks[i] != ranks[i-1] + 1) {
return false;
}
}
return true;
}
int countControlCards() const {
int count = 0;
for (const Card& card : hand) {
if (card.rank >= Card::ACE || card.suit == Card::JOKER) {
count++;
}
}
return count;
}
};
// 人类玩家
class HumanPlayer : public Player {
public:
HumanPlayer(int pos) : Player("你", pos) {}
int callLandlord(int currentMaxBid) override {
int choice;
cout << "\n当前最高叫分: " << currentMaxBid << "分";
cout << "\n请选择叫分 (0:不叫, 1:1分, 2:2分, 3:3分): ";
cin >> choice;
return choice;
}
CardGroup playCard(const CardGroup& lastGroup, const GameState& state) override {
if (hand.empty()) {
return CardGroup();
}
cout << "\n=== 你的回合 ===\n";
printHand();
displayTracker();
if (lastGroup.type != CardType::ILLEGAL) {
cout << "上家出牌: ";
for (const Card& card : lastGroup.cards) {
cout << card.toString() << " ";
}
cout << " (牌型: ";
switch(lastGroup.type) {
case CardType::SINGLE: cout << "单张"; break;
case CardType::PAIR: cout << "对子"; break;
case CardType::TRIPLET: cout << "三张"; break;
case CardType::TRIPLET_WITH_SINGLE: cout << "三带一"; break;
case CardType::TRIPLET_WITH_PAIR: cout << "三带二"; break;
case CardType::STRAIGHT: cout << "顺子"; break;
case CardType::STRAIGHT_PAIRS: cout << "连对"; break;
case CardType::BOMB: cout << "炸弹"; break;
case CardType::ROCKET: cout << "火箭"; break;
case CardType::FOUR_WITH_TWO: cout << "四带二"; break;
case CardType::PLANE: cout << "飞机"; break;
case CardType::PLANE_WITH_SINGLES: cout << "飞机带单"; break;
case CardType::PLANE_WITH_PAIRS: cout << "飞机带对"; break;
default: cout << "未知牌型";
}
cout << ")" << endl;
}
while (true) {
string input;
cout << "请选择要出的牌 (输入牌号, 空格分隔, 0跳过): ";
if (cin.peek() == '\n') cin.ignore();
getline(cin, input);
if (input == "0") {
cout << "你选择跳过\n";
return CardGroup();
}
vector<int> indices;
stringstream ss(input);
int index;
bool validInput = true;
unordered_set<int> uniqueIndices;
while (ss >> index) {
if (index < 1 || index > hand.size()) {
cout << "无效的牌号: " << index << ", 请重新输入\n";
validInput = false;
break;
}
if (uniqueIndices.find(index) != uniqueIndices.end()) {
cout << "不能重复选择同一张牌: " << index << ", 请重新输入\n";
validInput = false;
break;
}
uniqueIndices.insert(index);
indices.push_back(index - 1);
}
if (!validInput || indices.empty()) {
continue;
}
if (!ss.eof()) {
string remaining;
ss >> remaining;
cout << "输入包含无效内容: " << remaining << ", 请重新输入\n";
continue;
}
vector<Card> selected;
for (int idx : indices) {
selected.push_back(hand[idx]);
}
CardGroup group = checkCardType(selected);
if (group.type == CardType::ILLEGAL) {
cout << "不合法的牌型! 请重新选择\n";
continue;
}
if (lastGroup.type != CardType::ILLEGAL) {
if (group.type == CardType::ROCKET) {
return group;
}
if (group.type == CardType::BOMB) {
if (lastGroup.type == CardType::BOMB) {
if (group.mainRank > lastGroup.mainRank) {
return group;
} else {
cout << "你的炸弹不够大!" << endl;
continue;
}
} else if (lastGroup.type != CardType::ROCKET) {
return group;
}
}
if (group.type != lastGroup.type ||
group.cards.size() != lastGroup.cards.size() ||
group.mainRank <= lastGroup.mainRank) {
cout << "你的牌型不能压过上家! 请重新选择\n";
continue;
}
}
return group;
}
}
private:
CardGroup checkCardType(const vector<Card>& cards) {
CardGroup group;
group.cards = cards;
sort(group.cards.begin(), group.cards.end(), [](const Card& a, const Card& b) {
return a.rank < b.rank;
});
int size = cards.size();
map<Card::Rank, int> rankCount;
for (const Card& card : cards) {
rankCount[card.rank]++;
}
if (size == 1) {
group.type = CardType::SINGLE;
group.mainRank = cards[0].rank;
return group;
}
if (size == 2) {
if (cards[0].rank == cards[1].rank) {
group.type = CardType::PAIR;
group.mainRank = cards[0].rank;
return group;
}
if (cards[0].suit == Card::JOKER && cards[1].suit == Card::JOKER) {
group.type = CardType::ROCKET;
group.mainRank = Card::B_JOKER;
return group;
}
}
if (size == 3) {
if (cards[0].rank == cards[1].rank && cards[1].rank == cards[2].rank) {
group.type = CardType::TRIPLET;
group.mainRank = cards[0].rank;
return group;
}
}
if (size == 4) {
if (cards[0].rank == cards[1].rank &&
cards[1].rank == cards[2].rank &&
cards[2].rank == cards[3].rank) {
group.type = CardType::BOMB;
group.mainRank = cards[0].rank;
return group;
}
}
if (size == 4) {
bool hasTriplet = false;
Card::Rank tripletRank = Card::THREE;
for (const auto& entry : rankCount) {
if (entry.second == 3) {
hasTriplet = true;
tripletRank = entry.first;
break;
}
}
if (hasTriplet) {
group.type = CardType::TRIPLET_WITH_SINGLE;
group.mainRank = tripletRank;
return group;
}
}
if (size == 5) {
bool hasTriplet = false;
bool hasPair = false;
Card::Rank tripletRank = Card::THREE;
for (const auto& entry : rankCount) {
if (entry.second == 3) {
hasTriplet = true;
tripletRank = entry.first;
} else if (entry.second == 2) {
hasPair = true;
}
}
if (hasTriplet && hasPair) {
group.type = CardType::TRIPLET_WITH_PAIR;
group.mainRank = tripletRank;
return group;
}
}
if (size >= 5) {
bool isStraight = true;
for (int i = 1; i < size; i++) {
if (group.cards[i].rank != group.cards[i-1].rank + 1 ||
group.cards[i].rank >= Card::TWO ||
group.cards[i-1].rank >= Card::TWO) {
isStraight = false;
break;
}
}
if (isStraight) {
group.type = CardType::STRAIGHT;
group.mainRank = group.cards.back().rank;
group.length = size;
return group;
}
}
if (size >= 6 && size % 2 == 0) {
bool isStraightPair = true;
for (int i = 0; i < size; i += 2) {
if (i + 1 >= size || group.cards[i].rank != group.cards[i+1].rank) {
isStraightPair = false;
break;
}
if (i + 2 < size) {
if (group.cards[i+2].rank != group.cards[i].rank + 1 ||
group.cards[i].rank >= Card::TWO) {
isStraightPair = false;
break;
}
}
}
if (isStraightPair) {
group.type = CardType::STRAIGHT_PAIRS;
group.mainRank = cards.back().rank;
group.length = size / 2;
return group;
}
}
int tripletCount = size / 3;
if (tripletCount >= 2 && size % 3 == 0) {
bool isPlane = true;
for (int i = 0; i < tripletCount; i++) {
int idx = i * 3;
if (!(group.cards[idx].rank == group.cards[idx+1].rank &&
group.cards[idx+1].rank == group.cards[idx+2].rank)) {
isPlane = false;
break;
}
if (i < tripletCount - 1) {
if (group.cards[idx+3].rank != group.cards[idx].rank + 1 ||
group.cards[idx].rank >= Card::TWO) {
isPlane = false;
break;
}
}
}
if (isPlane) {
group.type = CardType::PLANE;
group.mainRank = cards.back().rank;
group.length = tripletCount;
return group;
}
}
if (tripletCount >= 2 && size == tripletCount * 4) {
vector<Card::Rank> triplets;
vector<Card::Rank> singles;
for (const auto& entry : rankCount) {
if (entry.second == 3) {
triplets.push_back(entry.first);
} else if (entry.second == 1) {
singles.push_back(entry.first);
}
}
if (triplets.size() == tripletCount) {
sort(triplets.begin(), triplets.end());
bool isContinuous = true;
for (int i = 1; i < tripletCount; i++) {
if (triplets[i] != triplets[i-1] + 1 ||
triplets[i] >= Card::TWO) {
isContinuous = false;
break;
}
}
if (isContinuous) {
group.type = CardType::PLANE_WITH_SINGLES;
group.mainRank = triplets.back();
group.length = tripletCount;
return group;
}
}
}
if (tripletCount >= 2 && size == tripletCount * 5) {
vector<Card::Rank> triplets;
vector<Card::Rank> pairs;
for (const auto& entry : rankCount) {
if (entry.second == 3) {
triplets.push_back(entry.first);
} else if (entry.second == 2) {
pairs.push_back(entry.first);
}
}
if (triplets.size() == tripletCount && pairs.size() == tripletCount) {
sort(triplets.begin(), triplets.end());
bool isContinuous = true;
for (int i = 1; i < tripletCount; i++) {
if (triplets[i] != triplets[i-1] + 1 ||
triplets[i] >= Card::TWO) {
isContinuous = false;
break;
}
}
if (isContinuous) {
group.type = CardType::PLANE_WITH_PAIRS;
group.mainRank = triplets.back();
group.length = tripletCount;
return group;
}
}
}
if (size == 6) {
bool hasFour = false;
Card::Rank fourRank = Card::THREE;
int otherCount = 0;
for (const auto& entry : rankCount) {
if (entry.second == 4) {
hasFour = true;
fourRank = entry.first;
} else {
otherCount += entry.second;
}
}
if (hasFour && otherCount == 2) {
group.type = CardType::FOUR_WITH_TWO;
group.mainRank = fourRank;
return group;
}
}
group.type = CardType::ILLEGAL;
return group;
}
};
// AI玩家(增强版)
class AIPlayer : public Player {
private:
GameState currentGameState;
mt19937 rng;
public:
AIPlayer(const string& n, int pos) : Player(n, pos) {
rng.seed(static_cast<unsigned>(time(0)));
}
int callLandlord(int currentMaxBid) override {
double handStrength = calculateHandStrength();
int bid = 0;
if (handStrength < 18) {
bid = (rng() % 10 == 0) ? 1 : 0;
} else if (handStrength < 22) {
bid = 1 + (rng() % 2);
} else if (handStrength < 26) {
bid = 2 + (rng() % 2);
} else {
bid = 3;
}
if (bid <= currentMaxBid) {
bid = 0;
}
cout << name << (bid > 0 ? " 叫" : " 不叫") << (bid > 0 ? to_string(bid) + "分" : "") << endl;
return bid;
}
CardGroup playCard(const CardGroup& lastGroup, const GameState& state) override {
currentGameState = state;
cout << "\n=== " << name << "的回合 ===\n";
cout << name << " 正在思考...\n";
for (int i = 0; i < 3; i++) {
cout << ". ";
cout.flush();
Sleep(500);
}
cout << endl;
if (lastGroup.player != -1) {
updateTracker(lastGroup.player, lastGroup.cards);
}
// 残局策略优先
if (currentGameState.isEndgame) {
CardGroup endgameGroup = handleEndgame(lastGroup, currentGameState);
if (endgameGroup.type != CardType::ILLEGAL) {
return endgameGroup;
}
}
// 主动出牌策略
if (lastGroup.type == CardType::ILLEGAL) {
CardGroup aggroGroup = aggressivePlay(currentGameState);
if (aggroGroup.type != CardType::ILLEGAL) {
return aggroGroup;
}
return playFirstCard();
}
// 跟牌逻辑(增强版)
return optimizedFollow(lastGroup, currentGameState);
}
private:
// 判断是否处于中期(手牌较多)
bool isMidGame() const {
return hand.size() > 8;
}
// 判断是否处于后期(手牌较少)
bool isLateGame() const {
return hand.size() <= 5;
}
// 核心优化:地主剩余牌数策略
CardGroup handleEndgame(const CardGroup& lastGroup, const GameState& state) {
// 地主剩余2张牌策略:先打单牌试探
if (!isLandlord && state.landlordRemainingCards == 2) {
// 如果是农民且地主剩2张牌
if (lastGroup.type == CardType::ILLEGAL) {
// 轮到自己出牌,出单牌试探
return tryPlaySingleToTestLandlord();
}
else if (lastGroup.type == CardType::SINGLE) {
// 上家出单牌,尝试出比上家大的单牌
CardGroup group = tryFollowSingle(lastGroup, state, false);
if (group.type != CardType::ILLEGAL) {
return group;
}
}
}
// 地主剩余1张牌策略:避免出单牌,尤其是地主上家要顶大牌
if (!isLandlord && state.landlordRemainingCards == 1) {
// 地主上家策略(位置0是地主上家)
if (position == 0) {
if (lastGroup.type == CardType::ILLEGAL) {
// 轮到自己出牌,避免出单牌
return tryPlayNonSingle();
}
else if (lastGroup.type == CardType::SINGLE) {
// 顶大牌阻止地主出牌
return tryBlockLandlordWithHighCard(lastGroup);
}
}
// 地主下家策略(位置2是地主下家)
else if (position == 2) {
if (lastGroup.type == CardType::SINGLE) {
// 如果能接牌就接,否则跳过
return tryFollow(lastGroup, state);
}
}
}
// 当手牌少于5张时的通用策略
if (isLateGame()) {
if (tryPlayBomb().type != CardType::ILLEGAL) {
return tryPlayBomb();
}
CardGroup group = tryPlayStraightOrPairs();
if (group.type != CardType::ILLEGAL) {
return group;
}
return playHighValueControlCard();
}
// 地主策略:压制关键玩家
if (isLandlord) {
int minCardsPlayer = -1;
int minCards = 100;
for (int i = 0; i < 3; i++) {
if (i != position && state.playerCardCount.find(i) != state.playerCardCount.end() &&
state.playerCardCount.at(i) < minCards) {
minCards = state.playerCardCount.at(i);
minCardsPlayer = i;
}
}
if (minCards <= 3) {
return tryBlockPlayer(minCardsPlayer);
}
}
return CardGroup();
}
// 尝试出单牌试探地主(用于地主剩2张牌时)
CardGroup tryPlaySingleToTestLandlord() {
// 优先出中等牌(10-K),避免出太小或太大的牌
for (int r = Card::TEN; r <= Card::KING; r++) {
Card::Rank rank = static_cast<Card::Rank>(r);
if (getRankCountSafe(rank) > 0) {
vector<Card> cardToPlay;
for (const Card& c : hand) {
if (c.rank == rank) {
cardToPlay.push_back(c);
break;
}
}
CardGroup group;
group.type = CardType::SINGLE;
group.cards = cardToPlay;
group.mainRank = rank;
return group;
}
}
// 其次出小牌(3-9)
for (int r = Card::THREE; r <= Card::NINE; r++) {
Card::Rank rank = static_cast<Card::Rank>(r);
if (getRankCountSafe(rank) > 0) {
vector<Card> cardToPlay;
for (const Card& c : hand) {
if (c.rank == rank) {
cardToPlay.push_back(c);
break;
}
}
CardGroup group;
group.type = CardType::SINGLE;
group.cards = cardToPlay;
group.mainRank = rank;
return group;
}
}
// 实在没有合适的牌,出最小单牌
vector<Card> cardToPlay;
cardToPlay.push_back(hand.back());
CardGroup group;
group.type = CardType::SINGLE;
group.cards = cardToPlay;
group.mainRank = hand.back().rank;
return group;
}
// 尝试出非单牌(用于地主剩1张牌时)
CardGroup tryPlayNonSingle() {
// 优先出对子
CardGroup group = tryPlaySmallPair();
if (group.type != CardType::ILLEGAL) {
return group;
}
// 其次尝试出顺子
group = tryPlayStraight();
if (group.type != CardType::ILLEGAL) {
return group;
}
// 再尝试三张
group = tryPlayTriplet();
if (group.type != CardType::ILLEGAL) {
return group;
}
// 实在没有非单牌,出最小单牌
vector<Card> cardToPlay;
cardToPlay.push_back(hand.back());
group.type = CardType::SINGLE;
group.cards = cardToPlay;
group.mainRank = hand.back().rank;
return group;
}
// 尝试用大牌顶地主(地主上家用)
CardGroup tryBlockLandlordWithHighCard(const CardGroup& lastGroup) {
// 找出所有能压过的牌
vector<Card> candidates;
for (const Card& card : hand) {
if (card.rank > lastGroup.mainRank) {
candidates.push_back(card);
}
}
if (candidates.empty()) {
return CardGroup();
}
// 优先选择大牌(A、2、王)
vector<Card> highCards;
for (const Card& c : candidates) {
if (isHighValueCard(c.rank)) {
highCards.push_back(c);
}
}
if (!highCards.empty()) {
// 选择最大的牌
sort(highCards.begin(), highCards.end());
vector<Card> cardToPlay;
cardToPlay.push_back(highCards.back());
CardGroup group;
group.type = CardType::SINGLE;
group.cards = cardToPlay;
group.mainRank = highCards.back().rank;
return group;
}
// 没有大牌,选择最小的能压过的牌
sort(candidates.begin(), candidates.end(), [](const Card& a, const Card& b) {
return a.rank < b.rank;
});
CardGroup group;
group.type = CardType::SINGLE;
group.cards.push_back(candidates[0]);
group.mainRank = candidates[0].rank;
return group;
}
// 尝试出三张
CardGroup tryPlayTriplet() {
CardGroup group;
map<Card::Rank, int> rankCount = getRankCount();
Card::Rank minTripletRank = Card::B_JOKER;
for (const auto& entry : rankCount) {
if (entry.second >= 3 && entry.first < minTripletRank) {
minTripletRank = entry.first;
}
}
if (minTripletRank != Card::B_JOKER) {
int count = 0;
for (const Card& c : hand) {
if (c.rank == minTripletRank) {
group.cards.push_back(c);
count++;
if (count == 3) break;
}
}
group.type = CardType::TRIPLET;
group.mainRank = minTripletRank;
}
return group;
}
CardGroup tryBlockPlayer(int targetPlayer) {
Card::Rank weakRank = cardTracker.detectOpponentWeakness(targetPlayer);
if (weakRank != Card::B_JOKER) {
auto rankCount = getRankCount();
for (int r = weakRank + 1; r <= Card::ACE; r++) {
Card::Rank rank = static_cast<Card::Rank>(r);
auto it = rankCount.find(rank);
if (it != rankCount.end() && it->second > 0) {
vector<Card> cardToPlay;
for (const Card& c : hand) {
if (c.rank == rank) {
cardToPlay.push_back(c);
break;
}
}
CardGroup group;
group.type = CardType::SINGLE;
group.cards = cardToPlay;
group.mainRank = rank;
return group;
}
}
}
vector<Card> cardToPlay;
cardToPlay.push_back(hand.back());
CardGroup group;
group.type = CardType::SINGLE;
group.cards = cardToPlay;
group.mainRank = hand.back().rank;
return group;
}
// 主动出牌策略增强
CardGroup aggressivePlay(const GameState& state) {
if (canFinishInTwoRounds()) {
CardGroup bomb = tryPlayBomb();
if (bomb.type != CardType::ILLEGAL) return bomb;
return playHighValueControlCard();
}
if (isMidGame()) {
return playMidGameCard();
}
return playSetupCard();
}
// 新增:中期出牌策略(优先出中小牌)
CardGroup playMidGameCard() {
for (int r = Card::THREE; r <= Card::TEN; r++) {
Card::Rank rank = static_cast<Card::Rank>(r);
if (getRankCountSafe(rank) > 0) {
vector<Card> cardToPlay;
for (const Card& c : hand) {
if (c.rank == rank) {
cardToPlay.push_back(c);
break;
}
}
CardGroup group;
group.type = CardType::SINGLE;
group.cards = cardToPlay;
group.mainRank = rank;
return group;
}
}
return tryPlaySmallPair();
}
bool canFinishInTwoRounds() {
if (hand.size() <= 4) {
CardGroup group = tryPlayStraightOrPairs();
if (group.type != CardType::ILLEGAL && group.cards.size() >= hand.size() - 2)
return true;
if (hasBomb() && hand.size() <= 5) return true;
}
return false;
}
CardGroup playSetupCard() {
for (int r = Card::JACK; r <= Card::KING; r++) {
Card::Rank rank = static_cast<Card::Rank>(r);
if (getRankCountSafe(rank) > 0) {
vector<Card> cardToPlay;
for (const Card& c : hand) {
if (c.rank == rank) {
cardToPlay.push_back(c);
break;
}
}
CardGroup group;
group.type = CardType::SINGLE;
group.cards = cardToPlay;
group.mainRank = rank;
return group;
}
}
return tryPlaySmallPair();
}
CardGroup playHighValueControlCard() {
vector<Card> highCards;
for (const Card& c : hand) {
if (c.rank >= Card::ACE || c.suit == Card::JOKER) {
highCards.push_back(c);
}
}
if (!highCards.empty()) {
sort(highCards.begin(), highCards.end());
vector<Card> cardToPlay;
cardToPlay.push_back(highCards.back());
CardGroup group;
group.type = CardType::SINGLE;
group.cards = cardToPlay;
group.mainRank = highCards.back().rank;
return group;
}
map<Card::Rank, int> rankCount = getRankCount();
Card::Rank maxPairRank = Card::THREE;
for (const auto& entry : rankCount) {
if (entry.second >= 2 && entry.first > maxPairRank) {
maxPairRank = entry.first;
}
}
if (maxPairRank > Card::THREE) {
vector<Card> cards;
for (const Card& c : hand) {
if (c.rank == maxPairRank) {
cards.push_back(c);
if (cards.size() == 2) break;
}
}
CardGroup group;
group.type = CardType::PAIR;
group.cards = cards;
group.mainRank = maxPairRank;
return group;
}
return CardGroup();
}
// 增强跟牌策略
CardGroup optimizedFollow(const CardGroup& lastGroup, const GameState& state) {
// 关键改进:农民不压制队友的牌
if (!isLandlord) {
int lastPlayer = lastGroup.player;
// 上家是队友(同为农民)
if (lastPlayer != -1 && lastPlayer != position && lastPlayer != state.landlordPosition) {
// 除非手牌很少或能直接获胜,否则不压制队友
if (hand.size() > 3) {
// 队友出牌后,自己尽量不出牌
return CardGroup();
}
}
}
if (isLandlord && lastGroup.type != CardType::BOMB &&
cardTracker.getKeyCardProbability(Card::ACE, 1) > 0.5) {
if (hasBomb() && hand.size() > 8) {
return CardGroup();
}
}
CardGroup group = tryFollow(lastGroup, state);
if (group.type != CardType::ILLEGAL) {
return group;
}
if (lastGroup.type != CardType::ROCKET) {
if (hasRocket()) {
vector<Card> rocketCards;
for (const Card& c : hand) {
if (c.suit == Card::JOKER) {
rocketCards.push_back(c);
if (rocketCards.size() == 2) break;
}
}
if (rocketCards.size() == 2) {
CardGroup group;
group.type = CardType::ROCKET;
group.cards = rocketCards;
group.mainRank = Card::B_JOKER;
return group;
}
}
if (lastGroup.type != CardType::BOMB) {
group = tryPlayBomb();
if (group.type != CardType::ILLEGAL) {
return group;
}
} else {
group = tryPlayBiggerBomb(lastGroup.mainRank);
if (group.type != CardType::ILLEGAL) {
return group;
}
}
}
return CardGroup();
}
CardGroup tryPlayBiggerBomb(int lastBombRank) {
CardGroup group;
map<Card::Rank, int> rankCount = getRankCount();
for (const auto& entry : rankCount) {
if (entry.second == 4 && entry.first > lastBombRank) {
for (const Card& c : hand) {
if (c.rank == entry.first) {
group.cards.push_back(c);
}
}
group.type = CardType::BOMB;
group.mainRank = entry.first;
return group;
}
}
return group;
}
CardGroup tryPlayStraightOrPairs() {
CardGroup group = tryPlayStraightPairs();
if (group.type != CardType::ILLEGAL) {
return group;
}
group = tryPlayStraight();
if (group.type != CardType::ILLEGAL) {
return group;
}
return group;
}
CardGroup tryPlayPair() {
CardGroup group;
map<Card::Rank, int> rankCount = getRankCount();
Card::Rank minPairRank = Card::B_JOKER;
for (const auto& entry : rankCount) {
if (entry.second >= 2 && entry.first < minPairRank) {
minPairRank = entry.first;
}
}
if (minPairRank != Card::B_JOKER) {
int count = 0;
for (const Card& c : hand) {
if (c.rank == minPairRank) {
group.cards.push_back(c);
count++;
if (count == 2) break;
}
}
group.type = CardType::PAIR;
group.mainRank = minPairRank;
}
return group;
}
CardGroup playFirstCard() {
CardGroup group;
group = tryPlayStraightPairs();
if (group.type != CardType::ILLEGAL) {
return group;
}
group = tryPlayStraight();
if (group.type != CardType::ILLEGAL) {
return group;
}
group = tryPlaySmallPair();
if (group.type != CardType::ILLEGAL) {
return group;
}
group.cards.push_back(hand.back());
group.type = CardType::SINGLE;
group.mainRank = hand.back().rank;
return group;
}
CardGroup tryFollow(const CardGroup& lastGroup, const GameState& state, bool conservative = true) {
CardGroup group;
switch (lastGroup.type) {
case CardType::SINGLE:
group = tryFollowSingle(lastGroup, state, conservative);
break;
case CardType::PAIR:
group = tryFollowPair(lastGroup, state, conservative);
break;
case CardType::TRIPLET:
group = tryFollowTriplet(lastGroup, state, conservative);
break;
case CardType::STRAIGHT:
group = tryFollowStraight(lastGroup, state);
break;
case CardType::STRAIGHT_PAIRS:
group = tryFollowStraightPairs(lastGroup, state);
break;
case CardType::PLANE:
case CardType::PLANE_WITH_SINGLES:
case CardType::PLANE_WITH_PAIRS:
group = tryFollowPlane(lastGroup, state);
break;
case CardType::TRIPLET_WITH_SINGLE:
group = tryFollowTripletWithSingle(lastGroup, state);
break;
case CardType::TRIPLET_WITH_PAIR:
group = tryFollowTripletWithPair(lastGroup, state);
break;
case CardType::FOUR_WITH_TWO:
group = tryFollowFourWithTwo(lastGroup, state);
全部评论 3
1
2025-07-15 来自 福建
1%%%
2025-07-19 来自 上海
02025-07-15 来自 浙江
0确实有bug
明天改好2025-07-15 来自 福建
2
有帮助,赞一个