忍者ブログ
Profile
HN:gp-hss

職業:高校生

趣味:3DCG

言語:C全般

環境:VC++ 2008 EE

3DCG:Softimage Mod Tool

自己紹介:
ゲームプログラマー目指して勉強している者です。
現在 C++ 修得にむけて頑張っています。

Began study since 2009/8/21

Latest CM
[06/28 soulmorning]
Latest TB
1  2 
×

[PR]上記の広告は3ヶ月以上新規記事投稿のないブログに表示されています。新しい記事を書く事で広告が消えます。

author : gp-hss ×

久しぶりの覚書です。
ていうか本当に「かくしょ」と読んでしまう。
「おぼえがき」ね「おぼえがき」・・・。

ということで今回は #ifdef について簡単に説明していきたいと思います。
#ifdef とは読んで字のごとく ”もし○○が #define されていたならば” という感じの意味です。
例えば、

#ifdef CREAT
val = 1;
#else
#define CREAT
#endif

CREATが #define されていたならば
val = 1を実行し、
そうでなければ
CREATを #define する。
最後は必ず #endif で終わる。

また、必ずしも #else はいるわけではありません。
必要なときにだけ使うようにしましょう。

じつは #fidef 以外にも #ifndef , #undef というものがあります。
#ifndef は #ifdef とは逆で、”もし○○が#defineされていなければ”という意味です。
#undef はすでに #define されているものを無効にします。

#define CRE_DEF



#undef CRE_DEF

これでCRE_DEFは無効になります。
だいたいこんな感じです。
このことを知らなかった人にはぜひ参考にしてほしいです。
これを知ってるのと知らないのとでは全然違いますからね。

ではまた今度。

拍手[4回]

PR
author : gp-hss ×

とうとうこいつを解説するときが来ました。
この下長くなりますのでご注意を。

    1.グローバル変数の説明
int x
 ○ または × を置く座標の x 座標値。
 5 , 10 , 15 の x 座標に置くようにする。
それぞれの座標値は画面に出力される表の横に並んだ 1 , 2 , 3 と同じ座標値。

int y
 ○ または × を置く座標の y 座標値。
 1 , 3 , 5 の y 座標に置くようにする。
それぞれの座標値は画面に出力される表の縦に並んだ 1 , 2 , 3 と同じ座標値。

int y2
縦横の数値を入力したあと、「その場所には置けません。(改行)もう一度入力してください。」という表示がされる y 座標値。
正確には「その場所には置けません。」という出力があった座標の y 座標値。

int p_y
第1回目の入力をさせる y 座標値。
または、第1回目に ○ :  と出力される座標の y 座標値。
その y 座標値は 7 。

int x_y[][]
 ○ または × が置かれた座標にどちらが置かれたか記憶するオブジェクト。
 [ 0 ][ 0 ] ~ [ 2 ][ 2 ] までの範囲で数値を格納する。
例えば [ 0 ][ 2 ] に数値が格納された場合、出力された表の横 1 , 縦 3 の位置に ○ か × のどちらかが置かれたことを意味する。

int ans
出力された表からの視点で、縦、横、斜めに値を足したときに生成される値を格納するオブジェクト。
例えば横 1 で縦の場合、 x_y[ 0 ][ 0 ] ~ x_y[ 0 ][ 2 ] の値を足した値が格納される。 
右上がりの斜めの場合は、 x_y[ 0 ][ 2 ] , x_y[ 1 ][ 1 ] , x_y[ 2 ][ 0 ] を足した値が格納される。

int ans_val[]
各ゲーム(試合)での勝敗を表す数値を格納するオブジェクト。

int ans_i
オブジェクト ans_val[] を操作するためのオブジェクト。

int ans_i_j
ゲームが何試合行われたかを表す数値を格納するオブジェクト。


    2.指定した座標へカーソルを移動させる方法の説明
まずは、標準出力のハンドルを得るために HANDLE 型の変数( hOut とします)を宣言します。
次に、カーソルの座標値を扱っている構造体の型をもつ変数( coset とします)を宣言します。
そして、先ほど宣言した hOut に標準出力のハンドルを取得させます。

    hOut = GetStdHandle(STD_OUTPUT_HANDLE);

そしてカーソルの座標値を指定します。

    coset.X = 2;
    coset.Y = 2;

