色当てゲーム(前編)

こんにちは、きうちです。

寒さも本格的になってきて、ダウンコートや手袋、マフラーなんかが手放せない日々ですね。

インフルエンザも流行っているようですし、手洗いうがいなんかしっかりして、風邪をひいたりしないように元気に過ごしていきたいですね。

もう12月も目前。今年も残すところあとわずか。

これをご覧のみなさまは、今年やりたかったこととか、抱負とか、野望?とか…どうですか?

実は、私は

「このブログにミニプログラムネタを毎月載せよう!」

というのを今年の抱負にしていました。今書いているこれが済めば、あとは12月を残すのみ!

気を引き締めてまいりたいと思いますね!^^

★ ★ ★

今回は、「色当てゲーム」というものをお送りしようかと思います。

これも昔からよくあるものだと思いますが、以下のようなルールのちょっとしたゲームです。

  • コンピュータがランダムに色を4つ選ぶ(=問題作成)。
  • その色が一瞬表示される。
  • プレイヤーはその一瞬の表示を記憶し、どんな色だったか回答する。
  • 4つとも全て当たればクリア。

★ ★ ★

言語は例によってC#にします。Windowsフォームアプリとして作成します。

画面サイズは800×600にします。

背景はグレーにします。といいますか、デフォルトのままでOK!

今回はコントロールを並べる部分はすべてコーディングしていきたい(その方が効率がよさそう)なので、

デザイナーでいじるのはここまで。

続いてコード。

色は青、赤、緑、黄の4色にしたいので、その4つをフィールドの配列変数で定義しておきます。

また、色の表示はピクチャーボックスの必要な数だけ画面に並べてあとで便利に参照するために、
そのピクチャーボックスを格納しておくフィールドのリストを用意します。

private Color[] colors = { Color.Blue, Color.Red, Color.Lime, Color.Yellow }; // 色

private List<PictureBox[]> pictureBoxes = new(); // 問題表示用および回答表示用のピクチャーボックス。1要素で1列分

コンストラクタはこんな風にします。

public Form1()
{
    InitializeComponent();

    Init();
}

ここで呼び出されるInitメソッドはピクチャーボックスやボタンを並べるためのメソッドであり、次のようにします。

private void Init()
{
    for(int i = 0; i < 4; i++) // 列の繰り返し
    {
        Put(i);
    }
}

private void Put(int i) // 1列分のコントロールを配置する
{
    int x = i * 180 + 64; // 列の左上のX座標
    int y = 50; // 列の左上のY座標

    // 色表示用ピクチャーボックスを配置
    PictureBox[] pp = new PictureBox[2];
    for (int j = 0; j < 2; j++) // 段の繰り返し
    {
        PictureBox p = new PictureBox();
        p.SetBounds(x, y + j * 160, 128, 128);
        p.BackColor = Color.Black;
        this.Controls.Add(p);
        pp[j] = p;
    }
    pictureBoxes.Add(pp); // ピクチャーボックスを保持

    // 色選択ボタンを配置
    for (int j = 0; j < 4; j++)
    {
        Button b = new Button();
        b.SetBounds(x + j * 32, y + 288, 32, 32);
        b.BackColor = colors[j];
        b.Click += button_click;
        b.Tag = new int[] { i, j };
        this.Controls.Add(b);
    }
}

ボタンを1列につき4つ並べます。
ボタンのクリックイベントとして button_click を設定していますが、
このクリックイベントメソッドは次のようにします。

private void button_click(object? sender, EventArgs e)
{
    Button? b = sender as Button;
    int[]? ij = b?.Tag as int[];
    if (b == null || ij == null) return;

    pictureBoxes[ij[0]][1].BackColor = colors[ij[1]];
}

さあ、ここまででいったん実行してみましょう。

こんな風に黒いピクチャーボックスが上に4枚、下に4枚。

そして、下の4枚のそれぞれ下部に色のボタンが並びましたね。

そして、色のボタンを押すと、各々の列の下の段のピクチャーボックスがその色になると思います。

上図は1列目の赤ボタンを押してみたところ。

これで、回答をするための仕組み(と、出題をする部分の一部)ができました。

★ ★ ★

今度は出題する機能を作っていきます。

まず、乱数を発生させるためのRandomのインスタンスと、答えを格納するためのColorの配列を用意します。

配列のサイズは4です。

private Random random = new Random(); // 乱数発生用

private Color[] answer = new Color[4]; // 答え格納用

問題を作成して表示するメソッドを実装します。名前はGiveQuestionとします。

private void GiveQuestion() // 問題を作る
{
    for(int i = 0; i < 4; i++)
    {
        answer[i] = colors[random.Next(0, 4)]; // 答えを決める
        pictureBoxes[i][0].BackColor = answer[i]; // 上の段に答えを表示
        pictureBoxes[i][1].BackColor = Color.Black; // 下の段は黒にしておく
    }
}

やっていることは0~3の乱数を発生させてその数字で色の配列を参照し、答えを格納する配列に入れています。
あとは、それを上の段に表示します。

続いて、回答ボタンを画面に追加する処理を実装します。

Initメソッドに次のように追加します。

private void Init()
{
    for(int i = 0; i < 4; i++)
    {
        Put(i);
    }

    // --- 追加ここから ---
    Button btnAnswer = new Button();
    btnAnswer.SetBounds(64, 380, 180, 24);
    btnAnswer.Text = "回答する";
    btnAnswer.Click += btnAnswer_click;
    Controls.Add(btnAnswer);
    // --- 追加ここまで ---
}

追加したボタンのクリックイベントとして btnAnswer_click を設定していますが、それはこのように実装します。

private void btnAnswer_click(object? sender, EventArgs e)
{
    if (Check())
    {
        for (int i = 0; i < 4; i++)
        {
            pictureBoxes[i][0].BackColor = answer[i];
        }
        MessageBox.Show(this, "正解!");
        GiveQuestion();
    }
    else
    {
        MessageBox.Show(this, "不正解!");
    }
}

ここで呼び出されている Check メソッドは「回答が合っているか?」をチェックするためのメソッドで、合っていればtrueを返します。

trueが返ってきたら上の段に正解を表示するとともに、「正解!」とメッセージボックスで表示します。
このメッセージボックスでOKボタンを押したらGiveQuestionメソッドを呼びます。
これにより、次の問題が作成・表示されます。

false が返ってきたら「不正解!」とメッセージボックスで表示します。

Check メソッドは以下のようにします。

private bool Check()
{
    int ct = 0;

    for (int i = 0; i < 4; i++)
    {
        if (answer[i] == pictureBoxes[i][1].BackColor)
        {
            ct++;
        }
    }

    return ct == 4;
}

やっていることは、answerの各要素と下の段のピクチャボックスの色が一致しているかを1列ずつ見てカウントしていくことです。

その結果が4だった場合は正解なのでtrueを返します。4でない場合はfalseです。

さらにコンストラクタに追加をして、画面が開くと同時に出題がなされるようにします。

public Form1()
{
    InitializeComponent();

    Init();

    GiveQuestion(); // 追加
}

ここまでで実行してみましょう。

色当てができるようになりましたね!

★ ★ ★

といったところで、今日はここまで。

・・・ここまでだと、最初にお伝えした「答えが一瞬表示される」ができていないですね?
表示されっぱなしですね?

次回は、さらにゲームらしくしてみたいなと思います。

それでは!