次へ進む  [目次]  [STEP0]  [STEP1]  [STEP2]  [STEP3]  [STEP4]  [STEP5]  [STEP6]  [STEP7]  [終わりに]

猫でも作れる神経衰弱♪

STEP4:カードをめくってみますにゃ   STEP4−結果

 STEP4では、ゲームで使うカードを並べて、ひとりでめくって遊ぶところまで作りますにゃ。できあがるものはこれですにゃ。
 STEP3で作ったHTMLに、いろいろ書き加えていくんですけど、その前に、この神経衰弱のルールを決めておきましょにゃ。

○基本ルール
 ・30枚のカードを使って、2人で遊ぶ神経衰弱ですにゃ。
 ・自分の番になったら、好きなカードを2枚めくりますにゃ。
 ・その2枚のカードの数字(または絵柄)がおなじだったら、その2枚のカードを取りますにゃ。得点が1点加算されて、もう1回その人の番になりますにゃ。
 ・2枚のカードの数字が違う時は、その2枚のカードをその場で裏返しにして、相手と交代ですにゃ。
 ・取れるカードがなくなった時、得点が多い人が勝ちですにゃ。
 ・30枚のカードの内訳は「1〜7までの数字が各4枚」「追加カードが2枚」で、その2枚はゲームタイプによって変わりますにゃ。
○ゲームタイプのルール
 ・ゲームタイプA 追加カードとして「数字の0」が2枚追加されますにゃ。基本的な遊び方は変わらにゃいんですけど、他の数字は4枚ずつあるのに0は2枚しかありませんから、ちょっと揃えにくいカードになりますにゃ。
 ・ゲームタイプB 追加カードとして「くるくるカード」が2枚追加されますにゃ。めくった2枚のカードの中に「くるくるカード」があった場合、その2枚が揃っても揃わにゃくても、場に残っているカードがシャッフルされますにゃ。2枚ともくるくるカードだった時は、他のカードとおなじように、その2枚を取って1点ですにゃ。
 ・ゲームタイプC 追加カードとして「数字の0」と「くるくるカード」が1枚ずつ追加されますにゃ。くるくるカードの効果はタイプBとおなじですにゃ。タイプCで追加された2枚のカードは絶対に揃いませんから、最後に場に2枚のカードが残った状態でゲームが終わることになりますにゃ。
 ・ゲームタイプD 追加カードとして「おなじ数字ではにゃい1〜7までの数字カード」が1枚ずつ追加されますにゃ。つまり、1〜7までの数字カードのうち、どれか2種類のカードが5枚になって、どのカードが増えたのか遊んでいる人にはわかりませんにゃ。タイプCとおなじように、最後に揃わにゃい2枚が場に残った状態でゲームが終わることになりますにゃ。
○ラッキーカードのルール
 ・ラッキーカード「なし」を選択した時は、このルールは適用されませんにゃ。
 ・「1点」または「2点」を選択すると、ゲームが始まる時に、数字の1〜7のどれかひとつが自動的に選ばれてラッキーカードとして表示されますにゃ。麻雀がわかる人は、ドラ(表示牌)みたいなものだと思ってくださいにゃ。ラッキーカードとして表示される画像は、そのために用意した画像ですから、ゲームで使うカードが1枚減るわけではありませんにゃ。
 ・ラッキーカードを2枚揃えると、通常の1点に加えて「1点または2点」が加算されますにゃ。ラッキーカードを狙ってそろえると、ちょっと有利になるかもしれませんにゃ。

 ゲームのルールは、こんな感じですにゃ。STEP3とおなじように、何回かに分けて、プログラムを書き加えていきますにゃ。ここから先は、HTMLの部分は1回書き換えるだけで、おもにJavaScriptを書き加えていきますにゃ。まずは、ここまで作ってみましょにゃ。こういうものができると思いますにゃ。

<html>
<head>
<title>神経衰弱♪</title>
<script language="JavaScript">
<!--
tims=0;times=0;flg=0;
var player=new Array(2);var p=new Array(30);
var gz=new Array(11);
for (i=0;i<=10;i++) {gz[i]=new Image(48,60);}
for (i=0;i<=10;i++) {gz[i].src="./card-"+i+".gif";}

// メイン
function main()
{
 player[0]=document.hyoji.player1.value*1;
 player[1]=document.hyoji.player2.value*1;
 gametype=document.hyoji.gmt.value*1;
 lcrule=document.hyoji.lc.value*1;
 for (i=0;i<=6;i++)
 {
  for (j=0;j<=3;j++)
  {
   p[i*4+j]=i+1;
  }
 }
 p[28]=8;p[29]=8;
 h=carddataput();
 if (lcrule>=1)
 {
  luckycard=1;
 }
 else {luckycard=10;}
 h=charaput(30,luckycard);
 document.hyoji.tm.value="0分0秒";
 tims=0;times=0;flg=1;
}

// マウスクリック処理
function pic(n)
{
 c=""+n+"番目のカードをクリックしましたにゃ";
 document.hyoji.h1.value=c;
}

