璀璨宝石

目录

〇,璀璨宝石

一,游戏数据

二,界面设计

三,输入接口

四,总控程序

五,游戏规则

1,洗牌

2,拿宝石

3,界面

4,拿牌

5,扣牌

6,购买已扣牌

7,拿贵族牌

8,游戏结束

9,胜负判断

10,完整代码V1

六,悔棋功能

1,记录棋谱

2,查看棋谱

3,悔棋操作

4,完整代码V2

七,BUG修复、代码优化

1,新增可拿牌提示

2,新增显示宝石和牌的总数、显示扣牌数

3,新增大小写兼容

4,修复退还宝石BUG

5,修复BUG——拿牌的时候宝石没有退还供应堆

6,修复BUG——扣牌之后没有校验宝石数

7,完整代码V3

八,规则对比

九,策略

十,拓展玩法

元素争夺拓展

终极成就拓展

全新战场拓展

领悟图腾拓展


〇,璀璨宝石

本文是我实现的双人对战,因为是一边设计一边写代码一边写博客,所以本文的中间代码很多都不是最新版本,代码以最终完整版为准。

也可以下载手机apk:哇喔桌游。因为是在线版的,所以我就不放安装包了。

璀璨宝石也叫宝石商人。

   

   

   

一,游戏数据

1,公共宝石

初始数量:红白黑绿蓝各4个,黄色万能宝石5个

2,公共牌

绿色低分牌,黄色中分牌,蓝色高分牌

牌的位置分为供应堆、牌池各色4张

3,贵族牌

4,个人数据

(1)个人牌:个人已购牌堆、个人暂扣牌堆

(2)个人宝石:6色宝石

#include<iostream>
using namespace std;

typedef enum {
    YELLOW,RED,WHITE,BLACK,GREEN,BLUE
}Color;

typedef struct {
    int x;  //待补充
}Card;
typedef struct {
    int x;  //待补充
}PointCard;

#define MaxTmpCard 3  //最大暂扣牌数,数值待确认
typedef struct {
    int jew[6];
    int card[5];
    Card tmpCard[3];
}Player;


int turn;//0-1
int jew[6];//公共宝石
const int LC = 1, MC = 1, HC = 1;//数值待确认
Card Lcard[LC], Mcard[MC], Hcard[HC];
const int PC = 1;
PointCard Pcard[PC];
Player player[2];

int main()
{
    
    return 0;
}

二,界面设计

void out()
{
    cout << "先手: 黄 红 白 黑 绿 蓝              后手: 黄 红 白 黑 绿 蓝\n";
    cout << "宝石: ";
    for (int i = 0; i < 6; i++)cout << player[0].jew[i] << "  ";
    cout << "             宝石: ";
    for (int i = 0; i < 6; i++)cout << player[1].jew[i] << "  ";
    cout << "\n已购牌:  ";
    for (int i = 0; i < 5; i++)cout << player[0].card[i] << "  ";
    cout << "             已购牌:  ";
    for (int i = 0; i < 5; i++)cout << player[1].card[i] << "  ";
    cout << "\n\n公共宝石:黄 红 白 黑 绿 蓝\n          ";
    for (int i = 0; i < 6; i++)cout << jew[i] << "  ";
    //公共牌
    //贵族牌
}

运行:

三,输入接口

int getColor(char c)
{
    if (c == 'R')return RED;
    if (c == 'W')return WHITE;
    if (c == 'B')return BLACK;
    if (c == 'G')return GREEN;
    if (c == 'U')return BLUE;
    return 0;
}

bool play(int player)
{
    out();
    cout << "输入操作对应数字:1拿宝石,2拿牌,3扣牌,4买自己的已扣牌\n";
    int op;
    CIN(op);
    if (op == 1) {
        cout << "输入最多3个字母:RWBGU分别表示红白黑绿蓝,如RWB表示拿3个宝石,如GG表示拿2个绿宝石\n";
        string s;
        cin >> s;
        s += "   ";
        int c1, c2, c3;
        c1 = getColor(s[0]), c2 = getColor(s[1]), c3 = getColor(s[2]);
        
    }
    if (op == 2) {
        cout << "输入目标牌所在的行(1-3)列(1-4)\n";
        int r, c;
        CIN2(r, c);
        
    }
    if (op == 3) {
        cout << "输入目标牌所在的行(1-3)列(0-4)\n";
        int r, c;
        CIN2(r, c);
        
    }
    if (op == 4) {
        cout << "输入目标牌的序号";
        int id;
        CIN(id);
        
    }
    return false;
}

四,总控程序

int main()
{
    while (true) {
        while (!play(turn));
        turn = 1 - turn;
    }
    return 0;
}

PS:

这里没写游戏结束处理,先把游戏运行过程中的总控写了,先跑起来

五,游戏规则

1,洗牌

需要设置随机种子,不然每次洗牌结果都不变就不好玩了。

template<typename T>
void shuf(T a[], int len)//洗牌
{
    srand(time(NULL));
    for (int i = 0; i < len * len * 100; i++) {
        int x = rand() % len, y = rand() % len;
        exchange(a[x], a[y]);
    }
}

void init()
{
    shuf(Pcard, PC);
    shuf(Lcard, LC);
    shuf(Mcard, MC);
    shuf(Hcard, HC);
}

2,拿宝石

(1)可以拿最多三种不同颜色的宝石,或者两个单色宝石

bool checkJew(int &c1, int &c2, int &c3)//拿宝石的参数校验
{
    if (c1 == 0)return false;
    if (c2 == 0) {
        c3 = 0;
        return jew[c1]>0;
    }
    if (c1 == c2) {
        c3 = 0;
        return jew[c1] >= 4;
    }
    if (c3 == c1 || c3 == c2)return false;
    if (jew[c1] == 0 || jew[c2] == 0)return false;
    if (c3 && jew[c3] == 0)return false;
    return true;
}

(2)拿宝石

typedef struct {
    int jew[6];
    int sumJew;
    int card[5];
    Card tmpCard[3];
}Player;
void takeJew(int id, int c)
{
    player[id].jew[c]++, player[id].sumJew++;
    jew[c]--;
}
void takeJew(int id, int c1, int c2, int c3)
{
    takeJew(id, c1);
    takeJew(id, c2);
    takeJew(id, c3);
}

(3)回退宝石

如果拿完之后,宝石数量超过10,那么需要退还宝石

void rebackJew(int id)//退还宝石
{
    if (player[id].sumJew <= 10)return;
    out();
    cout << "宝石总数为" << player[id].sumJew << "个,需要退还" << player[id].sumJew-10 << "个\n";
    cout << "请输入白蓝绿红黑各色宝石退还数量,如1 0 1 0 0表示退还1个白宝石和1个绿宝石\n";
    int x[5];
    for (int i = 0; i < 5; i++)CIN(x[i]);
    for (int i = 0; i < 5; i++)if (player[id].jew[i + 1] < x[i] || x[i] < 0)return rebackJew(id);
    for (int i = 0; i < 5; i++)player[id].jew[i + 1] -= x[i];
    rebackJew(id);
}

(4)拿宝石完整处理

bool op1(int id)//操作1拿宝石
{
    cout << "输入最多3个字母:WUGRB分别表示白蓝绿红黑,如RWB表示拿3个宝石,如GG表示拿2个绿宝石\n";
    string s;
    cin >> s;
    s += "   ";
    int c1, c2, c3;
    c1 = getColor(s[0]), c2 = getColor(s[1]), c3 = getColor(s[2]);
    if (!checkJew(c1, c2, c3))return false;
    takeJew(id, c1, c2, c3);
    rebackJew(id);
    return true;
}

3,界面

补全界面代码,确认随机函数正常工作。

template<typename T>
void out(T card)
{
    int flag = 0;
    for (int j = 0; j < 5; j++)if (card.cost[j]) {
        cout << card.cost[j] << strColor[j + 1];
        flag++;
    }
    if (flag)cout << "   ";
    for (int i = 0; i < 5 - flag; i++)cout << "   ";
}
void out(Card card[], int num)
{
    cout << "有 " << num << "张   ";
    for (int i = 0; i < 4 && i < num; i++) {
        cout << card[i].point << "分";
        cout << strColor[card[i].color] << "色牌 ";
        out(card[i]);
    }
}
void out()
{
    system("cls");
    string s = turn ? "轮到后手操作" : "轮到先手操作";
    cout << s << "\n先手: 黄 白 蓝 绿 红 黑              后手: 黄 白 蓝 绿 红 黑\n";
    cout << "宝石: ";
    for (int i = 0; i < 6; i++)cout << player[0].jew[i] << "  ";
    cout << "             宝石: ";
    for (int i = 0; i < 6; i++)cout << player[1].jew[i] << "  ";
    cout << "\n已购牌:  ";
    for (int i = 0; i < 5; i++)cout << player[0].card[i] << "  ";
    cout << "             已购牌:  ";
    for (int i = 0; i < 5; i++)cout << player[1].card[i] << "  ";
    cout << "\n\n公共宝石:黄 白 蓝 绿 红 黑\n          ";
    for (int i = 0; i < 6; i++)cout << jew[i] << "  ";
    cout << "\n\n贵族牌(每张三分)         ";
    for (int i = 0; i < pointNum; i++) {
        out(Pcard[i]);
    }
    cout << "\n\n发展卡: 列0    列1                         列2                         列3                         列4\n";
    cout << "高分牌";
    out(Hcard, highNum);
    cout << "\n中分牌";
    out(Mcard, midNum);
    cout << "\n低分牌";
    out(Lcard, lowNum);
}

4,拿牌

在写拿牌的代码的时候,我意识到用3个数组分别存低分牌中分牌高分牌的话,代码不好写,还是用统一的数组来存比较简洁。

int cardNum[3] = { LC,MC,HC };//发展卡数量
Card* card[3] = { Lcard ,Mcard ,Hcard };

这样代码就好写了:

bool checkCard(int id, int r, int c)//拿牌参数校验
{
    if (r < 0 || r>=3 || c < 0 || c>=4)return false;
    if (c > cardNum[r])return false;
    int s = 0;
    for (int i = 0; i < 5; i++) {
        s = max(s, s + card[r][c].cost[i] - player[id].card[i] - player[id].jew[i + 1]);
    }
    return s<= player[id].jew[0];
}

void takeCard(int id, int r, int c)//拿牌
{
    for (int i = 0; i < 5; i++) {
        player[id].jew[i + 1]-=card[r][c].cost[i] - player[id].card[i];
        if (player[id].jew[i + 1] < 0)player[id].jew[0] += player[id].jew[i + 1], player[id].jew[i + 1] = 0;
    }
    player[id].point += card[r][c].point;
    player[id].card[card[r][c].color]++;
}

void disCard(int r, int c)//发牌
{
    exchange(card[r][c], card[r][--cardNum[r]]);
}

bool op2(int id)//操作2拿牌
{
    cout << "输入目标牌所在的行(1-3)列(1-4)\n";
    int r, c;
    CIN2(r, c);
    r = 3 - r, c--;
    if (!checkCard(id, r, c))return false;
    takeCard(id, r, c);
    disCard(r, c);
    return true;
}

5,扣牌

扣牌的规则是,如果扣暗置牌堆的牌,是不需要公示的。

如果有万能宝石那就拿1个,如果没有那就仅扣牌不拿宝石。

bool op3(int id)//操作3扣牌
{
    cout << "输入目标牌所在的行(1-3)列(0-4)\n";
    int r, c;
    CIN2(r, c);
    r = 3 - r, c = c ? c - 1 : 4;
    if (r < 0 || r >= 3 || c < 0 || c > 4 || player[id].tmpNum >= 3)return false;
    player[id].tmpCard[player[id].tmpNum++] = card[r][c];
    if (jew[0])jew[0]--, player[id].jew[0]++, player[id].sumJew++;
    disCard(r, c);
    return true;
}

6,购买已扣牌

服用拿牌的接口即可

bool op5(int id)//操作5购买已扣牌
{
    cout << "输入目标牌的序号: 1-3";
    int x;
    CIN(x);
    x--;
    if (x < 0 || x >= player[id].tmpNum)return false;
    if (!checkCardCost(id, player[id].tmpCard[x]))return false;
    takeCard(id, player[id].tmpCard[x]);
    exchange(player[id].tmpCard[x], player[id].tmpCard[--player[id].tmpNum]);
    return true;
}

至此,play函数变成:

typedef bool (*pfun)(int id);

bool play(int id)
{
    out();
    cout << "\n\n输入操作对应数字:1拿宝石,2拿牌,3扣牌,4查看已扣牌,5购买已扣牌\n";
    int op;
    CIN(op);
    if (op < 1 || op>5)return false;
    pfun p[] = { op1,op2,op3,op4,op5 };
    return p[--op](id);
}

7,拿贵族牌

每个回合只能拿一张贵族牌

void op6(int id)//操作6拿贵族牌
{
    int takeAble[] = { 0,0,0 };
    for (int i = 0; i < pointNum; i++) {
        takeAble[i] = 1;
        for (int j = 0; j < 5; j++)if (player[id].card[j] < Pcard[i].cost[j])takeAble[i] = 0;
    }
    int s = takeAble[0] + takeAble[1] + takeAble[2];
    if (!s)return;
    cout << "请拿贵族牌:";
    for (int i = 0; i < pointNum; i++) {
        cout << i + 1;
        if (!takeAble[i])cout << "不";
        cout << "可拿 ";
    }
    int x;
    CIN(x);
    if (--x < 0 || x>=pointNum || takeAble[x]==0)return op6(id);
    exchange(Pcard[x], Pcard[--pointNum]);
    player[id].point += 3;
}

8,游戏结束

当一方玩家达到15分及以上,游戏结束,如果先手率先达到15分及以上,那么后手还有最后一次操作。

bool end()
{
    if (turn)return false;
    return player[0].point >= 15 || player[1].point >= 15;
}

int main()
{
    init();
    while (!end()) {
        while (!play(turn));
        turn = 1 - turn;
    }
    return 0;
}

9,胜负判断

得分高的胜,得分相同的话发展卡少的胜,发展卡也相同的话平局。

int gameRes()//0先手胜 1后手胜 2平局
{
    if (player[0].point == player[1].point) {
        int s = 0;
        for (int i = 0; i < 5; i++)s += player[0].card[i] - player[1].card[i];
        return s ? (s > 0 ? 0 : 1) : 2;
    }
    return player[0].point > player[1].point ? 0 : 1;
}

10,完整代码V1

#include<iostream>
#include<windows.h>
#include<time.h>
using namespace std;

typedef enum {
    Y, W, U, G, R, B
}Color;
string strColor[6] = { "黄","白", "蓝", "绿", "红", "黑" };

typedef struct {
    int point;
    int color;
    int cost[5];
}Card;//发展卡
typedef struct {
    int cost[5];
}PointCard;//贵族得分牌

typedef struct {
    int jew[6];//各色宝石数量,0-5六种颜色
    int sumJew;//宝石总数
    int card[5];//各色卡牌数量
    int tmpNum;//暂扣牌数量
    Card tmpCard[3];//暂扣牌,最多三张
    int point;//得分
}Player; //玩家数据

const int PC = 10; //贵族得分牌
PointCard Pcard[PC] = {
    3,3,3,0,0,    3,3,0,0,3,    3,0,0,3,3,    0,0,3,3,3,    0,3,3,3,0,
    4,4,0,0,0,    4,0,0,0,4,    0,0,0,4,4,    0,0,4,4,0,    0,4,4,0,0
};
const int LC = 40, MC = 30, HC = 20; //低分牌,中分牌,高分牌
Card Lcard[LC] = {
    1,W,0,0,4,0,0,    1,U,0,0,0,4,0,    1,G,0,0,0,0,4,    1,R,4,0,0,0,0,    1,B,0,4,0,0,0,
    0,W,0,3,0,0,0,    0,U,0,0,0,0,3,    0,G,0,0,0,3,0,    0,R,3,0,0,0,0,    0,B,0,0,3,0,0,
    0,W,0,0,0,2,1,    0,U,1,0,0,0,2,    0,G,2,1,0,0,0,    0,R,0,2,1,0,0,    0,B,0,0,2,1,0,
    0,W,0,1,1,1,1,    0,U,1,0,1,1,1,    0,G,1,1,0,1,1,    0,R,1,1,1,0,1,    0,B,1,1,1,1,0,
    0,W,0,2,0,0,2,    0,U,0,0,2,0,2,    0,G,0,2,0,2,0,    0,R,2,0,0,2,0,    0,B,2,0,2,0,0,
    0,W,0,2,2,0,1,    0,U,1,0,2,2,0,    0,G,0,1,0,2,2,    0,R,2,0,1,0,2,    0,B,2,2,0,1,0,
    0,W,0,1,2,1,1,    0,U,1,0,1,2,1,    0,G,1,1,0,1,2,    0,R,2,1,1,0,1,    0,B,1,2,1,1,0,
    0,W,3,1,0,0,1,    0,U,0,1,3,1,0,    0,G,1,3,1,0,0,    0,R,3,1,0,0,1,    0,B,1,0,0,1,3
};
Card Mcard[MC] = {
    3,W,6,0,0,0,0,    3,U,0,6,0,0,0,    3,G,0,0,6,0,0,    3,R,0,0,0,6,0,    3,B,0,0,0,0,6,
    2,W,0,0,0,5,0,    2,U,0,5,0,0,0,    2,G,0,0,5,0,0,    2,R,0,0,0,0,5,    2,B,5,0,0,0,0,
    2,W,0,0,0,5,3,    2,U,5,3,0,0,0,    2,G,0,5,3,0,0,    2,R,3,0,0,0,5,    2,B,0,0,5,3,0,
    2,W,0,0,1,4,2,    2,U,2,0,0,1,4,    2,G,4,2,0,0,1,    2,R,1,4,2,0,0,    2,B,0,1,4,2,0,
    1,W,0,0,3,2,2,    1,U,0,2,2,3,0,    1,G,2,3,0,0,2,    1,R,2,0,0,2,3,    1,B,3,2,2,0,0,
    1,W,2,3,0,3,0,    1,U,0,2,3,0,3,    1,G,3,0,2,3,0,    1,R,0,3,0,2,3,    1,B,3,0,3,0,2
};
Card Hcard[HC] = {
    5,W,3,0,0,0,7,    5,U,7,3,0,0,0,    5,G,0,7,3,0,0,    5,R,0,0,7,3,0,    5,B,0,0,0,7,3,
    4,W,0,0,0,0,7,    4,U,7,0,0,0,0,    4,G,0,7,0,0,0,    4,R,0,0,7,0,0,    4,B,0,0,0,7,0,
    4,W,3,0,0,3,6,    4,U,6,3,0,0,3,    4,G,3,6,3,0,0,    4,R,0,3,6,3,0,    4,B,0,0,3,6,3,
    3,W,0,3,3,5,3,    3,U,3,0,3,3,5,    3,G,5,3,0,3,3,    3,R,3,5,3,0,3,    3,B,3,3,5,3,0
};


