https://www.acmicpc.net/problem/1181

 

1181번: 단어 정렬

첫째 줄에 단어의 개수 N이 주어진다. (1 ≤ N ≤ 20,000) 둘째 줄부터 N개의 줄에 걸쳐 알파벳 소문자로 이루어진 단어가 한 줄에 하나씩 주어진다. 주어지는 문자열의 길이는 50을 넘지 않는다.

www.acmicpc.net

0. 개요

 

이번 문제의 조건은 다음과 같습니다.

1. 길이가 짧은 것 부터

2. 길이가 같으면 사전순으로

3. 여러번 입력된 단어는 한 번만 출력한다

 

길이가 같으면 사전순으로 정렬하는 것은 sort 함수가 처리 해 줄테니 1, 3번만 고려하면 되겠습니다.

 

1. 소스 코드

#include<iostream>
#include<algorithm>
#include<string>

using namespace std;

bool compare(string a, string b) {
	if (a.length() == b.length()) return a < b;
	else return a.length() < b.length();
}

int main(void) {
	string arr[200000];
	int n;
	cin >> n;
	for (int i = 0; i < n; i++)
		cin >> arr[i];

	sort(arr, arr + n, compare);

	for (int i = 0; i < n; i++){
		cout << arr[i] << "\n";
		while (!arr[i].compare(arr[i + 1])) 
			i++;
		
	}
	return 0;
}

 

1. compare 함수를 통하여 길이가 짧은 순으로, 길이가 같으면 사전순으로 먼저 정렬해줍니다.

2. 중복되는 문자열의 경우 다음번의 문자열과 비교하여 같다면 반복에 사용되는 변수 i를 1 증가하므로써 건너뛰는 효과를 주어 해결했습니다.

 

2. 마무리

정렬과 같이 저명한 파트는 이미 구현된 훌륭한 알고리즘이 너무 많습니다.

따라서 문제 풀이 시간도 자연스럽게 줄어들었습니다.

'백준 온라인 저지 - 단계별로 풀어보기 > 정렬' 카테고리의 다른 글

11650 - 좌표 정렬하기  (0) 2021.08.24
2750 - 수 정렬하기  (0) 2021.01.10

https://www.acmicpc.net/problem/11650

 

11650번: 좌표 정렬하기

첫째 줄에 점의 개수 N (1 ≤ N ≤ 100,000)이 주어진다. 둘째 줄부터 N개의 줄에는 i번점의 위치 xi와 yi가 주어진다. (-100,000 ≤ xi, yi ≤ 100,000) 좌표는 항상 정수이고, 위치가 같은 두 점은 없다.

www.acmicpc.net

0. algorithm 라이브러리 활용

다양한 정렬 알고리즘을 구현할 수 있는 것도 매우 중요한 일이지만, 

이미 구현된 훌륭한 라이브러리를 활용하는 것도 하나의 방법입니다.

 

C++의 algorithm 라이브러리에 있는 sort 함수를 활용하여 문제를 푸는 시간을 가져보았습니다.

 

일반적인 사용 방법은 다음과 같습니다.

 

sort(시작, 끝)

 

위와같이 활용할 경우 범위 내의 요소들이 오름차순으로 정렬됩니다.

다만 sort 함수의 세번째 옵션에서 직접 정렬 기준을 만들어 낼 수 있습니다.

 

bool compare(int a, int b){
	return a < b;
}

위가 default값으로 되어있는 sort 함수의 compare 함수입니다.

a < b 즉, 왼쪽값이 작은 값이 되도록 되어있습니다.

부호의 방향을 바꾼다면, 내림차순으로 구현이 되겠죠.

이를 활용하여 문제를 해결해보겠습니다.

 

1. 소스코드

#include<iostream>
#include<algorithm>

using namespace std;

typedef struct _pos {
	int x;
	int y;
}pos;

int n;
pos arr[100000];

bool compare(pos a, pos b) {
	if (a.x == b.x) return a.y < b.y;
	else return a.x < b.x;
}


int main(void) {
	cin >> n;

	for (int i = 0; i < n; i++) 
		cin >> arr[i].x >> arr[i].y;
	
	sort(arr, arr + n, compare); //세번째 옵션으로 함수 이름을 넣는다.

	for (int i = 0; i < n; i++)
		cout << arr[i].x << " " << arr[i].y << "\n";
	return 0;
}

 

sort 함수가 호출되는 부분을 보시게 되면 compare()함수를 호출하여 인자를 넣는 것이 아닌 함수명을 인수로 넣어주는 모습을 확인할 수 있습니다.

 

구현은 간단하게 x값이 같다면 y값을 기준으로 정렬하도록 구현했습니다.

 

2. 정리

다양한 정렬 기법을 구현할 수 있는 것도 중요합니다.

하지만 이미 만들어진 훌륭한 라이브러리를 활용할 줄 아는 것도 하나의 능력이라 생각이 됩니다.

'백준 온라인 저지 - 단계별로 풀어보기 > 정렬' 카테고리의 다른 글

1181 - 단어 정렬  (0) 2021.08.24
2750 - 수 정렬하기  (0) 2021.01.10

https://www.acmicpc.net/problem/11724

 

11724번: 연결 요소의 개수

첫째 줄에 정점의 개수 N과 간선의 개수 M이 주어진다. (1 ≤ N ≤ 1,000, 0 ≤ M ≤ N×(N-1)/2) 둘째 줄부터 M개의 줄에 간선의 양 끝점 u와 v가 주어진다. (1 ≤ u, v ≤ N, u ≠ v) 같은 간선은 한 번만 주

www.acmicpc.net

 

 

0. 연결 요소란 무엇인가?

 

그래프에서 연결요소란 위의 사진과 같은 것입니다.

영어로는 Connected Component로 표현합니다.

위와 같은 형태를 글로 표현하잠면 아래와 같습니다.

 

1. 연결 요소의 노드의 쌍은 경로로 서로 연결 되어 있다. (노드 A에서 노드 B로 가는 길이 존재한다.)

2. 각각의 연결요소에서 다른 연결 요소로 가는 경로는 없다.(노란색 집합에서 녹색 집합으로 연결된 경로가 없다.)

 

구하는 방법은 의외로 간단한데, DFS나 BFS를 진행하며 더이상 탐색을 할 수 없을 경우 하나의 연결 요소를 찾게 됩니다.

모든 정점에 대해서 DFS 혹은 BFS를 진행하게 된다면 모든 연결요소를 찾을 수 있게 됩니다.

 

1. 소스코드

#include<iostream>

using namespace std;

int matrix[1001][1001] = { 0, };
int visit[1001] = { 0, };
int n, m;
int cnt = 0;
int dfs(int cur) {
	if (visit[cur] == 1) return 0;
	else visit[cur] = 1;
	for (int i = 1; i <= n; i++) {
		//연결되었다면
		if (matrix[cur][i] == 1 && visit[i] != 1) {
			dfs(i);
		}
	}
	return 1;
}

int main(void) {
	cin >> n >> m;

	int a, b;
	for (int i = 0; i < m; i++) {
		cin >> a >> b;
		matrix[a][b] = 1;
		matrix[b][a] = 1;
	}

	for (int i = 1; i <= n; i++) {
		if(dfs(i)) cnt++;
	}
	cout << cnt << "\n";
	return 0;
}

 

2. 마무리

다양한 용어들이 많은 이론인 만큼 개념을 확실히 정립해야 하겠습니다.

'백준 온라인 저지 - 단계별로 풀어보기 > 그래프' 카테고리의 다른 글

1753. 최단경로  (0) 2022.07.02

0. 개요

https://www.acmicpc.net/problem/11729

 

11729번: 하노이 탑 이동 순서

세 개의 장대가 있고 첫 번째 장대에는 반경이 서로 다른 n개의 원판이 쌓여 있다. 각 원판은 반경이 큰 순서대로 쌓여있다. 이제 수도승들이 다음 규칙에 따라 첫 번째 장대에서 세 번째 장대로

www.acmicpc.net

재귀 문제 중에서 가장 이해가 안되었던 문제였습니다.

코드를 봐도 이해가 안갔던 문제인데 오늘 접근방법이 떠올랐고 문제를 드디어 해결했습니다.

저와 같이 코드를 봤어도 이해가 안갔던 분들을 위하여 상세하게 풀이 과정을 서술해보겠습니다.

 

 

1. 풀이과정

문제에서 제시하는 바는 위와 같습니다.

1번 기둥에 있는 원판을 모두 3번으로 이동 시키는 것입니다.

하노이의 탑 문제의 규칙은 모두 알고 있다고 가정을 하겠습니다.

 

 

1-1. 이동할 방향 선정?

