Java - 洗撲克牌之亂數排列

小弟以前擔任大學課程助教時,是乎有出過這個題目給同學們寫,想不到自己也有機會在面

試時撰寫到這個題目XD,後來發現網路上還有不少解答供參考,幸好以前為了避免同學們

抄襲,都很注重註解及報告的撰寫! 還記得占了40%左右(不人道阿!)。因此在這邊分享一下

小弟的作法,來彌補以前如此殘忍的作法!orz

基本上,一樣是亂數洗牌,但是還多了決定多少玩家的條件在裡面。我想比較麻煩的應該是

怎麼將已經產生的牌進行亂數排列又或者是預先就亂數一張一張產生撲克牌。

亂數的作法我是套用以前寫過的這篇之作法二

在這邊總共設計四個類別

ProcessGame.java 主程式

PokerSend.java 發牌者

PokerPlayer.java 玩家

PokerCard.java 撲克牌

接下來各自說明如下:

1. PokerCard.java
public class PokerCard {
 private String id;
 private String kind;
 private String kindName[] = {"黑桃","紅桃","方塊","梅花"};
 private String idName[] = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
 
 public PokerCard(int idIndex, int kindIndex){
  this.id = idName[idIndex];
  this.kind = kindName[kindIndex];
 }
 
 public String toString(){
  return "["+kind+"]"+id;
 }
}

這邊主要設定就是撲克牌的屬性,id表示牌號、kind表示種類

2. PokerPlayer.java
public class PokerPlayer {
 public PokerCard mycards[];
 public String playername;
 
 public PokerPlayer(String name, int cardNum){
  playername = name;
  mycards = new PokerCard[cardNum];
 }
 
 public void setMyCards(int pos, PokerCard cards){
  mycards[pos] = cards;
 }
 
 public void showMyCards(){
  StringBuilder showcard = new StringBuilder();
  for(int i = 0 ; i < mycards.length ; i++){
   showcard.append(mycards[i]+",");
  }
  showcard.deleteCharAt(showcard.length()-1);
  System.out.println(showcard.toString());
 }
 
 public String toString(){
  return playername;
 }
}

再來是玩家的類別,玩家本身所具備的就是有幾張撲克牌及稱呼

在初始化時決定有幾張牌,並且在洗牌時能夠set當下mycard array的值

3. PokerSend
public class PokerSend {
 private PokerCard cards[]; //存放牌
 private final int MAX = 52;
 private List<Integer> cardNumber = new ArrayList<Integer>(); //暫存牌ID
 int index = 0;
 
 public PokerSend(){
  init();
 }
 
 private void init(){
  cards = new PokerCard[MAX];
  for(int i = 0 ; i < MAX ; i++){
   cardNumber.add(i);
  }
 }
 
 //進行洗牌
 public void doShuffle(PokerPlayer player[]){
  Random rmd = new Random();
  int r_num = 0;
  index = 0;
  //產生52張的不重複的亂數牌至cards array
  while(cardNumber.size() != 0){
   r_num = rmd.nextInt(cardNumber.size());
   cards[cardNumber.size()-1] = getCard(cardNumber.get(r_num));
   cardNumber.remove(r_num);
  }
  
  int cardnum = MAX / player.length;
  int left = MAX % player.length;
  
  //進行發牌
  System.out.println("每人平均"+cardnum+",多出"+left+"張給前幾位!");
  for(int i = 0 ; i < player.length ; i++){
   String playerName = "玩家"+(i+1);
   if(left != 0){
    cardnum = (MAX / player.length) + 1;
    left--;
   }else{
    cardnum = (MAX / player.length);
   }
   player[i] = new PokerPlayer(playerName, cardnum);
   setPlayerCard(player[i], cardnum);
  }
 }
 
 public void setPlayerCard(PokerPlayer player, int cardNum){
  for(int i = 0 ; i < cardNum ; i++){
   player.setMyCards(i, cards[index++]);
  }
 }
 
 private PokerCard getCard(int id){
  return new PokerCard(id % 13,id / 13);
 }
}

PokerSend主要定義了存放撲克牌PokerCard變數及暫存撲克牌的ID的ArrayList

在初始化的當下會先產生暫存撲克牌的ID的 0 ~ 51的數值 (亂數產生撲克牌時使用)

再來是doShuffle內亂數產生撲克牌的部分
while(cardNumber.size() != 0){
   r_num = rmd.nextInt(cardNumber.size());
   cards[cardNumber.size()-1] = getCard(cardNumber.get(r_num));
   cardNumber.remove(r_num);
  }

首先,一開始cardNumber的size為52,此時產生一亂數值介於0 ~ 51

當下若取得35,則由cardNumber.get(35) => 35, getCard得到一撲克牌應為方塊10

0 ~ 12 => 黑桃 A ~ K

13 ~ 25 => 紅桃 A ~ K

26 ~ 38 => 方塊 A ~ K

39 ~ 51 => 梅花 A ~ K

並放置於cards[34]內,此時remove掉數值35,因此cardNumber的size變為51

此時若還是產生35的數值透過cardNumber.get已經不是35了,而是其他數值

以此類推,直到cardNumber size = 0為止

之後,就是將產生的亂數撲克牌array cards平均分配給玩家

4. ProcessGame
public class ProcessGame {

 /**
  * @param args
  */
 public static void main(String[] args) {
  // TODO Auto-generated method stub
  Scanner input = new Scanner(System.in);
  try{
   System.out.print("參與的玩家人數:");
   int playerNum = checkInput(input.nextInt());
   PokerSend sender = new PokerSend();
   PokerPlayer players[] = new PokerPlayer[playerNum];
   sender.doShuffle(players); //進行洗牌,並發牌
   
   System.out.println("每位玩家手中的牌, 如下:");
   for(int i = 0 ; i < players.length ; i++){
    System.out.print(players[i]+"==>");
    players[i].showMyCards();
   }
  }catch(Exception e){
   if(e.getMessage() != null){
    System.out.println(e.getMessage());
   }else{
    System.out.println("遊戲即將中止....");
   }
  }finally{
   input.close();
  }
 }
 
 public static int checkInput(int num)throws RuntimeException{
  if(num > 0 && num <= 52){
   return num;
  }else if(num == 0){
   throw new RuntimeException("玩家人數輸入有誤!");
  }else if(num > 52){
   throw new RuntimeException("玩家人數超過上限!");
  }
  return num;
 }
}

主程式一開始決定玩家的人數,接著進行發牌類別的初始化動作

並依據玩家的人數進行洗牌的動作,最後則是呈列各玩家手上的撲克牌

DEMO如下


留言

  1. 可以請問您如果想要運用arraylist來呈現的方法為何嗎?如果我是希望把沒發出去的牌也印出該如何做呢?
    初次學程式,邏輯不好,但想精進能力,謝謝您!

    回覆刪除
    回覆
    1. 我看了一下code我這邊的arraylist是用在暫存牌ID, 呈現牌的變數為一array

      關於您說"想要運用arraylist來呈現的方法" 不就是將我的cards array 換成arraylist?
      然後就用這dynamic array to add就好了.

      至於"希望把沒發出去的牌也印出", 是否指的是發剩的牌?
      若是的話, 表示left != 0. 簡單的方式假設您剩3張牌發不完, 那可以將cards的最後三筆index直接印出不就可以了?

      刪除
    2. 了解了!謝謝您

      刪除

張貼留言