/*
 * 콘솔 오목 — 스타터 코드
 *
 * 이미 완성된 것: 판(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;
}

Embed on website

To embed this project on your website, copy the following code and paste it into your website's HTML: