前言
大年初七下午四点突然来了兴致。初七晚上写成麻将1.0版本,初八上午2.0版本,晚上3.0版本经过多次调试,确定无误,现将3.0版本代码放在下方,有兴趣的朋友可以在visual studio上玩玩。由于前端知识不会,没做界面,敬请谅解。
仪征麻将和国标麻将有些不同,程序里有简短介绍。
程序里最复杂的知识点在于用DFS来判断各玩家当前的牌能否胡牌。中午想让程序自动判断能否胡牌,灵光一现,想出了这个方法,枚举牌的排列顺序,加上剪枝,时间复杂度不会大。
代码
#include <iostream>
using namespace std;
#include <set>
#include <algorithm>
#include <cmath>
#include <map>
#include <cstdio>
#include <string>
#include <cstring>
#include <string.h>
#include <stdlib.h>
#include <iomanip>
#include <fstream>
#include <stdio.h>
#include <stack>
#include <queue>
#include <ctype.h>
#include <vector>
#include <random>
#define ll long long
#define pb push_back
#define rep(i, a, n) for (int i = a; i <= n; i++)
#define per(i, a, n) for (int i = n; i >= a; i--)
#define pii pair<int, int>
#define pli pair<ll, int>
#define pil pair<int, ll>
#define endl '\n'
const double pai = acos(-1);
ll extend_gcd(ll a, ll b, ll& x, ll& y)
{
if (b == 0)
{
x = 1;
y = 0;
return a;
}
ll d = extend_gcd(b, a % b, y, x);
y -= a / b * x;
return d;
}
ll fastpow(ll a, ll n, ll mod)
{
ll ans = 1;
a %= mod;
while (n)
{
if (n & 1)
ans = (ans * a) % mod;
a = (a * a) % mod;
n >>= 1;
}
return ans;
}
int dir[4][2] =
{
{0, 1}, {0, -1}, {1, 0}, {-1, 0} };
const int inf = 100000000;
const ll mod = 1e9 + 7;
const int N = 1e5 + 10, M = 5000 + 10;
// 变量设定区
int sum = 84; // 记录剩余的麻将数
multiset<int> all; // 存所有牌(临时数组)
set<int> duizi[5], gang[5]; // 存每个人已经打出的对子和杠(对子和杠不算手牌)
int card[5][35]; // 存每个人现有手牌
int ever[35], every_ever[5][35]; // 存全部玩家和单个玩家曾经打出的牌(包括对子和杠)
bool flag[5]; // 记录单个玩家是否还符合"七对"成牌条件(即打出至少一个对子或杠则不满足"七对")
int paidui[69][3]; // 存每一个位置的牌
bool vis[69][3]; // 存每一个位置的牌有无被摸过
int firs; // 目前抓牌的垒数
int now; // 目前抓牌的层数
int lastt; // 最后一张牌的位置(开杠摸牌专用)
int peizi; // 配子的数字号
int temp;//场上现在打出的牌
int linshi[50];
int lin[44];
int noww;//当前打出牌的人
int a[20];//判定能否成牌的数组
bool viss[20];//dfs搜索成牌的数组
bool flag2;
int bb[20];
int zhanshi[20];
//
mt19937 rnd(12009);
void dfs(int ceng)
{
if (ceng != 1 && (ceng - 1) % 3 == 0)
{
if ((a[ceng - 3] == a[ceng - 2] && a[ceng - 2] == a[ceng - 1])
|| (a[ceng - 3] + 1 == a[ceng - 2] && a[ceng - 2] + 1 == a[ceng - 1] && (a[ceng - 3] <= 7 || (10 <= a[ceng - 3] && a[ceng - 3] <= 16) || (19 <= a[ceng - 3] && a[ceng - 3] <= 25)))
|| (a[ceng - 3] == a[ceng - 2] && a[ceng - 1] == peizi) || (a[ceng - 3] == a[ceng - 1] && a[ceng - 2] == peizi) || (a[ceng - 2] == a[ceng - 1] && a[ceng - 3] == peizi)
|| (a[ceng - 3] == peizi && a[ceng - 2] + 1 == a[ceng - 1] && (a[ceng - 2] <= 8 || (10 <= a[ceng - 2] && a[ceng - 2] <= 17) || (19 <= a[ceng - 2] && a[ceng - 2] <= 26)))
|| (a[ceng - 2] == peizi && a[ceng - 3] + 2 == a[ceng - 1] && (a[ceng - 3] <= 7 || (10 <= a[ceng - 3] && a[ceng - 3] <= 16) || (19 <= a[ceng - 3] && a[ceng - 3] <= 25)))
|| (a[ceng - 1] == peizi && a[ceng - 3] + 1 == a[ceng - 2] && (a[ceng - 3] <= 8 || (10 <= a[ceng - 3] && a[ceng - 3] <= 17) || (19 <= a[ceng - 3] && a[ceng - 3] <= 26)))
|| (a[ceng - 3] == peizi && a[ceng - 2] == peizi) || (a[ceng - 1] == peizi && a[ceng - 2] == peizi)
|| (a[ceng - 3] == peizi && a[ceng - 1] == peizi))
{
}
else
{
/*
rep(i, 1, 14)
{
cout << a[i] << " ";
}
cout << endl;
//*/
return;
}
}
if (ceng == 15)
{
if (a[13] == a[14] ||a[13]==peizi || a[14]==peizi )
{
rep(i, 1, 14)
{
zhanshi[i] = a[i];
}
flag2 = 1;
}
return;
}
rep(i, 1, 14)
{
if (viss[i] == 0)
{
viss[i] = 1;
a[ceng] = bb[i];
dfs(ceng + 1);
viss[i] = 0;
}
}
}
int ifsucceed(int x)
{
if (flag[x]) //自动检测能否七对胡牌
{
//统计除了配子外有多少组两张牌
int num_two = 0;
rep(i, 1, 34)
{
if (i == peizi)
{
continue;
}
num_two += card[x][i] / 2;
}
if (num_two + card[x][peizi] >= 7)//ok
{
return 2;
}
}
//dfs搜索检测能否常规成牌
int cnt = 0;
for (auto i : duizi[x])
{
rep(i, 1, 3)
{
bb[++cnt] = i;
}
}
for (auto i : gang[x])
{
rep(i, 1, 3)
{
bb[++cnt] = i;
}
}
rep(i, 1, 34)
{
rep(j, 1, card[x][i])
{
bb[++cnt] = i;
}
}
flag2 = 0;
fill(viss + 1, viss + 15, 0);
fill(a + 1, a + 15, 0);
dfs(1);
if (flag2)
{
return 1;
}
return 0;
}
void showw(int x);
void hupai(int x,int method)
{
cout << x << "号玩家的手牌为:" << endl;
rep(i, 1, 14)
{
showw(zhanshi[i]);
}
cout << endl << endl;
cout << "恭喜" << x << "号玩家";
if (method == 1)
{
cout << "普通方式胡牌" << endl;
}
else
{
cout << "七对方式胡牌" << endl;
}
cout << "祝您龙年大吉,在龙年打出属于你的独特的王者龙耀" << endl<<endl;
}
int print_feng(int x)
{
cout << "本局游戏的配子是: ";
if (x % 4 == 1)
{
cout << "东风";
cout << endl;
return x;
}
else if (x % 4 == 2)
{
cout << "南风";
cout << endl;
return x + 17;
}
else if (x % 4 == 3)
{
cout << "西风";
cout << endl;
return x + 34;
}
else if (x % 4 == 0)
{
cout << "北风";
cout << endl;
return x + 51;
}
}
void showw(int i)
{
if (i <= 9)
{
cout << " " << i << "万 ";
}
else if (i <= 18)
{
cout << " " << i - 9 << "条 ";
}
else if (i <= 27)
{
cout << " " << i - 18 << "饼 ";
}
else if (i == 28)
{
cout << " 东风 ";
}
else if (i == 29)
{
cout << " 南风 ";
}
else if (i == 30)
{
cout << " 西风 ";
}
else if (i == 31)
{
cout << " 北风 ";
}
else if (i == 32)
{
cout << " 红中 ";
}
else if (i == 33)
{
cout << " 白皮 ";
}
else if (i == 34)
{
cout << " 发财 ";
}
}
void show(int x)//展示x号玩家手牌
{
cout << "配子是";
showw(peizi);
cout << endl;
cout << "第" << x << "号玩家的牌为:" << endl;
rep(i, 1, 34)
{
rep(j, 1, card[x][i])
{
cout << " ";
cout << setw(2) << i;
cout << " ";
}
}
cout << endl << endl;
rep(i, 1, 34)
{
rep(j, 1, card[x][i])
{
showw(i);
}
}
cout << endl << endl;
cout << "对子为 ";
for (auto i : duizi[x])
{
showw(i);
}
cout << endl << endl;
cout << "杠为 ";
for (auto i : gang[x])
{
showw(i);
}
cout << endl
<< endl;
}
void huangzhuang()
{
cout << "荒庄,所有牌已被摸完,游戏结束。感谢您的参与" << endl;
exit(0);
}
int kaigang() // 开杠后最后摸一张牌并打一张牌,返回摸的那张牌的序列号
{
if (sum == 0)
{
huangzhuang();
}
sum--;
if (vis[lastt][1] == 0)
{
vis[lastt][1] = 1;
return paidui[lastt][1];
}
else
{
if (vis[lastt][2] == 0)
{
vis[lastt][2] = 1;
return paidui[lastt][2];
}
else//这垒牌已经打掉,到前一垒牌搜索
{
lastt--;
if (lastt == 0)
{
lastt = 68;
}
if (vis[lastt][1] == 0)
{
vis[lastt][1] = 1;
return paidui[lastt][1];
}
else
{
vis[lastt][2] = 1;
return paidui[lastt][2];
}
}
}
}
void phase(int x);
void getcard(int x)
{
do
{
if (now == 2)
{
firs++;
if (firs == 69)
{
firs = 1;
}
}
now = 3 - now;
} while (vis[firs][now]);
vis[firs][now] = 1;
card[x][paidui[firs][now]]++; // 牌抓进来
cout << "您抓的牌是";
showw(paidui[firs][now]);
cout << endl;
}
void getcard2(int x)
{
cout << "您抓的牌是";
int tee = kaigang();//注:vis标记在kaigang()函数里
card[x][tee]++;
showw(tee);
cout << endl;
}
void take(int x);
void throwcard(int x,int y);
void ask(int x)//问x号玩家是否需要temp这张牌
{
bool b = 0, c = 0;
if (card[x][temp] >= 2)
{
b = 1;
}
if (card[x][temp] >= 3)
{
c = 1;
}
if (b == 0 && c == 0)
{
return;
}
cout << noww << "号玩家,对于这张牌";
showw(temp);
if (b)
{
cout << "对对子请按2 ";
}
if (c)
{
cout << "开杠请按3 ";
}
cout << "不需要请按0"<<endl;
int te;
cin >> te;
if (te == 0)
{
return;
}
if (te == 2)
{
if (card[x][temp] >= 2)
{
flag[x] = 0;//失去七对条件
system("cls");
cout << "对对子成功" << endl;
card[x][temp] -= 2;
duizi[x].insert(temp);
ever[temp] += 3;
every_ever[x][temp] += 3;
// 接着x玩家需要打出一张牌
show(x);
throwcard(x, 0);
}
else
{
cout << "您不符合对对子条件,请再接再厉,按1继续" << endl;
int e;
cin >> e;
return;
}
}
if (te == 3 )
{
if (card[x][temp] >= 3)
{
flag[x] = 0;//失去七对条件
system("cls");
cout << "开杠成功" << endl;
card[x][temp] -= 3;
gang[x].insert(temp);
ever[temp] += 4;
every_ever[x][temp] += 4;
getcard2(x);//牌堆最后摸一张
show(x);
throwcard(x, 0);
}
else
{
cout << "您不符合开杠条件,请再接再厉,按1继续" << endl;
int te;
cin >> te;
return;
}
}
}
void throwcard(int x,int number)//x号玩家弃牌阶段
{
if (number == 0)
{
t1:
cout << "现在场上已打出手牌为:" << endl << endl;
rep(i, 1, 34)
{
showw(i);
cout << ever[i];
if (i % 9 == 0)
{
cout << endl << endl;
}
}
cout << endl;
do
{
cout << "请弃置一张牌" << endl;
cin >> temp;
} while (temp < 1 || temp>34 || card[x][temp] == 0);
//
//
if (temp == peizi)
{
cout << "这张牌是配子,坚持弃牌请按1,重新弃牌请按2" << endl;
int ch;
cin >> ch;
if (ch == 2)
{
goto t1;
}
}
}
else
{
temp = number;
}
card[x][temp]--;
noww = x;
//优先考虑是否有玩家这张能成牌,考虑至少两个玩家同时成牌的情况
bool flaggg = 0;
rep(i, 1, 3)
{
noww++;
if (noww == 5)
{
noww = 1;
}
card[noww][temp]++;
int win = ifsucceed(noww);
card[noww][temp]--;
if (win)
{
show(noww);
cout << noww << "号玩家,拿过";
showw(temp);
cout<<"这张牌您可以胡牌,拿取请按1,不拿请按0:" << endl;
int te;
cin >> te;
if (te == 1)
{
hupai(noww, win);
flaggg = 1;
}
}
}
if (flaggg)
{
cout << "游戏结束" << endl;
exit(0);
}
noww = x;
rep(i, 1, 3)
{
noww++;
if (noww == 5)
{
noww = 1;
}
system("cls");//新玩家操作界面,要清屏
show(noww);
ask(noww);//询问
}
// temp这张牌别人没拿,是x自己打出的
ever[temp]++;
every_ever[x][temp]++;
if (x == 4)
{
phase(1);
}
else
{
phase(x + 1);
}
}
void phase(int x)
{
system("cls");
if (sum == 0)
{
huangzhuang();
}
sum--;
cout << "现在是" << x << "号玩家的回合:";
// 抓牌
getcard(x);
//展示手牌
show(x);
//cout << 1999999 << endl;
int win = ifsucceed(x);
if (win)
{
cout << "您已达成胡牌条件,直接胡牌请按1,放弃请按2:" << endl;
int ch;
cin >> ch;
if (ch == 1)
{
hupai(x, win);
exit(0);
}
}
//cout << 1999999 << endl;
cout << "现在场上已打出手牌为:" << endl << endl;
rep(i, 1, 34)
{
showw(i);
cout << ever[i];
if (i % 9 == 0)
{
cout << endl << endl;
}
}
cout << endl;
p1:
int te;
do
{
cout << "正常弃牌按序列号,开杠按35,查询单个选手已打过手牌分别按41,42,43,44" << endl;
cin >> te;
} while (te < 1 || te>44 || (36 <= te && te <= 40));
if (te >= 41)//查询功能
{
cout << endl;
cout << te - 40 << "号玩家已经打出的手牌为:" << endl << endl;
rep(i, 1, 34)
{
showw(i);
cout << every_ever[te - 40][i];
if (i % 9 == 0)
{
cout << endl << endl;
}
}
cout << endl;
goto p1;
}
else if (te == 35)
{
cout << "正常开杠请按1,追加对子开杠请按2:" << endl;
int ch;
cin >> ch;
if (ch == 1)
{
cout << "请输入开杠的数字:" << endl;
int tem;
cin >> tem;
if (card[x][tem] == 4)
{
flag[x] = 0;
gang[x].insert(tem);
card[x][tem] = 0;
ever[tem] = 4;
every_ever[x][tem] = 4;
cout << "开杠成功" << endl;
getcard2(x);
show(x);
throwcard(x,0);
}
else
{
cout << "不符合开杠条件" << endl;
}
}
else
{
cout << "请输入开杠的数字:" << endl;
int tem;
cin >> tem;
if (duizi[x].count(tem) && card[x][tem])
{
card[x][tem] = 0;
ever[tem]++;
every_ever[x][tem]++;
duizi[x].erase(tem);
gang[x].insert(tem);
cout << "开杠成功" << endl;
getcard2(x);
show(x);
throwcard(x,0);
}
else
{
cout << "不符合开杠条件" << endl;
}
}
}
temp = te;
if (temp == peizi)
{
cout << "这张牌是配子,坚持弃牌请按1,重新弃牌请按2" << endl;
int ch;
cin >> ch;
if (ch == 2)
{
goto p1;
}
}
//正常回合结束
throwcard(x, temp);
}
int main()
{
cout << "尊敬的玩家,你们好,仪征麻将欢迎您的光临!" << endl << endl;
cout << "仪征麻将不可以吃,有配子,配子可以当做任意牌" << endl << endl;
fill(flag + 1, flag + 5, 1); //"七对"成牌条件初始设定为真
rep(i, 1, 34)
{
rep(j, 1, 4)
{
all.insert(i);
}
}
// 初始化牌堆麻将
int temp = 136;
int cnt = 0;
while (temp)
{
cnt++;
int suiji = rnd() % temp;
auto it = all.begin();
rep(i, 1, suiji)
{
it++;
}
paidui[cnt][1] = *it;//1为牌堆上层
all.erase(it);
temp--;
suiji = rnd() % temp;
it = all.begin();
rep(i, 1, suiji)
{
it++;
}
paidui[cnt][2] = *it;//2为牌堆下层
all.erase(it);
temp--;
}
int shai_first = rnd() % 6 + 1, shai_second = rnd() % 6 + 1;
cout << "投掷骰子的点数分别是" << shai_first << " " << shai_second << endl;
firs = print_feng(shai_first + shai_second); // 告知玩家是什么配子,算出起始敦数减1
lastt = firs;//最后的敦数,预留给开杠牌
peizi = shai_first + shai_second;
while (peizi > 4)
{
peizi -= 4;
}
peizi += 27;//确定本局配子的序号数
// 发牌
rep(ii, 1, 3)
{
rep(i, 1, 4) // 四个人
{
rep(j, 1, 2) // 两垒牌
{
firs++;
if (firs == 69) // 越界
{
firs = 1;
}
rep(k, 1, 2) // 两层
{
int now = paidui[firs][k];
card[i][now]++;
vis[firs][k] = 1;
}
}
}
}
now = 2;
rep(i, 1, 4) // 四个人补齐最后一张牌
{
if (now == 2)//这垒牌已经摸完了
{
firs++;
}
now = 3 - now;
card[i][paidui[firs][now]]++;
vis[firs][now] = 1;
}
cout << "游戏已准备就绪!\n按1开始游戏" << endl;
int tt;
cin >> tt;
phase(1);
}