[Labyrinthe Noir]>[Top]>[こども工作教室]>

「スロットゲーム2」のソースと解説

前回作成したスロットゲームの改善点を考えて、より楽しいゲームを目指しましょう。
いくつか実現できそうなアイデアを出してみます。

・得点を見やすく大きくしたい。
・背景に色を付けたい。
・絵柄をカラフルに色を付るか、画像にしたい。
・スロットの絵柄の組み合わせ(役)で得点が変わるようにしたい。

というとこでしょうか。

前回のソースは以下の通りです。

<!DOCTYPE HTML>
<html>
<head>
<meta charset="utf-8">
<title>スロットゲーム</title> </head> <body> <h3>スロットゲーム</h3> <hr> <form name="slot"> <table border="2"> <tr> <th colspan="3"><div id="score">0点</div></th> </tr> <tr> <td><div id="dram0">☆</div></td> <td><div id="dram1">☆</div></td> <td><div id="dram2">☆</div></td> </tr> <tr> <td><input type="button" value="おす" onClick="dramstop(0)"></td> <td><input type="button" value="おす" onClick="dramstop(1)"></td> <td><input type="button" value="おす" onClick="dramstop(2)"></td> </tr> <tr> <td colspan="3"><input type="button" value="リセット" onClick="dramreset()"></td> </tr> </table> </form> <script type="text/javascript"> img = new Array("-","●","■","♪","☆","★"); dramreset(); function dramreset() { document.getElementById("dram0").innerHTML = img[0]; document.getElementById("dram1").innerHTML = img[0]; document.getElementById("dram2").innerHTML = img[0]; document.slot.elements[0].disabled = false; document.slot.elements[1].disabled = false; document.slot.elements[2].disabled = false; scr = 0; } function dramstop(btn) { r = Math.floor(Math.random() * 6); document.getElementById("dram"+btn).innerHTML = img[r]; document.slot.elements[btn].disabled = true; scr += r; document.getElementById("score").innerHTML = scr + "点"; } </script> </body> </html>

【この時点の slot.html を別枠で表示】

変更のない部分は省略して表示します。追記や変更は、場所の移動はです。

改良点:スロットの得点を増やす

スロットの基本の点数を10倍にしてみましょう。

function dramstop(btn) {
	r = Math.floor(Math.random() * 6);
	document.getElementById("dram"+btn).innerHTML = img[r];
	document.slot.elements[btn].disabled = true;
	scr += r * 10; 
	document.getElementById("score").innerHTML = scr + "点";
}
</script>

得点は、変数rから取っていました。ということは、これを10倍にすれば良いことが判ります。
しかし、スロットの出目の絵柄の表示(img[r])にも使っているため、その邪魔にならない位置でかけ算する必要があります。

dramstop()を見ると、1行目で変数rを取り出していますので、これより下でないといけないことが判ります。
次に「=」の右側に変数rが出てくるところを調べます。2行目と4行目にありますね。
2行目は、配列変数imgの要素として使っているので、これを変更することはできません。変数rの使い方が得点ではないからです。
次に4行目を見ると、こちらは得点として変数rを変数scrに足しています。
この後の5行目で得点として表示されるので、これよりは上でないと意味がありません。
4行目を改良するのが一番良い場所だということになります。

5行目でも改良はできます。
document.getElementById("score").innerHTML = scr*10 + "点";
表示する直前で10を掛ける訳です。この場合変数scrの数値は増えていないことに注意が必要です。
表示するときに後ろに0さえ付いていたら結果は同じなので、中身は小さいままでもかまわないのです。
どちらにしても、他の所で変数scrを使うときに、しっかりと中身を把握していないと間違った点数計算になってしまいます。
変数scrには、そのまま表示される得点が入っている方が判りやすいですね。

改良点:スロットの役で得点を加算する

同じ絵柄がそろった場合に得点を増やしましょう。基本得点の2倍の点数をボーナス点として加算します。

<script type="text/javascript">
img = new Array("-","●","■","♪","☆","★");
kiroku = new Array();

dramreset();

function dramreset() {
	document.getElementById("dram0").innerHTML = img[0];
	document.getElementById("dram1").innerHTML = img[0];
	document.getElementById("dram2").innerHTML = img[0];
	document.slot.elements[0].disabled = false;
	document.slot.elements[1].disabled = false;
	document.slot.elements[2].disabled = false; 
	scr = 0;
}

