ま総研2000(まどの総合研究所ver.5.1)

みんながみんなその物事に興味があると思っちゃいけないよという精神で綴る備忘録

あみだくじゲームは一応完成 C言語適当練習日記 第9回

   

独習C 第4版などを眺めつつ、作ったあみだくじゲームは一応完成。今後、ここで書いたコードを、1か月か2か月後ぐらいしてから見て、ああ、これはこうした方がいいのに的な反省が出来るくらいになれればいいかなあといった感じ。

で、今回のあみだくじでは、まだ構造体がどうたらこうたらという部分には、一切触れていないので、その項を読んで、もう少し練習していきたいかなあというところ。

[c language="++"]// amida.hでプロトタイプ宣言。
#define FINALSTAGENUMBER 3 //定義:最終ステージの数字 今回は3

int SelectNumber(char message[], int numbermax); // 整数番号選択(メッセージ文、選択数値の最大値)
int MapDataReading(int stage_number, char *mapp, int *map_width); //マップデータのロード(選択したステージ番号、マップ配列用P、マップの広さP)
int Update(char *mapp); // 画面表示遷移の文。(マップ配列用のポインタ)
int PMove(int kuji_number, char *mapp, int map_width); // Pマーク移動などの文。(選択したくじ番号、マップ配列用P、マップの広さP)
char PMoveDown(char *mapp, int map_width, int px, int py, char buf); //Pマークの下への移動の文。単にダブったので関数化
int NextStepQuestion(void); // 次の画面に進むかどうかの質問。[/c]

[c language="++"]// Amida.cpp あみだくじゲームのメインです。
#include <stdio.h>
#include "amida.h"// amida.hはあみだくじ作成用の自作ヘッダファイル

int main(void)
{
int decide_stage_number; // 選択決定したステージ番号
int decide_kuji_number; // 選択決定したくじの番号
int final_kuji_number; // 選択できるくじの最大数、マップデータ読み込みにより取得

char mapdata[300]; // とりあえずのマップデータ用の各座標ごとの文字入れ300にしておく。

int *width = NULL; // マップの横軸の長さP、ただし\nを含んだ長さ。
int default_width = 0;// 横の長さの数値の方。一応初期設定ゼロ。

width = &default_width; // widthに番地を代入。

//入力数値でステージ選択 FINALSTAGENUMBERは選択できるステージの最大数
decide_stage_number = SelectNumber("ステージ", FINALSTAGENUMBER);

// ステージ数を渡して、マップデータ読み込み
// この関数からもってくるのは、選択できるくじの最大数
final_kuji_number = MapDataReading(decide_stage_number, mapdata, width);

// 文字列を画面に表示する文。
Update(mapdata);

//入力数値でくじ選択、関数そのものは、ステージ選択のものと同じ。
decide_kuji_number = SelectNumber("くじ", final_kuji_number);

// 設定されたくじ番号のところにPマークを入れる。
// この中で再表示もしたりしなかったり。
PMove(decide_kuji_number, mapdata, default_width);

// 一応次の画面に進むか聞く。どっちにしろ終わる。
NextStepQuestion();

return 0;
}[/c]

[c language="++"]// SelectNumber.cpp 整数を選択するための関数が入っています。
#include <stdio.h>
#include <stdlib.h> // atoiを使うことにしたのでこれを追加。

// message[]は「なんとかの番号を入力してください」という表示用の文字列、numbermaxは入力できる整数の最大値
int SelectNumber(char message[], int numbermax){

int number = 0; // bufferの数値を整数化した数値。これが決定した数字になる。while文の関係上初期値0設定
char buffer[10]; // scanfで打ち込む文字列。バッファー10まで。

// もし打ち込んだ数字が1からnumbermaxまでの数字じゃないときはループ。
while((number<1) || (number>numbermax)){

printf("1から %d までの %s の番号を入力してください。\n", numbermax, message); // 指示用のメッセージを表示
scanf("%8s",&buffer); //文字列打ち込み 制限8
number = atoi(buffer); //文字を整数化
}

// printf("今選択した%sは %d です。\n",message, number); // 動作確認用の選択数値の表示。

return number; // 選択数字を帰す
}
[/c]

[c language="++"]// MapDataReading.cpp マップデータのファイルを読み込む文
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int MapDataReading(int stage_number, char *mapp, int *map_width)
{
FILE *fp = NULL;
char str[64]; // ファイル読み込み1回分あたりの読み込み量
char textfile_name[20]; // 読み込みたいファイルの名前を入れる文字列
int final_kuji_number; // くじ番号の最大数
int char_length;

// 選択したステージの数字によって、mapdata数値.txtを読み込むための文字列作成
sprintf(textfile_name, "mapdata%d.txt", stage_number);

// textfile_nameのファイルが開いたかどうかの判定。読み込み用。
if((fp = fopen(textfile_name,"r")) == NULL){
printf("ファイルロードエラー\n");
exit(1);
}

// マップファイル1行目でくじの最大数の読み込み 
fgets(str, 64, fp);

// EOFの場合の処理のみ、他のエラー処理(数字じゃないとか、空欄とか)は後で考える。
if(!feof(fp)) {
final_kuji_number = atoi(str);
}

// ファイル2行目でマップの広さについて数を確保。ポインタ利用。
fgets(str, 64, fp);
char_length = strlen(str);
*map_width = char_length;

// ファイル2行目のとりこみ
for(int x=0; x < char_length; x++){
*mapp = str[x];
*mapp++;
}

// ファイル3行目からは、while文でマップデータを読み込み
// この方針だとEOFが配列に入ってからwhileを抜けることになる。
while(!feof(fp)) {
*mapp= fgetc(fp);
*mapp++;
}
// 読み込みファイルを閉じる
fclose(fp);

// Update()画面表示第1回目をここでやりたかったが、
// *mappをインクリメントをしている影響で、表示がヘンテコになるので却下。

return final_kuji_number; // くじの最大数を返す。
}
[/c]

