ページ 11

乱数(GetRand)がうまく動作しない。

Posted: 2016年9月13日(火) 02:30
by 乱猿
連投になってしまい申し訳ありません。
前回の問題は解決したのですが、新たにわからないところが出てきたので質問させてください。
DXライブラリにて簡単なタイピングプログラムを組んでいます。
ランダムでSeikaiから問題を出し、一文字ずつ成否を判定、すべての文字を入力が正解すると、次の問題へというプログラムを作りたいのですが、ランダムの部分がうまく動作しません。
問題が表示され、文字入力を行うと途中で問題がランダムで切り替わってしまいます。
GetRandの配置がおかしいのかと思い、グローバル変数にしてみたところ、入力、成否の判定はできるのですが、問題がランダムになりません。

コード:

#include "DxLib.h"


int Cr = GetColor(255, 255, 255);      // 白の色コードを取得

typedef enum {
	eScene_syoki,
	eScene_Game,
}eScene;

static int Scene = eScene_syoki;//初期画面

void UpdateScene() {//状態の更新
	if (CheckHitKey(KEY_INPUT_RETURN) != 0) {
		Scene = eScene_Game;
	}
}

void Game(char Seikai[], int n) {//入力、判定の関数
	

	static int i = 0;
	static char Nyuryoku;
	static char Nyuryoku_end[30];


	Nyuryoku = GetInputCharWait(TRUE);//文字入力バッファから一文字取得
	if (Nyuryoku != 0 && Nyuryoku >= CTRL_CODE_CMP) {
		Nyuryoku_end[i] = Nyuryoku;
		if (Seikai[i] == Nyuryoku) {
			i++;

		}
		else {
			DrawFormatString(310, 260, GetColor(250, 0, 0), "×");
		}
		Nyuryoku_end[i] = '\0';
		DrawFormatString(240, 300, Cr, Nyuryoku_end);
		if (i == n) {
			i = 0;
			DrawFormatString(640 / 2 - 40, 200, Cr, "正解です");
			return;
		}
	}
}


void syoki() {//初期状態
	DrawFormatString(640 / 2 - 130, 240 - 32, Cr, "Enterを押すとスタートします。"); // 文字を描画する
}

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int){
	ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen(DX_SCREEN_BACK); //ウィンドウモード変更と初期化と裏画面設定

	// while(裏画面を表画面に反映, メッセージ処理, 画面クリア)
	while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0,CheckHitKey(KEY_INPUT_ESCAPE)==0) {
		
		int StrLen_S;
		char *Seikai[] = { "haro-","ganbare","kansei","dekiru" };
		int j = GetRand(3);
		StrLen_S = strlen(Seikai[j]);
		
		switch (Scene) {
		case eScene_Game:
			DrawFormatString(640 / 2, 50, Cr, Seikai[j]);
			Game(Seikai[j],StrLen_S);
			break;

		case eScene_syoki:
			syoki();
			break;
		}
		UpdateScene();
	}

	DxLib_End(); // DXライブラリ終了処理
	return 0;
}
計算の順序がおかしいとは思うのですが、どこがおかしいのかがわからず質問させて頂いた次第です。
出来ればどこが間違っていて、どの用に修正すればよいのかを教えていただきたいです。
もしくはそれ以前の問題なのでしょうか?
よろしくお願いします。

Re: 乱数(GetRand)がうまく動作しない。

Posted: 2016年9月13日(火) 09:21
by みけCAT
何も考えず毎周問題を変えてしまっているのが間違っています。
問題を変えたい時(開始時と正解を出した時)だけ問題を変えるようにしましょう。
乱猿 さんが書きました:GetRandの配置がおかしいのかと思い、グローバル変数にしてみたところ、入力、成否の判定はできるのですが、問題がランダムになりません。
jをグローバル変数にするという方針は、(誉められるかはともかく)間違っていません。
問題がランダムにならないのは、問題をランダムに(更新?)する処理を書いていないからではないですか?

Re: 乱数(GetRand)がうまく動作しない。

