Botzone斗地主算法的简单思路

深搜+评估+特例优化

(在积分赛的表现大概处于20名左右的位置。)现在的天梯排名10名左右,不是该版本。

如果可以用机器学习的方法优化参数就好了,虽然开发了对战平台,但并没有用于炼丹。后附实现代码,对战平台及算法报告pdf。

建议先看一看算法报告!

算法报告链接:https://pan.baidu.com/s/119b3MlZfZjXgkZB5cO3A5Q 密码:d1tg

具体算法如下:

// 斗地主(FightTheLandlord)程序
// 深搜策略
// 最后更新于2018-6-3
// 作者:dreamingshao基于zhouhy框架下开发
// 游戏信息:http://www.botzone.org/games#FightTheLandlord

#include 
#include 
#include 
#include 
#include  // 注意memset是cstring里的
#include 
#include 
#include 
#include 
#include "jsoncpp/json.h" // 在平台上,C++编译时默认包含此库
using std::cout;
using std::endl;
using std::vector;
using std::sort;
using std::unique;
using std::set;
using std::string;
using std::time;
using std::srand;
using std::rand;
using std::exception;
constexpr int PLAYER_COUNT = 3;

enum class CardComboType
{
	PASS, // 过
	SINGLE, // 单张
	PAIR, // 对子
	STRAIGHT, // 顺子
	STRAIGHT2, // 双顺
	TRIPLET, // 三条
	TRIPLET1, // 三带一
	TRIPLET2, // 三带二
	BOMB, // 炸弹
	QUADRUPLE2, // 四带二(只)
	QUADRUPLE4, // 四带二(对)
	PLANE, // 飞机
	PLANE1, // 飞机带小翼
	PLANE2, // 飞机带大翼
	SSHUTTLE, // 航天飞机
	SSHUTTLE2, // 航天飞机带小翼
	SSHUTTLE4, // 航天飞机带大翼
	ROCKET, // 火箭
	INVALID // 非法牌型
};

int cardComboScores[] = {
	0, // 过
	1, // 单张
	2, // 对子
	6, // 顺子
	6, // 双顺
	4, // 三条
	4, // 三带一
	4, // 三带二
	10, // 炸弹
	8, // 四带二(只)
	8, // 四带二(对)
	8, // 飞机
	8, // 飞机带小翼
	8, // 飞机带大翼
	10, // 航天飞机(需要特判:二连为10分,多连为20分)
	10, // 航天飞机带小翼
	10, // 航天飞机带大翼
	16, // 火箭
	0 // 非法牌型
};

#ifndef _BOTZONE_ONLINE
string cardComboStrings[] = {
	"PASS",
	"SINGLE",
	"PAIR",
	"STRAIGHT",
	"STRAIGHT2",
	"TRIPLET",
	"TRIPLET1",
	"TRIPLET2",
	"BOMB",
	"QUADRUPLE2",
	"QUADRUPLE4",
	"PLANE",
	"PLANE1",
	"PLANE2",
	"SSHUTTLE",
	"SSHUTTLE2",
	"SSHUTTLE4",
	"ROCKET",
	"INVALID"
};
#endif

// 用0~53这54个整数表示唯一的一张牌
using Card = short;
constexpr Card card_joker = 52;
constexpr Card card_JOKER = 53;

// 除了用0~53这54个整数表示唯一的牌,
// 这里还用另一种序号表示牌的大小(不管花色),以便比较,称作等级(Level)
// 对应关系如下:
// 3 4 5 6 7 8 9 10	J Q K	A	2	小王	大王
// 0 1 2 3 4 5 6 7	8 9 10	11	12	13	14
using Level = short;
constexpr Level MAX_LEVEL = 15;
constexpr Level MAX_STRAIGHT_LEVEL = 11;
constexpr Level level_joker = 13;
constexpr Level level_JOKER = 14;

/**
* 将Card变成Level
*/
constexpr Level card2level(Card card)
{
	return card / 4 + card / 53;
}

// 牌的组合,用于计算牌型
struct CardCombo
{
	// 表示同等级的牌有多少张
	// 会按个数从大到小、等级从大到小排序
	struct CardPack
	{
		Level level;
		short count;

		bool operator< (const CardPack& b) const
		{
			if (count == b.count)
				return level > b.level;
			return count > b.count;
		}
	};
	vector cards; // 原始的牌,未排序
	vector packs; // 按数目和大小排序的牌种
	CardComboType comboType; // 算出的牌型
	Level comboLevel = 0; // 算出的大小序

						  /**
						  * 检查个数最多的CardPack递减了几个
						  */
	int findMaxSeq() const
	{
		for (unsigned c = 1; c < packs.size(); c++)
			if (packs[c].count != packs[0].count ||
				packs[c].level != packs[c - 1].level - 1)
				return c;
		return packs.size();
	}

	/**
	* 这个牌型最后算总分的时候的权重
	*/
	int getWeight() const
	{
		if (comboType == CardComboType::SSHUTTLE ||
			comboType == CardComboType::SSHUTTLE2 ||
			comboType == CardComboType::SSHUTTLE4)
			return cardComboScores[(int)comboType] + (findMaxSeq() > 2) * 10;
		return cardComboScores[(int)comboType];
	}

	// 创建一个空牌组
	CardCombo() : comboType(CardComboType::PASS) {}

