#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;} |