ゲームを作ってみる①~じゃんけん~
ゲームを作ってみる①
はじめに
初心者を脱却したいのであれば、一度自分でプログラムを作ってみるのがいいと、知恵袋で見かけたので、今回はc言語で簡単なゲームを自己流で作ってみようと思う。
といっても、私はまだ参考書を1冊写経した程度で大したものが作れる自信がないので、今回はひとまず簡単そうなじゃんけんゲームを作ってみる。
設計書作成
まず最初に、設計書を作ってみる。
1.プログラムを起動
↓
2.じゃんけんを開始
↓
3.プログラム側で敵のじゃんけんの値を決める
↓
4.自分側の入力
↓
5.比べる
↓
6.結果報告
↓
7.終了
やりたいことは大体7つに分かれた。
だいぶ雑だが仕事じゃないし、こんな感じで大丈夫だろう。これをもとにプログラムの大枠を作ってみる。
プログラム作成
我ながらこれは酷い。
もう少し自分で書き込めるかと思ったが本もネットも見ないとすぐに書けるのはこの程度だった。
これじゃあ、コンビニ店員に裏でprintfさんとか呼ばれそう
とりあえず自分の中でわからないところは、ダブルクォーテーション("")でくくったので、やりたいことを調べながら1つずつ処理を追加してみる。
一番最初は
""ランダムで値を決める関数を使う(ここで敵側の値を決める)""
ランダムな値を使う関数を調べてみたところ"rand関数"なるものが乱数を発生させてくれるらしい。
何か使えそうなので使ってみる。
使い方は
例)1~10のランダムな数値の出力
rand() % 10 + 1
こんな感じらしい。
おお、何かランダムに出てるっぽい!
試しにもう一回使ってみる。
え?
同じ値が出てきた。
同じ画像使ってるだけだろと思うかもしれないが違う 。本当に何度繰り返しても同じ値が返ってくる。
調べてみたら、どうやらrand関数だけでは完全なランダムな値を出せるわけではないらしい。
ランダムな値を出させるにはrand関数と別にsrand関数とtime関数を組み合わせて使うようだ。
rand関数は乱数生成
srand関数でその乱数の種類を決めている
time関数はsrand関数を可変にするために必要
要は、randだけだと
例(今回の私の環境)
4、7、6、4、6、7、3、10、2........
こんな風にあらかじめ決められているらしい。
それをsrandを使うことで変えられる。
srand(1)
srand(2)
srand(5)
srand(100)
こんな風にかっこの中に数字を入れると出てくる乱数の種類が変わる。
けど、このやり方だと数字が固定化されてrand関数使っているときと同じようにプログラムを呼び出すたびに同じパターンの乱数しか返らないので、time関数を使うことで回避する。time関数は現在時刻を出力する(※正確には違う)から基本同じ値を返さない。
srand((unsigned int)time(NULL));
基本定型文みたいなもので、この形で記述すればsrandが必要な数字の形で取得ができる。
おおお!同じコードを繰り返しても毎回出てくる値の順番もパターンも完全に変わっている。
これで、乱数を生成がわかったので、じゃんけんのコードに追加してみる。
本当は1回きりで終わると思ってたけど、予想よりも先が流そうなので次回もじゃんけんゲーム作っていきます。
まとめ
一応、忘れないように今回覚えたことをまとめ。
ゲームなどを作る際に必要な乱数を発生させるには、rand関数、srand関数、time関数の3つを組み合わせて作る。どれか一つでもかけてたらダメ。
それそれの役割は
rand関数は乱数生成
srand関数でその乱数の種類を決めている
time関数はsrand関数を可変にするために必要
使い方は
--------------------------------------------------------
int a;
srand((unsigned int)time(NULL));
a=rand() % 10 + 1;
print("d%\n",a);
--------------------------------------------------------
これで、1から10の値をランダムに出すことができる。
C言語のポインタについて 4.ポインタのポインタとは?
C言語のポインタについて4
1.そもそもポインタとは何か?
2.ポインタで出てくる&と*はなに?
3.宣言する型が違う場合どうなっているのか?
4.ポインタのポインタとは?
今回はポインタのポインタについて考えていきます。
ポインタのポインタとは文字通りで、今までは変数のアドレスをポインタに格納していましたが、そのポインタのアドレスを別のポインタに格納するというだけです。
例:今まで(普通のポインタ)
変数A
↑
ポインタB(変数Aのアドレスを格納している)
例:今回(ポインタのポインタ)
変数A
↑
ポインタB(変数Aのアドレスを格納している)
↑
ポインタC(ポインタBのアドレスを格納している)
このポインタCのことをポインタのポインタといいます。
これによって何かいろいろできるようになるみたい
使い方は、今までと同じ要領で作ればできますが一部違います。
ポインタのポインタを定義するときは、*が増えます。
普通のポインタの時は int *XXXX; と定義しますが
ポインタのポインタの時は int **XXXX; と一つ増えます。
この*の数が違うとうまくいきません。
※ちなみにポインタのポインタのポインタを使う場合 int ***XXXX; とまた1つ*が増えます。この要領で、ポインタのポインタの・・・・と数が増えても同じようにすれば大丈夫。
実際にやってみます。
今回は
Aは変数
Bはポインタ(Aのアドレスを格納)
Cはポインタのポインタ(Bのアドレスを格納)
となっています。
一つ一つ見ていきます。
1行目は変数Aの値なので
A= 123
これであっています。
2行目は変数Aのアドレスなので
&A= 0x7fff3ff3fc9c
これでいいです。
3行目はBの値です。BはAのアドレスを格納しているので
B= 0x7fff3ff3fc9c
2行目と同じ値のこれであっています。
4行目はBの*(アドレスの中の値)なので、0x7fff3ff3fc9cのアドレスの中の値は変数Aの123となるので
*B= 123
これであっています。
5行目はBポインタのアドレスなので
&B= 0x7fff3ff3fc90
これでいいです。
ここまでは前座です。(普通のポインタを知っていればできます。)
ここからが今回のメインです。
6行目はCの値です。CにはBのアドレスを格納していますなので
C= 0x7fff3ff3fc90
5行目と同じ値のこれであっています。
7行目はCの*(アドレスの中の値→変数Bの値)なので、0x7fff3ff3fc90のアドレスの中の値は変数Bの0x7fff3ff3fc9cとなるので
*C= 0x7fff3ff3fc9c
これであっています。
★重要
8行目はCの**(アドレスの中の値の中の値)
→要はCの2個前のアドレスの値なので、1個前はBの値の0x7fff3ff3fc9cで、それのもう一つ前はAの値なので
**C= 123
このように変数Aの値の123が出てきます。
※ポインタのポインタのポインタになったときは***とすればいい。
元の変数からポインタの数が増えた分だけ*を増やせばいい。
9行目はCのアドレスなのでこれでいいです。
&C= 0x7fff3ff3fca0
まとめ
1.ポインタのポインタは、ポインタのアドレスを格納する方法のこと
2.ポインタのポインタの宣言は【型 **変数名;】の形で宣言
例) int **XXXX;
3.ポインタのポインタから大もとの変数の値を見ることができる。
【**ポインタのポインタ;】この形で指定すれば、大もとの変数の値を指定できる。
例) **YYY;
4.ポインタが増えても同じように使える。
ポインタのポインタのポインタの宣言は
例) int ***XXXX;
ポインタのポインタのポインタからもとの変数の値を見るときは
例) ***YYY;
ポインタのポインタのポインタのポインタの宣言は
例) int ****XXXX;
ポインタのポインタのポインタのポインタからもとの変数の値を見るときは
例) ****YYY;
追記
2021年1月29日リンク先の追加しました。
C言語のポインタについて 3.宣言する型が違う場合どうなっているのか?
C言語のポインタについて3
1.そもそもポインタとは何か?
2.ポインタで出てくる&と*はなに?
3.宣言する型が違う場合どうなっているのか?
4.ポインタのポインタとは?
今回はポインタ変数の型について考えていこうと思います。
正直、ポインタ変数に入る値はアドレスという数字と文字の羅列(16進表記のメモリアドレス)なので、変数の型なんてポインタ変数であれば、どうでもいいように感じますが一応意味があります。
それは、ポインタ変数からの代入ができないことと、アドレス操作時のバイト数の違いです。
①ポインタ変数からの代入ができない
1つ目のポインタ変数から代入できないとは、
int A;
A=1;
char *Ap = &A;
*Ap = 123;
printf(" A = %d\n", A);
上記のプログラムだと、文字通り普通に値を代入ではなくポインタ変数経由で値を入れるとき(4行目)、型が一致していないとうまくいかない。逆に型を一致させていれば、
int A;
A=1;
int *Ap = &A;
*Ap = 123;
printf(" A = %d\n", A);
代入ができます。(これだけです。)
②アドレス操作時のバイト数の違い
2つ目にアドレス操作時のバイト数の違いは、型によってバイト数が決められています。
そのバイト数分を利用してアドレス操作をする必要があるため、そのときに型を意識する必要が出てきます。
【型サイズ一覧表】
型名 | サイズ (32 ビット) | サイズ (64 ビット) |
---|---|---|
char | 1 バイト | 1 バイト |
short int | 2 バイト | 2 バイト |
int | 4 バイト | 4 バイト |
long int | 4 バイト | 8 バイト |
float | 4 バイト | 4 バイト |
double | - | 8 バイト |
long | - | 8 バイト |
※32bitのdoubleとlongはどのような扱いになるかわからなかったので記載しまていません。
試しに、まず私の開発環境は何bitどうなっているのか確認してみようと思います。
やり方は簡単で、ポインタ変数のサイズを測ればわかります。
測った際のサイズが8の時は64bit、4の時は32bitとなります。
ポインタ変数のサイズが8なので、私の開発環境は64bitとなります。
次は、型のサイズが上記通りか確認してみます。
3行目~9行目は変数の宣言
11行目~17行目は変数に対応したポインタ変数の代入
を行っています。
型 | 変数名 | 値 |
---|---|---|
char | sizeof A | 1 |
Ap | 0x7ffcba727ca5 | |
Ap+1 | 0x7ffcba727ca6 | |
short int | sizeof B | 2 |
Bp | 0x7ffcba727ca6 | |
Bp+1 | 0x7ffcba727ca8 | |
int | sizeof C | 4 |
Cp | 0x7ffcba727cac | |
Cp+1 | 0x7ffcba727cb0 | |
long int | sizeof D | 8 |
Dp | 0x7ffcba727cc0 | |
Dp+1 | 0x7ffcba727cc8 | |
float | sizeof E | 4 |
Ep | 0x7ffcba727ca8 | |
Ep+1 | 0x7ffcba727cac | |
double | sizeof F | 8 |
Fp | 0x7ffcba727cb8 | |
Fp+1 | 0x7ffcba727cc0 | |
long | sizeof G | 8 |
Gp | 0x7ffcba727cb0 | |
Gp+1 | 0x7ffcba727cb8 |
出力結果をわかりやすく表にしてみました。
ひとつづつ見ていきます。
①.char型
char型のサイズは1バイトです。なので、アドレス操作(+1の奴)をした際にアドレスは1だけ値がずれます。
Ap:0x7ffcba727ca5
↓アドレス操作で+1したらアドレスの値が1バイト増える。
Ap+1:0x7ffcba727ca6
②short int型
short int型のサイズは2バイトです。なので、アドレス操作(+1の奴)をした際にアドレスは2だけ値がずれます。
Bp:0x7ffcba727ca6
↓アドレス操作で+1したらアドレスの値が2バイト増える。
Bp+1:0x7ffcba727ca8
③int型
int型のサイズは4バイトです。なので、アドレス操作(+1の奴)をした際にアドレスは4だけ値がずれます。
Cp:0x7ffcba727cac
↓アドレス操作で+1したらアドレスの値が4バイト増える。
Cp+1:0x7ffcba727cb0
※アドレスは16進数で決められているから0,1,2,3,4,5,6,7,8,9,a,b,c,d,e,f,まで行くと桁が繰り上がる。今回だと
0x7ffcba727cac
↓+1バイト(計1バイト)
0x7ffcba727cad
↓+1バイト(計2バイト)
0x7ffcba727cae
↓+1バイト(計3バイト)
0x7ffcba727caf
↓+1バイト(計4バイト)
0x7ffcba727cb0
となるから4バイト上がっていることがわかる。
④long int型
long int型のサイズは8バイトです。なので、アドレス操作(+1の奴)をした際にアドレスは8だけ値がずれます。
Dp:0x7ffcba727cc0
↓アドレス操作で+1したらアドレスの値が8バイト増える。
Dp+1:0x7ffcba727cc8
⑤float型
float型のサイズは4バイトです。なので、アドレス操作(+1の奴)をした際にアドレスは4だけ値がずれます。
Ep:0x7ffcba727ca8
↓アドレス操作で+1したらアドレスの値が4バイト増える。
Ep+1:0x7ffcba727cac
これも先程の16進数の考え通りすれば、
0x7ffcba727ca8
↓+1バイト(計1バイト)
0x7ffcba727ca9
↓+1バイト(計2バイト)
0x7ffcba727caa
↓+1バイト(計3バイト)
0x7ffcba727cab
↓+1バイト(計4バイト)
0x7ffcba727cac
4バイト分増えていることがわかる。
⑥double型
double型のサイズは8バイトです。なので、アドレス操作(+1の奴)をした際にアドレスは8だけ値がずれます。
Fp:0x7ffcba727cb8
↓アドレス操作で+1したらアドレスの値が8バイト増える。
Fp+1:0x7ffcba727cc0
これも16進数の考え通りで、
0x7ffcba727cb8
↓+1バイト(計1バイト)
0x7ffcba727ca9
↓+1バイト(計2バイト)
0x7ffcba727caa
↓+1バイト(計3バイト)
0x7ffcba727cab
↓+1バイト(計4バイト)
0x7ffcba727cac
↓+1バイト(計5バイト)
0x7ffcba727cad
↓+1バイト(計6バイト)
0x7ffcba727cae
↓+1バイト(計7バイト)
0x7ffcba727caf
↓+1バイト(計8バイト)
0x7ffcba727cc0
8バイト分増えていることがわかる。
⑦long型
long型のサイズは8バイトです。なので、アドレス操作(+1の奴)をした際にアドレスは8だけ値がずれます。
Gp:0x7ffcba727cb0
↓アドレス操作で+1したらアドレスの値が8バイト増える。
Gp+1:0x7ffcba727cb8
このように型自体にバイト数が決められていることが分かった。
※今回は+1を指定したが
char型で+2を指定すれば2バイトのアドレスの値が増える
char型で+3を指定すれば3バイトのアドレスの値が増える
char型で+4を指定すれば4バイトのアドレスの値が増える
int型で+2を指定すれば8バイトのアドレスの値が増える
int型で+3を指定すれば12バイトのアドレスの値が増える
int型で+4を指定すれば16バイトのアドレスの値が増える
double型で+2を指定すれば16バイトのアドレスの値が増える
double型で+3を指定すれば24バイトのアドレスの値が増える
double型で+4を指定すれば32バイトのアドレスの値が増える
一応、計算式を作るとすれば
【型のバイト数】×【+の数字】= アドレス操作時のバイト数
となります。
これで、型ごとのバイト数の増え方が表どおりであることが分かりました。
じゃあ、この型のバイトを利用してアドレス操作ってなにするのかというと、
文字列や数列などから目的の値をとる時などに使います。
例)
char A[]=”ABCDEFGH”
ここからDEFGHだけを取り出して別の変数に入れる。
int B=123456789
ここから56だけを取り出して別の変数に入れる。
このようなときにこの型を意識して使う必要が出てきます。
③まとめ
ポインタ変数で型を意識する理由
長ったらしくいろいろ書きましたが、要は3点です。
1.ポインタ変数からの代入ができないこと
※正直、ポインタ変数から代入をする機会はそこまでないと思うので、あまり気にしなくていい。
2.アドレス操作時のバイト数の違い
アドレス操作時に+1した際、型ごとに増え方が違う。
→使い道は、文字列や数列から目的の値をとる時などに意識する。
型名 | サイズ (32 ビット) | サイズ (64 ビット) |
---|---|---|
char | 1 バイト | 1 バイト |
short int | 2 バイト | 2 バイト |
int | 4 バイト | 4 バイト |
long int | 4 バイト | 8 バイト |
float | 4 バイト | 4 バイト |
double | - | 8 バイト |
long | - | 8 バイト |
3.特に型に思い入れがないなら、とりあえず変数と同じ型にしておけばいい
追記
2021年1月29日リンク先を追加しました。
C言語のポインタについて 2.ポインタで出てくる&と*はなに?
C言語のポインタについて2
1.そもそもポインタとは何か?
2.ポインタで出てくる&と*はなに?
3.宣言する型が違う場合どうなっているのか?
4.ポインタのポインタとは?
ネットでポインタを調べると、&と*のこの二つがクソほど出てきます。
けどこいつらが何なのかよくわからないまま進めるとポインタのポインタのところで躓きます。
なので、今回はポインタで出てくる&と*について考えます。
結論をいうと
&はアドレス演算子といい、アドレスを指します。
*は間接演算子といい、(指定したアドレスの)中の値を指します。
これだけです。
実際に見てみます。
結果
/*出力*/ → 結果
printf("%d\n",A); → 1
printf("%p\n",&A); → ox7ffd3993d5f4
printf("%p\n",A_p); → ox7ffd3993d5f4
printf("%p\n",*A_p); → 1
printf("%p\n",&A_p); → ox7ffd3993d5f8
となりました。
ひとつづつ考えていきます。
1行目は普通の変数Aを指定しているので、Aに入っている値は1なので1が出力されます。
2行目は普通の変数Aの&(アドレス)を指定しています。
よってAのアドレス、今回ですとox7ffd3993d5f4が出力されます。
3行目はポインタ変数A_pの値を指定しています。
ポインタ変数A‗pは&Aを代入しています。
&Aは2行目で触れたとおり変数Aのアドレスなので今回はox7ffd3993d5f4という2行目と同じ値が入ります。
ここまではなんとなくわかりますが、次がすこしややこしいです
4行目はポインタ変数A‗pの*(アドレスの中の値)を指しています。ポインタ変数A‗pの中の値はox7ffd3993d5f4です。このアドレスの中の値を返します。
アドレスox7ffd3993d5f4に対応した変数は変数Aです。その変数Aの値は1となるのでポインタ変数*A‗pの値は1となります。
5行目はA‗pの&(アドレス)なので今までのアドレスとは全く違うox7ffd3993d5f8という値が出てきます。
一応まとめますと、
&↓
①指定した変数のアドレスを返す。
*↓
①ポインタ変数だと使える(普通の変数には使えない)
②指定したポインタ変数のアドレスに対応した変数の値を返す。
追記
2021年1月29日リンク先を追加しました。
C言語のポインタについて 1.そもそもポインタとは何か?
C言語のポインタについて
最近C言語を勉強していて、ポインタが難しかったので
自分用に要点をまとめてみました。
まずポインタを理解するために大きく4つに分けてみました。
1.そもそもポインタとは何か?
2.ポインタで出てくる&と*はなに?
3.宣言する型が違う場合どうなっているのか?
4.ポインタのポインタとは?
今回は、1つ目のそもそもポインタとは何か?に触れてみようと思います。
※私自身がポインタを完全に理解しているわけではないので
修正点がありましたらその都度追記すると思いますのでご了承ください。
目次
ポインタとは
まずポインタとは何でしょうか。
wikipediaには以下のように書かれています。
ポインタ (pointer) とは、あるオブジェクトがなんらかの論理的位置情報でアクセスできるとき、それを参照する(指し示す)ものである。
(https://ja.wikipedia.org/wiki/ポインタ_(プログラミング))
簡単にまとめると
変数の値の格納場所(アドレス)を表すときに使うものです。
一応、概要を説明しますと、
パソコンがプログラムを処理するときをメモリというものを使います。
メモリとは変数の値を一時的に保存しておくものです。
そのメモリの中のどこに変数の値を格納したか(アドレス)をポインタを使うことでわかります。
要はパソコンも人間と同じように物事を考えるとき(プログラムを処理するとき)一時的に考えたことをメモ(メモリに保存)します。
その時、メモの何ページ(アドレス)に書いたかがポインタという方法を使うとわかるだけです。
ポインタを使うことで、処理の高速化やパソコンへの処理の負荷を軽減などすることができます。
また、ポインタを使うことで複雑な処理もできるようになります。
それでは実際にプログラムを使って考えてみようと思います。
実際に動かす
Case1.普通の変数の宣言、代入、出力
まず、普通の変数を宣言、代入、出力をしてみようと思います。
※このプログラムはこれからのプログラムのベースになるものなのでこれ自身はポインタをまだ使用していません。
結果
プログラム通り、Aの中に1という値を入れているので
出力結果は1となります。
Case2.変数Aのアドレスをみる
次に、変数Aのアドレス(メモの何ページか)を見てみようと思います。
やり方は以下のように出力する時、変数に&を追加すれば見れます。この時出力フォーマットはpを選んでください。
※%p以外を指定した場合でも値が出ますが違います。
printf("%p\n",&A);
結果
出力結果は
1
0x7ffca93e5684
となりました。
1行目はCase1の時の変数Aの値で、2行目はその変数Aのアドレス(メモの何ページか)です。
これは、人間では理解できませんがパソコンはこの表記で理解しています。
※このアドレスの出力結果は毎回違う値が出てきますのでこのテストと違う値が出ていても問題ありません。
Case3.ポインタ変数の宣言、代入、出力
続いて、ポインタ変数の宣言、代入、出力です。
先程までは、変数Aのアドレスを出力だけしました。
そうではなく、ポインタ変数を宣言、代入、出力をしてみようと思います。
1.ポインタ変数の宣言は、宣言時に*を変数の前につけるだけ
(これで、アドレスを格納する変数を宣言します。)
※今回int型のみでテストを行っていますが他の型でも同じ要領でコーディングします。
【例】目的の型 *変数名;
int *A_p;
2.ポインタ変数の代入は、代入時にアドレスを知りたい変数(今回だとA)の前に&をつける
(これで変数Aのアドレスをポインタ変数A‗pに格納しました。)
【例】アドレスを格納する変数 = &アドレスを知りたい変数;
A_p = &A;
3.ポインタ変数の出力は出力フォーマットに%pを指定
今回はA_pでいいです。&A_pとは書きません。
(これで、出力した際ポインタ変数A‗pを出力することができます。)
【例】printf("%p\n",アドレスを格納する変数);
printf("%p\n",A_p);
結果
出力結果は
1
0x7fffb4d4643c
0x7fffb4d4643c
となりました。
1行目はCase1の時の変数Aの値で、2行目はその変数Aのアドレス(メモの何ページか)です。
そして3行目は2行目と同じ値が入ります。これは、変数代入の際に&A(Aのアドレス)を指定しているためで、出力時の2行目と3行目が同じ値がとなります。
※先程も記載しましたが、このアドレスの出力結果は毎回違う値が出てきますのでこのテストと違う値が出ていても問題ありません。また、テストするたびに値が変わることもあります。
これで、本当に最低限のポインタを使うぐらいはできるはずです。
残りの3項目は後日また追加していこうと思います。
2021年1月29日、残りの3項目追加しました。