練習問題-ビンゴゲーム

前提

4*4マスのビンゴカードがあります。 →入力から任意のサイズのカードを作成。
一行目で、すでに呼ばれた数字の個数を入力します。 →カードサイズ・呼ばれた数字の個数を入力。
続いて各マスの数字・呼ばれた数字を入力します。

問題

次に呼ばれたらビンゴになる数のうち一つを出力し、条件にあてはまるものがなければ”no”と出力し終了。

入力・出力一例

・入力
4 3
1 2 3 4
5 6 7 8
9 10 11 12
13 14 15 16
1 2 3

・出力
4

以下作成したサンプルコードです。

#include <stdio.h>
#include <stdlib.h>

//リーチになっているラインからビンゴを作るための最後の数字を探し出し、出力する。
void detectNum(int *line, int lineSize){
    for(int i = 0; i < lineSize; i++){
        if(*(line + i) != 0){
            printf("%d", *(line + i));
        }
    }
}


int main(void){
    int numbers;//既に呼ばれた数字の個数
    int size;//size*sizeの大きさのビンゴカード
    int detected = 0;//数字を見つけたかどうか
    scanf("%d %d",&size ,&numbers);
    
    //カードを用意し、各マスの数字を入力。
    int **card;
    card = (int **) malloc(sizeof(int *) * size);
    for (int i = 0; i < size; i++) {
        card[i] = (int *) malloc(sizeof(int) * size);
    }
    for(int i = 0; i < size; i++){
        for(int j = 0; j < size; j++){
            scanf("%d", &card[i][j]);
        }
    }
    
    //既に呼ばれた数字を入力し、手元のカード上にあれば0とし、穴をあけたことを表す。
    int calledNum;
    for(int i = 0; i < numbers; i++){
        scanf("%d", &calledNum);
        for(int j = 0; j < size; j++){
            for(int s = 0; s < size; s++){
                if(card[j][s] == calledNum){
                    card[j][s] = 0;
                }
            }
        }
    }
    
    int zeroCounter;//同列上の0の数(穴をあけた数)を数え、リーチかどうか判定する変数。
    int *lineCountainer = (int *)malloc(sizeof(int) * size);//縦横斜めの各一列を一時的に保存する配列。
    
    //左上から右下への斜め一列を確認。
    if(detected == 0){
        zeroCounter = 0;
        for(int i = 0; i < size; i++){
            lineCountainer[i] = card[i][i];
            if(card[i][i] == 0){
                zeroCounter += 1;
            }
        }
        //カウンターがリーチ状態であれば関数を呼び出しビンゴに必要な数字を判定。
        if(zeroCounter == size - 1){
            detectNum(lineCountainer, size);
            detected = 1;
        }
    }
    
    
    //右上から左下への斜め一列を確認。
    if(detected == 0){
        zeroCounter = 0;
        for(int i = 0; i < size; i++){
            lineCountainer[i] = card[i][size - 1 - i];
            if(card[i][size - 1 - i] == 0){
                zeroCounter += 1;
            }
        }
        //カウンターがリーチ状態であれば関数を呼び出しビンゴに必要な数字を判定。
        if(zeroCounter == size - 1){
            detectNum(lineCountainer, size);
            detected = 1;
        }
    }
    
    
    //各縦列についてそれぞれリーチかを確認。
    if(detected == 0){
        zeroCounter = 0;
        for(int i = 0; i < size; i++){
            zeroCounter = 0;
            for(int j = 0; j < size; j++){
                lineCountainer[j] = card[j][i];
                if(card[j][i] == 0){
                    zeroCounter += 1;
                }
            }
            //カウンターがリーチ状態であれば関数を呼び出しビンゴに必要な数字を判定。
            if(zeroCounter == size - 1){
                detectNum(lineCountainer, size);
                detected = 1;
            }
        }
    }
    
    
    //各横列についてそれぞれリーチかを確認。
    if(detected == 0){
        zeroCounter = 0;
        for(int i = 0; i < size; i++){
            zeroCounter = 0;
            for(int j = 0; j < size; j++){
                lineCountainer[j] = card[i][j];
                if(card[i][j] == 0){
                    zeroCounter += 1;
                }
            }
            //カウンターがリーチ状態であれば関数を呼び出しビンゴに必要な数字を判定。
            if(zeroCounter == size - 1){
                detectNum(lineCountainer, size);
                detected = 1;
            }
        }
    }
    
    
    //上記いずれにも当てはまらなければ次でビンゴになることはないため「no」を出力。
    if(detected == 0){
        printf("no");
    }
    free(lineCountainer);
    free(card);
    return 0;
}

7/15 追記

大幅にコード全体を変えるのではなく、削れる部分を削る方向性で調整しました。

主に以下の点を変更しました。
①横縦斜めの確認において、各々で判定していた処理をmain外の関数に譲渡。
これにより繰り返し書かれていた部分を省略。
➁カードを用意し、各マスの数字を入力する処理のコードサイズを削減。

コード量としては最終的に15%ほど削減されています

以下調整後のコードです。

#include <stdio.h>
#include <stdlib.h>

//リーチになっているラインからビンゴを作るための最後の数字を探し出し、出力する。
int detectNum(int totalNum, int zeroNum, int *line, int lineSize){
    if(totalNum - zeroNum == 1){
        for(int i = 0; i < lineSize; i++){
            if(*(line + i) != 0){
                printf("%d", *(line + i));
            }
        }
        return 1;
    }
    else{
        return 0;   
    }
}