(아래는 도출 과정입니다. 답을 원하시는 분은 1-2로 바로 넘어가 주세요.)

제가 맨처음 접근한 방식은 다음과 같습니다.

맨위의 녀석을 2번과 3번 둘중 하나에 선택하여 이동하고 그다음 원판을 같이 이동, 이동,.. 하여 맨 밑에까지,

맨위의 작은 원판부터 접근을 했습니다.

 

그러자 하나의 문제점에 도달했습니다.

원판의 갯수에 따라 제일 작은 원판이 2번에 가야 할 수도, 3번에 가야 할 수도 있는 상황이 되었습니다.

조건이 많아지는 것은 매우 귀찮아지는 일입니다.

또한 알고리즘은 똑같은 동작 원리에 의하여 이동하는 것이 일반적으로 편하겠죠.

 

그렇다면 어떻게 해야할까요?

여기서 떠올린 점이 다음과 같습니다.

 

1-2. 맨 아래서 부터 선택하여 올라가자.

가장 큰 원판을 3번으로 이동시켜 놓는다면 모든 일이 해결됩니다.

 

가장 큰 선반을 3번으로 옮긴다고 가정했을 때, 위의 나머지 4개를 옆으로 치워줘야 3번으로 이동 할 수 있겠죠.

 

그렇다면 파란색 구간을 옮기기 위해서는 파란색 구간의 가장 넓은 원판을 치워야합니다.

 

이제 감이 좀 오시는가요?

 

파란색 구간을 치우기 위해서는 녹색 구간을 치워야하고..

녹색 구간을 치우기 위해서는 노란색 구간을 치워야하고..

 

여기서 재귀적 특징이 나타납니다.

 

그럼 치우는 것 까지는 알겠습니다.

 

그렇다면 어디로 치워줘야 할까요?

위의 그림을 살펴보면 알 수 있습니다.

편의상 가장 큰 원판을 5부터 가장 작은 원판을 1이라 하겠습니다.

 

먼저 우리의 목표는 1번에 있는 5번 원판을 3번으로 이동시키는 것입니다.(빨간색 화살표)

그렇게 하기 위해서는 1~4번까지의 윗부분이 자리를 비켜줘야 합니다.

 

파란색 부분은 2번밖에 비켜줄 자리가 없는 것을 확인할 수 있습니다.

 

다만 4개의 원판을 한번에 움직이는 것은 불가능하죠.

재귀의 원리에 의해서 맨 꼭대기 부터 이동해줘야 합니다.

다만 빨간색이 3번으로 가야하기 때문에 빨간색을 3번으로 먼저 보내고,

4-3-2-1 순서로 2번과 3번을 교차해서 보내주면 위와 같이 됩니다.

 

즉, 우리가 처음 할 일은 먼저 각 원판이 어디로 자리를 비켜줄지를 먼저 정하는겁니다.

 

 

먼저 위와 같이 1번에 있는 4개의 원판이 자리를 비켜줬다고 생각해봅시다.

그러면 가장큰 5번 원반을 3번으로 이동하기만 하면 되겠죠.

 

 

그 다음 상황은 다음과 같습니다.

5번 원판은 3번으로 이동했습니다.

그럼 나머지 원판을 3번으로 이동시켜 줘야겠죠.

 

이것 또한 위와 같이 각각 이동할 수 있는 위치를 살펴보고 빈 자리 에다 이동해줍니다.

 

여기서 빈자리는 어떻게 찾을것인가에 대한 논의가 필요합니다!!

 

1번 2번 3번 세개의 위가 있습니다.

 

1+2+3을 한다면 6이라는 값이 나오겠죠?

 

만약 지금 2 -> 3으로 갔다고 생각한다면, 1번이 비어있습니다.

 

이는 다음과 같은 식으로 구할 수 있습니다.

 

6 - (2 + 3) = 1

 

즉, 6에서 현재 위치와 도착 지점을 더한 값을 뺀다면 다음에는 어디로 보내야 할지 알 수 있습니다.

 

위의 예제로 생각한다면,

주황색은 3번으로 갔습니다. 2->3입니다.

 

그렇다면 다음에 가야할 위치는 6-(2+3) = 1 번째 라고 알 수 있습니다.

 

나머지 부분에 대해서도  동일하게 적용하면 됩니다.

 

 

그렇다면 아래와 같이 이동하게 되고 끝나겠죠.

 

2. 소스코드

 

#include<iostream>

using namespace std;

int cnt = 0;