function dramstop(btn) {
	r = Math.floor(Math.random() * 6);
	document.getElementById("dram"+btn).innerHTML = img[r];
	document.slot.elements[btn].disabled = true;
	kiroku[btn] = r;
	scr += r * 10;
	if ((kiroku[0] == kiroku[1]) && (kiroku[0] == kiroku[2])) {scr += r * 20;} 
	document.getElementById("score").innerHTML = scr + "点";
}
</script>

まず、準備段階として、3つの出目が同じ絵柄かどうかを調べるために記録を残しておく必要があります。
そこで配列変数kirokuを用意して、出目の順番を要素として3つの配列変数で出目を残したいと思います。
ここでは「new Array()」としていますが、後で出目を記録するので()の中には何もありません。

最初に配列変数に何も入っていない場合、どこかでデータを入れてやる必要があります。
もし、データを入れる前に配列変数を参照しようとするとエラーが発生してプログラムが止まります。
プログラムが止まらないように先に仮のデータを入れておく場合もあります。

次に、ユーザー関数dramstop()の中で出目の記録を行います。
配列変数kirokuに変数rを移し替えます。変数rには乱数で作った絵柄の番号が入っています。

そして、基本スコアを計算した次のところで、3つの出目が同じかどうか確認しています。
このif文には2つの条件が「&&」で結ばれています。これは2つの条件の両方が正しいときに真となって、後の命令が実行されます。
この条件では、「1つ目と2つ目」が同じで「1つ目と3つ目」も同じ場合という条件になりますので、結果として3つの出目が同じということになります。

if文が真のとき{}内の命令が実行されます。 ここでは、変数rに20を掛けています。

前項の注意書きと連動しますが、もし、表示の時に得点を10倍にしていた場合、ここでは「scr += r * 20」ではなく「scr += r * 2」としなければいけません。
表示のときに10倍にするので、ここは逆に1/10にしないと計算が合わなくなるのです。
こういう部分がややこしくなるので、実際の数字と変数の数値を合わせておいた方が判りやすいですね。

if文について

書式:if (条件式) {真の場合の命令文} else {偽の場合の命令文}

if文は、条件式に応じて処理をするかどうか判断するために使います。
例えば、くじ引きのように当たりが出たら「おめでとう」と表示するとか、すごろくでコマが50マス進んだらゴールだという場合に判断が必要です。
与えられた条件が満たされていたり、正しい場合を真(しん)と言います。そうでない場合が偽(ぎ)です。
else以降は省略される場合があります。

判断をするための条件式にはいくつかの種類があります。「==」は、2つのデータが同じかどうかを調べます。同じならば真、同じでなければ偽になります。例えば「a == b」のように2つのデータを比較します。データは文字列でも数値でもかまいません。
また、「>」や「<」のように数値を比べて、大きいか小さいかを判断することもあります。これもはい、いいえと同じように真と偽になります。例えば「a > b」なら、aよりbが大きいという条件ですから、a=3でb=2なら真、a=2でb=3なら偽となります。もし、a=2でb=2ならどうでしょうか。これも偽になります。
「>=」ならば2つの数字が同じときも真になります。「<=」もあります。
「!=」という式もあります。これは2つが同じでない(異なる)ときに真となります。「==」の逆ということです。

2つ以上の条件を比べる場合、「&&」や「||」を使います。各条件は()でくくります。
「&&」は、条件を同時に満たす必要があります。すべての条件が真でなければいけません。ところが「||」はどれか1つの条件が真ならば、全体として真となります。

改良点:テーブル内の文字と背景に色を着ける

<form name="slot">
<table border="2">
<tr>
<th colspan="3" bgcolor="#000000"><font color="#FFFFFF"><div id="score">0点</div></font></th>
</tr>
<tr bgcolor="#CCCCCC">
<td><div id="dram0">☆</div></td>
<td><div id="dram1">☆</div></td>
<td><div id="dram2">☆</div></td>
</tr>
<tr>
<td><input type="button" value="おす" onClick="dramstop(0)"></td>
<td><input type="button" value="おす" onClick="dramstop(1)"></td>
<td><input type="button" value="おす" onClick="dramstop(2)"></td>
</tr>
<tr>
<td colspan="3"><input type="button" value="リセット" onClick="dramreset()"></td>
</tr>
</table>
</form>

背景に色を着ける

テーブルの背景に色を着けるには「bgcolor」を使います。
テーブルに関するどのタグ内に置くかによって、背景の色が着く場所が変わります。

・<table>内だとテーブル全体。
・<tr>内だとその行の箱全部。
・<td><th>内だと、その箱だけ。

また優先順位もあり、上の順番に色が塗られて、最後の色が上になって見えます。

文字に色を着ける

文字に色を着ける場合は、<font>タグとcolorオプションを使います。
<div>タグの中に<font>タグを置くと、得点の表示をしたときに消えてしまうため、必ず<div>タグの外側に設置します。

改良点:スロットの絵柄に色を着ける

<font>タグを使ってスロットの絵柄にも色を着けることができます。

<script type="text/javascript">
img = new Array("-" ,"<font color='#0000FF'></font>","<font color='#006600'></font>","<font color='#FF0000'></font>","<font color='#FF5500'></font>","<font color='#FFFF00'></font>");
kiroku = new Array();

スロットの絵柄は配列変数imgで用意していました。
出目の色をテーブル内に<font>タグを書いて付けることができます。
しかし、ここでは絵柄によって色を変えたいので、直接絵柄を指定するときに<font>タグを取り付けます。

このとき、注意しなければいけないことがあります。
「""」でくくられた文字列にタグを書き込む場合、タグのオプションにある「""」をそのまま書いてしまうとスクリプトが動作しなくなります。
「""」の中で「""」を使うと、取り違えてしまうからです。そこで、代わりに「''」を中で使います。

好きな色を着けてみよう。
色番号は、 最初の2桁が赤、次の2桁が緑、最後の2桁が青です。
それぞれの色は2桁の16進数になっていて、0~9の数字の他に、A(10)からF(15)の6文字を数字とみなしています。
例:#FF0000 #00FF00 #0000FF #FFFF00 #FF00FF #00FFFF #000000 #999999 #FFFFFF #FF5500 #666600 #006600
色を着ける場合「○」や「☆」よりも「●」や「★」を使った方が、表示されたときに見やすくなります。

【この時点の slot.html を別枠で表示】

改良点:スロットの絵柄を画像にする

絵柄の記号に替えて画像を使ってみましょう。
画像は<img>タグで表示できますので、そのまま配列変数imgの初期設定に書き入れます。

下の動物から好きな画像を6つ選んでください。
欲しい画像の上で右クリックするとショートカットメニューが出たら「名前を付けて画像を保存」を選びます。ダイヤログが出たら、slot.htmlと同じ場所に保存してください。

画像 タグ 画像 タグ 画像 タグ 画像 タグ
ぶた <img src='buta.gif'> とら <img src='tora.gif'> さる <img src='saru.gif'> ねずみ <img src='nezumi.gif'>
いぬ <img src='inu.gif'> ぱんだ <img src='panda.gif'> うさぎ <img src='usagi.gif'> こあら <img src='koara.gif'>
くま <img src='kuma.gif'> ねこ <img src='neko.gif'> うし <img src='usi.gif'> かっぱ <img src='kappa.gif'>

選んだ画像に対応する<img>タグは画像の右にあります。これを絵柄として置き換えます。

上の画像は「あおのり村」さんから学習用にお借りしています。他の用途での使用はしないでください。

<script type="text/javascript">
img = new Array("<img src='buta.gif'>" ,"<img src='usi.gif'>","<img src='saru_f.gif'>","<img src='kuma.gif'>","<img src='tora.gif'>","<img src='panda.gif'>");
kiroku = new Array();

スロットの絵柄が動物になると、デザイン的にも見栄えが良くなりますね。

配列変数imgにタグを書かずに、ファイル名だけを書く方法もありますが、今回は止めておきます。

改良点:同じ処理を繰り返す

機能的な違いはないのですが、同じ処理を繰り返す部分のスクリプトをまとめるように書き直します。
これはプログラムが無駄に長くならないようにして見やすくします。また、同じ部分を1つにまとめることで変更や修正も分かりやすくなるのです。
変更前と変更後を比較して見てください。

変更前

