티스토리 뷰
과제
오목을 두는 프로그램을 만들어보자. 주어진 조건은 아래와 같다.
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 |
'프로그래밍 > C' 카테고리의 다른 글
[창의적it 프로그래밍] 2차 과제 -동적메모리와 연결리스트- (0) | 2018.03.21 |
---|---|
[창의적it 프로그래밍] 1차 과제 -포인터- (0) | 2018.03.16 |
[창의적it 프로그래밍] 1차 과제 -구조체- (0) | 2018.03.13 |
[자료구조] 1일차_생각해 볼 내용(180312) (0) | 2018.03.08 |
[자료구조] 1일차_토론(180312) (0) | 2018.03.07 |
- Total
- Today
- Yesterday
- Gatsby.js
- MySQL
- Next.js
- nuxt.js
- 이진탐색 #중복
- nosql
- Cloud
- Remix
- alpine.js
- node.js
- RDBMS
- REACT
- PostgreSQL
- vue.js
- oracle
- gcp
- DevOps
- vue
- Azure
- svelte
- aws
- Quasar
- SQLite
- Angular
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |