[Labyrinthe Noir]>[Top]>[Computer Forum]>[実用JavaScript集]>[こども工作教室]>

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


<html>
<head>
<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 language="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 を別枠で表示】

アイデアを出して、改良してみましょう。

・得点を大きくしたい。
・背景に色を付けたい。
・絵柄に色を付けたい。
・画像に置き換えたい。
・スロットの絵柄の組み合わせ(役)で得点が増えるようにしたい。

このようなことをしたいと思います。


ここからは変更のない部分は省略して表示することがあります。追記や変更は、場所の移動はです。


(省略)

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には、そのまま表示される得点が入っている方が判りやすいですね。


(省略)

<script language="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>

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

同じ絵柄がそろったら更に得点を増やしましょう。ここでは乱数から得た数値を20倍にして足しています。

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

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

次に、dramstop()の中で出目の記録を行います。
変数rに入った絵柄の番号を配列変数kirokuに入れます。

そして、表示の前にif文を使って、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></div>の中に<font>タグを置くと、得点の表示をしたときに消えてしまうため、外側に設置します。

<div></div>の中はスクリプトの処理によって書き換えられます。
もし、得点によって色が変わるのなら、タグは得点表示のときに用意するので、ここには必要ありません。

(省略)

<script language="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();

dramreset();

(省略)

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

スロットの絵柄は配列変数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

色を着ける場合「○」や「☆」よりも「●」や「★」を使った方が、表示されたときに見やすくなる。

見やすくするため、改行してあるが、imgは1行で記入します。

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


(省略)

<script language="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();

dramreset();

(省略)

【改良点:スロットの絵柄を画像にする(1)】

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

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

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

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

(省略)

<script language="JavaScript">
img = new Array("buta.gif" ,"usi.gif","saru.gif","kuma.gif" ,"tora.gif","panda.gif");
kiroku = new Array();

dramreset();

function dramreset() {
document.getElementById("dram0").innerHTML = "<img src='" + img[0] + "'>";
document.getElementById("dram1").innerHTML = "<img src='" + img[0] + "'>";
document.getElementById("dram2").innerHTML = "<img src='" + 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 src='" + 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>

(省略)

【改良点:スロットの絵柄を画像にする(2)】

配列変数imgにタグを直接書かないで、ファイル名だけを書く方法もあります。
この場合は、画面に表示するときにタグの中にファイル名を入れるようにします。

画像の数が増えたときや、ファイル名の前にフォルダ名が付くときなど、初期設定が長くなってしまうときに見やすくなります。
また、同じ文字列が繰り返されるのを防ぐために、このように分けて書くことがあります。

今回はこれは使わずに(1)から続けます。


(省略)

<script language="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();

dramreset();

function dramreset() {
for (i=0; i<3; i++) {
document.getElementById("dram" + i ).innerHTML = img[0];
document.slot.elements[i].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>

(省略)

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

機能的な違いはないのですが、同じ処理を繰り返す部分をまとめて書き直したいと思います。

dramreset()の中で表示の初期化が3行、ボタンの初期化が3行あります。
どちらも数字の部分が0から2へと変化している以外は、同じ命令文になっています。
まったく同じ行だけでなく、数値の部分が変化している行であっても、変数を利用して繰り返し処理をすることができます。

繰り返し処理の利点は、何回でも繰り返すことができるということです。例え5回でも100回でも1万回でも、ほとんど命令は変わりません。
これを1行ずつやる場合、1万回なら1万行必要になります。しかし、for文を使えば変数を1つ書き換えるだけで1行だけで済みます。

繰り返し処理には、for文を使います。
変数iを用意して、最初の値、繰り返しの条件、変数の増減の指示を書きます。これだけで、何回でも処理を繰り返すのです。
書式に従って書くと「for (i=0;i<3;i++)」となります。
変数iが0から始まり(i=0)、3より小さいとき(i<3)は命令文を実行し、実行後は変数iを1増やす(i++)という処理をしています。変数iは0、1、2と増えて、3になるときは処理が実行されないため、3回同じ処理を繰り返すことができます。
その間の命令文では、各行が丁度0、1、2と変化していたので、そのまま変数iを当てています。

繰り返しのfor文はこのような使い方がほとんどです。
1つの 呪文のように覚えてしまったほうが良いのかもしれません。
「i<3」の部分を繰り返しの回数に合わせて数字を変えるます。
変数iを{}内で使う場合は、必要な変化に合わせて初期値や条件を書き換えなければなりません。

【この時点の 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 language="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;
}

(省略)

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

これまでの出目と得点を履歴として記録して表示したいと思います。

まずは、<div>タグを使って、「rireki」という表示場所を作ります。
<hr>タグで線を引いてから、その下に<div>タグを書きます。

次に、スクリプトの初期設定で、履歴を記録するために変数rrkを用意します。出目を絵柄(<img>タグ)で記録します。

履歴を表示するタイミングは、リセットボタンを押した時にしたいので、dramreset()内にプログラムを書きます。
出目を表示用に並べて、得点と一緒に表示させるようにします。

まず、 3つの出目は配列変数kirokuに数値で記録されていますので、それを取り出します。
そのままでは絵柄が判らないので配列変数imgから対応する絵柄を取り出します。
1つ目の絵柄は「img[kiroku[0]]」で取り出せます。0から2までの絵柄が必要になるので、for文の中で3回取り出して変数rrkに書き足します。

for文が終わってから、3つの出目の後に得点を付け足します。<br>タグは改行の意味です。
次にストップボタンを押したときは、改行の後に出目と得点が足されることになります。

あとは、そのまま画面に出力してやれば、履歴として見ることができます。
このように、変数rrkにデータを足しながら履歴として見る形を意識しておくと、表示は簡単です。


(省略)

<script language="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;
}

(省略)

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

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

そこで、これを制御するためにもう1つの変数を利用します。
初期設定に変数rrk_numを用意します。これを使って履歴の回数を数えることにします。
あとは、 dramreset()の履歴に関する2ヶ所に、if文を使って初期設定の直後に履歴を表示しないように工夫します。

「if (rrk_num > 0)」という条件文を使います。
これは、「rrk_numが0より大きければ実行しなさい」という意味ですから、そのときは履歴を作成し、表示します。
初期設定の直後は変数rrk_numは0なので、履歴は実行されません。
履歴の処理が終わったところで、変数rrk_numに数字を1つ足していますので、次からは履歴が実行されるようになります。

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


(省略)

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;
}