function dramreset() {
	document.getElementById("dram0").innerHTML = img[0];
	document.getElementById("dram1").innerHTML = img[0];
	document.getElementById("dram2").innerHTML = img[0];
	document.slot.elements[0].disabled = false;
	document.slot.elements[1].disabled = false;
	document.slot.elements[2].disabled = false; 
	scr = 0;
}

変更後

function dramreset() {
	for (i=0; i<3; i++) { 
		document.getElementById("dram" + i).innerHTML = img[0]; 
		document.slot.elements[i].disabled = false; 
	}
	scr = 0;
}

ユーザー関数dramreset()の中でドラムの初期化とボタンの初期化がそれぞれ3行ありました。
変更前を見ると、ドラムの初期化では、位置を表すID名が0~2まであり違いはそこだけです。ボタンの初期化も0~2の番号で処理しています。
どちらも数字の部分が0から2へと変化していますので、その部分を自動化することでひとまとめにします。

繰り返し処理には、for文を使います。
for文の中では変化する値を変数で処理します。変数iを用意し、最初の値、上限の値、値の変化を決めます。
今回は0から始まりますので、最初の値は0です。これを「i=0」と表記します。
上限は2ですので、ここでは3より小さいという意味で「i<3」と書きます。
値の変化は、1ずつ増えますので、「i++」と書きます。これは「i = i + 1」と同じ意味です。
これら3つの条件がfor文には必要です。()の中に「;」で区切って書きます。
これで{}の中の処理を3回繰り返すことになります。

変数iが0のとき、1のとき、2のときにどうなるか考えてみると、変更前と同じ処理になることが判るでしょうか。

繰り返し処理の利点は、何回でも繰り返すことができるということです。例え5回でも100回でも1万回でも、上限を変更するだけで処理してくれます。
そう考えるとこれを1行ずつ書くのがどれだけ大変で無駄になるか判ります。しかし、
for文もまたプログラムを書くときにはなくてはならない命令文です。

【この時点の slot.html を別枠で表示】

for文について

書式:for (初期値; 比較式; 増分) {繰り返したい命令文}

任意の変数を使って初期値を設定します。比較式には、その変数の値を比べて条件が真のときに命令文を実行します。増分には、命令文実行の後に、変数に変化を与えます。
例えば、for (i=0; i<5; i++) ならば、変数iは0から始まり、5より小さい間、1ずつ増える、ということになります。変数iは0から4までの間の5回、処理を繰り返すことになります。
また、実行命令中で変数iを使う必要はありません。ただ繰り返すだけの場合もあれば、変数iの変化を利用する場合もあります。

改良点:出目と得点を記録する

これまでの出目と得点を履歴として記録して表示するようにしましょう。

</form>

<hr>
<div id="rireki"></div>

<script type="text/javascript">
img = new Array("<img src='buta.gif'>" ,"<img src='usi.gif'>","<img src='saru_f.gif'>","<img src='kuma.gif'>","<img src='tora.gif'>","<img src='panda.gif'>");
kiroku = new Array();
rrk = ""; 

dramreset();

function dramreset() {
	for (i=0; i<3; i++) { 
		rrk += img[kiroku[i]]; 
		document.getElementById("dram" + i).innerHTML = img[0]; 
		document.slot.elements[i].disabled = false;
	}
	rrk += scr +"点<br>";
	document.getElementById("rireki").innerHTML = rrk; 
	scr = 0;
}

フォームの下に<hr>タグで仕切り線を引きます。
その下に<div>タグを書いてID名をrirekiとします。ここが履歴を表示する位置となります。

次に、スクリプトの初期設定のところで、履歴を記録するための変数rrkを用意します。

履歴を画面に表示するのはどの段階が良いでしょうか。

スロットが揃った後が良いでしょう。しかし、ユーザー関数dramstop()の中に書いてしまうとドラムを1つ止めるごとに履歴を表示することになってしまいます。
では、もう少し後にしましょう。スロットが3つ揃った後に押すボタンはリセットボタンです。この時に履歴を表示するのが良さそうです。
ユーザー関数dramreset()内にプログラムを追加しましょう。

まず、 3つの出目は配列変数kirokuに数値で記録されていますのでこれが必要になります。しかし、後で取り出しやすいように配列変数imgを利用して<img>タグを取り出して記録します。
3つの絵柄を記録するためこれもfor文の中でで処理します。
これで3つの出目が記録できます。

