第54節 - Java: Blackjack Card Game
這一節我會介紹Java的Blackjack(21點)程式設計,Java的Blackjack程式是一個文字版(text-based)紙牌遊戲,下一節會介紹LibGDX的Blackjack程式設計,LibGDX Blackjack程式是一個2D動畫版紙牌遊戲。
我在上幾節介紹過以下內容,這些內容會在本節用到:
- 第51節 - Java: toString() Method
- 第52節 - Java: Blackjack Shuffle Methods
執行結果:
以下是本節Java: Blackjack Card Game的執行結果:
Java Blackjack Class Diagram
以下是Java Blackjack的Class Diagram:

- 首先建立一個Card Class,再建立一個Deck Class,用ArrayList類別把Card類別傳入Deck,建立cards物件;
- 最後建立三個Deck類別物件(mainDeck, playerDeck和dealerDeck)。
1) Card Class
以下會用圖片說明如何建立撲克牌的Card Class(類別):

- 要建立一個全新的Class(類別),首先要訂立所有有關的Property(屬性)和Method(方法),Method則Action(動作)。 以撲克牌來說,它只有Property,而沒有Method,則沒有Action;
- 要訂立撲克牌的所有有關的Property(屬性),其實很簡單,大家只要問自己以下問題就可以:
在這撲克牌上,我可以看見甚麼資料?
1) 我看見"A";
2) 我看見"Spade";
3) 我看見"黑色";
4) 我看見四角是"圓"的。
- 但是以上四個屬性裡,只有兩個屬性是有用的,
1) 我看見"A";
2) 我看見"Spade";
- 因為Spade一定是黑色,沒有紅色,另外, 四角是圓的是一個沒有用的資料;
- 所以撲克牌只有兩個屬性,則Degree of Freedom (DOF) = 2。

- 我們的眼睛會自動把撲克牌的資料傳入大腦;
- 但是電腦程式就要我們主動把"A"和"Spade"傳入電腦,Java程式可以用getValue()和getSuit()方法主動把"A"和"Spade"傳入電腦;
- 最後用toString()方法顯示在屏幕上。

- 以上是Card Class的Property(屬性)和Method(方法)。
注意: 以上的getSuit()方法是不須要用到的,因為Blackjack紙牌遊戲是不須要考慮撲克牌的圖案,我把getSuit()方法放在Card類別內,是方便將來用在其他紙牌遊戲上,例如:鋤大D,十三張等等。
2) Deck Class
以下會用圖片說明如何建立撲克牌的Deck Class(類別):

- 建立一個Deck類別物件(mainDeck);
- 建立一個addAllCards()方法,把52張撲克牌加入mainDeck物件內;
- 建立一個shuffle()方法,把52張撲克牌打亂;
- 建立一個Deck類別物件(playerDeck);
- 建立一個Deck類別物件(dealerDeck);
- 所有遊戲邏輯(Game Logic)部分,都會在MyDemo54.java的main()內進行,例如輸入玩家想投注的金額。
- 建立一個playerDeck的draw()方法,在mainDeck內抽出兩張撲克牌給playerDeck,
在mainDeck物件內抽出(getCard())第一張撲克牌,然後刪除(removeCard())有關撲克牌,
把剛抽出的撲克牌加入(addCard())playerDeck物件內; - 建立一個dealerDeck的draw()方法,在mainDeck內抽出兩張撲克牌給dealerDeck,
在mainDeck物件內抽出(getCard())第一張撲克牌,然後刪除(removeCard())有關撲克牌,
把剛抽出的撲克牌加入(addCard())playerDeck物件內; - 建立一個cardsTotalValue()方法,計算手上撲克牌的總和,最後用toString()方法顯示在屏幕上;
- 完成一局後,建立一個returnAllCards()方法,運用步驟7的getCard()和平removeCard()方法把playerDeck和dealerDeck的撲克牌抽出和刪除,最後運用addCard()方法把剛抽出的撲克牌放回mainDeck物件內。

- 以上是Deck Class的Property(屬性)和Method(方法)。
程式部分
1. 建立Java Class

- 首先建立Card、 Deck和MyDemo54 Java class。
2. 建立Card Class

