前回までの話(すごろくの演習問題のつづき)

演習問題:すごろくを作ってみよう

すごろく
int goal_dist = 10;

盤面の構成
S_ _ _ _ _ _ _ _ _ _G
 1 2 3 4 5 6 7 8 9 10コマ

Oが自分の駒(スタート状態)
SO _ _ _ _ _ _ _ _ _G

3コマ目に自分がいる
S_ _ O _ _ _ _ _ _ _G
 
ゴールした状態
S_ _ _ _ _ _ _ _ _ OG
 
⓪初期盤面表示
①サイコロ振る(賽の目表示)
 rand() % 6 + 1 
②盤面表示(自駒を進める)
③ゴールしたか?
(goal_distを自分の駒が過ぎたか?)
  YES:おめでとうの表示
     NO:2に戻る

乱数とサイコロ

さいころは以下のように作ることができそう。

  1. 乱数の初期化(プログラム中ではじめの方で1度だけ行われればよい)
  2. 乱数の発生(1~6)
  3. あとは変数に好きにぶっこんで使う!
//乱数の初期化(1回だけやる)
srand((unsigned int)time(nullptr));
int saikoro;
//1~6の乱数を作ってsaikoroに代入
saikoro = rand() % 6 + 1;

さいころを10回振るサンプルソースコード

#include <iostream>
 
using std::cout;
using std::cin;
using std::endl;
 
int main()
{
	//const つけると定数になります。定数は変えられない数
	const int goal_dist = 10; 
	//乱数の初期化(1回だけやる)
	srand((unsigned int)time(nullptr));
 
	//さいころを10回振る繰り返し
	for (int i = 0; i < 10; i++)
	{
		cout << rand() % 6 + 1 << endl;
	}
 
 
	return 0;
}

すごろくの設計

  1. 初期盤面の表示
  2. サイコロを振る
  3. コマを進める
  4. ゴールした?
    • YES:ループを抜ける
    • NO :2に戻る
  5. おめでとうのメッセージ表示→終了

盤面の表示から考えてみる

盤面は以下のように表される。

S: スタート地点
G: ゴール地点
O: 自分のコマ

SO _ _ _ _ _ _ _ _ _G

初めに、自分のコマなしで盤面のみ表示してみる。

ソースコード:盤面の表示
#include <iostream>
 
using std::cout;
using std::cin;
using std::endl;
 
int main()
{
	//const つけると定数になります。定数は変えられない数
 
	//すごろくのマスの数を表す
	const int goal_dist = 10;
	//乱数の初期化(1回だけやる)
	srand((unsigned int)time(nullptr));
 
	//盤面を表示するブロック
	cout << "S";
	for (int i = 1; i <= goal_dist; i++)
	{
		cout << "_";
	}
	cout << "G";
	return 0;
}
実行結果:盤面の表示
S__________G
  • このままだと、“\_”が全部つながってしまって、自分が何コマ目にいるか分かりづらい。
    • スペースを空けるなど工夫してみよう
    • 例:S_ _ _ _ _ _ _ _ _ _G
    • スペースの表示の仕方とGの表示の仕方でちょっと条件判断がひつようになるよね

盤面に自分のコマを表示

自分のコマが今どこにいるかという変数を考える。

    //盤面上の位置を1~goal_distで表す
    int piece = 1;

この変数を使って、現在の盤面を表示するときに、
マスの位置=自コマの位置なら、“O”
それ以外なら、“_”を表示するように改良する。

ソースコード:自コマの表示
#include <iostream>
 
using std::cout;
using std::cin;
using std::endl;
 
int main()
{
	//const つけると定数になります。定数は変えられない数
	//すごろくのマスの数を表す
	const int goal_dist = 10;
	//乱数の初期化(1回だけやる)
	srand((unsigned int)time(nullptr));
	//すごろくのコマを表す(1がスタート、goal_distがゴール)
	int piece = 4;//特に意味はないが4マス目にいることにする
 
	//盤面を表示するブロック
	cout << "S";//スタートを表示
	for (int i = 1; i <= goal_dist; i++)
	{
		//iがpieceと同じときは"O"それ以外はマス"_"を表示
		if (i == piece)
			{cout << "O";} //自コマを表示するブロック
		else
			{cout << "_";} //マスを表示するブロック
 
		//goal_dist回目はスペースじゃなくGoalを表示
		if (i != goal_dist)
			{cout << " ";} //最後以外はスペースを入れる
		else
			{cout << "G";} //最後はスペースじゃなくゴール(G)
	}
 
	return 0;
}
実行結果:自コマの表示

goal_dist = 10で
piece = 1 のとき

SO _ _ _ _ _ _ _ _ _G

goal_dist = 10で
piece = 4 のとき

S_ _ _ O _ _ _ _ _ _G
  • goal\_distを変えると盤面のマスの数が変化するよね?
  • 自分のマスの位置とgoal\_distを変更して、表示がどう変化するかいろいろ試してみよう

せめて、すごろくらしく

最後に、すごろくとして完成させる!

  1. 初期盤面の表示
  2. サイコロを振る
  3. コマを進める
  4. ゴールした?
    • YES:ループを抜ける
    • NO :2に戻る
  5. おめでとうのメッセージ表示→終了

どのようにループを組み合わせれば、サイコロを振ってゴールまでコマを進められるかな?

繰り返しの条件、つまりゴールの条件
初めは、ゴールを超えたら、ゴール!
{
サイコロ振る
コマ進める

//盤面を表示する繰り返し
現在の盤面が表示される
SO _ _ _ _ _ _ _ _ _G
}
ループ抜けたら、ゴールしてるはずだから。。。
cout << "ゴール!おめでとう!" << endl;

追加:
さいころがピッタリじゃないとゴールできない
進めなかったり、戻ったりを追加
  • ゲームとして考えてみると、すごろくはゲーム開始後必ず一度はサイコロを振って駒を進める処理を行う
    • 。。。という事は、前判定?後判定?どちらが適していますか?
  • 応用として、今回盤面の表示は、初期盤面と、ゲーム中の盤面表示と、同じループブロックを2回も書きました
    • 現代的なプログラムでは同じことを2回書くのは無駄とされています。
    • こんな時はどうすればいいのか調べてみよう