ArrayList | 简单的洗牌算法

简介: 这是一个关于创建和洗牌扑克牌程序的摘要:程序包括以下步骤:1. 创建一副扑克牌(52张,不包括大小王)。2. 洗牌,随机打乱扑克牌的顺序。3. 揭牌,模拟玩家轮流从牌堆中抽取指定数量的牌。

一个洗牌程序需要包含:


  1. 创建一副扑克牌(除去大小王剩下52张,每种花色13张)。
  2. 洗牌,打乱牌的顺序。
  3. 揭牌,每位玩家轮流揭牌,从洗完后的牌组中获得自己的牌。


因此,我们可以依照以下思路来完成一个洗牌程序:



一、创建扑克牌类Poker



观察扑克牌可知,每张扑克牌都具备的属性有:花色(suit)与牌面数字(rank)。因此,我们创建的Poker类中就需包含这两个属性,以及它们对应的构造方法,getter()与setter()方法(这两个方法在本程序中用不到,之所以加上是为了体现这里的封装特性,不加也不会对程序有很大影响)。


同时,为了让程序运行之后的结果更加清楚,我们还需重写一下toString()方法。这样,一个Poker类就设计好了。



public class Poker {
    private String suit;    //花色
    private int rank;   //面值
 
    //构造方法
    public Poker(String suit, int rank) {
        this.suit = suit;
        this.rank = rank;
    }
 
    //getter()与setter()
    public String getSuit() {
        return suit;
    }
 
    public void setSuit(String suit) {
        this.suit = suit;
    }
 
    public int getRank() {
        return rank;
    }
 
    public void setRank(int rank) {
        this.rank = rank;
    }
 
    //重写toString()
    @Override
    public String toString() {
        return "{" + suit + " " + rank + "}";
    }
}


二、创建游戏类Game


Game类中包含创建一副扑克牌、洗牌、揭牌的各种方法,控制的是游戏的主要环节。


1、获得一副扑克牌


在Poker中,我们并没有给出扑克牌具体的花色和牌面数字。Poker类相当于只是一张扑克牌的概念,只是规定了每张Poker牌由suit和rank组成。但在Game中,我们需要真正“得到”这一整副扑克牌。


(1)花色数组


可以用一个花色数组来存放这四种花色:[ ♥ , ♠ , ♣ , ♦ ]


private static final String[] suits = {"红桃","黑桃","梅花","方片"};


(2)指定玩家人数与玩家摸牌数


我们指定有3位玩家,每位玩家共可以摸5张牌。


public final int PLAYERS_NUMBER = 3;
public final int DEFAULT_CARDS = 5;


(3)创建一副扑克


首先,我们是通过ArrayList这个容器来存储一副扑克的。可以把ArrayList想象成装牌的小方盒。因此我们要new ArrayList<>(),并把传入的数据类型指定为Poker:


List<Poker> pokerList = new ArrayList<>();


然后我们就可以根据花色与数值,将不同的牌添加进pokerList当中。扑克牌共4种花色,每种花色的数值是1~13(这里先不把11、12、13单独转换成J、Q、K)。用两个for循环嵌套即可完成这个过程。


    public List<Poker> buyPoker() {
        //通过ArrayList存放Poker
        List<Poker> pokerList = new ArrayList<>();
 
        //给pokerList中添加扑克牌
        for (int i = 0; i < 4; i++) {
            for (int j = 1; j <= 13; j++) {
                //构造单张扑克牌
                Poker poker = new Poker(suits[i],j);
                //加入
                pokerList.add(poker);
            }
        }
        return pokerList;
    }


4花色从前面的花色数组中通过suits[i]依次获取,数值则通过j的值来获取。最终返回pokerList即可。

这时我们在Test类的main()方法中调用buyPoker()方法获取扑克牌,并打印,呈现如下效果:


//Test.java
 
import java.util.List;
 
public class Test {
    public static void main(String[] args) {
        Game game = new Game();
        List<Poker> pokerList = game.buyPoker();
        System.out.println(pokerList);
    }
 
}



...



可以看到,52张扑克牌依次顺序展现在我们眼前。获取扑克牌的过程就完成了。


2、洗牌


接下来要做的是洗牌,也就是把牌的顺序随机打乱。通过random对象即可搞定。


下图是创建了之后的扑克牌,打乱的方式就是随机的不同的牌之间两两调换位置。我们可以用random对象获取一定范围内的随机数作为要调换的目标牌的下标,这样就能做到随机调换了。但这里有一个问题,那就是洗牌的过程中牌在不断地变化,如何方便地控制好random的范围呢?




random.nextInt(a)创出的随机数范围是[0.a)。如果从左向右遍历,那么左边的牌需要在右边随机找牌换,随着牌下标i的不断变化,在右边随机找牌这个操作相对比较繁琐。但如果我们转换思路,从最右边也就是ArrayList的最后一个元素由右向左遍历,那么控制随机数下标范围就会容易得多。用i表示下标的话,只需要random.nextInt(i),即可获得[0,i)之间的随机数,恰好是左半边的范围。



结合ArrayList获取与设置元素的方法get()和set(),可以得到如下代码:


    public void shuffle(List<Poker> pokerList) {
        for (int i = pokerList.size()-1; i > 0; i--) {
            //创建random对象
            Random random = new Random();
            int index = random.nextInt(i);
            //交换
            this.swap(pokerList,i,index);
        }
    }
    private void swap(List<Poker> pokerList,int i,int j) {
        Poker tmp = pokerList.get(i);
        pokerList.set(i,pokerList.get(j));
        pokerList.set(j,tmp);
    }