[c language="++"]// Update.cpp あみだくじゲームの画面遷移が目的のものです。。
#include <stdio.h>
#include "amida.h"

int Update(char *mapp){

// 中身がEOFになる直前まで文字を表示。
while(!(*mapp == EOF)){
printf("%c", *mapp); //画面表示。今回はEOFの手前、要するにラスト行の"\n"まで表示。
*mapp++;
}

// 次の画面(処理)に進むかどうかの判定
NextStepQuestion();

return 0;
}[/c]

[c language="++"]// NextStepQuestionは単に画面遷移を確かめるもの。適当
#include <stdio.h>
#include <conio.h>
#include <stdlib.h>

int NextStepQuestion(void){
char ch;

puts("次の画面に進みますか?(Q,qで終了、その他は続行)");
ch = getche(); // これを使うと、コンパイルで警告がでる。
if (ch =='Q') ch ='q'; // 基本シフト押した時点で続行、CapsLock対策対策
if (ch =='q') {
puts("\n終了します\n");
exit(0);
}
puts("");

return 0;
}[/c]

[c language="++"]// PMove.cppはくじで選択したPを移動させるための文
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "amida.h"

// くじ番号は選択した番号、*mappはマップ文字列のP、map_widthはマップ1行ごとの長さ\n含む
int PMove(int kuji_number, char *mapp, int map_width){

char buffer = '\0'; // 文字入れ替え用のバッファー、
char kuji[3]; // くじの番号を文字に変えるための下準備

int start_x = 0; // PのX座標設定用

// sprintfでくじの番号を文字化、kuji[0]=番号、kuji[1]='\0'
sprintf(kuji, "%d", kuji_number);

/* *mappの2行目で、kuji[0]の文字と同じものがあるかどうか調べる。
もし、同じものがあれば、その場所にPを置いて、元々あった文字をbufferに格納。
*/
do{
if (*(((mapp+map_width)+start_x)) == kuji[0]){
// ここで変換コマンド
buffer = (*((mapp+map_width)+start_x));
*(((mapp+map_width)+start_x)) = 'P';
break; // 変換したら抜ける
}
start_x++;
}while(start_x < map_width);

// while文でbreakしないで抜けた場合の異常終了処理・適当
if (start_x >= map_width) exit(2);

// 画面表示アップデート
Update(mapp);

// yはそのままY軸、都合上、1からスタート、16はあくまで便宜的なもの。
for(int y=1 ; y <16 ; y++){

// 先ほど、Pと交換した文字が格納されたbuffer の様子で、Pの移動を判断
switch(buffer){

/* R:次は右に行くマス、なのでPの右隣のマス'+'との交換、
  さらに'+'にて、となりのLとの交換
  そして、さらに下のHマスと交換という3段階を行わないとならない。
  Lについては、左に行って下に行く動きを、Rに準じて行わないとならない。
  うまくやればPMoveDown()に、このシステムを組み込めたかもしれない。
*/
case 'R':
for(int i=0; i<2; i++){
*((mapp+map_width*y)+start_x) = buffer;

start_x++;

buffer = (*((mapp+map_width*y)+start_x));
*(((mapp+map_width*y))+start_x) = 'P';

Update(mapp);
}

buffer= PMoveDown(mapp, map_width, start_x, y, buffer);

break;

case 'L':
for(int i=0; i<2; i++){
*((mapp+map_width*y)+start_x) = buffer;

start_x--;

buffer = (*((mapp+map_width*y)+start_x));
*(((mapp+map_width*y))+start_x) = 'P';

Update(mapp);
}

buffer= PMoveDown(mapp, map_width, start_x, y, buffer);

break;

/* defaultは 本来、エラー処理をすべきなのだろうけれど、面倒なので、
kuji[0]が数字の場合か'H'の場合が該当することにした。
下のマスの文字と'P'と文字の交換、bufferに入れていたものを交換前のPの位置に戻す作業
上のL、Rに比べたら単純作業である。
*/
default:
buffer= PMoveDown(mapp, map_width, start_x, y, buffer);
break;
}

// あみだくじのラスト、bufferがOならハズレで終了、Wならあたりで終了となる。
// do while文とcase文を使って中に組み込もうとしたけれども、
// どうすりゃいいのかわからないため適当にやった。
if (buffer=='W'){
puts("当たり!");
break;
}
if (buffer=='O'){
puts("ハズレ");
break;
}
}

puts("ゲーム終了です");

return 0;
}

// Pが下へ行くときの文字交換その他の式。
char PMoveDown(char *mapp, int map_width, int px, int py, char buf){
*((mapp+map_width*py)+px) = buf;
buf = (*((mapp+map_width*(py+1))+px));
*(((mapp+map_width*(py+1))+px)) = 'P';

Update(mapp);

return buf;
}
[/c]

mapdata1.txt
[c language="++"]3
*********
* 1 2 3 *
* H H H *
* R+L H *
* H R+L *
* R+L H *
* H H H *
* O W O *
*********
[/c]

mapdata2.txt
[c language="++"]3
*********
* 1 2 3 *
* H H H *
* R+L H *
* H R+L *
* R+L H *
* H H H *
* O O W *
*********
[/c]

mapdata3.txt
[c language="++"]4
***********
* 1 2 3 4 *
* H H H H *
* R+L H H *
* H R+L H *
* R+L H H *
* H H H H *
* W O O O *
***********
[/c]

PR2

 - C言語練習