C言語で作る五目並べ
初めに
本稿はSLP KBIT Advent Calendar 2014 の12日目の記事です。
前書き
最近競技プログラミングしかやっていなかったので、たまにはGMVらしく
ゲームを作りたいと思います。
今回は比較的簡単に作れそうな五目並べをC言語で作ってみたいと思います。
配列と関数の知識があれば作れるので、プログラミング初学者におすすめです!
ルール確認
まず、ゲームのルール・仕様を決めていきましょう。
ここを曖昧にしたままコーディングを始めると、後々修正が大変になります。
- 盤面の大きさは10×10
- 縦横斜めのいずれかに5つ自分の石を揃えれば勝ち
- 6連以上も勝利とする
- 三三などの禁止手はなし
- 先手は黒の石
今回はシンプルにこんな感じにしましょう。
フロー
次に、ゲームの流れを確認しましょう。
- ゲーム情報初期化
- ゲーム開始
- 順番に空マスに石を置いていく
- 五連がそろえば結果を出力して終了
コーディング
仕様と大まかな流れを確認したので、実際にプログラムを書いていきましょう!
まず盤面の大きさと駒の色を定数として定義しておきましょう。
#define BOARD_SIZE 10 // 盤面サイズ 10 * 10 #define STONE_SPACE 0 // 盤面にある石 なし #define STONE_BLACK 1 // 盤面にある石 黒 #define STONE_WHITE 2 // 盤面にある石 白
コード内で1や2などのマジックナンバーを書いてしまうと、
プログラムが読みずらくなってしまいます。
盤面サイズを定数として定義しておけば、この値を変えるだけで
盤面の大きさを自由に変えることができます。
盤面の石情報を管理する配列と、現在どちらのターンなのかを
格納する配列をmain文で宣言します。
int main() { int board[BOARD_SIZE][BOARD_SIZE]; int which_turn; return 0; }
ここからいろいろ関数を宣言していきます。
盤面初期化
引数で渡した盤面の配列を、二重ループで回して
全て「STONE_SPACE」にしてやります。
void boardInit(int board[][BOARD_SIZE]) { int i, j; for (i = 0; i < BOARD_SIZE; i++) { for (j = 0; j < BOARD_SIZE; j++) { board[i][j] = STONE_SPACE; } } }
ゲーム初期化
盤面を初期化する関数の呼び出しと、which_turnを初期化してやります。
先手は黒なので、「STONE_BLACK」を格納します。
void gameInit(int board[][BOARD_SIZE], int *which_turn) { boardInit(board); *which_turn = STONE_BLACK; }
盤面出力
二重ループを回し、board[i][j]に対応する駒を出力します。
分かりやすいように、左と上に番号を出力しておきます。
void boardPrint(int board[][BOARD_SIZE]) { int i, j; printf(" "); for (i = 0; i < BOARD_SIZE; i++) { printf("%d ", i); } puts(""); for (i = 0; i < BOARD_SIZE; i++) { printf("%d ", i); for (j = 0; j < BOARD_SIZE; j++) { switch (board[i][j]) { case STONE_SPACE: printf("・"); break; case STONE_BLACK: printf("●"); break; case STONE_WHITE: printf("○"); break; } } puts(""); } puts(""); }
入力処理
正しい入力がくるまでループを回して入力を促します。
正しくない入力というのは、盤面の範囲外か
すでに石が置いてあるかです。
正しい入力がきたらループを抜け、その場所に石を置きます。
void inputPutPos(int board[][BOARD_SIZE], int which) { int pos_x, pos_y; printf("%s", (which == 1) ? "●" : "○"); printf("の番です。どこに置きますか\n> "); while (1) { scanf("%d %d", &pos_x, &pos_y); if (checkOutPos(pos_x, pos_y) && board[pos_y][pos_x] == STONE_SPACE) { break; } printf("不正な入力です\n> "); } board[pos_y][pos_x] = which; }
手番交代処理
黒なら白に、白なら黒に変えます。
void changeTurn(int *which_turn) { *which_turn = (*which_turn == STONE_BLACK) ? STONE_WHITE : STONE_BLACK; }
範囲外チェック
引数で座標を渡し、範囲内なら1を、範囲外なら0を返します。
int checkOutPos(int x, int y) { return (x >= 0 && x < BOARD_SIZE && y >= 0 && y < BOARD_SIZE); }
5連確認
引数で渡した座標から、下、右、斜め右下の順で盤面を見ていきます。
指定した座標の石が5連続いていれば1を返します。
int lenCheck(int board[][BOARD_SIZE], int x, int y) { int i, j, len_flag; int dx[] = { 0, 1, 1 }; int dy[] = { 1, 0, 1 }; for (i = 0; i < 3; i++) { for (j = 1, len_flag = 1; j <= 4; j++) { if (board[y][x] != board[y+j*dy[i]][x+j*dx[i]]) { len_flag = 0; break; } } if (len_flag == 1) { return 1; } } return 0; }
ゲーム終了処理
盤面を見て、5連があるか確認します。
あれば、結果を出力し、1を返します。
int gameEndProcess(int board[][BOARD_SIZE]) { int i, j, len_flag; for (i = 0; i < BOARD_SIZE; i++) { for (j = 0; j < BOARD_SIZE; j++) { if (board[i][j] == STONE_SPACE) { continue; } if (lenCheck(board, j, i)) { printf("%sの勝ちです。\n", (board[i][j] == STONE_BLACK) ? "●" : "○"); return 1; } } } return 0; }
main文
必要な関数の定義がし終わったので、プロトタイプ宣言をして、
main文で使っていきます。
int main() { // 変数宣言 int board[BOARD_SIZE][BOARD_SIZE]; int which_turn; // 初期処理 gameInit(board, &which_turn); boardPrint(board); //---- メインループ while (1) { //--- 入力処理 inputPutPos(board, which_turn); //--- 演算処理 changeTurn(&which_turn); //--- 出力処理 boardPrint(board); //--- 終了判定 if (gameEndProcess(board)) { break; } } return 0; }
コード
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #define BOARD_SIZE 10 // 盤面サイズ 10 * 10 #define STONE_SPACE 0 // 盤面にある石 なし #define STONE_BLACK 1 // 盤面にある石 黒 #define STONE_WHITE 2 // 盤面にある石 白 void inputPutPos(int board[][BOARD_SIZE], int which); void changeTurn(int *which_turn); int checkOutPos(int x, int y); void gameInit(int board[][BOARD_SIZE], int *which_turn); void boardInit(int board[][BOARD_SIZE]); void boardPrint(int board[][BOARD_SIZE]); int gameEndProcess(int board[][BOARD_SIZE]); int lenCheck(int board[][BOARD_SIZE], int x, int y); //======================================================= // main //======================================================= int main() { // 変数宣言 int board[BOARD_SIZE][BOARD_SIZE]; int which_turn; // 初期処理 gameInit(board, &which_turn); boardPrint(board); //---- メインループ while (1) { //--- 入力処理 inputPutPos(board, which_turn); //--- 演算処理 changeTurn(&which_turn); //--- 出力処理 boardPrint(board); //--- 終了判定 if (gameEndProcess(board)) { break; } } return 0; } //------------------------------------------------- // 置く場所入力 //------------------------------------------------- void inputPutPos(int board[][BOARD_SIZE], int which) { int pos_x, pos_y; printf("%s", (which == 1) ? "●" : "○"); printf("の番です。どこに置きますか\n> "); while (1) { scanf("%d %d", &pos_x, &pos_y); if (checkOutPos(pos_x, pos_y) && board[pos_y][pos_x] == STONE_SPACE) { break; } printf("不正な入力です\n> "); } board[pos_y][pos_x] = which; } //------------------------------------------------- // 手番交代処理 //------------------------------------------------- void changeTurn(int *which_turn) { *which_turn = (*which_turn == STONE_BLACK) ? STONE_WHITE : STONE_BLACK; } //------------------------------------------------- // 範囲外チェック //------------------------------------------------- int checkOutPos(int x, int y) { return (x >= 0 && x < BOARD_SIZE && y >= 0 && y < BOARD_SIZE); } //------------------------------------------------- // ゲーム情報初期化 //------------------------------------------------- void gameInit(int board[][BOARD_SIZE], int *which_turn) { boardInit(board); *which_turn = STONE_BLACK; } //------------------------------------------------- // 盤面初期化 //------------------------------------------------- void boardInit(int board[][BOARD_SIZE]) { int i, j; for (i = 0; i < BOARD_SIZE; i++) { for (j = 0; j < BOARD_SIZE; j++) { board[i][j] = STONE_SPACE; } } } //------------------------------------------------- // 盤面出力 //------------------------------------------------- void boardPrint(int board[][BOARD_SIZE]) { int i, j; printf(" "); for (i = 0; i < BOARD_SIZE; i++) { printf("%d ", i); } puts(""); for (i = 0; i < BOARD_SIZE; i++) { printf("%d ", i); for (j = 0; j < BOARD_SIZE; j++) { switch (board[i][j]) { case STONE_SPACE: printf("・"); break; case STONE_BLACK: printf("●"); break; case STONE_WHITE: printf("○"); break; } } puts(""); } puts(""); } //------------------------------------------------- // ゲーム終了処理 //------------------------------------------------- int gameEndProcess(int board[][BOARD_SIZE]) { int i, j, len_flag; for (i = 0; i < BOARD_SIZE; i++) { for (j = 0; j < BOARD_SIZE; j++) { if (board[i][j] == STONE_SPACE) { continue; } if (lenCheck(board, j, i)) { printf("%sの勝ちです。\n", (board[i][j] == STONE_BLACK) ? "●" : "○"); return 1; } } } return 0; } //------------------------------------------------- // 5連確認 //------------------------------------------------- int lenCheck(int board[][BOARD_SIZE], int x, int y) { int i, j, len_flag; int dx[] = { 0, 1, 1 }; int dy[] = { 1, 0, 1 }; for (i = 0; i < 3; i++) { for (j = 1, len_flag = 1; j <= 4; j++) { if (board[y][x] != board[y+j*dy[i]][x+j*dx[i]]) { len_flag = 0; break; } } if (len_flag == 1) { return 1; } } return 0; }
実行
DXライブラリ
GUIのゲームを作るとなるとライブラリが必要になります。
有名なものに、DirectXやOpenGLなどがあります。
今回は使いやすいDXライブラリを使ってみましょう!
ダウンロード
DXライブラリ置き場 ダウンロードページ
↑でがんばって設定します。
プロジェクトの準備が終わったらコードを書いていきます。
内部処理はだいたい一緒なので、入力、出力の部分を変更します。
画像は自分で用意します。
コード
#include "DxLib.h" #include <stdio.h> #include <string.h> #include <stdlib.h> #include <time.h> #define BOARD_SIZE 10 // 盤面サイズ 10 * 10 #define STONE_SPACE 0 // 盤面にある石 なし #define STONE_BLACK 1 // 盤面にある石 黒 #define STONE_WHITE 2 // 盤面にある石 白 int processLoop(); void loadImage(); int updateMouse(); int inputPutPos(int board[][BOARD_SIZE], int which); void changeTurn(int *which_turn); int checkOutPos(int x, int y); void gameInit(int board[][BOARD_SIZE], int *which_turn); void boardInit(int board[][BOARD_SIZE]); void boardPrint(int board[][BOARD_SIZE]); int gameEndProcess(int board[][BOARD_SIZE]); int lenCheck(int board[][BOARD_SIZE], int x, int y); // 画像ハンドル int image_stone_w; int image_stone_b; int image_board; // クリック状態 int click; //======================================================= // main //======================================================= int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { ChangeWindowMode(TRUE); // ウィンドウモードで起動する SetGraphMode(540, 540, 16); // ウィンドウサイズ(540×540) 使用カラー16bit(65536色) SetWindowText("五目並べ"); // タイトル名 if (DxLib_Init() == -1) { return -1; } // DXライブラリ初期化 if (SetDrawScreen(DX_SCREEN_BACK) != 0) { return -1; } // 描画先を裏画面に int board[BOARD_SIZE][BOARD_SIZE]; int which_turn; // デバッグ用コマンドプロンプト /* AllocConsole(); FILE* fp; freopen_s(&fp, "CONOUT$", "w", stdout); freopen_s(&fp, "CONIN$", "r", stdin); */ // 初期処理 gameInit(board, &which_turn); loadImage(); //---- メインループ while (processLoop() == 0) { //--- 入力処理 if (inputPutPos(board, which_turn)) { changeTurn(&which_turn); } //--- 出力処理 boardPrint(board); ScreenFlip(); //--- 終了判定 if (gameEndProcess(board)) { break; } } DxLib_End(); return 0; } //------------------------------------------------- // ループ処理 //------------------------------------------------- int processLoop() { if (ProcessMessage() != 0) { return -1; } if (ClearDrawScreen() != 0) { return -1; } if (updateMouse() != 0) { return -1; } return 0; } //------------------------------------------------- // マウス入力状態更新 //------------------------------------------------- int updateMouse() { if (GetMouseInput() & MOUSE_INPUT_LEFT) { click++; } else { click = 0; } return 0; } //------------------------------------------------- // 画像読み込み処理 //------------------------------------------------- void loadImage() { image_stone_w = LoadGraph("white_stone.png"); image_stone_b = LoadGraph("black_stone.png"); image_board = LoadGraph("board.png"); } //------------------------------------------------- // 置く場所入力 //------------------------------------------------- int inputPutPos(int board[][BOARD_SIZE], int which) { int pos_x, pos_y; if (click == 1) { GetMousePoint(&pos_x, &pos_y); pos_x = (pos_x - 25) / 50; pos_y = (pos_y - 25) / 50; if (checkOutPos(pos_x, pos_y) && board[pos_y][pos_x] == 0) { board[pos_y][pos_x] = which; return 1; } } return 0; } //------------------------------------------------- // 手番交代処理 //------------------------------------------------- void changeTurn(int *which_turn) { *which_turn = (*which_turn == STONE_BLACK) ? STONE_WHITE : STONE_BLACK; } //------------------------------------------------- // 範囲外チェック //------------------------------------------------- int checkOutPos(int x, int y) { return (x >= 0 && x < BOARD_SIZE && y >= 0 && y < BOARD_SIZE); } //------------------------------------------------- // ゲーム情報初期化 //------------------------------------------------- void gameInit(int board[][BOARD_SIZE], int *which_turn) { boardInit(board); *which_turn = STONE_BLACK; } //------------------------------------------------- // 盤面初期化 //------------------------------------------------- void boardInit(int board[][BOARD_SIZE]) { int i, j; for (i = 0; i < BOARD_SIZE; i++) { for (j = 0; j < BOARD_SIZE; j++) { board[i][j] = STONE_SPACE; } } } //------------------------------------------------- // 盤面出力 //------------------------------------------------- void boardPrint(int board[][BOARD_SIZE]) { int i, j; DrawGraph(0, 0, image_board, TRUE); for (i = 0; i < BOARD_SIZE; i++) { for (j = 0; j < BOARD_SIZE; j++) { if (board[i][j] == STONE_BLACK) { DrawGraph(j * 49, i * 49, image_stone_b, TRUE); } else if (board[i][j] == STONE_WHITE) { DrawGraph(j * 49, i * 49, image_stone_w, TRUE); } } } } //------------------------------------------------- // ゲーム終了処理 //------------------------------------------------- int gameEndProcess(int board[][BOARD_SIZE]) { int i, j; for (i = 0; i < BOARD_SIZE; i++) { for (j = 0; j < BOARD_SIZE; j++) { if (board[i][j] == STONE_SPACE) { continue; } if (lenCheck(board, j, i)) { return 1; } } } return 0; } //------------------------------------------------- // 5連確認 //------------------------------------------------- int lenCheck(int board[][BOARD_SIZE], int x, int y) { int i, j, len_flag; int dx[] = { 0, 1, 1 }; int dy[] = { 1, 0, 1 }; for (i = 0; i < 3; i++) { for (j = 1, len_flag = 1; j <= 4; j++) { if (board[y][x] != board[y + j*dy[i]][x + j*dx[i]]) { len_flag = 0; break; } } if (len_flag == 1) { return 1; } } return 0; }
実行結果
おわり
みんなゲーム作ろう!