您的位置:首页 > 其它

斗地主算法

2015-07-30 14:28 155 查看
不得不承认,算法搁置了一些时间,代码的风格下降了好多! 贴上一个曹点多多且丑的代码! Orz...

题目要求:

编码:3表示3点 ,4表示4点点 。。。。 10表示10点 11表示J 12表示Q 13表示K 14表示A 15表示2 16表示小王 17表示大王

要求:

1)出牌牌型包括(单子,对子, 三张,三带一,三带二,顺子,连对(3连对,4连对,5连对....),四张炸弹,四带一 ,四带二(四带2个单张,四带2个对子),对王炸弹)

2)初始化1副牌(54张),随机发一手牌(17张)显示出来

3)手动输入一个几张牌。判断输入牌是否是1)中的牌型。 程序根据牌型判断自己的手牌是否可以压该牌,如果可以压,则显示可以压的牌型,并输出,从手牌减去已压的牌。显示剩余的牌

4)循环过程要求3 直到牌出完。

代码:

#define Gxjun
#define LOCAL
#define Test
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<time.h>
#define Arr_Len(arr)  sizeof(arr)/sizeof(arr[0])
#define maxn 18

int cod[maxn]={0};
int mycod[maxn];
int chu[maxn];
int rtmp[maxn]={0} ,mtmp[maxn]={0};
int mx  , mn ;

typedef struct LianD
{
int en ; //连对结尾的牌号
int du ; //最接近的度数
int cnt ; //连对的个数
int tem ; //临时存储的结尾牌号
//复制值函数
void copfun(struct LianD  *Ld){

Ld->cnt=cnt;
Ld->du=du;
Ld->en=en;
Ld->tem=tem;
}
//初始化
void init(){
en=du=cnt=tem=0;
}
};

enum AlgTyNn{  ShunZi=1 ,
LianDui
};

int cmp(const void * arg  , const void * brg){

int * ar =(int *) arg ;
int * br =(int *) brg ;
return *ar - *br ;

}

#ifdef Test
void init()
{
int i , cnt=0 ;

#ifdef Test
#ifndef LOCAL
#define LOCAL
#endif // LOCAL
#endif // Test

#ifdef LOCAL
freopen("data.in","r",stdin);
#endif // Test
memset(mycod , 0 , sizeof(mycod));
mycod[cnt]=17;
cod[17]=cod[16]=1;   //一对王
for(i=3 ; i<16 ;i++)
cod[i]=4;
while(++cnt<maxn){
scanf("%d" ,&mycod[cnt]);
cod[mycod[cnt]]--;
}
#ifdef Gxjun
fclose(stdin);
freopen("CON","r",stdin);
#endif // Gxjun
qsort(mycod+1 , cnt-1 , sizeof(mycod-1) , cmp);
}

#endif // Test

#ifndef Test
void init(){

int i , hopg ,cnt=0 ;
memset( mycod , 0 , sizeof(mycod) ) ;
mycod[cnt]=17;
for(i=3 ; i<16 ;i++)
cod[i]=4;
cod[16]=1;   //一对王
cod[17]=1;
srand(time(NULL));

while(cnt<maxn){

hopg = rand()%maxn ;

if( hopg>0&&cod[hopg] > 0 ){
cod[hopg]--;
mycod[++cnt]=hopg;
}
}
qsort(mycod+1 , cnt-1 , sizeof(mycod-1) , cmp);

}
#endif // Test

//对方输出
void inputs(){

int cnt=0;
memset(chu , 0 , sizeof(chu));
while(1){
scanf("%d",&chu[++cnt]);
if(chu[cnt]<0) break;
}
chu[0]=cnt-1 ;  //最开头放置纸牌的数目

}

//打印剩余
void output(){

printf("  |   剩牌系: ");
//剩余牌系
int cont=0;
for(int j=3 ; j<maxn ;j++){
cont+=mtmp[j];
for(int k=0; k<mtmp[j] ; k++){
printf("%d ",j);
}
}
mtmp[0]=cont;
printf("\t剩牌数: [ %d ]",mtmp[0]);
puts("");
}

//计数排序
void connt(int *tmp , const int ss []){

//memset(tmp ,0 ,sizeof(tmp));
tmp[0]=ss[0];
for(int i= 1 ; i<=ss[0] ; i++)
tmp[ss[i]]++;     //计数排序
}

//判断是否有炸弹或者王炸
void ZhaDan(){

for(int i=3 ; i<maxn ;i++)
if(mtmp[i]==4){
printf("%d %d %d %d ",i,i,i,i);
mtmp[i]-=4;
output();
return ;
}

if(mtmp[16]==1&&mtmp[17]==1){
printf("%d %d ",16,17);
mtmp[16]=mtmp[17]=0;
output();
return ;
}
puts("没有牌能压过!");
return ;
}

//如果是连对处理方案
void AlgLDOrShZi(int var){

int i=mn+1 ,len = mx-mn+1 ,kk=0;
struct LianD   Ld , Tmpld;

Ld.init();
//时间复杂度为0(n^2)
for( i = mn+1 ; i<15 ; i++){
Tmpld.init();
Tmpld.tem=i;
for(int j=i ; j<i+len ; j++){
if(mtmp[j]>=var){

if(mtmp[j]==var){
Tmpld.du++;
}
Tmpld.cnt++;
}else{
Tmpld.init();
Tmpld.tem=j+1;
}
if(Tmpld.cnt==len){
Tmpld.en =Tmpld.tem;
if(Ld.en==0||Ld.du<Tmpld.du)
Tmpld.copfun(&Ld);
break;
}
}
}
if(Ld.cnt == len){
puts("提示:");
for(i=Ld.en; i<Ld.en+len ;i++){
mtmp[i]-=var;
kk=0;
while(kk++<var)
printf("%d " ,i);
}
output();
}else{
//寻求炸弹或者王炸
ZhaDan();
}

}

//三带的处理情况

void  AlgShanDai(){

int pos=0 , cnt=0 , res=0,fcnt=0;
int mcnt=0 , mpos=0 ;
//分析牌型
for(int i=mn;i<=mx ;i++){
if(rtmp[i]==3){
pos=i;
cnt++;
}else if(rtmp[i]>0){
res+=rtmp[i];  //统计剩余牌的数量
fcnt++;
}
}
if((fcnt==cnt&&res%cnt==0)||(fcnt<cnt&&(fcnt*res==cnt||0==res))) ;
else{
puts("输出的牌不符合规则!请重新输出:");
return ;
}
res/=cnt;
for(int i=pos-cnt+2 ; i<=pos; i++){
if(rtmp[i]!=3){
puts("输出的牌不符合规则!请重新输出:");
return ;
}
}

//如果为三带情况 即 cnt =1
for(int i=pos-cnt+2 ; i<17 ;i++){
if(mtmp[i]==3){
mpos=i;
mcnt++;
}else
mcnt=0;
if(mcnt==cnt) break;
}
//查询副牌是否能够满足
if(mcnt==cnt){
//说明有解决方案
int stpos = mpos - cnt +1 ;
int src[maxn]={0} ,tt=0;
bool tag = false;
for(int i=3 ; i<17 ;i++){
//满足不再连续范围之内的即可333444不能为3,4
if(i<stpos||i>mpos){
if(mtmp[i]>=res){
for(int kk=0 ; kk<mtmp[i] ;kk+=res){
src[tt++]=i;
if(tt==cnt){
tag = true;
break;
}
}
}
}
if(tag)  break ;
}
if(tt==cnt){
//则解决方案为
int mstpos = mpos - cnt +1;
for(int i=mstpos ; i<=mpos ; i++){
printf("%d %d %d " ,i,i,i);
mtmp[i]-=3;
}
//打印副牌
for(int i=0; i<tt ;i++){
for(int k=0 ;k<res ;k++){
printf("%d ",src[i]);
}
mtmp[src[i]]-=res;
}
output();   //打印剩余牌
}
}else{

//查询是否有炸弹
ZhaDan();
}
}

