https://eatstar.tistory.com/21
기말과제 - C언어로 오목 프로그램 만들기 (上- 헤더 파일과 사용자 정의함수)
* 본문은 Microsoft Visual Studio 2022 버전 17.6.3를 사용하였으므로 다른 버전의 비쥬얼 스튜디오나 다른 C언어 작동 프로그램으로 작동할 경우 오류가 생길 수 있음을 양지해주세요. 1학기 과목에서 학
eatstar.tistory.com
헤더 파일, 사용자 정의 함수 등의 내용이 위 링크에 있으니 위 게시물을 먼저 보고 와주세요.
* 본문은 Microsoft Visual Studio 2022 버전 17.6.3를 사용하였으므로 다른 버전의 비쥬얼 스튜디오나 다른 C언어 작동 프로그램으로 작동할 경우 오류가 생길 수 있음을 양지해주세요.
상 편에서 사용자 정의함수를 살펴보았으니, 이제 이것들을 활용한 전체 코드의 진행을 알아보자.
포함되는 헤더 파일들 (line 1-9)
#include <stdio.h>
// 유니코드 출력을 위한 헤더 파일
#include <wchar.h>
#include <locale.h>
// cls(초기화) 작업을 위한 헤더 파일
#include <windows.h>
// 난수 랜덤을 위한 헤더 파일
#include <stdlib.h>
#include <time.h>
이 코드에서는 6개의 헤더 파일이 포함된다.
stdio.h는 입출력에 관여하는 헤더 파일로, 모든 C언어 파일에 필수적으로 등장한다.
whar.h와 locale.h는 특수문자 출력에 필요한 헤더 파일이다. 교재에 나오지는 않지만 인터넷 검색으로 해결했다.
windows.h는 cls(초기화) 작업을 위한 헤더 파일이다.
stdlib.h와 time.h는 난수를 호출하기 위한 헤더 파일이다.
특수문자 배열 선언, 사용자 정의함수 호출 (line 11-17)
#define SIZE 19
// 유니코드 출력을 위한 배열 선언 wprint도 그것을 위한 것이다.
wchar_t s[SIZE][SIZE];
void printgame();
void editgame();
void whowin();
이 코드에서는 배열의 사이즈를 define으로 정의하고 wchar_t를 활용해 s 배열을 정의한다. 여기서 굳이 SIZE를 19로 정의하고 배열 정의시에 s[SIZE][SIZE]로 하는 이유는 이후에 for문을 진행할 때 범위를 SIZE에서 얼마를 더하거나 빼야할 때가 있는데, 그 때 안헷갈리기 위함이다.
그리고 printgame()과 editgame(), whowin()함수가 정의되었다. 모두 사용자 정의함수인데, 컴퓨터가 미리 입출력 형식을 알아야 오류가 안생기기 때문에 이와 같이 정의해준다. 상 편에서 다뤘던 함수들이다.
main 함수 돌입, 초기화면 구현 (line 19-30)
int main(void) {
int playmode;
int x = -1;
int y = -1; // 오목 행렬 변수
setlocale(LC_ALL, ""); // 유니코드 문자 출력을 위한 코드
printf("오목게임에 오신 것을 환영합니다!\n");
printf("2인용 게임을 하시겠습니까? AI 게임을 하시겠습니까? (2인용(프리 룰): 1번 / AI게임: 2번)\n");
printf("이 프로그램에서는 프리룰만 지원합니다. 프리룰은, 3.3 4.4 6목 등 모든 수가 허용됩니다.\n\n");
printf("플레이 모드 : ");
scanf_s("%d", &playmode);
오목을 착수할 값 x,y 변수를 정의하고, 플레이모드 변수도 정의한다.
유니코드 문자를 출력하기 위한 함수도 추가한다.
초기 화면을 구현하는데, 게임에 대한 설명을 하고 플레이 모드에 대한 값을 받는다.
playmode == 1 진입, 2인용 게임 플레이 (line 32-92)
if (playmode == 1) {
system("cls");
printf("\t<< 오목게임 >>\t<< 2인용 >>\n");
printgame();
while (1) {
black:
printf("당신은 어느 곳에 수를 놓겠습니까? (x,y)\n");
scanf_s("%d %d", &x, &y);
// 흑 중복수 방지, 기존 행렬에서 보기 좋게 x,y로 표기할 수있게 배열상 순서 변경
if (s[y - 1][x - 1] == L'●' || s[y - 1][x - 1] == L'○') {
printf("중복수입니다. 다시 하십시오.");
goto black;
}
// 흑 오좌표 방지
else if (x < 1 || x> SIZE) {
printf("좌표가 잘못되었습니다. 다시 선택하십시오.");
goto black;
}
else if (y < 1 || y> SIZE) {
printf("좌표가 잘못되었습니다. 다시 선택하십시오.");
goto black;
}
else {
s[y - 1][x - 1] = L'●';
}
// 판 초기화 및 재생성
system("cls");
printf("\t<< 오목게임 >>\t<< 2인용 >>\n");
editgame();
// 게임 종료 여부 판단
whowin();
white:
printf("당신은 어느 곳에 수를 놓겠습니까? (x,y)\n");
scanf_s("%d %d", &x, &y);
// 백 중복수 방지
if (s[y - 1][x - 1] == L'●' || s[y - 1][x - 1] == L'○') {
printf("중복수입니다. 다시 하십시오.");
goto white;
}
// 백 오좌표 방지
else if (x < 1 || x > SIZE) {
printf("좌표가 잘못되었습니다. 다시 선택하십시오.");
goto white;
}
else if (y < 1 || y > SIZE) {
printf("좌표가 잘못되었습니다. 다시 선택하십시오.");
goto white;
}
else {
s[y - 1][x - 1] = L'○';
}
// 판 초기화 및 재생성
system("cls");
printf("\t<< 오목게임 >>\t<< 2인용 >>\n");
editgame();
// 게임 종료 여부 판단
whowin();
}
}
일단 이것은 playmode == 1로 진입하면 진행되는 코드다.
처음에는 system("cls")로 초기화면을 없애준다. 그리고 printgame()을 호출해 오목판을 생성한다.
어느 곳에 착수할지 x y 좌표로 입력받는다. (공백을 사이에 두고) 입력받으면 이것이 배열에 등록되기 전에,
알맞은 착수인지 판단하는데, 기준은 다음과 같다.
(1) x 또는 y 좌표가 범위를 초과하는 착수인가?
(2) 이미 둔 곳에 착수를 하려고 하는가?
(2)번을 방지하기 위해 [y-1][x-1]이 흑돌이거나 백돌이면 이미 둬진 자리라고 판단하고 goto함수를 통해 다시 black:으로 돌아간다. 여기서 [y-1][x-1]인 이유는, 행렬과 xy좌표의 순서는 반대이기 때문에 반전시켜주고, 배열은 0부터 시작되기 때문에 실제 좌표에서 -1을 해야 배열에 알맞게 저장된다.
(1)번을 방지하기 위해서는 x가 1보다 작거나 SIZE(19)보다 크면 goto함수를 통해 black:으로 되돌아간다. y도 같은 과정을 반복한다.
(1)번과 (2)번 모두 만족했다면, 배열에 그 좌표를 저장하고, cls로 초기화를 한 뒤, editgame() 함수를 호출한다.
그리고 whowin() 함수를 호출하여 게임 종료 여부를 판단한다. 만약 종료되지 않았다면 exit(0) 함수가 호출되지 않기 때문에 계속 코드를 진행한다. (while(1) 루프를 돌고 있으므로)
흑 다음에는 백도 같은 과정을 반복한다. 특수문자가 ●가 아니라 ○인것만 다르다.
한 편, AI 게임이라던 playmode == 2를 확인해보자.
playmode == 2 진입, AI 게임 플레이 (line 93-155)
else {
system("cls");
printf("\t<< 오목게임 >>\t<< AI 게임 >>\n");
printgame();
while (1) {
black2:
printf("당신은 어느 곳에 수를 놓겠습니까? (x,y)\n");
scanf_s("%d %d", &x, &y);
// 흑 중복수 방지, 기존 행렬에서 보기 좋게 x,y로 표기할 수있게 배열상 순서 변경
if (s[y - 1][x - 1] == L'●' || s[y - 1][x - 1] == L'○') {
printf("중복수입니다. 다시 하십시오.");
goto black2;
}
// 흑 오좌표 방지
else if (x < 1 || x> SIZE) {
printf("좌표가 잘못되었습니다. 다시 선택하십시오.");
goto black2;
}
else if (y < 1 || y> SIZE) {
printf("좌표가 잘못되었습니다. 다시 선택하십시오.");
goto black2;
}
else {
s[y - 1][x - 1] = L'●';
}
// 판 초기화 및 재생성
system("cls");
printf("\t<< 오목게임 >>\t<< AI 게임 >>\n");
editgame();
// 게임 종료 여부 판단
whowin();
white2:
//컴퓨터 턴
printf("컴퓨터가 수를 선택하고 있습니다.");
Sleep(1000);
printf(".");
Sleep(1000);
printf(".");
Sleep(1000);
srand(time(NULL)); // 난수 랜덤화
x = rand() % 19;
y = rand() % 19;
// 백(컴퓨터) 중복수 방지, 기존 행렬에서 보기 좋게 x,y로 표기할 수있게 배열상 순서 변경
if (s[y - 1][x - 1] == L'●' || s[y - 1][x - 1] == L'○') {
printf("컴퓨터의 중복수입니다. 잠시 기다려주세요.\n");
goto white2;
}
else {
s[y][x] = L'○';
}
// 판 초기화 및 재생성
system("cls");
printf("\t<< 오목게임 >>\t<< AI 게임 >>\n");
editgame();
// 게임 종료 여부 판단
whowin();
}
}
기본적으로 playmode == 2와 동일하다. 하지만 백을 컴퓨터가 잡는다. "컴퓨터가 수를 선택하고 있습니다."라는 메세지를 sleep함수를 이용해 1000ms(1초)를 3번 호출하여 3초간 기다리게 하고, rand()으로 0~18에서 난수를 출력하여 배열에 저장한다. 0~18 사이의 난수이므로 배열 저장 때는s[y-1][x-1]이 아닌, s[y][x]로 해준다. 컴퓨터가 오좌표를 출력할리는 없지만, 중복수가 등장할 수는 있으므로 중복수 판정 함수는 넣어준다.
black: white:는 이미 사용되었으므로 black2:와 white2:를 사용해주자.
Ending
이렇게 상, 하 편 해설을 통해 C언어를 통한 오목 프로그램 생성에 대해 알아보았다.
개인적으로 wprint를 활용해 printgame() 함수를 만드는 것이 가장 어려웠던거 같다. (교재 외의 내용이라..) 물론 규칙 만드는 것도 어려웠고, 내가 한 것이 프리룰이라 망정이지, 렌주룰에 따라서 규칙을 생성하려면 더 어려웠을 것이다.
교재에 있는 틱택토 예제에서 영감을 얻은 것 같다.
ChatGPT에게도 어느정도 도움을 받았으나.. 전체적인 설계능력은 전혀 없기 때문에 오타 수정이나 간단한 오류, 간단한 알고리즘을 추천받을 때 사용했다. 지분은 10% 정도 줄 수 있다.
아직 실력이 부족하지만, 많은 도움이 되었기를 바라고, 이 글을 봐주어서 고맙습니다.
아래에는 프로그램의 전체 소스코드가 있다.
전체 소스코드
https://eatstar.tistory.com/23
기말과제 - C언어로 오목 프로그램 (전체 소스코드)
* 본문은 Microsoft Visual Studio 2022 버전 17.6.3를 사용하였으므로 다른 버전의 비쥬얼 스튜디오나 다른 C언어 작동 프로그램으로 작동할 경우 오류가 생길 수 있음을 양지해주세요. 해설은 다음 링크
eatstar.tistory.com
'프로그래밍 project > C언어' 카테고리의 다른 글
기말과제 - C언어로 오목 프로그램 (전체 소스코드) (1) | 2023.06.18 |
---|---|
기말과제 - C언어로 오목 프로그램 만들기 (上- 사용자 정의 함수) (1) | 2023.06.17 |