(省略)

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

履歴が増えていくと、とても不便なことが起こります。
履歴が上から下に順番に表示されるため、新しい履歴が画面の外に出てしまい、見えなくなるのです。

履歴の順番を逆にして、新しい履歴が上になった方が変化が目について良くなります。
順番を逆にしようとすると、処理の流れ通りに、変数rrkにデータを入れていくことができません。後でひっくり返す必要があるからです。
こういうときは、新たにもう1つの変数を用意して、仮に1回分の履歴を作成してから、全体の履歴を作った方が判りやすくなります。

そこで、まずdramreset()に変数sを用意します。
これはリセットボタンを使う毎に初期化される必要があるので、最初に空のデータを入れてやります。

変数sの前に「var」と書いているのは、「これはローカル変数です」という意味です。
ローカル変数というのは、ここだけで使う変数ということです。
例えば、他のユーザー関数でも仮に変数が必要になったら、変数sを使うかもしれません。その時に、それらが混ざらないようにするのがローカル変数なのです。

じゃあ、名前を変えれば良いんじゃないの?と思いますが、同じ名前だと役割が共通だと判って便利なのです。
このような使い捨てとも言える使い方をするのに短い名前の変数はよく使われます。出てくる度に名前を考えるほど意味がないこともあるので、名前を考えるのに無駄な時間を使いたくありません。
また、変数がいつまでも残っていると、コンピュータはいつまでもそれを記憶しようとします。

何度も似たような用途で使う変数だからこそ、ローカル変数にしておくことで、後々の混乱を避けることができるのです。

次に、1つ目のif文の中で変数rrkに出目を取り込んでいましたが、これを変数sで受け取ります。

更に、2つ目のif文では、変数sを得点の前に置いて新しい履歴を作っています。その後ろに前回までの履歴が入った変数rrkを付け足していて、これが新しい履歴になります。

このように変数sを使うことで、表示の直前に順番を決めることができるようになりました。


(省略)

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;
}

(省略)

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

履歴はうまく表示できるようになりました。ここまで来ると、あと1つやりたいことがでてきました。
画像の幅が違うために、履歴の列が揃わないことが気になります。得点部分が波打ってずれているのが判ります。

そこで、整列させるためにテーブルを使いたいと思います。
ついでに変数rrk_numで履歴の順番も数えているので、これをカウンターとして表示させましょう。

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

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

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


【完成】

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


戻る