創造プログラミング演習報告書
( Java™言語 )
1 プログラムの内容
カジノなどで有名な「ブラックジャック」のトランプゲームを作った。チップを賭けたりする機
能はなく、本当にゲームの基本的な部分しかないが、そこそこ遊べるものになったと思う。
「ブラ
ックジャック」のゲームの性質上、自分は自分の意志で自由にカードを処理できるが、相手は決め
られたルールに従ってカードを処理するので、相手の思考プログラムというのが必要ない分、プロ
グラムは簡単なものになった。今回作ったプログラムに採用した「ブラックジャック」の基本ルー
ルを以下に示す。なおこのルールは、現在通用している一般的なルールと若干異なる。
2 インターフェース設計
ここでは図を用いて説明する。このアプレットは図 1 のような画面の上で操作が行われる。上下
のカードのうち、上側(裏面が赤)の方は親を示し、下側(裏面が青)の方はプレイヤーを示す。ユー
ザが操作する部分は、hit と stand のボタン 2 つのみであり、それぞれ状況に応じて押せなくなっ
たり、名前が変わったりする(図 2、図 3)。このボタンの下には、現在のカードの値の合計が表示さ
れている。
また、勝敗が決定されると、ダイアログボックスが表示される(図 4)。ダイアログボックスに表
示するテキストは、勝敗とその状況に応じて、7 種類用意されている。
「ブラックジャック」は、親(ディーラー)との間で 1 対 1 の勝負を行う。プレイヤーの目標は、手持ちのカードの 値の合計を 21 に近づけ、その数字が親より 21 に近づくことである。カードの値は、2~10 ではその数字通りの値で あり、K,Q,J は 10 と数える。A は 1 か 11 のどちらかを自由に選ぶことができる。 親はカードを全員に 2 枚ずつ配る。親の 2 枚のカードのうちの 1 枚は見ることができ、もう 1 枚のカードは伏せら れている。この時点でプレイヤーの手札が J と A の組み合わせならば、自動的にプレイヤーの勝ちとなる(BLACK JACK という)。 次にプレイヤーは hit(カードをもう 1 枚引く)または stand(カードを引かずにその時点の値で勝負する)の選択を行 う。プレイヤーは 21 を超えなければ 5 枚表になるまで hit することができる。21 を超えてしまうことを bust と呼び、 直ちにプレイヤーの負けとなる。プレイヤーが stand すると親は伏せてあったカードを開く(この時点で親が BLACK JACK なら親の勝ち)。親は値が 17 以上になるまで hit しなければならず、17 以上になったらその後は hit すること はできない。ただし 5 枚表になった時点で hit はできなくなる。親が hit できなくなった時点で、親とプレイヤーの値 を比べ勝敗を決める。また、親が 21 を超えた場合にはプレイヤーの勝ちとなり、プレイヤーと親が同じ値の場合には 引き分けとなる。図 1 アプレットの主な画面 図 2 hit と stand のボタンの変化(1)
3 プログラムの処理概要(フローチャート)
No
Yes
Yes
No
No Yes(hit)
Yes(stand)
Yes
No
No
Yes
Yes
No
No
start
各変数の初期化 相手側に 1 枚,自分側に 2 枚のカード を表で表示し、他を裏で表示する。 ボタンが押された 自分側のカードをさら に 1 枚表にする 5 枚表になった 相手のカードを 1 枚 表にする 21 を超えた BLACK JACK になった 21 を超えた 16 を超えた 勝敗を判定する NewGame が押された 初期化が必要な 変数の初期化4 プログラムリスト
//--- // プログラミング III 創造プログラミング演習
// ~ Black Jack (Java version) ~ // 3I 4 番 大上 雅史 // // ++++++更新履歴++++++++++++++++++++ // 2005.02.02 ほぼ完成 // 02.04 エースの処理を改良 // 02.05 カードの gif 画像を変更 //--- import java.awt.*; import java.awt.event.*; import javax.swing.*;
public class bj extends JApplet implements ActionListener{ int[] ply=new int[5]; // 自分のカード int[] com=new int[5]; // 相手のカード
int ply_n=0; // 自分が開いたカード枚数 int com_n=0; // 相手が開いたカード枚数 int p_sum=0; // 自分の合計 int c_sum=0; // 相手の合計 int p_ace=0; // 自分の手札の「A」のうち、11 として数えている枚数 int c_ace=0; // 相手の手札の「A」のうち、11 として数えている枚数 int x; // 乱数取得の際の変数
int flg=0; // BLACK JACK になったときの使う変数 String fn; // ファイル名
JButton bt[]=new JButton[2]; // hit か stand のボタン JPanel pn[]=new JPanel[2]; // パネル
Icon icb1; // カード裏(相手) Icon icb2; // カード裏(自分)
Icon icc[]=new Icon[13]; // カード表(A,1,2,・・・,K) JLabel lb[]=new JLabel[10]; // 画像用ラベル
JLabel ptxt,ctxt,msglb; // テキスト用ラベル Container c; // コンテナ public void init(){
// コンポーネントの設定
c=getContentPane(); // コンテナの取得 bt[0]=new JButton("hit"); // hit ボタン bt[1]=new JButton("stand"); // stand ボタン pn[0]=new JPanel(); pn[1]=new JPanel(); ptxt=new JLabel(); ctxt=new JLabel(); msglb=new JLabel(); icb1=new ImageIcon(getImage(getDocumentBase(),"back1.gif")); // カード裏(相手) icb2=new ImageIcon(getImage(getDocumentBase(),"back2.gif")); // カード裏(自分) c.setLayout(new BorderLayout()); // ボーダーレイアウト pn[0].setLayout(new GridLayout(2,5)); // グリッドレイアウト 2 行 5 列 pn[1].setLayout(new GridLayout(2,2)); // グリッドレイアウト 2 行 2 列 for(int i=0;i<5;i++){ // 相手の表示範囲を初期化 lb[i]=new JLabel(); lb[i].setIcon(icb1); // 相手のカード裏の画像をセット lb[i].setHorizontalTextPosition(JLabel.CENTER); lb[i].setVerticalTextPosition(JLabel.CENTER); ply[i]=0; //ついでに初期化 com[i]=0; //ついでに初期化 } for(int i=5;i<10;i++){ // 自分の表示範囲を初期化
lb[i]=new JLabel(); lb[i].setIcon(icb2); // 自分のカード裏の画像をセット lb[i].setHorizontalTextPosition(JLabel.CENTER); lb[i].setVerticalTextPosition(JLabel.CENTER); } for(int i=0;i<13;i++){ // カード表の画像の準備 fn="card"+(i)+".gif"; icc[i]=new ImageIcon(getImage(getDocumentBase(),fn)); //0~12 が A~K に対応 } // カードを引く処理【☆】 x=((int)(Math.random()*52981))%13; // 0~12 までの乱数発生(52981 は適当に選んだ大きめの数) lb[0].setIcon(icc[x]); // 選ばれた数字の画像をセット com[0]=x; // 1 枚目に数字をセット if(x==0){ //「A」を引いたとき c_sum+=11; // 11 とする。 c_ace++; // 11 としている「A」が 1 枚増えた。 }else if(x>=10){ //「J,Q,K」を引いたとき c_sum+=10; // 10 とする。 }else{ // それ以外を引いたとき c_sum+=(x+1); // そのまま }
if(c_ace>=1 && c_sum>21){ // 11 としている「A」が 1 枚以上あって 21 を超えていたら c_sum-=10; // その「A」を 1 と数える c_ace--; // 11 としている「A」が 1 枚減った。 } com_n++; // 引いたカードが増えた // カードを引く処理(【☆】と同様なのでコメント省略) x=((int)(Math.random()*52981))%13; lb[5].setIcon(icc[x]); ply[0]=x; if(x==0){ p_sum+=11; p_ace++; }else if(x>=10){ p_sum+=10; }else{ p_sum+=(x+1); }
if(p_ace>=1 && p_sum>21){ p_sum-=10; p_ace--; } ply_n++; // カードを引く処理(【☆】と同様なのでコメント省略) x=((int)(Math.random()*52981))%13; lb[6].setIcon(icc[x]); ply[1]=x; if(x==0){ p_sum+=11; p_ace++; }else if(x>=10){ p_sum+=10; }else{ p_sum+=(x+1); }
if(p_ace>=1 && p_sum>21){ p_sum-=10; p_ace--; }
for(int i=0;i<lb.length;i++){ pn[0].add(lb[i]); // パネルにラベル追加 } pn[1].add(bt[0]); // パネルにボタン追加 pn[1].add(bt[1]); // パネルにボタン追加 ptxt.setText("Player : "+(p_sum)); // 自分の合計表示 ctxt.setText("Computer : "+(c_sum)); // 相手の合計表示 msglb.setText("~Black Jack~\n"); // タイトル表示 msglb.setFont(new Font("Dialog",Font.BOLD,20)); // フォント設定 pn[1].add(ptxt); // パネルにラベル追加 pn[1].add(ctxt); // パネルにラベル追加 // コンテナに追加 c.add(msglb,"North"); c.add(pn[0],"Center"); c.add(pn[1],"South"); bt[0].addActionListener(this); // リスナの登録 bt[1].addActionListener(this); // リスナの登録 // 自分が BLACK JACK になったときの処理
if((ply[0]==0 && ply[1]==10) || (ply[1]==0 && ply[0]==10)){ Dlg(6);
}else if(p_sum==21){ // ちょうど 21 なら bt[0].setEnabled(false); // hit を押せなくする bt[1].setText("Computer's cards open"); // stand ボタンのラベル変更 }
}
public void actionPerformed(ActionEvent e){
String bname=e.getActionCommand(); // 押されたボタンのテキスト取得 if(bname=="hit"){ // hit で if(ply_n<5){ // 5 枚未満なら // カードを引く処理(【☆】と同様なのでコメント省略) x=(int)(Math.random()*52981)%13; lb[ply_n+5].setIcon(icc[x]); ply[ply_n]=x; if(x==0){ p_sum+=11; p_ace++; }else if(x>=10){ p_sum+=10; }else{ p_sum+=(x+1); }
if(p_ace>=1 && p_sum>21){ p_sum-=10; p_ace--; } ply_n++; ptxt.setText("Player : "+(p_sum)); // 合計表示 if(ply_n==5){ // 5 枚に達したら bt[0].setEnabled(false); // hit を押せなくする
bt[1].setText("Computer's cards open"); // stand ボタンのラベル変更 }
if(p_sum>21){ // 合計 22 以上なら Dlg(1); // ダイアログ表示 }else if(p_sum==21){ // ちょうど 21 なら
bt[0].setEnabled(false); // hit を押せなくする
bt[1].setText("Computer's cards open"); //stand ボタンのラベル変更 VictoryOrDefeat(); // 勝敗判定
} }
}
if(bname=="stand"){ // stand なら bt[0].setEnabled(false); // hit を押せなくする bt[1].setText("Computer's cards open"); // stand ボタンのラベル変更 VictoryOrDefeat(); // 勝敗判定
}
if(bname=="Computer's cards open"){
if(com_n<5){ // 相手が 5 枚未満 // カードを引く処理(【☆】と同様なのでコメント省略) x=(int)(Math.random()*52981)%13; lb[com_n].setIcon(icc[x]); com[com_n]=x; com_n++; if(com_n==2){ // 相手の BLACK JACK の処理
if((com[0]==0 && com[1]==10) || (com[1]==0 && com[0]==10)){ Dlg(7); // ダイアログ表示 flg=1; // 相手が BLACK JACK になったことをあらわす } } if(x==0){ c_sum+=11; c_ace++; }else if(x>=10){ c_sum+=10; }else{ c_sum+=(x+1); }
if(c_ace>=1 && c_sum>21){ c_sum-=10; c_ace--; } ctxt.setText("Computer : "+(c_sum)); // 合計表示 if(c_sum>21){ // 相手が 22 以上なら Dlg(2); // ダイアログ表示
}else if(flg==0){ // BLACK JACK 以外でちょうど 21 なら VictoryOrDefeat(); // 勝敗判定 } } } if(bname=="NewGame"){ // NewGame なら NewGame(); // 初期化処理 } } //----勝敗判定メソッド--- void VictoryOrDefeat(){ if(c_sum>16 || com_n==5){ // 相手が 17 以上か 5 枚なら勝敗を決める if(c_sum>p_sum){ Dlg(3); }else if(c_sum<p_sum){ Dlg(4); }else if(c_sum==p_sum){ Dlg(5); } } } //----ダイアログ表示メソッド--- void Dlg(int cp){
int type=JOptionPane.WARNING_MESSAGE; c=getContentPane();
if(cp==1){
JOptionPane.showMessageDialog(c,"BUST!! Player lost.","Game Information",type); }else if(cp==2){
JOptionPane.showMessageDialog(c,"Computer BUST. Player won!!","Game Information",type); }else if(cp==3){
JOptionPane.showMessageDialog(c,"Player lost.","Game Information",type); }else if(cp==4){
JOptionPane.showMessageDialog(c,"Player won!!","Game Information",type); }else if(cp==5){
JOptionPane.showMessageDialog(c,"Draw Game.","Game Information",type); }else if(cp==6){
JOptionPane.showMessageDialog(c,"BLACK JACK!! Player won!!","Game Information",type); }else if(cp==7){
JOptionPane.showMessageDialog(c,"Computer is BLACK JACK.","Game Information",type); }
bt[0].setEnabled(false); // hit を押せなくする bt[1].setEnabled(true); // stand を押せるようにする bt[1].setText("NewGame"); // stand を NewGame に変更 } //----再初期化メソッド--- void NewGame(){ // 画像をカード裏にする for(int i=0;i<5;i++){ lb[i].setIcon(icb1); lb[i].setHorizontalTextPosition(JLabel.CENTER); lb[i].setVerticalTextPosition(JLabel.CENTER); ply[i]=0; // ついでに初期化 com[i]=0; // ついでに初期化 } for(int i=5;i<10;i++){ lb[i].setIcon(icb2); lb[i].setHorizontalTextPosition(JLabel.CENTER); lb[i].setVerticalTextPosition(JLabel.CENTER); } // 各変数の初期化 ply_n=0; com_n=0; p_sum=0; c_sum=0; p_ace=0; c_ace=0; flg=0; // カードを引く処理(【☆】と同様なのでコメント省略) x=((int)(Math.random()*52981))%13; lb[0].setIcon(icc[x]); com[0]=x; if(x==0){ c_sum+=11; c_ace++; }else if(x>=10){ c_sum+=10; }else{ c_sum+=(x+1); }
if(c_ace>=1 && c_sum>21){ c_sum-=10; c_ace--; }
// カードを引く処理(【☆】と同様なのでコメント省略) x=((int)(Math.random()*52981))%13; lb[5].setIcon(icc[x]); ply[0]=x; if(x==0){ p_sum+=11; p_ace++; }else if(x>=10){ p_sum+=10; }else{ p_sum+=(x+1); }
if(p_ace>=1 && p_sum>21){ p_sum-=10; p_ace--; } ply_n++; // カードを引く処理(【☆】と同様なのでコメント省略) x=((int)(Math.random()*52981))%13; lb[6].setIcon(icc[x]); ply[1]=x; if(x==0){ p_sum+=11; p_ace++; }else if(x>=10){ p_sum+=10; }else{ p_sum+=(x+1); }
if(p_ace>=1 && p_sum>21){ p_sum-=10; p_ace--; } ply_n++; // ボタンの初期化 bt[0].setEnabled(true); // ボタンを押せるようにする bt[0].setText("hit"); // ボタン名を hit にする bt[1].setEnabled(true); // ボタンを押せるようにする bt[1].setText("stand"); // ボタン名を stand にする // ラベルにテキストを表示 ptxt.setText("Player : "+(p_sum)); ctxt.setText("Computer : "+(c_sum)); // 自分の BLACK JACK の処理
if((ply[0]==0 && ply[1]==10) || (ply[1]==0 && ply[0]==10)){ Dlg(6);
}else if(p_sum==21){ // ちょうど 21 なら bt[0].setEnabled(false); // hit を押せなくする
bt[1].setText("Computer's cards open"); // stand ボタンのラベル変更 }
} }
5 実行手順(使い方)
※ゲームのルールは、1 で示したとおりである。
①アプレットを実行すると、図 5 の画面が表示され
る。この画面を初期状態とする。この状態で、hit
もしくは stand のボタンを押すと、処理が進む。た
だし、初期状態で自分が 21 ならば、hit は押せない
ようになっており、
stand は Computer’s cards open
というボタンになっている。また、初期状態で
BLACK JACK を成立させていたら、自分が勝った
ときのメッセージを表示して、NewGame(stand が
変化したボタン)のみ押せる状態になる。(図 6)
②hit を押すと、自分のカードが 1 枚表になる。こ
の時点で自分が 21 を超えてしまったら、自分が負
けたときのメッセージを表示して図 6 のような状態
になる。21 を超えなかったら、自分が 5 枚になる
まで、さらに hit を押すこともできる。
③stand を押すと、stand が Computer’s cards open
に変わり、hit は押せなくなる。Computer’s cards
open を押すと相手のカードが 1 枚表になる(図 7)。
この時点で、相手が BLACK JACK を成立させてい
たら、自分が負けたときのメッセージを表示して図
6 のような状態になる。相手が 16 を超えるか、5 枚
になるまで、Computer’s cards open を押す度に相
手のカードが 1 枚表になる。
④相手が 16 を超えるか、5 枚になったら、その時
点での両者の合計を比べて、21 に近い方を勝ちとし、
メッセージ(例:図 8)を表示して図 6 のような状態
になる。
⑤NewGame が押されると、図 5 の初期状態に戻る。
図 5 初期状態 図 6 NewGame ボタンのみが押せる状態図 7 Computer’s cards open を押したとき