ペグゲームを作ろう

整列や探索のアルゴリズムについて勉強してきましたが,正直,面白くありません.初めてコンピュータに触ったとき,実は 8 ビット機でして(笑),簡単なゲームを作って遊んでいました.当時は,電源を入れると Basic の画面が立ち上がるというもので,自分でプログラムしないと何もしないという代物でした.作って楽しいのはゲームだ! 私,コンピュータでも,スマホでも,ゲーム機でも一切ゲームをやりません.嫌いだからではなく,絶対にのめり込むから(笑).

ということで,ゲームを作ろうと思い立ったのです.しかし,OS 毎に異なる window の操作を覚える気はありません.コマンドプロンプトだけでできるもの.そんなものがあるのでしょうか? 一つ思いつきました.それがペグゲームです.所謂ダイヤモンドゲームを究極まで単純化したもの.よく碁石などを使います.

〇〇〇〇 ●●●●

と白石と黒石が並んでいて,真ん中に一つだけ空きがあります.白石は右に,黒石は左にそれぞれ空きがあったとき進むことができます.また,進行方向に別の色の石があったとき,1つだけ別の色の石を跳び越すことができます.動かす石の色は,交互である必要はありません.最終的に色を入れ替え

●●●● 〇〇〇〇

となるように並べるという遊びです.

中学校の数学の課題学習で時々取り上げられます.その実践記録は,ネットを検索すると,簡単に見つけることができます.

まずは,作ったプログラムをご覧いただきます.

#include <stdio.h>
#include <string.h>
#include <stdbool.h>

int showLetters(char* a) {
    printf("\n");
    for (int i = 1; i < 10; i++) {
        printf("%2d", i);
    }

    printf("\n");

    for (int i = 0; i < 9; i++) {
        printf("%2c", a[i]);
    }

    printf("\n");

    return 0;
}

int checkLetter(char* a) {
    int ret = 2;
    int i;

    if (strcmp(a, "LLLL RRRR") == 0) {ret = 0;}

    i = -1;
    while (ret == 2 && ++i < 8) {
        if (a[i] == 'R' && a[i + 1] == ' ') {ret = 1;}
    }

    i = -1;
    while (ret == 2 && ++i < 7) {
        if (a[i] == 'R' && a[i + 1] == 'L' && a[i + 2] == ' ') {ret = 1;}
    }

    i = -1;
    while (ret == 2 && ++i < 8) {
        if (a[i] == ' ' && a[i + 1] == 'L') {ret = 1;}
    }

    i = -1;
    while (ret == 2 && ++i < 7) {
        if (a[i] == ' ' && a[i + 1] == 'R' & & a[i + 2] == 'L') {ret = 1;}
    }

    return ret;
}

bool checkMovable(char* a, int num) {
    bool ret;

    if (a[num] == 'R' && a[num + 1] ==' ') {
        ret = true;
    } else if (a[num] == 'R' && a[num + 1] =='L' & & a[num + 2] == ' ') {
        ret = true;
    } else if (a[num] == 'L' && a[num - 1] == ' ') {
        ret = true;
    } else if (a[num] == 'L' && a[num - 1] == 'R' & & a[num - 2] == ' ') {
        ret = true;
    } else {
        ret = false;
    }

    return ret;
}

int moveLetter(char* a, int num) {
    if (num < 0 || num > 8 || a[num] == ' ') {
        printf("There is no letter at this number.");
    printf("\n");
    } else if (checkMovable(a, num)) {
        if (a[num + 1] == ' ') {
            a[num + 1] = a[num];
            a[num] = ' ';
        } else if (a[num - 1] == ' ') {
            a[num - 1] = a[num];
            a[num] = ' ';
        } else if (a[num + 2] == ' ') {
            a[num + 2] = a[num];
            a[num] = ' ';
        } else if (a[num - 2] == ' ') {
            a[num - 2] = a[num];
            a[num] = ' ';
        }
    } else {
        printf("This letter can't be moved!");
        printf("\n");
    }
}

int main(void) {
    char letter[] = "RRRR LLLL";
    int num;

    while (checkLetter(letter) == 1) {
        showLetters(letter);
        printf("Which letter will you move? : ");
        scanf("%d", &anp;num);
        moveLetter(letter, --num);
    }

    if (checkLetter(letter) == 0) {
        showLetters(letter);
        printf("Congratulations! You did it!");
        printf("\n");
    } else {
        showLetters(letter);
        printf("Sorry! This is your lose!");
        printf("\n");
    }

    return 0;
}