// カードの内容表示
function carddataput()
{
 c="";
 for (i=0;i<=4;i++)
 {
  for (j=0;j<=5;j++)
  {
   a=i*6+j;
   c=c+"["+p[a]+"]";
  }
  c=c+"\n";
 }
 document.hyoji.text1.value=c;
}

// 画像表示
function charaput(n1,n2)
{
 name="img"+n1;
 document.images[name].src=gz[n2].src;
}

// タイマー処理
function timerX()
{
 if (flg>=1)
 {
  tims=tims+1;
  if (tims%10==0)
  {
   times=times+1;
   a1=Math.floor(times/60);a2=times%60;
   document.hyoji.tm.value=""+a1+"分"+a2+"秒";
  }
 }
}

// -->
</script>
</head>
<body bgcolor="A0FFA0" onLoad='setInterval("timerX()",100);'>
※この後はおなじですから省略しますにゃ

 「開始」をクリックすると、1秒ごとに時間表示が進んでいきますにゃ。もう1回「開始」を押すと、時間がリセットされて、また0分0秒から進み始めますにゃ。そして、薄黄色の小部屋の「ラッキーカード」のセレクトで「+1」または「+2」を選択してから「開始」をクリックすると、ラッキーカードの画像として1のカードが表示されますにゃ。また1行ずつ解説していきますにゃ。読み飛ばしてもかまいませんにゃ。
・「tims=0;times=0;flg=0;」
 1行の中に3つの命令を書いていますにゃ。最初の命令の意味は「変数timsの値として0を代入する」で、あとはおなじようなことですから説明は省略しますにゃ。ただし、後で出てくる割り込み処理の関係で、この3つの変数は最初からにゃいと困りますにゃ。だから、プログラムの最初に書くんですにゃ。
・「var gz=new Array(11);」
 gzという名前の配列を使うことを宣言しますにゃ。要素の数は、ゲームで使う画像ファイルの数とおなじにゃんですけど、実際には無意味ですにゃ。
・「for (i=0;i<=10;i++)」「 {」「}」
 STEP3でも出てきた繰り返し処理(iループ)ですにゃ。前回と違うのは、2重ループになってにゃいってことと、1行の中にまとめて書いているってこと、そして繰り返しの回数は11回っていうことだけですにゃ。
・「gz[i]=new Image(48,60);」
 配列gzのi番目の要素を横48、縦60の大きさの新しい画像にするっていう意味ですにゃ。ちょっとわかりにくいかもしれにゃいんですけど、そういうものだと思ってくださいにゃ。わたしが用意した画像じゃにゃくてご自身で作った画像を使う人で、大きさの違う画像にした場合は、ここの数値をそれに合わせて変更してくださいにゃ。
・「gz[i].src="./card-"+i+".gif";」
 配列gzのi番目の要素として、「=」の右側の名前の画像ファイルを取り込みますにゃ。=の右側は、「./card-」という文字列に変数iの値を追加したものに「.gif」という文字列を追加したもので、繰り返し処理のたびにiの値は変わりますにゃ。具体的に言うと…
・1回目 iの値は0で、できあがる文字列は「./card-0.gif」になりますにゃ。それはつまり、画像gzの0番目の要素として、おなじフォルダの中にあるcard-0.gifという名前の画像ファイルを取り込むってことになりますにゃ。
・2回目 iの値は1になって、できあがる文字列は「./card-1.gif」になりますにゃ。画像gzの1番目の要素として、card-1.gifを取り込むってことですにゃ。これを11回目(card-10.gif)まで続けますにゃ。あとはおなじことですから、説明は省略しますにゃ。この行と前の行は、おなじiループの中にまとめて書いてもおなじ結果になるんですけど、わかりやすくするために2行に分けましたにゃ。
・「if (lcrule>=1)」「{」「}」「else」「{」「}」
 繰り返し処理とおなじくらい重要で本当によく使う命令「if」が、ここで登場しましたにゃ。意味は、もし( )の中の条件(ここでは変数lcruleの値が1以上)が正しければ、{ }の中の命令を実行しにゃさい。そうでにゃい場合は「else」の後にある{ }の中の命令を実行しにゃさいってことですにゃ。elseとそれに続く部分は省略できますにゃ。これは本当に大事なことでこれがわからにゃいとこの先どうにもなりませんから、プログラミングを覚えたい人はこの機会にぜひ覚えてくださいにゃ。
・「luckycard=1;」
 変数luckycardの値として1を代入しますにゃ。これは、ifの後の{ }の中にありますから、ifの条件が正しい時(ここでは変数lcruleの値が1以上の時)だけ実行されますにゃ。
・「else {luckycard=10;}」
 こういう感じで1行にまとめて書くこともありますにゃ。変数luckycardの値として10を代入するんですけど、elseの後の{ }の中にある命令はifの条件が正しくにゃい時(ここでは変数lcruleの値が1未満の時)だけ実行されますにゃ。今回のように、ifの後、またはelseの後に続く命令がひとつしかにゃい時は、本当は{ }で囲む必要はありませんにゃ。でも、後で命令を追加する時などに間違えやすいから、わたしは命令の数に関係にゃく{ }を付けるようにしていますにゃ。説明が長くてわかりにくかったかもしれにゃいんですけど、ようするに「変数lcruleの値が1以上(セレクトで+1または+2を選択した)なら変数luckycardの値は1、そうじゃにゃかったら変数luckycardの値は10ですよ」って言ってるだけの話ですにゃ。