	/**
	* 通过Card(即short)类型的迭代器创建一个牌型
	* 并计算出牌型和大小序等
	* 假设输入没有重复数字(即重复的Card)
	*/
	template 
	CardCombo(CARD_ITERATOR begin, CARD_ITERATOR end)
	{
		// 特判:空
		if (begin == end)
		{
			comboType = CardComboType::PASS;
			return;
		}

		// 每种牌有多少个
		short counts[MAX_LEVEL + 1] = {};

		// 同种牌的张数(有多少个单张、对子、三条、四条)
		short countOfCount[5] = {};
		cards = vector(begin, end);
		for (Card c : cards)
			counts[card2level(c)]++;
		for (Level l = 0; l <= MAX_LEVEL; l++)
			if (counts[l])
			{
				packs.push_back(CardPack{ l, counts[l] });
				countOfCount[counts[l]]++;
			}
		sort(packs.begin(), packs.end());

		// 用最多的那种牌总是可以比较大小的
		comboLevel = packs[0].level;

		// 计算牌型
		// 按照 同种牌的张数 有几种 进行分类
		vector kindOfCountOfCount;
		for (int i = 0; i <= 4; i++)
			if (countOfCount[i])
				kindOfCountOfCount.push_back(i);
		sort(kindOfCountOfCount.begin(), kindOfCountOfCount.end());

		int curr, lesser;

		switch (kindOfCountOfCount.size())
		{
		case 1: // 只有一类牌
			curr = countOfCount[kindOfCountOfCount[0]];
			switch (kindOfCountOfCount[0])
			{
			case 1:
				// 只有若干单张
				if (curr == 1)
				{
					comboType = CardComboType::SINGLE;
					return;
				}
				if (curr == 2 && packs[1].level == level_joker)
				{
					comboType = CardComboType::ROCKET;
					return;
				}
				if (curr >= 5 && findMaxSeq() == curr &&
					packs.begin()->level <= MAX_STRAIGHT_LEVEL)
				{
					comboType = CardComboType::STRAIGHT;
					return;
				}
				break;
			case 2:
				// 只有若干对子
				if (curr == 1)
				{
					comboType = CardComboType::PAIR;
					return;
				}
				if (curr >= 3 && findMaxSeq() == curr &&
					packs.begin()->level <= MAX_STRAIGHT_LEVEL)
				{
					comboType = CardComboType::STRAIGHT2;
					return;
				}
				break;
			case 3:
				// 只有若干三条
				if (curr == 1)
				{
					comboType = CardComboType::TRIPLET;
					return;
				}
				if (findMaxSeq() == curr &&
					packs.begin()->level <= MAX_STRAIGHT_LEVEL)
				{
					comboType = CardComboType::PLANE;
					return;
				}
				break;
			case 4:
				// 只有若干四条
				if (curr == 1)
				{
					comboType = CardComboType::BOMB;
					return;
				}
				if (findMaxSeq() == curr &&
					packs.begin()->level <= MAX_STRAIGHT_LEVEL)
				{
					comboType = CardComboType::SSHUTTLE;
					return;
				}
			}
			break;
		case 2: // 有两类牌
			curr = countOfCount[kindOfCountOfCount[1]];
			lesser = countOfCount[kindOfCountOfCount[0]];
			if (kindOfCountOfCount[1] == 3)
			{
				// 三条带?
				if (kindOfCountOfCount[0] == 1)
				{
					// 三带一
					if (curr == 1 && lesser == 1)
					{
						comboType = CardComboType::TRIPLET1;
						return;
					}
					if (findMaxSeq() == curr && lesser == curr &&
						packs.begin()->level <= MAX_STRAIGHT_LEVEL)
					{
						comboType = CardComboType::PLANE1;
						return;
					}
				}
				if (kindOfCountOfCount[0] == 2)
				{
					// 三带二
					if (curr == 1 && lesser == 1)
					{
						comboType = CardComboType::TRIPLET2;
						return;
					}
					if (findMaxSeq() == curr && lesser == curr &&
						packs.begin()->level <= MAX_STRAIGHT_LEVEL)
					{
						comboType = CardComboType::PLANE2;
						return;
					}
				}
			}
			if (kindOfCountOfCount[1] == 4)
			{
				// 四条带?
				if (kindOfCountOfCount[0] == 1)
				{
					// 四条带两只 * n
					if (curr == 1 && lesser == 2)
					{
						comboType = CardComboType::QUADRUPLE2;
						return;
					}
					if (findMaxSeq() == curr && lesser == curr * 2 &&
						packs.begin()->level <= MAX_STRAIGHT_LEVEL)
					{
						comboType = CardComboType::SSHUTTLE2;
						return;
					}
				}
				if (kindOfCountOfCount[0] == 2)
				{
					// 四条带两对 * n
					if (curr == 1 && lesser == 2)
					{
						comboType = CardComboType::QUADRUPLE4;
						return;
					}
					if (findMaxSeq() == curr && lesser == curr * 2 &&
						packs.begin()->level <= MAX_STRAIGHT_LEVEL)
					{
						comboType = CardComboType::SSHUTTLE4;
						return;
					}
				}
			}
		}

		comboType = CardComboType::INVALID;
	}

	/**
	* 判断指定牌组能否大过当前牌组(这个函数不考虑过牌的情况!)
	*/
	bool canBeBeatenBy(const CardCombo& b) const
	{
		if (comboType == CardComboType::INVALID || b.comboType == CardComboType::INVALID)
			return false;
		if (b.comboType == CardComboType::ROCKET)
			return true;
		if (b.comboType == CardComboType::BOMB)
			switch (comboType)
			{
			case CardComboType::ROCKET:
				return false;
			case CardComboType::BOMB:
				return b.comboLevel > comboLevel;
			default:
				return true;
			}
		return b.comboType == comboType && b.cards.size() == cards.size() && b.comboLevel > comboLevel;
	}

	/**
	* 从指定手牌中寻找第一个能大过当前牌组的牌组
	* 如果随便出的话只出第一张
	* 如果不存在则返回一个PASS的牌组
	*/
	template 
	CardCombo findFirstValid(CARD_ITERATOR begin, CARD_ITERATOR end) const
	{
		if (comboType == CardComboType::PASS) // 如果不需要大过谁,只需要随便出
		{
			CARD_ITERATOR second = begin;
			second++;
			return CardCombo(begin, second); // 那么就出第一张牌……
		}

		// 然后先看一下是不是火箭,是的话就过
		if (comboType == CardComboType::ROCKET)
			return CardCombo();

		// 现在打算从手牌中凑出同牌型的牌
		auto deck = vector(begin, end); // 手牌
		short counts[MAX_LEVEL + 1] = {};

		unsigned short kindCount = 0;

		// 先数一下手牌里每种牌有多少个
		for (Card c : deck)
			counts[card2level(c)]++;

		// 手牌如果不够用,直接不用凑了,看看能不能炸吧
		if (deck.size() < cards.size())
			goto failure;

		// 再数一下手牌里有多少种牌
		for (short c : counts)
			if (c)
				kindCount++;

		// 否则不断增大当前牌组的主牌,看看能不能找到匹配的牌组
		{
			// 开始增大主牌
			int mainPackCount = findMaxSeq();
			bool isSequential =
				comboType == CardComboType::STRAIGHT ||
				comboType == CardComboType::STRAIGHT2 ||
				comboType == CardComboType::PLANE ||
				comboType == CardComboType::PLANE1 ||
				comboType == CardComboType::PLANE2 ||
				comboType == CardComboType::SSHUTTLE ||
				comboType == CardComboType::SSHUTTLE2 ||
				comboType == CardComboType::SSHUTTLE4;
			for (Level i = 1; ; i++) // 增大多少
			{
				for (int j = 0; j < mainPackCount; j++)
				{
					int level = packs[j].level + i;

					// 各种连续牌型的主牌不能到2,非连续牌型的主牌不能到小王,单张的主牌不能超过大王
					if ((comboType == CardComboType::SINGLE && level > MAX_LEVEL) ||
						(isSequential && level > MAX_STRAIGHT_LEVEL) ||
						(comboType != CardComboType::SINGLE && !isSequential && level >= level_joker))
						goto failure;

					// 如果手牌中这种牌不够,就不用继续增了
					if (counts[level] < packs[j].count)
						goto next;
				}

				{
					// 找到了合适的主牌,那么从牌呢?
					// 如果手牌的种类数不够,那从牌的种类数就不够,也不行
					if (kindCount < packs.size())
						continue;

					// 好终于可以了
					// 计算每种牌的要求数目吧
					short requiredCounts[MAX_LEVEL + 1] = {};
					for (int j = 0; j < mainPackCount; j++)
						requiredCounts[packs[j].level + i] = packs[j].count;
					for (unsigned j = mainPackCount; j < packs.size(); j++)
					{
						Level k;
						for (k = 0; k <= MAX_LEVEL; k++)
						{
							if (requiredCounts[k] || counts[k] < packs[j].count)
								continue;
							requiredCounts[k] = packs[j].count;
							break;
						}
						if (k == MAX_LEVEL + 1) // 如果是都不符合要求……就不行了
							goto next;
					}


					// 开始产生解
					vector solve;
					for (Card c : deck)
					{
						Level level = card2level(c);
						if (requiredCounts[level])
						{
							solve.push_back(c);
							requiredCounts[level]--;
						}
					}
					return CardCombo(solve.begin(), solve.end());
				}

			next:
				; // 再增大
			}
		}

	failure:
		// 实在找不到啊
		// 最后看一下能不能炸吧

		for (Level i = 0; i < level_joker; i++)
			if (counts[i] == 4 && (comboType != CardComboType::BOMB || i > packs[0].level)) // 如果对方是炸弹,能炸的过才行
			{
				// 还真可以啊……
				Card bomb[] = { Card(i * 4), Card(i * 4 + 1), Card(i * 4 + 2), Card(i * 4 + 3) };
				return CardCombo(bomb, bomb + 4);
			}

		// 有没有火箭?
		if (counts[level_joker] + counts[level_JOKER] == 2)
		{
			Card rocket[] = { card_joker, card_JOKER };
			return CardCombo(rocket, rocket + 2);
		}

		// ……
		return CardCombo();
	}

