斗地主手牌最少手数的搜索

EasyGame 同时被 3 个专栏收录
14 篇文章 0 订阅
5 篇文章 0 订阅
13 篇文章 0 订阅

基本思路:

        1) 先确定火箭:判断是否有大小王。 
2) 再确定炸弹:判明是否有四头。 
        3) 再确定三条和三顺:在已经确定的三条中判断是否包含相邻的三条,如果有,则将其组成三顺。注意,应该使三顺的数量尽可能大。
即如果有444555666,则将其合成一个三顺,而不是分成444555一个三顺和666一个三条。 
        4) 再确定单顺:判断单顺时必须去除四个2以外的所有炸弹。
首先判断是否存在除了三条牌(这里的三条是指所有的三条)以外的连牌。
if(havaNext)则将其提取出来。then,将剩余的牌与每一个三条(不包含三顺)进行试组合,
if能够重新组成单顺和对子,则将原有的三条取消,重新组合成连牌和单顺
        5) 再确定双顺:首先,如果两单顺牌完全重合,则将其重新组合成双顺。
其次,在除炸弹、三顺、三条、单顺以外的牌中检测是否包含双顺。如果有,将其提取出来。 
        6 再确定对子:在炸弹、三顺、三条、连牌、双顺以外的牌中检测是否存在对子,如果存在将其提取出来。 
        7) 再确定单牌:除了炸弹、三顺、三条、连牌、双顺、对子以外的所有牌张都是单牌。 
补充:
很显然,单顺和三条炸弹等的处理最追求最少手数的基础上是非常麻烦的;一种方法是穷举,二种方法是字典法,方法三就是dp问题的形式求解。
上面的4)是一种方法,另一种方法在放弃除去2王炸弹的情况下,我们可以实现的最少手数依次和放弃三条的情况下的手数做比较,最终选择其中手数最少的一种。
打分系统:并将各个牌的情况打分。这个打分系统将后面再说。

中心程序

package com.byk.play;

import java.util.ArrayList;
import com.byk.ddz.PaixDic;
import com.byk.ddz.XuanPai;

public class CopareShouPaiShuLiang {
	static int[] arr1 = XuanPai.set17Pai();
	static int[] arr  = new int[]{3,4,5,6,7,8,9,10,11,12,13,14,16,20,30};
	public static void main(String[] args) {
		ArrayList<PaixDic> alp1 = new ArrayList<>();
		ArrayList<PaixDic> alp = new ArrayList<>();
		int b = fangfa2(alp1);
		int a = fangfa1(alp);
		if(a>b){
			System.out.println("决定选择的方法"+alp);
		}else if(a<b){
			System.out.println("决定选择的方法"+alp1);
		}else{
			System.out.println("两种方法可以酌情考虑");
		}
	}
	public static int fangfa2(ArrayList<PaixDic> alp1) {
		ArrayList<Integer> alr2= new ArrayList<>();
		for(int i=0;i<arr1.length;i++){
			alr2.add(arr1[i]);
		}//将数组转到集合中
		if(pdHuoJian(alr2)){//判断火箭
			ArrayList<Integer> alk  = new ArrayList<>();
			alk.add(20);
			alk.add(30);
			removeAll(alr2, alk);
			PaixDic  pxdic= new PaixDic("火箭",alk);	
			alp1.add(pxdic);
		}
		ArrayList<PaixDic> alpk = new ArrayList<>();
		returnShunZi(alpk, alr2);//顺子处理
		alp1.addAll(alpk);
		returnAfterRemove(alpk,alr2);
		
		ArrayList<PaixDic> ack = returnZhaDan(alr2);//炸弹处理
		alp1.addAll(ack);//[炸弹,null]
		returnAfterRemove(ack,alr2);
		
		ArrayList<PaixDic> ack1 = return3Tiao(alr2);//三条处理
		alp1.addAll(ack1);
		returnAfterRemove(ack1,alr2);
		
		ArrayList<PaixDic> ack2 = returnDuiZi(alr2);//对子处理
		alp1.addAll(ack2);
		returnAfterRemove(ack2,alr2);
		
		ArrayList<PaixDic> alpd = returnDanZhi(alr2);//单牌处理
		alp1.addAll(alpd);
		
		for(PaixDic ap:alp1){
			System.out.println(ap);
		}
		int num  = comuShouShu(alp1);
		System.out.println("[火箭->顺子->炸弹->三带->对子->单牌]这样的手牌数为:"+num);
		return num;
	}
	public static int fangfa1(ArrayList<PaixDic> alp){
		ArrayList<Integer> alr2= new ArrayList<>();	
		for(int i=0;i<arr1.length;i++){
			alr2.add(arr1[i]);
		}//将数组转到集合中
		if(pdHuoJian(alr2)){//判断火箭
			ArrayList<Integer> alk  = new ArrayList<>();
			alk.add(20);
			alk.add(30);
			removeAll(alr2, alk);
			PaixDic  pxdic= new PaixDic("火箭",alk);	
			alp.add(pxdic);
		}
		ArrayList<PaixDic> ack = returnZhaDan(alr2);//炸弹处理
		alp.addAll(ack);//[炸弹,null]
		returnAfterRemove(ack,alr2);
		ArrayList<PaixDic> ack1 = return3Tiao(alr2);//三条处理
		alp.addAll(ack1);
		returnAfterRemove(ack1,alr2);
		ArrayList<PaixDic> alpk = new ArrayList<>();//顺子需要自己建立一个中间字典
		returnShunZi(alpk, alr2);//顺子处理
		alp.addAll(alpk);
		returnAfterRemove(alpk,alr2);
		ArrayList<PaixDic> ack2 = returnDuiZi(alr2);//对子处理
		alp.addAll(ack2);
		returnAfterRemove(ack2,alr2);
		ArrayList<PaixDic> alpd = returnDanZhi(alr2);//单排处理
		alp.addAll(alpd);
		for(PaixDic ap:alp){
			System.out.println(ap);
		}
		int num  = comuShouShu(alp);
		System.out.println("[炸弹->三带->火箭->顺子->对子->个]这样的手牌数为:"+num);
		return num;
	}
	//在原集合中删除字典集合中出现过的元素;输入字典集合和原始整数集合。
	public static void returnAfterRemove(ArrayList<PaixDic> alpd,ArrayList<Integer> al){
		if(alpd.get(0).getAlr().isEmpty()){
			return;
		}else{
			for(int i=0;i<alpd.size();i++){
				ArrayList<Integer> alr = alpd.get(i).getAlr();
	//			al.removeAll(alr);
				removeAll(al, alr);
			}
		}
	}
	
