#include #include #include #include #include #include #include #include #include using namespace std; int mymax(int *hands)//求主牌面 { int mx = 0; for (int i = 0; i < 15; i++) { if (hands[i] > mx) mx = hands[i]; } return mx; } int minpoker(int *hands) { int mx = mymax(hands); int j = 0; for (int i = 0; i < 15; i++) { if (hands[i] == mx) { j = i; break; } } return j; }//主牌面最小的牌 int calculate(int *put) { int n = 0; for (int i = 0; i < 15; i++) n = n + put[i]; return n; } int len(int *hands) { int n = 0; int mx = mymax(hands); for (int i = 0; i < 15; i++) { if (hands[i] == mx) n++; } return n; } bool judge1(int *obj) { int m = calculate(obj); int max = mymax(obj); int length = len(obj); int x = (m - max * length); if (x == 0) return true; else return false; } void back(int *hands, int *put) { for (int i = 0; i < 15; i++) { while (put[i] != 0) { put[i]--; hands[i]++; } } } bool straight(int *hands, int *obj, int *put) { int min = minpoker(obj); int objmax = mymax(obj); int length = len(obj); int number = 0; if (length == 1) { for (int i = min + 1; i < 15; i++) { if (hands[i] >= objmax) { for (int k = 0; k < objmax; k++) { put[i]++; hands[i]--; } return true; } } return false; } for (int i = min + 1; i < 12; i++) { if (hands[i] >= objmax) { number++; if (number >= length) { for (int j = i; j > i - length; j--) { for (int k = 0; k < objmax; k++) { put[j]++; hands[j]--; } } return true; } } else { number = 0; } } return false; } bool straight_attach(int *hands, int *obj, int *put) { int m = calculate(obj); int max = mymax(obj); int length = len(obj); int x = (m - length * max) / length; bool judge = straight(hands, obj, put); if (!judge || x == 0) return false; if (max == 4) { length = length * 2; x = x / 2; } int number = 0; for (int i = 0; i < 15; i++) { if (hands[i] >= x && put[i] == 0) { for (int k = 0; k < x; k++) { put[i]++; hands[i]--; } number++; } if (number == length) return true; } return false; } bool boomjudge(int *obj) { int m = calculate(obj); int length = len(obj); int max = mymax(obj); int x = (m - max * length); if (x == 0 && obj[max] == 4) return true; else return false; } bool boom(int *hands, int *obj, int *put) { int m = calculate(obj); back(hands, put); if (put[13] == 1 && put[14] == 1) return false; if (boomjudge(obj)) { int min = minpoker(obj); for (int i = min + 1; i < 12; i++) { if (hands[i] == 4) { put[i] = 4; hands[i] = 0; return true; } } } else { for (int i = 0; i < 12; i++) { if (hands[i] == 4) { put[i] = 4; hands[i] = 0; return true; } } } return false; } bool kingboom(int *hands, int *put) { back(hands, put); if (hands[13] != 0 && hands[14] != 0) { put[13]++; put[14]++; hands[13]--; hands[14]--; return true; } else return false; } bool kingboomjudge(int *hands) { int m = calculate(hands); if (m == 2) { if (hands[13] == 1 && hands[14] == 1) return true; else return false; } return false; } class Card : public string { public: static const int N_CARD_VALUES; static const int N_CARD_SUITS; static const string CARD_VALUES[]; static const string CARD_SUITS[]; Card(const char* str) :string(str) {}; Card() :string() {}; Card(string str) :string(str) {}; static vector get_new_deck(); // 重载操作符,使得牌面可以比较大小 bool operator <(const Card &other) const; bool operator >(const Card &other) const; }; class DDZPlayer { protected: string name; // 玩家名 int position; // 你的位置编号,0为地主,1为地主下家,2为地主上家 vector hand; // 手牌 int myhands[15] = { 0 }; int prePos; //上一个出牌的人位置编号,-1表示还没有人出过牌 vector prePlay; // 上一个出牌的人出了什么牌? int preput[15] = { 0 }; int myput[15] = { 0 }; int pmyput[15] = { 0 }; void start(); bool judge(); bool judge2(); void cdcards(vector &cards); bool legal(vector cards); // 判断当前打出cards是否合法 void substractFromHand(vector cards); // 从当前手牌中删去cards中的牌 public: DDZPlayer(string name); // 构造函数,初始化玩家名 string getName(); virtual void draw(Card card); // 将cards中的牌加入手牌 virtual void draw(vector cards); // 将cards中的牌加入手牌 virtual void setPosition(int pos); // 初始化用,决定地主后设置 virtual void showHand(); // 打印手牌 virtual void observed(int pos, vector cards); // 观测到玩家出牌 virtual vector play(); // 轮到自己时决定出什么牌 void creat();//讲手牌转换为一个数组 void clear();//初始化 void advpre(vector cards);//准备出的牌; bool have(); bool myputcreat(vector cards); bool leftNoCard(); // 返回是否打完了牌? }; class DDZGame { private: static int my_random(int i); // 洗牌时用到的随机函数 vector players; // 保存三个玩家的指针 void showCards(vector cards); // 输出一组牌 public: DDZGame(DDZPlayer *p1, DDZPlayer *p2, DDZPlayer *p3); // 构造函数 void run(); // 执行游戏流程 }; class DDZHumanPlayer : public DDZPlayer { public: DDZHumanPlayer(string name) : DDZPlayer(name) {}; vector play(); }; string DDZPlayer::getName() { return name; } vector DDZHumanPlayer::play() { vector cards; string s, c; while (true) { showHand(); s = ""; c = ""; cout << "Please input the cards you want to play." << endl; getline(cin, s); istringstream iss(s); while (iss >> c) { cards.push_back(Card(c)); } // 现在cards是一个vector,存储了用户输入的牌 // 你需要检测输入的牌是持有的牌,并且合法。请完成legal函数。 if (legal(cards)) { break; } else { cout << "Illegal cards. Input again." << endl; cards.clear(); } } substractFromHand(cards); return cards; } bool Card::operator <(const Card &other) const { int a = -1, b = -1; for (int i = 0; i < N_CARD_VALUES; ++i) { if (*this == CARD_VALUES[i]) a = i; if (other == CARD_VALUES[i]) b = i; } return a < b; } bool Card::operator >(const Card &other) const { return other < *this; } bool DDZPlayer::myputcreat(vector cards) { for (int i = 0; i < cards.size(); i++) { if (cards[i] == "3") myput[0]++; else if (cards[i] == "4") myput[1]++; else if (cards[i] == "5") myput[2]++; else if (cards[i] == "6") myput[3]++; else if (cards[i] == "7") myput[4]++; else if (cards[i] == "8") myput[5]++; else if (cards[i] == "9") myput[6]++; else if (cards[i] == "10") myput[7]++; else if (cards[i] == "J") myput[8]++; else if (cards[i] == "Q") myput[9]++; else if (cards[i] == "K") myput[10]++; else if (cards[i] == "A") myput[11]++; else if (cards[i] == "2") myput[12]++; else if (cards[i] == "joker") myput[13]++; else if (cards[i] == "JOKER") myput[14]++; else return false; } return true; } bool DDZPlayer::have() { for (int i = 0; i < 15; i++) { if (myput[i] > myhands[i]) return false; } return true; } DDZPlayer::DDZPlayer(string name) : name(name) { // 玩家类的构造函数 prePos = -1; } void DDZPlayer::advpre(vector cards) { int number = 0; int len = 2; bool pd = false; for (int i = 0; i < 12; i++) { if (myput[i] == 3) number++; else { if (number >= len) { for (int j = i - 1; j > i - number - 1; j--) { for (int m = 0; m < 3; m++) { myput[j]--; pmyput[j]++; } } int number2 = 0; if (mymax(myput) == 2) { for (int l = 0; l < 15; l++) { if (myput[l] == 2) { while (myput[l] != 0) { myput[l]--; pmyput[l]++; number2++; } } if (number2 == number * 2) return; } if (number2 < number * 2) { back(myput, pmyput); return; } } else if (mymax(myput) == 1) { for (int l = 0; l < 15; l++) { if (myput[l] >= 1) { myput[l]--; pmyput[l]++; number2++; } if (number2 == number) return; } if (number2 < number) { back(myput, pmyput); return; } } } number = 0; } } len = 3; number = 0; for (int k = 2; k > 0; k--) { if (k == 1) { len = 5; number = 0; } for (int i = 0; i < 12; i++) { if (myput[i] >= k && i != 11) number++; else { if (number >= len) { for (int j = i - 1; j > i - number - 1; j--) { for (int m = 0; m < k; m++) { pmyput[j]++; myput[j]--; } } return; } number = 0; } } } len = 1; for (int i = 0; i < 12; i++) { if (myput[i] == 3) { for (int m = 0; m < 3; m++) { pmyput[i]++; myput[i]--; } if (cards.size() == 4) { for (int i = 0; i < 15; i++) { if (myput[i] >= 1) { myput[i]--; pmyput[i]++; return; } } } else if (cards.size() > 4) { for (int i = 0; i < 15; i++) { if (myput[i] > 1) { myput[i] -= 2; pmyput[i] -= 2; return; } } } return; } else if (myput[i] == 4) { for (int m = 0; m < 4; m++) { pmyput[i]++; myput[i]--; } if (cards.size() == 6) { int number = 0; for (int i = 0; i < 15; i++) { if (myput[i] >= 1) { myput[i]--; pmyput[i]++; number++; } if (number == 2) return; } } else if (cards.size() > 6) { int number2 = 0; for (int i = 0; i < 15; i++) { if (myput[i] > 1) { myput[i] -= 2; pmyput[i] -= 2; number2 ++; } if (number == 2) return; } } return; } } for (int i = 0; i < 15; i++) { if (myput[i] > 0) { while (myput[i] > 0) { myput[i]--; pmyput[i]++; } return; } } return; } bool DDZPlayer::legal(vector cards) { // 通过上家打出的牌prePlay和手牌hand判断cards作为打出牌是否合法 // TODO:(第二题)请补全这个函数 clear(); creat(); if (cards.size() == 0) { if (prePos == position || prePos == -1) return false; else return true; } if (myputcreat(cards)) { if (!have()) return false; if (kingboomjudge(myput)) return true; advpre(cards); if (calculate(myput) == 0) { if (prePos == position || prePos == -1) { return true; } else { if (judge2()) return true; else return false; } } else { back(myput, preput); return false; } } else { return false; } } void DDZPlayer::substractFromHand(vector cards) { // 这个函数从hand中删除cards。假设cards中的牌hand可以拿得出来(否则会出错)。 sort(hand.begin(), hand.end(), greater()); sort(cards.begin(), cards.end(), greater()); vector::iterator i = hand.begin(), k = cards.begin(); for (vector::iterator j = hand.begin(); j != hand.end(); ++j) { if (k == cards.end() || *k != *j) *(i++) = *j; else if (k != cards.end()) ++k; } hand.erase(i, hand.end()); } void DDZPlayer::showHand() { // 输出玩家名和手牌。 cout << name << " holds: "; for (vector::iterator i = hand.begin(); i != hand.end(); ++i) { cout << *i << " "; } cout << endl; } void DDZPlayer::draw(Card card) { // 将card加入手牌 hand.push_back(card); sort(hand.begin(), hand.end(), greater()); } void DDZPlayer::draw(vector cards) { // 将cards中的牌加入手牌 hand.insert(hand.end(), cards.begin(), cards.end()); sort(hand.begin(), hand.end(), greater()); } void DDZPlayer::setPosition(int pos) { position = pos; } void DDZPlayer::observed(int pos, vector cards) { // 将上一个出牌的人和出了什么牌记录下来。 // 如果你想记录更多的信息供你的策略使用,可以改动这个函数。 // 例如,记录已经有哪些牌被打出(记牌器),以推测场上是否可能还存在炸弹。 if (cards.size() == 0) return; prePos = pos; prePlay = cards; } void DDZPlayer::creat() { for (int i = 0; i < hand.size(); i++) { if (hand[i] == "3") myhands[0]++; else if (hand[i] == "4") myhands[1]++; else if (hand[i] == "5") myhands[2]++; else if (hand[i] == "6") myhands[3]++; else if (hand[i] == "7") myhands[4]++; else if (hand[i] == "8") myhands[5]++; else if (hand[i] == "9") myhands[6]++; else if (hand[i] == "10") myhands[7]++; else if (hand[i] == "J") myhands[8]++; else if (hand[i] == "Q") myhands[9]++; else if (hand[i] == "K") myhands[10]++; else if (hand[i] == "A") myhands[11]++; else if (hand[i] == "2") myhands[12]++; else if (hand[i] == "joker") myhands[13]++; else myhands[14]++; } if (prePlay.size() != 0) { for (int i = 0; i < prePlay.size(); i++) { if (prePlay[i] == "3") preput[0]++; else if (prePlay[i] == "4") preput[1]++; else if (prePlay[i] == "5") preput[2]++; else if (prePlay[i] == "6") preput[3]++; else if (prePlay[i] == "7") preput[4]++; else if (prePlay[i] == "8") preput[5]++; else if (prePlay[i] == "9") preput[6]++; else if (prePlay[i] == "10") preput[7]++; else if (prePlay[i] == "J") preput[8]++; else if (prePlay[i] == "Q") preput[9]++; else if (prePlay[i] == "K") preput[10]++; else if (prePlay[i] == "A") preput[11]++; else if (prePlay[i] == "2") preput[12]++; else if (prePlay[i] == "joker") preput[13]++; else preput[14]++; } } } void DDZPlayer::clear() { for (int i = 0; i < 15; i++) { pmyput[i] = 0; myhands[i] = 0; preput[i] = 0; myput[i] = 0; } } bool DDZPlayer::judge() { if (judge1(preput)) { if (straight(myhands, preput, myput)) return true; else { if (boom(myhands, preput, myput)) return true; else if (kingboom(myhands, myput)) return true; } } else { if (preput[13] == 1 && preput[14] == 1) return false; if (straight_attach(myhands, preput, myput)) return true; else { if (boom(myhands, preput, myput)) return true; else if (kingboom(myhands, myput)) return true; } } return false; } bool DDZPlayer::judge2() { if (judge1(preput)) { if (straight(pmyput, preput, myput)) return true; else { if (boom(pmyput, preput, myput)) return true; else if (kingboom(pmyput, myput)) return true; } } else { if (preput[13] == 1 && preput[14] == 1) return false; if (straight_attach(pmyput, preput, myput)) return true; else { if (boom(pmyput, preput, myput)) return true; else if (kingboom(pmyput, myput)) return true; } } return false; } void DDZPlayer::cdcards(vector &cards) { for (int i = 0; i < 15; i++) { while (myput[i] != 0) { myput[i]--; cards.push_back(Card::CARD_VALUES[i]); } } } void DDZPlayer::start() { int number = 0; int len = 2; bool pd = false; for (int i = 0; i < 12; i++) { if (myhands[i] == 3) number++; else { if (number >= len) { for (int j = i - 1; j > i - number - 1; j--) { for (int m = 0; m < 3; m++) { myput[j]++; myhands[j]--; } } int number2 = 0; for (int l = 0; l < 15; l++) { if (myhands[l] >= 1) { myput[l]++; myhands[l]--; number2++; } if (number2 == number) return; } if (number2 < number) { back(myhands, myput); for (int i = 0; i < 12; i++) { if (myhands[i] >= 3) number++; else { if (number >= len) { for (int j = i; j > i - number + 1; j--) { for (int m = 0; m < 3; m++) { myput[j]++; myhands[j]--; } } return; } } } } } number = 0; } } len = 3; number = 0; for (int k = 2; k > 0; k--) { if (k == 1) { len = 5; number = 0; } for (int i = 0; i < 12; i++) { if (myhands[i] >= k && i != 11) number++; else { if (number >= len) { for (int j = i - 1; j > i - number - 1; j--) { for (int m = 0; m < k; m++) { myput[j]++; myhands[j]--; } } return; } number = 0; } } } len = 1; for (int i = 0; i < 12; i++) { if (myhands[i] == 3) { for (int m = 0; m < 3; m++) { myput[i]++; myhands[i]--; } if (hand.size() >= 4) { for (int i = 0; i < 15; i++) { if (myhands[i] >= 1) { myhands[i]--; myput[i]++; return; } } } return; } } for (int i = 0; i < 15; i++) { if (myhands[i] > 0) { while (myhands[i] > 0) { myhands[i]--; myput[i]++; } return; } } return; } vector DDZPlayer::play() { // 轮到你出牌,返回打出的牌。 // TODO:(第一题)请完善这个函数 // 如果你使用不同的数据结构进行处理,你可以现将hand变量转换为你使用的结构, // 处理过后再将打出的牌转换为vector,存入card变量。 clear(); creat(); int mx = 0; vector cards; if (prePos == position || prePos == -1) { start(); cdcards(cards); } else { bool pd = judge(); if (pd) cdcards(cards); } // 你需要保证打出的牌是合法的 // assert函数在参数为false的时候会使程序报错退出。 // 这样做的好处是,如果你有没注意到的错误导致程序在此报错退出, // 你就知道是在出牌的合法性上出了问题,而不用排查程序的其他部分。 //assert(legal(cards)); // 将打出的牌从手牌中删去 substractFromHand(cards); return cards; } bool DDZPlayer::leftNoCard() { // 返回当前手牌为空 return hand.empty(); } const int Card::N_CARD_VALUES = 15; const int Card::N_CARD_SUITS = 4; const string Card::CARD_VALUES[] = { "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2", "joker", "JOKER" }; const string Card::CARD_SUITS[] = { "Spades", "Hearts", "Diamonds", "Clubs" }; vector Card::get_new_deck() { // 生成一副新牌 vector deck; for (int i = 0; i < N_CARD_VALUES - 2; ++i) { for (int j = 0; j < N_CARD_SUITS; ++j) { Card card(CARD_VALUES[i]); deck.push_back(card); } } deck.push_back(Card(CARD_VALUES[13])); deck.push_back(Card(CARD_VALUES[14])); return deck; } int DDZGame::my_random(int i) { return std::rand() % i; } DDZGame::DDZGame(DDZPlayer *p1, DDZPlayer *p2, DDZPlayer *p3) { // 牌局类的构造函数,需要接受三个玩家作为参数 players.push_back(p1); players.push_back(p2); players.push_back(p3); } void DDZGame::showCards(vector cards) { sort(cards.begin(), cards.end(), greater()); for (vector::iterator i = cards.begin(); i != cards.end(); ++i) { cout << *i << " "; } if (cards.size() == 0) cout << "YAO BU QI"; cout << endl << endl; } void DDZGame::run() { // 斗地主游戏的主要流程 // 取一副新牌并洗牌 vector deck = Card::get_new_deck(); random_shuffle(deck.begin(), deck.end(), my_random); // 每个玩家抽17张牌,留下三张底牌 for (int i = 0; i < 54 - 3; ++i) players[i % 3]->draw(deck[i]); // 随机选取地主,发给地主最后三张牌 // * 你可以实现自己的叫地主流程, // * 你需要在DDZPlayer类里面加入相应的变量(存储其他玩家叫地主的情况) // * 和函数bool DDZPlayer::bid()(返回自己是否要叫地主)。 // * 请参阅play()函数和叫地主的规则设计这个流程。 int landlordPos = my_random(3); for (int i = 54 - 3; i < 54; ++i) players[landlordPos]->draw(deck[i]); for (int i = 0; i < 3; ++i) players[i]->setPosition((i + 3 - landlordPos) % 3); // 计算与地主的相对位置 cout << players[landlordPos]->getName() << " is the landlord." << endl; int currentPlayer = landlordPos; while (true) { // 当前玩家打牌 vector currentCards = players[currentPlayer]->play(); cout << players[currentPlayer]->getName() << " plays:"; showCards(currentCards); // 其他玩家看到了打出的牌。(并不是只有下家看到,上家也能看到) for (int i = 0; i < 3; ++i) { // 玩家看到的位置编号都是从地主为0开始的 int relativePos = (currentPlayer + 3 - landlordPos) % 3; players[i]->observed(relativePos, currentCards); } // 如果刚出完牌的玩家没有牌了游戏结束 if (players[currentPlayer]->leftNoCard()) { bool landlordwins = (currentPlayer == landlordPos); cout << (landlordwins ? "Landlord wins!" : "Farmers win!") << endl; break; } // 计算下一个玩家的下标 currentPlayer = (currentPlayer + 1) % 3; } } int main() { string name; cout << "Please input your name:" << endl; getline(cin, name); //int n; //cout << "Please input a number" << endl; //cin >> n; srand(01223); DDZPlayer p1("Alice"), p2("Bob"); DDZHumanPlayer p3(name); DDZGame g(&p1, &p2, &p3); g.run(); system("pause"); return 0; }