	void debugPrint()
	{
#ifndef _BOTZONE_ONLINE
		std::cout << "【" << cardComboStrings[(int)comboType] <<
			"共" << cards.size() << "张,大小序" << comboLevel << "】";
#endif
	}
};

// 我的牌有哪些
set myCards;

// 地主被明示的牌有哪些
set landlordPublicCards;

// 大家从最开始到现在都出过什么
vector> whatTheyPlayed[PLAYER_COUNT];

// 当前要出的牌需要大过谁
CardCombo lastValidCombo;

// 大家还剩多少牌
short cardRemaining[PLAYER_COUNT] = { 20, 17, 17 };

// 我是几号玩家(0-地主,1-农民甲,2-农民乙)
int myPosition;


/*
自己写的bug部分---------------------------------------------------------------------我是萌萌的分割线
*/

//-----------------------------------------------------------------------------------检测地主的手牌数量?
int last_player = 0, who_last_player = 0;//1代表队友,0代表对手,2表示自己
short Curmycard[MAX_LEVEL];
int Cardnum;
double maxscore = 1e10;
int leasttimes = 1e9, max_times = 1e9;
int HowManyTimes = 0;
vector bestcard;
void evaluate_prepare()
{
	memset(Curmycard, 0, sizeof(Curmycard));
	for (auto c : myCards)
	{
		Curmycard[card2level(c)]++;
	}
	Cardnum = myCards.size();
	max_times = leasttimes = 1e9;
	maxscore = 1e10;
	HowManyTimes = 0;
	return;
}
set remain_card; //里面装着剩下的外面还没出的牌

					   //寻找第一个最长的n顺子
int straight_k[4] = { 0, 5, 3, 1 };
int find_straight(int &s, int k, int begin = 0)
{
	int j, i;
	for (i = begin; i <= 10;)
	{
		while (i <= 10 && Curmycard[i] < k)
			++i;
		j = 1;
		while (i + j <= 11 && Curmycard[i + j] >= k)
			++j;
		if (j >= straight_k[k] && i <= 10)
		{
			s = j;
			return i;
		}
		i = i + j;
	}
	s = 0;
	return 0;
}

//判断是否全场最大
bool judge_best()
{
	if (CardCombo(bestcard.begin(), bestcard.end()).findFirstValid(remain_card.begin(), remain_card.end()).comboType == CardComboType::PASS)
		return true;
	else
		return false;
}

//附加分
double extra_point(const int num, const int i)
{
	if (judge_best())
	{
		int n = 0;
		for (int i = 0; i < MAX_LEVEL; ++i)
			if (Curmycard[i] > 0)
				++n;
		if (n <= 1)
			return -1e9;
	}
	double extra = 0;
	if (myPosition == 0)
	{
		if (cardRemaining[1] < 3 && num == cardRemaining[1])
			extra += (16 - i) / 10.0;
		if (cardRemaining[2] < 3 && num == cardRemaining[2])
			extra += (16 - i) / 10.0;
	}
	else
	{
		if (cardRemaining[0] < 3 && num == cardRemaining[0] && last_player == 2)
			extra += (16 - i) / 10.0;
	}
	return extra;
}

