Notice
Recent Posts
Recent Comments
Link
«   2026/05   »
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
Archives
Today
Total
관리 메뉴

I'm FanJae.

[리마인드] 콘솔 화면 2인용 오목 만들기 (C++) 본문

Unity/Unity 초격차캠프

[리마인드] 콘솔 화면 2인용 오목 만들기 (C++)

FanJae 2026. 4. 22. 22:46

1. 구현 방법

배운 내용을 리마인드 하는 차원에서 구현하였지만, 다루지 않았던 내용의 일부도 포함하였다.

1. menu에서 입력을 받아서 메뉴에 따라 처리를 진행한다.
2. 게임 보드판을 출력하고, 플레이어의 턴에 따라 게임을 진행한다.
    2-1. 보드판에서의 이동은 키보드 방향키를 이용하여 이동할 수 있도록 처리한다.
    2-2. Enter를 누르면 바둑 돌을 놓을 수 있게 구현하되, 해당 위치에 이미 놓인 돌이 있으면 놓지 못하게 한다.
3. 바둑돌을 놓으면, 오목이 완성되는가를 체크한다.
    3-1. 오목의 완성 조건을 구현하는 방법은 8방향 체크가 기본이지만, 4방향 체크만으로 확인할 수 있다.
    (오목 승리 로직 보충 내용에 별첨한다.)
    3-2. 4방향 체크를 진행한 이후, 1개라도 바둑 돌의 놓인 개수가 5개라면, 오목이 완성된 것으로 해당 플레이어의 승리다. 4. 오목이 완성되지 않은 경우, 게임을 계속 진행한다.

 

2. 오목 승리 조건 체크

게임 보드판에 오목의 진행 상태를 계속 처리 할 때, 다음과 같은 형태라고 가정한다.

일반적으로 하나의 바둑돌을 기준으로 볼 때, 탐색을 해야하는 방향은 총 8방향이 된다.

하지만, 방향 탐색 중에는 방향 간의 중복으로 인해서 일부 방향은 탐색할 필요가 없다.

따라서, 오목에서 이 중복되는 4방향을 제외하면 ↗ ↘ → ↙ 만 탐색하면 된다.

 

3. 구현

※ C++ Source Code

더보기

① menu() - 메뉴 기능

int menu()
{
	int input;
	do
	{
		std::cout << "============================" << std::endl;
		std::cout << "오목 게임을 플레이합시다!" << std::endl;
		std::cout << "1. 2인용 플레이" << std::endl;
		std::cout << "2. 게임 방법" << std::endl;
		std::cout << "3. Credits" << std::endl;
		std::cout << "4. 종료" << std::endl;
		std::cout << "============================" << std::endl;
		std::cout << "원하는 메뉴를 입력하세요 : ";

		std::cin >> input;

		if (input <= 0 || input >= 5)
		{
			std::cout << "다시 입력하세요." << std::endl;
		}
		else
		{
			break;
		}
	} while (true);
	return input;
}
  • 메뉴 기능으로, 오목 게임을 플레이 하기 위한 입력을 받는 창이다. 범위 내 입력이 아닌 경우, 다시 입력 받도록 예외 처리를 진행하였다.

② IndexSafeCheck() - 인덱스 범위를 초과하는지 확인하는 함수

bool IndexSafeCheck(int x, int y) // 인덱스 범위를 초과하는지 확인하는 함수
{
	if (x < 0 || x >= 15)
	{
		return false;
	}
	if (y < 0 || y >= 15)
	{
		return false;
	}
	return true;
}
  • 게임 보드판(바둑판)을 이동할 때, 인덱스 범위를 넘었는지 확인하는 함수이다.
  • 보드판의 범위를 넘은 경우 false 를 보드판의 범위를 넘지 않은 경우 true 를 반환한다.

③ PrintBoard() - 게임 보드판을 출력하는 함수

void PrintBoard(int board[15][15], int cursorX, int cursorY, int turn) // 보드 출력
{
	std::string stone[2] = {"○","●"};
	for (int i = 0; i < 15; i++)
	{
		for (int j = 0; j < 15; j++)
		{
			if (i == cursorX && j == cursorY)
			{
				std::cout << stone[turn];
			}
			else if (board[i][j] == 0) 
			{
				std::cout << "□";
			}
			else if (board[i][j] == 1) // 1p의 돌은 ○
			{
				std::cout << stone[0];
			}
			else if (board[i][j] == 2) // 2p의 돌은 ●
			{
				std::cout << stone[1];
			}
		}
		std::cout << std::endl;
	}
}
  • 게임 보드를 출력하는 함수이다.
  • 여기서는 문자열(string )을 사용하여, 게임 보드판에서는 1p와 2p의 돌이 각각 1과 2로 들어가지만, 실제로 보일때는 돌 모양으로 보이도록 처리하였다.
  • std::string 은 이전에 C++을 학습할 때 사용한 경험이 있어서 사용했다. C++ 표준 라이브러리에서 제공하는 문자열 클래스이다. 

WinCheck() - 오목의 승리 여부를 체크 하는 함수

bool WinCheck(int board[15][15], int turn)
{
	for (int i = 0; i < 15; i++)
	{
		for (int j = 0; j < 15; j++)
		{
			int cnt[4] = { 0 };
			for (int k = 0; k < 5; k++)
			{
				
				if (IndexSafeCheck(i,j+k) == true)  // → 방향으로 해당 턴 유저가 깔아놓은 돌 개수 체크
				{
					if (board[i][j + k] == turn + 1)
					{
						cnt[0]++;
					}
				}
				if (IndexSafeCheck(i + k, j) == true)  // ↓ 방향으로 해당 턴 유저가 깔아놓은 돌 개수 체크
				{
					if (board[i+k][j] == turn + 1)
					{
						cnt[1]++;
					}
				}
				if (IndexSafeCheck(i + k, j + k) == true)  // ↘ 방향으로 해당 턴 유저가 깔아놓은 돌 개수 체크
				{
					if (board[i + k][j + k] == turn + 1)
					{
						cnt[2]++;
					}
				}
				if (IndexSafeCheck(i + k, j - k) == true)  // ↙ 방향으로 해당 턴 유저가 깔아놓은 돌 개수 체크
				{
					if (board[i + k][j - k] == turn + 1)
					{
						cnt[3]++;
					}
				}
			}
			for (int i = 0; i < 4; i++)
			{
				if (cnt[i] == 5) // 이 값이 하나라도 5가 되었다는 것은 오목이 되었음을 의미한다.
				{
					return true;
				}
			}
		}
	}
	return false;
}
  • 게임 보드판에서 ➡️, ⬇️, ↘️, ↙️ 에 대한 순회를 진행한다.
  • 플레이어의 자신의 turn 과 맞는 바둑돌의 개수를 세어, 돌의 개수가 5개인 경우 오목이 완성된 것으로 판단한다.

⑤ PlayGame() - 게임 플레이 함수

void PlayGame() // 게임 플레이 함수
{
	int board[15][15] = { 0 }; // 보드판 
	int cursorX = 0, cursorY = 0; // 가상의 커서 위치
	int turn = 0;
	std::string system_message = "";
	while (1)
	{
		system("cls");
		PrintBoard(board, cursorX, cursorY, turn);
		std::cout << "Player " << turn + 1 << "P의 턴입니다." << std::endl;
		std::cout << "키보드(상,하,좌,우)키로 이동하고, Enter를 눌러 돌을 배치하여 주세요." << std::endl;
		std::cout << system_message;

		char input = _getch();

		if (input == 72) // 화살표 윗키
		{
			cursorX--;
			if (IndexSafeCheck(cursorX, cursorY) == false) cursorX++;
			system_message = "";
		}
		else if (input == 80) // 화살표 아랫키
		{
			cursorX++;
			if (IndexSafeCheck(cursorX, cursorY) == false) cursorX--;
			system_message = "";
		}
		else if (input == 75) // 화살표 왼쪽키
		{
			cursorY--;
			if (IndexSafeCheck(cursorX, cursorY) == false) cursorY++;
			system_message = "";
		}
		else if (input == 77) // 화살표 오른쪽키
		{
			cursorY++;
			if (IndexSafeCheck(cursorX, cursorY) == false) cursorY--;
			system_message = "";
		}
		else if (input == 13)
		{
			if (board[cursorX][cursorY] == 1 || board[cursorX][cursorY] == 2) // 이미 배치되어 있는 경우.
			{
				system_message = "이미 돌이 배치되어 있습니다. 다른 곳에 배치해주세요.\n";
			}
			else
			{
				board[cursorX][cursorY] = turn + 1;
				if (WinCheck(board, turn) == true) // 승부가 결정된 경우
				{
					system("cls");
					PrintBoard(board, cursorX, cursorY, turn);
					std::cout << "Player " << turn + 1 << "P의 승리로 게임을 종료합니다." << std::endl;
					std::cout << "아무 키를 입력하면 메뉴로 돌아갑니다." << std::endl;
					_getch();
					break;
				}
				else // 승부가 결정나지 않은 경우
				{
					turn = (turn + 1) % 2;
				}
			}
		}
	}
}
  • 게임에서는 임의의 ‘가상의 커서’를 방향키로 이동할 수 있게 처리하였다. 가상의 커서가 움직이거나 돌을 놓을때 마다 화면이 갱신되게 처리하였다.
  • 단, 가상의 커서가 움직이거나 돌을 놓는 즉시 처리하도록 만들기 위해서 _getch()를 사용하였다.
  • 일반적인 std::cin 과 달리 Enter 키를 추가로 누를 필요가 없기 때문에 커서 이동이나 즉시 반응이 필요할 때 사용한다. <conio.h> 에 존재하는 비표준 함수지만, 커서 이동을 깔끔하게 처리 하기 위해 사용하였다.

 ⑥ 전체 코드

#include <iostream>
#include <string>
#include <conio.h>
int menu()
{
	int input;
	do
	{
		std::cout << "============================" << std::endl;
		std::cout << "오목 게임을 플레이합시다!" << std::endl;
		std::cout << "1. 2인용 플레이" << std::endl;
		std::cout << "2. 게임 방법" << std::endl;
		std::cout << "3. Credits" << std::endl;
		std::cout << "4. 종료" << std::endl;
		std::cout << "============================" << std::endl;
		std::cout << "원하는 메뉴를 입력하세요 : ";

		std::cin >> input;

		if (input <= 0 || input >= 5)
		{
			std::cout << "다시 입력하세요." << std::endl;
		}
		else
		{
			break;
		}
	} while (true);
	return input;
}
void HowToPlay()  
{
	system("cls");
	std::cout << "============================" << std::endl;
	std::cout << "오목은 보드판 위에 5개의 돌을 연속으로 놓으면 승리하는 게임입니다." << std::endl;
	std::cout << "현재 2인용 게임만 구현되어 있으며, 1p가 먼저 시작합니다." << std::endl;
	std::cout << "[단축키]" << std::endl;
	std::cout << "키보드 ↑ ↓ ← → : 보드판에서 이동합니다." << std::endl;
	std::cout << "Enter : 바둑돌을 놓습니다." << std::endl;
}
void Credits() // 크레딧
{
	system("cls");
	std::cout << "Made by. FanJae." << std::endl;
}
bool IndexSafeCheck(int x, int y) // 인덱스 범위를 초과하는지 확인하는 함수
{
	if (x < 0 || x >= 15)
	{
		return false;
	}
	if (y < 0 || y >= 15)
	{
		return false;
	}
	return true;
}
void PrintBoard(int board[15][15], int cursorX, int cursorY, int turn) // 보드 출력
{
	std::string stone[2] = {"○","●"};
	for (int i = 0; i < 15; i++)
	{
		for (int j = 0; j < 15; j++)
		{
			if (i == cursorX && j == cursorY)
			{
				std::cout << stone[turn];
			}
			else if (board[i][j] == 0) 
			{
				std::cout << "□";
			}
			else if (board[i][j] == 1) // 1p의 돌은 ○
			{
				std::cout << stone[0];
			}
			else if (board[i][j] == 2) // 2p의 돌은 ●
			{
				std::cout << stone[1];
			}
		}
		std::cout << std::endl;
	}
}
bool WinCheck(int board[15][15], int turn)
{
	for (int i = 0; i < 15; i++)
	{
		for (int j = 0; j < 15; j++)
		{
			int cnt[4] = { 0 };
			for (int k = 0; k < 5; k++)
			{
				
				if (IndexSafeCheck(i,j+k) == true)  // → 방향으로 해당 턴 유저가 깔아놓은 돌 개수 체크
				{
					if (board[i][j + k] == turn + 1)
					{
						cnt[0]++;
					}
				}
				if (IndexSafeCheck(i + k, j) == true)  // ↓ 방향으로 해당 턴 유저가 깔아놓은 돌 개수 체크
				{
					if (board[i+k][j] == turn + 1)
					{
						cnt[1]++;
					}
				}
				if (IndexSafeCheck(i + k, j + k) == true)  // ↘ 방향으로 해당 턴 유저가 깔아놓은 돌 개수 체크
				{
					if (board[i + k][j + k] == turn + 1)
					{
						cnt[2]++;
					}
				}
				if (IndexSafeCheck(i + k, j - k) == true)  // ↙ 방향으로 해당 턴 유저가 깔아놓은 돌 개수 체크
				{
					if (board[i + k][j - k] == turn + 1)
					{
						cnt[3]++;
					}
				}
			}
			for (int i = 0; i < 4; i++)
			{
				if (cnt[i] == 5) // 이 값이 하나라도 5가 되었다는 것은 오목이 되었음을 의미한다.
				{
					return true;
				}
			}
		}
	}
	return false;
}
void PlayGame() // 게임 플레이 함수
{
	int board[15][15] = { 0 }; // 보드판 
	int cursorX = 0, cursorY = 0; // 가상의 커서 위치
	int turn = 0;
	std::string system_message = "";
	while (1)
	{
		system("cls");
		PrintBoard(board, cursorX, cursorY, turn);
		std::cout << "Player " << turn + 1 << "P의 턴입니다." << std::endl;
		std::cout << "키보드(상,하,좌,우)키로 이동하고, Enter를 눌러 돌을 배치하여 주세요." << std::endl;
		std::cout << system_message;

		char input = _getch();

		if (input == 72) // 화살표 윗키
		{
			cursorX--;
			if (IndexSafeCheck(cursorX, cursorY) == false) cursorX++;
			system_message = "";
		}
		else if (input == 80) // 화살표 아랫키
		{
			cursorX++;
			if (IndexSafeCheck(cursorX, cursorY) == false) cursorX--;
			system_message = "";
		}
		else if (input == 75) // 화살표 왼쪽키
		{
			cursorY--;
			if (IndexSafeCheck(cursorX, cursorY) == false) cursorY++;
			system_message = "";
		}
		else if (input == 77) // 화살표 오른쪽키
		{
			cursorY++;
			if (IndexSafeCheck(cursorX, cursorY) == false) cursorY--;
			system_message = "";
		}
		else if (input == 13)
		{
			if (board[cursorX][cursorY] == 1 || board[cursorX][cursorY] == 2) // 이미 배치되어 있는 경우.
			{
				system_message = "이미 돌이 배치되어 있습니다. 다른 곳에 배치해주세요.\n";
			}
			else
			{
				board[cursorX][cursorY] = turn + 1;
				if (WinCheck(board, turn) == true) // 승부가 결정된 경우
				{
					system("cls");
					PrintBoard(board, cursorX, cursorY, turn);
					std::cout << "Player " << turn + 1 << "P의 승리로 게임을 종료합니다." << std::endl;
					std::cout << "아무 키를 입력하면 메뉴로 돌아갑니다." << std::endl;
					_getch();
					break;
				}
				else // 승부가 결정나지 않은 경우
				{
					turn = (turn + 1) % 2;
				}
			}
		}
	}
}
int main(void)
{
	int input;
	while (true)
	{
		input = menu();
		switch (input)
		{
			case 1:
				PlayGame();
				break;
			case 2:
				HowToPlay();
				break;
			case 3:
				system("cls");
				Credits();
				break;
			case 4:
				std::cout << "프로그램을 종료합니다.";
				return 0;
			default: 
				std::cout << "[Error] : Program Logic Error" << std::endl;
				break;
		}
	}
}

 

 

 

'Unity > Unity 초격차캠프' 카테고리의 다른 글

[20260427] C#의 기본적인 내용 정리  (0) 2026.04.27
[C#] 콘솔 지뢰찾기 만들기  (0) 2026.04.24
[20260406] C# - 반복문  (0) 2026.04.06
[20260405] C# - 조건문  (0) 2026.04.05
[20260404] C# - 연산자  (0) 2026.04.04
Comments