・「h=charaput(30,luckycard);」
 関数charaputを呼び出して、第1引数として数値30第2引数として変数luckycardの値を渡して実行するっていう意味ですにゃ。30は30番目の画像(ラッキーカード表示用の画像)で、変数luckycardの値は「1または10」ですにゃ。呼び出してにゃにをするのかは、後で説明しますにゃ。
・「document.hyoji.tm.value="0分0秒";」
 このプログラムを最初に実行する時には、この命令は必要ありませんにゃ。2回目以降、前回の表示が画面に残ったままになってしまうのを避けるために、「0分0秒」という文字列を書き直していますにゃ。
・「tims=0;times=0;」
 おなじ理由で、時間表示が前回の続きからになってしまうのを避けるために、2つの変数をリセットしていますにゃ。
・「flg=1;」
 わたしは変数flgをその時のゲームの状態を表すために使っていますにゃ。開始前は0、ゲーム中はその時の状況に応じて基本的に1以上の整数を使うようにしてるんですけど、これは賛否両論あるところで、「フラグというものは0か1のどちらかであるべきだ」っていう風に考える人もいますにゃ。ここでは、「開始」のボタンをクリックすると変数flgの値が1になって、後で出てくる割り込み処理の中で時間を進めるための命令が実行されるようになりますにゃ。
・「function charaput(n1,n2)」「{」「}」
 関数charaputの始まりと終わりですにゃ。この関数が呼び出された時に渡された第1引数を変数n1第2引数を変数n2として扱いますにゃ。
・「name="img"+n1;」
 文字列nameを作りますにゃ。「img」という文字列の後にn1の値(=呼び出された時に渡された第1引数)をくっつけますにゃ。この場合、文字列nameは「img30」になりますにゃ。 ・「document.images[name].src=gz[n2].src;」
 文字列nameとおなじ名前の画像配列gzのn2番目の要素として取り込まれた画像に差し替えますにゃ。n2は、この関数が呼び出された時の状況(「開始」をクリックする前に「なし」を選択したかどうか)によって1または10のどちらかになりますにゃ。
 この2行をまとめると、「img30という名前の画像(=ラッキーカードの画像)を配列gzの1番目の要素(「card-1.gif」=1の数字のカードの画像)または10番目の要素(「card-10.gif」=カードの裏の画像)に差し替える」ことになって、ようするに「ラッキーカードの絵を数字の1またはカードの裏の絵にする」ってことですにゃ。この後、カードをめくる処理も作っていくんですけど、実際にカードを裏返しにするんじゃにゃくて、カードの表の画像と裏の画像を差し替えることで「カードが裏表になっているように見せかける」ことになりますにゃ。
・「function timerX()」「{」「}」
 timerXという名前の関数の始まりと終わりですにゃ。「X」は半角アルファベットの大文字で、にゃんでこういう名前になったのかは知らにゃいんですけど、わたしがプログラミングの勉強をした時に参考にさせていただいたサイトさんで、こういう風にしにゃさいって書いてあったから、敬意を表するってことでそのまま使わせていただいてますにゃ。キーボードからアルファベットの大文字を入力する時は、「Caps lock」を使うか「Shift」を押しにゃがらアルファベットのキーを押すと出ますにゃ。後でまた説明することになるんですけど、この関数は一定の時間ごとに自動的に呼び出されて実行されますにゃ。
・「if (flg>=1)」「{」「}」
 「if」がまた出てきましたにゃ。意味は、「( )の中の条件が正しい時、{ }の中の命令を実行しにゃさい」ですにゃ。今回は必要ありませんからelse以下は省略していますにゃ。条件は「flg>=1」つまり「変数flgの値が1以上である」ですにゃ。そして、この後説明しますけど、{ }の中の命令は、時間表示を進めるためのものですにゃ。ちょっと前に「flg=1;」の説明をした時に「時間を進めるための命令が実行されるようになる」って言ったのは、こういうことですにゃ。
・「tims=tims+1;」
 変数timsの値に1を足しますにゃ。より正確に言うと、変数timsの元の値に1を足した数を変数timsに代入するってことですにゃ。そんなに難しく考えにゃくても、ようするにこの部分が実行されるたびににゃにか数を数えているっていうだけのことですにゃ。
