3. 3桁数当てゲーム(Hit&Blow)

ランダムに3桁の数字を作成しヒントを元にその数を当てるゲーム。
うちの学校でちょっと流行っていたので作成してみました。

[実行結果]

入力

答えつき版 チェック用

いろいろ機能付き版

ゲーム説明&やり方

[プログラム]

<script type="text/javascript" charset="Shift_JIS">

/* 変数宣言 */
   var ReOut="";      //HitとBlowの出力
   var Ransu;         //乱数を入れる配列変数
   var HitTarget;     //3桁の数値の答え
   var Input;         //入力された数値
   var Hit;           //Hitの数
   var Blow;          //Blowの数
   var InputNumber;   //入力された数値から一文字抜き出す
   var InputNumberSe; //3桁の数値の答えから一文字抜き出す(Blow用)
   var HitNumber;     //3桁の数値の答えから一文字抜き出す(Hit用)

   LuckyNumber();     //関数LuckyNumber()の実行

/* 答えの数値を作成する関数 */
   function LuckyNumber(){
      Ransu=new Array(3);     //答えを入れる配列を宣言

      for(Q=0;Q<3;Q++)     //乱数作成のループ
      {
         Ransu[Q]=Math.floor(Math.random() * 10)+"";    //乱数を作成
                                                        //↓同じ数値がないか、0があるか判断
         if(Ransu[Q] == "0" || Ransu[Q] == Ransu[Q-1] || Ransu[Q] == Ransu[Q-2]){
            Q--;                                        //ループを再び繰り返す
         }
      }

      HitTarget=Ransu[0]+Ransu[1]+Ransu[2];             //変数HitTarget に作成した3つの乱数をいれる
   }

/* Inputの入力チェックをする関数 */
   function NumberInput(){
      Input=document.form1.formInput.value;

      if(Input == "" || Input.match(/\D/i)){    //Inputが空か、または数字意外が入っているか判断
         alert("数値を入力してください");
         document.form1.formInput.value="";
      }else if(Input.length != 3){              //Inputに3文字入っているか判断
         lert("数値を3つ入力してください");
         document.form1.formInput.value="";
      }else if(Input.indexOf("0") != -1){       //Inputに0が入っているか判断
         alert("0を入力しないで下さい");
         document.form1.formInput.value="";
      }else if(Input.match(/1{2,}|2{2,}|3{2,}|4{2,}|5{2,}|6{2,}|7{2,}|8{2,}|9{2,}/)){
         alert("同じ数値を入力しないで下さい"); //↑同じ数値があるかを判断
         document.form1.formInput.value="";
      }else{
         Jikkou();                              //どの条件にも引っかからなければ実行
      }
   }

/* Inputと答えの数値を比較する関数 */
   function Jikkou(){
      Hit=0;                                //変数Hitを0に戻す(設定)
      Blow=0;                               //変数Blowを0に戻す(設定)

      for(i=0;i<3;i++)                   //Hitをだすループ
      {
         HitNumber=HitTarget.charAt(i);     //変数HitNumberに答えの数値から一文字抜き出して入れる
         InputNumber=Input.charAt(i)        //変数InputNumberにInputから一文字抜き出して入れる

         if(HitNumber == InputNumber){      //HitNumberとInputNumberが同じか判断 (Hitの判断)
            Hit++;                          //Hitに 1 を足す
            continue;                       //ループをスキップ
         }

         for(i2=0;i2<3;i2++)             //Blowをだすループ
         {
            InputNumberSe=Input.charAt(i2); //変数InputNumberSeにInputから一文字抜き出して入れる

            if(HitNumber == InputNumberSe){ //HitNumberとInputNumberSeが同じか判断 (Blowの判断)
               Blow++;                      //Blowに 1 をたす
            }
         }
      }

      ReOut=(Input+" ["+Hit+"Hit "+Blow+"Blow]\n")+ReOut; //変数ReOutに入力した数値とHitとBlowをいれる
      document.form1.formReOut.value=ReOut;               //変数ReOutを出力
      document.form1.formInput.value="";                  //Input のフォームを空にする

      if(Hit == 3){      //3Hitしたか判断
         alert("3Hit");  //3Hitをアラームに出力
         LuckyNumber();  //次のゲームのために新しい答え作成
         ReOut="";       //次のゲームのためにReOutを空にする
      }
   }


   function AllReset(){ //ゲームをリセットする関数
      LuckyNumber();    //新しい答えを作成
      ReOut="";         //ReOutを空にする
      document.form1.formReOut.value=""; //出力フォームを空にする
   }