/* ans() 함수는 hanoi() 함수에서 출력만 제거한 함수입니다. */
/* 문제에는 총 이동 횟수를 먼저 출력한 뒤 이동한 위치를 출력하도록 되어 있어 이동 횟수를 먼저 구하는 함수입니다 */
/* 내부적인 설명은 아래 hanoi() 함수에 적도록 하겠습니다. */

void ans(int s, int e, int n) {
	if (n == 1) {
		cnt++;
		return;
	}
	int next = 6 - (s + e);
	ans(s, next, n - 1);
	cnt++;
	ans(next, e, n - 1);
}

/* 시작과 끝, 현재 몇번째 원판을 빼야하는지에 대한 값을 매개변수로 받아옵니다. */
void hanoi(int s, int e, int n) {
	if (n == 1) {
        /* 만약 빼야하는 원판이 1, 즉 제일 작을 때는 다른걸 굳이 안옮기고 바로 옮길 수 있습니다 */
		cout << s << " " << e<<"\n";
		return;
	}
    
    /* 6에서 시작+끝 값을 빼줌으로써 다음 위치를 구합니다. */
	int next = 6 - (s + e);
    
    /* 현재의 가장 큰 원판을 옮기기 위하여 그 위의 것들을 이동시켜 줍니다. */
	hanoi(s, next, n - 1);
    
    /* 가장 큰 원판을 이동 시킵니다 */
	cout << s << " " << e<<"\n";
    
    /* 가장 큰 원판을 이동시키기 위하여 비켜준 친구들을 다시 가장큰 원판 위로 옮깁니다 */
	hanoi(next, e, n - 1);
}

int main(void) {
	int N;

	cin >> N;
	
    /* ans를 통하여 cnt 값을 구하고 출력 후 hanoi() 함수를 실행시켜 이동 경로를 출력합니다. */
	ans(1, 3, N);
	cout << cnt << "\n";
	hanoi(1, 3, N);
}

 

제가 가장 어려웠던 것은

hanoi(s, next, n-1);

cout<<s<<" '<<e"\n";

hanoi(next, e, n-1); //이부분

이었습니다.

 

이부분은 아래와 같이 비켜준 원판들을 가장 큰 원판이 이동한 위치로 옮겨준다고 생각하시면 됩니다.

위가 이부분에 해당하는 코드이고, 

hanoi(s, next, n-1);

cout<<s<<" '<<e"\n";

 

위가 이부분에 해당하는 코드라고 보시면 됩니다.

hanoi(next, e, n-1); 

'백준 온라인 저지 - 단계별로 풀어보기 > 재귀' 카테고리의 다른 글

2447 - 별 찍기 - 10  (0) 2020.12.30
10870 - 피보나치 수 5  (0) 2020.12.29

0. 서론

우리는 아주 간단한 세가지 함수를 다루는 방법을 배웠습니다.

다만 간단하지만 아주 강력한 기능들입니다.

이를 통하여 간단한 게임을 한가지 만들어보고자 합니다.

 

1. 설계

규칙은 아주 간단합니다.

 

1. 랜덤한 위치에 별이 생성된다.

2. 캐릭터가 별에 닿으면 점수가 오르고 별이 사라진다.

3. 특정 점수까지 도달할때까지 1~2를 반복한다.

 

위의 규칙을 바탕으로 게임을 만들어 볼겁니다.

 

 

2. 구현

2-0. 완성된 코드

#include<iostream>
#include<conio.h>
#include<WIndows.h>

// 방향키
#define UP 72
#define LEFT 75
#define RIGHT 77
#define DOWN 80

//좌표 출력
#define POS 30

using namespace std;

void gotoxy(int x, int y) {
	COORD pos = { x, y };
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}

int player_x, player_y;
int object_x, object_y;
int score = 0;
int star = 0;

void make_star() {
	srand(time(NULL));
	object_x = rand() % 20;
	object_y = rand() % 20;
	gotoxy(object_x, object_y);
	printf("★");
}

void draw_player() {
	gotoxy(player_x, player_y);
	printf("@");
}


void move(int input) {
	switch (input) {
	case DOWN:
		player_y++;
		break;
	case UP:
		player_y--;
		break;
	case LEFT:
		player_x--;
		break;
	case RIGHT:
		player_x++;
	}
}

void check() {
	if (object_x - player_x > -2 && object_x - player_x < 2 && object_y == player_y) {
		score++;
		star = 0;
		gotoxy(object_x, object_y);
		printf("      ");
	}
}