int main(void){
    int numbers;//既に呼ばれた数字の個数
    int size;//size*sizeの大きさのビンゴカード
    int detected = 0;//数字を見つけたかどうか
    scanf("%d %d",&size ,&numbers);
    
    //カードを用意し、各マスの数字を入力。
    int **card;
    card = (int **) malloc(sizeof(int *) * size);
    for(int i = 0; i < size; i++){
        card[i] = (int *) malloc(sizeof(int) * size);
        for(int j = 0; j < size; j++){
            scanf("%d", &card[i][j]);
        }
    }
    
    //既に呼ばれた数字を入力し、手元のカード上にあれば0とし、穴をあけたことを表す。
    int calledNum;
    for(int i = 0; i < numbers; i++){
        scanf("%d", &calledNum);
        for(int j = 0; j < size; j++){
            for(int s = 0; s < size; s++){
                if(card[j][s] == calledNum){
                    card[j][s] = 0;
                }
            }
        }
    }
    int zeroCounter;//同列上の0の数(穴をあけた数)を数え、リーチかどうか判定する変数。
    int *lineCountainer = (int *)malloc(sizeof(int) * size);//縦横斜めの各一列を一時的に保存する配列。
    
    //左上から右下への斜め一列を確認。
    if(detected == 0){
        zeroCounter = 0;
        for(int i = 0; i < size; i++){
            lineCountainer[i] = card[i][i];
            if(card[i][i] == 0){
                zeroCounter += 1;
            }
        }
        detected += detectNum(size, zeroCounter, lineCountainer, size);
    }
    
    //右上から左下への斜め一列を確認。
    if(!detected){
        zeroCounter = 0;
        for(int i = 0; i < size; i++){
            lineCountainer[i] = card[i][size - 1 - i];
            if(card[i][size - 1 - i] == 0){
                zeroCounter += 1;
            }
        }
        detected += detectNum(size, zeroCounter, lineCountainer, size);
    }
    
    //各縦列についてそれぞれリーチかを確認。
    if(!detected){
        for(int i = 0; i < size; i++){
            zeroCounter = 0;
            for(int j = 0; j < size; j++){
                lineCountainer[j] = card[j][i];
                if(card[j][i] == 0){
                    zeroCounter += 1;
                }
            }
            detected += detectNum(size, zeroCounter, lineCountainer, size);
        }
    }
    
    //各横列についてそれぞれリーチかを確認。
    if(!detected){
        for(int i = 0; i < size; i++){
            zeroCounter = 0;
            for(int j = 0; j < size; j++){
                lineCountainer[j] = card[i][j];
                if(card[i][j] == 0){
                    zeroCounter += 1;
                }
            }
            detected += detectNum(size, zeroCounter, lineCountainer, size);
        }
    }

    if(!detected){
        printf("no");
    }
    free(lineCountainer);
}

7/17 追記

以下今回の変更後の主なロジックの流れです。
①ビンゴカードを、入力と同時に確認用カードに変換
➁main外関数の中で確認用カードをチェックし結果を出力
※確認用カードは、入力者が意図するカードの縦横斜めを全て確認用カード内の横列として格納する仕様です。これにより、リーチ確認時は確認用カードの横列をチェックするだけで元と同じ挙動が可能です。

以下今回のコードです。コード量としては、行数なので参考程度ですが、初期のものから50%以上削減されています。

#include <stdio.h>
#include <stdlib.h>

//ビンゴを作るための最後の数字を探し出し、出力する。
void detectNum(int size, int **card){
    int height = size * 2 + 2;
    int zeroCounter = 0, lineSum = 0;
    for(int i = 0; i < height; i++){
        zeroCounter = 0;
        lineSum = 0;
        for (int j = 0; j < size; j++){
            if(card[i][j] == 0) zeroCounter += 1;
            lineSum += card[i][j];
        }
        if(zeroCounter == size - 1){
            printf("%d", lineSum);
            return;
        }
    }
    printf("no");
}

int main(void){
    int numbers;//既に呼ばれた数字の個数
    int size;//size*sizeの大きさのビンゴカード
    scanf("%d %d",&size ,&numbers);
    
    //カードを用意し、各マスの数字を入力。
    int **card = NULL, finalSize = (size * 2) + 2;
    card = (int **)malloc(sizeof(int *) * finalSize);
    for(int i = 0; i < finalSize; i++) card[i] = (int *)malloc(sizeof(int) * size);
    
    int num;
    for(int i = 0; i < size; i++){
        for(int j = 0; j < size; j++){
            scanf("%d", &num);
            card[i][j] = num;
            card[j + size][i] = num;
            if(j == i) card[finalSize - 2][i] = num;
            if(j + i == size - 1) card[finalSize - 1][i] = num;
        }
    }

    //既に呼ばれた数字を入力し、手元のカード上にあれば0とし、穴をあけたことを表す。
    int calledNum;
    for(int i = 0; i < numbers; i++){
        scanf("%d", &calledNum);
        for(int j = 0; j < finalSize; j++) for(int s = 0; s < size; s++) if(card[j][s] == calledNum) card[j][s] = 0;
    }
    
    detectNum(size, card);
    free(card);
}

Comments

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です