</script>

<form name="form1">
   <p>
      入力<input type="text" size="3" name="formInput" value="" />                   //Inputフォーム
      <input type="button" name="formStart" value="Input" onClick="NumberInput()" /> //関数NumberInput()を実行
      <input type="button" value="Reset" onClick="AllReset()" />                     //リセットをかける
   </p>

   <textarea name="formReOut" cols="17" rows="15"></textarea>                     //出力フォーム
</form>

[解説]

プログラムを見る限りややこしそうですね。 そんなに複雑なことをやっていないので理解は出来てもらえると思います。

というわけでいつものとおりプログラムの整理をしましょう。

  1. 答えの数値の作成
  2. 数値の入力チェック
  3. HitかBlowの判断
  4. 3の結果を出力

コレが簡単なプログラムの流れです。

次はもう少し細かいプログラムの流れを。

  1. 答えの数値の作成(乱数を使って作成)
    1. 答えの数値の作成
      1. 同じ数値は作成されていないか?
      2. 0は入っていないか?
  2. 数値の入力チェック
    1. フォームが空でないか?
    2. 数値以外の字が入っていないか?
    3. 3文字入力されているか?
    4. 0は入力されていないか?
    5. 同じ数字は入力されていないか?
      • 上記の判断に引っかかれば警告を出し入力フォームを空にする
  3. HitかBlowの判断
    1. 同じ場所に同じ数値が入っている(Hit)
      1. 変数Hitに1をプラス
      2. Blowの判断をスキップ
    2. 場所は違うが数値が同じ(Blow)
      1. 変数Blowに1をプラス
  4. 3の結果を出力
    1. 3Hitしていない
      • 出力フォームに入力した数値,Hit,Blowを出力し 数値の入力チェック に戻る
    2. 3Hitしている
      1. 3Hit を警告アラームで出力
      2. 新しい答えを作成する
      3. 全ての変数を初期化する

これがすべてのプログラムの流れです。 これらをプログラム化すると上のような長めのプログラムの出来上がりな分けです。

1.答えの数値の作成(乱数を使って作成)

1-1.答えの数値の作成

答えの3桁の数字は乱数を使って作成します。 乱数はMathメソッドの中のMath.random()を使います。

Math.random()
1未満0以上の乱数を作成する命令 0.****〜 の数値が発生します。

今回使うのは0以上10以下の整数なので Math.random() に10をかけます。 コレだけだと*.***になるので小数点以下を切り捨てなければなりません。 そこで使うのがMath.floor()です、これを使って少数点以下を切り捨てます。

それが下のようになります。
Math.floor(Math.random() * 10)
これで0以上10以下の整数の出来上がりです。

この作業を3回繰り返して毎回違う3桁の数字を作成します。 これを行うには単純に for文 を使います。

これらの数値を格納するには変数ではなく配列を使用します。 今回の場合は配列を使った方が余計な変数を使わずにすむし、操作しやすいためです。 今回はRansuという配列を作成します。

Ransu[Q]=Math.floor(Math.random() * 10)+"";
というようにRansu[x]の中に数値を入れていきます、Qは for文 で増えていく数値です。

1-1-1.同じ数値は作成されていないか? 1-1-2.0は入っていないか?

このゲームのルール上ゾロ目がないことと0がないことが必須となります。 そのため同じ数が出た場合はもう一度その桁だけを作り直します。 一番最後についている+""は文字列に変換するためについています。

それを行っているのが下の if文 とその処理です。

if(Ransu[Q] == "0" || Ransu[Q] == Ransu[Q-1] || Ransu[Q] == Ransu[Q-2]){
   Q--;
};

Check1 上記の判断に引っかかればもう一回作成