//评估函数,用来评价当前的手牌质量,分数越低手牌质量越好
double evaluate(int depth, int kth, double &score)
{

	if (Cardnum <= 0 || kth == 6)
	{
		score += 0.1 * HowManyTimes;
		if (maxscore > score)
		{
			maxscore = score;
			max_times = HowManyTimes;
		}
		if (HowManyTimes < leasttimes)
			leasttimes = HowManyTimes;
		score -= 0.1 * HowManyTimes;
		return 0;
	}
	switch (kth)
	{
	case 0:
		//找炸弹
		for (int i = 0; i < MAX_LEVEL - 2; ++i)
		{
			if (Curmycard[i] == 4)
			{
				score--;
				//炸弹想出就出,分数最低
				Curmycard[i] = 0;
				Cardnum -= 4;
				HowManyTimes++;//每次调用evaluate都意味着一手牌
				evaluate(depth + 1, 0, score);
				HowManyTimes--;//每次调用evaluate都意味着一手牌
				Curmycard[i] = 4;
				Cardnum += 4;
				score++;
			}
		}
		if (Curmycard[MAX_LEVEL - 2] == 1 && Curmycard[MAX_LEVEL - 1] == 1)//王炸
		{
			score -= 1.2;
			Curmycard[MAX_LEVEL - 2] = 0;
			Curmycard[MAX_LEVEL - 1] = 0;
			Cardnum -= 2;
			HowManyTimes++;//每次调用evaluate都意味着一手牌
			evaluate(depth + 1, 1, score);
			HowManyTimes--;//每次调用evaluate都意味着一手牌
			Curmycard[MAX_LEVEL - 2] = 1;
			Curmycard[MAX_LEVEL - 1] = 1;
			Cardnum += 2;
		}

	case 1:
		//找顺子(k顺)
	{
		int s = 0;
		int i = find_straight(s, 1);
		if (s >= 5)
		{
			for (int b = i; b <= i + s - 5; ++b)
			{
				for (int k = 5; k <= s && b + k <= i + s; ++k)
				{
					for (int j = 0; j < k; ++j)
					{
						Curmycard[b + j]--;
					}
					Cardnum -= k;
					score += 0.5;
					HowManyTimes++;//每次调用evaluate都意味着一手牌
					evaluate(depth + 1, 1, score);
					HowManyTimes--;//每次调用evaluate都意味着一手牌
					for (int j = 0; j < k; ++j)
					{
						Curmycard[b + j]++;
					}
					Cardnum += k;
					score -= 0.5;
				}
			}

		}
	}


	case 2:
		//找对顺
	{
		int s = 0;
		int i = find_straight(s, 2);
		if (s != 0)
		{
			for (int b = i; b <= i + s - 3; ++b)
			{
				for (int k = 3; k <= s && b + k <= i + s; ++k)
				{
					for (int j = 0; j < k; ++j)
					{
						Curmycard[b + j] -= 2;
					}
					Cardnum -= k * 2;
					score += 0.5;
					HowManyTimes++;//每次调用evaluate都意味着一手牌
					evaluate(depth + 1, 2, score);
					HowManyTimes--;//每次调用evaluate都意味着一手牌
					for (int j = 0; j < k; ++j)
					{
						Curmycard[b + j] += 2;
					}
					Cardnum += k * 2;
					score -= 0.5;
				}
			}
		}
	}

	case 3:
		//飞机或三个头
	{
		int s = 0;
		int i = find_straight(s, 3);


		int wings[6], pair = 0;
		int wing[6], single = 0;
		//有三个头
		if (s != 0)
		{
			for (int j = 0; j < s; ++j)
			{
				Curmycard[i + j] -= 3;
			}
			Cardnum -= 3 * s;
			for (int t = 0; t < 12 && (t < i || t >= i + s); ++t)
			{
				if (Curmycard[t] == 1 && single < s)
				{
					wing[single] = t;
					single++;
				}
				else if (Curmycard[t] == 2 && pair < s)
				{
					wings[pair] = t;
					pair++;
				}
			}
			//飞机带小翼
			if (single == s)
			{
				for (int o = 0; o < s; ++o)
				{
					Curmycard[wing[o]] = 0;
				}
				Cardnum -= s;
				if (i <= 7)
				{
					score += 1;
				}
				else if(s == 1)
				{
					score += 0.5;
				}
				else
				{
					score += 0.2;
				}
				HowManyTimes++;//每次调用evaluate都意味着一手牌
				evaluate(depth + 1, 3, score);
				HowManyTimes--;//每次调用evaluate都意味着一手牌
				for (int o = 0; o < s; ++o)
				{
					Curmycard[wing[o]] = 1;
				}
				Cardnum += s;
				if (i <= 7)
				{
					score -= 1;
				}
				else if (s == 1)
				{
					score -= 0.5;
				}
				else
				{
					score -= 0.2;
				}
			}

			//飞机带大翼
			if (pair == s)
			{
				for (int o = 0; o < s; ++o)
				{
					Curmycard[wings[o]] = 0;
				}
				Cardnum -= s * 2;
				if (i <= 7)
				{
					score += 1;
				}
				else if (s == 1)
				{
					score += 0.5;
				}
				else
				{
					score += 0.2;
				}
				HowManyTimes++;//每次调用evaluate都意味着一手牌
				evaluate(depth + 1, 3, score);
				HowManyTimes--;//每次调用evaluate都意味着一手牌
				for (int o = 0; o < s; ++o)
				{
					Curmycard[wings[o]] = 2;
				}
				Cardnum += s * 2;
				if (i <= 7)
				{
					score -= 1;
				}
				else if (s == 1)
				{
					score -= 0.5;
				}
				else
				{
					score -= 0.2;
				}
			}

			//飞机不带翼
			if (i <= 7)
			{
				score += 1;
			}
			else if (s == 1)
			{
				score += 0.5;
			}
			else
			{
				score += 0.2;
			}
			HowManyTimes++;//每次调用evaluate都意味着一手牌
			evaluate(depth + 1, 3, score);
			HowManyTimes--;//每次调用evaluate都意味着一手牌
			if (i <= 7)
			{
				score -= 1;
			}
			else if (s == 1)
			{
				score -= 0.5;
			}
			else
			{
				score -= 0.2;
			}
			for (int j = 0; j < s; ++j)
			{
				Curmycard[i + j] += 3;
			}
			Cardnum += 3 * s;


		}
	}

	case 4:
		//四带二
	{
		for (int i = 0; i <= 12; ++i)
		{
			if (Curmycard[i] == 4)
			{
				Curmycard[i] = 0;
				Cardnum -= 4;
				int _single[2], single = 0;
				int _pair[2], pair = 0;
				for (int t = 0; t < 12; ++t)
				{
					if (Curmycard[t] == 1 && single < 2)
					{
						_single[single] = t;
						single++;
					}
					else if (Curmycard[t] == 2 && pair < 2)
					{
						_pair[pair] = t;
						pair++;
					}
				}
				//四带二单张
				if (single == 2)
				{
					Curmycard[_single[0]] = Curmycard[_single[1]] = 0;
					Cardnum -= 2;
					score += 0.1;
					HowManyTimes++;//每次调用evaluate都意味着一手牌
					evaluate(depth + 1, 4, score);
					HowManyTimes--;//每次调用evaluate都意味着一手牌
					Curmycard[_single[0]] = Curmycard[_single[1]] = 1;
					Cardnum += 2;
					score -= 0.1;
				}
				//四带二对子
				if (pair == 2)
				{
					Curmycard[_pair[0]] = Curmycard[_pair[1]] = 0;
					Cardnum -= 4;
					score += 0;
					HowManyTimes++;//每次调用evaluate都意味着一手牌
					evaluate(depth + 1, 4, score);
					HowManyTimes--;//每次调用evaluate都意味着一手牌
					Curmycard[_pair[0]] = Curmycard[_pair[1]] = 2;
					Cardnum += 4;
					score -= 0;
				}
			}
		}
	}

	case 5:
		//对子 or 单牌
	{
		double t = score;
		int n = 0;
		for (int i = 0; i < MAX_LEVEL - 5; ++i)
		{
			if (Curmycard[i] != 0)
			{
				score += 1;
				++n;
			}
		}

		for (int i = MAX_LEVEL - 5; i < MAX_LEVEL - 3; ++i)//QKA
		{
			if (Curmycard[i] != 0)
			{
				score = score;
				++n;
			}
		}
		for (int i = MAX_LEVEL - 3; i < MAX_LEVEL - 1; ++i)//2 and joker
		{
			if (Curmycard[i] != 0)
			{
				score = score - 0.5;
				++n;
			}
		}
		if (Curmycard[MAX_LEVEL - 1] != 0)
		{
			score -= 1;
			++n;
		}
		HowManyTimes += n;
		evaluate(depth, 6, score);
		HowManyTimes -= n;
		score = t;
	}
	default:
		break;
	}
	return maxscore;
}



void pushcard(Level l, short c)
{
	if (l == MAX_LEVEL - 2 || l == MAX_LEVEL - 1)
	{
		bestcard.push_back(54 - (MAX_LEVEL - l));
		return;
	}
	for (short i = l * 4; i < l * 4 + 4; ++i)
	{
		if (myCards.find(i) != myCards.end() && c > 0)
		{
			bestcard.push_back(Card(i));
			c--;
		}
	}
	return;
}


CardCombo findBestValld()
{
	if (lastValidCombo.comboType == CardComboType::ROCKET)//先看看别人是不是王炸
		return CardCombo();
	//是敌是友,一看便知
	if (lastValidCombo.comboType == CardComboType::PASS)
		last_player = 2;
	else if (myPosition == 0)
	{
		last_player = 0;
		if (whatTheyPlayed[(myPosition + 2) % 3].back().size() != 0)
			who_last_player = (myPosition + 2) % 3;
		else
			who_last_player = (myPosition + 1) % 3;
	}
	else if (whatTheyPlayed[(myPosition + 2) % 3].back().size() != 0)
	{
		who_last_player = (myPosition + 2) % 3;
		if (myPosition + who_last_player == 3)
			last_player = 1;
		else
			last_player = 0;
	}
	else
	{
		who_last_player = (myPosition + 1) % 3;
		if (myPosition + who_last_player == 3)
			last_player = 1;
		else
			last_player = 0;
	}

	bestcard.clear();
	evaluate_prepare();
	double value = 1e9;
	double score = 0;
	double cur_score = evaluate(0, 0, score);

	if (lastValidCombo.comboType != CardComboType::PASS)
	{
		//有限制的出牌
		int i = 0;
		bestcard.clear();
		//之前是炸弹or任意牌也可出炸弹,但之前是炸弹时此时出炸弹有下限
		if (lastValidCombo.comboType == CardComboType::BOMB)
		{
			i = lastValidCombo.comboLevel + 1;
		}

		for (; i < MAX_LEVEL - 2; ++i)
		{
			evaluate_prepare();
			if (Curmycard[i] == 4)
			{
				Curmycard[i] = 0;
				Cardnum -= 4;
				double score = 0;
				double temp = evaluate(0, 0, score);
				Curmycard[i] = 4;
				Cardnum += 4;
				if (value > temp)
				{
					bestcard.clear();
					value = temp;
					for (int j = 0; j < 4; ++j)
						bestcard.push_back(Card(4 * i + j));
				}
			}
		}
		if (Curmycard[MAX_LEVEL - 2] == 1 && Curmycard[MAX_LEVEL - 1] == 1)//出王炸你怕不怕
		{
			evaluate_prepare();
			Curmycard[MAX_LEVEL - 2] = Curmycard[MAX_LEVEL - 1] = 0;
			Cardnum -= 2;
			double score = 0;
			double temp = evaluate(0, 0, score);
			Curmycard[MAX_LEVEL - 2] = Curmycard[MAX_LEVEL - 1] = 1;
			Cardnum += 2;
			if (value > temp)
			{
				bestcard.clear();
				value = temp;
				for (int j = 0; j < 2; ++j)
					bestcard.push_back(Card(52 + j));
			}
		}

		//必须出单张
		if (lastValidCombo.comboType == CardComboType::SINGLE)
		{
			evaluate_prepare();
			for (int i = lastValidCombo.comboLevel + 1; i < MAX_LEVEL; ++i)
			{
				if (Curmycard[i] != 0)
				{
					evaluate_prepare();
					Curmycard[i]--;
					Cardnum--;
					double score = 0;
					double temp = evaluate(0, 0, score);
					temp += extra_point(1, i);
					if (Cardnum == 0)//能一下出完,太棒了!
						temp = -1e9;
					Curmycard[i]++;
					Cardnum++;
					if (value > temp)
					{
						value = temp;
						bestcard.clear();
						pushcard(i, 1);
					}
				}
			}
		}
		//必须出对子
		else if (lastValidCombo.comboType == CardComboType::PAIR)
		{
			evaluate_prepare();
			int pair = 0;
			for (int i = lastValidCombo.comboLevel + 1; i < MAX_LEVEL - 2; ++i)
			{
				if (Curmycard[i] >= 2)
				{
					evaluate_prepare();
					Curmycard[i] -= 2;
					Cardnum -= 2;
					double score = 0;
					double temp = evaluate(0, 0, score);
					temp += extra_point(2, i);
					if (Cardnum == 0)//能一下出完,太棒了!
						temp = -1e9;
					Curmycard[i] += 2;
					Cardnum += 2;
					if (value > temp)
					{
						value = temp;
						bestcard.clear();
						pushcard(i, 2);
					}
				}
			}
		}

		//是单顺或双顺
		else if (lastValidCombo.comboType == CardComboType::STRAIGHT || lastValidCombo.comboType == CardComboType::STRAIGHT2)
		{
			evaluate_prepare();
			int c;
			if (lastValidCombo.comboType == CardComboType::STRAIGHT)//单顺
				c = 1;
			else//双顺
				c = 2;
			int s = lastValidCombo.cards.size() / c;
			for (int i = lastValidCombo.comboLevel + 1; i <= MAX_LEVEL - 3 - s; ++i)
			{
				int j = 0;
				while (i + j <= MAX_LEVEL - 3 && Curmycard[i + j] >= c)
				{
					j++;
				}
				if (j >= s)
				{
					evaluate_prepare();
					for (int k = 0; k <= j - s; ++k)
					{
						for (int t = k; t < s + k; ++t)
						{
							Curmycard[i + t] -= c;
						}
						Cardnum -= s * c;
						double score = 0;
						double temp = evaluate(0, 0, score);
						temp += extra_point(5, i);
						if (Cardnum == 0)//能一下出完,太棒了!
							temp = -1e9;
						for (int t = k; t < s + k; ++t)
						{
							Curmycard[i + t] += c;
						}
						Cardnum += s * c;
						if (value > temp)
						{
							value = temp;
							bestcard.clear();
							for (int t = k; t < s + k; ++t)
							{
								pushcard(i + t, c);
							}
						}
					}
				}
				i = i + j + 1;
			}
		}

		//三个头
		else if (lastValidCombo.comboType == CardComboType::TRIPLET || lastValidCombo.comboType == CardComboType::TRIPLET2 || lastValidCombo.comboType == CardComboType::TRIPLET1 ||
			lastValidCombo.comboType == CardComboType::PLANE || lastValidCombo.comboType == CardComboType::PLANE2 || lastValidCombo.comboType == CardComboType::PLANE1)
		{
			evaluate_prepare();
			int c, s;//分别是coumputer和science[/huaji]
			if (lastValidCombo.comboType == CardComboType::TRIPLET || lastValidCombo.comboType == CardComboType::TRIPLET2 || lastValidCombo.comboType == CardComboType::TRIPLET1)//三个头
			{
				c = lastValidCombo.cards.size() - 3;
				s = 1;
			}
			else//飞机
			{
				if (lastValidCombo.packs.back().count != 3)
					s = lastValidCombo.packs.size() / 2;
				else
					s = lastValidCombo.packs.size();
				c = (lastValidCombo.cards.size() - s * 3) / s;
			}

			for (int i = lastValidCombo.comboLevel + 2 - s; i <= MAX_LEVEL - 2 - s;)
			{
				int j = 0;
				while (i + j < MAX_LEVEL - 2 && Curmycard[i + j] == 3)
				{
					j++;
				}
				if (j >= s)//找到了三个头(飞机)
				{
					for (int t = 0; t <= j - s; ++t)
					{
						evaluate_prepare();
						int sc[4], single = 0;
						for (int k = i + t; k < i + s + t; ++k)
						{
							Curmycard[k] -= 3;
							Cardnum -= 3;
						}
						for (int k = 0; k < MAX_LEVEL; ++k)//找带的牌
						{
							if (c == 0 || single == s)
								break;
							if (Curmycard[k] == c && single < s && (k < i + t || k >= i + s + t))
							{
								sc[single] = k;
								single++;
								Curmycard[single] -= c;
								Cardnum -= c;
							}
						}
						if (c != 0 && single < s)//找带的牌,实在不行就拆牌吧
						{
							for (int k = 0; k < MAX_LEVEL; ++k)
							{
								if (c == 0)
									break;
								if (Curmycard[k] >= c && single < s && (k < i + t || k >= i + s + t))
								{
									sc[single] = k;
									single++;
									Curmycard[single] -= c;
									Cardnum -= c;
								}
							}
						}
						if (c != 0 && single < s)//拆牌都不行,别玩了555
						{
							break;
						}

						double score = 0;
						double temp = evaluate(0, 0, score);
						temp += extra_point(4, i);
						if (Cardnum == 0)//能一下出完,太棒了!
							temp = -1e9;
						if (value > temp)
						{
							value = temp;
							bestcard.clear();
							for (int k = i + t;k < s + i + t; ++k)
							{
								pushcard(k, 3);
							}
							if(c != 0)
								for (int t = 0; t < s; ++t)
								{
									pushcard(sc[t], c);
								}
						}
						for (int k = i + t; k < i + s + t; ++k)
						{
							Curmycard[k] += 3;
							Cardnum += 3;
						}
					}
				}
				i = i + j + 1;
			}

		}
		//四带二
		else if (lastValidCombo.comboType == CardComboType::QUADRUPLE2 || lastValidCombo.comboType == CardComboType::QUADRUPLE4)
		{
			int c;
			evaluate_prepare();
			if (lastValidCombo.comboType == CardComboType::QUADRUPLE2)
			{
				c = 1;
			}
			else
				c = 2;
			for (int i = lastValidCombo.comboLevel + 1; i < MAX_LEVEL; ++i)
			{
				if (Curmycard[i] == 4)
				{
					//如果可以单牌或对子全搜一遍就更好了
					evaluate_prepare();
					Curmycard[i] -= 4;
					Cardnum -= 4;
					int sc[2], single = 0;
					for (int k = 0; k < MAX_LEVEL - 3; ++k)
					{
						if (Curmycard[k] == c && single < 2)
						{
							sc[single] = k;
							single++;
							Curmycard[k] -= c;
							Cardnum -= c;
						}
					}
					if (single < 2)
					{
						i = MAX_LEVEL;
						break;
					}

					double score = 0;
					double temp = evaluate(0, 0, score);
					temp += extra_point(6, i);
					if (Cardnum == 0)//能一下出完,太棒了!
						temp = -1e9;
					if (value > temp)
					{
						bestcard.clear();
						value = temp;
						for (int j = 0; j < 4; ++j)
							bestcard.push_back(Card(4 * i + j));
						for (int j = 0; j < 2; ++j)
							pushcard(sc[j], c);
						break;
					}
				}
			}
		}
	}
	else
	{
		//没有限制的出牌
		//出顺子吗
		{
			evaluate_prepare();
			int s = 0;
			int i = find_straight(s, 1);
			if (s >= 5)
			{
				for (int b = i; b <= i + s - 5; ++b)
				{
					for (int k = 5; k <= s && b + k <= i + s; ++k)
					{
						for (int j = 0; j < k; ++j)
						{
							Curmycard[b + j]--;
						}
						Cardnum -= s;
						double score = 0;
						double temp = evaluate(0, 0, score);
						temp += extra_point(5, b);
						if (b + s < 11)
							temp = temp - 0.8;//根据i的大小来出牌, 出顺子加0.5分
						else
							temp = temp - 0.5;
						if (Cardnum == 0)//能一下出完,太棒了!
							temp = -1e9;
						for (int j = 0; j < k; ++j)
						{
							Curmycard[b + j]++;
						}
						Cardnum += s;
						if (value > temp)
						{
							value = temp;
							bestcard.clear();
							for (int t = 0; t < s; ++t)
							{
								pushcard(i + t, 1);
							}
						}
					}
				}
			}
		}
		//有单张出单张
		{
			int single = 0;
			evaluate_prepare();
			for (int i = 0; i < MAX_LEVEL; ++i)
			{
				if (Curmycard[i] != 0)
				{
					evaluate_prepare();
					Curmycard[i]--;
					Cardnum--;
					double score = 0;
					double temp = evaluate(0, 0, score);
					temp += extra_point(1, i) - 0.05 + i / 200.0;
					Curmycard[i]++;
					Cardnum++;
					if (value > temp)
					{
						value = temp;
						bestcard.clear();
						pushcard(i, 1);
					}
				}
			}
		}

		//出对子
		{
			int pair = 0;
			evaluate_prepare();
			for (int i = 0; i < MAX_LEVEL; ++i)
			{

				if (Curmycard[i] >= 2)
				{
					evaluate_prepare();
					Curmycard[i] -= 2;
					Cardnum -= 2;
					double score = 0;
					double temp = evaluate(0, 0, score);
					temp += extra_point(2, i) - 0.05 + i / 200.0;
					if (Cardnum == 0)//能一下出完,太棒了!
						temp = -1e9;
					Curmycard[i] += 2;
					Cardnum += 2;
					if (value > temp)
					{
						value = temp;
						bestcard.clear();
						pushcard(i, 2);
					}
				}
			}
		}

		//出连对
		{
			evaluate_prepare();
			int s = 0;
			int i = find_straight(s, 2);
			if (s >= 3)
			{
				for (int j = 0; j < s; ++j)
				{
					Curmycard[i + j] -= 2;
				}
				Cardnum -= s * 2;
				double score = 0;
				double temp = evaluate(0, 0, score);
				temp = temp - (15 - i) * 1.0 / 100 - 0.5;//根据i的大小来出牌
				if (Cardnum == 0)//能一下出完,太棒了!
					temp = -1e9;
				for (int j = 0; j < s; ++j)
				{
					Curmycard[i + j] += 2;
				}
				Cardnum += s * 2;
				if (value > temp)
				{
					value = temp;
					bestcard.clear();
					for (int t = 0; t < s; ++t)
					{
						pushcard(i + t, 2);
					}
				}
			}
		}
		

		//出三带x或者飞机-------------------------------------------能否对带的牌全部搜索?
		{
			int s = 0;
			evaluate_prepare();
			int begin = 0;
			while (true)
			{
				int i = find_straight(s, 3, begin);
				//有三个头
				if (s == 0)
					break;
				else
				{
					evaluate_prepare();
					for (int j = 0; j < s; ++j)
					{
						Curmycard[i + j] -= 3;
					}
					Cardnum -= 3 * s;
					int wings[15], pair = 0;
					int wing[15], single = 0;
					for (int t = 0; t < MAX_LEVEL && (t < i || t >= i + s); ++t)
					{
						if (Curmycard[t] == 1)
						{
							wing[single] = t;
							single++;
						}
						else if (Curmycard[t] == 2)
						{
							wings[pair] = t;
							pair++;
						}
					}

					//飞机不带翼
					double score = 0;
					double temp = evaluate(0, 0, score);
					if (i <= 7 && s >= 2)
						temp = temp - 0.5 + extra_point(3, i);//根据i的大小来出牌
					else if (i <= 7)
						temp = temp - 0.3 + extra_point(3, i);
					else
						temp -= 0.1 + extra_point(6, i);
					if (Cardnum == 0)//能一下出完,太棒了!
						temp = -1e9;

					if (value > temp)
					{
						value = temp;
						bestcard.clear();
						for (int t = i; t < i + s; ++t)
						{
							pushcard(t, 3);
						}
					}

					//飞机带小翼
					if (single >= s)
					{
						for (int o = 0; o < s; ++o)
						{
							Curmycard[wing[o]] = 0;
						}
						Cardnum -= s;
						double score = 0;
						double temp = evaluate(0, 0, score);
						if (i <= 7 && s >= 2)
							temp = temp - 0.5 + extra_point(3, i);//根据i的大小来出牌
						else if (i <= 7)
							temp = temp - 0.3 + extra_point(3, i);
						else
							temp -= 0.1 + extra_point(6, i);
						if (Cardnum == 0)//能一下出完,太棒了!
							temp = -1e9;
						for (int o = 0; o < s; ++o)
						{
							Curmycard[wing[o]] = 1;
						}
						Cardnum += s;
						if (value > temp)
						{
							value = temp;
							bestcard.clear();
							for (int t = i; t < i + s; ++t)
							{
								pushcard(t, 3);
							}
							for (int t = 0; t < s; ++t)
							{
								pushcard(wing[t], 1);
							}
						}
					}

					//飞机带大翼
					if (pair >= s)
					{
						for (int o = 0; o < s; ++o)
						{
							Curmycard[wings[o]] = 0;
						}
						Cardnum -= s * 2;
						double score = 0;
						double temp = evaluate(0, 0, score);
						if (i <= 7 && s >= 2)
							temp = temp - 0.5 + extra_point(3, i);//根据i的大小来出牌
						else if (i <= 7)
							temp = temp - 0.3 + extra_point(3, i);
						else
							temp -= 0.1 + extra_point(6, i);
						if (Cardnum == 0)//能一下出完,太棒了!
							temp = -1e9;
						for (int o = 0; o < s; ++o)
						{
							Curmycard[wings[o]] = 2;
						}
						Cardnum += s * 2;
						for (int j = 0; j < s; ++j)
						{
							Curmycard[i + j] += 3;
						}
						Cardnum += 3 * s;
						if (value > temp)
						{
							value = temp;
							bestcard.clear();
							for (int t = i; t < i + s; ++t)
							{
								pushcard(t, 3);
							}
							for (int t = 0; t < s; ++t)
							{
								pushcard(wings[t], 2);
							}
						}
					}


				}
				begin = i + s;
			}

		}

		//出四带二
		{
			evaluate_prepare();
			for (int i = 0; i <= 12; ++i)
			{
				if (Curmycard[i] == 4)
				{
					evaluate_prepare();
					Curmycard[i] = 0;
					Cardnum -= 4;
					int _single[2], single = 0;
					int _pair[2], pair = 0;
					for (int t = 0; t < 12; ++t)
					{
						if (Curmycard[t] == 1 && single < 2)
						{
							_single[single] = t;
							single++;
						}
						else if (Curmycard[t] == 2 && pair < 2)
						{
							_pair[pair] = t;
							pair++;
						}
					}
					//四带二单张
					if (single == 2)
					{
						Curmycard[_single[0]] = Curmycard[_single[1]] = 0;
						Cardnum -= 2;
						double score = 0;
						double temp = evaluate(0, 0, score);
						temp += extra_point(5, i);
						if (Cardnum == 0)//能一下出完,太棒了!
							temp = -1e9;
						Curmycard[_single[0]] = Curmycard[_single[1]] = 1;
						Cardnum += 2;
						if (value > temp)
						{
							bestcard.clear();
							value = temp;
							for (int j = 0; j < 4; ++j)
								bestcard.push_back(Card(4 * i + j));
							for (int j = 0; j < 2; ++j)
								pushcard(_single[j], 1);
						}
					}
					//四带二对子
					if (pair == 2)
					{
						Curmycard[_pair[0]] = Curmycard[_pair[1]] = 0;
						Cardnum -= 4;
						double score = 0;
						double temp = evaluate(0, 0, score);
						temp += extra_point(6, i);
						if (Cardnum == 0)//能一下出完,太棒了!
							temp = -1e9;
						Curmycard[_pair[0]] = Curmycard[_pair[1]] = 2;
						Cardnum += 4;
						if (value > temp)
						{
							bestcard.clear();
							value = temp;
							for (int j = 0; j < 4; ++j)
								bestcard.push_back(Card(4 * i + j));
							for (int j = 0; j < 2; ++j)
								pushcard(_pair[j], 2);
						}
					}
				}
			}
		}
	}

	if (last_player == 2)//自己前一把打的牌没人要
	{
		return CardCombo(bestcard.begin(), bestcard.end());
	}
	else if (last_player == 1)//队友出的牌
	{
		if (value >= cur_score || cardRemaining[who_last_player] <= 3)
			return CardCombo();
		else
			return CardCombo(bestcard.begin(), bestcard.end());
	}
	else
	{
		if (myPosition == 0 && value - cur_score >= 4 && cardRemaining[who_last_player] >= 5)
			return CardCombo();
		else if (value - cur_score >= 3 && cardRemaining[who_last_player] > 5 && myPosition == 1)
			return CardCombo();
		else if (value - cur_score >= 4 && cardRemaining[who_last_player] > 5 && myPosition == 2)
			return CardCombo();
		else
			return CardCombo(bestcard.begin(), bestcard.end());
	}
}
/*
自己写的部分---------------------------------------------------------------------我是萌萌的分割线
*/

