7 行でゲーム（ヒット＆ブロー、または 3 桁 MOO）を作る

いきなり結論のコード：

```C
#include <time.h>
int printf(const char*,...);int scanf(const char*,...);char a[3],g[4],z=48;int
rand();void srand(unsigned);int f(char*x){return x[0]==x[1]||x[0]==x[2]||x[1]
==x[2];}int i,h,b;int main(){srand(time(NULL));do{a[0]=z+rand()%9+1;a[1]=z+ra\
nd()%10;a[2]=z+rand()%10;}while(f(a));do{h=0;do printf(">"),scanf("%3s",g);wh\
ile(f(g));for(i=0;i<3;i++)h+=a[i]==g[i]?g[i]=0,1:0;b=0;for(i=0;i<9;i++)b+=a[i/
3]==g[i%3]?1:0;printf("H%d B%d\n",h,b);}while(h!=3);printf("OK!\n");return 0;}
```

# はじめに
　小学生のときに、紙と鉛筆だけでできる対戦ゲームが大流行したことがありました。今思うと [数当てゲーム MOO](http://math.a.la9.jp/week14.htm)を 3 桁にした変形版なんだろうと思います。マスターマインドの変形版とも言えますが、当時の僕らは H がいくつで B がいくつ、といったフォーマットで情報をやりとりしていたので、ここではヒット＆ブローという呼び名で統一したいと思います（正確に言えば、3 桁の Hit & Blow ということになるかとは思いますが、それはそれとして）。

　今回は、コンピュータが考えた答えをプレイヤーがひたすら推測し続ける、というプログラムとなります（対戦ゲームではありません）。

# ゲームのルール
## 準備
　小学生の僕たちが遊んでいたこのゲームは、2 人で遊ぶゲームでした。各自、最初に答えとなる 3 桁の数字を、相手に見えないようにメモします。この 3 桁の数字は何でも良いというわけではありません。各桁は全て異なる数字でなければいけません。つまり、111 とか 565 などの数字は、答えの数字にはしてはダメです。もうひとつのルールとしては、最初の桁は 0 にしない、というものがありました。MOO の説明をみると、最初の桁が 0 であっても良いみたいなのですが、小学生だったので、012 は 12 で 2 桁じゃん！という考えがあったのではないかと思われます（なので、ここでは回答となる数字は少なくとも 102〜987 の範囲にある整数、ということになります）。

## Hit と Blow
　いわゆる Hit & Blow と同じですが、念のため、ここでも説明しておきます。Hit は場所も数字も一致しているものの数を表します。例えば、987 という解に対して、907 と問い合わせる（攻撃した）場合、3 桁目の 9 と 1桁目の 7 が数字の種類と位置が合っているので、Hit は 2 となります（9 と 7 の 2 つの数字が Hit した、という意味です）。これを単に H2 と表記することとします。

　場所は合っていないけど、数字の種類が合っているものを Blow とします。Hit が命中なら、Blow はかすったという意味でしょうか。先の例で言うと、987 に対し、670 と問い合わせた場合、2 桁目の 7 は位置が異なっているけれども、種類は合っているので Blow となります。そしてそのような数はこの 7 だけなので、Blow は 1 となります。これを単に B1 と表記することにします。

　もちろん、987 に対し、123 という問い合わせをすると、Hit も Blow もひとつも無いので、H0 B0 という結果となります。このような結果が得られると、9 つの数字のうち、問い合わせに使った数字は全く関係ないこととなりますので、非常に大きなヒントとなります（今の例では、123 は解に存在しないので、4〜9 の 6 桁が解の候補となるわけです）。

 H と B の数を用いて、このゲームの終了条件を書くと、H3 となる数を入力するまでゲームが続くこととなります。なお、ネットの記事を色々みてみると、H3 とは書かずに 3H と数字が先にくる書き方の方が主流のようですが、ここでは私の小学生時代のルールに倣い、数値を後に示す流儀でいきます。

# 清書版のコードと、その解説
```C
// gcc spoiler.c

#include <time.h>

int printf(const char*,...);
int scanf(const char*,...);
int rand();
void srand(unsigned);

char a[3],g[4],z=48;    // 48=='0'
int i,h,b;  // h は Hit 数、b は Blow 数

// 同じ数字があったら true を返す
int f(char*x) {
    return x[0]==x[1] || x[0]==x[2] || x[1]==x[2];
}

int main() {
    srand(time(NULL));
    // 問題を作る
    do {
        a[0]=z+rand()%9+1;
        a[1]=z+rand()%10;
        a[2]=z+rand()%10;
    } while(f(a));

    do {
        do
            printf(">"), scanf("%3s",g); // カンマ式による短縮
        while(f(g));

        // Hit 数計算
        h=0;
        for(i=0;i<3;i++)
            h += a[i]==g[i] ? g[i]=0,1 : 0;

        // Blow 数計算
        b=0;
        for(i=0;i<9;i++)
            b += a[i/3] == g[i%3] ? 1 : 0;
        printf("H%d B%d\n",h,b);
    } while(h!=3);
    printf("OK!\n");
    return 0;
}
```

　コンピュータが想定した値は、配列 a に文字コードで格納します（実際は ASCII コード）。プレイヤーが入力した値は配列 g に格納されています。i=0,1,2 の全てに対して a[i]==g[i] が成立したらゲーム終了です。

## Hit 数計算部分
　Hit 数計算部分では、カンマ式を使って Hit 数計算と、Hit した数が次の Blow 計算で再度カウントされないようにしています（以下の部分です）。

```C
h += a[i]==g[i] ? g[i]=0,1 : 0;
```

もし、a[i]==g[i] が成立しているならば（つまり位置も数字も合っているならば）、g[i]=0,1 が実行されます。カンマ式のルールにより、g[i]=0 が実行され、その後 1 が評価されて、3 項演算子の評価結果は 1 となり、最終的には h+=1 が実行されることになります。

また、g[i]=0 としているため、次の Blow 数計算部分でも g[i] は Blow している入力としては評価されません（g には文字コードが入っているので、0 としておけば、'0'〜'9' の値（文字コード）とは異なる状況となります。

## Blow 数計算部分
　Blow 数計算部分は、

```C
        // Blow 数計算
        b=0;
        for(i=0;i<9;i++)
            b += a[i/3] == g[i%3] ? 1 : 0;
```
です。

i を 0〜8 まで回していますが、配列 a に対しては i/3 を、g に対しては g[i%3] としているので、結果として a[0],a[1],a[2] と b[0],b[1],b[2] の全てのペアを比較することとなります：

```
a[0] と b[0]  // i=0 のとき（以下同様）
a[0] と b[1]  // i=1
a[0] と b[2]  // i=2

a[1] と b[0]  // i=3
a[1] と b[1]  // i=4
a[1] と b[2]  // i=5

a[2] と b[0]  // i=6
a[2] と b[1]  // i=7
a[2] と b[2]  // i=8
```

## その他
　ヘッダのインクルードを減らすため、自前でプロトタイプ宣言を書いたり、また、カンマ式を活用したりしていますが、基本的にはそんなにトリッキーなことはしていません。プログラムとしては結構素直な実行なのではないかな、と思っております。

# ゲームの様子：

```
$ ./a.out
>123
H1 B0
>145
H1 B0
>167
H0 B0
>126
H0 B0
>146
H1 B0
>843
H2 B0
>943
H3 B0
OK!
```

この時の答えは 943 でした。



# 参考
* [マスターマインド Wikipedia](https://ja.wikipedia.org/wiki/%E3%83%9E%E3%82%B9%E3%82%BF%E3%83%BC%E3%83%9E%E3%82%A4%E3%83%B3%E3%83%89)
* [数当てゲームMOOの最小質問戦略と最強戦](https://www.tanaka.ecc.u-tokyo.ac.jp/ktanaka/papers/gpw96.pdf)
* [C言語で乱数を生成 Qiita](https://qiita.com/akspect/items/a1a3582da95ee6d10a21)