はじめに
こんにちは 高専1年生、ロボコン部部員です
今回はイギリス発祥のボードゲーム:『ウサギと猟犬』をC言語で実装してみました
久々に作ったゲームなので見どころこそあまりありませんが基礎的でもあります
ぜひ楽しんでいってください
プレイ映像
コード紹介
冒頭部分
#include <stdio.h>
void map_init(int *map);
int first_attack_decide(void);
void map_print(int *map);
int location_return(int *map,int indivi);
void input(int *map,int player);
int can_move_check(int *map,int player,int dest,int intivi);
void map_change(int *map,int dest,int intivi);
int game_check(int *map,int play_count);
int rabbit_win_check(int *map,int play_count);
int dog_win_check(int *map);
void game_print(int game_set);
main関数
今回のmain関数は特に、短く簡潔にすることを心掛けました
関数の呼び出しが多いので最初に下記の関数から把握することをお勧めします
この関数の呼び出す関数は、
- map_init関数
- first_attack_decide関数
- map_print関数
- input関数
- game_check関数
- game_print関数
の六つです
int main(void){
int map[11] = {0};
int player = 0;
int game_set = 0;
int play_count = 0;
map_init(map);
player = first_attack_decide();
do{
map_print(map);
printf("\x1b[0m%2d手目\n",play_count+1);
input(map,player);
game_set = game_check(map,play_count);
player = 1-player;
play_count++;
}while(game_set == 0);
map_print(map);
game_print(game_set);
return 0;
}
map_init関数
map配列をポインタを用いて初期化する関数です
1,2,3は猟犬を表し、4はウサギを表します。
何もないマスは0となります
void map_init(int *map){
int init_i;
for(init_i=0; init_i<11; init_i++){
map[init_i] = 0;
}
map[0] = 1;
map[1] = 2;
map[3] = 3;
map[10] = 4;
}
first_attack_decide関数
先攻をplayer変数を変更することで設定します
playerが0の時はウサギ、
1の時は猟犬のターンであることを表します
int first_attack_decide(void){
int ans;
printf("\x1b[0m------------ウサギと猟犬------------\n\n");
printf("先攻はどちらにしますか\n");
printf("\x1b[34mウサギ→ 0\n");
printf("\x1b[31m猟犬 → 1\n");
do{
printf("\x1b[0m入力してください:");
scanf("%d",&ans);
}while(ans<0 || 1<ans);
return ans;
}
一番初めに呼び出す関数なので、ここでタイトルも表示しちゃっています
map_print関数
これはmap配列からmapのイラストを表示する関数です
マップの形が複雑だったため、ゴリ押しコードになってしまいました
void map_print(int *map){
int print_data[45] = {
-1,-1, 1,-2, 4,-2, 7,-1,-1,
-1,-4,-3,-5,-3,-4,-3,-5,-1,
0,-2, 2,-2, 5,-2, 8,-2,10,
-1,-5,-3,-4,-3,-5,-3,-4,-1,
-1,-1, 3,-2, 6,-2, 9,-1,-1
};
char print_symb_data[5] = {'l','/','|','-',' '};
int twice_i,print_i;
printf("\n");
for(twice_i=0; twice_i<2; twice_i++){
for(print_i=0; print_i<45; print_i++){
if(print_data[print_i] < 0){
printf("\x1b[0m %c ",print_symb_data[print_data[print_i]+5]);
}else{
if(twice_i == 0){
printf("\x1b[0m%2d ",print_data[print_i]);
}else{
if(map[print_data[print_i]] == 4){
printf("\x1b[34m[○]");
}else if(map[print_data[print_i]] == 3 || map[print_data[print_i]] == 2 || map[print_data[print_i]] == 1){
printf("\x1b[31m[%d]",map[print_data[print_i]]);
}else{
printf("\x1b[0m[ ]");
}
}
}
if(print_i%9 == 8){
printf("\n");
}
}
printf("\n");
}
}
location_return関数
map配列と探したい数値から
その数値のある位置を返す関数です
主にウサギや猟犬の位置を調べるのに使います
int location_return(int *map,int intivi){
int exp_i;
for(exp_i=0; exp_i<11; exp_i++){
if(map[exp_i] == intivi){
return exp_i;
}
}
return 0;
}
input関数
ここで入力させるのは、
- 猟犬の番の時には、「どの個体を動かすか」
- 「どこに動かすか」
の二つです
- can_move_check関数で『入力されたマスに移動できるかどうか』をチェックしています
- また、入力されたマスに移動するようmap配列を変化させる、
map_change配列も呼び出しています
void input(int *map,int player){
int intivi = 4;
int dest;
if(player == 0){
printf("\x1b[34mウサギ:\n");
}else{
printf("\x1b[31m猟犬:\n\x1b[0mどれを動かしますか(1~3)\n");
do{
printf("\x1b[0m入力してください:");
scanf("%d",&intivi);
}while(intivi<1 || 3<intivi);
}
do{
do{
printf("\x1b[0mどこに移動しますか:");
scanf("%d",&dest);
}while(dest<0 || 10<dest);
}while(can_move_check(map,player,dest,intivi) == 0);
map_change(map,dest,intivi);
}
can_move_check関数
ここでは現在地とmap配列の情報、目的地から
『現在地→目的地の移動が可能か』を審査します
猟犬のターンでは特に、後ろに戻れないようにしていますね
int can_move_check(int *map,int player,int dest,int intivi){
int can_move_data[22] = {107,108,109,78,89,74,85,96,75,95,45,56,41,52,63,51,53,12,23,10,20,30};
int location = location_return(map,intivi);
int check_i;
if(map[dest] != 0){
return 0;
}else{
for(check_i=0; check_i<22; check_i++){
if(can_move_data[check_i] == location*10 + dest){
if(player == 1){
if((dest+2)/3 >= (location+2)/3){
return 1;
}
}else{
return 1;
}
}else if(can_move_data[check_i] == dest*10 + location){
return 1;
}
}
return 0;
}
return 0;
}
map_change関数
実際にmap配列を変更する関数です
void map_change(int *map,int dest,int intivi){
int location = location_return(map,intivi);
map[location] = 0;
map[dest] = intivi;
}
game_check関数
ゲームの勝敗を決める、
- rabbit_win_check関数
- dog_win_check関数
を呼び出し、勝敗を実際にmain関数に送る仕事をします
int game_check(int *map,int play_count){
int rabbit_win = rabbit_win_check(map,play_count);
int dog_win = dog_win_check(map);
if(rabbit_win == 1){
return 1;
}else if(dog_win == 1){
return 2;
}
return 0;
}
rabbit_win_check関数
ウサギが勝つと1,勝たないと0を返す関数です
『千日手』の処理はここで実装しています
int rabbit_win_check(int *map,int play_count){
int check_i;
if(play_count >= 19){
return 1;
}
for(check_i=0; check_i<3; check_i++){
if((location_return(map,4)+2)/3 >= (location_return(map,check_i+1)+2)/3){
return 0;
}
}
return 1;
}
dog_win_check関数
猟犬が勝つと1,勝たないと0を返す関数です
全マスを調べ、ウサギがそのマスから移動できるかできないかを明確にします
int dog_win_check(int *map){
int check_i;
for(check_i=0; check_i<11; check_i++){
if(can_move_check(map,0,check_i,4) == 1){
return 0;
}
}
return 1;
}
game_print関数
game_set変数の値からゲームの試合を表示します
void game_print(int game_set){
if(game_set == 1){
printf("\x1b[0mウサギが逃げた!ウサギの勝利\n");
}else if(game_set == 2){
printf("\x1b[0mウサギは動けない!猟犬の勝利\n");
}
}
さいごに
ここまで読んでくださりありがとうございました
改善点などあれば教えていただけるととても嬉しいです