调用后结果如下:



第一行是洗牌前,第二行是洗牌后,牌已经被随机打乱。


3、揭牌


揭牌的过程实质上就是将牌从pokerList中移除的过程。想象一下现实中揭牌的过程,在洗完牌后把牌倒扣在桌面上,每个玩家依次从一堆牌的最顶上摸走一张牌。在编程中的体现就是,将0下标(也就是第一个位置)的牌移除,并将移除了的牌添加到玩家手中。


        for (int i = 0; i < DEFAULT_CARDS; i++) {
            for (int j = 0; j < PLAYERS_NUMBER; j++) {
                players.get(j).add(pokerList.remove(0));
            }
        }



但如何实现“三位玩家轮流摸牌”呢?如果把三位player视作三个ArrayList,每个player简化为存放他们各自牌的容器,那么此时这三个player是彼此独立的。如果我们再创建一个List<Poker>类型的List容器,将这三个player装入,那么它们三个也就具有顺序关系了。于是,实现代码如下:


    public List<List<Poker>> getCards(List<Poker> pokerList){
        List<List<Poker>> players = new ArrayList<>();
 
        List<Poker> player1 = new ArrayList<>();
        List<Poker> player2 = new ArrayList<>();
        List<Poker> player3 = new ArrayList<>();
 
        players.add(player1);
        players.add(player2);
        players.add(player3);
 
        for (int i = 0; i < DEFAULT_CARDS; i++) {
            for (int j = 0; j < PLAYERS_NUMBER; j++) {
                players.get(j).add(pokerList.remove(0));
            }
        }
        return players;
    }


运行后得:



三、完整代码


//Test.java
 
import java.util.List;
 
public class Test {
    public static void main(String[] args) {
        Game game = new Game();
        List<Poker> pokerList = game.buyPoker();
        System.out.println(pokerList);
        game.shuffle(pokerList);
        System.out.println(pokerList);
        List<List<Poker>> players = game.getCards(pokerList);
        for (int i = 0; i < game.PLAYERS_NUMBER; i++) {
            System.out.println("玩家" + (i+1) + players.get(0));
            System.out.println();
        }
        System.out.println("剩下的牌:" + pokerList);
 
    }
 
}


//Poker.java
 
public class Poker {
    private String suit;    //花色
    private int rank;   //面值
 
    //构造方法
    public Poker(String suit, int rank) {
        this.suit = suit;
        this.rank = rank;
    }
 
    //getter()与setter()
    public String getSuit() {
        return suit;
    }
 
    public void setSuit(String suit) {
        this.suit = suit;
    }
 
    public int getRank() {
        return rank;
    }
 
    public void setRank(int rank) {
        this.rank = rank;
    }
 
    //重写toString()
    @Override
    public String toString() {
        return "{" + suit + " " + rank + "}";
    }
}


//Poker.java
 
public class Poker {
    private String suit;    //花色
    private int rank;   //面值
 
    //构造方法
    public Poker(String suit, int rank) {
        this.suit = suit;
        this.rank = rank;
    }
 
    //getter()与setter()
    public String getSuit() {
        return suit;
    }
 
    public void setSuit(String suit) {
        this.suit = suit;
    }
 
    public int getRank() {
        return rank;
    }
 
    public void setRank(int rank) {
        this.rank = rank;
    }
 
    //重写toString()
    @Override
    public String toString() {
        return "{" + suit + " " + rank + "}";
    }
}
相关文章
|
1月前
|
存储 算法 测试技术
ArrayList集合的两个实例应用,有趣的洗牌算法与杨辉三角
ArrayList集合的两个实例应用,有趣的洗牌算法与杨辉三角
29 1
|
1月前
|
存储 算法 PHP
开发一款扑克游戏,请给出一套洗牌算法,公平的洗牌并将洗好的牌存储在一个整形数组里?
开发一款扑克游戏,请给出一套洗牌算法,公平的洗牌并将洗好的牌存储在一个整形数组里?
18 1
开发一款扑克游戏,请给出一套洗牌算法,公平的洗牌并将洗好的牌存储在一个整形数组里?
|
10月前
|
算法 C# 图形学
彻底搞懂洗牌算法
彻底搞懂洗牌算法
118 0
|
10月前
|
算法 图形学
面试高频题之三-洗牌算法
面试高频题之三-洗牌算法
|
11月前
|
算法 Go 索引
870. 优势洗牌:田忌赛马:贪心算法+双指针
这是 力扣上的 870. 优势洗牌,难度为 中等。
|
11月前
|
算法 C# Python
转:洗牌算法,随机算法的别名
洗牌算法是随机打乱一组数据的算法。常用的洗牌算法有随机置换算法和Fisher-Yates算法。随机置换算法是在数组中随机交换元素的位置,而Fisher-Yates算法是从数组的末尾向前遍历,并在遍历过程中与随机位置交换元素。
91 0
|
12月前
|
算法 JavaScript Java
Math.random()传参?什么是随机种子?什么是洗牌算法?
Math.random()传参?什么是随机种子?什么是洗牌算法?
152 1
|
算法 索引
LeetCode算法小抄-- N 叉树 和 洗牌算法
LeetCode算法小抄-- N 叉树 和 洗牌算法
|
算法
洗牌算法在文档管理系统中的运用
洗牌算法在文档管理系统中的应用之一是随机化排序,用于将文档的顺序打乱,以提高用户查找文档时的效率和体验。
497 0
|
算法 JavaScript
洗牌算法实现随机排序
洗牌算法实现随机排序。
117 0