int main(void) {
	player_x = player_y = 20;

	int c;
	while (1) {
		gotoxy(POS, POS);
		printf("player : (%d, %d)    \n", player_x, player_y);
		gotoxy(POS, POS-1);
		printf("star   : (%d, %d)    \n", object_x, object_y);
		gotoxy(POS, POS-2);
		printf("score  : %d", score);



		draw_player();
		/* 별이 없다면 */
		if (!star) {
			make_star();
			star = 1;
		}

		if (_kbhit()) {
			/* 224 키보드 방향키값 제거 */
			do { c = _getch(); } while (c != 224);
			/* 기존 도형을 삭제*/
			gotoxy(player_x, player_y);
			printf("  ");
			/* 이동 처리 */
			move(_getch());
		}

		check();

	}
}

먼저 소스 코드를 모두 공개했습니다.

여기서 하나하나 요소를 모두 살펴보면서 소스코드를 분석해보겠습니다.

 

 

2-1. 요소를 그리기

void gotoxy(int x, int y) {
	COORD pos = { x, y };
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}

앞서 구현한 위의 gotoxy함수를 활용합니다.

위를 통하여 해당하는 위치에 원하는 요소들을 그릴 수 있습니다.

이번에 필요한 요소는 별과 캐릭터를 그리는 것이 되겠습니다.
이것을 기본으로 하나하나 게임을 설계해 봅시다.

 

2-2. 캐릭터의 이동

while(1){
  ...
  draw_player();
  ...
  if (_kbhit()) {
              /* 224 키보드 방향키값 제거 */
              do { c = _getch(); } while (c != 224);
              /* 기존 도형을 삭제*/
              gotoxy(player_x, player_y);
              printf("  ");
              /* 이동 처리 */
              move(_getch());
          }
  ...
}

먼저 key를 어떻게 받느냐에 따른 이동처리를 해줘야 합니다.

위 키를 누르면 위로 이동하도록, 아래 키를 누르면 아래로 이동하도록 만들어 줘야겠습니다.

 

1. 먼저 방향키 값을 제거하고,

2. gotoxy를 통하여 플레이어의 위치로 이동한 뒤 공백을 출력하여 기존 플레이어 도형을 삭제합니다.

3. 이후 함수를 통하여 이동 처리를 한 뒤 반복문을 통하여 다시 플레이어를 그려줍니다.

 

void move(int input) {
	switch (input) {
	case DOWN:
		player_y++;
		break;
	case UP:
		player_y--;
		break;
	case LEFT:
		player_x--;
		break;
	case RIGHT:
		player_x++;
	}
}

플레이어의 이동 처리를 하는 move 함수는 아주 간단합니다.

입력된 값에 따라서 플레이어의 좌표 값만 수정해주는 방식입니다.

다만 주의하여야 할 점은 일반적인 좌표계와 다르게 아래쪽이 증가하는 방향입니다.

(옜날 컴퓨터가 위에서 아래로 출력하는 것을 본적 있다면 쉽게 이해하실 겁니다.)

 

화면좌표계는 왼쪽 상단 위가 (0, 0) 이고 아래로 내려갈 수록 y좌표가 증가합니다.

따라서 DOWN, UP에 있어서는 일반적인 좌표계와 반대로 DOWN에서는 증가, UP에서는 감소를 시켜줍니다.

 

void draw_player() {
	gotoxy(player_x, player_y);
	printf("@");
}

캐릭터가 이동하였다면 그려줘야겠죠?

그냥 현재 플레이어의 좌표로 가서 플레이어의 캐릭터 모양인 "@"을 출력해줄 뿐입니다.

 

2-3. 별의 생성

void make_star() {
	srand(time(NULL));
	object_x = rand() % 20;
	object_y = rand() % 20;
	gotoxy(object_x, object_y);
	printf("★");
}

별의 생성도 아주 간단합니다.

srand() 함수를 통하여 시간에 따른 시드를 부여한 뒤,

(0, 0) ~ (20, 20) 사이에 별의 값을 부여하고 그 위치에 별을 출력하면 끝입니다.

 

2-4. 점수 획득

void check() {
	if (object_x - player_x > -2 && object_x - player_x < 2 && object_y == player_y) {
		score++;
		star = 0;
		gotoxy(object_x, object_y);
		printf("      ");
	}
}