int turn;//0-1
int jew[6] = { 5,4,4,4,4,4 };//公共宝石
Player player[2];
int pointNum=3;//贵族得分牌数量
int cardNum[3] = { LC,MC,HC };//发展卡数量
Card* card[3] = { Lcard ,Mcard ,Hcard };

template<typename T>
void out(T card)
{
    int flag = 0;
    for (int j = 0; j < 5; j++)if (card.cost[j]) {
        cout << card.cost[j] << strColor[j + 1];
        flag++;
    }
    for (int i = 0; i < 5 - flag; i++)cout << "   ";
}
void out(Card card[], int num)
{
    cout << "有 " << num << "张   ";
    for (int i = 0; i < 4 && i < num; i++) {
        cout << card[i].point << "分";
        cout << strColor[card[i].color] << "色牌 ";
        out(card[i]);
    }
}
void out()
{
    system("cls");
    string s = turn ? "轮到后手操作" : "轮到先手操作";
    cout << s << "\n先手: 黄 白 蓝 绿 红 黑              后手: 黄 白 蓝 绿 红 黑\n";
    cout << "宝石: ";
    for (int i = 0; i < 6; i++)cout << player[0].jew[i] << "  ";
    cout << "             宝石: ";
    for (int i = 0; i < 6; i++)cout << player[1].jew[i] << "  ";
    cout << "\n已购牌:  ";
    for (int i = 0; i < 5; i++)cout << player[0].card[i] << "  ";
    cout << "             已购牌:  ";
    for (int i = 0; i < 5; i++)cout << player[1].card[i] << "  ";
    cout << "\n得分: "<< player[0].point;
    cout << "                              得分: " << player[1].point;
    cout << "\n\n公共宝石:黄 白 蓝 绿 红 黑\n          ";
    for (int i = 0; i < 6; i++)cout << jew[i] << "  ";
    cout << "\n\n贵族牌(每张三分)         ";
    for (int i = 0; i < pointNum; i++) {
        out(Pcard[i]);
    }
    cout << "\n\n发展卡: 列0    列1                      列2                      列3                      列4\n";
    cout << "高分牌";
    out(Hcard, cardNum[2]);
    cout << "\n中分牌";
    out(Mcard, cardNum[1]);
    cout << "\n低分牌";
    out(Lcard, cardNum[0]);
}
template<typename T>
void exchange(T& a, T& b)
{
    T tmp = a;
    a = b, b = tmp;
}
template<typename T>
void shuf(T a[], int len)//洗牌
{
    srand(time(NULL));
    for (int i = 0; i < len * len * 100; i++) {
        int x = rand() % len, y = rand() % len;
        exchange(a[x], a[y]);
    }
}

void init()
{
    turn = 0;
    shuf(Pcard, PC);
    shuf(Lcard, LC);
    shuf(Mcard, MC);
    shuf(Hcard, HC);
}

#define CIN(x) while (!(cin >> x)) { \
        cin.clear();      \
        cin.ignore();     \
    }
#define CIN2(x, y) CIN(x)CIN(y)
#define CIN3(x, y, z) CIN(x)CIN(y)CIN(z)

int getColor(char c)
{
    if (c == 'W')return W;
    if (c == 'U')return U;
    if (c == 'G')return G;
    if (c == 'R')return R;
    if (c == 'B')return B;
    return 0;
}

bool checkJew(int &c1, int &c2, int &c3)//拿宝石的参数校验
{
    if (c1 == 0)return false;
    if (c2 == 0) {
        c3 = 0;
        return jew[c1]>0;
    }
    if (c1 == c2) {
        c3 = 0;
        return jew[c1] >= 4;
    }
    if (c3 == c1 || c3 == c2)return false;
    if (jew[c1] == 0 || jew[c2] == 0)return false;
    if (c3 && jew[c3] == 0)return false;
    return true;
}
void takeJew(int id, int c)//拿1个宝石
{
    if (c == 0)return;
    player[id].jew[c]++, player[id].sumJew++;
    jew[c]--;
}
void takeJew(int id, int c1, int c2, int c3)//拿宝石
{
    takeJew(id, c1);
    takeJew(id, c2);
    takeJew(id, c3);
}

void rebackJew(int id)//退还宝石
{
    if (player[id].sumJew <= 10)return;
    out();
    cout << "宝石总数为" << player[id].sumJew << "个,需要退还" << player[id].sumJew-10 << "个\n";
    cout << "请输入白蓝绿红黑各色宝石退还数量,如1 0 1 0 0表示退还1个白宝石和1个绿宝石\n";
    int x[5];
    for (int i = 0; i < 5; i++)CIN(x[i]);
    for (int i = 0; i < 5; i++)if (player[id].jew[i + 1] < x[i] || x[i] < 0)return rebackJew(id);
    for (int i = 0; i < 5; i++)player[id].jew[i + 1] -= x[i];
    rebackJew(id);
}

bool op1(int id)//操作1拿宝石
{
    cout << "输入最多3个字母:WUGRB分别表示白蓝绿红黑,如RWB表示拿3个宝石,如GG表示拿2个绿宝石\n";
    string s;
    cin >> s;
    s += "   ";
    int c1, c2, c3;
    c1 = getColor(s[0]), c2 = getColor(s[1]), c3 = getColor(s[2]);
    if (!checkJew(c1, c2, c3))return false;
    takeJew(id, c1, c2, c3);
    rebackJew(id);
    return true;
}

bool checkCardCost(int id, Card a)//拿牌参数校验
{
    int s = 0;
    for (int i = 0; i < 5; i++) {
        s = max(s, s + a.cost[i] - player[id].card[i] - player[id].jew[i + 1]);
    }
    return s <= player[id].jew[0];
}
bool checkCard(int id, int r, int c)//拿牌参数校验
{
    if (r < 0 || r>=3 || c < 0 || c>=4)return false;
    if (c > cardNum[r])return false;
    return checkCardCost(id, card[r][c]);
}
void takeCard(int id, Card a)//拿牌
{
    for (int i = 0; i < 5; i++) {
        player[id].jew[i + 1] -= a.cost[i] - player[id].card[i], player[id].sumJew -= a.cost[i] - player[id].card[i];
        if (player[id].jew[i + 1] < 0)player[id].jew[0] += player[id].jew[i + 1], player[id].jew[i + 1] = 0;
    }
    player[id].point += a.point;
    player[id].card[a.color]++;
}
void takeCard(int id, int r, int c)//拿牌
{
    takeCard(id, card[r][c]);
}

void disCard(int r, int c)//发牌
{
    exchange(card[r][c], card[r][--cardNum[r]]);
}

bool op2(int id)//操作2拿牌
{
    cout << "输入目标牌所在的行(1-3)列(1-4)\n";
    int r, c;
    CIN2(r, c);
    r = 3 - r, c--;
    if (!checkCard(id, r, c))return false;
    takeCard(id, r, c);
    disCard(r, c);
    return true;
}

bool op3(int id)//操作3扣牌
{
    cout << "输入目标牌所在的行(1-3)列(0-4)\n";
    int r, c;
    CIN2(r, c);
    r = 3 - r, c = c ? c - 1 : 4;
    if (r < 0 || r >= 3 || c < 0 || c > 4 || player[id].tmpNum >= 3)return false;
    player[id].tmpCard[player[id].tmpNum++] = card[r][c];
    if (jew[0])jew[0]--, player[id].jew[0]++, player[id].sumJew++;
    disCard(r, c);
    return true;
}

bool op4(int id)//操作4查看已扣牌
{
    system("cls");
    cout << "玩家准备查看已扣牌,其他玩家请回避\n";
    Sleep(2000);
    out(player[id].tmpCard, player[id].tmpNum);
    Sleep(5000);
    return false;
}

bool op5(int id)//操作5购买已扣牌
{
    cout << "输入目标牌的序号: 1-3";
    int x;
    CIN(x);
    x--;
    if (x < 0 || x >= player[id].tmpNum)return false;
    if (!checkCardCost(id, player[id].tmpCard[x]))return false;
    takeCard(id, player[id].tmpCard[x]);
    exchange(player[id].tmpCard[x], player[id].tmpCard[--player[id].tmpNum]);
    return true;
}

void op6(int id)//操作6拿贵族牌
{
    int takeAble[] = { 0,0,0 };
    for (int i = 0; i < pointNum; i++) {
        takeAble[i] = 1;
        for (int j = 0; j < 5; j++)if (player[id].card[j] < Pcard[i].cost[j])takeAble[i] = 0;
    }
    int s = takeAble[0] + takeAble[1] + takeAble[2];
    if (!s)return;
    cout << "请拿贵族牌:";
    for (int i = 0; i < pointNum; i++) {
        cout << i + 1;
        if (!takeAble[i])cout << "不";
        cout << "可拿 ";
    }
    int x;
    CIN(x);
    if (--x < 0 || x>=pointNum || takeAble[x]==0)return op6(id);
    exchange(Pcard[x], Pcard[--pointNum]);
    player[id].point += 3;
}

typedef bool (*pfun)(int id);

bool play(int id)
{
    out();
    cout << "\n\n输入操作对应数字:1拿宝石,2拿牌,3扣牌,4查看已扣牌,5购买已扣牌\n";
    int op;
    CIN(op);
    if (op < 1 || op>5)return false;
    pfun p[] = { op1,op2,op3,op4,op5 };
    if(!p[--op](id))return false;
    op6(id);
    return true;
}

bool end()
{
    if (turn)return false;
    return player[0].point >= 15 || player[1].point >= 15;
}

string strRes[3] = { "先手胜","后手胜","平局" };
int gameRes()
{
    if (player[0].point == player[1].point) {
        int s = 0;
        for (int i = 0; i < 5; i++)s += player[0].card[i] - player[1].card[i];
        return s ? (s > 0 ? 0 : 1) : 2;
    }
    return player[0].point > player[1].point ? 0 : 1;
}

int main()
{
    init();
    while (!end()) {
        while (!play(turn));
        turn = 1 - turn;
    }
    cout << strRes[gameRes()];
    return 0;
}

六,悔棋功能

1,记录棋谱

把所有的输入全部用字符串记下来即可。

完整代码V1中只有2个地方有输入,一个是CIN宏,一个是op1函数中输入需要拿的宝石颜色。

vector<string>chessbook; //棋谱

string NS = "";
#define CIN(x) do{while (!(cin >> x)) { \
        cin.clear();      \
        cin.ignore();     \
    };\
    chessbook.push_back(NS+char(x+'0'));}while(0);
#define CIN2(x, y) CIN(x)CIN(y)

bool op1(int id)//操作1拿宝石
{
    cout << "输入最多3个字母:WUGRB分别表示白蓝绿红黑,如RWB表示拿3个宝石,如GG表示拿2个绿宝石\n";
    string s;
    cin >> s;
    s += "   ";
    int c1, c2, c3;
    c1 = getColor(s[0]), c2 = getColor(s[1]), c3 = getColor(s[2]);
    if (!checkJew(c1, c2, c3))return false;
    takeJew(id, c1, c2, c3);
    rebackJew(id);
    chessbook.push_back(s);
    return true;
}

2,查看棋谱

bool op0(int id)
{
    for (int i = 0; i < chessbook.size(); i++)cout << chessbook[i] << endl;
    Sleep(3000);
    return false;
}

typedef bool (*pfun)(int id);

bool op1(int id)//操作1拿宝石
{
    cout << "输入最多3个字母:WUGRB分别表示白蓝绿红黑,如RWB表示拿3个宝石,如GG表示拿2个绿宝石\n";
    string s;
    cin >> s;
    s += "   ";
    chessbook.push_back(s);
    int c1, c2, c3;
    c1 = getColor(s[0]), c2 = getColor(s[1]), c3 = getColor(s[2]);
    if (!checkJew(c1, c2, c3))return false;
    takeJew(id, c1, c2, c3);
    rebackJew(id);
    return true;
}

3,悔棋操作

需要悔棋的时候,首先查看棋谱:

然后把棋谱copy下来:

1
GBU
1
WUB
1
WGB
1
UGR
2
3
1
3
3
0
1
URB
4

对于五子棋的棋谱,根据需要自己删减文本末尾,然后重新运行程序,直接全部粘贴进去即可。

然而对于璀璨宝石,我们发现结果变了!

原来,每次重新运行都会重新洗牌,所以需要保存开局时的牌的顺序。

这里有2个方案,一个是把洗牌之后的牌记下来,下次直接复原,但是这个就需要和棋谱分开存,因为这个信息不能公开给玩家,所以实现稍微复杂一点,

另外一个方法是控制随机种子,使得两次运行产生的随机数相同。

完整代码V1中,随机方案是用时间作为随机种子,这里,我改成开局输入种子:

unsigned int seed;//随机种子

int main()
{
    cout << "输入任意正整数,用来随机洗牌";
    cin >> seed;
    init();
    while (!end()) {
        while (!play(turn));
        turn = 1 - turn;
    }
    cout << strRes[gameRes()];
    return 0;
}

在输出棋谱之前先输出种子:

bool op0(int id)
{
    cout << seed << endl;
    for (int i = 0; i < chessbook.size(); i++)cout << chessbook[i] << endl;
    Sleep(3000);
    return false;
}

这样,除了开局需要输入随机种子之外,其他操作就都是正常的基本操作了。

即查看棋谱,copy棋谱,happy删减,重新运行,全部粘贴,搞定。

4,完整代码V2

#include<iostream>
#include<windows.h>
#include<time.h>
#include<vector>
using namespace std;

typedef enum {
    Y, W, U, G, R, B
}Color;
string strColor[6] = { "黄","白", "蓝", "绿", "红", "黑" };

typedef struct {
    int point;
    int color;
    int cost[5];
}Card;//发展卡
typedef struct {
    int cost[5];
}PointCard;//贵族得分牌

typedef struct {
    int jew[6];//各色宝石数量,0-5六种颜色
    int sumJew;//宝石总数
    int card[5];//各色卡牌数量
    int tmpNum;//暂扣牌数量
    Card tmpCard[3];//暂扣牌,最多三张
    int point;//得分
}Player; //玩家数据

const int PC = 10; //贵族得分牌
PointCard Pcard[PC] = {
    3,3,3,0,0,    3,3,0,0,3,    3,0,0,3,3,    0,0,3,3,3,    0,3,3,3,0,
    4,4,0,0,0,    4,0,0,0,4,    0,0,0,4,4,    0,0,4,4,0,    0,4,4,0,0
};
const int LC = 40, MC = 30, HC = 20; //低分牌,中分牌,高分牌
Card Lcard[LC] = {
    1,W,0,0,4,0,0,    1,U,0,0,0,4,0,    1,G,0,0,0,0,4,    1,R,4,0,0,0,0,    1,B,0,4,0,0,0,
    0,W,0,3,0,0,0,    0,U,0,0,0,0,3,    0,G,0,0,0,3,0,    0,R,3,0,0,0,0,    0,B,0,0,3,0,0,
    0,W,0,0,0,2,1,    0,U,1,0,0,0,2,    0,G,2,1,0,0,0,    0,R,0,2,1,0,0,    0,B,0,0,2,1,0,
    0,W,0,1,1,1,1,    0,U,1,0,1,1,1,    0,G,1,1,0,1,1,    0,R,1,1,1,0,1,    0,B,1,1,1,1,0,
    0,W,0,2,0,0,2,    0,U,0,0,2,0,2,    0,G,0,2,0,2,0,    0,R,2,0,0,2,0,    0,B,2,0,2,0,0,
    0,W,0,2,2,0,1,    0,U,1,0,2,2,0,    0,G,0,1,0,2,2,    0,R,2,0,1,0,2,    0,B,2,2,0,1,0,
    0,W,0,1,2,1,1,    0,U,1,0,1,2,1,    0,G,1,1,0,1,2,    0,R,2,1,1,0,1,    0,B,1,2,1,1,0,
    0,W,3,1,0,0,1,    0,U,0,1,3,1,0,    0,G,1,3,1,0,0,    0,R,3,1,0,0,1,    0,B,1,0,0,1,3
};
Card Mcard[MC] = {
    3,W,6,0,0,0,0,    3,U,0,6,0,0,0,    3,G,0,0,6,0,0,    3,R,0,0,0,6,0,    3,B,0,0,0,0,6,
    2,W,0,0,0,5,0,    2,U,0,5,0,0,0,    2,G,0,0,5,0,0,    2,R,0,0,0,0,5,    2,B,5,0,0,0,0,
    2,W,0,0,0,5,3,    2,U,5,3,0,0,0,    2,G,0,5,3,0,0,    2,R,3,0,0,0,5,    2,B,0,0,5,3,0,
    2,W,0,0,1,4,2,    2,U,2,0,0,1,4,    2,G,4,2,0,0,1,    2,R,1,4,2,0,0,    2,B,0,1,4,2,0,
    1,W,0,0,3,2,2,    1,U,0,2,2,3,0,    1,G,2,3,0,0,2,    1,R,2,0,0,2,3,    1,B,3,2,2,0,0,
    1,W,2,3,0,3,0,    1,U,0,2,3,0,3,    1,G,3,0,2,3,0,    1,R,0,3,0,2,3,    1,B,3,0,3,0,2
};
Card Hcard[HC] = {
    5,W,3,0,0,0,7,    5,U,7,3,0,0,0,    5,G,0,7,3,0,0,    5,R,0,0,7,3,0,    5,B,0,0,0,7,3,
    4,W,0,0,0,0,7,    4,U,7,0,0,0,0,    4,G,0,7,0,0,0,    4,R,0,0,7,0,0,    4,B,0,0,0,7,0,
    4,W,3,0,0,3,6,    4,U,6,3,0,0,3,    4,G,3,6,3,0,0,    4,R,0,3,6,3,0,    4,B,0,0,3,6,3,
    3,W,0,3,3,5,3,    3,U,3,0,3,3,5,    3,G,5,3,0,3,3,    3,R,3,5,3,0,3,    3,B,3,3,5,3,0
};

int turn;//0-1
int jew[6] = { 5,4,4,4,4,4 };//公共宝石
Player player[2];
int pointNum=3;//贵族得分牌数量
int cardNum[3] = { LC,MC,HC };//发展卡数量
Card* card[3] = { Lcard ,Mcard ,Hcard };
vector<string>chessbook; //棋谱
unsigned int seed;//随机种子

template<typename T>
void out(T card)
{
    int flag = 0;
    for (int j = 0; j < 5; j++)if (card.cost[j]) {
        cout << card.cost[j] << strColor[j + 1];
        flag++;
    }
    for (int i = 0; i < 5 - flag; i++)cout << "   ";
}
void out(Card card[], int num)
{
    cout << "有 " << num << "张   ";
    for (int i = 0; i < 4 && i < num; i++) {
        cout << card[i].point << "分";
        cout << strColor[card[i].color] << "色牌 ";
        out(card[i]);
    }
}
void out()
{
    system("cls");
    string s = turn ? "轮到后手操作" : "轮到先手操作";
    cout << s << "\n先手: 黄 白 蓝 绿 红 黑              后手: 黄 白 蓝 绿 红 黑\n";
    cout << "宝石: ";
    for (int i = 0; i < 6; i++)cout << player[0].jew[i] << "  ";
    cout << "             宝石: ";
    for (int i = 0; i < 6; i++)cout << player[1].jew[i] << "  ";
    cout << "\n已购牌:  ";
    for (int i = 0; i < 5; i++)cout << player[0].card[i] << "  ";
    cout << "             已购牌:  ";
    for (int i = 0; i < 5; i++)cout << player[1].card[i] << "  ";
    cout << "\n得分: "<< player[0].point;
    cout << "                              得分: " << player[1].point;
    cout << "\n\n公共宝石:黄 白 蓝 绿 红 黑\n          ";
    for (int i = 0; i < 6; i++)cout << jew[i] << "  ";
    cout << "\n\n贵族牌(每张三分)         ";
    for (int i = 0; i < pointNum; i++) {
        out(Pcard[i]);
    }
    cout << "\n\n发展卡: 列0    列1                      列2                      列3                      列4\n";
    cout << "高分牌";
    out(Hcard, cardNum[2]);
    cout << "\n中分牌";
    out(Mcard, cardNum[1]);
    cout << "\n低分牌";
    out(Lcard, cardNum[0]);
}
template<typename T>
void exchange(T& a, T& b)
{
    T tmp = a;
    a = b, b = tmp;
}
template<typename T>
void shuf(T a[], int len)//洗牌
{
    for (int i = 0; i < len * len * 100; i++) {
        int x = rand() % len, y = rand() % len;
        exchange(a[x], a[y]);
    }
}

void init()
{
    turn = 0;
    srand(seed);
    shuf(Pcard, PC);
    shuf(Lcard, LC);
    shuf(Mcard, MC);
    shuf(Hcard, HC);
}

string NS = "";
#define CIN(x) do{while (!(cin >> x)) { \
        cin.clear();      \
        cin.ignore();     \
    };\
    chessbook.push_back(NS+char(x+'0'));}while(0);
#define CIN2(x, y) CIN(x)CIN(y)

int getColor(char c)
{
    if (c == 'W')return W;
    if (c == 'U')return U;
    if (c == 'G')return G;
    if (c == 'R')return R;
    if (c == 'B')return B;
    return 0;
}

bool checkJew(int &c1, int &c2, int &c3)//拿宝石的参数校验
{
    if (c1 == 0)return false;
    if (c2 == 0) {
        c3 = 0;
        return jew[c1]>0;
    }
    if (c1 == c2) {
        c3 = 0;
        return jew[c1] >= 4;
    }
    if (c3 == c1 || c3 == c2)return false;
    if (jew[c1] == 0 || jew[c2] == 0)return false;
    if (c3 && jew[c3] == 0)return false;
    return true;
}
void takeJew(int id, int c)//拿1个宝石
{
    if (c == 0)return;
    player[id].jew[c]++, player[id].sumJew++;
    jew[c]--;
}
void takeJew(int id, int c1, int c2, int c3)//拿宝石
{
    takeJew(id, c1);
    takeJew(id, c2);
    takeJew(id, c3);
}

void rebackJew(int id)//退还宝石
{
    if (player[id].sumJew <= 10)return;
    out();
    cout << "宝石总数为" << player[id].sumJew << "个,需要退还" << player[id].sumJew-10 << "个\n";
    cout << "请输入白蓝绿红黑各色宝石退还数量,如1 0 1 0 0表示退还1个白宝石和1个绿宝石\n";
    int x[5];
    for (int i = 0; i < 5; i++)CIN(x[i]);
    for (int i = 0; i < 5; i++)if (player[id].jew[i + 1] < x[i] || x[i] < 0)return rebackJew(id);
    for (int i = 0; i < 5; i++)player[id].jew[i + 1] -= x[i];
    rebackJew(id);
}

bool op1(int id)//操作1拿宝石
{
    cout << "输入最多3个字母:WUGRB分别表示白蓝绿红黑,如RWB表示拿3个宝石,如GG表示拿2个绿宝石\n";
    string s;
    cin >> s;
    s += "   ";
    chessbook.push_back(s);
    int c1, c2, c3;
    c1 = getColor(s[0]), c2 = getColor(s[1]), c3 = getColor(s[2]);
    if (!checkJew(c1, c2, c3))return false;
    takeJew(id, c1, c2, c3);
    rebackJew(id);
    return true;
}

bool checkCardCost(int id, Card a)//拿牌参数校验
{
    int s = 0;
    for (int i = 0; i < 5; i++) {
        s = max(s, s + a.cost[i] - player[id].card[i] - player[id].jew[i + 1]);
    }
    return s <= player[id].jew[0];
}
bool checkCard(int id, int r, int c)//拿牌参数校验
{
    if (r < 0 || r>=3 || c < 0 || c>=4)return false;
    if (c > cardNum[r])return false;
    return checkCardCost(id, card[r][c]);
}
void takeCard(int id, Card a)//拿牌
{
    for (int i = 0; i < 5; i++) {
        player[id].jew[i + 1] -= a.cost[i] - player[id].card[i], player[id].sumJew -= a.cost[i] - player[id].card[i];
        if (player[id].jew[i + 1] < 0)player[id].jew[0] += player[id].jew[i + 1], player[id].jew[i + 1] = 0;
    }
    player[id].point += a.point;
    player[id].card[a.color]++;
}
void takeCard(int id, int r, int c)//拿牌
{
    takeCard(id, card[r][c]);
}

void disCard(int r, int c)//发牌
{
    exchange(card[r][c], card[r][--cardNum[r]]);
}

bool op2(int id)//操作2拿牌
{
    cout << "输入目标牌所在的行(1-3)列(1-4)\n";
    int r, c;
    CIN2(r, c);
    r = 3 - r, c--;
    if (!checkCard(id, r, c))return false;
    takeCard(id, r, c);
    disCard(r, c);
    return true;
}

bool op3(int id)//操作3扣牌
{
    cout << "输入目标牌所在的行(1-3)列(0-4)\n";
    int r, c;
    CIN2(r, c);
    r = 3 - r, c = c ? c - 1 : 4;
    if (r < 0 || r >= 3 || c < 0 || c > 4 || player[id].tmpNum >= 3)return false;
    player[id].tmpCard[player[id].tmpNum++] = card[r][c];
    if (jew[0])jew[0]--, player[id].jew[0]++, player[id].sumJew++;
    disCard(r, c);
    return true;
}

bool op4(int id)//操作4查看已扣牌
{
    system("cls");
    cout << "玩家准备查看已扣牌,其他玩家请回避\n";
    Sleep(2000);
    out(player[id].tmpCard, player[id].tmpNum);
    Sleep(5000);
    return false;
}

bool op5(int id)//操作5购买已扣牌
{
    cout << "输入目标牌的序号: 1-3";
    int x;
    CIN(x);
    x--;
    if (x < 0 || x >= player[id].tmpNum)return false;
    if (!checkCardCost(id, player[id].tmpCard[x]))return false;
    takeCard(id, player[id].tmpCard[x]);
    exchange(player[id].tmpCard[x], player[id].tmpCard[--player[id].tmpNum]);
    return true;
}

void op6(int id)//操作6拿贵族牌
{
    int takeAble[] = { 0,0,0 };
    for (int i = 0; i < pointNum; i++) {
        takeAble[i] = 1;
        for (int j = 0; j < 5; j++)if (player[id].card[j] < Pcard[i].cost[j])takeAble[i] = 0;
    }
    int s = takeAble[0] + takeAble[1] + takeAble[2];
    if (!s)return;
    cout << "请拿贵族牌:";
    for (int i = 0; i < pointNum; i++) {
        cout << i + 1;
        if (!takeAble[i])cout << "不";
        cout << "可拿 ";
    }
    int x;
    CIN(x);
    if (--x < 0 || x>=pointNum || takeAble[x]==0)return op6(id);
    exchange(Pcard[x], Pcard[--pointNum]);
    player[id].point += 3;
}

bool op0(int id)
{
    cout << seed << endl;
    for (int i = 0; i < chessbook.size(); i++)cout << chessbook[i] << endl;
    Sleep(3000);
    return false;
}

typedef bool (*pfun)(int id);

bool play(int id)
{
    out();
    cout << "\n\n输入操作对应数字:0查看棋谱,1拿宝石,2拿牌,3扣牌,4查看已扣牌,5购买已扣牌\n";
    int op;
    CIN(op);
    if (op < 0 || op>5)return false;
    pfun p[] = { op0,op1,op2,op3,op4,op5 };
    if(!p[op](id))return false;
    op6(id);
    return true;
}

bool end()
{
    if (turn)return false;
    return player[0].point >= 15 || player[1].point >= 15;
}

string strRes[3] = { "先手胜","后手胜","平局" };
int gameRes()
{
    if (player[0].point == player[1].point) {
        int s = 0;
        for (int i = 0; i < 5; i++)s += player[0].card[i] - player[1].card[i];
        return s ? (s > 0 ? 0 : 1) : 2;
    }
    return player[0].point > player[1].point ? 0 : 1;
}

int main()
{
    cout << "输入任意正整数,用来随机洗牌\n";
    cin >> seed;
    init();
    while (!end()) {
        while (!play(turn));
        turn = 1 - turn;
    }
    cout << strRes[gameRes()];
    return 0;
}

七,BUG修复、代码优化

1,新增可拿牌提示

bool checkCardCost(int id, Card a);//拿牌参数校验
void out(Card card[], int num)
{
    cout << "有 " << num << "张   ";
    for (int i = 0; i < 4 && i < num; i++) {
        if (checkCardCost(turn, card[i]))cout << "√";
        else cout << " ";
        cout << card[i].point << "分";
        cout << strColor[card[i].color] << "色牌 ";
        out(card[i]);
    }
}

2,新增显示宝石和牌的总数、显示扣牌数

void out()
{
    system("cls");
    string s = turn ? "轮到后手操作" : "轮到先手操作";
    cout << s << "\n先手: 黄 白 蓝 绿 红 黑              后手: 黄 白 蓝 绿 红 黑\n";
    cout << "宝石: ";
    for (int i = 0; i < 6; i++)cout << player[0].jew[i] << "  ";
    cout << "             宝石: ";
    for (int i = 0; i < 6; i++)cout << player[1].jew[i] << "  ";
    cout << "\n已购牌:  ";
    for (int i = 0; i < 5; i++)cout << player[0].card[i] << "  ";
    cout << "             已购牌:  ";
    for (int i = 0; i < 5; i++)cout << player[1].card[i] << "  ";
    cout << "\n总数: " << player[0].jew[0]<<"  ";
    for (int i = 0; i < 5; i++)cout << player[0].jew[i+1]+player[0].card[i] << "  ";
    cout << "             总数: " << player[1].jew[0] << "  ";
    for (int i = 0; i < 5; i++)cout << player[1].jew[i+1]+player[1].card[i] << "  ";
    cout << "\n得分: "<< player[0].point;
    cout << "                              得分: " << player[1].point;
    cout << "\n已扣牌数: " << player[0].tmpNum;
    cout << "                          已扣牌数: " << player[1].tmpNum;
    cout << "\n\n公共宝石:黄 白 蓝 绿 红 黑\n          ";
    for (int i = 0; i < 6; i++)cout << jew[i] << "  ";
    cout << "\n\n贵族牌(每张三分)         ";
    for (int i = 0; i < pointNum; i++) {
        out(Pcard[i]);
    }
    cout << "\n\n发展卡: 列0    列1                      列2                      列3                      列4\n";
    cout << "高分牌";
    out(Hcard, cardNum[2]);
    cout << "\n中分牌";
    out(Mcard, cardNum[1]);
    cout << "\n低分牌";
    out(Lcard, cardNum[0]);
}

3,新增大小写兼容

int getColor(char c)
{
    if (c == 'W'||c=='w')return W;
    if (c == 'U'||c=='u')return U;
    if (c == 'G'||c=='g')return G;
    if (c == 'R'||c=='r')return R;
    if (c == 'B'||c=='b')return B;
    return 0;
}

4,修复退还宝石BUG

void rebackJew(int id)//退还宝石
{
    if (player[id].sumJew <= 10)return;
    out();
    cout << "宝石总数为" << player[id].sumJew << "个,需要退还" << player[id].sumJew-10 << "个\n";
    cout << "请输入白蓝绿红黑各色宝石退还数量,如1 0 1 0 0表示退还1个白宝石和1个绿宝石\n";
    int x[5];
    for (int i = 0; i < 5; i++)CIN(x[i]);
    for (int i = 0; i < 5; i++)if (player[id].jew[i + 1] < x[i] || x[i] < 0)return rebackJew(id);
    for (int i = 0; i < 5; i++)player[id].jew[i + 1] -= x[i], player[id].sumJew-=x[i],jew[i + 1] += x[i];
    rebackJew(id);
}

5,修复BUG——拿牌的时候宝石没有退还供应堆

void takeCard(int id, Card a)//拿牌
{
    for (int i = 0; i < 5; i++) {
        if (a.cost[i] <= player[id].card[i])continue;
        int tc = a.cost[i] - player[id].card[i];
        player[id].sumJew -= tc;
        if (player[id].jew[i + 1] >= tc) {
            player[id].jew[i + 1] -= tc, jew[i + 1] += tc;
        } else {
            player[id].jew[0] -= tc - player[id].jew[i + 1], jew[i+1] += tc - player[id].jew[i + 1];
            jew[i + 1] += player[id].jew[i + 1], player[id].jew[i + 1] = 0;

        }
    }
    player[id].point += a.point;
    player[id].card[a.color-1]++;
}

6,修复BUG——扣牌之后没有校验宝石数

bool op3(int id)//操作3扣牌
{
    cout << "输入目标牌所在的行(1-3)列(0-4)\n";
    int r, c;
    CIN2(r, c);
    r = 3 - r, c = c ? c - 1 : 4;
    if (r < 0 || r >= 3 || c < 0 || c > 4 || player[id].tmpNum >= 3)return false;
    player[id].tmpCard[player[id].tmpNum++] = card[r][c];
    if (jew[0])jew[0]--, player[id].jew[0]++, player[id].sumJew++;
    rebackJew(id);
    disCard(r, c);
    return true;
}

7,完整代码V3

#include<iostream>
#include<windows.h>
#include<time.h>
#include<vector>
using namespace std;

typedef enum {
    Y, W, U, G, R, B
}Color;
string strColor[6] = { "黄","白", "蓝", "绿", "红", "黑" };

typedef struct {
    int point;
    int color;
    int cost[5];
}Card;//发展卡
typedef struct {
    int cost[5];
}PointCard;//贵族得分牌

typedef struct {
    int jew[6];//各色宝石数量,0-5六种颜色
    int sumJew;//宝石总数
    int card[5];//各色卡牌数量
    int tmpNum;//暂扣牌数量
    Card tmpCard[3];//暂扣牌,最多三张
    int point;//得分
}Player; //玩家数据

