前回までの話(すごろくの演習問題のつづき)
演習問題:すごろくを作ってみよう
すごろく
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~6)
- あとは変数に好きにぶっこんで使う!
//乱数の初期化(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; }
すごろくの設計
- 初期盤面の表示
- サイコロを振る
- コマを進める
- ゴールした?
- YES:ループを抜ける
- NO :2に戻る
- おめでとうのメッセージ表示→終了
盤面の表示から考えてみる
盤面は以下のように表される。
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を変更して、表示がどう変化するかいろいろ試してみよう
せめて、すごろくらしく
最後に、すごろくとして完成させる!
- 初期盤面の表示
- サイコロを振る
- コマを進める
- ゴールした?
- YES:ループを抜ける
- NO :2に戻る
- おめでとうのメッセージ表示→終了
どのようにループを組み合わせれば、サイコロを振ってゴールまでコマを進められるかな?
繰り返しの条件、つまりゴールの条件
初めは、ゴールを超えたら、ゴール!
{
サイコロ振る
コマ進める
//盤面を表示する繰り返し
現在の盤面が表示される
SO _ _ _ _ _ _ _ _ _G
}
ループ抜けたら、ゴールしてるはずだから。。。
cout << "ゴール!おめでとう!" << endl;
追加:
さいころがピッタリじゃないとゴールできない
進めなかったり、戻ったりを追加
- ゲームとして考えてみると、すごろくはゲーム開始後必ず一度はサイコロを振って駒を進める処理を行う
- 。。。という事は、前判定?後判定?どちらが適していますか?
- 応用として、今回盤面の表示は、初期盤面と、ゲーム中の盤面表示と、同じループブロックを2回も書きました
- 現代的なプログラムでは同じことを2回書くのは無駄とされています。
- こんな時はどうすればいいのか調べてみよう