最後に指定した座標値にカーソルを移動させます。

    SetConsoleCursorPosition(hOut, coset);

これで完了です。
型については、こんな型があるんだな程度に思ってください。
ちなみに x 座標において 1 とは 1 バイト分を指しますが、 y 座標においては 2 バイト分の大きさを指します。
つまりカーソルを x 座標において 1 右に移動させた場合のカーソル移動距離を 1 とすると、カーソルを y 座標において 1 下に移動させた場合のカーソル移動距離は 2 なのです。
よって、 y 座標で移動させる場合は x 座標で移動させる場合の 2 倍の距離を移動します。
説明はわかりにくいと思いますが、やってみるとわかります。
何かを動かすプログラミングをするときはこういったことを考慮しなければいけません。
少なくともそのプログラムがコマンドプロンプト上で実行される場合ではのことですが。


   3.main()以外の関数の説明
void output_hyou()
まずは横の表を作っていきます。
 i を 3 で初期化し、 14 未満までの範囲で実行するようにし、1回処理が終わるごとに i に +5 したものを i に格納しています。
よって、それぞれの処理で i は以下のように値を変化させます。
1回目:i = 3
2回目:i = 8
3回目:i = 13
処理後:i = 18
 co.Y は 0 のまま一定ですが、 co.X は 3 , 8 , 13 と値を格納していきます。