- 建立Card類別的兩個主要屬性(suit, value)。
- 建立getSuit()方法,它用來提取suit的數值。
- 建立getValue()方法,它用來提取value的數值。
- 建立toString()方法,它用來把Card提取出來的suit和value數值顯示在屏幕上。
3. 建立Deck Class

- 建立一個Card類別的ArrayList物件(cards);
- 把Spade、Heart、Club和Diamond的Unicode傳入spade、heart、club和diamond的字串內;
- 建立cardSuit[]和cardValue[] Array,把撲克牌的圖案和數值傳入;
- 建立一個addAllCards()方法,把52張撲克牌加入mainDeck物件內;
- 建立一個shuffle()方法,把52張撲克牌打亂;
- 建立一個getCard()方法,用來提取撲克牌的屬性;
- 建立一個removeCard()方法,用來刪除Deck物件內的撲克牌;
- 建立一個addCard()方法,用來把撲克牌加入Deck物件內;
- 建立一個draw()方法,用來在Deck物件內抽出(getCard())一張撲克牌,然後刪除(removeCard())有關撲克牌,再把剛抽出的撲克牌加入(addCard())Deck物件內;
- 建立一個cardsTotalValue()方法,計算手上撲克牌的總和,最後用toString()方法顯示在屏幕上;
注意,ACE的數值可以是"1"或"11"。 - 建立一個returnAllCards()方法,運用getCard()和平removeCard()方法把Deck物件內的撲克牌抽出和刪除,最後運用addCard()方法把剛抽出的撲克牌放回Deck物件內。
- toString()方法把撲克牌顯示在屏幕上。
注意,我們用toString2()方法把dealerDeck的兩張撲克牌顯示在屏幕上是因為Dealer的第二張牌必須是隱藏。
4. 建立MyDemo54 Main Class

- 建立一個Deck類別物件(mainDeck),再建立一個addAllCards()方法,把52張撲克牌加入mainDeck物件內;
- 用mainDeck.toString()方法把52張順序的撲克牌顯示在屏幕上;
- 建立一個shuffle()方法,把52張撲克牌打亂;
- 再用mainDeck.toString()方法把52張打亂的撲克牌顯示在屏幕上;
- 建立一個Deck類別物件(playerDeck)和(dealerDeck);
- 建立一個nextAction()方法,把所有遊戲邏輯(Game Logic)部分放在這裡;
- 所有遊戲邏輯(Game Logic)部分,都會在MyDemo54.java的main()內進行,例如輸入玩家想投注的金額
;
遊戲邏輯1 - 輸入玩家想投注的金額和其他用來比較的程式碼; - 遊戲邏輯2 - 建立一個dealerDeck的draw()方法,在mainDeck內抽出兩張撲克牌給dealerDeck
;
注意,我們用toString2()方法把dealerDeck的兩張撲克牌顯示在屏幕上是因為Dealer的第二張牌必須是隱藏。 - 遊戲邏輯3 - 建立一個playerDeck的draw()方法,在mainDeck內抽出兩張撲克牌給playerDeck ;
- 遊戲邏輯4 - 輸入玩家下一步,例如:Hit(要牌)、 Stand(不要牌)或Bust(爆煲);
- 遊戲邏輯5 - 電腦自動執行Dealer的決定,如果Dealer的撲克牌總和小過17,就Hit(要牌),如果Dealer的撲克牌總和等於或大過17,就Stand(不要牌);
- 遊戲邏輯6 - 比較playerDeck和dealerDeck手上撲克牌的總和,決定誰勝誰負,最後計算勝負後的金額;
- 遊戲邏輯7 - 顯示勝負後的金額;
- 遊戲邏輯8 - 問玩家是否繼續;
- 遊戲邏輯9 - 完成一局後,建立一個returnAllCards()方法,運用getCard()和平removeCard()方法把playerDeck和dealerDeck的撲克牌抽出和刪除,最後運用addCard()方法把剛抽出的撲克牌放回mainDeck物件內;
- 如果金額小過"零",遊戲完結(Game Over)。
5. 執行程式結果

- 以上是程式執行的結果,首先列出順序的52張撲克牌,再列出打亂後的52張撲克牌,然後電腦就會問玩家下注。