Download : sample-001.c ( SJIS 版 )
# # adddate.m4 (20141128) created by /home/kurino/bin/MkAddDateM4.sh # /* * 2014/11/28 sample-001.c */ /* * フラグ * * 利用方法 * コンパイル * cc -Ic:\usr\c\include -o BASENAME.exe sample-001.c * 実行 * BASENAME */ #include <stdio.h> /* * trump * トランプの番号から、その名前を表示する */ void trump ( int number ) { if ( number == 1 ) { /* 番号が 1 なら */ printf ( "エース\n" ); } else { printf ( "範囲外です\n" ); } } /* * main * */ int main( int argc, char *argv[] ) { trump ( 1 ); trump ( 20 ); return 0; }
C:\usr\c>sample-001 C:\usr\c>
v11
/* * board.c */ #include <stdio.h> /* 表示の部分 */ /* print_board : 与えらたボードデータを表示する <<ポイント>> 配列の引数には特殊事情がある : 後回し */ void print_board ( char board[8][8] ) { int tate; /* 縦の添字 */ /* 追加(Copy) */ int yoko; /* 横の添字 */ /* 表示機能 (移動) */ /* ボードの内容を出力する */ /* 8x8 の内容を全て出力(標準出力)すればよい */ /* 配列の出力 -> for 文でやればよい */ /* 二次元配列なので、二重ループになる */ /* A B C D E F G H \yoko 0 1 2 3 4 5 6 7 tate 1 0 +----> 2 1 | 3 2 v 4 3 5 4 6 5 7 6 8 7 */ printf ( " A B C D E F G H\n" ); printf ( " ┏━┳━┳━┳━┳━┳━┳━┳━┓\n" ); for ( tate = 0; tate < 8; tate++ ) { printf ( "%d ┃", tate + 1 ); /* 左の、この行の枠 */ for ( yoko = 0; yoko < 8; yoko++ ) { if ( board[tate][yoko] == ' ') { /* 空き */ printf ( " " ); } else if ( board[tate][yoko] == 'o') { /* 白 */ printf ( "◯" ); } else /* if board[tate][yoko] == 'x') */ { /* 黒 */ printf ( "●" ); } printf ( "┃" ); /* コマとコマの間の枠 */ } printf ( "\n" ); /* 改行 */ if ( tate < 8 - 1 ) { /* 最後は、これを使わない */ printf ( " ┣━╋━╋━╋━╋━╋━╋━╋━┫\n" ); } else { /* 最後の行に使うのはこれ */ printf ( " ┗━┻━┻━┻━┻━┻━┻━┻━┛\n" ); } } } /* * 初期化 */ void init_board( char board[8][8] ) { int tate; /* 縦の添字 */ int yoko; /* 横の添字 */ /* ゲーム盤の初期化 */ /* とりあえず、全部 ' ' にして、後から真ん中の4つを置く */ /* とりあえず、全部空白 */ for ( tate = 0; tate < 8; tate++ ) { for ( yoko = 0; yoko < 8; yoko++ ) { board[tate][yoko] = ' '; /* 全ての場所を空白に */ } } /* 真ん中の 4 つだけ特別処理 */ board[3][3] = 'o'; /* D4 なので白 */ board[4][4] = 'o'; /* E5 なので白 */ board[3][4] = '*'; /* D5 なので黒 */ board[4][3] = '*'; /* E4 なので黒 */ }
/* * othello v10 (2014/11/14) の main.c */ #include <stdio.h> /* * main */ int main(int argc, char *argv[]) { char board[8][8]; /* ゲームボード */ char yoko; /* 横の記号 'A' 〜 'F' */ char tate; /* 縦の記号 '1' 〜 '8' */ char color; /* 色の記号 '*' : 黒 / 'o' : 白 */ init_board ( board ); /* 引数に配列名しか指定しない */ print_board ( board ); /* 引数に配列名しか指定しない */ while ( 0 < 1 ) { printf ( "次の一手> " ); /* ここで入力をする */ /* キーボードから 「A2*<改行>」とされた事を想定 */ yoko = getchar(); /* 横の指定の一文字 'A' を入力 */ if ( yoko == 'Z' ) { /* 'Z' が入ったら.. */ printf ( "プログラムを終了します...\n" ); return 0; /* プログラムを終了 */ } tate = getchar(); /* 縦の指定の一文字 '2' を入力 */ color = getchar(); /* 色の記号 '*' を入力 */ getchar(); /* 改行を読み飛ばす */ printf ( "着手 (%c,%c) %c ", yoko, tate, color ); if ( check_sandwich_all_direct ( board, yoko, tate, color ) ) { printf ( "置く事ができます。\n" ); } else { printf ( "置く事ができません。\n" ); } /* その結果が反映されているかどうかを確認する */ print_board ( board ); } return 0; }
/* * オセロ盤プログラム (version 10 : 2014/11/14) * * 機能 * 初期化の部分 * 表示の部分 : 位置を表す記号 1 〜 8, A 〜 F * 入力の部分(コマを置いてみたい..) * 入力の指示はキーボード * ゲームをしてみたい * 繰返し入力をする * 繰返しは while で実現できるが * いつまで繰り返すか ? * -> ルールでは、両方パスするまで * とりあえず、サボって * 無限に繰り返す事にする * ( 終了は 'Z' を入れる ) * v8 に向けて * 着手禁止の所にはうてないようにしたい * 「着手禁止の所」 * 既にコマがある * 相手のコマをひっくりかえせない * 挟んだコマを自動的にコマをひっくりかえして欲い * -> * コマを引っくり返す * 自分のコマで、相手のコマを挟む * イメージ : 黒で白を挟む * *oooo* -> ****** * ^ ^ * 元々 打つ手 * 上記の例は打つ手の左の状況 * この他に全部で 8 方向あるので、 * 他の 7 方向も考える * 取り敢えず、右横方向だけを考える * * 右方向の着手チェック * */ #include <stdio.h> /* ボードを作る */ /* ボードは、8x8 の要素からなる */ /* ボードの一つの要素には 空、黒、白のいずかが入る */ /* 空 ' ' , 白 'o', 黒 '*' -> 一文字 */ /* 'A2' に黒 -> put_board_black ( 'A', '2' ) -> board[1][0] = '*'; */ char get_board ( char board[8][8], char yoko, char tate ) { return board[tate-'1'][yoko-'A']; } void put_board ( char board[8][8], char yoko, char tate, char color ) { board[tate-'1'][yoko-'A'] = color; /* 本当は、ここで tate, yoko, color が正しい値かを チェックする必要がある (後回し) */ } void put_board_black ( char board[8][8], char yoko, char tate ) { put_board ( board, yoko, tate, '*' ); /* put_board_black ( 'A', '2' ) -> board['2'-'1']['A'-'A'] = '*'; -> board[1][0] = '*'; */ } void put_board_white ( char board[8][8], char yoko, char tate ) { put_board ( board, yoko, tate, 'o' ); } /* * oposit ( char color ) * 自分の色を指定して、相手の色を返す * '*' -> 'o' * 'o' -> '*' */ char oposit ( char color ) { /* if ( color == '*' ) { return 'o'; } else if ( color == 'o' ) { return '*'; } */ return 'o' + '*' - color; } /* 横方向のチェックを行う */ int check_yoko ( char yoko ) { if ( yoko <= 'F' ) { if ( 'A' <= yoko ) { return 1; /* 'A' <= yoko <= 'F' なので OK */ } } return 0; /* その他の場合は、駄目 */ } /* 横方向のチェックを行う */ int check_tate ( char tate ) { if ( tate <= '8' ) { if ( '1' <= tate ) { return 1; /* '1' <= tate <= '8' なので OK */ } } return 0; /* その他の場合は、駄目 */ } int check_play ( char yoko, char tate ) { if ( check_yoko ( yoko ) ) { if ( check_tate ( tate ) ) { return 1; } } return 0; } int check_sandwich_play_direct_sub ( char board[8][8], char yoko, char \ tate, char color, char ocolor, int ydir, int tdir ) { yoko = yoko + ydir; tate = tate + tdir; if ( check_play ( yoko, tate ) ) { /*右も左両方*/ if ( get_board ( board, yoko, tate ) == color ) { return 1; } else if ( get_board ( board, yoko, tate ) == ocolor ) { return check_sandwich_play_direct_sub ( board, yoko, tate, color, \ ocolor, ydir, tdir ); } } return 0; } /* * check_sandwich_play_direct ( char board[8][8], char yoko, char tate, char \ color ) * * P : 現在のゲーム盤 (board) の状態で、(yoko,tate) に color のコマが置けるかどうか ? * 置けたら 1 (0 以外 : C 言語の「真」) / 置けない場合は 0 (C 言語の「偽」) */ int check_sandwich_play_direct ( char board[8][8], char yoko, char tate, \ char color, int ydir, int tdir ) { /* 「置ける」条件 */ if ( get_board ( board, yoko, tate ) == ' ' ) { /* まず、そこに他のコマがない事を確認 */ yoko = yoko + ydir; /* 一つ横にずらす */ tate = tate + tdir; /* 一つ横にずらす */ if ( check_play ( yoko, tate ) ) { /* 盤外でない事を確認 */ char ocolor = oposit ( color ); /* 相手の色を入手 */ /* color == 'o' -> '*', color == '*' -> 'o' */ if ( get_board ( board, yoko, tate ) == ocolor ) { /* 一つ右が相手の色である事を確認 */ return check_sandwich_play_direct_sub ( board, yoko, tate, color, \ ocolor, ydir, tdir ); /* 後は Q に任せる */ } } } return 0; /* どれかの条件が不成立なら、全体として不成立 (偽:0) を返す */ } int check_sandwich_all_direct ( char board[8][8], char yoko, char tate, \ char color ) { int ydir; int tdir; for ( ydir = -1; ydir <= 1; ydir++ ) { for ( tdir = -1; tdir <= 1; tdir++ ) { if ( tate * tate + yoko * yoko != 0 ) { /* tate = yoko = 0 の時だけ不成立 */ if ( check_sandwich_play_direct ( board, yoko, tate, color, ydir, tdir ) \ ) { return 1; } } } } return 0; }
# # v11 の makefile # # - makefile の基本的な構造 # -- rule の集合 # -- rule の書き方 # 作りたい物 : 材料 # <TAB> 作り方 # # rule の意味 # 「作りたい物」が作りたければ「材料」を利用して、「作り方」を実行する # 材料がなければ、材料も作ろうとする !! # # - make コマンドの実行 # make で引数を指定しなければ、最初のルールを実行しようとする # 引数(作りたい物)が指定されると、指定された物を作ろうとする # <ポイント> # 既に、作りたいものが存在して、材料より新しいなら、何もしない # 作りたいものがないか、材料より古いならば、作り直す # 作り方をしらない物が材料に指定されていても、「それがあれば」よい # 材料がなく、作り方もしらないとエラーになる # # board.c 系列がふえた.. # othello.exe : othello.o main.o board.o cc -o othello.exe othello.o main.o board.o othello.o : othello.c cc -c othello.c main.o : main.c cc -c main.c board.o : board.c cc -c board.c clean : rm *.o *.exe makezip : zip v11.zip makefile main.c othello.c board.c
v12
/* * board.c */ #include <stdio.h> /* 表示の部分 */ /* print_board : 与えらたボードデータを表示する <<ポイント>> 配列の引数には特殊事情がある : 後回し */ void print_board ( char board[8][8] ) { int tate; /* 縦の添字 */ /* 追加(Copy) */ int yoko; /* 横の添字 */ /* 表示機能 (移動) */ /* ボードの内容を出力する */ /* 8x8 の内容を全て出力(標準出力)すればよい */ /* 配列の出力 -> for 文でやればよい */ /* 二次元配列なので、二重ループになる */ /* A B C D E F G H \yoko 0 1 2 3 4 5 6 7 tate 1 0 +----> 2 1 | 3 2 v 4 3 5 4 6 5 7 6 8 7 */ printf ( " A B C D E F G H\n" ); printf ( " ┏━┳━┳━┳━┳━┳━┳━┳━┓\n" ); for ( tate = 0; tate < 8; tate++ ) { printf ( "%d ┃", tate + 1 ); /* 左の、この行の枠 */ for ( yoko = 0; yoko < 8; yoko++ ) { if ( board[tate][yoko] == ' ') { /* 空き */ printf ( " " ); } else if ( board[tate][yoko] == 'o') { /* 白 */ printf ( "◯" ); } else /* if board[tate][yoko] == 'x') */ { /* 黒 */ printf ( "●" ); } printf ( "┃" ); /* コマとコマの間の枠 */ } printf ( "\n" ); /* 改行 */ if ( tate < 8 - 1 ) { /* 最後は、これを使わない */ printf ( " ┣━╋━╋━╋━╋━╋━╋━╋━┫\n" ); } else { /* 最後の行に使うのはこれ */ printf ( " ┗━┻━┻━┻━┻━┻━┻━┻━┛\n" ); } } } /* * 初期化 */ void init_board( char board[8][8] ) { int tate; /* 縦の添字 */ int yoko; /* 横の添字 */ /* ゲーム盤の初期化 */ /* とりあえず、全部 ' ' にして、後から真ん中の4つを置く */ /* とりあえず、全部空白 */ for ( tate = 0; tate < 8; tate++ ) { for ( yoko = 0; yoko < 8; yoko++ ) { board[tate][yoko] = ' '; /* 全ての場所を空白に */ } } /* 真ん中の 4 つだけ特別処理 */ board[3][3] = 'o'; /* D4 なので白 */ board[4][4] = 'o'; /* E5 なので白 */ board[3][4] = '*'; /* D5 なので黒 */ board[4][3] = '*'; /* E4 なので黒 */ }
/* * othello v10 (2014/11/14) の main.c */ #include <stdio.h> /* * main */ int main(int argc, char *argv[]) { char board[8][8]; /* ゲームボード */ char yoko; /* 横の記号 'A' 〜 'F' */ char tate; /* 縦の記号 '1' 〜 '8' */ char color; /* 色の記号 '*' : 黒 / 'o' : 白 */ init_board ( board ); /* 引数に配列名しか指定しない */ print_board ( board ); /* 引数に配列名しか指定しない */ while ( 0 < 1 ) { printf ( "次の一手> " ); /* ここで入力をする */ if ( color == '* ' ) { /* 人間の手順 */ /* キーボードから 「A2*<改行>」とされた事を想定 */ yoko = getchar(); /* 横の指定の一文字 'A' を入力 */ if ( yoko == 'Z' ) { /* 'Z' が入ったら.. */ printf ( "プログラムを終了します...\n" ); return 0; /* プログラムを終了 */ } tate = getchar(); /* 縦の指定の一文字 '2' を入力 */ getchar(); /* 改行を読み飛ばす */ } else { /* コンピュータ */ char result[2]; /* 置く場所を保存する配列 */ if ( computer ( result, board, color ) == 1 ) { yoko = result[0]; /* 打つ結果を、変数に保存 */ tate = result[1]; /* 打つ結果を、変数に保存 */ /* 配列名を関数に渡すと、関数の中で、配列の要素の値を変更できる */ } else { printf ( "うてません\n" ); return 0; /* プログラムを終了 */ } } printf ( "着手 (%c,%c) %c ", yoko, tate, color ); if ( check_sandwich_all_direct ( board, yoko, tate, color ) ) { printf ( "置く事ができます。\n" ); /* (v12) 挟んだコマを実際に引っくり返す */ turn_sandwich_all_direct ( board, yoko, tate, color ); /* (v12) そこにコマを置く */ put_board ( board, yoko, tate, color ); } else { printf ( "置く事ができません。\n" ); } /* その結果が反映されているかどうかを確認する */ print_board ( board ); } return 0; }
/* * オセロ盤プログラム (version 11 : 2014/11/28) * * 機能 * 初期化の部分 * 表示の部分 : 位置を表す記号 1 〜 8, A 〜 F * 入力の部分(コマを置いてみたい..) * 入力の指示はキーボード * ゲームをしてみたい * 繰返し入力をする * 繰返しは while で実現できるが * いつまで繰り返すか ? * -> ルールでは、両方パスするまで * とりあえず、サボって * 無限に繰り返す事にする * ( 終了は 'Z' を入れる ) * v10 -> v11 * v10 では、8 方向のチェックをした * 着手禁止のチェックができるようになった * v11 * 着手できるなら、自動的に挟んだコマをひっくりかえしたい * 基本はチェックのプログラムが、ほぼ、ひっくりかえしのプログラムと同等 * 「天下り」ですが、チェックの関数を変更してひっくりかえし関数を作る */ #include <stdio.h> /* ボードを作る */ /* ボードは、8x8 の要素からなる */ /* ボードの一つの要素には 空、黒、白のいずかが入る */ /* 空 ' ' , 白 'o', 黒 '*' -> 一文字 */ /* 'A2' に黒 -> put_board_black ( 'A', '2' ) -> board[1][0] = '*'; */ char get_board ( char board[8][8], char yoko, char tate ) { return board[tate-'1'][yoko-'A']; } void put_board ( char board[8][8], char yoko, char tate, char color ) { board[tate-'1'][yoko-'A'] = color; /* 本当は、ここで tate, yoko, color が正しい値かを チェックする必要がある (後回し) */ } void put_board_black ( char board[8][8], char yoko, char tate ) { put_board ( board, yoko, tate, '*' ); /* put_board_black ( 'A', '2' ) -> board['2'-'1']['A'-'A'] = '*'; -> board[1][0] = '*'; */ } void put_board_white ( char board[8][8], char yoko, char tate ) { put_board ( board, yoko, tate, 'o' ); } /* * oposit ( char color ) * 自分の色を指定して、相手の色を返す * '*' -> 'o' * 'o' -> '*' */ char oposit ( char color ) { /* if ( color == '*' ) { return 'o'; } else if ( color == 'o' ) { return '*'; } */ return 'o' + '*' - color; } /* 横方向のチェックを行う */ int check_yoko ( char yoko ) { if ( yoko <= 'F' ) { if ( 'A' <= yoko ) { return 1; /* 'A' <= yoko <= 'F' なので OK */ } } return 0; /* その他の場合は、駄目 */ } /* 横方向のチェックを行う */ int check_tate ( char tate ) { if ( tate <= '8' ) { if ( '1' <= tate ) { return 1; /* '1' <= tate <= '8' なので OK */ } } return 0; /* その他の場合は、駄目 */ } int check_play ( char yoko, char tate ) { if ( check_yoko ( yoko ) ) { if ( check_tate ( tate ) ) { return 1; } } return 0; } int check_sandwich_play_direct_sub ( char board[8][8], char yoko, char \ tate, char color, char ocolor, int ydir, int tdir ) { yoko = yoko + ydir; tate = tate + tdir; if ( check_play ( yoko, tate ) ) { /*右も左両方*/ if ( get_board ( board, yoko, tate ) == color ) { return 1; } else if ( get_board ( board, yoko, tate ) == ocolor ) { return check_sandwich_play_direct_sub ( board, yoko, tate, color, \ ocolor, ydir, tdir ); } } return 0; } /* * check_sandwich_play_direct ( char board[8][8], char yoko, char tate, char \ color ) * * P : 現在のゲーム盤 (board) の状態で、(yoko,tate) に color のコマが置けるかどうか ? * 置けたら 1 (0 以外 : C 言語の「真」) / 置けない場合は 0 (C 言語の「偽」) */ int check_sandwich_play_direct ( char board[8][8], char yoko, char tate, \ char color, int ydir, int tdir ) { /* 「置ける」条件 */ if ( get_board ( board, yoko, tate ) == ' ' ) { /* まず、そこに他のコマがない事を確認 */ yoko = yoko + ydir; /* 一つ横にずらす */ tate = tate + tdir; /* 一つ横にずらす */ if ( check_play ( yoko, tate ) ) { /* 盤外でない事を確認 */ char ocolor = oposit ( color ); /* 相手の色を入手 */ /* color == 'o' -> '*', color == '*' -> 'o' */ if ( get_board ( board, yoko, tate ) == ocolor ) { /* 一つ右が相手の色である事を確認 */ return check_sandwich_play_direct_sub ( board, yoko, tate, color, \ ocolor, ydir, tdir ); /* 後は Q に任せる */ } } } return 0; /* どれかの条件が不成立なら、全体として不成立 (偽:0) を返す */ } int check_sandwich_all_direct ( char board[8][8], char yoko, char tate, \ char color ) { int ydir; int tdir; for ( ydir = -1; ydir <= 1; ydir++ ) { for ( tdir = -1; tdir <= 1; tdir++ ) { if ( tate * tate + yoko * yoko != 0 ) { /* tate = yoko = 0 の時だけ不成立 */ if ( check_sandwich_play_direct ( board, yoko, tate, color, ydir, tdir ) \ ) { return 1; } } } } return 0; } /* v12:2014/11/28 追加部分 */ /* 以下の三つの関数を追加 */ /* 関数名の check -> turn に書き換えた */ /* 変更部分: チェックの結果、挟んでいる事がわかったら、 現在着目している場所の「コマを引っくり返す」という手続きを挿入 */ /* check から turn に変更する */ /* check の仕組 いまみている所が敵のコマなら、一つ先を調べる ココから始めて 右にみてゆく ↓ oxxxxxxxo ^ ココ : みている所が相手のコマなので左をみる 最初にみた時点では、ひっくりかえしてよいかどうかはわからない 右をみる(再帰呼出しをする)と結果がわかります 結果がわかったら、その結果をつかって、ひっくりかえすかどうかをきめる */ int turn_sandwich_play_direct_sub ( char board[8][8], char yoko, char tate, \ char color, char ocolor, int ydir, int tdir ) { yoko = yoko + ydir; tate = tate + tdir; /* 今みている所 */ if ( check_play ( yoko, tate ) ) { /*右も左両方*/ /* その場所は盤内 */ if ( get_board ( board, yoko, tate ) == color ) { return 1; } else if ( get_board ( board, yoko, tate ) == ocolor ) { /* 相手のコマがあった */ /* まだ引っくり返す事ができない */ /* return turn_sandwich_play_direct_sub ( board, yoko, tate, color, ocolor, \ ydir, tdir ); v11: return 文一つを複数の文に変更 */ int result; /* ひっくりかえせるかどうかの結果を保持 */ result = turn_sandwich_play_direct_sub ( board, yoko, tate, color, \ ocolor, ydir, tdir ); /* 結果を、二度(コマを引っくり返すかどうか/結果として返す) 利用するので、結果を変数に保存する (もういちど、関数をよびだしてもよいが無駄だったり、余計だったりする) */ /* check で結果を返す所で、 結果を返すだけでなく、結果を利用して、 もう一仕事(コマを引っ繰り返す) */ if ( result == 1 ) { /* ココでは、ひっくりかえして良いことわかる */ put_board ( board, yoko, tate, color ); /* 自分の色のコマに変更する : 引っくり返す */ } return result; /* 結果を返す */ } } return 0; } int turn_sandwich_play_direct ( char board[8][8], char yoko, char tate, \ char color, int ydir, int tdir ) { /* 「置ける」条件 */ if ( get_board ( board, yoko, tate ) == ' ' ) { /* まず、そこに他のコマがない事を確認 */ yoko = yoko + ydir; /* 一つ横にずらす */ tate = tate + tdir; /* 一つ横にずらす */ if ( check_play ( yoko, tate ) ) { /* 盤外でない事を確認 */ char ocolor = oposit ( color ); /* 相手の色を入手 */ /* color == 'o' -> '*', color == '*' -> 'o' */ if ( get_board ( board, yoko, tate ) == ocolor ) { /* 一つ右が相手の色である事を確認 */ /* return turn_sandwich_play_direct_sub ( board, yoko, tate, color, \ ocolor, ydir, tdir ); を以下のように変更する */ int result; /* ひっくりかえせるかどうかの結果を保持 */ result = turn_sandwich_play_direct_sub ( board, yoko, tate, color, \ ocolor, ydir, tdir ); if ( result == 1 ) { put_board ( board, yoko, tate, color ); } return result; } } } return 0; /* どれかの条件が不成立なら、全体として不成立 (偽:0) を返す */ } int turn_sandwich_all_direct ( char board[8][8], char yoko, char tate, char \ color ) { int ydir; int tdir; for ( ydir = -1; ydir <= 1; ydir++ ) { for ( tdir = -1; tdir <= 1; tdir++ ) { if ( tate * tate + yoko * yoko != 0 ) { /* tate = yoko = 0 の時だけ不成立 */ turn_sandwich_play_direct ( board, yoko, tate, color, ydir, tdir ); } } } return 0; }
# # v11 の makefile # # - makefile の基本的な構造 # -- rule の集合 # -- rule の書き方 # 作りたい物 : 材料 # <TAB> 作り方 # # rule の意味 # 「作りたい物」が作りたければ「材料」を利用して、「作り方」を実行する # 材料がなければ、材料も作ろうとする !! # # - make コマンドの実行 # make で引数を指定しなければ、最初のルールを実行しようとする # 引数(作りたい物)が指定されると、指定された物を作ろうとする # <ポイント> # 既に、作りたいものが存在して、材料より新しいなら、何もしない # 作りたいものがないか、材料より古いならば、作り直す # 作り方をしらない物が材料に指定されていても、「それがあれば」よい # 材料がなく、作り方もしらないとエラーになる # # board.c 系列がふえた.. # othello.exe : othello.o main.o board.o cc -o othello.exe othello.o main.o board.o othello.o : othello.c cc -c othello.c main.o : main.c cc -c main.c board.o : board.c cc -c board.c clean : rm *.o *.exe makezip : zip v11.zip makefile main.c othello.c board.c
v13
/* * board.c */ #include <stdio.h> /* 表示の部分 */ /* print_board : 与えらたボードデータを表示する <<ポイント>> 配列の引数には特殊事情がある : 後回し */ void print_board ( char board[8][8] ) { int tate; /* 縦の添字 */ /* 追加(Copy) */ int yoko; /* 横の添字 */ /* 表示機能 (移動) */ /* ボードの内容を出力する */ /* 8x8 の内容を全て出力(標準出力)すればよい */ /* 配列の出力 -> for 文でやればよい */ /* 二次元配列なので、二重ループになる */ /* A B C D E F G H \yoko 0 1 2 3 4 5 6 7 tate 1 0 +----> 2 1 | 3 2 v 4 3 5 4 6 5 7 6 8 7 */ printf ( " A B C D E F G H\n" ); printf ( " ┏━┳━┳━┳━┳━┳━┳━┳━┓\n" ); for ( tate = 0; tate < 8; tate++ ) { printf ( "%d ┃", tate + 1 ); /* 左の、この行の枠 */ for ( yoko = 0; yoko < 8; yoko++ ) { if ( board[tate][yoko] == ' ') { /* 空き */ printf ( " " ); } else if ( board[tate][yoko] == 'o') { /* 白 */ printf ( "◯" ); } else /* if board[tate][yoko] == 'x') */ { /* 黒 */ printf ( "●" ); } printf ( "┃" ); /* コマとコマの間の枠 */ } printf ( "\n" ); /* 改行 */ if ( tate < 8 - 1 ) { /* 最後は、これを使わない */ printf ( " ┣━╋━╋━╋━╋━╋━╋━╋━┫\n" ); } else { /* 最後の行に使うのはこれ */ printf ( " ┗━┻━┻━┻━┻━┻━┻━┻━┛\n" ); } } } /* * 初期化 */ void init_board( char board[8][8] ) { int tate; /* 縦の添字 */ int yoko; /* 横の添字 */ /* ゲーム盤の初期化 */ /* とりあえず、全部 ' ' にして、後から真ん中の4つを置く */ /* とりあえず、全部空白 */ for ( tate = 0; tate < 8; tate++ ) { for ( yoko = 0; yoko < 8; yoko++ ) { board[tate][yoko] = ' '; /* 全ての場所を空白に */ } } /* 真ん中の 4 つだけ特別処理 */ board[3][3] = 'o'; /* D4 なので白 */ board[4][4] = 'o'; /* E5 なので白 */ board[3][4] = '*'; /* D5 なので黒 */ board[4][3] = '*'; /* E4 なので黒 */ }
/* computer.c コンピュータが人間の相手をしてくれる かしこさ 右上から、順番に調べて、うてる場所に打つ 結果としては、 返り値は 1 : うてる 0 : うてない うつ場所は result[2] という配列に保存 result[0] には yoko の場所 result[1] には tate の場所 */ int computer ( char result[2], char board[8][8], char color ) { int yoko; int tate; /* 左上から、左から右、上から下に、すべての場所を調べる */ for ( yoko = 'A'; yoko <= 'F'; yoko++ ) { for ( tate = '1'; tate <= '8'; tate++ ) { if ( check_sandwich_all_direct ( board, yoko, tate, color ) == 1 ) { /* この場所に置く事ができる */ result[0] = yoko; result[1] = tate; return 1; /* 置く場所がみつかった */ } } } return 0; /* 置く場所が見付からなかった */ }
/* * othello v10 (2014/11/14) の main.c */ #include <stdio.h> /* * main */ int main(int argc, char *argv[]) { char board[8][8]; /* ゲームボード */ char yoko; /* 横の記号 'A' 〜 'F' */ char tate; /* 縦の記号 '1' 〜 '8' */ char color; /* 色の記号 '*' : 黒 / 'o' : 白 */ init_board ( board ); /* 引数に配列名しか指定しない */ print_board ( board ); /* 引数に配列名しか指定しない */ color = '*'; while ( 0 < 1 ) { printf ( "次の一手> " ); /* ここで入力をする */ if ( color == '*' ) { /* 人間の手順 */ /* キーボードから 「A2*<改行>」とされた事を想定 */ yoko = getchar(); /* 横の指定の一文字 'A' を入力 */ if ( yoko == 'Z' ) { /* 'Z' が入ったら.. */ printf ( "プログラムを終了します...\n" ); return 0; /* プログラムを終了 */ } tate = getchar(); /* 縦の指定の一文字 '2' を入力 */ getchar(); /* 改行を読み飛ばす */ } else { /* コンピュータ */ char result[2]; /* 置く場所を保存する配列 */ if ( computer ( result, board, color ) == 1 ) { yoko = result[0]; /* 打つ結果を、変数に保存 */ tate = result[1]; /* 打つ結果を、変数に保存 */ /* 配列名を関数に渡すと、関数の中で、配列の要素の値を変更できる */ printf ( "\n" ); } else { printf ( "うてません\n" ); return 0; /* プログラムを終了 */ } } printf ( "着手 (%c,%c) %c ", yoko, tate, color ); if ( check_sandwich_all_direct ( board, yoko, tate, color ) ) { printf ( "置く事ができます。\n" ); /* (v12) 挟んだコマを実際に引っくり返す */ turn_sandwich_all_direct ( board, yoko, tate, color ); /* (v12) そこにコマを置く */ put_board ( board, yoko, tate, color ); color = oposit ( color ); } else { printf ( "置く事ができません。\n" ); } /* その結果が反映されているかどうかを確認する */ print_board ( board ); } return 0; }
/* * オセロ盤プログラム (version 11 : 2014/11/28) * * 機能 * 初期化の部分 * 表示の部分 : 位置を表す記号 1 〜 8, A 〜 F * 入力の部分(コマを置いてみたい..) * 入力の指示はキーボード * ゲームをしてみたい * 繰返し入力をする * 繰返しは while で実現できるが * いつまで繰り返すか ? * -> ルールでは、両方パスするまで * とりあえず、サボって * 無限に繰り返す事にする * ( 終了は 'Z' を入れる ) * v10 -> v11 * v10 では、8 方向のチェックをした * 着手禁止のチェックができるようになった * v11 * 着手できるなら、自動的に挟んだコマをひっくりかえしたい * 基本はチェックのプログラムが、ほぼ、ひっくりかえしのプログラムと同等 * 「天下り」ですが、チェックの関数を変更してひっくりかえし関数を作る */ #include <stdio.h> /* ボードを作る */ /* ボードは、8x8 の要素からなる */ /* ボードの一つの要素には 空、黒、白のいずかが入る */ /* 空 ' ' , 白 'o', 黒 '*' -> 一文字 */ /* 'A2' に黒 -> put_board_black ( 'A', '2' ) -> board[1][0] = '*'; */ char get_board ( char board[8][8], char yoko, char tate ) { return board[tate-'1'][yoko-'A']; } void put_board ( char board[8][8], char yoko, char tate, char color ) { board[tate-'1'][yoko-'A'] = color; /* 本当は、ここで tate, yoko, color が正しい値かを チェックする必要がある (後回し) */ } void put_board_black ( char board[8][8], char yoko, char tate ) { put_board ( board, yoko, tate, '*' ); /* put_board_black ( 'A', '2' ) -> board['2'-'1']['A'-'A'] = '*'; -> board[1][0] = '*'; */ } void put_board_white ( char board[8][8], char yoko, char tate ) { put_board ( board, yoko, tate, 'o' ); } /* * oposit ( char color ) * 自分の色を指定して、相手の色を返す * '*' -> 'o' * 'o' -> '*' */ char oposit ( char color ) { /* if ( color == '*' ) { return 'o'; } else if ( color == 'o' ) { return '*'; } */ return 'o' + '*' - color; } /* 横方向のチェックを行う */ int check_yoko ( char yoko ) { if ( yoko <= 'F' ) { if ( 'A' <= yoko ) { return 1; /* 'A' <= yoko <= 'F' なので OK */ } } return 0; /* その他の場合は、駄目 */ } /* 横方向のチェックを行う */ int check_tate ( char tate ) { if ( tate <= '8' ) { if ( '1' <= tate ) { return 1; /* '1' <= tate <= '8' なので OK */ } } return 0; /* その他の場合は、駄目 */ } int check_play ( char yoko, char tate ) { if ( check_yoko ( yoko ) ) { if ( check_tate ( tate ) ) { return 1; } } return 0; } int check_sandwich_play_direct_sub ( char board[8][8], char yoko, char \ tate, char color, char ocolor, int ydir, int tdir ) { yoko = yoko + ydir; tate = tate + tdir; if ( check_play ( yoko, tate ) ) { /*右も左両方*/ if ( get_board ( board, yoko, tate ) == color ) { return 1; } else if ( get_board ( board, yoko, tate ) == ocolor ) { return check_sandwich_play_direct_sub ( board, yoko, tate, color, \ ocolor, ydir, tdir ); } } return 0; } /* * check_sandwich_play_direct ( char board[8][8], char yoko, char tate, char \ color ) * * P : 現在のゲーム盤 (board) の状態で、(yoko,tate) に color のコマが置けるかどうか ? * 置けたら 1 (0 以外 : C 言語の「真」) / 置けない場合は 0 (C 言語の「偽」) */ int check_sandwich_play_direct ( char board[8][8], char yoko, char tate, \ char color, int ydir, int tdir ) { /* 「置ける」条件 */ if ( get_board ( board, yoko, tate ) == ' ' ) { /* まず、そこに他のコマがない事を確認 */ yoko = yoko + ydir; /* 一つ横にずらす */ tate = tate + tdir; /* 一つ横にずらす */ if ( check_play ( yoko, tate ) ) { /* 盤外でない事を確認 */ char ocolor = oposit ( color ); /* 相手の色を入手 */ /* color == 'o' -> '*', color == '*' -> 'o' */ if ( get_board ( board, yoko, tate ) == ocolor ) { /* 一つ右が相手の色である事を確認 */ return check_sandwich_play_direct_sub ( board, yoko, tate, color, \ ocolor, ydir, tdir ); /* 後は Q に任せる */ } } } return 0; /* どれかの条件が不成立なら、全体として不成立 (偽:0) を返す */ } int check_sandwich_all_direct ( char board[8][8], char yoko, char tate, \ char color ) { int ydir; int tdir; for ( ydir = -1; ydir <= 1; ydir++ ) { for ( tdir = -1; tdir <= 1; tdir++ ) { if ( ydir != 0 || tdir != 0 ) { /* ydir == ydir == 0 の時だけ不成立 */ if ( check_sandwich_play_direct ( board, yoko, tate, color, ydir, tdir ) \ ) { return 1; } } } } return 0; } /* v12:2014/11/28 追加部分 */ /* 以下の三つの関数を追加 */ /* 関数名の check -> turn に書き換えた */ /* 変更部分: チェックの結果、挟んでいる事がわかったら、 現在着目している場所の「コマを引っくり返す」という手続きを挿入 */ /* check から turn に変更する */ /* check の仕組 いまみている所が敵のコマなら、一つ先を調べる ココから始めて 右にみてゆく ↓ oxxxxxxxo ^ ココ : みている所が相手のコマなので左をみる 最初にみた時点では、ひっくりかえしてよいかどうかはわからない 右をみる(再帰呼出しをする)と結果がわかります 結果がわかったら、その結果をつかって、ひっくりかえすかどうかをきめる */ int turn_sandwich_play_direct_sub ( char board[8][8], char yoko, char tate, \ char color, char ocolor, int ydir, int tdir ) { yoko = yoko + ydir; tate = tate + tdir; /* 今みている所 */ if ( check_play ( yoko, tate ) ) { /*右も左両方*/ /* その場所は盤内 */ if ( get_board ( board, yoko, tate ) == color ) { return 1; } else if ( get_board ( board, yoko, tate ) == ocolor ) { /* 相手のコマがあった */ /* まだ引っくり返す事ができない */ /* return turn_sandwich_play_direct_sub ( board, yoko, tate, color, ocolor, \ ydir, tdir ); v11: return 文一つを複数の文に変更 */ int result; /* ひっくりかえせるかどうかの結果を保持 */ result = turn_sandwich_play_direct_sub ( board, yoko, tate, color, \ ocolor, ydir, tdir ); /* 結果を、二度(コマを引っくり返すかどうか/結果として返す) 利用するので、結果を変数に保存する (もういちど、関数をよびだしてもよいが無駄だったり、余計だったりする) */ /* check で結果を返す所で、 結果を返すだけでなく、結果を利用して、 もう一仕事(コマを引っ繰り返す) */ if ( result == 1 ) { /* ココでは、ひっくりかえして良いことわかる */ put_board ( board, yoko, tate, color ); /* 自分の色のコマに変更する : 引っくり返す */ } return result; /* 結果を返す */ } } return 0; } int turn_sandwich_play_direct ( char board[8][8], char yoko, char tate, \ char color, int ydir, int tdir ) { /* 「置ける」条件 */ if ( get_board ( board, yoko, tate ) == ' ' ) { /* まず、そこに他のコマがない事を確認 */ yoko = yoko + ydir; /* 一つ横にずらす */ tate = tate + tdir; /* 一つ横にずらす */ if ( check_play ( yoko, tate ) ) { /* 盤外でない事を確認 */ char ocolor = oposit ( color ); /* 相手の色を入手 */ /* color == 'o' -> '*', color == '*' -> 'o' */ if ( get_board ( board, yoko, tate ) == ocolor ) { /* 一つ右が相手の色である事を確認 */ /* return turn_sandwich_play_direct_sub ( board, yoko, tate, color, \ ocolor, ydir, tdir ); を以下のように変更する */ int result; /* ひっくりかえせるかどうかの結果を保持 */ result = turn_sandwich_play_direct_sub ( board, yoko, tate, color, \ ocolor, ydir, tdir ); if ( result == 1 ) { put_board ( board, yoko, tate, color ); } return result; } } } return 0; /* どれかの条件が不成立なら、全体として不成立 (偽:0) を返す */ } int turn_sandwich_all_direct ( char board[8][8], char yoko, char tate, char \ color ) { int ydir; int tdir; for ( ydir = -1; ydir <= 1; ydir++ ) { for ( tdir = -1; tdir <= 1; tdir++ ) { if ( tate * tate + yoko * yoko != 0 ) { /* tate = yoko = 0 の時だけ不成立 */ turn_sandwich_play_direct ( board, yoko, tate, color, ydir, tdir ); } } } return 0; }
# # v11 の makefile # # - makefile の基本的な構造 # -- rule の集合 # -- rule の書き方 # 作りたい物 : 材料 # <TAB> 作り方 # # rule の意味 # 「作りたい物」が作りたければ「材料」を利用して、「作り方」を実行する # 材料がなければ、材料も作ろうとする !! # # - make コマンドの実行 # make で引数を指定しなければ、最初のルールを実行しようとする # 引数(作りたい物)が指定されると、指定された物を作ろうとする # <ポイント> # 既に、作りたいものが存在して、材料より新しいなら、何もしない # 作りたいものがないか、材料より古いならば、作り直す # 作り方をしらない物が材料に指定されていても、「それがあれば」よい # 材料がなく、作り方もしらないとエラーになる # # board.c 系列がふえた.. # computer.c がふえた # othello.exe : othello.o main.o board.o computer.o cc -o othello.exe othello.o main.o board.o computer.o othello.o : othello.c cc -c othello.c main.o : main.c cc -c main.c board.o : board.c cc -c board.c computer.o : computer.c cc -c computer.c clean : rm *.o *.exe makezip : zip v11.zip makefile main.c othello.c board.c computer.c