OhaystackO

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

    之前在家里和妈妈姐姐一起玩了斗地主,玩的时候我妈突然问我“欢乐斗地主是怎么洗牌发牌的?”那个时候还不知道怎么解决,最近正好学完了集合,觉得里面有很多东西可以利用一下,就把这个过了大半年的问题翻出来解决一下

 

1.思路分析

   思来想去这个案例的难点就难在洗牌上面,如果使用Random生成随机数来实现乱序,也可以实现这个案例,但是有没有更简便的方法呢。于是我向度娘发起了提问,然后得到了这样一个答案:

 然后我在向帮助文档寻求帮助的时候又让我发现一个好东西:

只能说是瞌睡来了送枕头的程度。

 

2.生成一副牌并装进“牌盒”

上面可以看到这个方法的形参是List类型,点开这个形参List后我看到了两个熟悉的身影:

这不等价于已经给了我答案了嘛,说干就干!

ArrayList<String> box = new ArrayList<String>();

//把牌装进去
String[] colors = {"♦", "♥", "♣", "♠"};
String[] nums = {"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"};
    for (int i = 0; i < colors.length; ++i) {
        for (int j = 0; j < nums.length; ++j) {
            box.add(colors[i] + nums[j]);
        }
    }
    box.add("JOKER");
    box.add("joker");

//输出看看效果
for(String card:box){
    System.out.print(card);
}

实现效果如下:

效果还不错,但是因为用的是增强for进行遍历,不能控制换行看起来有点长,但是效果是对的

 

3.洗牌,打乱顺序

用刚刚说的shuffle方法进行打乱顺序试试看:

//因为是静态方法所以直接使用方法名进行调用即可
Collections.shuffle(box);

//还是输出看看效果
for(String card:box){
    System.out.print(card);
}

实现效果如下:

可以看到在调用了shuffle方法之后顺序已经变了

4.发牌,一人一张

想要斗地主至少要有三个玩家,再者就是玩家拿到牌之后会存起来不是一次性的,所以要有一个地方用来存储玩家的牌,还是一脉相承的使用ArrayList来生成玩家的牌的序列吧:

//生成玩家,hole是底牌(3张)
ArrayList<String> player1 = new ArrayList<String>();
ArrayList<String> player2 = new ArrayList<String>();
ArrayList<String> player3 = new ArrayList<String>();
ArrayList<String> hole = new ArrayList<String>();

有了玩家现在来思考发牌的时候有什么讲究没有,想了一圈之后总结出两点:1.每人一张,轮着来  2.最后三张是底牌,没有抢地主之前不能翻,自然也就要留下来。

留底牌的操作很简单,当已经发出去box.size()-3张牌的时候就停止发牌,剩下的牌全部存到hole中作为底牌。

for (int i = 0; i < box.size(); ++i) {
    String card = box.get(i);
    if (i >= box.size() - 3) {//剩三张牌是底牌
        hole.add(card);
    }
}

这里有一个小难点就是怎么做到轮流发牌,用图表说明就是以下这样:

现在来找规律:可以看到玩家那一排是三个一循环,所以不难想到把索引为3的倍数的牌发给玩家三,索引为2的倍数的牌发给玩家2,索引为1的倍数的牌发给玩家一。但是这个办法肯定是行不通的,比如索引为6的牌既是3的倍数也是2的倍数又是1的倍数。那么顺着倍数的思路发散会发现%是个好东西,0%3==0,1%3==1,2%3==2,3%3==0,4%3==1,5%3==2 ...,所以最后用对3取余的操作来判断发牌发给谁。

for (int i = 0; i < box.size(); ++i) {
    String card = box.get(i);
    if (i >= box.size() - 3) {
        hole.add(card);//循环还在继续,最后三张牌会循环存入hole
    } else {//用余数来判断轮到谁拿牌
        if (i % 3 == 0) {
            player3.add(card);
        } else if (i % 3 == 1) {
            player2.add(card);
        } else if (i % 3 == 2) {
            player1.add(card);
        }
    }
}

现在主体需求基本结束了,来看看最后大家拿到的牌都是什么:

