Thinkers'Studio
JavaとC言語の自習ツール
数字当てゲーム ヒットアンドブロー

 数字当てゲーム(Hit and Blow)は、基本情報技術者試験のC言語問題に出題されたことがあります。

数字当てゲームのルール

  ・ コンピュータが生成した各桁が異なる4桁の数(目標数)を当てる。
  ・ 推測数を入力すると次のようなヒントが出され、それを元に次の推測をする。当たるまでこれを繰り返す。
    Hit:桁も数字も一致している数字の個数、Blow:桁は違うが含まれる数字の個数
       例) 目標数 1632 に対して 7613 は「 1Hit, 2Blow 」
  ※ 問題原本(平成18年度 基本情報秋期試験 問6) は 過去問ページ でご覧になれます

数字当てゲームのプログラム 】 (H18年基本情報秋期試験 問6より)
 問題文の空欄に正解を埋め、足りない処理を追加して実行できるようにしました。
 それ以外は原文のまま載せています。DIGITS定義を変える場合、変更が必要な部分にコメントをつけました。
#include <stdio.h>
#define DIGITS 4   /* けた数 */
#define TRUE  1
#define FALSE 0

void createRandomNumber(char[]);
int isValidNumber(char[]);
void GuessNumber();
int isMatch(char[], char[]);

void GuessNumber(){
    char target[DIGITS + 1];   /* 目標数の保存領域 */
    char num[DIGITS + 1];      /* 推測数の保存領域 */
    int count = 0;             /* 推測回数 */

    createRandomNumber(target);   /* 目標数の生成 */
    do{
        printf("[%d回目] 各けたが異なる%dけたの数を入力してください:",
                                                       ++count, DIGITS);
        scanf("%4s", num);  /* DIGITS桁読み込み */
        while( isValidNumber(num) == FALSE ){
            printf("入力が正しくありません。再度入力してください:");
            scanf("%4s", num);  /* DIGITS桁読み込み */
        }
    }while( isMatch(target, num) == FALSE );
}

int isMatch(char target[], char num[]){
    int i, j, numHit = 0, numBlow = 0;

    for(i = 0; i < DIGITS; i++){
        for(j = 0; j < DIGITS; j++){
            if(target[i] == num[j]){
                if(i == j){
                    numHit++;
                } else {
                    numBlow++;
                }
            }
        }
    }
    if(numHit == DIGITS){
        printf("正解です。\n");
        return TRUE;
    }
    printf("%sは%dHit,%dBlowです。\n\n", num, numHit, numBlow);
    return FALSE;
}

/* ------ 以下は追加したコード ------ */
#include <stdlib.h>
#include <time.h>
#include <ctype.h>
int isValidNumber(char num[]) {
    int i, j;

    for( i = 0; isdigit(num[i]); i++ );
    if( i != DIGITS ) return FALSE;

    for( i = 0; i < DIGITS - 1; i++ ) {
        for( j = i + 1; j < DIGITS; j++ ) {
            if( num[i] == num[j] ) return FALSE;
        }
    }
    return TRUE;
}

void createRandomNumber(char num[]) {
    int i;

    num[DIGITS] = '\0';
    do {
        for( i = 0; i < DIGITS; i++ ) {
            num[i] = '0' + rand()%10;
        }
    } while( isValidNumber( num ) == FALSE );
}

main()
{
    srand( time(NULL) );
    GuessNumber();
}
■ 書き換え例
 printf文を追加して目標数や照合の様子を見たり、DIGITS の define を変えて 3桁のゲームにするなど楽しんでください。
 別のヒントを出すようにカスタマイズすることもできます。 たとえば 5回までに当たらなければ、ある桁が High(5〜9)か Low(0〜4)かのヒントを表示するなどです。これを下でクイズにします。
 また、下記を常に DIGITS を反映した書式で読み込むようにするにはどうすればよいかも考えてみてください。
    scanf("%4s", num);  //DIGITS桁読み込み

■ 出題プログラムの実演つき試験対策講座 --> 目指せ 基本情報のC言語
 上のプログラムもこの講座に含まれています。 C言語実行環境内蔵、ログインするだけでコンパイルや実行ができます。
 出題プログラムを動かせるので、実際の動作を見たりチェックプリントを入れて確かめるなど、疑問を残さず学習できます。

(今回のクイズです)
 クイズ1: ヒントを出す関数 HigtOrLow を考えてください。
   関数の形式  void HighOrLow( char target[], int digit )
  引数        target --- 目標数の文字列, digit --- 桁の指定(1〜DIGITS)
   digit が 1〜DIGITS でなければエラーメッセージを表示する。
   digit で指定した桁の数字が 5以上なら High、それ以外は Low と表示する。
 クイズ2: scanf で読み込む書式を DIGITS を変えるたびに書き換えなくても済むようにするには?
   (答えは、次回のC言語の Tips で ・・・)

[ 関連記事 ] 乱数生成(rand, srand)

前回のクイズの答え:  sprintf で書くなら、たとえば次のようにします。 (前回の問題を見る
  int num = 1234;
  char str[12];
  sprintf( str, "%d", num );  //numを%dの書式でstrに出力する