namespace BotzoneIO
{
	using namespace std;
	void input()
	{
		// 读入输入(平台上的输入是单行)
		string line;
		getline(cin, line);
		Json::Value input;
		Json::Reader reader;
		reader.parse(line, input);

		// 首先处理第一回合,得知自己是谁、有哪些牌
		{
			auto firstRequest = input["requests"][0u]; // 下标需要是 unsigned,可以通过在数字后面加u来做到
			auto own = firstRequest["own"];
			auto llpublic = firstRequest["public"];
			auto history = firstRequest["history"];
			for (unsigned i = 0; i < own.size(); i++)
				myCards.insert(own[i].asInt());
			for (unsigned i = 0; i < llpublic.size(); i++)
				landlordPublicCards.insert(llpublic[i].asInt());
			if (history[0u].size() == 0)
				if (history[1].size() == 0)
					myPosition = 0; // 上上家和上家都没出牌,说明是地主
				else
					myPosition = 1; // 上上家没出牌,但是上家出牌了,说明是农民甲
			else
				myPosition = 2; // 上上家出牌了,说明是农民乙
		}

		// history里第一项(上上家)和第二项(上家)分别是谁的决策
		int whoInHistory[] = { (myPosition - 2 + PLAYER_COUNT) % PLAYER_COUNT, (myPosition - 1 + PLAYER_COUNT) % PLAYER_COUNT };

		int turn = input["requests"].size();
		for (int i = 0; i < turn; i++)
		{
			// 逐次恢复局面到当前
			auto history = input["requests"][i]["history"]; // 每个历史中有上家和上上家出的牌
			int howManyPass = 0;
			for (int p = 0; p < 2; p++)
			{
				int player = whoInHistory[p]; // 是谁出的牌
				auto playerAction = history[p]; // 出的哪些牌
				vector playedCards;
				for (unsigned _ = 0; _ < playerAction.size(); _++) // 循环枚举这个人出的所有牌
				{
					int card = playerAction[_].asInt(); // 这里是出的一张牌
					playedCards.push_back(card);
				}
				whatTheyPlayed[player].push_back(playedCards); // 记录这段历史
				cardRemaining[player] -= playerAction.size();

				if (playerAction.size() == 0)
					howManyPass++;
				else
					lastValidCombo = CardCombo(playedCards.begin(), playedCards.end());
			}

			if (howManyPass == 2)
				lastValidCombo = CardCombo();

			if (i < turn - 1)
			{
				// 还要恢复自己曾经出过的牌
				auto playerAction = input["responses"][i]; // 出的哪些牌
				vector playedCards;
				for (unsigned _ = 0; _ < playerAction.size(); _++) // 循环枚举自己出的所有牌
				{
					int card = playerAction[_].asInt(); // 这里是自己出的一张牌
					myCards.erase(card); // 从自己手牌中删掉
					playedCards.push_back(card);
				}
				whatTheyPlayed[myPosition].push_back(playedCards); // 记录这段历史
				cardRemaining[myPosition] -= playerAction.size();
			}
		}
	}