//每个玩家看牌时都会用到,所以单独提出一个方法
public static void check(String name,ArrayList<String> arr) {
    System.out.print(name+"'s cards: ");
    for(String s : arr){
        System.out.print(s+" ");
    }
    System.out.println();
}

check("haystack",player1);
check("bed",player2);
check("boat",player3);
System.out.print("the hole cards is:"+hole);

调用结果如下:

 当然,每次运行调用结果肯定是不一样的。

5.改进升级

        上面其实已经实现了洗牌发牌的功能了,但是也能看到结果输出是无序的,看起来很难受,如果能把拿到手的牌排好序再输出就好了。

既然有了想法,那就再进行一次分析吧。

要想遍历结果是有序的,也就是说在输出前要做好有序化,那么就要考虑两个问题:

   1.洗牌之后所有的牌是无序的怎么在拿到各个玩家手里之后有序化

    2.大小顺序由什么来定?

在集合的学习中总结的集合层级结构

在学集合的时候好像看到过TreeSet是有序的集合结构,所以我想到帮助文档中求证一下

其中TreeSet在帮助文档中的介绍是这样的:

 这里提到元件的排序方法由构造方法决定,TreeSet的构造方法中,无参构造方法的描述中也提到了自然排序(natural ording)

 那么上面提到的natural ording又是什么呢?继续点击跟进会发现跳转到了Comparable接口的界面:

也就是说TreeSet的无参构造方法会自动将元素按照自然排序的顺序进行整理。那么每个玩家都给一个TreeSet进行保存手里牌好了:

TreeSet<Integer> player1 = new TreeSet<Integer>();
TreeSet<Integer> player2 = new TreeSet<Integer>();
TreeSet<Integer> player3 = new TreeSet<Integer>();
TreeSet<Integer> hole = new TreeSet<Integer>();//底牌

TreeSet是按照自然顺序对元素进行排序,但是扑克牌的顺序并不是单纯地自然排序。

我最初想到的是Collections的sort方法,因为在初始版本的时候瞟过一眼有点印象

但是具体描述长这样:

也就说说如果我要用这个方法,我就要自己定义一个比较器,也不是说不行,就是想有没有更简单的方法呢。

所以最后还是要从TreeSet下手,自然排序指的是如果元素的值是1,2,3...10,11,12或者a,b,c...x,y,z,那么就会按照升序排列

但是扑克牌会先有一个特殊字符,再来才是数字。

那么怎么把扑克牌的序列内容和自然数字或字母联系在一起呢?

这里我能想到的最简单办法的就是索引,但是洗牌的时候只会打乱扑克牌但是不会打乱索引

虽然索引是按照自然排序排列的但是牌已经被打乱了,洗牌过后的索引对应的就是无序的牌,此时发牌给玩家就是初级版本里面的效果:

 那么如果单独打乱索引呢?

 

如果需要单独洗索引,就要把索引单独拿出来放到ArrayList中单独操作。

//单独创建一个List保存索引,这样不会影响到box中的索引和牌的顺序
ArrayList<Integer> indexs = new ArrayList<Integer>();
for (int i = 0; i < 54; ++i) {
    indexs.add(i);
}
Collections.shuffle(indexs);

这个时候玩家拿到手的无序索引在经过TreeSet排序后,再按照对应的索引去原来的box中找到对应的牌

说到一对一还有谁能比键值对更合适吗?所以这里就用键值对来实现索引和牌的对应

HashMap<Integer, String> box = new HashMap<Integer, String>();
String[] colors = {"♦", "♣", "♥", "♠"};
String[] nums = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
Integer order = 0;

for (int i = 0; i < nums.length; ++i) {
    for (int j = 0; j < colors.length; ++j) {
        box.put(order, colors[j] + nums[i]);
        order++;
    }
}
box.put(53, "joker");
box.put(52, "JOKER");

到这里思路大概清晰了,那下面看一下运行效果吧。

check("haystack",player1,box);
check("sheep",player2,box);
check("cloud",player3,box);

运行结果如下:


 

 是我想要的结果!

总结:帮助文档yyds

posted on 2021-08-29 12:04  OhaystackO  阅读(95)  评论(0编辑  收藏  举报