	public static boolean pdHuoJian(ArrayList<Integer> alr) {
		int count=0;
		for(int i=0;i<alr.size();i++){
			if(alr.get(i) == 20 || alr.get(i) == 30){
				count++;
			}
		}
		if(count>1){
			return true;
		}else{
			return false;
		}
	}
	//判断炸弹并返回炸弹的个数,ps炸弹,三顺,对子都可以抽象为一个方法,略。
	public static ArrayList<PaixDic> returnZhaDan(ArrayList<Integer> alr){
		ArrayList<PaixDic> alk = new ArrayList<>();
		boolean flag=false;
		for(int i=0;i<13;i++){
			int count = 0;
			for(int j=0;j<alr.size();j++){
				if(alr.get(j)==arr[i]){
					count++;
				}
			}
			ArrayList<Integer>  al2= new ArrayList<>();
			if(count==4){
				flag = true;
				for(int k=0;k<4;k++){
					al2.add(arr[i]);
				}
				PaixDic pxdic = new PaixDic("炸弹",al2);
				alk.add(pxdic); 
			}
		}
		if(!flag){
			PaixDic pxdic = new PaixDic("炸弹",new ArrayList<Integer>());
			alk.add(pxdic); 
		}
		return alk;
	}
	//判断是否存在三条;无视d):将三条弄到一起出;判断手数的时候再判断三顺。
	public static ArrayList<PaixDic> return3Tiao(ArrayList<Integer> alr){
		ArrayList<PaixDic> alk = new ArrayList<>();
		boolean flag=false;
		for(int i=0;i<13;i++){
			int count = 0;
			for(int j=0;j<alr.size();j++){
				if(alr.get(j)==arr[i]){
					count++;
				}
			}
			ArrayList<Integer>  al2= new ArrayList<>();
			if(count==3){
				flag = true;
				for(int k=0;k<3;k++){
					al2.add(arr[i]);
				}
				PaixDic pxdic = new PaixDic("三条",al2);
				alk.add(pxdic); 
			}
		}
		if(!flag){
			PaixDic pxdic = new PaixDic("三条",new ArrayList<Integer>());
			alk.add(pxdic); 
		}
		return alk;
	}
	//逼近最后阶段,判断完单顺和双顺就基本结束了。
	public static void returnShunZi(ArrayList<PaixDic> alpk,ArrayList<Integer> alr){
//		ArrayList<PaixDic> alpp = new ArrayList<>();
		for(int i=0;i<8;i++){
			ArrayList<Integer> ar = new ArrayList<>();
			for(int j=arr[i];j<arr[i]+5;j++){
				ar.add(j);
			}//for里面装的一个5连集合。
			if(alr.containsAll(ar)){
				PaixDic pdc = new PaixDic("单顺",ar);
				alpk.add(pdc);
				removeAll(alr, ar);
//				System.out.println(alr);
				returnShunZi(alpk,alr);
			}
		}
		if(alpk.isEmpty()){
			PaixDic pxdic = new PaixDic("顺子",new ArrayList<Integer>());
			alpk.add(pxdic); 
			return;
		}
		//在进入穷举前需要对alr自动排序;但是递归过程中会逐步遍历
//		System.out.println(alr);
		for(Integer i:alr){
			for(PaixDic pdc:alpk){//遍历每一个独立出来的元素;寻找是否存在在一个Dic的Al后添加上来
				if(i==(pdc.getAlr().get(pdc.getAlr().size()-1)+1)){
					pdc.getAlr().add(i);
				}
			}
		}
	}
	//建立一个自己的remove函数,只删除一套元素.
	public static void removeAll(ArrayList<Integer> alr,ArrayList<Integer> ar){
		a:for(int i=0;i<ar.size();i++){
			for(int j=0;j<alr.size();j++){
				if(ar.get(i)==alr.get(j)){
					alr.remove(j);
					continue a;
				}
			}
		}
	}
	//判断对子;并返回有对子的字典。
		public static ArrayList<PaixDic> returnDuiZi(ArrayList<Integer> alr){
			ArrayList<PaixDic> ald = new ArrayList<>();
			boolean flag=false;
			for(int i=0;i<13;i++){
				int count = 0;
				for(int j=0;j<alr.size();j++){
					if(alr.get(j)==arr[i]){
						count++;
					}
				}
				ArrayList<Integer>  al2= new ArrayList<>();
				if(count==2){
					flag = true;
					for(int k=0;k<2;k++){
						al2.add(arr[i]);
					}
					PaixDic pxdic = new PaixDic("对子",al2);
					ald.add(pxdic); 
				}
			}
			if(!flag){
				PaixDic pxdic = new PaixDic("对子",new ArrayList<Integer>());
				ald.add(pxdic); 
			}
			return ald;
		}
		//判断对子;并返回有对子的字典。
		public static ArrayList<PaixDic> returnDanZhi(ArrayList<Integer> alr){
			ArrayList<PaixDic> aldz = new ArrayList<>();
			for(int i=0;i<alr.size();i++){
				ArrayList<Integer> aldr = new ArrayList<>();
				aldr.add(alr.get(i));
				aldz.add(new PaixDic("单牌",aldr));
			}
			return aldz;
		}
		public static int comuShouShu(ArrayList<PaixDic> alp){
			int num=0;
			for(PaixDic dic:alp){
				if(!dic.getAlr().isEmpty()){
					num += 1;
					}
				if(dic.getPaixing().equals("三条") && !dic.getAlr().isEmpty()){
					num-=1;
				}
				if(dic.getPaixing().equals("炸弹") && !dic.getAlr().isEmpty()){
					num -= 2;
				}
				if(dic.getPaixing().equals("火箭") && !dic.getAlr().isEmpty()){
					num -= 2;
				}//因为绝对手牌的原因
			}
			return num;
		}
}
工具类和便于手牌类型统计建立的集合类附上。