Posted: 2016年9月13日(火) 11:47
by 乱猿
おはようございます。
みけCAT様>前回から引き続きご回答いただき、まことにありがとうございます。

ご指摘がランダムの処理は、開始時と正解を出したときにとのことだったので、問題を表示するDrawFormatStringをGame関数の冒頭に置き、正解表示の後に再度GetRandで乱数をjに代入したところ、ランダムで問題が出てくるようになり、成否も出来るようになりましたが、問題文が欠けて表示されるようになってしまいました。
jに代入された数値の分、問題文の表示が消えるといった状況です。
”haro-”は正常に表示され、以降の"ganbare"は"anbare"と表示され、"kansei"は"nsei"に、"dekiru"は"iru"といった具合です。
なお、成否はすべて正常で"iru"と表示されていても"dekiru"で成否を判定しています。
問題文を消去するような事はしていないと思うのですが、文字列の処理が間違っているのでしょうか?
自分で解決できず質問ばかりで申し訳ないとは思うのですがご教授いただければ幸いです。

コード:

#include "DxLib.h"


int Cr = GetColor(255, 255, 255);      // 白の色コードを取得
int j = GetRand(3);

typedef enum {
	eScene_syoki,
	eScene_Game,
}eScene;

static int Scene = eScene_syoki;//初期画面

void UpdateScene() {//状態の更新
	if (CheckHitKey(KEY_INPUT_RETURN) != 0) {
		Scene = eScene_Game;
	}
}

void Game(char Seikai[], int n) {//入力、判定の関数
	
	static int i = 0;
	static char Nyuryoku;
	static char Nyuryoku_end[30];

	DrawFormatString(640 / 2, 50, Cr, &Seikai[j]);

	Nyuryoku = GetInputCharWait(TRUE);//文字入力バッファから一文字取得
	if (Nyuryoku != 0 && Nyuryoku >= CTRL_CODE_CMP) {
		Nyuryoku_end[i] = Nyuryoku;
		if (Seikai[i] == Nyuryoku) {
			i++;

		}
		else {
			DrawFormatString(310, 260, GetColor(250, 0, 0), "×");
		}

		Nyuryoku_end[i] = '\0';
		DrawFormatString(240, 300, Cr, Nyuryoku_end);
		if (i == n) {
			i = 0;
			DrawFormatString(640 / 2 - 40, 200, Cr, "正解です");
				j=GetRand(3);
				return;
		}
	}
}


void syoki() {//初期状態
	DrawFormatString(640 / 2 - 130, 240 - 32, Cr, "Enterを押すとスタートします。"); // 文字を描画する
}

int WINAPI WinMain(HINSTANCE, HINSTANCE, LPSTR, int){
	ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen(DX_SCREEN_BACK); //ウィンドウモード変更と初期化と裏画面設定

	// while(裏画面を表画面に反映, メッセージ処理, 画面クリア)
	while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0,CheckHitKey(KEY_INPUT_ESCAPE)==0) {

		int StrLen_S;
		char *Seikai[] = { "haro-","ganbare","kansei","dekiru" };
		StrLen_S = strlen(Seikai[j]);
		
		switch (Scene) {
		case eScene_Game:
			Game(Seikai[j],StrLen_S);
			break;

		case eScene_syoki:
			syoki();
			break;
		}
		UpdateScene();
	}

	DxLib_End(); // DXライブラリ終了処理
	return 0;
}
よろしくお願いします。

Re: 乱数(GetRand)がうまく動作しない。

Posted: 2016年9月13日(火) 15:19
by みけCAT
乱猿 さんが書きました:問題を表示するDrawFormatStringをGame関数の冒頭に置き、正解表示の後に再度GetRandで乱数をjに代入したところ、ランダムで問題が出てくるようになり、成否も出来るようになりましたが、問題文が欠けて表示されるようになってしまいました。
jに代入された数値の分、問題文の表示が消えるといった状況です。
”haro-”は正常に表示され、以降の"ganbare"は"anbare"と表示され、"kansei"は"nsei"に、"dekiru"は"iru"といった具合です。
DrawFormatStringを移動したからではなくて、DrawFormatStringで文字列のj文字(バイト)目から表示する仕様に変更したからそういう表示になっただけですね。
常に最初から表示したければ、26行目の&Seikai[j]を素直にSeikaiとすればいいでしよう。

また、書式を使わないのであれば、DrawFormatStringではなく素直にDrawStringを使うべきでしょう。
なぜなら、書式の処理が入らない分速そうな気がしますし、書式を利用しないのにDrawFormatStringを使うと、うっかり書式として認識される%を入れて誤動作させる心配もあるからです。

Re: 乱数(GetRand)がうまく動作しない。

Posted: 2016年9月13日(火) 15:29
by みけCAT
今のコードは、
  1. DrawFormatString(640 / 2, 50, Cr, Seikai[j]); をそのまま移動したらコンパイルエラーになった
  2. 適当に&をつけてみたらコンパイルが通った
みたいな感じで生成されたと予想できます。
もしそうだとすると、次のような疑問が出てきます。
  • どうして&をつけるという発想に至ったのですか?
  • どうして逆に[j]を取るという発想に至らなかったのですか?
  • そもそも&や[j]の意味をわかっていますか?
深く考えず機械学習のように適当にコードを書き換えてたまたま改善したものを採用するのではなく、
しっかり各部分の意味をわかった上で組み立てる努力をするべきであるという気がします。

Re: 乱数(GetRand)がうまく動作しない。

Posted: 2016年9月13日(火) 17:48
by 乱猿
みけCAT様>ご回答感謝です。
みけCAT さんが書きました: また、書式を使わないのであれば、DrawFormatStringではなく素直にDrawStringを使うべきでしょう。
なぜなら、書式の処理が入らない分速そうな気がしますし、書式を利用しないのにDrawFormatStringを使うと、うっかり書式として認識される%を入れて誤動作させる心配もあるからです。
なるほど。そのような誤動作の可能性があるのですね。
もともと、正解の文の上に文字列で"ハロー"や"できる"と表示する使用にしたかったので、あわせてDrawFormatStringを使用していました。そこは使い分けをしていきたいと思います。
みけCAT さんが書きました: 今のコードは、
  1. DrawFormatString(640 / 2, 50, Cr, Seikai[j]); をそのまま移動したらコンパイルエラーになった
  2. 適当に&をつけてみたらコンパイルが通った
みたいな感じで生成されたと予想できます。
もしそうだとすると、次のような疑問が出てきます。
  • どうして&をつけるという発想に至ったのですか?
  • どうして逆に[j]を取るという発想に至らなかったのですか?
  • そもそも&や[j]の意味をわかっていますか?
ご指摘ごもっともです。
[j]で現在ランダムで選ばれているSeikaiを選択しているものだと思い込んでいて、[j]を取るという発想に至りませんでした。
&に関しては文字列配列を記憶しているメモリアドレスだと認識しています。
正直、そのまま移動してだめだったので、[j]を取らずコンパイラを通すには・・・アドレスを渡してみたらどうなるだろうと思って付けたらコンパイルが通ったと、まぁみけCAT様のおっしゃるとおりのありさまです。

&Seikai[j]が示しているのは文字列の[j]バイト目である事がわかっていなかった様です。
みけCAT さんが書きました: 深く考えず機械学習のように適当にコードを書き換えてたまたま改善したものを採用するのではなく、
しっかり各部分の意味をわかった上で組み立てる努力をするべきであるという気がします。
今回、ご指摘のとおりもっと各部の意味を理解する必要性を痛感いたしました。
しっかりと基本や意味を理解するように努力しようと思います。

Re: 乱数(GetRand)がうまく動作しない。

Posted: 2016年9月14日(水) 09:15
by 乱猿
最後の返信が仕事の直前だったので解決するのをわすれていました。

みけCAT様>素人丸出しの質問に、ご助力頂き本当にありがとうございました。
ご指摘の様に、しっかりと考えながら組み立てていく様に精進していこうと思います。

質問はこれで解決とさせていただきます。
ありがとうございました。