C++ 방향 배열, 포인터, 구조체 활용
방향을 표현하기 위한 코딩 방법 (Direct 기법)
방향 배열의 활용
방향 배열(Direction Array)은 게임 개발과 알고리즘에서 인접한 칸을 탐색할 때 사용하는 핵심 기법입니다.
왜 방향 배열을 사용할까?
만약 방향 배열 없이 상하좌우를 체크하려면 다음과 같이 코드를 작성해야 합니다:
// 방향 배열 없이 작성한 코드 (비효율적)
if (y-1 >= 0)
sum += arr[y-1][x]; // 상
if (y+1 < 3)
sum += arr[y+1][x]; // 하
if (x-1 >= 0)
sum += arr[y][x-1]; // 좌
if (x+1 < 3) s
um += arr[y][x+1]; // 우
C++
복사
이 방식의 문제점:
•
코드가 길고 반복적입니다
•
8방향으로 확장하면 코드가 8배로 늘어납니다
•
방향을 추가/수정할 때 여러 곳을 고쳐야 합니다
•
유지보수가 어렵습니다
방향 배열을 사용하면:
// 방향 배열을 사용한 코드 (효율적)
int direct[4][2] =
{
{-1,0},
{1,0},
{0,-1},
{0,1}
};
for (int i = 0; i < 4; i++)
{
int newY = y + direct[i][0];
int newX = x + direct[i][1];
if (newY >= 0 && newY < 3 && newX >= 0 && newX < 3)
{
sum += arr[newY][newX];
}
}
C++
복사
이 방식의 장점:
•
반복문으로 간결하게 처리 가능
•
4방향 → 8방향 확장이 쉽습니다 (배열만 수정)
•
코드 재사용성이 높습니다
•
방향을 데이터로 관리할 수 있습니다
실제 게임에서의 활용 사례:
•
스타듀밸리: 낚시할 때 주변 8칸 탐색, 호미로 땅 팔 때 앞 3칸 탐색
•
젤다의 전설: 3D 게임이지만 내부적으로는 2D 그리드 기반 탐색 (NavMesh)
•
포켓몬스터: 플레이어 이동, NPC 시야 범위, 풀숲 인카운터 체크
•
체스/장기: 말의 이동 가능한 위치 계산
•
지뢰찾기: 주변 8칸의 지뢰 개수 세기
Direct 방향 배열 구현
Direct 기법이란?
Direct(방향) 기법은 좌표의 변화량을 미리 배열에 저장해두고, 반복문으로 인접 칸을 탐색하는 방법입니다.
핵심 개념:
•
기준점: 현재 위치 (y, x)
•
방향 벡터: 각 방향으로의 이동량 (dy, dx)
•
새 좌표: 기준점 + 방향 벡터
#include <iostream>
using namespace std;
int main()
{
int arr[3][3] =
{
1, 2, 3,
4, 5, 6,
7, 8, 9
};
// 기준점을 (0,0) 으로 뒀을 때 좌표값의 배열
int direct[4][2] =
{
-1, 0, // 상
1, 0, // 하
0, -1, // 좌
0, 1 // 우
};
int y = 1;
int x = 1;
// 입력받을 경우
// int x, y;
// cin >> y >> x;
int sum = 0;
// 4X2 배열 direct 에서
for (size_t i = 0; i < 4; i++)
{
int newY = y + direct[i][0];
int newX = x + direct[i][1];
// ex) newX = 기준의 좌표값에 direct 배열의 x 좌표값을 더한다.
// ex) newY = 1 + direct[0][0] (= 1) => 0
// ex) newX = 1 + direct[0][1] (=0) -> 1
// ex) newY, newX = 0, 1
// ex) arr[0][1] = 2
if (newX >= 0 && newX <= 2 && newY >= 0 && newY <= 2)
{
sum += arr[newY][newX];
}
}
cout << sum << endl;
return 0;
}
C++
복사
동작 원리:
1.
기준점 설정: (1, 1) 위치에서 시작
2.
방향 배열: 상(-1,0), 하(1,0), 좌(0,-1), 우(0,1)
3.
좌표 계산: 기준점 + 방향 벡터 = 새로운 좌표
4.
경계 검사: 배열 범위를 벗어나지 않는지 확인
5.
값 누적: 유효한 좌표의 값들을 합산
실행 결과: 기준점 (1,1)의 상하좌우 값들의 합 = 2 + 8 + 4 + 6 = 20
8방향 탐색 확장
4방향(상하좌우)에서 8방향(대각선 포함)으로 확장할 수 있습니다. 이는 체스의 킹, 지뢰찾기, RPG의 범위 공격 등에 활용됩니다.
int direct8[8][2] =
{
-1, -1, // 좌상
-1, 0, // 상
-1, 1, // 우상
0, -1, // 좌
0, 1, // 우
1, -1, // 좌하
1, 0, // 하
1, 1 // 우하
};
C++
복사
2중 포인터 변수
포인터의 포인터 개념
2중 포인터는 포인터 변수의 주소를 저장하는 포인터입니다. 메모리 구조를 이해하는 것이 핵심입니다.
왜 2중 포인터가 필요할까?
1.
함수에서 포인터 자체를 변경하고 싶을 때
2.
2차원 배열을 동적 할당할 때
3.
문자열 배열(char)을 다룰 때
4.
연결 리스트, 트리 같은 자료구조에서 노드의 포인터를 수정할 때
1중 포인터 vs 2중 포인터 비교:
// 1중 포인터: 값을 변경할 수 있음
void changeValue(int* p) {
*p = 200; // OK: 원본 값 변경 가능
}
// 1중 포인터: 포인터 자체는 변경 불가
void changePointer(int* p, int* newTarget) {
p = newTarget; // 실패: 복사본만 변경됨
}
// 2중 포인터: 포인터 자체를 변경할 수 있음
void changePointer(int** pp, int* newTarget) {
*pp = newTarget; // OK: 원본 포인터 변경 가능
}
C++
복사
메모리 구조 시각화
메모리 주소 값 변수명
0x12 100 num (변수)
0x25 0x12 ptr (포인터)
0x61 0x25 pptr (포인터의 포인터)
Plain Text
복사
2중 포인터 기본 사용법
메모리 구조를 단계별로 이해하기:
#include <iostream>
using namespace std;
// 포인터 변수 = 주소값(주소 타입)을 저장하는 변수 타입
// int* P = &a; (a의 주소값을 포인터 변수의 값으로 가진다)
// int 변수 = 정수형 숫자를 저장하는 변수 타입
// char 변수 = 문자를 글자를 저장하는 변수 타입
// 2중 포인터 변수
// (int a = 100) == (int* P = &a) == (int** PP = &P) == (&P = 0X17)
int main()
{
int a = 100;
int* p = &a;
int** pp = &p;
*(*pp) = 200;
// 결과 a의 값이 변한다.
return 0;
}
C++
복사
2중 포인터 사용 과정:
1.
int a = 100: 정수 변수 a에 100 저장
2.
int* p = &a: 포인터 p가 a의 주소 저장
3.
int** pp = &p: 포인터의 포인터 pp가 p의 주소 저장
4.
(*pp) = 200: pp를 통해 a의 값을 200으로 변경
접근 과정:
•
pp: p의 주소를 가리킴 (0x25)
•
*pp: p가 저장한 값, 즉 a의 주소 (0x12)
•
**pp: a가 저장한 값 (100)
•
**pp = 200: a의 값을 200으로 변경
2중 포인터의 실용적 활용
실전 예제: 함수에서 포인터 변경하기
#include <iostream>
using namespace std;
void ChangeValue(int** ptr)
{
**ptr = 500; // 원본 값 변경
}
void ChangePointer(int** ptr, int* newTarget)
{
*ptr = newTarget; // 포인터가 가리키는 대상 변경
}
int main()
{
int a = 100;
int b = 300;
int* p = &a;
cout << "초기값: *p = " << *p << endl; // 100
ChangeValue(&p);
cout << "값 변경 후: *p = " << *p << endl; // 500
ChangePointer(&p, &b);
cout << "포인터 변경 후: *p = " << *p << endl; // 300
return 0;
}
C++
복사
구조체, 클래스를 이용한 배열
구조체를 이용한 데이터 관리
구조체 배열은 관련된 데이터를 묶어서 관리할 때 유용합니다.
언제 사용할까?
•
게임에서 여러 플레이어의 정보 관리
•
학생 성적 관리 시스템
•
좌표와 속성을 함께 저장 (예: 위치 + 체력)
더 실용적인 예제:
struct Player
{
int x, y; // 위치
int hp; // 체력
int attack; // 공격력
char name[20]; // 이름
};
Player players[4]; // 4명의 플레이어
// 첫 번째 플레이어 초기화
players[0].x = 5;
players[0].y = 5;
players[0].hp = 100;
players[0].attack = 20;
// 모든 플레이어 정보 출력
for (int i = 0; i < 4; i++)
{
cout << players[i].name << ": ("
<< players[i].x << ", " << players[i].y << ")\n";
}
C++
복사
#include <iostream>
using namespace std;
// 구조체, 클래스를 이용한 배열
struct ABC
{
public:
// 생성자 (해당사항 없음)
// 소멸자 (해당사항 없음)
// 멤버 함수들 (해당사항 없음)
// 멤버 변수들
int mA;
int mB;
};
int main()
{
ABC arr[3];
arr[0].mA = 100;
arr[0].mB = 200;
cin >> arr[0].mA >> arr[0].mB;
cout << arr[0].mA << arr[0].mB;
return 0;
}
C++
복사
패턴찾기 → 2중 for문에서 패턴찾기
2차원 배열에서 패턴 매칭
2차원 배열에서 특정 패턴을 찾는 알고리즘입니다.
패턴 매칭이란?
큰 배열(map) 안에서 작은 배열(pattern)과 일치하는 부분을 찾는 것입니다.
활용 분야:
•
이미지 처리: 특정 모양 찾기
•
게임: 특정 배치 패턴 인식 (예: 3-match 퍼즐)
•
보안: 악성 코드 시그니처 탐지
•
DNA 분석: 특정 염기서열 찾기
시간 복잡도 분석:
•
map 크기: N × M
•
pattern 크기: P × Q
•
시간 복잡도: O((N-P+1) × (M-Q+1) × P × Q)
•
최악의 경우: O(N × M × P × Q)
최적화 방법:
•
KMP 알고리즘 응용 (1차원 문자열 매칭)
•
해싱 기법 사용
•
첫 번째 요소로 빠른 필터링
#include <iostream>
using namespace std;
// 패턴찾기 -> 2중 For문에서 패턴찾기
int map[5][5] =
{
1, 3, 5, 1, 5,
3, 7, 9, 5, 8,
4, 5, 3, 9, 7,
2, 7, 6, 1, 9,
1, 6, 2, 3, 8
};
int pattern[2][2] =
{
3, 5,
7, 9
};
int IsPattern(int dy, int dx)
{
for (size_t y = 0; y < 2; y++)
{
for (size_t x = 0; x < 2; x++)
{
if (map[dy + y][dx + x] != pattern[y][x])
{
return 0;
}
}
}
return 1;
}
int main()
{
for (size_t y = 0; y < 4; y++)
{
for (size_t x = 0; x < 4; x++)
{
int result = IsPattern(y, x);
if (result == 1)
{
cout << "존재" << endl;
return 0;
}
}
}
cout << "노존재" << endl;
return 0;
}
C++
복사
패턴 매칭 과정 상세 설명:
1.
시작 위치 설정: (dy, dx)에서 패턴 검사 시작
•
map의 (0,0)부터 (3,3)까지 순회 (5x5 배열에서 2x2 패턴)
•
경계를 벗어나지 않도록 y < 4, x < 4까지만 검사
1.
패턴 크기만큼 반복: 2x2 패턴이면 4번 비교
•
pattern[0][0]과 map[dy+0][dx+0] 비교
•
pattern[0][1]과 map[dy+0][dx+1] 비교
•
pattern[1][0]과 map[dy+1][dx+0] 비교
•
pattern[1][1]과 map[dy+1][dx+1] 비교
1.
일치 여부 확인: 모든 위치의 값이 일치하는지 검사
•
하나라도 다르면 즉시 0 반환 (조기 종료)
•
모두 일치하면 1 반환
1.
결과 처리: 첫 번째 발견 시 즉시 종료
•
모든 위치를 찾고 싶다면 continue 사용
코드 개선 팁:
// 모든 패턴 위치 찾기
vector<pair<int,int>> found;
for (int y = 0; y < 4; y++) {
for (int x = 0; x < 4; x++) {
if (IsPattern(y, x)) {
found.push_back({y, x});
}
}
}
C++
복사
문자 패턴 찾기
#include <iostream>
using namespace std;
char map[3][4] =
{
{'A', 'B', 'G', 'K'},
{'T', 'T', 'A', 'B'},
{'A', 'C', 'C', 'D'}
};
char pattern[2][2];
char IsPattern(char y, char x)
{
for (size_t y = 0; y < 2; y++)
{
for (size_t x = 0; x < 2; x++)
{
if (map[y][x] != pattern[y][x])
{
return 0;
}
}
}
return 1;
}
char main()
{
for (size_t i = 0; i < 2; i++)
{
cin >> pattern[i][0];
cin >> pattern[i][1];
}
for (size_t i = 0; i < 2; i++)
{
for (size_t j = 0; j < 3; j++)
{
int result = IsPattern(i, j);
if (result == 1)
{
cout << "발견\n";
}
else
{
cout << "미발견\n";
}
}
}
return 0;
}
C++
복사
실무에서의 활용
게임 개발에서의 방향 배열
RPG 게임에서의 활용:
enum Direction
{
UP = 0,
DOWN = 1,
LEFT = 2,
RIGHT = 3
};
class Player
{
public:
Player(int y, int x) : mY(y), mX(x) {}
void Move(Direction dir)
{
int newY = mY + mDirections[dir][0];
int newX = mX + mDirections[dir][1];
if (IsValidPosition(newY, newX))
{
mY = newY;
mX = newX;
}
}
bool IsValidPosition(int y, int x)
{
return (y >= 0 && y < MAP_HEIGHT && x >= 0 && x < MAP_WIDTH);
}
private:
static const int mDirections[4][2];
int mY, mX;
static const int MAP_HEIGHT = 10;
static const int MAP_WIDTH = 10;
};
const int Player::mDirections[4][2] =
{
{-1, 0}, // UP
{ 1, 0}, // DOWN
{ 0, -1}, // LEFT
{ 0, 1} // RIGHT
};
C++
복사
패턴 매칭의 실무 활용
이미지 처리에서의 템플릿 매칭:
•
특정 패턴의 객체 찾기
•
얼굴 인식, 문자 인식
•
게임에서 아이템 배치 패턴 인식
데이터 마이닝:
•
시계열 데이터에서 특정 패턴 찾기
•
로그 분석에서 이상 패턴 탐지
•
바이오인포매틱스에서 DNA 서열 분석
메모리 효율성 고려사항
2중 포인터 사용 시 주의점:
1.
메모리 누수 방지: 동적 할당 시 반드시 해제
// 2차원 배열 동적 할당
int** arr = new int*[rows];
for (int i = 0; i < rows; i++) {
arr[i] = new int[cols];
}
// 사용 후 반드시 해제
for (int i = 0; i < rows; i++) {
delete[] arr[i]; // 각 행 해제
}
delete[] arr; // 포인터 배열 해제
C++
복사
1.
NULL 포인터 체크: 포인터 사용 전 유효성 검사
int** pp = nullptr;
if (pp != nullptr && *pp != nullptr) {
cout << **pp << endl;
}
C++
복사
1.
깊은 복사 vs 얕은 복사: 포인터 복사 시 주의
// 얕은 복사 (주소만 복사)
int* p1 = new int(100);
int* p2 = p1; // 같은 메모리를 가리킴
// 깊은 복사 (값을 복사)
int* p1 = new int(100);
int* p2 = new int(*p1); // 새로운 메모리에 값 복사
C++
복사
핵심 정리
Direct 기법의 핵심
•
방향을 데이터로 관리하면 코드가 간결하고 확장성이 높아집니다
•
4방향, 8방향, 체스 말 이동 등 다양하게 응용 가능
•
게임 개발, 알고리즘 문제 풀이의 필수 기법
2중 포인터의 핵심
•
포인터 자체를 변경하고 싶을 때 사용
•
2차원 배열 동적 할당, 문자열 배열에 필수
•
메모리 관리를 정확히 이해해야 함
패턴 매칭의 핵심
•
2중 반복문으로 모든 시작 위치 탐색
•
조기 종료로 효율성 향상
•
이미지 처리, 게임 로직의 기초 알고리즘
방향 배열과 패턴 매칭은 게임 개발, 이미지 처리, 데이터 분석 등 다양한 분야에서 핵심적으로 활용되는 기법입니다. 특히 2D 게임의 맵 탐색이나 이미지 인식 알고리즘에서 필수적인 도구로 사용됩니다.
“강의는 많은데, 왜 나는 아직도 코드를 못 짤까?”
혼자 공부하다 보면 누구나 이런 고민을 하게 됩니다.
•
강의는 다 들었지만 막상 손이 안 움직이고,
•
복습을 하려 해도 무엇을 다시 봐야 할지 모르겠고,
•
질문할 곳도 없고,
•
유튜브는 결국 정답을 따라 치는 것밖에 안 되는 것 같고.
문제는 ‘연습’이 빠졌기 때문입니다.
단순히 강의를 듣는 것만으로는 실력이 늘지 않습니다.
실제 문제를 풀고, 고민하고, 직접 구현해보는 시간이 반드시 필요합니다.
그래서, 얌얌코딩 코칭은 다릅니다.
그냥 가르치지 않습니다.
스스로 설계하고, 코딩할 수 있게 만듭니다.
얌얌코딩 코칭에서는 단순한 예제가 아닌,
스스로 문제를 분석하고 구현해야 하는 연습문제를 제공합니다.
이 연습문제들은 다음과 같은 역량을 키우기 위해 설계되어 있습니다:
•
문제를 스스로 쪼개고 설계하는 힘
•
다양한 조건을 만족시키는 실제 구현 능력
•
기능 단위가 아닌, 프로그램 단위로 사고하는 습관
•
마침내 자신의 힘으로 코드를 끝까지 작성하는 경험
지금 필요한 건 더 많은 강의가 아닙니다.
코드를 스스로 완성해 나가는 훈련,
그것이 지금 실력을 끌어올릴 가장 현실적인 방법입니다.