마지막으로 플레이어가 별에 다가가면 점수를 획득해야겠죠?

별의 크기가 화면에서 출력할 때 2칸정도 크기를 잡아먹습니다.

따라서 별의 x축과 플레이어의 y축의 차이가 2 이내일 경우,

별과 플레이어가 같은 높이에 있을 경우(y값이 같음)

점수를 올려줍니다.

star = 0;을 통하여 star 변수를 0으로 만들어 main함수에서 별을 다시 생성할 수 있도록 해줍니다.

또한 별의 위치로 이동하여 공백을 출력함으로 별을 지워줍니다.

 

2-5. 마무리

자 이제 필수 요소들을 모두 살펴봤으니 처음 봤던 소스코드를 다시 한번 살펴보도록 하겠습니다.

자잘한 것들은 주석을 달아놓을테니 확인해보세요.

#include<iostream>
#include<conio.h>
#include<WIndows.h>

// 방향키
#define UP 72
#define LEFT 75
#define RIGHT 77
#define DOWN 80

/* 아래 위치에 별과 플레이어, 점수 등을 표시하기 위하여 선언했습니다. */
#define POS 30

using namespace std;

/* 해당 좌표로 이동하는 함수입니다. */
void gotoxy(int x, int y) {
	COORD pos = { x, y };
	SetConsoleCursorPosition(GetStdHandle(STD_OUTPUT_HANDLE), pos);
}

/* 플레이어와 별의 위치를 기억하기 위하여 선언한 변수입니다.*/
int player_x, player_y;
int object_x, object_y;

/* 점수와 게임 화면에 별이 있는지 확인하기 위한 변수입니다. */
int score = 0;
int star = 0;

/* 위에서 다루었던 별을 만드는 함수입니다. */
void make_star() {
	srand(time(NULL));
	object_x = rand() % 20;
	object_y = rand() % 20;
	gotoxy(object_x, object_y);
	printf("★");
}

/* 위에서 다루었던 플레이어의 위치를 표시하는 함수입니다. */
void draw_player() {
	gotoxy(player_x, player_y);
	printf("@");
}

/* 위에서 다루었던 입력에 따른 플레이어 좌표 변환 함수입니다.*/
void move(int input) {
	switch (input) {
	case DOWN:
		player_y++;
		break;
	case UP:
		player_y--;
		break;
	case LEFT:
		player_x--;
		break;
	case RIGHT:
		player_x++;
	}
}

/* 위에서 다루었던 플레이어가 별을 획득했는지(별에 닿였는지) 여부를 확인하는 함수입니다. */
void check() {
	if (object_x - player_x > -2 && object_x - player_x < 2 && object_y == player_y) {
		score++;
		star = 0; /* 별 상태를 0으로 만들어 다시 별이 생성되게끔 합니다. */
		gotoxy(object_x, object_y);
		printf("      ");
	}
}

int main(void) {
    /* 초기 플레이어 위치는 20으로 설정했으나 얼마로 설정하든 상관없습니다. */
	player_x = player_y = 20;
	int c;
	while (1) {
        /* 플레이어와 별, 점수를 나타내기 위한 출력문입니다. */
        /* POS의 위치에서 차례대로 출력합니다. */
		gotoxy(POS, POS);
		printf("player : (%d, %d)    \n", player_x, player_y);
		gotoxy(POS, POS-1);
		printf("star   : (%d, %d)    \n", object_x, object_y);
		gotoxy(POS, POS-2);
		printf("score  : %d", score);
        
        /* 플레이어를 그립니다. */
		draw_player();
        
		/* 별을 획득하여 별이 없을 경우 별을 그립니다. */
		if (!star) {
			make_star();
			star = 1; // 별을 그리고 별 상태를 1로 바꿉니다.
		}
		
		if (_kbhit()) {
			/* 224 키보드 방향키값 제거 */
			do { c = _getch(); } while (c != 224);
			/* 기존 도형을 삭제*/
			gotoxy(player_x, player_y);
			printf("  ");
			/* 이동 처리 */
			move(_getch());
		}
		check();
	}
}

3. 실행 화면

점수도 잘 오르고 플레이어와 별의 위치 또한 잘 나타내고 있는 모습이 보입니다.

 

다만 아쉬운 점은 소스코드에 종료 버튼을 넣지 않아서 컨트롤+C를 통한 콘솔 종료를 해야한다는 점이네요 ㅜ

 

커서도 많이 신경쓰일 수 있구요!

 