	/**
	* 输出决策,begin是迭代器起点,end是迭代器终点
	* CARD_ITERATOR是Card(即short)类型的迭代器
	*/
	template 
	void output(CARD_ITERATOR begin, CARD_ITERATOR end)
	{
		Json::Value result, response(Json::arrayValue);
		for (; begin != end; begin++)
			response.append(*begin);
		result["response"] = response;

		Json::FastWriter writer;
		cout << writer.write(result) << endl;
	}
}

//构造一个remain_card的集合,里面是对面出剩的牌
void insert_all_card()
{
	for (int i = 0; i < 53; ++i)
		remain_card.insert(Card(i));
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < whatTheyPlayed[i].size(); ++j)
			for (int k = 0; k < whatTheyPlayed[i][j].size(); ++k)
			{
				remain_card.erase(whatTheyPlayed[i][j][k]);
			}
	}
	for (auto i = myCards.begin(); i != myCards.end(); ++i)
	{
		remain_card.erase(*i);
	}
	return;
}

int main()
{
	BotzoneIO::input();
	insert_all_card();
	// 是合法牌
	CardCombo myAction = CardCombo();
	try {
		myAction = findBestValld();
		assert(myAction.comboType != CardComboType::INVALID);

		assert(
			// 在上家没过牌的时候过牌
			(lastValidCombo.comboType != CardComboType::PASS && myAction.comboType == CardComboType::PASS) ||
			// 在上家没过牌的时候出打得过的牌
			(lastValidCombo.comboType != CardComboType::PASS && lastValidCombo.canBeBeatenBy(myAction)) ||
			// 在上家过牌的时候出合法牌
			(lastValidCombo.comboType == CardComboType::PASS && myAction.comboType != CardComboType::INVALID)
		);
	}
	catch (exception& e)
	{
		myAction = lastValidCombo.findFirstValid(myCards.begin(), myCards.end());
	}


	//// 决策结束,输出结果(你只需修改以上部分)

	BotzoneIO::output(myAction.cards.begin(), myAction.cards.end());
}
 
 

 

 