・「if (tims%10==0)」「{」「}」
 またifですにゃ。しかも、さっきのifの{ }の中に出てきましたにゃ。こんな風にifが2重になっている時、内側の{ }の中の命令は、直前のifだけじゃにゃくて外側のifの条件にもあてはまらにゃいと実行されませんにゃ。慣れにゃいとちょっとややこしいんですけど、こういう書き方ができるようになると、いろんな条件を組み合わせていろいろ使えるようになりますにゃ。「%」は、左の数値を右の数値で割った時の余りを計算する時に使いますにゃ。数値はどちらか一方または両方が変数でも問題ありませんにゃ。「%」をキーボードから入力する時は、「Shift」を押しにゃがら数字の「5」(テンキーは不可)を押すと出ますにゃ。「==」は、「左の値と右の値は同値である(等しい)」っていう意味で、数学でいう「=(イコール)」とおなじにゃんですけど、代入の「=」と間違えやすいですから気を付けましょにゃ。この条件は、「変数timの値を10で割った余りが0である」で、もっとわかりやすく言うと「変数timの値が10で割り切れる」ってことですにゃ。ここでは、この命令を含むまとまった命令が実行されるたびににゃにか数を数えて、それが10、20、30…になるたびに次のまとまった命令を実行することになりますにゃ。
・「times=times+1;」
 さっきとおなじで、変数timesの値に1を足しますにゃ。後でもう1回説明することになるんですけど、function timerX()は0.1秒に1回呼び出されて実行されますにゃ。つまり、変数timsの時ににゃにか数を数えていると言ったのは、0.1秒ごとに1、2、3…と数えていたってことで、それが10、20、30…になるたびにその後のまとまった命令が実行されるわけですから、変数timesの値は1秒ごとに1ずつ増えていくことになりますにゃ。
・「a1=Math.floor(times/60);」
 Math.floorは「( )内の値の小数点以下を切り捨てる」っていう意味で、例えば「1.5」→「1」になりますにゃ。元の数値が0以上ならにゃにも問題にゃいんですけど、元の数値がマイナスの時には「それより小さい整数」、例えば「-1.5」→「-2」になってしまいますにゃ。ここではマイナスの計算はしませんから、大丈夫ですにゃ。この命令の意味は「変数a1の値として、変数timesを60で割って小数点以下を切り捨てた値を代入する」にゃんですけど、timesは1秒ごとに1増えることになりますから、ようするに60秒=1分として数えるってことですにゃ。
・「a2=times%60;」
 %は、さっきも出てきましたにゃ。左の数を右の数で割った余り、ここでは変数timesの値を60で割った余りですにゃ。それを変数a2に代入しますにゃ。この2行をまとめると、変数timesの値を元に計算して変数a1と変数a2を作って、変数a1は「分」変数a2は「秒」を表すようになっていますにゃ。ちなみに、変数timesを3600で割って切り捨てた値を…余った値を…っていう計算をすると、ゲーム時間が1時間を超えた時の表示もできるようになるんですけど、そこまでする必要はにゃいと思いますにゃ。
・「document.hyoji.tm.value=""+a1+"分"+a2+"秒";」
 文字列を作って、tmという名前の場所(「開始」ボタンの横)に表示しますにゃ。表示する文字列は、変数a1の値に「"分"」を追加したものに変数a2の値を追加してそこに「"秒"」を追加したものにゃんですけど、ようするににゃにが言いたいのか、もう説明しにゃくてもわかりますにゃ?
・「 onLoad='setInterval("timerX()",100);'」
 これは、JavaScriptじゃにゃくて「<body>のタグの中」に書き込みますにゃ。「’」は「シングルクォート」または「シングルクォーテーション」という特殊文字で、本当は意味が違うんですけど「アポストロフィ」と呼ばれることもありますにゃ。キーボードから入力する時は、「Shift」を押しにゃがら数字の「7」(テンキーは不可)を押すと出ますにゃ。「"」とおなじように2つの「'」で囲んで文字列を表すのに使うんですけど、今回みたいに何らかの理由で文字列を2重に囲む必要がある時に「"」と一緒に使うことが多いですにゃ。この命令の意味は「このページが読み込まれた時、1000分の100秒(=0.1秒)ごとに関数「timerX()を呼び出す割り込み処理を始める」ですにゃ。「,」の後の数字は、1000分の1秒を1として、理論上は1以上の数なら自由に指定できるんですけど、実際に1000分の1秒ごとに割り込み処理をさせたら、パソコンの処理速度が追い付かにゃいと思いますにゃ。素早い動きを必要とするアクション系のゲームでも10(100分の1秒)くらいで十分で、今回のような動きのほとんどにゃいゲームなら、100(10分の1秒)でも速すぎるくらいですにゃ。ちなみに、割り込み処理というのは、昔むかしパソコンの性能が今より劣っていた時代に、にゃにか処理をするのにちょっと時間がかかる、でもその処理を中断してでもこっちの処理を先にやってほしい、そういう時に「強制的に割り込ませる」からこういう名前が付いたんですけど、今のパソコンの性能だと、割り込みって言うほどのことはにゃいかもしれませんにゃ。

 ルールの説明の時に「ラッキーカードは麻雀のドラみたいなもの」って言ったと思いますにゃ。いつもいつも数字の1がラッキーカードっていうのも、ちょっとどうかと思いますにゃ。そんなわけで、ゲームを始めるたびにラッキーカードの数字が変わるようにしてみましょにゃ。今回は簡単で、たった1行書き換えるだけですにゃ。こういうものができると思いますにゃ。