const int PC = 10; //贵族得分牌
PointCard Pcard[PC] = {
    3,3,3,0,0,    3,3,0,0,3,    3,0,0,3,3,    0,0,3,3,3,    0,3,3,3,0,
    4,4,0,0,0,    4,0,0,0,4,    0,0,0,4,4,    0,0,4,4,0,    0,4,4,0,0
};
const int LC = 40, MC = 30, HC = 20; //低分牌,中分牌,高分牌
Card Lcard[LC] = {
    1,W,0,0,4,0,0,    1,U,0,0,0,4,0,    1,G,0,0,0,0,4,    1,R,4,0,0,0,0,    1,B,0,4,0,0,0,
    0,W,0,3,0,0,0,    0,U,0,0,0,0,3,    0,G,0,0,0,3,0,    0,R,3,0,0,0,0,    0,B,0,0,3,0,0,
    0,W,0,0,0,2,1,    0,U,1,0,0,0,2,    0,G,2,1,0,0,0,    0,R,0,2,1,0,0,    0,B,0,0,2,1,0,
    0,W,0,1,1,1,1,    0,U,1,0,1,1,1,    0,G,1,1,0,1,1,    0,R,1,1,1,0,1,    0,B,1,1,1,1,0,
    0,W,0,2,0,0,2,    0,U,0,0,2,0,2,    0,G,0,2,0,2,0,    0,R,2,0,0,2,0,    0,B,2,0,2,0,0,
    0,W,0,2,2,0,1,    0,U,1,0,2,2,0,    0,G,0,1,0,2,2,    0,R,2,0,1,0,2,    0,B,2,2,0,1,0,
    0,W,0,1,2,1,1,    0,U,1,0,1,2,1,    0,G,1,1,0,1,2,    0,R,2,1,1,0,1,    0,B,1,2,1,1,0,
    0,W,3,1,0,0,1,    0,U,0,1,3,1,0,    0,G,1,3,1,0,0,    0,R,3,1,0,0,1,    0,B,1,0,0,1,3
};
Card Mcard[MC] = {
    3,W,6,0,0,0,0,    3,U,0,6,0,0,0,    3,G,0,0,6,0,0,    3,R,0,0,0,6,0,    3,B,0,0,0,0,6,
    2,W,0,0,0,5,0,    2,U,0,5,0,0,0,    2,G,0,0,5,0,0,    2,R,0,0,0,0,5,    2,B,5,0,0,0,0,
    2,W,0,0,0,5,3,    2,U,5,3,0,0,0,    2,G,0,5,3,0,0,    2,R,3,0,0,0,5,    2,B,0,0,5,3,0,
    2,W,0,0,1,4,2,    2,U,2,0,0,1,4,    2,G,4,2,0,0,1,    2,R,1,4,2,0,0,    2,B,0,1,4,2,0,
    1,W,0,0,3,2,2,    1,U,0,2,2,3,0,    1,G,2,3,0,0,2,    1,R,2,0,0,2,3,    1,B,3,2,2,0,0,
    1,W,2,3,0,3,0,    1,U,0,2,3,0,3,    1,G,3,0,2,3,0,    1,R,0,3,0,2,3,    1,B,3,0,3,0,2
};
Card Hcard[HC] = {
    5,W,3,0,0,0,7,    5,U,7,3,0,0,0,    5,G,0,7,3,0,0,    5,R,0,0,7,3,0,    5,B,0,0,0,7,3,
    4,W,0,0,0,0,7,    4,U,7,0,0,0,0,    4,G,0,7,0,0,0,    4,R,0,0,7,0,0,    4,B,0,0,0,7,0,
    4,W,3,0,0,3,6,    4,U,6,3,0,0,3,    4,G,3,6,3,0,0,    4,R,0,3,6,3,0,    4,B,0,0,3,6,3,
    3,W,0,3,3,5,3,    3,U,3,0,3,3,5,    3,G,5,3,0,3,3,    3,R,3,5,3,0,3,    3,B,3,3,5,3,0
};

int turn;//0-1
int jew[6] = { 5,4,4,4,4,4 };//公共宝石
Player player[2];
int pointNum=3;//贵族得分牌数量
int cardNum[3] = { LC,MC,HC };//发展卡数量
Card* card[3] = { Lcard ,Mcard ,Hcard };
vector<string>chessbook; //棋谱
unsigned int seed;//随机种子

template<typename T>
void out(T card)
{
    int flag = 0;
    for (int j = 0; j < 5; j++)if (card.cost[j]) {
        cout << card.cost[j] << strColor[j + 1];
        flag++;
    }
    for (int i = 0; i < 5 - flag; i++)cout << "   ";
}
bool checkCardCost(int id, Card a);//拿牌参数校验
void out(Card card[], int num)
{
    cout << "有 " << num << "张   ";
    for (int i = 0; i < 4 && i < num; i++) {
        if (checkCardCost(turn, card[i]))cout << "√";
        else cout << " ";
        cout << card[i].point << "分";
        cout << strColor[card[i].color] << "色牌 ";
        out(card[i]);
    }
}
void out()
{
    system("cls");
    string s = turn ? "轮到后手操作" : "轮到先手操作";
    cout << s << "\n先手: 黄 白 蓝 绿 红 黑              后手: 黄 白 蓝 绿 红 黑\n";
    cout << "宝石: ";
    for (int i = 0; i < 6; i++)cout << player[0].jew[i] << "  ";
    cout << "             宝石: ";
    for (int i = 0; i < 6; i++)cout << player[1].jew[i] << "  ";
    cout << "\n已购牌:  ";
    for (int i = 0; i < 5; i++)cout << player[0].card[i] << "  ";
    cout << "             已购牌:  ";
    for (int i = 0; i < 5; i++)cout << player[1].card[i] << "  ";
    cout << "\n总数: " << player[0].jew[0]<<"  ";
    for (int i = 0; i < 5; i++)cout << player[0].jew[i+1]+player[0].card[i] << "  ";
    cout << "             总数: " << player[1].jew[0] << "  ";
    for (int i = 0; i < 5; i++)cout << player[1].jew[i+1]+player[1].card[i] << "  ";
    cout << "\n得分: "<< player[0].point;
    cout << "                              得分: " << player[1].point;
    cout << "\n已扣牌数: " << player[0].tmpNum;
    cout << "                          已扣牌数: " << player[1].tmpNum;
    cout << "\n\n公共宝石:黄 白 蓝 绿 红 黑\n          ";
    for (int i = 0; i < 6; i++)cout << jew[i] << "  ";
    cout << "\n\n贵族牌(每张三分)         ";
    for (int i = 0; i < pointNum; i++) {
        out(Pcard[i]);
    }
    cout << "\n\n发展卡: 列0     列1                       列2                       列3                       列4\n";
    cout << "高分牌";
    out(Hcard, cardNum[2]);
    cout << "\n中分牌";
    out(Mcard, cardNum[1]);
    cout << "\n低分牌";
    out(Lcard, cardNum[0]);
}
template<typename T>
void exchange(T& a, T& b)
{
    T tmp = a;
    a = b, b = tmp;
}
template<typename T>
void shuf(T a[], int len)//洗牌
{
    for (int i = 0; i < len * len * 100; i++) {
        int x = rand() % len, y = rand() % len;
        exchange(a[x], a[y]);
    }
}

void init()
{
    turn = 0;
    srand(seed);
    shuf(Pcard, PC);
    shuf(Lcard, LC);
    shuf(Mcard, MC);
    shuf(Hcard, HC);
}

string NS = "";
#define CIN(x) do{while (!(cin >> x)) { \
        cin.clear();      \
        cin.ignore();     \
    };\
    chessbook.push_back(NS+char(x+'0'));}while(0);
#define CIN2(x, y) CIN(x)CIN(y)

int getColor(char c)
{
    if (c == 'W'||c=='w')return W;
    if (c == 'U'||c=='u')return U;
    if (c == 'G'||c=='g')return G;
    if (c == 'R'||c=='r')return R;
    if (c == 'B'||c=='b')return B;
    return 0;
}

bool checkJew(int &c1, int &c2, int &c3)//拿宝石的参数校验
{
    if (c1 == 0)return false;
    if (c2 == 0) {
        c3 = 0;
        return jew[c1]>0;
    }
    if (c1 == c2) {
        c3 = 0;
        return jew[c1] >= 4;
    }
    if (c3 == c1 || c3 == c2)return false;
    if (jew[c1] == 0 || jew[c2] == 0)return false;
    if (c3 && jew[c3] == 0)return false;
    return true;
}
void takeJew(int id, int c)//拿1个宝石
{
    if (c == 0)return;
    player[id].jew[c]++, player[id].sumJew++;
    jew[c]--;
}
void takeJew(int id, int c1, int c2, int c3)//拿宝石
{
    takeJew(id, c1);
    takeJew(id, c2);
    takeJew(id, c3);
}

void rebackJew(int id)//退还宝石
{
    if (player[id].sumJew <= 10)return;
    out();
    cout << "宝石总数为" << player[id].sumJew << "个,需要退还" << player[id].sumJew-10 << "个\n";
    cout << "请输入白蓝绿红黑各色宝石退还数量,如1 0 1 0 0表示退还1个白宝石和1个绿宝石\n";
    int x[5];
    for (int i = 0; i < 5; i++)CIN(x[i]);
    for (int i = 0; i < 5; i++)if (player[id].jew[i + 1] < x[i] || x[i] < 0)return rebackJew(id);
    for (int i = 0; i < 5; i++)player[id].jew[i + 1] -= x[i], player[id].sumJew-=x[i],jew[i + 1] += x[i];
    rebackJew(id);
}

bool op1(int id)//操作1拿宝石
{
    cout << "输入最多3个字母:WUGRB分别表示白蓝绿红黑,如RWB表示拿3个宝石,如GG表示拿2个绿宝石\n";
    string s;
    cin >> s;
    s += "   ";
    chessbook.push_back(s);
    int c1, c2, c3;
    c1 = getColor(s[0]), c2 = getColor(s[1]), c3 = getColor(s[2]);
    if (!checkJew(c1, c2, c3))return false;
    takeJew(id, c1, c2, c3);
    rebackJew(id);
    return true;
}

bool checkCardCost(int id, Card a)//拿牌参数校验
{
    int s = 0;
    for (int i = 0; i < 5; i++) {
        s = max(s, s + a.cost[i] - player[id].card[i] - player[id].jew[i + 1]);
    }
    return s <= player[id].jew[0];
}
bool checkCard(int id, int r, int c)//拿牌参数校验
{
    if (r < 0 || r>=3 || c < 0 || c>=4)return false;
    if (c > cardNum[r])return false;
    return checkCardCost(id, card[r][c]);
}
void takeCard(int id, Card a)//拿牌
{
    for (int i = 0; i < 5; i++) {
        if (a.cost[i] <= player[id].card[i])continue;
        int tc = a.cost[i] - player[id].card[i];
        player[id].sumJew -= tc;
        if (player[id].jew[i + 1] >= tc) {
            player[id].jew[i + 1] -= tc, jew[i + 1] += tc;
        } else {
            player[id].jew[0] -= tc - player[id].jew[i + 1], jew[i+1] += tc - player[id].jew[i + 1];
            jew[i + 1] += player[id].jew[i + 1], player[id].jew[i + 1] = 0;

        }
    }
    player[id].point += a.point;
    player[id].card[a.color-1]++;
}
void takeCard(int id, int r, int c)//拿牌
{
    takeCard(id, card[r][c]);
}

void disCard(int r, int c)//发牌
{
    exchange(card[r][c], card[r][--cardNum[r]]);
}

bool op2(int id)//操作2拿牌
{
    cout << "输入目标牌所在的行(1-3)列(1-4)\n";
    int r, c;
    CIN2(r, c);
    r = 3 - r, c--;
    if (!checkCard(id, r, c))return false;
    takeCard(id, r, c);
    disCard(r, c);
    return true;
}

bool op3(int id)//操作3扣牌
{
    cout << "输入目标牌所在的行(1-3)列(0-4)\n";
    int r, c;
    CIN2(r, c);
    r = 3 - r, c = c ? c - 1 : 4;
    if (r < 0 || r >= 3 || c < 0 || c > 4 || player[id].tmpNum >= 3)return false;
    player[id].tmpCard[player[id].tmpNum++] = card[r][c];
    if (jew[0])jew[0]--, player[id].jew[0]++, player[id].sumJew++;
    rebackJew(id);
    disCard(r, c);
    return true;
}

bool op4(int id)//操作4查看已扣牌
{
    system("cls");
    cout << "玩家准备查看已扣牌,其他玩家请回避\n";
    Sleep(2000);
    out(player[id].tmpCard, player[id].tmpNum);
    Sleep(5000);
    return false;
}

bool op5(int id)//操作5购买已扣牌
{
    cout << "输入目标牌的序号: 1-3";
    int x;
    CIN(x);
    x--;
    if (x < 0 || x >= player[id].tmpNum)return false;
    if (!checkCardCost(id, player[id].tmpCard[x]))return false;
    takeCard(id, player[id].tmpCard[x]);
    exchange(player[id].tmpCard[x], player[id].tmpCard[--player[id].tmpNum]);
    return true;
}

void op6(int id)//操作6拿贵族牌
{
    int takeAble[] = { 0,0,0 };
    for (int i = 0; i < pointNum; i++) {
        takeAble[i] = 1;
        for (int j = 0; j < 5; j++)if (player[id].card[j] < Pcard[i].cost[j])takeAble[i] = 0;
    }
    int s = takeAble[0] + takeAble[1] + takeAble[2];
    if (!s)return;
    cout << "请拿贵族牌:";
    for (int i = 0; i < pointNum; i++) {
        cout << i + 1;
        if (!takeAble[i])cout << "不";
        cout << "可拿 ";
    }
    int x;
    CIN(x);
    if (--x < 0 || x>=pointNum || takeAble[x]==0)return op6(id);
    exchange(Pcard[x], Pcard[--pointNum]);
    player[id].point += 3;
}

bool op0(int id)
{
    cout << seed << endl;
    for (int i = 0; i < chessbook.size(); i++)cout << chessbook[i] << endl;
    Sleep(3000);
    return false;
}

typedef bool (*pfun)(int id);

bool play(int id)
{
    out();
    cout << "\n\n输入操作对应数字:0查看棋谱,1拿宝石,2拿牌,3扣牌,4查看已扣牌,5购买已扣牌\n";
    int op;
    CIN(op);
    if (op < 0 || op>5)return false;
    pfun p[] = { op0,op1,op2,op3,op4,op5 };
    if(!p[op](id))return false;
    op6(id);
    return true;
}

bool end()
{
    out();
    cout << endl;
    if (turn)return false;
    return player[0].point >= 15 || player[1].point >= 15;
}

string strRes[3] = { "先手胜","后手胜","平局" };
int gameRes()
{
    if (player[0].point == player[1].point) {
        int s = 0;
        for (int i = 0; i < 5; i++)s += player[0].card[i] - player[1].card[i];
        return s ? (s > 0 ? 0 : 1) : 2;
    }
    return player[0].point > player[1].point ? 0 : 1;
}

int main()
{
    cout << "输入任意正整数,用来随机洗牌\n";
    cin >> seed;
    init();
    while (!end()) {
        while (!play(turn));
        turn = 1 - turn;
    }
    cout << strRes[gameRes()];
    return 0;
}

实战棋谱:
9
1
ugb
1
urb
1
wug
1
urb
1
wub
1
wbr
1
wgr
2 3 3
2 3 3
1
ubg
1
wub
2 3 3
2 3 3
1
ubr
2 3 1
2 3 3
1
urw
1
bug
2 3 1
1
ubw
1
ubw
1
ubr
2 3 1
1
bwu
2 3 3
2 3 2
1
ubr
1
wub
2 3 1
2 3 1
2 3 1
2 3 1
1
uwb
1
uwb
1
ubw
2 2 4
2 2 2
1
wbu
1
wug
2 3 2
1
wub
2 2 4
2 2 3
2 3 2
2 2 3
1
wur
2 3 2
3
1
wgu
2 2 3
2 3 4
1
ruw
1
ugr
2 2 4
2 2 4
2 1 3
2 2 3
2 2 4
后手胜

八,规则对比

严格对比代码逻辑和璀璨宝石的规则,有几点区别:

(1)支付宝石时,默认支付普通宝石,普通宝石不够时才支付万能宝石。

实际规则是,随时都可以支付万能宝石而不支付普通宝石,

理论上有可能存在一些场景,支付万能宝石而不支付普通宝石是有好处的,因为支付了普通宝石的话,轮到对方对方可能刚好需要它就把它拿走了。

我只能说有可能存在,是不是真的存在不确定,毕竟最优策略这种事本就是超级复杂的事情。

(2)退宝石的时候只让玩家退普通宝石,没提供退万能宝石的选项

这个设定的理由和可能存在的场景同上

(3)扣牌时,默认只要有黄色万能宝石就直接取一个。

实际规则是,即使有万能宝石,也可以选择只扣牌而不拿万能宝石。

不过,因为拿万能宝石的时候可以回退普通宝石,拿普通宝石的时候也可以回退万能宝石,所以问题还是归结为退宝石的操作,

对于宝石少于10个的情况,拿一个万能宝石一定不会比不拿要差。

九,策略

一般分为平摊流和速攻流,网上有很多厉害的分析。

五个颜色之间的轮换是有规律的,网上也有很多厉害的分析。

不同的牌的价值是不一样的,比如中分牌里面较好的就是5、6、142三种,高分牌里面较好的就是7、73两种。

两人局、三人局、四人局的策略各不同,人均宝石数也不同,这些统统都有大神总结。

还有一些统计数据,比如平均有多少轮。

把这些理解了,大概可以离开新手村,也就是我现在的水平。

十,拓展玩法

元素争夺拓展

 压了三个要塞就可以购买,照常支付费用,但是可以省一个回合。

终极成就拓展

 

一共七张成就牌(上图的七行),每个牌都有两面,一共14种不同的成就。

全新战场拓展

 

拓展牌一共30张。

一级牌16张选10张,有2种:

 

大概有三种选法,最常见的第一种是5+5,重复的三红两蓝牌去掉,第二种是6+4,五个双金牌选四个,第三种是0+10,采用2套双金牌。

二级牌10张,有3种:

三级牌10张,有2种:

领悟图腾拓展

 

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值