次へ: 7 最後に 上へ: AWK による数合てゲームの作成 前へ: 5.2 入力判定関数 (PDF ファイル: awk3.pdf)


6 ソースコード全体

あとは、出力用に、配列を文字列に直す以下のような関数を用意しておきます。
  function array2str(N,array,   j,s)
  {
      s="" 
      for(j=1;j<=N;j++) s=s array[j]
      return s
  }
逆に、入力文字列を配列に直すのは、[2] で見たように split(str,input,"") でできます。

以上をまとめると以下のようになります。

  BEGIN{
      Maxloop=20      # 制限回数
      if(N=="") N=4   # 数字列の長さ
      if(L=="") L=10  # 使用する数字の個数
      Times=1         # 回数を記録する変数
      win=0
      # (1) 課題文を作成してゲーム開始
      printf "数当てゲームを開始します。\n"
      printf "使用する数は 0~%d, 数字列の長さは %d です。\n",L-1,N
      printf "判定結果の意味は以下の通りです。\n"
      printf "  H=数も位置も合ってる数\n"
      printf "  B=数のみ合ってる数\n"
      if(mkkadai(N,L,kadai)<0){ printf "Error.\n"; errorexit=1; exit }
      else
         printf "準備 OK です。%d 個の数の並びを入力してください。\n",N
      printf "[%d] ",Times
  }
  { ## ここはキーボードからの 1 行の入力が済んだ後に実行される
      # (2) 入力文字列を確認
      gsub(/[ \t]/,"")
      if(split($0,input,"")!=N || inputcheck(N,L,input)<0){
          printf "入力エラー\n"
          printf "[%d] ",Times   # 改めて入力をうながす
          next
      }
      # (3) 入力行と課題文とを比較
      ret=judgeinput(N,input,kadai)
      # (4) 違いがなければ終了し、
      #     違いがあれば判定結果を表示
      if(ret==1){ win=1; exit }
      else
        printf "%dH%dB\n",H,B
      
      if(Times>=Maxloop){ win=-1; exit }
      else printf "[%d] ",++Times
  }
  END{
      if(errorexit) exit
      # (5) 途中でやめた場合はそのような表示をする
      # (6) 正常終了した場合はそのような表示をする
      if(win==0) printf "途中であきらめましたね。\n"
      else if(win==-1) 
        printf "制限回数 (%d) 内に回答できませんでした。\n",Maxloop
      else printf "おめでとう。正解です。\n"
  
      printf "回数 %d、正解 %s\n",Times,array2str(N,kadai)
  }
  
  # 配列 --> 文字列
  function array2str(N,array,        j,s)
  {
      s="" 
      for(j=1;j<=N;j++) s=s array[j]
      return s
  }
  
  # 入力チェック関数
  function inputcheck(N,L,input,     j,k)
  {
      for(j=1;j<=N;j++) 
        if(input[j] !~ /^[0-9]$/ || input[j]>=L) return -1
      for(j=1;j<N;j++)
        for(k=j+1;k<=N;k++)
          if(input[j]==input[k]) return -2
      return 0
  }
  
  # 回答チェック
  # 一致したら 1, そうでなければ 0 を返す
  # B,H は大域変数としてそこに結果を代入する
  function judgeinput(N,input,kadai,        j,k,A)
  {
      H=0  # 数も場所も合っているものの個数
      A=0  # 数が合っている (H も含む) ものの個数
      for(j=1;j<=N;j++)
        for(k=1;k<=N;k++)
          if(input[k]==kadai[j]){
              A++
              if(j==k) H++
              break
          }
      B=A-H
      if(H>=N) return 1
      else return 0
  }
  
  # 課題の作成
  function mkkadai(N,L,kadai,     j,k,x,m,a,L1)
  {
      srand()
      for(j=0;j<L;j++) a[j]=0
      L1=L   # 使っていない数字の個数
      for(j=1;j<=N;j++){
          x=int(rand()*L1)+1   # 1 から L1 までの乱数
          m=0;
          for(k=0;k<L;k++){    # x 番目の使っていない数の検索
              if(a[k]==0) m++
              if(m==x) break
          }
          if(k>=L) return -1   # エラー
          if(a[k]!=0) return -2   # エラー
          a[k]=1
          kadai[j]=k    # j 番目の課題数字を k とする
          L1--
      }
      return 0
  }

ここでは一応 Maxloop($=20$) 回を制限回数とし、 これ以上かかったら強制終了としています。

また、最初の BEGIN ブロックで、$N$, $L$ を、

  if(N=="") N=4   # 数字列の長さ
  if(L=="") L=10  # 使用する数字の個数
としていますが、 これは実行時に $N$, $L$ を容易に変更できるようにするための仕組みです。 通常は、最初は $N$, $L$ の値は定義されていませんから、 空文字列として "" という値を取りますから、 それぞれデフォルトの 4, 10 が設定されますが、 awk のコマンドラインオプションで -v オプションを使って、
  awk -v N=3 -v L=5 -f kazuate.awk
のように変数の値の初期値を設定すれば、 スクリプトを書きかえずに変数の値を変更できます。

なお、同じように例えば Maxloop を変えようとして

  awk -v Maxloop=100 -f kazuate.awk
としても、$Maxloop$ は BEGIN ブロックで強制的に初期値が設定されますから、 このようにして変更することはできません。 awk の -v オプションで値を変えたい変数に対しては、 上の $N$, $L$ のように "" との比較をチェックするように 書いておく必要があります。


次へ: 7 最後に 上へ: AWK による数合てゲームの作成 前へ: 5.2 入力判定関数
竹野茂治@新潟工科大学
2006年6月8日