if (lcrule>=1)
 {
  luckycard=1;
  luckycard=Math.floor(Math.random()*7)+1;
 }
(※他の部分はおなじですから省略しますにゃ)

 ラッキーカードのセレクトで「+1」または「+2」を選択した状態で、「開始」を何回か繰り返しクリックしてみてくださいにゃ。1行書き換えただけで、「開始」をクリックたびにラッキーカードの数字が変わるようになって、にゃんとなくゲームっぽくなってきましたにゃ。何回もやってみるとわかるんですけど、たまに「開始」をクリックしたのにカードの数字が変わらにゃいことがありますにゃ。それは、ボタンが反応してにゃいとか、プログラムが正しく動作してにゃいとか、そういうことではありませんにゃ。プログラム内でラッキーカードの数字をランダムに決めた結果、たまたま前回とおなじ数字が選ばれた時にそうなりますにゃ。これは、普通のゲームでもよくあることですから、別に問題にゃいですにゃ? やろうと思えば、前回のラッキーカードを記憶させておいて、それとは違うカードが選ばれるようなプログラムも作れますけど、そこまでする必要はにゃいと思いますにゃ。1行だけですけど、この後、解説しますにゃ。読み飛ばしても大丈夫ですにゃ。
・「luckycard=Math.floor(Math.random()*7)+1;」
 1行だけにゃんですけど、ちょっと長いですから、内側から見ていきますにゃ。
・Math.random() 0以上1未満の乱数(でたらめな数)を作る命令ですにゃ。「0.0000000001」かもしれにゃいし、「0.9999999999」かもしれにゃいし、その間のどんな数にゃのかもわからにゃい、そういう数を作りますにゃ。
・Math.random()*7 その乱数に7を掛けますにゃ。そうすると、答として0以上7未満の乱数が出てきますにゃ。「0.0000000001」に7を掛けた「0.0000000007」かもしれにゃいし、「0.9999…」に7を掛けた「6.9999…」かもしれにゃいし、その間のどんな数にゃのかもわからにゃい数になりますにゃ。
・Math.floor(Math.random()*7) その数の小数点以下を切り捨てますにゃ。その答は…
 ・「0.0000…」〜「0.9999…」の数は、小数点以下を切り捨てると0になりますにゃ。
 ・「1.0000…」〜「1.9999…」の数は、小数点以下を切り捨てると1になりますにゃ。
 ・おなじことですから途中は省略して、「6.0000…」〜「6.9999…」までの数は6になりますにゃ。
 難しそうに聞こえるかもしれにゃいんですけど、ようするに、この時点でで「0〜6までのでたらめな整数」ができたってことですにゃ。
・Math.floor(Math.random()*7)+1 ラッキーカードは、数字の1〜7のどれかひとつを選ぶんでしたにゃ。つまり、ここで必要になってくるのは0〜6の整数じゃにゃくて1〜7の整数ですにゃ。それを求める方法は簡単で、その数に1を足すだけですにゃ。
・luckycard=Math.floor(Math.random()*7)+1; 最後にできた数(1〜7までのでたらめな整数)を変数luckycardの値として代入しますにゃ。こんな感じで好きな範囲の乱数を作る方法は、いろんなゲームを作る時によく使いますから、詳しく説明しましたにゃ。

 次に、選択したゲームモードによって2枚の追加カードが変わるという処理を作ってみますにゃ。「function main()」の中にちょっと書き足すだけですにゃ。こういうものができると思いますにゃ。

