티스토리 뷰

과제


오목을 두는 프로그램을 만들어보자. 주어진 조건은 아래와 같다.


1. 화면에 바둑판을 그려라.

2. 좌표를 입력받아 바둑판에 돌을 놓는 프로그램을 작성하라. (돌의 표시는 마음대로 해도 좋다)

3. 검은돌과 흰돌을 번갈아 놓도록 수정하라.

4. 바둑판에 놓이 검은 돌의 개수와 흰 돌의 개수가 출력되도록 수정하라.



순서도를 그려보자.





과제의 흐름도를 되새겨서 작성해본다.


1. 흑백돌, 승패여부를 나열했다.


1
2
typedef enum target { WHITE = -1, EMPTY = 0, BLACK = 1 } TARGET;
typedef enum game { WIN = 1, DRAW = 0, LOSE = -1 } GAME;                // white 입장의 승패
cs



2. 숫자보단 'A7'과 같은 형태로 작성하면 보기 쉬울 것 같아 이에 필요한 열거형 타입, 구조체를 정의했다.


1
2
3
4
5
6
typedef enum charlocation { A = 1, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z } charLocation;
 
typedef struct point {                    // H9 와 같은 방법으로 입력받기 위한 구조체
    charLocation col;
    int row;
} POINT;
cs



3. 바둑판을 그렸다. 메인문이 복잡해 지는 게 싫어 처음부터 함수로 만들어 바둑판을 구현했다.


초기엔 printf를 통헤 정말 바둑판만 구현했고 다음엔 바둑돌에 맞게 끔 바둑판을 재 구성하였다. 

또한 좌표를 그리기 위해 for문을 사용했고 이를 인자로 전달받아 표시해보았다. 이런 식으로 중간중간 변형하여 사용하였다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
void draw_board(TARGET locate[][WIDTH]) {
    system("cls");
    for (int i = 64; i <= 64 + WIDTH; i++) {        // X좌표계 출력
        if (i == 64) {
            printf("     ");
        }
        else {
            printf("%c ", (char)i);
        }
    }
    printf("\n");
    // 윗줄 격자 생성
    printf("  ┌─");
    for (int j = 0; j < WIDTH; j++) {
        printf("┬─");
    }
    printf("┐\n");
 
    // 메인 바둑판
    for (int i = 0; i < HEIGHT; i++) {
        printf("%02d├─", i + 1);                        // Y좌표계 출력
        for (int j = 0; j < WIDTH; j++) {
            if (locate[i][j] == 0) {
                printf("┼─");
            }
            else if (locate[i][j] == -1) {
                printf("○");
            }
            else {
                printf("●");
            }
        }
        printf("┤\n");
    }
    
    // 아래줄 격자 생성
    printf("  └─");
    for (int j = 0; j < WIDTH; j++) {
        printf("┴─");
    }
    printf("┘\n");
}
cs



4. 이차원 배열을 통해 돌의 위치를 -1, 0, 1로 각각 백돌, 없음, 흑돌로 할당하려고 했다. 여기서 다음과 같은 문제들이 발생했다.


1. 입력받은 "K4" 같은 값을 위의 단순화 하는 과정이 필요했다.

- 이는 5번에 나온 change_point() 함수를 통해 해결했다.

2. 이미 있는 위치에 바둑돌을 둘 수 있다. 중복을 불가능하게 하는 장치가 필요했다. 

- 이는 리턴값을 사용해 반복 루프를 돌리는 방법으로 처리했다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 
int took_stone(TARGET locate[][WIDTH], POINT pointLocation, shortchar turn) {
    int c = pointLocation.col - 65;
    int r = pointLocation.row - 1;
 
    if (locate[r][c] != 0) {
        return 1;                                // 해당 위치에 돌이 있는 경우
    }
    else {
 
        if (turn % 2) {
            locate[r][c] = WHITE;                // status[HEIGHT][WIDTH]에 대입
        }
        else {
            locate[r][c] = BLACK;
        }
    }
    return 0;
}
cs


1
2
3
        if (took_stone(status, pointLocation, turn)) {
            continue;
        }
cs



5. 돌의 위치를 문자열로 입력받아 각각 문자, 정수형 데이터로 나누어 저장하였다.


