소개
이 프로젝트에서는 C 언어로 간단한 틱택토 (Tic-Tac-Toe) 게임을 만드는 방법을 배웁니다. 이 게임은 3x3 격자에서 두 명의 플레이어가 번갈아 가며 공간을 표시하는 방식으로 진행됩니다. 가로, 세로 또는 대각선으로 자신의 표시를 세 개 먼저 만드는 플레이어가 승리합니다. 모든 공간이 채워졌지만 어느 플레이어도 세 개의 표시를 연속으로 만들지 못하면 게임은 무승부로 끝납니다.
👀 미리보기

🎯 과제
이 프로젝트에서 다음을 배우게 됩니다:
- 게임 보드를 생성하고 빈 공간으로 초기화하는 방법.
- 화면을 지우고, 게임 보드를 표시하고, 게임이 종료되었는지 확인하는 함수를 구현하는 방법.
- 게임의 승자를 결정하는 방법.
- 플레이어가 턴을 하고 게임과 상호 작용할 수 있도록 메인 게임 루프를 구현하는 방법.
🏆 성과
이 프로젝트를 완료하면 다음을 수행할 수 있습니다:
- C 언어에서 배열을 생성하고 조작할 수 있습니다.
- 루프와 조건문을 사용하여 게임 로직을 구현할 수 있습니다.
- 명령줄 인터페이스를 통해 사용자와 상호 작용할 수 있습니다.
- 코드의 모듈성과 가독성을 향상시키기 위해 코드를 함수로 구성할 수 있습니다.
프로젝트 파일 생성
먼저, tictactoe.c라는 새 파일을 생성하고 선호하는 코드 편집기에서 엽니다.
cd ~/project
touch tictactoe.c
상수 정의
이제 C 코드를 작성해야 합니다. 첫 번째 단계는 헤더 파일을 포함하는 것입니다:
#include <stdio.h>
#include <stdbool.h>
보드 크기를 선언합니다:
char board[3][3];
게임 보드 초기화
빈 공간으로 게임 보드를 초기화합니다.
void initializeBoard() {
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
board[i][j] = ' ';
}
}
}
첫 번째 for는 외부 루프로, 변수 i를 사용하여 0부터 2까지 반복하며, 게임 보드의 행 수를 나타냅니다. 두 번째 for는 내부 루프로, 변수 j를 사용하여 0부터 2까지 반복하며, 차례대로 게임 보드의 열 수를 나타냅니다.
board[i][j] = ' ' 내부 루프에서, 게임 보드의 행 i와 열 j에 있는 사각형의 내용을 공백 문자 (' ')로 설정합니다. 이는 전체 게임 보드가 비어있는 상태, 즉 보드에 아무도 플레이하지 않은 상태로 초기화됨을 의미합니다.
각 턴 후 화면 지우기
각 턴 후에 화면을 지우는 함수를 구현합니다.
void clearScreen() {
printf("\033[H\033[J");
}
clearScreen 함수는 주로 각 턴이 끝날 때 이전 게임 상태와 출력을 지우기 위해 터미널 또는 콘솔 화면의 내용을 지우는 데 사용됩니다. 이는 화면의 텍스트 내용을 지우는 특수 이스케이프 시퀀스를 표준 출력 스트림 (일반적으로 터미널 창) 에 출력하여 수행됩니다.
printf("\033[H\033[J")는 ANSI 이스케이프 시퀀스를 사용합니다:
\033은ASCII이스케이프 문자의 8 진수 표현으로, 시퀀스의 시작을 나타냅니다.[H는 커서를 화면의 왼쪽 상단 모서리로 이동하는 것을 나타내며, 화면의 첫 번째 행과 첫 번째 열로 커서를 위치시키는 것과 같습니다.[J는 화면 지우기를 의미합니다. 커서 위치 이후의 모든 텍스트 내용, 즉 이전 게임 상태와 출력을 지웁니다.
이는 clearScreen 함수가 호출될 때마다 다음 게임 세션 전에 새로운 게임 상태를 표시하기 위해 현재 터미널 또는 콘솔 화면의 내용을 지워 더 깔끔한 인터페이스를 제공함을 의미합니다.
게임 보드 표시
현재 게임 보드의 상태를 화면에 시각적으로 표시하는 printBoard 함수를 생성하여 플레이어가 보드 위의 조각 위치를 이해할 수 있도록 합니다. 이는 틱택토 게임에서 게임의 상태를 보여주기 위해 사용되는 게임의 일부입니다.
void printBoard() {
printf("\n");
printf(" 1 2 3\n");
printf("1 %c | %c | %c\n", board[0][0], board[0][1], board[0][2]);
printf(" ---------\n");
printf("2 %c | %c | %c\n", board[1][0], board[1][1], board[1][2]);
printf(" ---------\n");
printf("3 %c | %c | %c\n", board[2][0], board[2][1], board[2][2]);
}
게임 종료 여부 확인
틱택토 게임이 종료되었는지 여부를 결정하고 부울 값 (true 또는 false) 을 반환하도록 정의된 isGameOver 함수를 구현합니다.
bool isGameOver() {
// Check rows
for (int i = 0; i < 3; i++) {
if (board[i][0] != ' ' && board[i][0] == board[i][1] && board[i][0] == board[i][2]) {
return true;
}
}
// Check columns
for (int j = 0; j < 3; j++) {
if (board[0][j] != ' ' && board[0][j] == board[1][j] && board[0][j] == board[2][j]) {
return true;
}
}
// Check diagonals
if (board[0][0] != ' ' && board[0][0] == board[1][1] && board[0][0] == board[2][2]) {
return true;
}
if (board[0][2] != ' ' && board[0][2] == board[1][1] && board[0][2] == board[2][0]) {
return true;
}
// Check for a draw
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
if (board[i][j] == ' ') {
return false;
}
}
}
return true;
}
행에 승자가 있는지 확인: 행의 세 셀이 모두 동일한 플레이어에 의해 점유된 경우 승자가 있으며 함수는 true를 반환합니다.
열에 승자가 있는지 확인: 열의 세 셀이 모두 동일한 플레이어에 의해 점유된 경우 승자가 있으며 함수는 true를 반환합니다.
대각선에 승자가 있는지 확인: 대각선상의 세 칸이 모두 동일한 플레이어에 의해 점유된 경우 승자가 있으며 함수는 true를 반환합니다.
무승부 확인: 마지막으로, 함수는 모든 셀이 점유되었지만 승자가 없는지 확인합니다. 이 경우 게임은 무승부이며 함수는 true를 반환합니다.
승자 결정
getWinner라는 함수가 정의되어 있으며, 이 함수의 목적은 틱택토 게임에서 승자가 있는지 확인하고 승자의 플래그 (X 또는 O) 를 반환하거나, 승자가 없는 경우 공백 (' ')을 반환하는 것입니다.
char getWinner() {
// Check rows
for (int i = 0; i < 3; i++) {
if (board[i][0] != ' ' && board[i][0] == board[i][1] && board[i][0] == board[i][2]) {
return board[i][0];
}
}
// Check columns
for (int j = 0; j < 3; j++) {
if (board[0][j] != ' ' && board[0][j] == board[1][j] && board[0][j] == board[2][j]) {
return board[0][j];
}
}
// Check diagonals
if (board[0][0] != ' ' && board[0][0] == board[1][1] && board[0][0] == board[2][2]) {
return board[0][0];
}
if (board[0][2] != ' ' && board[0][2] == board[1][1] && board[0][2] == board[2][0]) {
return board[0][2];
}
return ' '; // No winner
}
메인 게임 루프 구현
플레이어가 턴을 진행하고 게임과 상호 작용할 수 있도록 메인 게임 루프를 작성합니다.
int main() {
initializeBoard();
int currentPlayer = 1;
while (1) {
clearScreen();
printf("Current board state:\n");
printBoard();
int row, col;
printf("Player %d, please enter a row and column (e.g., 1 2):", currentPlayer);
while (scanf("%d %d", &row, &col) != 2) {
printf("Invalid input, please try again: ");
while (getchar() != '\n');
}
if (row < 1 || row > 3 || col < 1 || col > 3 || board[row - 1][col - 1] != ' ') {
printf("Invalid move, please try again.\n");
} else {
if (currentPlayer == 1) {
board[row - 1][col - 1] = 'X';
currentPlayer = 2;
} else {
board[row - 1][col - 1] = 'O';
currentPlayer = 1;
}
}
if (isGameOver()) {
clearScreen();
printf("Game over!\n");
printBoard();
char winner = getWinner();
if (winner != ' ') {
printf("Player %c wins!\n", winner);
} else {
printf("It's a draw!\n");
}
break;
}
}
return 0;
}
initializeBoard 함수를 호출하여 게임 보드를 초기화하고 모든 칸을 공백으로 설정합니다.
현재 턴이 누구인지 추적하기 위해 정수 변수 currentPlayer를 생성합니다. 초기 설정은 1 이며, 플레이어 1 을 나타냅니다.
while (1) 메인 루프에서:
clearScreen함수를 호출하여 화면을 지워 각 턴 후에 디스플레이가 새로 고쳐지도록 합니다.printBoard함수를 호출하여 현재 게임 보드를 출력합니다.
플레이어 입력: 현재 플레이어에게 scanf 함수를 통해 행 및 열 좌표를 입력하도록 요청합니다 (예: Player 1, please enter a row and column (e.g., 1 2):). 입력이 유효하지 않은 경우 (두 개의 정수가 아닌 경우) Invalid input, please try again: 메시지가 표시되고 유효한 입력이 얻어질 때까지 입력 버퍼가 지워집니다.
입력 유효성 검사: 그런 다음 입력된 행 및 열 좌표가 유효한지 (1 에서 3 까지의 범위) 와 선택한 위치가 공백인지 확인합니다. 입력이 유효하지 않거나 선택한 위치가 이미 점유된 경우 Invalid move, please try again. 메시지가 표시되고 플레이어에게 다시 입력하라는 메시지가 표시됩니다.
체스: 입력이 유효하면 코드는 현재 플레이어 (1 또는 2) 에 따라 게임 보드의 해당 위치에 X 또는 O를 배치합니다.
게임 종료 감지: 그런 다음 코드는 isGameOver() 함수를 호출하여 게임이 종료되었는지 확인합니다. 게임이 종료되면 화면이 지워지고 게임이 종료되었다는 메시지가 표시되며, 승자 (있는 경우) 또는 무승부가 포함됩니다.
게임 결과: 승자가 있는 경우 코드는 Player X wins! 또는 Player O wins!를 표시합니다. 승자가 누구인지에 따라 다릅니다. 승자가 없는 경우 코드는 It's a draw!를 표시하여 무승부임을 나타냅니다.
프로젝트 컴파일 및 실행
gcc 명령을 실행하여 컴파일합니다.
cd ~/project
gcc -o tictactoe tictactoe.c
./tictactoe

요약
축하합니다! C 로 간단한 틱택토 게임을 성공적으로 만들었습니다. 플레이어는 3x3 그리드에서 턴을 번갈아 가며 자신의 위치를 표시할 수 있습니다. 게임은 각 턴 후에 승자 또는 무승부를 확인하고 그에 따라 결과를 표시합니다. 이 간단한 프로젝트는 C 프로그래밍의 기본 개념을 보여주며, 더 복잡한 게임 개발의 시작점으로 사용할 수 있습니다.



