본문 바로가기
<TOY PROJECT>/CPP_CONSOLE

0. [CPP]피라미드 생성 어플리케이션

by CodeGrimie 2021. 1. 11.

https://github.com/gyunam-park/CPP_TOY_PROJECT

 

gyunam-park/CPP_TOY_PROJECT

This is where I am collecting toy projects to review what I learned while studying CPP. - gyunam-park/CPP_TOY_PROJECT

github.com

▼ 실행화면

순환문, 조건문, 입출력 그리고... 클래스..?

CPP에서 순환 문과 조건문, 입출력을 배우고 피라미드를 만드는 과제를 진행했다.

 

▼ 과제로 내어진 이미지

처음에는 교수님이 수열을 사용해보는 것도 좋다고 해서 '피보나치수열'인가?! 하고 짜 봤는데 예시 이미지는 '*'이 하나씩 늘어나는 거라서 결괏값이 달랐다..

 

아무튼 처음에는 for문 두 개로 다 구현을 했다. 그냥 그대로 과제를 마무리하고 정리를 할까 하다가..  그래도 교수님의 미션인데 놓칠 수 없지 싶어서 for문 하나로 만들어보기로 했다.

 

▼ 그래서 탄생한 내 인생 최고의 괴랄한 코드

int currStar = 0;
for (int col = 0; col < colMax;)
{
    if (currStar < (col + 1))
    {
        printf("*");
        currStar++;
        continue;
    }

    if (currStar == (col + 1))
    {
        printf("\n");
        col++;
        currStar = 0;
    }
}

한눈에 봐도 이게 무슨 코드인지 잘 안 들어온다. (홀리 지저스 크리스피 치킨)

교수님이 문제를 내주신 의도와 이건 전혀 다를 것이다.

 

for문 두 개 쓰는 예제가 단순히 발상과 코딩이 쉬워서 많은 게 아니라 일단 한 번에 보기 좋아서 인 것 같다.

변수 명도 내 나름 내가 이해하기 쉬운 방법대로 쓰긴 했는데 나중에 갈아엎을 때 한눈에 들어오지 않는 걸 발견했다.

괜스레 굳이 다른 방법으로 짜 보고 싶어서 끙끙댄 게 아까워서 다른 형태의 피라미드들도 구현했다.

 

▼ 의외로 추가적인 구현은 어렵지 않았다.

int currWhite = colMax;
int currStar = 0;
for (int col = 0; col < colMax;)
{
    if (currWhite > 0)
    {
        printf(" ");
        currWhite--;
        continue;
    }

    if (currStar < (col + 1))
    {
        printf("*");
        printf(" ");
        currStar++;
        continue;
    }

    if (currStar == (col + 1))
    {
        printf("\n");
        col++;
        currStar = 0;
        currWhite = colMax - col;
    }
}

 

그렇게 다시 한번 코드를 다 완료하고 진짜 마무리하려다가 'CPP로 객체지향적으로 짜면 어떨까?'란 생각이 들었다.

난 아직도 C의 절차 지향적인 코드가 더 익숙하다. 왠지 이번에 객체지향적으로 짜보는 삽질을 해봐야 이후의 과제들도 객체지향적으로 짜 볼 생각이 계속 들 꺼 같아서 구글만 믿고 다시 처음부터 작성을 시작했다.

 

▼ 그렇게 인터넷 보고 짜기 시작한 App 클래스

class App
{
public:
	AppState    appState;
	i32         pyramidType;
	i32         collumNum;
	i32         inputNum;
	bool        isAppRunning;

public:
	App();
	virtual ~App();

	void Init();
	void Clear();
	void Draw();
	void Update();

	void GetPyramidType();
	void GetCollumNumber();
	void GetNextChoice();

	void Input(i32& contents);
};

처음 보는 변 수형 일 i32는 내가 common.h 파일에 재정의한 변 수형이다.

App의 구조는 Init, Clear, Draw, Update로 가지고 가다가 나중에 좀 더 프로그램답게 만들어보려고 입력 부분을 넣다 보니 너저분 해졌다.

 

좀 더 객체와 객체끼리 데이터를 주고받는 걸 이해해야 할 필요가 있다고 느꼈다.

클래스를 처음 써보다 보니 무작정 매번 새 클래스를 만들어서 사용했다.

 

▼ 인간의 욕심은 끝이 없고 내 코드는 끝났다(?).

void	App::Draw()
{
	switch (pyramidType)
	{
	case HALF:
	{
		HalfPyramid half(collumNum);
		half.Draw();
	}
	break;
	case INVERTED_HALF:
	{
		InvertedHalfPyramid	iHalf(collumNum);
		iHalf.Draw();
	}
	break;
	case HOLLOW_INVERTD_HALF:
	{
		HollowInvertedHalfPyramid iHollowHalf(collumNum);
		iHollowHalf.Draw();
	}
	break;
	case FULL:
	{
		FullPyramid full(collumNum);
		full.Draw();
	}
	break;
	case INVERTED_FULL:
	{
		InvertedFullPyramid iFull(collumNum);
		iFull.Draw();
	}
	break;
	case HOLLOW_FULL:
	{
		HollowFullPyramid hollowFull(collumNum);
		hollowFull.Draw();
	}
	break;
	default:
		break;
	}
}

만약 게임이었다면 정말 비효율적이지 않았을까? 란 생각이 든다.

어차피 안에서 새로 메모리를 할당하는 게 없긴 해서 큰 문제는 안 될 테지만 내가 해보고 싶었던 건 Pyramid 객체를 하나 만들어서 가지고 있다가 필요에 따라서 교체하는 걸 생각했었다.

 

근데 그게 잘 안 돼서(내가 몰라서) 결국 맨땅 헤딩식으로 작성해야 했다.

C도 그 나름의 콤팩트함이 좋은데 CPP도 클래스를 사용하다 보니 재밌는 구석이 있다.

 

▼ State Machine 이란 걸 공부해야 할 필요를 느꼈다.

void	App::Update()
{
	while (isAppRunning == true)
	{
		switch (appState)
		{
		case AppState::IDLE:
			appState = AppState::GET_TYPE;
			break;
		case AppState::GET_TYPE:
			GetPyramidType();
			break;
		case AppState::GET_NUM:
			GetCollumNumber();
			break;
		case AppState::RESULT:
			Draw();
			GetNextChoice();
			break;
		default:
			break;
		}
		system("@cls||clear");
	}
}

일단 나름 프로그램의 상태를 관리하려고 switch문을 사용했다.

나중에 수업이 끝나고 뒤적여보니 FSM과 같이 상태를 관리하는 기법들이 있었는데 실제 게임 개발할 때에도 큰 도움이 될 것 같아서 주말에 시간 내서 살펴봐야겠다.

'<TOY PROJECT> > CPP_CONSOLE' 카테고리의 다른 글

2. 간단한 탑뷰 게임  (0) 2021.01.27
1. [CPP]특수문자 타일 맵 생성기  (0) 2021.01.13

댓글