Index |
HOME > プログラムTOP > JavaScript |
||
JSブラックジャック
コンピュータと対戦するブラックジャックゲームのソースです。ディーラー方式の一般的なルールとは若干異なるところがあります。「ルールを見る」ボタンで確認してみてください。ゲームの難易度は結構難しいと思います。参考にされる方はもう少しやさしくした方がいいかもしれません。DOMやらクラスやら使っていますが、もう少しすっきりできそうな気がします。対象ブラウザはIE5以上、NN6以上、Firefox、Safariです。
sasaraan programming ![]() Exposition ■宣言部
クラスをふたつつくります。JavaScriptではすべてがオブジェクトなので、関数もクラスも区別はないということなのでしょう。functionキーワードで作成できるようです。プロパティはthisキーワードで登録できるということ。さらに、宣言がそのままコンストラクタとして作用するらしいです。メソッドも登録できますが今回は使用していません。
@ Cardクラス ・・・ トランプの札52枚 + 裏面 + 配置場所表示用 = 54枚 A Playerクラス ・・・ プレーヤーのステータス記憶用(CPU用とユーザー用)
/*** 宣言部 ***/
// トランプ(Cardオブジェクト)の配列
var nCard = 54;
var aCard = new Array(nCard);
// プレーヤー(Playerオブジェクト)の配列 :: [0]CPU, [1]ユーザー
var aPlayer = new Array(2);
// Card クラス
function Card() {
this.image = null; // Imageオブジェクト
this.figure = 0; // 数字(1-13)
this.selected = false; // 選択済みかどうか
}
// プレーヤークラス
function Player() {
this.money = 10000; // 持ち金
this.stand = true; // standコール
this.score = 0; // 手札の点数
this.count = 0; // 手札の枚数
this.images = new Array(5); // 表示イメージ
this.indices = new Array(5); // 手札のカードのインデックス
this.textnode = null; // textnodeオブジェクト(表示文字列用)
this.tdstatus = null; // tdオブジェクト(文字列表示用)
this.tdimage = null; // tdオブジェクト(イメージ表示用)
}
■初期化のための関数
初期化はInit関数を呼び出して行います。この関数は以下の処理を行います。
@ バージョンチェック A トランプ画像のプリロード(54イメージ分) B プレーヤーオブジェクトの作成(CPU用・ユーザー用) C ゲーム表示領域の作成(テーブルとボタン)
/*** 初期化のための処理 ***/
// プリロード
function PreLoad() {
var strDir = "image/";
var strExt = ".gif";
var aFile = new Array(
"S1","S2","S3","S4","S5","S6","S7","S8","S9","S10","S11","S12","S13",
"H1","H2","H3","H4","H5","H6","H7","H8","H9","H10","H11","H12","H13",
"D1","D2","D3","D4","D5","D6","D7","D8","D9","D10","D11","D12","D13",
"C1","C2","C3","C4","C5","C6","C7","C8","C9","C10","C11","C12","C13",
"Rev6"/* 裏面用 */,"XV"/* 配置場所用 */);
var i;
for(i = 0; i < nCard; i++) {
aCard[i] = new Card();
with (aCard[i]) {
image = new Image();
image.src = strDir + aFile[i] + strExt;
index = i;
figure =i % 13 + 1;
selected = false;
}
}
}
// テーブルの作成
// ... 引数 index :: 0=CPU用, 1=ユーザー用
// ... 戻り値 :: 作成したテーブルオブジェクト
function CreateTable(index) {
// ゲーム領域の追加
tBJ = document.createElement("table");
var tbodyElem = document.createElement("tbody");
var trElem1 = document.createElement("tr");
var trElem2 = document.createElement("tr");
aPlayer[index].tdstatus = document.createElement("td");
aPlayer[index].tdimage = document.createElement("td");
// テーブルの設定
with (tBJ) {
style.width = "170px";
style.fontSize = "9pt";
style.border = "1px solid white";
style.margin = "8px";
}
// ステータス表示領域の追加
with (aPlayer[index].tdstatus) {
style.color = "white";
style.backgroundColor = "green";
style.borderWidth = "0 0 1px 0";
style.borderStyle = "solid";
style.borderColor = "white";
style.padding = "1pt 2pt";
}
// カード表示領域の追加
with (aPlayer[index]) {
tdimage.style.height = "40px";
for (i=0; i<5; i++) {
images[i] = new Image();
images[i].src = aCard[53].image.src;
tdimage.appendChild(aPlayer[index].images[i]);
}
}
// 各オブジェクトの追加
aPlayer[index].textnode = index == 0?
document.createTextNode("ブラックジャックゲーム"):
document.createTextNode("Playボタンを押してください");
aPlayer[index].tdstatus.appendChild(aPlayer[index].textnode);
trElem1.appendChild(aPlayer[index].tdstatus);
trElem2.appendChild(aPlayer[index].tdimage);
tbodyElem.appendChild(trElem1);
tbodyElem.appendChild(trElem2);
tBJ.appendChild(tbodyElem);
return tBJ;
}
// ボタンの追加
function AddButtons()
{
var sTag = "<input type='button'";
var sStyle = "style='width:43px; margin:1px;'>";
document.write("<form style='width:182px'>");
document.write(sTag, " value='Play' ", "onclick='Play_Click()' ", sStyle);
document.write(sTag, " value='Hit' ", "onclick='Hit_Click()' ", sStyle);
document.write(sTag, " value='Stand' ", "onclick='Stand_Click()' ", sStyle);
document.write(sTag, " value='Help' ", "onclick='Help_Click()' ", sStyle);
document.write("</form>");
}
// プレーヤーの登録
function RegistPlayer()
{
aPlayer[0] = new Player(); // CPU用
aPlayer[1] = new Player(); // ユーザー用
}
// 初期化
// ... 引数oParent :: 親オブジェクト
function Init(oParent)
{
if (!document.createElement) {
document.write("ゲームを起動することができませんでした。");
return;
}
// トランプ画像のプリロード
PreLoad();
// プレーヤーの登録
RegistPlayer();
// ゲーム領域の追加
with (oParent) {
style.backgroundColor="green"; // 親オブジェクトの背景色
appendChild(CreateTable(0)); // CPU側テーブル
appendChild(CreateTable(1)); // ユーザー側テーブル
}
// ボタンの追加
AddButtons();
}
■イベント処理のための関数
四つのボタンのクリックイベントに対応するコードを記述していきます。
[Play]ボタン ・・・ ゲームを初期化し、カードを二枚づつ配ります。 [Hit]ボタン ・・・ カードを一枚引きます。 [Stand]ボタン ・・・ 一ゲームの終了を宣言。点数が計算され結果が表示されます。 [Help]ボタン ・・・ ヘルプを表示します。
/*** イベント ***/
// [Play]ボタンのクリックイベント
function Play_Click()
{
var i, j;
if (!aPlayer[1].stand) {
alert("まだゲームが終わっていません。\nHitボタンかStandボタンを押してください。");
return;
}
// プレーヤーの初期化
var bMinus = (aPlayer[0].money < 0 || aPlayer[1].money < 0); // 破産しているか
for (i=0; i<2; i++) {
with (aPlayer[i]) {
for (j=0; j<5; j++) {
if (indices[j] >= 0) aCard[indices[j]].selected = false;
images[j].src = aCard[53].image.src;
indices[j] = -1;
}
stand = false;
score = 0;
count = 0;
if (bMinus) money = 10000; // 破産の場合は初期値に戻す
}
}
// シャッフル
Shuffle();
// プレーヤーに二枚づつ配る
for (i=0; i<2; i++) {
for (j=0; j<2; j++) DrawCard(i);
}
ShowStatus(); // ステータス文字列の表示
}
// [Hit]ボタンのクリックイベント
function Hit_Click()
{
if (aPlayer[1].stand) {
alert("ゲームは終了しています。\nPlayボタンを押してください。");
return;
}
// ユーザー側
if (aPlayer[1].count < 5) DrawCard(1);
else alert("引けるカードは5枚までです。");
// CPU側
if (!aPlayer[0].stand) DrawCard(0);
ShowStatus(); // ステータス文字列の表示
}
// [Stand]ボタンのクリックイベント
function Stand_Click()
{
if (aPlayer[1].stand) {
alert("ゲームは終了しています。\nPlayボタンを押してください。");
return;
}
// ユーザーのstand宣言
aPlayer[1].stand = true;
while (!aPlayer[0].stand) {
DrawCard(0);
}
// CPUの一枚目を表にする
aPlayer[0].images[0].src = aCard[aPlayer[0].indices[0]].image.src;
ShowStatus(); // ステータス文字列の表示
}
// [Help]ボタンのクリックイベント
function Help_Click()
{
window.open("bjhelp.html", "HelpWnd", "");
}
■ゲーム実行にかかわる関数
ゲームの心臓部です。すべて、ボタンのクリックイベントから呼び出されます。
・Shuffle ・・・ カードをシャッフルします。 ・DrawCard ・・・ カードを一枚引いて現在の手札の点数を算出し生ます。 ・ShowStatus ・・・ ゲームの終了を判定し、ステータス文字列を表示します。
/*** ゲームの実行関数 ***/
// シャッフル
function Shuffle() {
var i, j;
for (i=0; i<52; i++) {
j = Math.floor(Math.random() * 100) % 52;
var oCard = aCard[i];
aCard[i] = aCard[j];
aCard[j] = oCard;
}
}
// 一枚引く
// ... 引数 :: プレーヤー番号
function DrawCard(nPlayer) {
// カードを選択
var nIndex;
while (1) {
nIndex = Math.floor(Math.random() * 100) % 52;
if (aCard[nIndex].selected == false) {
aCard[nIndex].selected = true;
break;
}
}
// カードを配る
with (aPlayer[nPlayer]) {
if (nPlayer == 0 && count == 0)
images[count].src = aCard[52].image.src; // CPUの一枚目を裏に
else
images[count].src = aCard[nIndex].image.src;
indices[count] = nIndex;
count++;
}
// 点数の計算
var bAce = false; // エースがあるか
var i;
aPlayer[nPlayer].score = 0;
for (i=0; i<5; i++) {
var nCard = aPlayer[nPlayer].indices[i];
if (nCard != -1) {
if (aCard[nCard].figure >= 10) aPlayer[nPlayer].score += 10;
else aPlayer[nPlayer].score += aCard[nCard].figure;
if (aCard[nCard].figure == 1) bAce = true;
}
}
// エースがあるときは再計算
if (bAce && aPlayer[nPlayer].score+10 <= 21)
aPlayer[nPlayer].score += 10;
// CPUのときはstandの判断
if (nPlayer == 0) {
var nNum;
with (aPlayer[0]) {
if (count == 5 || score >= 18) { /* 手札5枚か18以上でstand */
stand = true;
} else if (score == 17) { /* 17→9/10の確率でstand */
nNum = Math.random();
if (nNum > 0.1) stand = true;
} else if (score == 16) { /* 16→1/5の確率でstand */
nNum = Math.random();
if (nNum < 0.2) stand = true;
} else if (score == 15) { /* 15→1/20の確率でstand */
nNum = Math.random();
if (nNum < 0.05) stand = true;
}
}
}
}
// ステータスの表示
function ShowStatus() {
var i;
var nScore; // 点数
var sResult; // 表示文字列
var nWin, nLoose; //勝った方のインデックス、負けた方のインデックス
// ゲーム終了 :: 0:継続、1:終了、2:破産終了
var nOver = (aPlayer[0].stand && aPlayer[1].stand)? 1: 0;
if (nOver) {
if (aPlayer[0].score > 21) {
if (aPlayer[1].score <= 21) nWin = 1;
else nWin = -1;
} else {
if (aPlayer[1].score > 21) nWin = 0;
else if (aPlayer[0].score > aPlayer[1].score) nWin = 0;
else if (aPlayer[0].score < aPlayer[1].score) nWin = 1;
else nWin = -1;
}
if (nWin != -1) {
nLoose = nWin==0? 1: 0;
if (aPlayer[nWin].score == 21) { /* ブラックジャックは二倍 */
if (aPlayer[nWin].count == 2) {
aPlayer[nWin].money += 2000;
aPlayer[nLoose].money -= 2000;
} else { /* 21のときは1.5倍 */
aPlayer[nWin].money += 1500;
aPlayer[nLoose].money -= 1500;
}
} else { /* 通常は1000点 */
aPlayer[nWin].money += 1000;
aPlayer[nLoose].money -= 1000;
}
// 破産終了のチェック
if (aPlayer[nWin].money < 0 || aPlayer[nLoose].money < 0) nOver = 2;
}
}
// ステータス文字列
for (i=0; i<2; i++) {
with (aPlayer[i]) {
// CPUの一枚目は"??"を表示
nScore = (i==0 && nOver==0)? "??": aPlayer[i].score;
if (nOver == 2) { /* 破産終了時 */
sResult = money < 0? " >>> 破産 <<<": " <<< 優勝 >>>";
} else if (nOver == 1) { /* 一回分の終了 */
if (nWin == -1) sResult = " = 引き分け =";
else if (nWin == i) sResult = " +++ 勝ち +++";
else sResult = " --- 負け ---";
} else if (i == 1 && count == 2 && score == 21) { /* ブラックジャック成立時 */
sResult = " BlackJack !";
} else { /* ゲーム継続時 */
sResult = " $" + aPlayer[i].money;
}
// ステータス文字列の削除と追加
tdstatus.removeChild(textnode);
textnode = i == 0?
document.createTextNode("CPU : " + nScore + "点" + sResult):
document.createTextNode("YOU : " + nScore + "点" + sResult);
tdstatus.appendChild(textnode);
}
}
}
■導入例
駆け引きを重視するなら、掛け金を設定できるようにした方がおもしろいと思います。難易度は、DrawCard関数内のCPUがStandする確率の箇所を調整するといいでしょう。一般には、ディーラーは16以下で必ずHitしなければならず、また、17以上で必ずStandしなければならず、その代わり21を超えても、プレーヤーが21を超えたら勝ちとなるようです。
*** 使用例 ***
<head>
...
<script type="text/javascript" src="*****.js"></script>
...
</head>
<body id="bj">
<script type="text/javascript"><!--
Init(document.getElementById("bj"));
--></script>
</body>
|
|||
www.sasaraan.net |
(c) morijoh |