package com.byk.ddz;

import java.util.ArrayList;
/*
 * 在54张牌中随机选出17张用数组记录
 */
public class XuanPai {
	static int[] arr  = new int[]{3,4,5,6,7,8,9,10,11,12,13,14,16,20,30};
	//核心函数。10行的样子
	public static int[] set17Pai(){
		ArrayList<Integer> alr= new ArrayList<>(); 
		for(int i=0;i<arr.length-2;i++){
			for(int j=0;j<4;j++){
				alr.add(arr[i]);
			}
		}
		alr.add(20);
		alr.add(30);
		int[] arr2 = new int[17];
		int i=0;
		while(i<17){
			int k = (int) (alr.size()*Math.random());
			arr2[i] = alr.get(k);
			alr.remove((Integer)arr2[i]);
			i++;
		}
		return arr2;
	}
}




package com.byk.ddz;

import java.util.ArrayList;

public class PaixDic {
	String paixing;
	ArrayList<Integer> alr;
	public PaixDic(String paixing, ArrayList<Integer> alr) {
		super();
		this.paixing = paixing;
		this.alr = alr;
	}
	public PaixDic() {
		// TODO Auto-generated constructor stub
	}
	public String getPaixing() {
		return paixing;
	}
	public void setPaixing(String paixing) {
		this.paixing = paixing;
	}
	public ArrayList<Integer> getAlr() {
		return alr;
	}
	public void setAlr(ArrayList<Integer> alr) {
		this.alr = alr;
	}
	@Override
	public String toString() {
		// TODO Auto-generated method stub
		return paixing+":"+alr;
	}
	
}
给出一个执行结果:

单顺:[4, 5, 6, 7, 8, 9, 10]
单顺:[8, 9, 10, 11, 12, 13, 14]
炸弹:[]
三条:[]
对子:[]
[火箭->顺子->炸弹->三带->对子->单牌]这样的手牌数为:2
炸弹:[]
三条:[8, 8, 8]
单顺:[9, 10, 11, 12, 13, 14]
对子:[4, 4]
单牌:[5]
单牌:[7]
单牌:[6]
[炸弹->三带->火箭->顺子->对子->个]这样的手牌数为:5
决定选择的方法:
单顺:[4, 5, 6, 7, 8, 9, 10]
单顺:[8, 9, 10, 11, 12, 13, 14]
炸弹:[]
三条:[]
对子:[]



  • 0
    点赞
  • 2
    评论
  • 1
    收藏
  • 打赏
    打赏
  • 扫一扫,分享海报

©️2022 CSDN 皮肤主题:大白 设计师:CSDN官方博客 返回首页

打赏作者

章半仙

你的鼓励将是我创作的最大动力

¥2 ¥4 ¥6 ¥10 ¥20
输入1-500的整数
余额支付 (余额:-- )
扫码支付
扫码支付:¥2
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值