それぞれの x 座標で [ kの値 ] を出力させています。
 k は 0 で初期化されていますが、 ++k なので k をインクリメントした値が出力されます。
 k++ だと k の値が出力され、その後 k がインクリメントされます。
 [ は1バイト文字なので数値自体は 4 , 9 , 14 の x 座標値に出力されます。

次に縦の表を作っていきます。
基本的には横の表を作るのと同じです。
 i の変化は、
1回目:i = 1
2回目:i = 3
3回目:i = 5
処理後:i = 7

最後に "横   縦" という文字列を出力します。
 ( x , y ) = ( 7 , 7 ) の位置から出力させています。


void output_setumei()
まずは i の変化について見ていきましょう。
1回目 ~ 5回目:i = 1 ~ 5
処理後:i = 6
カーソルの x 座標値は一定です。
文字列を出力するまえにカーソル位置を移動させています。
ここでどの文字列を出力させるかの選定で switch 分を利用しています。
 i = 1 のとき、 ( x , y ) = ( 55 , 1) の位置から文字列が表示されます。
つまり、行ごとにどの文字列を表示させるか選定しているわけです。


int maru_batu(int count)
この関数は勝敗判定のための関数です。
勝敗が定まったときには指定の場所に勝敗結果通知を出力します。
では上から順に for 文を見ていきましょう。

1.
 i と j はそれぞれ 0 ~ 2 までの値をとります。
i = 0 : j = 0 ~ 2
i = 1 : j = 0 ~ 2
i = 2 : j = 0 ~ 2
という具合で値をとっていきます。
そして、 x_y[ j ][ i ] というふうになっています。
よって、 x_y[ 0 ~ 2 ][ i ] の値を足した値を ans に格納しています。
つまりこれは、 「横」 の勝敗判定だということがわかります。
ans が 3 なら ○ が勝ち 、 -3 なら × が勝ちというふうになっています。
なぜかというと、○ が入力された場合はその座標を表す x_y[][] に 1 を、× の場合は -1 を格納しているからです。
勝敗がついたならば return 1 、つまり 1 を返します。
勝敗がついていない場合は、最後に ans を 0 で初期化しています。
ここで初期化しないと、複合代入演算子による演算でどんどん ans の値が増えていってしまうだけですね。

2.
これは前回の for 文の i と j が逆になっているだけですね。
つまり、 「縦」 の勝敗判定だということがわかります。
それ以外は前回の for 文と全く同じです。

3.
この for 文とセットなのは2つの if 文と1つの式文です。
これは、右下がりの 「斜め」 の勝敗判定です。
右下がりの場合、 x_y[ 0 ][ 0 ] , x_y[ 1 ][ 1 ] , x_y[ 2 ][ 2 ] ですね。
だからまず for 文でそれを足した値を ans に格納しているのです。
あとは前の for 文と全く同じです。

4.
この for 文では最後の右上がりの 「斜め」 の勝敗判定です。
この場合、x_y[ 0 ][ 2 ] , x_y[ 1 ][ 1 ] , x_y[ 2 ][ 3 ] が該当しますね。
ということで、 for 文に行く前に j を 2 で初期化し、for 文の中でデクリメントしています。
そしてすべての値を足した値を ans に格納しています。
あとは前の for 文と全く同じです。


最後の if 文では引き分け判定をしています。
 count が 10 のとき ○ と × で埋め尽くされているということになりますので、引き分けとなり 1 を返します。
なぜ 10 なのかは後の関数の説明で明らかになります。


最後に勝敗がつかなかった場合には 0 を返しています。 


void input_marubatu()
この関数は入力に関する制御から勝敗判定まで一挙に取り扱っています。
まず 入力をさせる回数は 9 回だけでいいというのは分かりますね。
なぜなら ○ と × を置けるところは 9 個しかないのですから。
ということで、この for 文は 9 回繰り返されるようにしています。
そして i が偶数か奇数かで ○ を入力させるターンか × を入力させるターンかを決定しています。
今回は偶数なら ○ のターンとなります。
ここでは分かりやすく i を 2 で初期化しています(本当は 0 です)。
○ のターンであれば ○ を出力させる関数をもってきています。
そして勝敗判定をする関数を呼び出し、勝敗がついたら for 文を抜けて関数を終了させるようにしています。
× のターンも同じことをさせています。


void output_maru()
まずは入力項目を出力させています。
 p_y ははじめ "横   縦" が出力された y 座標と同じ値をもっています。
つまり、まず "横   縦" が出力された行の先頭にカーソルを置き、文字列を出力させる前に改行文字を使って改行したところに項目を出力させています。
改行することで、次の行の先頭にカーソルが移動されます。
改行したことで1つ下の行にカーソルが移ったので p_y + 1 としています。
そして、なぜカーソルを x 座標 8 と 13 にしているのかというと、これはただ単に"横"という文字と"縦"という文字の位置に合わせるためです。
こうすることによって、プレイヤーは何の値を入力しているのか明示的になります。
最後に p_y++ することで次からは1つ下の行で今やったことをさせています。

次の if 文では横1 ~ 3 、縦1 ~ 3 までの範囲外の値が入力されたとき、指定の場所にその場所には置けないのでもう一度入力するように促す文を出力させます。
最初の if (p_y == (y2 + 1)) では今から出力する促し文が前回出力した促し文と重複していないか判断して、重複している場合は、前回の促し文を消す関数を呼び出しています。
促し文は入力項目と同じ y 座標値に出力しますので重複している場合は p_y == (y2 + 1) となるのです。
促し文を出力した後は goto 文で先ほどの入力のところまでプログラムの処理を戻しています。

次の if 文ではすでに何かが置かれていないかどうかを判定して、おかれている場合は重複の確認と促し文の出力をしたあと goto 文で戻しています。
 ○ か × かがおかれている場合、必ずその位置を表す x_y[][] に 1 か -1 が格納されています。

入力が正常に終わったならば、その座標を表す x_y[][] に ○ 表す 1 を格納しています。
例えば、横[1] , 縦[2] に ○ がおかれた場合、 x_y[ 0 ][ 1 ] に 1 が格納されます。

次に x と y に格納された値を専用の座標値に変換する関数を呼んで、そのあとにその位置にカーソルを移動して ○ を出力しています。


void output_batu()
ほとんど先の関数と同じで、相違点は入力が正常に完了したあとにその位置を表す x_y[][] に × を表す -1 が格納されるということだけです。


void cls_new()
この関数では空白で埋め尽くすことによって文字などを消しています。
行ごとに文字列を消すのに十分な量の空白を上書きしていきます。
ちなみにこの関数は、前の試合の勝敗と説明のところ以外を消すのに使います。
たとえば、もう一度ゲームをするときなどに使います。


void cls_string()
重複した促し文を消すのにつかいます。
促し文が表示されている座標を指定して、そこに空白を上書きしています。


void output_before()
前回の試合の結果を指定の場所に出力します。
前の試合の勝敗は別のオブジェクトに記憶させているので、条件分岐でどれを出力させるか指定しています。


void cls_new_be()
前の試合の結果の表示を消すために使います。
勝敗を記憶するオブジェクトは過去5回までしか記憶できないので、6回目からはまた新たに記憶するオブジェクトの先頭から順に勝敗を記憶させていきます。
よって、表示もまた新たに1から作っていかなければならないのです。


void x_y_val()
x , y の値を専用の座標値に変換します。
x の場合は座標値と横表の値との関係性を利用して変換していますが、 y のように switch をつかって変換させるのが簡単です。
ただ取りうる値の範囲が広いときは switch だと面倒なので、前者のような関係性や法則性を利用するわけです。


    4.main関数の説明
とりあえず WORD 型の変数の宣言は無視してください。
不必要です。
do によってもう一度試合をするときはもう一度この処理をさせるようにさせています。
最初の if はもう一度処理をさせる場合のオブジェクトの初期化です。

次に順次関数を呼び出して処理を進めさせます。
そして勝敗がついて main() に帰ってきたらその勝敗の結果を専用のオブジェクトに格納します。
最後に終了する場合の説明ともう一度する場合の説明を出力して getchar() で入力された情報を取得しています。




あぁ~疲れた。
もう無理・・・・・。
ていうか後半ものすごく雑だなぁ。
説明も下手だし。
ということでわからないところがあったらコメントください。
個別に返答していきます。

ではまた今度。

拍手[1回]

author : gp-hss ×

前々から言っていた類似関数の説明をしていきます。
具体的には、putc , putchar , puts , getc , getch , getchar , gets , fgets , scanf です。

・putc(プットシー)
    第一引数に指定した文字を第二引数で指定した出力ストリームに出力しま    す。

    putc('C', stdout);

    重要なのは出力ストリームを指定できるということです。
    基本的には fputc と同じです。

・putchar(プットチャー)
    第一引数に指定された文字を標準出力ストリームに出力します。

    putchar('C');

    重要なのは、出力は標準出力ストリームにされるということです。

・puts(プットエス)
    第一引数に指定された文字列へのポインタが指す文字列を標準出力ストリ    ームに出力する。

          puts("string");

          最後は改行されます。

・getc(ゲットシー)
    第一引数で指定された入力ストリームから文字を読み込みます。

    getc(stdin);

    これは入力ストリームを指定することができます。
    基本的には fgetc と同じです。

・getch(ゲットシーエイチ)
    標準入力ストリームからエコーなしで文字を読み込みます。
    つまり文字を入力するごとに ENTER を押す必要はありません。

    getch();

    ENTER を押さずに文字を読み込む。

・getchar(ゲットチャー)
    標準入力ストリームから文字を読み込みます。

    getchar();

    ENTER を押さないと読み込みが終了しません。

・gets(ゲットエス)
    第一引数で指定したオブジェクトに標準入力ストリームから1行分の文字列    を読み込み、格納します。

    char str[10];
    gets(str);

    ENTER を押して読み込みを終了したあと、改行文字を終端文字 \0 に変換    します。
    また、この関数はバッファオーバーランを防ぐことができません。
    fgets を用いることでバッファオーバーランの問題を防ぐことができる。

・fgets(エフゲットエス)
    第一引数で読み込んだ文字列を格納するオブジェクトを指定。
    第二引数で読み込める文字列のサイズを指定します。
    第三引数で入力ストリームを指定します。

    char str[20];

    fgets(str, 20, stdin);

    改行文字もサイズ内に含み、最後に終端文字 \0 を付与します。
    ですので格納される改行文字を考慮したサイズ数にしなければいけませ    ん。
    サイズ以上の文字列を入力した場合、例えば今回の場合は、
    20バイト以上文字列を入力した場合、それ以降に入力したものは入力ストリ    ームに残ってしまいます。
    20バイト以内なら入力ストリームには何も残りません。

・scanf(スキャンエフ)
    第一引数には変換指定を指定します(複数可)。
    第二引数には読み込んだものを格納するオブジェクトのアドレスを渡しま    す。

    scanf("%c", &ch1);

    入力ストリームには改行文字が残ってしまう。
    だが改行文字が残ってしまうことを回避する方法もある。
    変換指定に代入を抑止するが読み込みは行う * を記入することで実現す    る。

    scanf("%c%*c", &ch1);

    この場合一文字目は代入されるが、二文字目は読み込みは行われるが代    入はされない。
    よって、入力ストリームには改行文字は残らないのである。
    これを利用すれば、文字を読み込む場合でなくてもきちんと改行文字を消    費することができる。


なかなか長くなりましたが、今回は簡単に、かつ重要な部分をお届けできたかなと思います。
たまに入力待ちの状態にするために getchar() を呼び出したはいいものの、無視されたようになることがありますよね。
それは入力ストリームに改行文字が残ってしまっているためです。
改行文字が残らないようにするか、getchar()を2回呼んで1回目で改行文字を消費して2回目で入力待ちの状態を実現するという方法があります。
ちなみにまだまだ詳しい仕様がありますので、もっと知りたい方はご自分で調べてください。

ではまた今度。

拍手[0回]

author : gp-hss ×

えぇ~今回は(擬似)乱数を発生させる rand() 関数の説明をしようと思います。
この関数は精度が悪いらしいのですが、練習に使う分には十分です。

まずはこの関数を使うために stdlib.h をインクルードする必要があります。

        #include <stdlib.h>

使い方は、まず rand() 関数内に組み込まれている 乱数の種 の値を設定してやらなければいけません。
それを実現するのが srand() 関数です。
 srand() に何か適当な値を渡すことで 擬似乱数の発生系列 を設定できます。
例えば、何か適当な定数・・・・・・・まぁ 100 でも与えてみましょう。
そして rand() を呼び出した回数ごとに値を比較してみます。
僕の場合は、

1回目:365
2回目:1216
3回目:5415
4回目:16704
5回目:24504

となりました。
このプログラムを何度実行しても同じ結果になります。
なぜなら 乱数の種 の値が同じだからです。
乱数の発生系列が同じだから1回目、2回目に出る値もすべて同じになってしまいます。
それを改善するためのメジャーな方法が、現在時刻を取得してそれを与えるということです。
まずは、 time.h をインクルードする必要があります。

        #include <time.h>

つぎに srand() に与える値に現在時刻を設定します。

        srand(time(NULL));

こうすることによってプログラムを実行するたびに違う値を srand() に与えていますので、1回目、2回目に出る値も変わります。

#include <stdlib.h>
#include <time.h>

int main()
{
        int i;

        srand(time(NULL));
        
        for(i  = 0; i <  6; i++) 
                printf("%d回目:%d", i, rand());

        return 0;
}

ようするに srand() を呼び出すのはプログラムの一番最初のみでよいのです。
何度も何度も種の値を変更する必要はありません。
わかってもらえたでしょうか?

ではまた今度。

拍手[0回]

author : gp-hss ×
まず新規作成からプロジェクトを選択します。
ここで躓く方が多少いると思うのですが、コンソールアプリケーションWindows フォームアプリケーションの違いはなんでしょうか。
簡単に言えばコンソールアプリケーションはCの標準言語範囲でのプログラミングに向いていて、Windows フォームアプリケーションはWindowsAPIなどWindowsが提供している機能を使用することができます。

入門者はもちろんコンソールアプリケーションの方です。
コンソールアプリケーションにもCLRとWin32とがありますが、これはそれぞれ新仕様旧仕様を指します。
新仕様は機能は充実していますが、入門者には向いていません。
なので入門者には旧仕様のWin32コンソールアプリケーションが最も最適です。

ではWin32コンソールアプリケーションを選択してプロジェクト名を入力してOKをクリック。
Win32 アプリケーション ウィザードに着いたら「完了」ではなく「次へ」をクリックしてください。
「追加のオプション」という項目で空のプロジェクトを選択してください。
プリコンパイル済みヘッダー(意味はそのうち分かる)は初心者には向かないと思います。
まずは入門書の知識を完璧に身につけてからにしてください。
では完了をクリックしてください。

次に予め開かれているソースファイルにコードを打っていってください。
打ち終わったら上の右を指している緑色の三角みたいな矢印をクリックしてください。
これでデバックが開始されエラーなどがなければビルドされ自動的に実行されます。
ただものすごく簡単なプログラムなどをビルドする場合、いちいちデバックしなくても大丈夫なのであれば上のデバック>デバックなしで開始でデバックをしないでビルドされます。

ですがやはり本当に本当の入門者にはコマンドプロンプトでのコンパイル+実行をオススメします。


拍手[0回]

author : gp-hss ×
忍者ブログ | [PR]
 | PAGE TOP
write | reply | admin