ブラックジャックプログラムの実装
前回BlackJackの設計が終わったので、今回はその設計を元にプログラムを実装していきます。
実装の流れ
作成した型図(クラス図)で必要なクラスが、Player、Dealer、Hand、Deck、Cardでした。 今回は処理の流れを制御するBlackJackというメインクラスを用意しました。
それぞれクラスの雛形を作成して、作成した型図(クラス図)を参考にして、 関係ありそうなクラスのインスタンスを作成しました。
次にシーケンス図を参考にして、それぞれのクラスで必要になるメソッドを実装していきました。 しかし、実装を進めていくとクラス間の関係や、不足している処理など UML図の通りでは実装できない部分が多く発生してしまいました。
そのため、なるべくUML図で作成した図に合わせて作成するように進めましたが、 不足している部分は随時継ぎ足しながら作成しました。
完成
完成したプログラムが以下のとおりです。 プログラム全文記載しましたが、長いので開閉式にしています。 (Java環境があって自分の環境で試してみたいという方や、前回作成したクラス図との比較、単に興味があるという方は見ていってください。)
public class BlackJack {
public static void main(String[] args) {
// TODO 自動生成されたメソッド・スタブ
Player player;
Dealer dealer;
dealer = new Dealer();
player = new Player();
dealer.startNewGame();
player.init();
dealer.init();
String askAns = "";
askAns = player.askHit();
if(askAns.equals("BJ")) {
System.out.println("★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★");
System.out.println("★☆★☆★☆★☆ブラックジャック!!!★☆★☆★☆★☆");
System.out.println("☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆★☆");
System.out.println("あなたの勝ちです!");
System.out.println("ブラックジャック終了!また遊んでね!");
return;
}else if(askAns.equals("Bust")) {
System.out.println("バースト!あなたの負けです。。。");
return;
}else {System.out.println("Playerのカードの引きを終了します");}
dealer.checkHit();
System.out.println("ディーラーの用意が整いました。");
System.out.println("勝負を開始します!");
int pScore = player.pHand.scoreOfCards;
int dScore = dealer.dHand.scoreOfCards;
System.out.println("あなたの得点は"+pScore+"です");
System.out.println("ディーラーの得点は"+dScore+"です");
if(pScore > dScore || dScore > 21) {
System.out.println("あなたの勝ちです!Conguratulations!!");
}else {
System.out.println("あなたの負けです。");
}
System.out.println("ブラックジャック終了!また遊んでね!");
}
}
Dealer.java
public class Dealer {
Deck deck;
Hand dHand;
Dealer(){
deck = new Deck();
}
public void startNewGame() {
// TODO 自動生成されたメソッド・スタブ
System.out.println("★☆★☆★☆★☆★☆ BLACK JACKへようこそ ★☆★☆★☆★☆★☆");
deck.shuffleDeck();
}
public void init() {
// TODO 自動生成されたメソッド・スタブ
dHand = new Hand();
for(int i=0; i<2; i++) {
dHand.addCard();
if(i==0) {
dHand.showCard("Dealer");
}else {System.out.println("Dealerの二枚目のカードは分かりません");}
}
System.out.println();
}
public void checkHit() {
// TODO 自動生成されたメソッド・スタブ
while(dHand.scoreOfCards < 17){
dHand.addCard();
System.out.println("ディーラーはカードを引きました。");
}
}
public void judge() {
// TODO 自動生成されたメソッド・スタブ
}
}
Player.java
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
public class Player {
Hand pHand;
public void init() {
// TODO 自動生成されたメソッド・スタブ
pHand = new Hand();
for(int i=0; i<2; i++) {
pHand.addCard();
pHand.showCard("Player");
}
pHand.showCardSum();
System.out.println();
}
public String askHit() {
// TODO 自動生成されたメソッド・スタブ
System.out.println("カードを引きますか?引く場合はYを、引かない場合はNを入力してください。");
BufferedReader reader = new BufferedReader (new InputStreamReader (System.in));
try {
while(true){
String line = reader.readLine();
if(line.equals("Y") || line.equals("y")){
pHand.addCard();
pHand.showCard("Player");
pHand.showCardSum();
if(pHand.getScore() == 21){
return "BJ";
}
if(pHand.getScore() > 21){
return "Bust";
}
System.out.println("もう一度引きますか?引く場合はYを、引かない場合はNを入力して下さい。");
}else if(line.equals("N") || line.equals("n")){
break;
}else{
System.out.println("入力エラーです。もう一度入力してください。");
}
}
} catch (IOException e) {
System.out.println (e);
}
return "";
}
}
Deck.java
import java.util.ArrayList;
import java.util.Collections;
public class Deck {
Card card;
static ArrayList<Card> deckCards = new ArrayList<Card>();
final int totalCards = 52;
Deck(){
for(int i=0; i<totalCards; i++) {
card = new Card(i);
card.cardNum = i;
deckCards.add(card);
}
}
void shuffleDeck() {
// TODO 自動生成されたメソッド・スタブ
Collections.shuffle(deckCards);
}
public static Card getCards() {
Card card = deckCards.get(0);
deckCards.remove(0);
return card;
}
}
Hand.java
import java.util.ArrayList;
public class Hand {
int handOfCards = 0;
int scoreOfCards = 0;
Card card;
ArrayList<Card> ownCards = new ArrayList<Card>();
public void addCard() {
// TODO 自動生成されたメソッド・スタブ
ownCards.add(Deck.getCards());
handOfCards++;
int cardSum = 0;
for(int i=0; i<ownCards.size(); i++) {
cardSum += ownCards.get(i).digit;
}
scoreOfCards = cardSum;
}
void showCard(String name) {
// TODO 自動生成されたメソッド・スタブ
card = ownCards.get(handOfCards-1);
System.out.println(name+"の引いたカードは"+card.mark+"の"+card.num+"です");
}
void showCardSum() {
System.out.println("あなたの現在の得点は"+scoreOfCards+"です");
}
public int getScore() {
// TODO 自動生成されたメソッド・スタブ
return scoreOfCards;
}
}
Card.java
public class Card {
static String[] marks = new String[] {"ハート","スペード","クラブ","ダイヤ"};
static String[] nums = new String[] {"A","2","3","4","5","6","7","8","9","10","J","Q","K"};
int cardNum;
String mark;
String num;
int digit;
Card(int i){
cardNum = i;
mark = marks[cardNum/13];
num = nums[cardNum%13];
digit = changeCardNum(num);
}
//受け取った値からカードの数字を判定する
public static int changeCardNum(String cardNum){
switch (cardNum){
case "A":
cardNum = "1";
break;
case "J":
cardNum = "10";
break;
case "Q":
cardNum = "10";
break;
case "K":
cardNum = "10";
break;
default:
break;
}
return Integer.parseInt(cardNum);
}
}
クラス図
完成したプログラムですが、前回作成したUMLとは異なった構成になってしまいました。 そこでEclipseのプラグイン「AmaterasUML」というのを使って、どのような構成になったかを出力してみました。
http://amateras.osdn.jp/cgi-bin/fswiki/wiki.cgi?page=AmaterasUML
おおよそ作成したUMLの型図(クラス図)と似ているのですが、DealerとDeckで関連が引かれていたり、やはり設計通りにはできていませんでした。
ほかのアクティビティ図やシーケンス図なども作成できるのですが、自動生成のやり方が分かりませんでした。
まとめ
1から設計をしてプログラムを書くという経験がほぼなかったので、実際やってみると設計と実装で大きく乖離してしまいました。 やはり、設計書をもれなく作成するというのは難しかったです。
また、プログラムを書いている際にこう書いた方がスマートなやり方と思っても、設計書に沿うように書こうとするとそれが制限になって書きづらいと感じるところもありました。
なかなか正確な設計を作ることは困難を極めると思いますが、試行錯誤して少しでもミスがない設計ができるようにしていきたいです。
今度機会があれば、ブラウザで動くようなブラックジャックの作成にも挑戦したいです。