Ransu[Q] == Ransu[Q-1]Ransu[Q] == Ransu[Q-2] というのは今作った数字と前回作った数と前々回作った数値が同じなのかを判断しています。 もし同じならもう一回ループさせるためにQ--;をしてループ回数を一回増やしています。

HitTarget=Ransu[0]+Ransu[1]+Ransu[2]; これで作成した数値を全て合わせて3桁の数値にしています。 数値を文字列に変換したのは作成された数値を文字列に変換してないとただの数値の足し算になってしまうからです。

小技1

数値等を文字列に変換するにはString(xxx)がありますが わざわざこれを使わなくとも+""を使うと簡単に文字列に返還できるのです。
A=(123+"")+(456+"");
A=String(123)+String(456);
上の2つは全く同じことです。Aには「123456」が入ります。

同じように数値に変化するのもあります、Number(xxx)という数値変換する命令がありますが -0とすることで数値に変換が出来ます。
A=A=("123"-0)+("456"-0);
A=Number(123)+Number(456);
上の2つは同じ事をしており、Aには 579 が入ります。

2.数値の入力チェック

2-1.フォームが空でないか?

最初の入力チェックではフォーム内が空でないか、フォームに数字以外の文字が入っていないかをチェックしています。 それが Input == "" || Input.match(/\D/i) です。

2-2.数値以外の字が入っていないか?

if(Input.match(/\D/i))
は、正規表現というものです。これで数値以外の文字が入っているかを判断します。

正規表現についてはボクもまだよく理解していないので他のサイトを回ってください(^_^;)。 正規表現検索

2-3.3文字入力されているか?

if(Input.length != 3)
でフォーム内に3文字入っているかを判断します。 Input.lengthというのはInputの文字数を返します。(***.length参照)

2-4.0は入力されていないか?

if(Input.indexOf("0") != -1)
フォーム内に0が入力されているかを判断しています。

***.indexOf(x)
***の中からからxを検索します。見つかった場合はxの場所を(0〜)、見つからなかった場合は -1 を返します。 "12345".indexOf(4) では 3 となります。
2-5.同じ数字は入力されていないか?

if(Input.match(/1{2,}|2{2,}|3{2,}|4{2,}|5{2,}|6{2,}|7{2,}|8{2,}|9{2,}/))
で、同じ数値が入力されていないかを判断します、コレも正規表現です。 たぶんこんな使い方をしなくとももっとスマートな正規表現があるでしょうけど、未熟者であるため分かりません(・・;)

これらの条件に引っかからなければ次の 「3.HitかBlowの判断」 へいけるのです。

3.HitかBlowの判断

まずは答えの数値からと入力された数値から一文字ずつ抜き出して両者を判断します。 抜き出すには charAt(x) を使います。

3-1.同じ場所に同じ数値が入っている(Hit)

HitNumber=HitTarget.charAt(i);答えから i 番目の数値を抜き出します。
InputNumber=Input.charAt(i) 入力さら数値から i 番目の数値を抜き出します。
※i 番目の数値というのは for文 で増えていく変数。

この二つが同じということは数値も同じで場所も同じということになり Hit になります。 つまり if(HitNumber == InputNumber) です。

ここで行う処理は Hit している数を格納しておく 変数Hit にプラス1をすることです。 次に Hit しているのでこの数値での Blow はありえないのでコレ以下の処理を時間短縮のためスキップします。 それが continue です。

continue
ループ内で残りの処理をスキップして次のループを実行させる命令。
3-2.場所は違うが数値が同じ(Blow)

次は Blow の判断です。 Blow は場所が違って一致している場合なので入力された文字から次の文字(前の文字)を抜き出して判断します。 この処理を行うためループは入れ子になっています。

InputNumberSe=Input.charAt(i2)入力さら数値から i2 番目の数値を抜き出します。
※i 番目の数値というのは for文 で増えていく変数。

Hit と同じように if(HitNumber == InputNumberSe) で両者を判断します。

そして Hit のときと同じように HitNumberInputNumberSe を判断します。 これだと場所も同じで数値も同じ場合もありえますが Hit していればが外側のループのHitの判断で 引っかかっているので Hit にカウントはされません。なので場所は違うが数字があっている場合になるのです。