이 부분에 대해서는 각자 숙제로 맡기겠습니다~

0. 서론

컴퓨터가 발전하고 여러가지 입출력 장치가 생겨났습니다.

오늘 살펴볼 것은 강력한 입력 장치 중 하나인 키보드를 활용한 방법입니다.

 

1. getch()를 통한 키값 확인

 

키보드 입력 값 출력

위의 사진 처럼 키보드로 입력할 경우 키보드에 해당하는 문자를 출력하고 입력값이 얼마인지 출력하도록 했습니다.

 

ESC키를 누르게 되면 프로그램은 종료를 합니다.

 

다만 재미있는 점은 아래와 같습니다.

 

특수한 입력에 대한 출력

먼저, 위의 출력을 먼저 살펴보겠습니다.

? 224 이후 H: 72 와 같은 패턴으로 4가지가 나옵니다.

이는 방향키에 대한 출력입니다.

 

방향키를 비롯한 몇몇 입력들은 1바이트로 처리되는 아스키코드와 다르게 확장 아스키 코드를 사용하기 때문에 저렇게 두번의 변환을 통하여 표현됩니다.

 

아래의 : 0 이후 ; : 59와 같은 패턴은 F1~F5번을 입력했을 때 나타나는 결과입니다.

 

이를 통하여 우리는 일반적인 키보드 입력을 처리하는 방법과 특수한 키를 처리하는 방법 두가지를 익혔습니다.

 

위의 사진에서 사용한 코드는 아래와 같습니다.

#include<iostream>
#include<conio.h>

using namespace std;

/* 키입력 테스트 */
int main(void) {
	int key;
	
	while (1) {
		key = _getch();
		cout << (char)key << ": " << key << endl;
		if (key == 27) {
			cout << "ESC";
			break;
		}
	}
}

2. kbhit()을 통한 입력 확인

kbhit()은 키 입력이 있는지 없는지 확인하는 함수입니다.

getch()는 키 입력이 생길 때 까지 기다리는 반면,

kbhit()은 입력버퍼를 확인하여 당장에 있으면 true, 없으면 false를 반환하는 함수입니다.

즉, 기다림이 없습니다.

 

쉽게 표현하자면 getch()은 내가 무언가를 입력해야 다음으로 진행하기 때문에 턴제 게임,

kbhit()은 입력이 있으면 하고 없으면 안하는 실시간 게임과 같은 효과를 줍니다.

 

kbhit()이 얼마나 중요성이 이제 실감이 나실까요?

다음의 캡처를 살펴보겠습니다.

 

getch()

위의 코드에서 while문이 시작할 때 "while문 시작.."이라는 문자열만 출력하도록 추가했습니다.

내가 무언가 입력을 해야만 다음 단계로 처리가 이루어집니다.

 

여기서 kbhit()을 이용하여 코드를 수정해보겠습니다.

#include<iostream>
#include<Windows.h> // Sleep() 함수를 사용하기 위하여 추가
#include<conio.h>

using namespace std;

/* kbhit을 추가한 키입력 테스트 */
int main(void) {
	int key;
	
	while (1) {
		if (_kbhit()) {
			key = _getch();
			cout << (char)key << ": " << key << endl;
			if (key == 27) {
				cout << "ESC";
				break;
			}
		}
		else {
			cout << "입력대기...\n";
			Sleep(100); //Sleep을 넣지 않으면 너무 빨라 확인하기 힘들다.
		}
	}
}

 

while문 아래에 if문을 추가하여 키보드 입력이 있으면 이전과 같이 입력 값을 출력하도록 하고,

만약 입력이 없다면 "입력대기..."라는 문자열을 출력하도록 만들었습니다.

 

실행해본다면 다음과 같이 나타납니다.

kbhit() 이 없었을 경우에는 입력을 하지 않으면 함수의 흐름 또한 멈춥니다.

kbhit()을 넣었을 때는 입력을 하지 않아도 프로그램은 다른 작업을 계속 수행하며 함수가 흘러갑니다.

 

3. 정리

우리는 getch()를 통하여 키보드 입력을 처리하는 방법과,

kbhit()을 통하여 실시간 상호작용을 할 수 있는 기반을 다졌습니다!

 

참고로 kbhit은 KeyBoard hit의 줄임말이라고 합니다.

함수를 기억할 때 도움이 되셨으면 좋겠네요.

 

 

 

 

+ Recent posts