对战平台(由聪巨开发)

 

 

#include 
#include 
#include 
#include 
#include 
#include 
using namespace std;

template 
fstream& operator << (fstream& fout,vector& v)
{
    fout<<'[';
    if(v.begin()==v.end()) {fout<<']'; return fout;}
    for(auto it=v.begin();it!=v.end()-1;it++) fout<<*it<<',';
    fout<<*v.rbegin()<<']';
    return fout;
}

int main()
{

    string in="in.txt";
    string out="out.txt";
    string botadd="/Users/16000/Desktop/斗地主/";
    string bot[2]={"bot8","bot7"};
    int score[2]={0,0};

    for(int r=0;r<10000;r++)
    {
        vector publiccard;
        vector own[3];
        int a[54];
        for(int i=0;i<54;i++) a[i]=1;
        for(int i=0;i<3;) {int x=rand()%54;if(a[x]) {publiccard.push_back(x);own[0].push_back(x);a[x]=0;i++;}}
        for(int i=0;i<3;i++) for(int j=0;j<17;) {int x=rand()%54;  if(a[x]) {own[i].push_back(x);a[x]=0;j++;}}
        for(int o=0;o<=1;o++)
        {
            vector> his;
            vector a;
            int num[3]={20,17,17};
            int over=0;
            his.push_back(a);
            his.push_back(a);

            int n=0;//第几回合
            while(++n)
            {
                for(int i=0;i<3;i++)//0地主 1农民甲 2农民乙
                {
                    fstream fout;
                    fout.open(in, ios::out);
                    vector> first(his.begin()+i,his.begin()+i+2);
                    fout<<"{\"requests\":[{\"history\":";
                    fout<> responses;
                        vector>> history;
                        for(auto it=his.begin()+i+2;it tmp1=*it;
                            vector> tmp2(it+1,it+3);
                            responses.push_back(tmp1);
                            history.push_back(tmp2);
                        }
                        for(auto v:history)
                        {
                            fout<<",{\"history\":";
                            fout< " + out;
                    system(str.c_str());


                    fstream fin(out, ios::in);

                    vector tmp;
                    char c;
                    int m;
                    fin.get(c);
                    for(int i=1;i<14;i++) fin.get(c);
                    if(c!=']')
                    {
                        m=c-'0';
                        fin.get(c);
                        if(c>='0'&&c<='9') {m=c-'0'+10*m;fin.get(c);}
                        tmp.push_back(m);
                    }
                    while (c!=']')
                    {
                        fin>>m;
                        tmp.push_back(m);
                        fin.get(c);
                    }
                    fin.close();
                    his.push_back(tmp);
                    num[i]-=tmp.size();
                    if(!num[i]) {score[abs(o-(i+1)/2)]++; over=1; break;}
                }
                if(over) break;
            }
        }
        cout<

 

 

 

 

你可能感兴趣的:(Botzone斗地主算法的简单思路)