4.3の結果を出力

4-1.3Hitしていない

ReOut=(Input+" ["+Hit+"Hit "+Blow+"Blow]\n")+ReOut;
で入力された文字、Hit数、Blow数を 変数ReOut へ書き込みます。 最後に+ReOutがついているのはログを出力していくためです。

この中に\nというのが入っていますがコレは改行コードです。 これを使うことで改行を行うことが出来ます。

document.form1.formReOut.value=ReOut;
入力された文字等が入った 変数ReOut を出力フォームへ出力します。

document.form1.formReOut.value=ReOut;
次入力しやすいように入力フォームを空にしておきます。

4-2.3Hitしている

if(Hit == 3)
最後に 3Hit した場合の処理を行います。 といっても単純に警告アラームで 3Hit と表示するだけですが・・・ 見た目ではここだけの処理ですが裏では別の処理を行います。

LuckyNumber();
次のゲームのために再び新しい答えを作成しておきます。

ReOut="";
これも同じように次のゲームのために今までの入っていた入力されていた数値等の情報を消します。

リセット処理

ゲームが途中で中断できないのは悲しいのでリセット機能をつけましょう。

リセットは単純に全ての変数を初期化するだけです。やっている処理 3Hit と全く同じです。 違うのは出力フォーム内を空にすることだけです。空にしないとリセットした感じがしないからね(笑

コレの関数化したのが function AllReset() です。

ちょっと一言

コレで解説終わりです。 かなり疲れました、やっぱ長いプログラムの解説はしんどいですわ。

だいたいこんな長いプログラムの解説を読む人がいるか不明。 やっぱ長いプログラムの解説は止めましょうかね・・・

今回はちょっと解説の仕方を変えてみました。 最初から要点だけをリンクして飛ばすようにしてみました。 これが好評ならばこの解説の仕方を続けていく予定です。

機能付き版&ゲーム説明,やり方

いろいろ機能付き版 プレビュー

いろいろ機能付き版 ソース

いろいろ機能とついていますが具体的な違いは

  • 0をいれるか入れないかの選択
  • 何回ゲームをやるかの選択
  • 1ゲーム何秒で出来るかの時計機能
  • ゲームをトータル何秒で出来るかの時計機能
  • 1ゲーム何回で当てれるかのカウントアップ機能
  • ゲームをトータル何回でカウントアップ機能
  • GiveUp機能
という機能がついています。

3ゲームや5ゲームをいかに早く、いかに少ない間違いで終えれるかを競っても面白いかと。
自分の最短記録は
[3回 0無し TotalTime 56秒 TotalMiss15回]
[5回 0無し TotalTime152秒 TotalMiss31回]
でした。 記録を更新したり早い記録が出たりしたら掲示板にでも記録報告してください。

ゲーム説明

コンピューターがランダム(ゾロ目と0はなし)に決めた数値をヒント(HitとBlow)をもとに当てるゲーム。

Hitは場所も数値も一致している場合。1Hitなら一箇所、数値も場所も一致している。
Blowは場所は違うが数値は一致している場合。1Blowは一箇所、数値が一致している。

数値と場所の関係を推理して、 いかに早く少ないミスで終わらせるかがこのゲームの目的。

やり方

[264]が答えだとする。
[123]を入れると 0Hit 1Blow と表示される。
[456]を入れると 0Hit 2Blow 。
この時点で数値は3つ出てきたため、[789]の可能性はない。
[123]からひとつ、[456]からふたつづ抜き出しいけばいいのだ。
[265]と入れて 2Hit 0Blow、なので(2と6)(2と5)(6と5)の組み合わせがあっている
[254]と入れる 2Hit 0Blow、この時点で 2 は確実にHitしていることが分かる。
[246]と入れる 1Hit 2Blow、3つの数値が出てきたので後は並び替えるだけ。
2 は場所は確定しているので 4 と 6 を入れ替える。
[264]と入れて 3Hit 0Blow となりコレでゲーム終了である。


© 2000-2003 Tsukimi / HobbySpace