// メイン
function main()
{
 player[0]=document.hyoji.player1.value*1;
 player[1]=document.hyoji.player2.value*1;
 gametype=document.hyoji.gmt.value*1;
 lcrule=document.hyoji.lc.value*1;
 for (i=0;i<=6;i++)
 {
  for (j=0;j<=3;j++)
  {
   p[i*4+j]=i+1;
  }
 }
 p[28]=8;p[29]=8;
 if (gametype==1) {p[28]=9;p[29]=9;}
 if (gametype==2) {p[28]=8;p[29]=9;}
 if (gametype==3)
 {
  for (i=0;i<=2;i++)
  {
   r1=Math.floor(Math.random()*7)+1;
   r2=Math.floor(Math.random()*7)+1;
   if (r1==r2) {i=0;}
   else {p[28]=r1;p[29]=r2;break;}
  }
 }
 h=carddataput();
(※他の部分はおなじですから省略しますにゃ)

 ゲームタイプを選択して「開始」をクリックしてみてくださいにゃ。最後の2枚(灰色の部屋の枠内の右下角とその隣の数字)が変わりますにゃ。「8」は数字の0のカード、「9」はくるくるカードで、ゲームタイプDを選択すると(たまたま前回とおなじ2枚が選ばれてしまった場合を除いて)「開始」をクリックするたびに2つの数字が変わりますにゃ。そして、その2つがおなじ数字になることはにゃいってことを確認してくださいにゃ。この後、解説するんですけど、読み飛ばしても大丈夫ですにゃ。
・「if (gametype==1) {p[28]=9;p[29]=9;}」
 変数gametypeの値が1であるなら、配列pの28番目の要素の値を「9」に、配列pの29番目の要素の値も「9」にしますにゃ。わかりやすく言うと、ゲームタイプBを選択したら、2枚の追加カードは2枚とも「くるくるカード」にするってことですにゃ。
・「if (gametype==2) {p[28]=8;p[29]=9;}」
 変数gametypeの値が2であるなら、配列pの28番目の要素の値を「8」に、配列pの29番目の要素の値を「9」にしますにゃ。さっきとほとんどおなじに見えますけど、意味はがらっと変わりますにゃ。わかりやすく言うと、ゲームタイプCを選択したら、2枚の追加カードのうち1枚は数字の0、もう1枚はくるくるカードになって、ゲームが終わる時に絶対に揃わにゃい2枚のカードが場に残ることになりますにゃ。
・「if (gametype==3)」「{」「}」
 変数gametypeの値が3であるなら、{ }の中の命令を実行しますにゃ。つまり、ゲームタイプDを選択した時は、1行では書けにゃい処理をしにゃいといけにゃいってことですにゃ。この場合、else以下は必要ありませんから省略していますにゃ。
・「for (i=0;i<=2;i++)」「{」「}」
 何回も出てきた繰り返し処理(iループ)にゃんですけど、今回は特別な使い方をしますにゃ。繰り返し回数は3回ではありませんにゃ。
・「r1=Math.floor(Math.random()*7)+1;」
 ラッキーカードの時にも出てきた乱数ですにゃ。1〜7までのでたらめな整数を作って、それを変数r1の値にしますにゃ。
・「r2=Math.floor(Math.random()*7)+1;」
 おなじように、1から7までのでたらめな整数を作って、それを変数r2の値にしますにゃ。r1もr2も1〜7までのでたらめな整数ですから、7分の1の確率で変数r1と変数r2の値はおなじになりますにゃ。でも、ゲームタイプDは最後に揃わにゃい2枚が残るルールですから、それでは困りますにゃ。そこで、繰り返し処理の特別な使い方をして、それを回避しますにゃ。
・「if (r1==r2) {i=0;}」
 もし、変数r1と変数r2の値がおなじであるなら、変数iの値を0にしますにゃ。変数iは繰り返し回数を数えて繰り返し処理が終わるかどうかを決めるための変数ですにゃ。それを0にするっていうのはどういうことにゃのか…今回は「ifの条件にあてはまる時に」それをやってるんですけど、ifの条件に関係にゃくただ単にiループの中で「i=0;」を実行するとどうなるのか説明しますにゃ。
・普通に繰り返し処理をおこなう場合、変数iの値はそのたびに増えていって、条件から外れる(この場合は3になる)と繰り返しが終わりますにゃ。
・繰り返し処理の中で変数iの値を0にすると、その繰り返しが終わった時点で変数iの値は1増えて1になりますにゃ。
・また繰り返し処理をするんですけど、そこでもまた変数iの値は0になって、その繰り返しが終わった時点で変数iの値は1増えて1になりますにゃ。
・また繰り返し処理をして、変数iの値は0になって、1増えて1になって、また繰り返し処理をして、変数iの値は0に…つまり、この繰り返しはいつまでたっても終わりませんにゃ。このような繰り返し処理のことを無限ループと言って、これはプログラムを組むのに失敗した時に起こるバグですにゃ。でも、それを意図的に起こさせることで、今回のように都合の悪い状況を避けることができるんですにゃ。
※わたしはこれを一種の高等テクニックとして使っていますけど、こういう使い方を嫌う人もいて、そのあたりは賛否両論あるところですにゃ。
 話を元に戻して、この命令は「変数r1と変数r2の値がおなじであるなら…」ですから、実際には無限ループにはなりませんにゃ。
・「else」「{」「}」
 話がまとまったように見せかけて、実はさっきの「if」はまだ続いていましたにゃ。elseはifの条件に関して「そうでにゃい場合は」っていう意味ですから、ここでは変数r1とr2の値が違う場合に{ }の中の命令を実行しますにゃ。
・「p[28]=r1;p[29]=r2;」
 配列pの28番目の要素として変数r1の値(1から7までのでたらめな整数)を、配列pの29番目の要素として変数r2の値(さっきとおなじ)を代入しますにゃ。わかりやすく言うと2枚の追加カードを1〜7までのどれかにするってことですにゃ。そして、配列pの28番目と29番目の要素がおなじになることはにゃい(2枚の追加カードがおなじ数字になることはにゃい)っていうのは、もう説明しにゃくてもわかりますにゃ? 今までやってきたのは、そういうことですにゃ。
・「break;」
 繰り返し処理の中でこの命令を実行すると、繰り返し回数や条件に関係にゃくその繰り返し処理を強制的に終わらせてループから抜けることになりますにゃ。ようするに、変数r1と変数r2の値が違う(=数字の違う2枚の追加カードが決まった)時に、繰り返し処理を終わらせるってことですにゃ。
 ここまでの話をまとめると、繰り返し処理の特別な使い方をして、2枚の追加カードの数字を決めて、その2枚のカードの数字がおなじだと困るから、ちょっと強引な方法で数字の違う2枚のカードを作った、こういうことですにゃ。ちょっとわかりにくいかもしれにゃいんですけど、わかんにゃくても大丈夫ですにゃ。

 2枚の追加カードが決まって、ゲームで使う30枚のカードが決まったら、次に、場に並べる30枚のカードをシャッフルしますにゃ。一般的なトランプのシャッフルは、積み重ねられた全部のカードの中から何枚かを抜き取って、それをカードの一番上に置いて、また何枚かを抜き取って、それをカードの一番上に置いて…っていうことを繰り返すんですけど、プログラミングでカードをシャッフルする時はそれとは全然違う方法を使いますにゃ。本当は「function main()」の中にちょっと書き足すだけで作れるんですけど、都合により、新しい関数を作って呼び出すという形にしますにゃ。こういうものができると思いますにゃ。

// メイン
function main()
{
※関係にゃい部分はちょっと省略しますにゃ
 if (gametype==3)
 {
  for (i=0;i<=2;i++)
  {
   r1=Math.floor(Math.random()*7)+1;
   r2=Math.floor(Math.random()*7)+1;
   if (r1==r2) {i=0;}
   else {p[28]=r1;p[29]=r2;break;}
  }
 }
 h=cardmix();
 h=carddataput();
※関係にゃい部分はちょっと省略しますにゃ
}

// カードシャッフル
function cardmix()
{
 for (i=0;i<=999;i++)
 {
  r1=Math.floor(Math.random()*30);
  r2=Math.floor(Math.random()*30);
  a=p[r1];p[r1]=p[r2];p[r2]=a;
 }
 for (i=0;i<=29;i++)
 {
  if (p[i]==0) {a=0;} else {a=10;}
  h=charaput(i,a);
 }
}

// マウスクリック処理
(※他の部分はおなじですから省略しますにゃ)

 「開始」をクリックするたびにカードがシャッフルされているということが、おわかりいただけると思いますにゃ。ちなみに、いくつにゃのか見当も付きませんけど、天文学的な数字分の1の確率で、まったくシャッフルされていにゃい(最初の並びとまったくおなじ)になるということが、いちおうあり得るんですけど、そんな細かいことは無視しますにゃ。この後、今までとおなじように解説するんですけど、今までとおなじように読み飛ばしても問題ありませんにゃ。
・「h=cardmix();」
 関数cardmixを呼び出して実行しますにゃ。後で説明しますけど、その関数の中でカードをシャッフルしますにゃ。「h=」は便宜上付けているだけで意味はありませんにゃ。今までとおなじにゃんですけど、大事なのはこの命令を書く場所で、「h=carddataput();」より前(画面上の見た目は上)に書く必要がありますにゃ。コンピュータは、基本的に書いてある順番通りに命令を実行しますにゃ。ここでは「カードをシャッフルするための関数を呼び出して実行」して、その次に「カードの中身を表示させるための関数を呼び出して実行」することになりますから、にゃにも問題ありませんにゃ。でも、この順番を逆にしてしまうと、先にカードの中身を表示して、その後でカードをシャッフルするになって、表示されるカードの中身はシャッフルする前の状態になってしまいますにゃ。
・「function cardmix()」「{」「}」
 cardmixという名前の関数の始まりと終わりですにゃ。この関数が呼び出された時に、カードをシャッフルしますにゃ。ここでは必要にゃいんですけど、諸々の事情で、画面上にカードの裏の画像も表示しますにゃ。
・「for (i=0;i<=999;i++)」「{」「}」
 繰り返し処理(iループ)の始まりと終わりですにゃ。今回は、ただ単に1000回繰り返すというだけで、変数iに意味はありませんにゃ。
・「r1=Math.floor(Math.random()*30);」
 変数r1の値を0〜29までのでたらめな整数にしますにゃ。これは配列pの要素の配列番号(何番目の要素にゃのかを表す数値)、言い換えると何枚目のカードにゃのかを表す数値ですから、カードの枚数とおなじ30通りの数で、0から始まる数になりますから、最後に「+1」は付けませんにゃ。
・「r2=Math.floor(Math.random()*30);」
 さっきとおなじですから、説明は省略しますにゃ。これで、0〜29までのでたらめな整数が2つできましたにゃ。
・「a=p[r1];p[r1]=p[r2];p[r2]=a;」
 この行の命令で、配列pのr1番目の要素と配列pのr2番目の要素を入れ替えるということをしていますにゃ。それなら「p[r1]=p[r2];p[r2]=p[r1];」と書けばいいんじゃにゃいかって思った人、鋭い感覚にゃんですけど、それだとちょっと困ったことになりますにゃ。まずはそっちの残念な書き方から説明しますにゃ。
・p[r1]=p[r2]; 配列pのr1番目の要素として、配列pのr2番目の要素を代入しますにゃ。ここまでは問題ありませんにゃ。
・p[r2]=p[r1]; 配列pのr2番目の要素として、配列pのr1番目の要素を代入するんですけど、さっきの命令が実行された後ですから、この時点で配列pのr1番目の要素はもう書き換えられていますにゃ。その書き換えられた数値を代入することになりますから、これだと両方ともおなじ数値になってしまいますにゃ。それを避けるために…
・a=p[r1]; 変数a(特に意味のにゃい汎用変数)に配列pのr1番目の要素を代入しますにゃ。わかりやすく言うと書き換えられてしまう前に元の数値を覚えさせるってことですにゃ。
・p[r1]=p[r2]; 配列pのr1番目の要素として、配列pのr2番目の要素を代入しますにゃ。これは、にゃにも問題ありませんにゃ。この時点で配列pのr1番目の要素は書き換えられてしまったんですけど…
・p[r2]=a; ここで、さっきの変数aが役に立つんですにゃ。配列pのr1番目の要素の書き換えられる前の値(=変数aの値)を配列pのr2番目の要素として代入しますにゃ。
 ここまでの話をまとめると、「でたらめな整数を作ることで1枚目のカードを決めて」「おなじく2枚目のカードを決めて」「その2枚のカードの中身を入れ替える」っていうことを1回だけ実行していますにゃ。でも、この命令はループの中にあるから実際には1000回繰り返し実行されることになりますにゃ。2枚のカードを入れ替えるだけの命令でも、1000回も繰り返し実行したら、元のカードの並びは見る影もにゃいくらいばらばらで、その結果は「開始」をクリックするとわかりますにゃ。ここで、2つの疑問が出てきますにゃ。
・30分の1の確率で、変数r1と変数r2の値がおなじになって、実質的にカードの中身の入れ替えが実行されにゃい(命令としては実行されるものの、おなじ配列番号を指定しているので意味がにゃい)
・一定の確率で、直前に入れ替えた2枚のカードとおなじ2枚のカードを入れ替えることになって、実質的にカードの中身の入れ替えが実行されにゃい(おなじ命令が2回続けて実行されるため、入れ替えられた中身が元に戻ってしまう)
 これらのことを避けるために、プログラムを書き加えるということは、やろうと思えばできますにゃ。でも、1000回も繰り返すんですから、仮にそういうことが100回あったとしても、残りの900回でカードはばらばらになりますから、細かいことは無視しますにゃ。

 さて、いよいよ今回のテーマ「カードをめくってみる」の処理を作りますにゃ。また長い話が始まるのかって思ってるかもしれませんけど、安心してくださいにゃ。function pic(n)の中にちょっと書き加えるだけでできますにゃ。こういうものができると思いますにゃ。

// マウスクリック処理
function pic(n)
{
 c=""+n+"番目のカードをクリックしましたにゃ";
 document.hyoji.h1.value=c;
 if (flg==1)
 {
  h=charaput(n,p[n]);
 }
}
(※他の部分はおなじですから省略しますにゃ)

 今までと違ってただカードの画像をクリックしただけでは何も起こりませんにゃ。関数mainが実行されるまで、カードの中身はまだ決まっていませんから、その前に表示させようとすると困ったことになるからですにゃ。「開始」をクリックしてから、カード(ラッキーカード以外)をクリックしてみてくださいにゃ。カードの表の画像が表示されますにゃ。そして、そのカードの数字は灰色の部分の枠内に表示された数字とおなじ(0とくるくるカードを除く)ってことを確認してくださいにゃ。実質2行しかにゃいんですけど、解説しますにゃ。読み飛ばしても大丈夫ですにゃ。
・「c=""+n+"番目のカードをクリックしましたにゃ";」「document.hyoji.h1.value=c;」
 カードの画像を見ればわかることですから、消しますにゃ。
・「if (flg==1)」「{」「}」
 変数flgの値が1なら{ }の中の命令を実行しますにゃ。本当は、命令がひとつしかにゃい時は{ }で囲む必要がにゃいんですけど、わかりやすくするために付けていますにゃ。「変数flgの値が1なら」という条件を付けることで、カードの中身が決まる前に表示させようとしてしまうことを回避していますにゃ。
・「h=charaput(n,p[n]);」
 関数charaputを呼び出して実行しますにゃ。その時に、第1引数として変数nの値(=クリックしたカードの番号)を、第2引数として配列pのn番目の要素(=クリックしたカードの中身)を渡しますにゃ。呼び出された関数charaputは、受け取った2つの値を使って、n番目のカードとしてp[n]番目の画像を表示(わかりやすく言うと)クリックしたカードの場所にそのカードの画像を表示しますにゃ。実際に神経衰弱で遊ぶ時にする「カードを裏返す」という動作を、プログラム内では「表示する画像を差し替える」ことで、カードが裏表になったように見せかけているんですにゃ。ちなみに、見た目はわからにゃいんですけど、すでにめくられたカードをもう1回クリックした時にもプログラム内ではおなじ命令を実行しておなじ画像を表示していますにゃ。これは、プログラムを書き加えて回避しようと思えばできるんですけど、このままで何の問題もにゃいから無視しますにゃ。

次へ進む  [目次]  [STEP0]  [STEP1]  [STEP2]  [STEP3]  [STEP4]  [STEP5]  [STEP6]  [STEP7]  [終わりに]