/*
* 콘솔 오목 — 스타터 코드
*
* 이미 완성된 것: 판(board), init_board, print_board, main의 입력 루프, run_tests
* 여러분이 짜는 것: 미션 1 can_place / 미션 2 count_dir / 미션 3 check_win
* 미션 4 종료 처리(main) / 미션 5 컴퓨터 AI
*
* 프로그램을 실행하면 먼저 테스트 결과(PASS/FAIL)가 나온다.
* 미션을 하나 끝낼 때마다 실행해서 해당 미션의 테스트가 전부 PASS인지 확인할 것.
*/
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define N 15
char board[N][N];
/* 판을 전부 빈칸('.')으로 채운다 */
void init_board(void) {
for (int r = 0; r < N; r++)
for (int c = 0; c < N; c++)
board[r][c] = '.';
}
/* 판 출력 (위쪽에 열 번호, 왼쪽에 행 번호) */
void print_board(void) {
printf("\n ");
for (int c = 0; c < N; c++) printf("%2d", c);
printf("\n");
for (int r = 0; r < N; r++) {
printf("%2d ", r);
for (int c = 0; c < N; c++) printf(" %c", board[r][c]);
printf("\n");
}
}
/* ============================================================
* 미션 1: (r, c)에 돌을 놓을 수 있는가?
* 판 밖이면 0, 이미 돌이 있으면 0, 놓을 수 있으면 1
* ============================================================ */
int can_place(int r, int c) {
if (r < 0 || r >= N || c < 0 || c >= N)
return 0;
if (board[r][c] != '.')
return 0;
return 1;
}
/* ============================================================
* 미션 2: (r, c)에서 (dr, dc) 방향으로 한 칸씩 가며
* stone과 같은 돌이 몇 개 이어지는지 센다.
* (r, c) 자기 자신은 세지 않는다.
* ============================================================ */
int count_dir(int r, int c, int dr, int dc, char stone) {
/* TODO */
int count = 0;
int R = r + dr;
int C = c + dc;
while (R>=0 && C>=0 && R<N && C<N && board[R][C] == stone) {
R += dr;
C += dc;
count++;
}
return count;
}
/* ============================================================
* 미션 3: 방금 둔 돌 (r, c)로 5목이 완성됐는가? 1 = 예, 0 = 아니오
* 가로, 세로, 대각선 2개 = 4방향. count_dir을 이용할 것.
* ============================================================ */
int check_win(int r, int c, char stone) {
/* TODO */
int dr[4] = { 0, 1, 1, 1 };
int dc[4] = { 1, 0, 1, -1 };
for (int i = 0; i < 4; i++) {
int count = 1;
count += count_dir(r, c, dr[i], dc[i], stone);
count += count_dir(r, c, -dr[i], -dc[i], stone);
if (count >= 5) return 1;
}
return 0;
}
/* ============================================================
* 미션 5: 컴퓨터 AI
* ai_move(me, enemy)가 끝나면 ai_r, ai_c에 둘 자리가 들어 있어야 한다.
* 1) 내가 이번 수로 이길 수 있으면 그 자리
* 2) 상대가 다음 수로 이길 수 있으면 그 자리(막기)
* 3) 아니면 아무 빈칸
* ============================================================ */
int ai_r, ai_c;
/* stone을 놓으면 바로 5목이 되는 빈칸이 있으면 1 (그 자리를 ai_r, ai_c에 저장), 없으면 0 */
int find_win_spot(char stone) {
/* TODO */
int r = 0, c = 0;
for (r = 0; r < N; r++) {
for (c = 0; c < N, c++) {
if (board[r][c] != '.') continue;
board[r][c] = stone;
int win = check_win(r, c, stone);
board[r][c] = '.';
if (win) {
ai_r = r;
ai_c = c;
return 1;
}
}
}
return 0;
}
void ai_move(char me, char enemy) {
/* TODO */
if (find_win_spot(me)) return;
if (find_win_spot(enemy)) return;
do {
ai_r = rand() % N;
ai_c = rand() % N;
} while (board[ai_r][ai_c] != '.');
}
/* ============================================================
* 테스트 — 건드리지 말 것
* ============================================================ */
static int pass_cnt, total_cnt;
static void check(const char *name, int got, int want) {
total_cnt++;
if (got == want) { pass_cnt++; printf(" PASS %s\n", name); }
else printf(" FAIL %s (기대 %d, 결과 %d)\n", name, want, got);
}
static void put(int r, int c, char s) { board[r][c] = s; }
void run_tests(void) {
pass_cnt = total_cnt = 0;
printf("[미션 1] can_place\n");
init_board(); put(7, 7, 'O');
check("빈칸 (0,0)은 둘 수 있다", can_place(0, 0), 1);
check("돌이 있는 (7,7)은 안 된다", can_place(7, 7), 0);
check("판 밖 (15,0)은 안 된다", can_place(15, 0), 0);
check("판 밖 (3,-1)은 안 된다", can_place(3, -1), 0);
printf("[미션 2] count_dir\n");
init_board();
put(7, 5, 'O'); put(7, 6, 'O'); put(7, 7, 'O'); put(7, 8, 'O'); put(7, 9, 'X');
check("(7,5)에서 오른쪽: O O O 다음 X -> 3", count_dir(7, 5, 0, 1, 'O'), 3);
check("(7,5)에서 왼쪽: 바로 빈칸 -> 0", count_dir(7, 5, 0, -1, 'O'), 0);
check("(7,8)에서 오른쪽: 바로 X -> 0", count_dir(7, 8, 0, 1, 'O'), 0);
check("(7,7)에서 X를 세면 -> 0", count_dir(7, 7, 0, 1, 'X'), 0);
put(3, 2, 'O'); put(4, 2, 'O'); put(5, 2, 'O');
check("(3,2)에서 아래로 -> 2", count_dir(3, 2, 1, 0, 'O'), 2);
check("(0,0)에서 왼쪽 위: 판 밖 -> 0", count_dir(0, 0, -1, -1, 'O'), 0);
printf("[미션 3] check_win\n");
init_board(); for (int c = 0; c < 5; c++) put(2, c, 'O');
check("가로 5개, 가운데 돌로 판정 -> 1", check_win(2, 2, 'O'), 1);
check("가로 5개, 끝 돌로 판정 -> 1", check_win(2, 4, 'O'), 1);
check("O 5개인데 X로 물어보면 -> 0", check_win(2, 2, 'X'), 0);
init_board(); for (int c = 0; c < 4; c++) put(10, c, 'O');
check("가로 4개뿐 -> 0", check_win(10, 3, 'O'), 0);
init_board(); for (int i = 0; i < 5; i++) put(i, i, 'O');
check("대각선 \\ 5개 -> 1", check_win(4, 4, 'O'), 1);
init_board(); for (int i = 0; i < 5; i++) put(i, 4 - i, 'O');
check("대각선 / 5개 -> 1", check_win(2, 2, 'O'), 1);
init_board(); for (int i = 0; i < 5; i++) put(i + 5, 7, 'X');
check("세로 5개 -> 1", check_win(7, 7, 'X'), 1);
printf("[미션 5] find_win_spot\n");
init_board(); for (int c = 0; c < 4; c++) put(7, c, 'O');
check("O O O O . -> 이길 자리 있음 1", find_win_spot('O'), 1);
check(" 그 자리의 행은 7", ai_r, 7);
check(" 그 자리의 열은 4", ai_c, 4);
check("X는 이길 자리 없음 0", find_win_spot('X'), 0);
check("놓아본 자리는 다시 빈칸이어야 함", board[7][4] == '.', 1);
printf("==> %d / %d PASS\n\n", pass_cnt, total_cnt);
}
/* ============================================================
* main — 미션 4에서 종료 처리(무승부, 입력 실수, 다시 하기)를,
* 미션 5에서 X 차례에 AI 호출을 추가한다.
* ============================================================ */
int main(void) {
srand((unsigned)time(NULL));
run_tests();
char stones[2] = { 'O', 'X' };
int turn = 0;
init_board();
while (1) {
print_board();
char stone = stones[turn];
int r, c;
if (turn == 1) {
ai_move('X', 'O');
r = ai_r;
c = ai_c;
printf("컴퓨터가 %d, %d에 돌을 뒀습니다.\n", r, c);
}
else {
printf("\n[%c 차례] 행 열 입력 (예: 7 7): ", stone);
scanf("%d %d", &r, &c);
if (!can_place(r, c)) {
printf("거기는 둘 수 없어요!\n");
continue;
}
}
board[r][c] = stone;
if (check_win(r, c, stone)) {
print_board();
printf("\n*** %c 승리! ***\n", stone);
break;
}
turn = 1 - turn;
}
return 0;
}
To embed this project on your website, copy the following code and paste it into your website's HTML: