C言語でBINGOを作ろう #15 抽選結果の保存
C言語でBINGOを作ろう!
C言語でBINGOを作ろう!
CUI上で動くビンゴゲームの完成目指してひっそりとプログラムの勉強を始めました。前回はバケツソートを扱えるようになりました。
しかし、まだ抽選結果の保存が出来ていません。このままでは何度でも不死鳥の如く同じ数字が出てきます。
そこで次は、バケツソートを応用して抽選結果の保存を行います。抽選結果の保存ができれば、現在の抽選結果の表示もできるようになります。
開発環境
本記事は以上の開発環境を前提に説明を行います。
抽選結果を保存する
抽選結果の保存
抽選結果を保存するには、抽選で出た数字(または、まだ出ていない数字)を記録する必要があります。
様々な方法が考えられますが、今回は配列とバケツソートを利用して抽選結果の保存を行います。
バケツソートの利用
バケツソートは、並べ替えの際に対象の数字そのものを配列のインデックスに見立てて並べ替えています。
この、「数字をインデックスとして配列を操作する」という性質を利用して抽選結果を保存します。
抽選結果の保存の手順
抽選結果の保存の手順は以下のような感じです。
なお、オレンジの枠で囲んだ内容は説明がわかりやすくなるようにつけています。実際は抽選結果が確定するまでは画面には何も表示しません。
1.配列を準備する
抽選結果を保存する配列を準備します。
その後、抽選を行います。
パターン1 まだ出ていない数字が選ばれた場合
(最初の抽選は必ずパターン1になります。)
2-1.数字を抽選する(上が抽選結果の数字、下が配列のイメージ)
数字を抽選します。今回は2が出たものとして話を進めます。
3-1.配列の要素を調べる
抽選結果の数字がインデックスになっている配列の要素を調べます。
今回は2が出たので配列の要素2を調べます。
4-1.配列の要素を変更する
該当する配列の要素が空だったので、要素を変更します。
今回は2が出たので配列の要素2を変更します。
その後、結果を画面に表示します。
表示が終わったら、次の数字の抽選へ。
パターン2 すでに出ている数字が抽選された場合
2-2.数字を抽選する(上が抽選結果の数字、下が配列のイメージ)
再度抽選を行います。すでに2が出ている状況で、もう一度2が出たとします。
3-2.配列の要素を調べる
抽選結果の数字がインデックスになっている配列の要素を調べます。
今回は2が出たので配列の要素2を調べます。
4-2.もう一度抽選からやり直す
該当する配列の要素(要素2)が空ではなかったので、その数字は一度出ていることになります。
画面の表示に移らずにもう一度抽選を繰り返します。
では、具体的にプログラムを見てみましょう。
1.空の配列を準備する
まずは並べ替える元の数字の列と、空の配列を準備します。
これは前回のバケツソートと同じ。
#include <stdio.h> int main(void){ //変数の準備 int bucket[75] = {0}; //バケツを作り、要素をすべて0で初期化 /* 中略 */ return 0; }
2.抽選を行う
次に、抽選を行います。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void){ /* 中略 */ printf("抽選を開始します。\r\n"); for(i = 0; i < 75; ){ // すべての数字が出るまでループさせる。 num = rand() % 75; //乱数を読み込んで0~74になるように変更 /* 中略 */ } printf("抽選を終了します。"); return 0; }
3.再抽選するかどうか判定する
再抽選が必要かどうか判定します。
抽選で出た数字をインデックスにしてbucketを調べ、要素が0ならまだ出ていない数字とする。
例:bucket[2]が0なら、2番はまだ出ていない。bucket[7]が1なら、7番はすでに抽選で出ている。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void){ /* 中略 */ for(i = 0; i < 75; ){ // すべての数字が出るまでループさせる。 num = rand() % 75; //乱数を読み込んで0~74になるように変更※1 //すでに抽選済みの数字かどうか判定 if(bucket[num] == 0){ //numがインデックスのbucketを調べる。値が0ならまだ出ていない数字。 /* ここで抽選結果の保存の処理を行う */ } //もし調べたbucketの値が0出なければ、何もせずにもう一度乱数の読み込み※1から繰り返し } printf("抽選を終了します。"); return 0; }
4.抽選結果を配列に保存
1~3の手順でバケツソートによる並べ替えは完了です。しかし、このままでは本当に並べ変わっているのかよくわからないので、最後に表示部分を作ります。
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void){ /* 中略 */ for(i = 0; i < 75; ){ // すべての数字が出るまでループさせる。 num = rand() % 75; //乱数を読み込んで0~74になるように変更 if(bucket[num] == 0){ //抽選の結果出た数字を表示する。 printf("%02dが出ました。\r\n", num); //該当のバケツを空でない状態にする。 bucket[num]++; //for文の継続処理の部分では何もせず、抽選結果がまだ出ていない数字だったときにループカウンタを増やす。 i++; } } printf("抽選を終了します。"); return 0; }
これで、抽選結果を保存できるようになりました。
試作6号機のサンプル全容
サンプルソースの全容は↓のようになります。読み込みを一時停止する部分なども実装しています。
サンプルソース(bingomachine06.c)
#include <stdio.h> #include <stdlib.h> #include <time.h> int main(void){ //変数の初期化と乱数の初期設定 int i = 0; //ループカウンタ int num = 0; //抽選結果を保存する int bucket[75] = {0}; //バケツを75個作り、すべて0で初期化 srand( (unsigned)time(NULL) ); //srand()関数の引数に現在時刻を設定 printf("抽選を開始します。\r\n"); for(i = 0; i < 75; ){ // すべての数字が出るまでループさせる。 num = rand() % 75; //乱数を読み込んで0~74になるように変更 //すでに抽選済みの数字かどうか判定 if(bucket[num] == 0){ printf("(Enterを押してください)\n"); char ch = getc(stdin); //キーボードからなにか入力するまで画面を一時停止する。 printf("%02dが出ました。\r\n", num); bucket[num]++; //該当のバケツに数字を加える(空でない状態にする) i++; //ループカウンタを1増やす } } printf("抽選を終了します。"); return 0; }
実行結果
>gcc BINGOMachine06.c -o BINGOMachine06.exe -std=c99 >BINGOMachine06.exe 抽選を開始します。 (Enterを押してください) 06が出ました。 (Enterを押してください) 43が出ました。 (Enterを押してください) 17が出ました。 (Enterを押してください) (中略) 49が出ました。 (Enterを押してください) 33が出ました。 (Enterを押してください) 36が出ました。 抽選を終了します。
試作6号機の弱点
これで数字がかぶらないようになりました。ついにフェニックスを倒したぞ
しかしこのままでは一度出た数字を確認するのが大変です。
そこで、次回は一度出た数字も合わせて表示されるようにします。
いよいよラストスパートやで!!
おまけ
何だかプログラムが長くなりすぎて見づらい。。。
宿題コーナー
気が付けば夏に逆戻り!? 宿題のコーナーです。
前回の宿題と解答例
今回作ったバケツソートっぽいものは同じ数字が2回以上出てくるとうまく並べ替えができません。そこで、どうやれば同じ数字が2回以上出てきても並べ替えできるか、考えてみましょう。
解答例(homework14.c)
#include <stdio.h> int main(void){ int number[] = {7, 4, 2, 9, 0, 7, 8, 7, 0}; int bucket[10] = {0}; int i; int j; int length = sizeof(number) / sizeof(number[0]); int bkt_length = sizeof(bucket) / sizeof(bucket[0]); for(i = 0; i < length; i++){ bucket[number[i]]++; //変更点1 bucket[取り出した数字] を『1つ』増やす。こうすることで数字がダブっていても数えることができる。 } //バケツから数字を戻す i = 0; j = 0; for(j = 0; j < bkt_length; ){ //変更点2 bucketの要素が空になるまで取り出し続ける。空でない場合、jは増やさない。 if(bucket[j] > 0){ //もし、bucketの中身が入っていたら number[i++] = j; //バケツの番号をnumber[]の i 番目に戻し、その後 i を1つ進める bucket[j]--; //バケツの中身は1つ減らす }else{ //そうでなければ(=バケツが空なら) j++; //jを1増やす(次のバケツへ) } } //数字を表示する i = 0; for(i = 0; i < length; i++){ printf("number[%d] = %d\n", i, number[i]); } return 0; }
実行結果
>gcc homework14.c -o homework14.exe -std=c99 >homework14.exe number[0] = 0 number[1] = 0 number[2] = 2 number[3] = 4 number[4] = 7 number[5] = 7 number[6] = 7 number[7] = 8 number[8] = 9
今日の宿題
そろそろプログラムが長くなってきたから関数で分けたいなぁ。。。(独り言。)
次回は今までの抽選結果も表示されるようにします。
実践力が身につくC言語講座 連載リンク
競技プログラミングをイメージしたライブラリ活用講座
競技プログラミング風-標準Cライブラリ入門 連載
アルゴリズムをマスターして技術力アップ!
実践アルゴリズム講座 連載
パズルゲームの解析をテーマにしたC++講座
ゲーム解析プログラミング 連載