for文が終わってから、3つの出目の後に得点を付け足します。更にその後に<br>タグを書きます。これは改行を表すため、履歴の1回分で改行されることになります。

次で変数rrkが画面に出力されて、履歴が表示されます。
このように、変数rrkにデータを足す時点で見やすくしておいたので、出力はとてもシンプルに書くことができました。

問題点:最初に履歴が表示される

ここで実行してみると、大変な問題点があることが判ります。
まだゲームを始めていないのに履歴が表示されてしまうのです。
これは、初期設定の後でdramreset()を実行していることが原因です。
そのために、リセットボタンを押したときと同じように履歴が作られて、画面に出てしまいます。

そこで、これを制御するために変数を用意してフラグチェック(状況確認)します。

<script type="text/javascript">
img = new Array("<img src='buta.gif'>" ,"<img src='usi.gif'>","<img src='saru_f.gif'>","<img src='kuma.gif'>","<img src='tora.gif'>","<img src='panda.gif'>");
kiroku = new Array();
rrk = "";
rrk_num = 0;

dramreset();

function dramreset() {
	for (i=0; i<3; i++) { 
		if (rrk_num > 0) {
			rrk += img[kiroku[i]];
		}
		document.getElementById("dram" + i).innerHTML = img[0]; 
		document.slot.elements[i].disabled = false;
	}
	if (rrk_num > 0) {
		rrk += scr +"点<br>";
		document.getElementById("rireki").innerHTML = rrk; 
	}
	rrk_num++;
	scr = 0;
}

初期設定に変数rrk_numを用意します。これを使ってユーザー関数の中を通った回数を記録します。
変数rrk_numの初期値は0です。この0を一つのキーワードして考え、履歴の処理を実行するかどうかを判断する条件とします。

ユーザー関数dramreset()の中で条件判断を行います。
まず、変数rrkに履歴を追加する部分です。これを変数rrk_numが0のときは実行しませんので、逆に0より大きいときは実行するようにif文を書きます。
「if (rrk_num > 0)」という条件文は「rrk_numが0より大きければ実行しなさい」という意味です。

変数rrkに追加する部分はもう一カ所あります。また、変数rrkを出力するところも続けてありますので、この2行を同じif文で囲みます。
これで履歴の出力も変数rrk_numが0のときには実行されません。

そして、それらの処理の後、変数rrk_numに数字を1つ足します。これで次回ユーザー関数が実行されたときには、if文の条件が真となり、履歴に関する処理が実行されます。

【この時点の slot.html を別枠で表示】

問題点:点数のバグを修正

非常に気づきにくいのですが、点数計算に問題点があります。
3つの出目を記録した後、リセットをしていないので、1つ前の出目の記録がそのまま配列変数kirokuに残っています。そのため、3つ目のボタンを押すまでもボーナス点が加算されてしまう場合があります。
配列変数kirokuを初期化する位置を変更して対処します。

<script type="text/javascript">
img = new Array("<img src='buta.gif'>" ,"<img src='usi.gif'>","<img src='saru_f.gif'>","<img src='kuma.gif'>","<img src='tora.gif'>","<img src='panda.gif'>");
rrk = "";
rrk_num = 0;

dramreset();

function dramreset() {
	for (i=0; i<3; i++) { 
		if (rrk_num > 0) {
			rrk += img[kiroku[i]];
		}
		document.getElementById("dram" + i).innerHTML = img[0]; 
		document.slot.elements[i].disabled = false;
	}
	if (rrk_num > 0) {
		rrk += scr +"点<br>";
		document.getElementById("rireki").innerHTML = rrk; 
	}
	rrk_num++;
	scr = 0;
	kiroku = new Array();
}

スクリプトの最初の2行目にあった配列変数kirokuの初期設定をユーザー関数dramreset()の最後に移動します。
これによって、起動時とリセット時に配列変数もリセットされます。

1つ前の行程で、変数rrk_numをif文で処理したことで、ユーザー関数dramreset()内の配列変数kirokuに関する部分も起動時には処理をしなくなりました。
そのため、このような簡単な移動だけで配列変数kirokuをリセットできるようになっています。
もし、そのif文がなかったら「rrk += img[kiroku[i]];」のところでエラーが発生します。

問題点:履歴の順番を逆順にしたい

履歴が増えていくと、とても不便なことが起こります。
履歴が上から下に順番に表示されるため、新しい履歴が画面の下に送られて、表示の外に出てしまうのでスクロールしないと見えなります。
履歴の順序を逆にして、新しい履歴が上になるようにした方が見やすくなります。

function dramreset() {
	var s = "";
	for (i=0; i<3; i++) { 
		if (rrk_num > 0) {
			s += img[kiroku[i]];
		}
		document.getElementById("dram" + i).innerHTML = img[0]; 
		document.slot.elements[i].disabled = false;
	}
	if (rrk_num > 0) {
		rrk = s + scr +"点<br>" + rrk; 
		document.getElementById("rireki").innerHTML = rrk;
	}
	rrk_num++;
	scr = 0;
	kiroku = new Array();
}

順番を逆にしようとすると、その部分は逆に並べなければなりません。しかし、1回分の履歴は処理の順番に記録しますので、2つの流れは向きが違います。
そこで、こういうときは別の変数を1つ用意して、一旦1回分の履歴を作成してから、全体の履歴と合わせることにします。

ではまず、ユーザー関数dramreset()に変数sを用意します。この時点で空のデータを入れておきます。
これが一回分の履歴を記録する変数となります。全体の履歴を記録する変数rrkは役割は変わりません。

変数sの前に「var」と書いているのは、「ローカル変数」という意味です。
ローカル変数に指定された変数は、ユーザー関数の処理が終わると消えてなくなります。
変数には汎用的にあちこちで同じ名前で使われるものがあります。そんなとき、中身が残っていると間違いの元となりますので、一時的な利用であることを宣言しているのです。

次に、1つ目のif文の中で変数rrkに出目を記録していましたが、これを変数sに替えます。

2つ目のif文では、全体の履歴である変数rrkに、順番を逆にして記録する必要があるので、新しい履歴である変数sと今回の得点をつなげた後、これまでの全体履歴をつなげています。それらが新しい変数rrkに入って履歴が更新されます。

このように変数を複数使うことで、データの移し替えが簡単に行えるようになります。

改良点:履歴の縦列を綺麗に揃えたい

履歴はうまく表示できるようになりましたが、微妙に気になる点があります。
画像の幅や点数の桁数が違うと、縦に表示される内容が綺麗に揃いません。
そこで、テーブルを使ってデータを揃えて表示させたいと思います。

function dramreset() {
	var s = "";
	for (i=0; i<3; i++) { 
		if (rrk_num > 0) {
			s += img[kiroku[i]];
		}
		document.getElementById("dram" + i).innerHTML = img[0]; 
		document.slot.elements[i].disabled = false;
	}
	if (rrk_num > 0) {
		rrk = "<tr><td>" + rrk_num + "</td><th>" + s + "</th><th>" + scr + "点</th></tr>" + rrk;
		document.getElementById("rireki").innerHTML = "<table>" + rrk +"</table>"; 
	}
	rrk_num++;
	scr = 0;
	kiroku = new Array();
}

ついでに変数rrk_numで履歴の順番も数えているので、これをカウンターとして表示させましょう。

履歴を変数rrkに入れるところに、タグを足していきます。
先頭に変数rrk_numを表示させましょう。
2つ目の箱に変数sの内容が表示されます。3つの出目が表示されます。
ここで<th>タグを使っているのは、全体を中央に寄せるためです。
3つめの箱に得点が入ります。ここも<th>タグにしています。
<br>タグはなくなっています。
<tr></tr>を使っていますので、テーブルではこれが1行になるので、改行は必要ありません。

履歴の中に<table>タグを入れてしまうと、1つ毎にテーブルが出来てしまいます。
表示のときに履歴を全体を<table></table>で囲むことで、全体を1つのテーブルにすることができます。

【この時点の slot.html を別枠で表示】

とりあえず完成

見た目の改良がほとんどできあがりました。普通に遊ぶにはもうこれで十分と言えます。
ただ、他の人に遊んでもらおうと思うと、まだ少し手直しをしておいた方が良い部分もあります。細かいところの手直しも含めて次回にやっていきたいと思います。

戻る