起動すると,画面に下の文字が表示されます.

1 2 3 4 5 6 7 8 9
R R R R  L L L L
Which letter will you move? :

2バイト文字を使いたくなかったので,RとLの文字を使うことにしました.Rは右に右に進みます.逆にLは左に左に進みます.この状態では,4のRか6のLしか動かせません.動かすために上段の数字を入力します.例えば,4を入力すると

1 2 3 4 5 6 7 8 9
R R R R  L L L L
Which letter will you move? : 4

1 2 3 4 5 6 7 8 9
R R R  R L L L L
Which letter will you move? :

となります.ここで動かせる文字は,3のRと6のLです.6のLを動かすと,5のRを飛び越え(異なる文字を1文字だけ飛び越えることができます)4の位置にLが入り,6の位置に空きができます.という操作を繰り返して,

1 2 3 4 5 6 7 8 9
L L L L  R R R R
Congratulations! You did it!

この画面を目指します.

ちょこっとコメント

作ってみれば,新たなコマンド類はまったくなし.しかも,分かりやすさを重視し,冗長で美しさの欠片もないプログラムです.細かく関数に分けていますので,関数ごとに少しばかりのコメントを.自分のための備忘録です.

インクルードしたライブラリ

<string.h>
これは,文字列を比較するために strcmp() を使っているためです.文字列の整列で用いましたが,strcmp(str1, str2) で 0 が返れば str1 == str2 が真になっているという使い方ができます.

<stdbool.h>
これは,ブール型の値を返す関数を作るために必要になります.

int showLetters(char* a)

引数として,現在のLとRの文字の並びの入った配列を受け取って,1 ~ 9 までの数字と,その下にLとRの文字を表示します.それだけです.

checkLetter(char* a)

変数 ret はこの関数の返り値です.0,1,2 のいずれかを返します.0 は L L L L  R R R R の状態になって,ゲームに成功した場合です.

if (strcmp(a, "LLLL RRRR") == 0)

により,配列に入った文字列とゴールの文字列を比較しています.

ret = 1 は,まだ動かせる文字がある状態です.Rで言えば,右側に空きがあるか,右側にLがあり更にその右に空きがある場合です.それをRとLについてチェックしているために少々長くなっています.

上2つのいずれでもなければ,動かせる文字がなくなり詰んだ状態で,ret = 2 を返します.

bool checkMovable(char* a, int num)

選択した文字が動かせる状態にあるかどうかをチェックしています.書いた内容は,checkCharactor(char* a) とまったく同じです.選択した文字が動かせる場合は true を,動かせない場合は false をそれぞれ返します.<stdbool.h> はこの関数のために必要になります.

int moveLetter(char* a, int num)

現在の文字の配列と動かそうとしている文字の番号とを受け取って,動かせる文字ならば指定された文字を動かし,動かせない文字であれば「This letter can't be moved!」を表示します.文字が動かせるかどうかの判定には,前述の int checkMovable(char* a, int num) を利用しています.

int main(void)

何度も文字列を表示して,どの文字を動かすかを聞いてくるのは,while 文で動かせる文字のある間は繰り返しているからです.動かせる文字があるかどうかは,int checkLetter(char* a)に文字列を渡して判定しています.動く文字がなくなったら,成功しているか? 失敗しているか?の判定をし「Congratulations! You did it!」または「Sorry! This is your lose!」を表示します.

ゲームを作るぞ!と意気込んだほどのものではありませんが,やはり,こういうものを作る方が楽しいですね.中学校の数学でこのゲームを用いることがあると書きました.仮に,このプログラムを使った場合,動かした記録が見られるというところに授業での有意性を感じました.キャラクタベースも捨てたものではないかも!

とちょっとだけ,良いことを書きましたが,エラートラップを何もかけていません.例えば全角の数字を入れると,とんでもない動きをします.早い話し,想定内の使い方ならば良いけれど,想定外のことをすると何が起こるか分からない・・・という状態です(爆笑).

Last modified: Thursday, 2 November 2023, 12:23 PM