ヒットアンドブローを作る

ヒットアンドブローとは、何桁かの数字を入力しながら出題者が予め決めておいた数を当てる数当てパズルゲームです。

数字を入力してその数字の「桁」ごとに正解と合っている/合っていない、あるいはその数字が正解に「含まれる」という情報を基に、数を推測していきます。数字と桁両方合っていれば「ヒット」、桁は違ってもその数字自体が正解に含まれていれば「ブロー」になります。

例えば、答えが1234という4桁の数字だったとします。解答者は、最初はどんな数字なのかまったく情報がないので、適当な数字を入れる事になるはずです。

仮に、1111と入れたとするとこれは最初の1が桁・数字とも合っていますが、他は桁・数ともあっている部分はないので「1ヒット・0ブロー」になります。次に、1が含まれる事がわかったので、2167と入れたとしましょう。今度は、ヒットはなしですが正解に含まれる1と2が正解とは違う桁にあるので「0ヒット2ブロー」。

と、こんな感じで数字を絞り込んで行くわけです。ヒット/ブロ−の判定アルゴリズムをどうするか、またその情報をどんな形式で表示するか、という点がヒットアンドブローのプログラムを作る時のポイントになります。今回は、4桁の数字を当てるヒットアンドブローを作ってみましょう。4桁には0も含まれるので、0000−9999の数字を当てる事になります。

画面デザインと判定

画面デザインとしては、入力用に数字を入れるテキストフィールドに入力ボタン、リセットボタンを用意しましょう。テキストフィールドに数字を入れたら入力ボタンで回答、ゲームをクリアするか降参ならリセットボタンで新しいゲームを始めるわけです。
 ヒット・ブロー数の表示は、アプレット中央にテキストエリアを用意しました。

ヒット・ブローの判定は、数字を一桁ずつ比較して行くので、まず正解の数値と入力された数値を一桁ずつの数字に分けて配列に入れてしまいましょう。ある数値の最下位一桁(10進数)を取り出すには、10で割った余りをとり、数値を一桁下位方向にずらす(最下位一桁を切り捨てる)には10で割れば良いので、一桁ずつ配列に入れていく処理は以下のようになります。

  iS=iSeikai;
  iI=iInput;

  for (i=0;i<4;i++) { // 数値を一桁ずつ配列に分解

      iIn[i]=iI % 10; // 最下位一桁を配列に保存
      iSn[i]=iS % 10;

      iI/=10; // 最下位を切り捨て
      iS/=10;

  }

これで準備は出来たので、まずヒットの判定をしてみましょう。ヒットとは、「同じ桁に同じ数字がある」事でした。これは、配列の同じ位置(添え字)に同じ数字がある、という事に対応するので、次のような判定式で判定できます。

  for (i=0;i<4;i++)
      if (iSn[i]==iIn[i]) {
       ・
              ・
    ヒットの時の処理
       ・
       ・
      }

次はブロー。「どこかに同じ数字がある」という条件だけを見れば、入力された数字配列の中に正解の数字が含まれているか1桁ずつ調べて行く(ただし、同じ桁については調べない)という方法で良さそうですね。

このアルゴリズムを判定式にすると、正解から一桁取り出しそれを入力と一桁ずつ比較して行く二重ループになるでしょう。

  for (i=0;i<4;i++)
      for (j=0;j<4;j++)
          if (i!=j && iSn[i]==iIn[j]) {
         ・
                ・
      ブローの時の処理
         ・
         ・
          }

しかし、この単純なアルゴリズムだと正解や入力の中に同じ数字が複数あった場合にブロー判定がおかしくなります。例えば、正解が1123で入力された数字が4561だったとしましょう。この場合、「0ヒット1ブロー」となるはずです。

しかし、上の処理方式では毎回入力された数字の同じ位置以外にあるすべての桁と正解の一桁が比較され一致すればブローになるので、正解1桁目と2桁目の判定で共に入力4桁目と一致、ブローと判定され「2ブロー」になってしまうのです。

これを防ぐには、一度ブローと判定された桁はその後ブローにならないようにします。今回は、0−9の数字が出てくるので、ブローと判定された正解・入力双方の桁をそれ以外の数字にしてしまえば良いわけです。今回は、ブローになった場合その桁の数字を表す配列に正解の方はー1、入力の方はー2を入れるようにしました。

  for (i=0;i<4;i++)
      for (j=0;j<4;j++)
          if (i!=j && iSn[i]==iIn[j]) {

              iBrrow++;
              iSn[i]=-1;
              iIn[j]=-2;

          }

結果の表示とゲームの初期化

では、次に求めたヒット/ブロ−数を入力された数字、ターン数と共に表示する事にしましょう。

この表示は、中央のテキストエリアに1行ずつ、一番上の行に追加していく事にします。数字を入れると毎回その結果が一番上に表示され、ターンが進む毎に前の結果が下のほうにずれて行く、という感じですね。一番上の行はテキストエリアの文字列の先頭に当たるので、結果を文字列化したら最後に改行(\n)を追加しそれをテキストエリアの文字列の先頭に追加して行く事になります。

  String str=new String(); // 結果の文字列

  if (iTurn<10) // 桁を合わせてターン数と入力された数字を文字列化
      str=" "+String.valueOf(iTurn)+" "+sInput+" ";
  else
      str=String.valueOf(iTurn)+" "+sInput+" ";

  if (iHit==4) { // 正解ならゲーム終了

      str+="Clear!\n";

      btnGo.enable(false);

  } else // ヒット/ブロ−数を文字列化
      str+=String.valueOf(iHit)+" Hit/"+
        String.valueOf(iBrrow)+" Brrow\n";

  txtDisp.setText(str+txtDisp.getText()); // 文字列を先頭に追加

 ゲーム初期化は、乱数で正解を決めてテキストフィールドとテキストエリアをクリアすれば良いですね。

  private void initGame() {

      Random rnd=new Random();

      iSeikai=Math.abs(rnd.nextInt() % 10000);
      iTurn=1;

      txtDisp.setText("");
      txtInput.setText("");
      btnGo.enable(true);

  }

ヒットアンドブローJavaアプレット

テキストフィールドに4桁の数字を入れてG oボタンをクリックすると判定結果が表示されます。正解すると、テキストエリアにClear!と表示されるので、Newボタンで再挑戦してください。

プログラムソース表示

今回のプログラムは、ヒットアンドブローの最低限の「流れ」のみを実装したスケルトンです。画面効果を加えたり、ヒントを出す機能を追加したりとプログラムに「遊び」を追加してみてください。


プログラミング資料庫 > プログラム工房