중요한 함수를 소개한다. atoi(char *) : 문자열을 정수로 변환하는 함수 <stdlib.h>에 선언되어 있다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
POINT change_point(char *buffer) {
 
    POINT returnPoint;
    char sAtoi[3];
 
    for (int i = 1; i <= 2; i++) {
        sAtoi[i - 1= buffer[i];
    }
 
    returnPoint.col = (charLocation)buffer[0];
 
    returnPoint.row = atoi(sAtoi);        // 문자열을 정수로 변환하여 height에 할당
 
    return returnPoint;
}
cs



6. 턴의 정보를 입력하기 위해 다음의 장치를 추가하였다.


1
2
3
4
5
6
if (turn % 2) {                                // 턴정보 알려줌
            printf("%d번째 턴, 백돌 : %d, 흑돌 : %d\n", turn, (turn - 1/ 2, (turn - 1/ 2);
        }
        else {
            printf("%d번째 턴, 백돌 : %d, 흑돌 : %d\n", turn, turn / 2, turn / 2 - 1);
        }
cs



7. 오목 여부를 판단하기 위해 다음의 함수를 추가하였다.


고민없이 사용해도 문제 없었지만 다음 상황을 고려해 보았다.

1. 끝에서 1, 2칸만 떨어진 위치에선 오목이 성립되는 조건이 한정적이다.

2. 8칸을 볼필요 없이 연속된 선을 찾아내는 것이 가능하다.

3. 분류를 통해 식을 좀 더 가독성있게 작성이 가능하다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
void find_checkmate(TARGET locate[][WIDTH], GAME *pResult) {
 
    for (int i = 0; i < HEIGHT; i++) {
        if (*pResult) {
            break;
        }
        for (int j = 0; j < WIDTH; j++) {
            if (locate[i][j] != 0) {                        // 뭔가 돌이 있으면
                if (i >= 2 && i <= HEIGHT - 3 && j >= 2 && j <= WIDTH - 3) {        
                    if (locate[i - 1][j - 1== locate[i][j] && locate[i][j] == locate[i + 1][j + 1]) {            // ↘방향
                        if (locate[i - 2][j - 2== locate[i][j] && locate[i][j] == locate[i + 2][j + 2]) {
                            *pResult = locate[i][j];
                            break;
                        }
                        else {
                            continue;
                        }
                    }
                    else if (locate[i + 1][j - 1== locate[i][j] && locate[i][j] == locate[i - 1][j + 1]) {    // ↗방향
                        if (locate[i + 2][j - 2== locate[i][j] && locate[i][j] == locate[i - 2][j + 2]) {
                            *pResult = locate[i][j];
                            break;
                        }
                        else {
                            continue;
                        }
                    }
                }
                else if (i >= 2 && i <= HEIGHT - 3) {
                    if (locate[i - 1][j] == locate[i][j] && locate[i][j] == locate[i + 1][j]) {                    // ↑방향
                        if (locate[i - 2][j] == locate[i][j] && locate[i][j] == locate[i + 2][j]) {
                            *pResult = locate[i][j];
                            break;
                        }
                        else {
                            continue;
                        }
                    }
                }
                else if (j >= 2 && j <= WIDTH - 3) {
                    if (locate[i][j - 1== locate[i][j] && locate[i][j] == locate[i][j + 1]) {                    // →방향
                        if (locate[i][j - 2== locate[i][j] && locate[i][j] == locate[i][j + 2]) {
                            *pResult = locate[i][j];
                            break;
                        }
                        else {
                            continue;
                        }
                    }
                }
                else {
                    continue;
                }
            }
        }
    }
}
cs



8. 승패여부를 표시한다.


1
2
3
4
5
6
7
8
9
10
11
12
13
void game_result(GAME result) {
    switch (result) {
    case -1:
        printf("백돌이 이겼습니다.");
        break;
    case 1:
        printf("흑돌이 이겼습니다.");
        break;
    case 0:
        printf("무승부입니다.");
        break;
    }
}
cs




개선사항


위 프로그램엔 승부가 나지 않고 모든 바둑돌을 둔 상태에 대한 결과를 구현하지 못했다.



느낀 점


함수의 선언과 서술을 같이하는 스타일의 코딩을 했었는데 가독성 면에서 불편함이 많았다. 선언과 서술을 분리함으로써 얻을 수 있는 효과가 상당했다. 위 내용에는 고민하고 반복해 시도한 이력은 나오지 않는다. 다만 그 이면엔 많은 시행착오와 실행, 디버깅 과정이 있었다. 



코드요약



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
 
#define WIDTH 19
#define HEIGHT 19
 
typedef enum charlocation { A = 1, B, C, D, E, F, G, H, I, J, K, L, M, N, O, P, Q, R, S, T, U, V, W, X, Y, Z } charLocation;
typedef enum target { WHITE = -1, EMPTY = 0, BLACK = 1 } TARGET;
typedef enum game { WIN = 1, DRAW = 0, LOSE = -1 } GAME;                // white 입장의 승패
 
typedef unsigned char shortchar;
typedef struct point {                    // H9 와 같은 방법으로 입력받기 위한 구조체
    charLocation col;
    int row;
} POINT;
 
void draw_board(TARGET locate[][WIDTH]);            // 바둑판 그리는 함수
int took_stone(TARGET locate[][WIDTH], POINT pointLocation, shortchar turn);
POINT change_point(char *buffer);                    // 입력받은 값을 POINT 구조체로 변환
void find_checkmate(TARGET locate[][WIDTH], GAME *pResult);
void game_result(GAME result);
 
int main() {
 
    shortchar turn = 1;                                // 턴 정보
    POINT pointLocation;                            // 위치 입력
    GAME result = DRAW;                                // 게임 결과
    GAME *pResult = &result;
    TARGET status[HEIGHT][WIDTH] = { EMPTY };        // 바둑판 상황 ROW, COLS
    char buffer[4];
 
    do {
        draw_board(status);                            // 바둑판 그리기
 
        if (turn % 2) {                                // 턴정보 알려줌
            printf("%d번째 턴, 백돌 : %d, 흑돌 : %d\n", turn, (turn - 1/ 2, (turn - 1/ 2);
        }
        else {
            printf("%d번째 턴, 백돌 : %d, 흑돌 : %d\n", turn, turn / 2, turn / 2 - 1);
        }
 
        printf("다음 돌의 위치를 지정하세요 : ");
        scanf("%s", buffer);
 
        pointLocation = change_point(buffer);        // C09, C9 둘다 C, 9로 나누어 저장
        
        if (took_stone(status, pointLocation, turn)) {
            continue;
        }
        
        find_checkmate(status, pResult);
        
        turn++;
 
    } while (!(result));
 
    game_result(result);
 
    return 0;
}
 
void draw_board(TARGET locate[][WIDTH]) {
    system("cls");
    for (int i = 64; i <= 64 + WIDTH; i++) {        // X좌표계 출력
        if (i == 64) {
            printf("     ");
        }
        else {
            printf("%c ", (char)i);
        }
    }
    printf("\n");
    // 윗줄 격자 생성
    printf("  ┌─");
    for (int j = 0; j < WIDTH; j++) {
        printf("┬─");
    }
    printf("┐\n");
 
    // 메인 바둑판
    for (int i = 0; i < HEIGHT; i++) {
        printf("%02d├─", i + 1);                        // Y좌표계 출력
        for (int j = 0; j < WIDTH; j++) {
            if (locate[i][j] == 0) {
                printf("┼─");
            }
            else if (locate[i][j] == -1) {
                printf("○");
            }
            else {
                printf("●");
            }
        }
        printf("┤\n");
    }
    
    // 아래줄 격자 생성
    printf("  └─");
    for (int j = 0; j < WIDTH; j++) {
        printf("┴─");
    }
    printf("┘\n");
}
 
int took_stone(TARGET locate[][WIDTH], POINT pointLocation, shortchar turn) {
    int c = pointLocation.col - 65;
    int r = pointLocation.row - 1;
 
    if (locate[r][c] != 0) {
        return 1;                                // 해당 위치에 돌이 있는 경우
    }
    else {
 
        if (turn % 2) {
            locate[r][c] = WHITE;                // status[HEIGHT][WIDTH]에 대입
        }
        else {
            locate[r][c] = BLACK;
        }
    }
    return 0;
}
 
POINT change_point(char *buffer) {
 
    POINT returnPoint;
    char sAtoi[3];
 
    for (int i = 1; i <= 2; i++) {
        sAtoi[i - 1= buffer[i];
    }
 
    returnPoint.col = (charLocation)buffer[0];
 
    returnPoint.row = atoi(sAtoi);        // 문자열을 정수로 변환하여 height에 할당
 
    return returnPoint;
}
 
void find_checkmate(TARGET locate[][WIDTH], GAME *pResult) {
 
    for (int i = 0; i < HEIGHT; i++) {
        if (*pResult) {
            break;
        }
        for (int j = 0; j < WIDTH; j++) {
            if (locate[i][j] != 0) {                        // 뭔가 돌이 있으면
                if (i >= 2 && i <= HEIGHT - 3 && j >= 2 && j <= WIDTH - 3) {        
                    if (locate[i - 1][j - 1== locate[i][j] && locate[i][j] == locate[i + 1][j + 1]) {            // ↘방향
                        if (locate[i - 2][j - 2== locate[i][j] && locate[i][j] == locate[i + 2][j + 2]) {
                            *pResult = locate[i][j];
                            break;
                        }
                        else {
                            continue;
                        }
                    }
                    else if (locate[i + 1][j - 1== locate[i][j] && locate[i][j] == locate[i - 1][j + 1]) {    // ↗방향
                        if (locate[i + 2][j - 2== locate[i][j] && locate[i][j] == locate[i - 2][j + 2]) {
                            *pResult = locate[i][j];
                            break;
                        }
                        else {
                            continue;
                        }
                    }
                }
                else if (i >= 2 && i <= HEIGHT - 3) {
                    if (locate[i - 1][j] == locate[i][j] && locate[i][j] == locate[i + 1][j]) {                    // ↑방향
                        if (locate[i - 2][j] == locate[i][j] && locate[i][j] == locate[i + 2][j]) {
                            *pResult = locate[i][j];
                            break;
                        }
                        else {
                            continue;
                        }
                    }
                }
                else if (j >= 2 && j <= WIDTH - 3) {
                    if (locate[i][j - 1== locate[i][j] && locate[i][j] == locate[i][j + 1]) {                    // →방향
                        if (locate[i][j - 2== locate[i][j] && locate[i][j] == locate[i][j + 2]) {
                            *pResult = locate[i][j];
                            break;
                        }
                        else {
                            continue;
                        }
                    }
                }
                else {
                    continue;
                }
            }
        }
    }
}
 
void game_result(GAME result) {
    switch (result) {
    case -1:
        printf("백돌이 이겼습니다.");
        break;
    case 1:
        printf("흑돌이 이겼습니다.");
        break;
    case 0:
        printf("무승부입니다.");
        break;
    }
}
cs






댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
글 보관함