//四带情况
void AlgSiDai(){
if(chu[0]>4)  ZhaDan();
else{
for(int i=chu[1]+1 ; i<15 ; i++){
if(mtmp[i]==4){
printf("%d %d %d %d ",i,i,i,i);
mtmp[i]-=4;
output();
return ;
}
}
if(mtmp[16]==1&&mtmp[17]==1){
printf("%d %d ",16,17);
mtmp[16]=mtmp[17]=0;
output();
return ;

}
puts("没有牌能压过!");
return ;
}
}

//对子的情况
void AlgDuiZi(){
for(int i=chu[1]+1 ; i<16 ;i++)
{
if(mtmp[i]>1&&mtmp[i]<4){
printf("%d %d \n",i,i);
mtmp[i]-=2;
output();
return ;
}
}
ZhaDan();
}

//对于个子的情况
void AlgGreZi(){

for(int i=chu[1]+1 ; i<18 ;i++)
{
if(mtmp[i]>0&&mtmp[i]<4){
printf("%d \n",i);
mtmp[i]-=1;
output();
return ;
}
}
ZhaDan();
}

//查询对应的方案

//对子

bool IsDuiZi(){

if(chu[0]==2)  //则必定是对子
return true;
return false ;
}

//个子
bool IsGreZi(){

if(chu[0]==1)  //则必定是对子
return true;

return false ;
}

//判断是否是顺子
bool IsShunZi(){

//顺子的条件
if(chu[0]>4){
if((mx-mn+1==chu[0])&& mx<15)
return true ;
}
return false;
}

//判断是否是连对
bool IsLianDui(){
if(chu[0]>5&&mx<15){
for(int i=mn; i<=mx ;i++)
if(rtmp[i]!=LianDui)
return false;
return true ;
}
return false;
}

//判断是否是三带
bool IsShanDai(){

for(int i=mn; i<=mx ;i++)
if(rtmp[i]==3)
return true;

return false ;
}

//判断是否是四带或者炸弹
bool IsSiDai(){

for(int i=mn; i<=mx ;i++)
if(rtmp[i]==4)
return true;

return false ;
}

//统计判断
void AlgMxn(){

//求最大值,最小值
for(int i =3 ; i<= 17 ; i++)
if(rtmp[i]>0){
mn = i;
break;
}
for(int i =17 ; i>= 3 ; i--)
if(rtmp[i]>0){
mx = i;
break;
}

}

void print(){

int i;
for(i=1; i<17; i++)
printf("%d ", mycod[i]);
printf("%d\n", mycod[17]);

}

//检测出牌方
bool checked(){

for(int i=1 ; i<maxn ;i++){
if(cod[i]<rtmp[i])
return false;
}

if(IsGreZi()
||IsDuiZi()
||IsShunZi()
||IsSiDai()
||IsShanDai()){
for(int i=1; i<=chu[0] ;i++){
cod[chu[i]]--;
}
}
return true ;
//如果为一对王
if(chu[0]==2&&mtmp[16]==1&&mtmp[17]==1)
return true;
return false;
}

int main(int argc , char * argv)
{

init();

memset(mtmp , 0 , sizeof(mtmp));

connt(mtmp , mycod);
print();

while(true){
printf("请出牌:\n");
while(1){
inputs();
memset(rtmp , 0 ,sizeof(rtmp));
connt(rtmp , chu);
AlgMxn();
if(checked())   break;
else
puts("输出的牌不符合规则!请重新输出:");
}
//如果满足顺子
if(IsGreZi())
AlgGreZi();
else
if(IsDuiZi())
AlgDuiZi();
else
if(IsShunZi())
AlgLDOrShZi(ShunZi);
else
if(IsLianDui())
AlgLDOrShZi(LianDui);   //对于连对的情况
else
if(IsShanDai())
AlgShanDai();
else
if(IsSiDai())
AlgSiDai();

if(mtmp[0]<1){
puts("恭喜你,win!");
break;
};
}
return 0;

}


内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: