#include <stdio.h> #include <stdlib.h> #include <time.h> /* /* タブはハードタブ(コード )を使わず全てスペースなのが特徴。 * スタイルは K&R になっている(https://ja.wikipedia.org/wiki/%E5%AD%97%E4%B8%8B%E3%81%92%E3%82%B9%E3%82%BF%E3%82%A4%E3%83%AB)。 * C++の1行コメントを使うのが特徴。 */ /* * define XXX の XXX の後にタブを入れないのが特徴。 * 例えば #define SIZE 3 #define EMPTY ' ' #define PLAYER 'X' #define AI 'O' * のようにしないと言うこと。 * */ #define SIZE 3 #define EMPTY ' ' #define PLAYER 'X' #define AI 'O' /* * 関数の宣言行に { を書くのはかなり特徴的。C言語では以下のように書くことの方が一般的だと思う。 void initializeBoard(char board[][SIZE]) { * java では 1行が一般的。 * また変数名、関数名は小文字始まりの区切り頭大文字になっている。 * C言語なら全て小文字のアンダースコア繋がりもありとは思う。 void initialize_board(char board[][SIZE]) { */ // 盤面の初期化 void initializeBoard( char board[][SIZE]) { /* * i, j を for文で使う場合は、インラインで宣言する傾向がある。 */ for ( int i = 0; i < SIZE; i++) { /* * for文、if文などで {} がなくても大丈夫なケースでも付ける。 * 付けるべきか付けないべきかケースバイケースにするかなどは * 別論争なので、ここではそれには触れない。 */ for ( int j = 0; j < SIZE; j++) { board[i][j] = EMPTY; } } } /* const宣言は律儀にする。*/ // 盤面の表示 void displayBoard( const char board[][SIZE]) { printf ( " 1 2 3\n" ); for ( int i = 0; i < SIZE; i++) { printf ( "%d " , i + 1); for ( int j = 0; j < SIZE; j++) { printf ( "%c " , board[i][j]); } printf ( "\n" ); } } /* * 関数名が上2個は動詞+名詞だけど以下は違っており、統一性がない。 * playerMove → inputPlayerMove * AIMove → selectAIMove */ // プレイヤーの手の入力 void playerMove( char board[][SIZE]) { int row, col; printf ( "Your turn: " ); /* ここは何故か1行空けていない */ scanf ( "%d %d" , &row, &col); row--; col--; // 入力の妥当性をチェック while (row < 0 || row >= SIZE || col < 0 || col >= SIZE || board[row][col] != EMPTY) { printf ( "Invalid move. Please try again: " ); scanf ( "%d %d" , &row, &col); row--; col--; } board[row][col] = PLAYER; } // AIの手を選択 void AIMove( char board[][SIZE]) { int row, col; // ランダムに有効な手を選択 do { row = rand () % SIZE; col = rand () % SIZE; } while (board[row][col] != EMPTY); printf ( "AI's turn: %d %d\n" , row + 1, col + 1); board[row][col] = AI; } /* * 真偽を返す関数でも isXxx とはなっていない。また stdbool.h も使っていない。 * stdbool.h を使っているコードを見たことがある気もする。 #include <stdbool.h> : : bool isPlayerWin(const char board[][SIZE], char player) { : return false; } */ // 勝利判定 int checkWin( const char board[][SIZE], char player) { // 横方向のチェック for ( int i = 0; i < SIZE; i++) { if (board[i][0] == player && board[i][1] == player && board[i][2] == player) { return 1; } } // 縦方向のチェック for ( int j = 0; j < SIZE; j++) { if (board[0][j] == player && board[1][j] == player && board[2][j] == player) { return 1; } } // 対角線のチェック if (board[0][0] == player && board[1][1] == player && board[2][2] == player) { return 1; } if (board[0][2] == player && board[1][1] == player && board[2][0] == player) { return 1; } return 0; } // 引き分け判定 int checkDraw( const char board[][SIZE]) { for ( int i = 0; i < SIZE; i++) { for ( int j = 0; j < SIZE; j++) { if (board[i][j] == EMPTY) { return 0; } } } return 1; } /* * 古臭い書き方なら、int main(void) */ int main() { char board[SIZE][SIZE]; int gameOver = 0; srand ( time (NULL)); initializeBoard(board); printf ( "Welcome to Tic-Tac-Toe!\n" ); printf ( "You are X, and the AI is O.\n" ); while (!gameOver) { displayBoard(board); playerMove(board); if (checkWin(board, PLAYER)) { displayBoard(board); printf ( "Congratulations! You win!\n" ); gameOver = 1; break ; } if (checkDraw(board)) { displayBoard(board); printf ( "It's a draw!\n" ); gameOver = 1; break ; } AIMove(board); if (checkWin(board, AI)) { displayBoard(board); printf ( "AI wins! Try again.\n" ); gameOver = 1; break ; } if (checkDraw(board)) { displayBoard(board); printf ( "It's a draw!\n" ); gameOver = 1; break ; } } return 0; } |