牌型权重
给定一组手牌,怎么计算它的权重呢?我们可以把手牌切分成不同的牌型,一次出牌只能出一个牌型(CardNode),手上有多少个Node就是手数。 我们可以对每种牌型,同一个牌型的每个面值,设置不同的权重。权重可以是正值,也可以是负值。
斗地主以跑牌成功为胜利,手上多一张牌,就多一分负担(。 我们可以考虑以厌恶度来衡量每张牌型的权重。如果出完手上的牌,那么手数为0,权重设为0;以此为基础,面值越小,权重越低,每多一手数就再降低一定的权重。//牌型结构体变量的定义
// 权重计算
#define kOneHandPower (-150) //是每多一手数,就需要降低的权重,
#define kPowerUnit (-100)//是基础厌恶值, 我们通过对不同的牌型,设置不同的bad度,最后得出牌型的权重:
#define kMinPowerValue (-1000000000.0f)//最小权重值
版型CardNode.h头文件:
#define kOneHandPower (-150) //是每多一手数,就需要降低的权重,
#define kPowerUnit (-100)//是基础厌恶值, 我们通过对不同的牌型,设置不同的bad度,最后得出牌型的权重:
#define kMinPowerValue (-1000000000.0f)//最小权重值
//合并两个vector
template <typename Ele>
void mergeTwoVectors(std::vector<Ele> & dest, const std::vector<Ele> &src) {
dest.insert(dest.end(), src.begin(), src.end());
}
//牌型结构体
struct CardNode
{
int32_t cardType;//牌型三种: 1 不是连续的牌,包括三条带 ,2 连续的牌 包括顺子 双顺 飞机 , 3 火箭 也就是双王
int32_t value ;//牌的值是指单纯类型为牌的面值,连续类型为起始牌的面值,相同牌型以此比较大小;
int32_t mainNum; //主牌张数
int32_t seralNum;//连续张数
int32_t subNum;//副牌张数(三条 四条所带的牌)
float aggregate;//权重
std::vector<int> cards;//所有的牌
public:
CardNode();
CardNode(int type, int val, int mainN, int len, int sub);
CardNode(const CardNode &other);
//赋值
CardNode & operator = (const CardNode &other);
bool isValidNode() const;
//重置为0
void resetNode();
//获取最大牌值
int getTopValue() const;
//把版型变为王炸
void fillJokers() ;
//合并牌型的牌
void merge(const CardNode & other);
//是否火箭
bool isRocket() const;
//是否炸弹
bool isBomb() const;
//比较 是否比Other牌型小
bool isExactLessThan(const CardNode & other) const;
//比较 是否比Other牌型小
bool isStrictLessThan(const CardNode &other) const;
//获取牌型的权重
float getPower() const;
//比较 是否小于
bool operator < (const CardNode & other) const;
//是否相等
bool isEqualTo(const CardNode & other) const;
//获取牌的花色和值字符串
std::string description() const ;
};
版型CardNode.cpp文件:
CardNode::CardNode() {
resetNode();
}
CardNode::CardNode(int type, int val, int mainN, int len, int sub) {
cardType = type;
value = val;
mainNum = mainN;
seralNum = len;
subNum = sub;
aggregate = 0.0f;
cards.clear();
}
CardNode::CardNode(const CardNode &other) {
cardType = other.cardType;
mainNum = other.mainNum;
value = other.value;
seralNum = other.seralNum;
subNum = other.subNum;
aggregate = other.aggregate;
cards = other.cards;
}
CardNode & CardNode::operator = (const CardNode &other) {
cardType = other.cardType;
mainNum = other.mainNum;
value = other.value;
seralNum = other.seralNum;
subNum = other.subNum;
aggregate = other.aggregate;
cards = other.cards;
return *this;
}
//是否是一种牌型
bool CardNode::isValidNode() const {
return mainNum != 0;
}
void CardNode::resetNode() {
cardType = 0;
value = 0;
mainNum = 0;
seralNum = 0;
subNum = 0;
aggregate = 0.0f;
cards.clear();
}
//获取最大一张牌的值
int CardNode::getTopValue() const {
int top = value;
//顺子 或 连对 或飞机
if (cardType == kCardType_Serial) {
//牌的起始值+连续长度-1
top = value + seralNum - 1;
}
return top;
}
//把大小王放到牌中
void CardNode::fillJokers() {
cards.clear();
if (cardType == kCardType_Rocket) {
cards.push_back(kCard_Joker1);
cards.push_back(kCard_Joker2);
} else {
assert(false);
}
}
//是否火箭
bool CardNode::isRocket() const {
return cardType == kCardType_Rocket;
}
bool CardNode::isBomb() const {
return (seralNum==1 && mainNum >= 4 && subNum == 0);
}
// 比较是否比同一个牌型other小
bool CardNode::isExactLessThan(const CardNode & other) const {
if (!isValidNode()) {
return true;
}
return (cardType == other.cardType && mainNum == other.mainNum
&& subNum == other.subNum && seralNum == other.seralNum
&& getTopValue() < other.getTopValue());
}
// 比较炸弹是否比other小
bool CardNode::isStrictLessThan(const CardNode &other) const {
if (!isValidNode())
return true;
if (isRocket()) {//自己是火箭最大
return false;
}
if (other.isRocket()) {//它是火箭,肯定比它小
return true;
}
if (isBomb() && other.isBomb()) {
return getTopValue() < other.getTopValue();
} else if (isBomb()) {
return false;
} else if (other.isBomb()) {
return true;
}
return isExactLessThan(other);
}
//合并两个型中的牌
void CardNode::merge(const CardNode & other) {
mergeTwoVectors(cards, other.cards);
}
// 计算当前牌型的权重,全部是预估的;应该用AI来估算会更准确
//会返回每个节点的权重。对于某些大牌,权重为正,剩下的牌型,越厌恶的权重越小
float CardNode::getPower() const {
float bad = 0.0f;
//牌型为火箭
if (cardType == kCardType_Rocket)
{
bad = -8.0f; // -1.5 * 4.2f
}
else
{
//最高值为 牌值大小*2+连续张数 再除以2
//假如对2的最高值为(15+15+1)/2=15.5 对3的最高值为: (3+3+1)/2=3.5
//334455这样的牌型值为:(3+3+3)/2=4.5
float top = ( value + value + seralNum)/2.0f;
if (mainNum == 4)//如果为炸弹
{
if (subNum)//四张带了牌,权重变低
{
bad = -1.5 * 3.0f + 0.003 * (kCard_Value2 - top) + (seralNum > 1 ? seralNum : 0) * 0.002 - subNum * 0.002;
}
else if (value == kCard_Value2)//四张2
{
bad = -1.5f * 3.1f;
}
else //其它的四张
{
bad = -1.5f * 4.0f + 0.175 * (kCard_Value2 - top) + (seralNum > 1 ? seralNum : 0) * 0.002;
}
}
else if (mainNum == 3)//三张
{
bad = 0.433 + 0.02 * (kCard_Value2 - top) + (seralNum > 1 ? seralNum : 0) * 0.02 - subNum * 0.01;
}
else if (mainNum == 2) //对子
{
bad = 0.437 + 0.015 * (kCard_Value2 - top) + (seralNum > 2 ? seralNum : 0) * 0.02;
}
else // 单牌
{
bad = 0.435 + 0.0151 * (kCard_Value2 - top) + (seralNum > 4 ? seralNum : 0) * 0.02;
}
}
float ret = kOneHandPower + kPowerUnit * bad;
return ret;
}
//比较牌是否比Other小
bool CardNode::operator < (const CardNode & other) const {
if (mainNum != other.mainNum) {
return mainNum > other.mainNum;
}
if (value != other.value) {
return value < other.value;
}
if (cardType != other.cardType) {
return cardType < other.cardType;
}
if (seralNum != other.seralNum) {
return seralNum < other.seralNum;
}
if (cards.size() != other.cards.size()) {
return cards.size() < other.cards.size();
}
for (int i=0; i<cards.size(); ++i) {
if (cards[i] != other.cards[i]) {
return cards[i] < other.cards[i];
}
}
return false;
}
//比较牌是否和Other相等
bool CardNode::isEqualTo(const CardNode & other) const {
if (isRocket() && other.isRocket()) {
return true;
}
if (mainNum == other.mainNum && value == other.value
&& seralNum == other.seralNum && subNum == other.subNum) {
return cards == other.cards;
} else {
return false;
}
}
static const char * cardSuits[] = {"♣️", "♦️", "♥️", "♠️"};
static const char * cardValues[] = {"", "1", "2", "3", "4", "5", "6", "7", "8", "9", "T", "J", "Q", "K", "A", "2"};
//获取牌的花色和值字符串
std::string CardNode::description() const {
std::string ret = "";
for (int i=0; i<cards.size(); ++i) {
if (cards[i] == kCard_Flower) {
ret += "🌹 ";
} else if (cards[i] == kCard_Joker1) {
ret += "jj ";
} else if (cards[i] == kCard_Joker2) {
ret += "JJ ";
} else {
ret += cardSuits[LordCards::getCardSuit(cards[i])];
ret += cardValues[LordCards::getCardValue(cards[i])];
ret += " ";
}
}
return ret;
}
// 计算当前牌型的权重,全部是预估的;应该用AI来估算会更准确
float CardNode::getPower() const {
float bad = 0.0f;
//牌型为火箭
if (cardType == kCardType_Rocket) {
bad = -8.0f; // -1.5 * 4.2f
} else {
//最高值为 牌值大小*2+连续张数 再除以2
//假如对2的最高值为(15+15+1)/2=15.5 对3的最高值为: (3+3+1)/2=3.5
//334455这样的牌型值为:(3+3+3)/2=4.5
float top = ( value + value + seralNum)/2.0f;
if (mainNum == 4)//如果为炸弹
{
if (subNum) {//四张带了牌,权重变低
bad = -1.5 * 3.0f + 0.003 * (kCard_Value2 - top) + (seralNum > 1 ? seralNum : 0) * 0.002 - subNum * 0.002;
} else if (value == kCard_Value2) { //四张2
bad = -1.5f * 3.1f;
} else {//其它的四张
bad = -1.5f * 4.0f + 0.175 * (kCard_Value2 - top) + (seralNum > 1 ? seralNum : 0) * 0.002;
}
} else if (mainNum == 3) {//三张
bad = 0.433 + 0.02 * (kCard_Value2 - top) + (seralNum > 1 ? seralNum : 0) * 0.02 - subNum * 0.01;
} else if (mainNum == 2) {//对子
bad = 0.437 + 0.015 * (kCard_Value2 - top) + (seralNum > 2 ? seralNum : 0) * 0.02;
} else { // 单牌
bad = 0.435 + 0.0151 * (kCard_Value2 - top) + (seralNum > 4 ? seralNum : 0) * 0.02;
}
}
float ret = kOneHandPower + kPowerUnit * bad;
return ret;
}
CardNode.getPower() 会返回每个节点的权重。对于某些大牌,权重为正,剩下的牌型,越厌恶的权重越小。
下面通过实例让你明白什么是权重
假如抓到这样一手牌

我列出所有能出的牌型(21种)的权重,其中火箭的权重达到650,其它都是负值,值的大小是由kOneHandPower (-150)和#kPowerUnit (-100)。决定 的,可以改变它们的值来改变权重的值,这里只注重大小,不注重正负!
0 "(7,6,5,4,3,)" "牌型:2,起始牌的值:3,主牌张数:1,连续张数:5,副牌数目:0,牌的权重:-217.845"
1 "(6,)" "牌型:1,起始牌的值:6,主牌张数:1,连续张数:1,副牌数目:0,牌的权重:-206.335"
2 "(14,14,13,13,12,12,)" "牌型:2,起始牌的值:12,主牌张数:2,连续张数:3,副牌数目:0,牌的权重:-201.95"
3 "(10,10,)" "牌型:1,起始牌的值:10,主牌张数:2,连续张数:1,副牌数目:0,牌的权重:-200.45"
4 "(11,)" "牌型:1,起始牌的值:11,主牌张数:1,连续张数:1,副牌数目:0,牌的权重:-198.785"
5 "(10,)" "牌型:1,起始牌的值:10,主牌张数:1,连续张数:1,副牌数目:0,牌的权重:-200.295"
6 "(14,13,12,11,10,)" "牌型:2,起始牌的值:10,主牌张数:1,连续张数:5,副牌数目:0,牌的权重:-207.275"
7 "(12,12,)" "牌型:1,起始牌的值:12,主牌张数:2,连续张数:1,副牌数目:0,牌的权重:-197.45"
8 "(13,13,)" "牌型:1,起始牌的值:13,主牌张数:2,连续张数:1,副牌数目:0,牌的权重:-195.95"
9 "(14,14,)" "牌型:1,起始牌的值:14,主牌张数:2,连续张数:1,副牌数目:0,牌的权重:-194.45"
10 "(12,)" "牌型:1,起始牌的值:12,主牌张数:1,连续张数:1,副牌数目:0,牌的权重:-197.275"
11 "(13,)" "牌型:1,起始牌的值:13,主牌张数:1,连续张数:1,副牌数目:0,牌的权重:-195.765"
12 "(14,)" "牌型:1,起始牌的值:14,主牌张数:1,连续张数:1,副牌数目:0,牌的权重:-194.255"
13 "(3,)" "牌型:1,起始牌的值:3,主牌张数:1,连续张数:1,副牌数目:0,牌的权重:-210.865"
14 "(4,)" "牌型:1,起始牌的值:4,主牌张数:1,连续张数:1,副牌数目:0,牌的权重:-209.355"
15 "(5,)" "牌型:1,起始牌的值:5,主牌张数:1,连续张数:1,副牌数目:0,牌的权重:-207.845"
16 "(6,6,)" "牌型:1,起始牌的值:6,主牌张数:2,连续张数:1,副牌数目:0,牌的权重:-206.45"
17 "(7,)" "牌型:1,起始牌的值:7,主牌张数:1,连续张数:1,副牌数目:0,牌的权重:-204.825"
18 "(17,16,)" "牌型:3,起始牌的值:16,主牌张数:1,连续张数:1,副牌数目:0,牌的权重:650"
19 "(16,)" "牌型:1,起始牌的值:16,主牌张数:1,连续张数:1,副牌数目:0,牌的权重:-191.235"
20 "(17,)" "牌型:1,起始牌的值:17,主牌张数:1,连续张数:1,副牌数目:0,牌的权重:-189.725"
看上面的列表,可以看出排在第一是34567顺子,为什么把它排在第一,这排是按什么算法排的,下一篇介绍:怎样列出所有能出的牌型和该出哪个牌型最对整个牌布局最好,也就是出哪个牌型对整个牌的权重最高!

探讨了在斗地主游戏中如何通过计算不同牌型的权重来评估手牌的优劣,权重计算综合考虑了牌型、面值及手数等因素。
6216

被折叠的 条评论
为什么被折叠?



