동적 크기의 무제한 "미로"에 대한 데이터 구조를 어떻게 구축해야합니까?


43

실제로 "미로"가 올바른 용어인지 확실하지 않습니다. 기본적으로 사용자 Room는 4 개의 도어 (N, S, E 및 W)가 있는 싱글에서 시작 합니다. 그들은 어느 방향 으로든 갈 수 있으며, 그 다음 각 방에는 다른 방으로가는 1-4 개의 출입구가있는 다른 방이 있습니다.

"미로"는 크기가 무제한이며 방을 움직일 때 성장해야합니다. Rooms사용 가능한 수는 제한되어 있지만 사용 가능한 수는 동적이며 변경 될 수 있습니다.

내 문제는이 유형의 패턴에 가장 적합한 데이터 구조를 확신하지 못한다는 것입니다.

나는 단지 [X] [X] 배열의 Room객체를 사용하는 것에 대해 처음으로 생각 했지만, 어떤 방향 으로든 성장해야하므로 "방문"된 방만 건축해야한다는 점을 피하고 싶습니다.

다른 생각은 각 Room클래스 Room에 N, S, E 및 W에 대한 4 개의 연결된 속성을 포함 시키고 이전에 링크하는 것이 Room었지만 사용자가 방에 들어 갔는지 식별하는 방법을 모른다는 문제 이미 "건축 된"인접 방이 있습니다

예를 들어

--- --- ----------
| | | |
   시작 5 4
| | | |
--- --- --- ---
--- --- ---------- --- ---
| | | | | |
| 1 2 3
| | | | | |
--- --- --- --- ----------

사용자가 시작> 1> 2> 3> 4> 5에서 이동하면 Room# 5는 W에 시작 룸이 포함되어 있음을 알아야하고, S는 룸 # 2이며이 경우 사용할 수 없으며 N은 다음 중 하나 일 수 있습니다. 새로운 Room또는 벽 (아무것도).

어쩌면 배열과 연결된 방을 혼합해야하거나 아마도 이것을 잘못보고있을 것입니다.

이 유형의 "미로"에 대한 데이터 구조를 구축하는 더 좋은 방법이 있습니까? 아니면 현재의 사고 과정을 제대로 따르고 있으며 몇 가지 정보가 누락 되었습니까?

(관심이있는 경우 프로젝트는 Munchkin Quest 와 매우 유사한 게임입니다 )


방이 어떤 방향 으로든 커질 것이기 때문에 어떤 종류의 배열도 효과가 없다고 생각합니다 ... [0,0]에서 시작하여 왼쪽으로 이동하면? [-1, 0]을 시도합니다.
Paul

@Paul 행 / 열을 추가하고 모든 배열 데이터를 이동 한 다음 모든 플레이어 위치를 이동하고 새 맵 배열과 일치시킵니다. 얼마나 많이 이동해야하는지에 따라 느리고 어려울 수 있지만 가능합니다. 여전히 Bubblewrap의 대답이 훨씬 좋습니다.
이즈 카타

아마 틀렸지 만 GameDev.SE에서 더 나쁘지 않습니까?
다이내믹

1
@Dynamic 이것은 데이터 구조 질문이므로 여기에 잘 맞습니다.
이즈 카타

답변:


44

각 룸 좌표 (시작은 (0,0) 임)를 제공하고 생성 된 각 룸을 좌표로 사전 / 해시 맵에 저장하십시오.

룸이 이미 존재하는지 확인하는 데 사용할 수있는 각 룸의 인접 좌표를 결정하는 것은 쉽지 않습니다. 룸이 존재하지 않는 것으로 이미 결정된 위치를 나타 내기 위해 널값을 삽입 할 수 있습니다. (또는 이것이 가능하지 않은 경우, 룸을 포함하지 않는 좌표에 대한 별도의 사전 / 하스 맵 인 atm을 잘 모르겠습니다)


2
각 Room 객체에 다른 Room 객체에 대한 북동-서남쪽 정보를 포함 시키므로 사전 / 해시 맵과 Room 기본 방향을 사용하여 미로를 탐색 할 수 있습니다 (때로는 절대 좌표와 때때로 당신은 Room object X)의 북쪽에 무엇이 있는지 알고 싶을 것입니다.
Neil

2
@Paul 아이디어는 방의 사전을 만들고 새 건물을 만들 때 사전 Room에서 인접한 방을 찾아 Room객체에 대한 링크를 추가하여 나머지 방에 새 방을 추가하는 것입니다. 따라서 사전 조회가 발생할 유일한 시간은 새 Room객체를 만들 때가 아니라 새 객체를 만들 때입니다.Rooms
Rachel

7
Room객체 내부의 다른 방에 대한 링크를 전혀 저장할 이유가 없습니다 . 방에 (x,y)있고 북쪽으로 여행 (x,y+1)하려면 사전에서 방을 찾아 존재하지 않는 경우 사전을 만듭니다. 사전 검색은 매우 빠릅니다 O(1).
Karl Bielefeldt

3
@KarlBielefeldt : @ 방 (x,y+1)은 존재할 수 있지만 (x,y)북쪽 으로 갈 수는 없습니다 . 즉 가장자리로부터 직접 이동하지 않을 수, 말을하는 것입니다 (x,y)(x,y+1).
Steven Evers

2
너희들은 이것을 복잡하게 만들고있다. 이것은 기본적으로 배열과 동일합니다. 2 차원 배열이 아닌 사전에서 찾고 있다는 점을 제외하고. 배열과 관련하여 발생하는 모든 문제도 발생하지만 어쨌든 배열을 사용하려고했습니다.
user606723

15

대부분의 경우 설명하는 것은 그래프처럼 들립니다. 귀하의 요구 사항 중 일부 (성장하는 측면)를 고려할 때 인접 목록 을 구현으로 선택합니다 (다른 공통 옵션은 인접 행렬 일 것입니다 ).

난 당신이 사용하는 어떤 언어 모르겠지만, 좋은 튜토리얼 / 설명 과 함께 .NET에서 인접리스트로 구현 한 그래프에 대한 구현의 세부 사항은 여기 .


1
N / E / S / W가 실제로 그래프 개념의 일부가 아니기 때문에이를 설명하기에 충분한 그래프인지 확실하지 않습니다. 연결되어 있고 가능하거나 들어갔을 수 있습니다.
Edward Strange

3
@CrazyEddie : 데이터 구조는 특정 도메인에 직접 매핑하기위한 것이 아니라는 점에 유의하십시오. 이 특정 예에서, 그래프는 지향성 일 수 있고 에지는 열거 형으로 간단하게 주석을 달 수있다.
Steven Evers

주석은 동서남북 관계를 사소하게 시행 할 수 있습니다. 이것은 링크 A로 인해 방 A에서 방 B로 서쪽으로 이동 한 다음 방 B에서 방 C로 (방 A 대신) 동쪽으로가는 문제를 제거합니다.
피터 스미스

3
플레이어를 임의의 방향으로 10 개의 정사각형으로 순간 이동시켜 자신을 보호하는 마법사로 채워진 방을 구현하고 싶다고합시다. 그래프 기반 데이터 구조에서 해당 포인트를 찾는 것은 특히 비용이 많이 들며, 특히 해당 룸이 존재하지 않고 해당 룸을 그래프의 다른 룸에 연결하는 모든 룸을 생성해야하는 경우 (구조에서 다중을 허용하지 않기 때문에, 서로 분리 된 던전 섹션).
Mark Booth

2
@MarkBooth 그러나 요구 사항을 변경했습니다.
Joshua Drake

9

구현에 대한 몇 가지 생각 (매트릭스를 구축하는 데 여러 가지 어려운 측면이있는 카르카손 (Carcassonne)에 대해 생각했습니다. 한 번 인터뷰 한 질문조차도 없었습니다).

이 구조에 대한 몇 가지 질문이 있습니다.

  1. X, Y에 방이 있습니까?
  2. 빈 위치 X, Y의 룸 [N / S / E / W]이 있습니까?

간단한 목록과 그래프의 문제

간단한 목록의 어려움은 주어진 위치에 무언가를 놓을 수 있는지 테스트하기 위해 구조를 걸어야한다는 것입니다. 시스템은 임의의 위치 O (1)에 액세스하는 방법이 필요합니다.

치다:

1 2 3 ? 4
5 . . * 6
7 8 9 A B

가능한 위치 '?'의 정보를 찾으려면 3시에 4까지 가려면 걸어 다녀야합니다. 얼마나 많은 노드가 점프하는지에 대한 추가 정보가 포함 된 링크의 대답 (3이 4에 연결됨) 6 또는 A에서 '*'의 인접성을 찾을 때 여전히 '점프 1'추가 정보가있는 경우에는 걷기가 필요합니다.

단순하고 큰 배열

사용 가능한 객실 수가 제한되어 있습니다

이것이 큰 숫자가 아닌 경우, 2N x 2N 어레이를 생성하여 그대로 두십시오. 100 x 100 배열은 '만'10,000 개의 포인터와 50 개의 객체입니다. 배열에 직접 색인합니다.

범위를 벗어난 활동으로 인해 어레이가 항상 제약 조건 내에 있도록 시프트 한 경우 NxN으로 감소 될 수 있습니다. 예를 들어, 배열을 언더 플로하는 방을 추가하려는 경우 더 이상 언더 플로가 없도록 그리드를 모든 요소를 ​​한 위치로 이동시킵니다. 이 시점에서 50 개의 방에 대한 세계의 크기는 2500 개의 포인터와 50 개의 객체입니다.

희소 배열 및 행렬

보다 최적의 상태로 만들려면 대부분의 요소가 0 또는 null 인 희소 배열 을 살펴보십시오 . 2 차원의 경우이를 희소 행렬이라고 합니다. 많은 프로그래밍 언어에는이 구조로 작업하기위한 다양한 라이브러리가 있습니다. 라이브러리가 정수로 제한되는 경우이 정수를 고정 된 방 배열의 색인으로 사용할 수 있습니다.

내가 선호하는 접근법은 방을 구조 (인접 방과 좌표에 대한 포인터)로 사용하고 방에 좌표로 해시되는 해시 테이블의 포인터를 갖는 것입니다. 이 상황에서 널 룸에서 어떤 룸이 [N / S / E / W]인지 묻기 위해 해시 테이블을 쿼리합니다. 우연히 이것은 희소 행렬을 저장하기위한 'DOK'형식입니다.


1
Bubblewrap이 제안한 것처럼 좋은 대답 은 위치 튜플을 키로 사용하는 사전은 희소 행렬을 구현하는 데 사용하는 합리적인 구조입니다.
Mark Booth

2

이것은 어떤가요..

각 셀에 각 이웃에 대한 링크를 제공하십시오. 각 셀에 일종의 이름을 지정하십시오 (예 : "0/0", "-10/0"(0,0에서 시작한다고 가정)). 모든 이름을 가진 HashSet을 추가하십시오. 다른 셀로 이동하려고하면 이웃이 HashSet에 없는지 확인하십시오.

또한 다른 방에 개구부를 만들면 방이 존재한다는 의미입니까? 따라서 빈 방에 대한 링크가없는 빈 방에 대한 링크를 만듭니다. 당신은 또한 새로운 방 이웃을 확인하고 싶을 것입니다. 하나가 존재하고 새 방을 열려면 해당 방에 문을 추가하십시오.

   Empty   
   (0,1)        

---    ---            ----------
|        |            |        |
    0,0       1,0        2,0       Empty
|        |            |        |   (3,0)
---    --- ---------- ---    ---
|        | |        | |        |
|   0,-1      1,-1       2,-1      Empty
|        | |        | |        |   (3,-1)
---    --- ---    --- ----------

   Empty     Empty   
  (0,-2)    (1,-2)

HashSet = {0 | 0, 1 | 0, 2 | 0, 3 | 0, 0 | -1, 1 | -1 ....} 1,0 : W = 0,0 / 도어; 1,0 : N = 1,1 / 빈; E = 2,0 / 도어; S = 1, -1 / 벽

또한 미로가 그 방향으로 성장할 수 있도록 각각의 새로운 방에 인접하지 않은 (다른 방에) 적어도 하나의 문을 주어야합니다.


1

디자인하는 것은 그래프처럼 들립니다.

미로의 그래프

이들은 종종 상태 세트 Q , 초기 상태 q 0Q 및 일부 전이 Δ로 표현된다 . 따라서 이렇게 보관하십시오.

  • 세트 Q : {s, 1, 2, 3, 5, 6}
  • 초기 상태 q 0 : s
  • 전이 관계의 맵 Δ : {s → 1, s → 5, 1 → 2, 2 → 3, 3 → 4, 4 → 5}

가장 합리적인 언어에는지도와 세트가 모두 있습니다.


0

4 가지 방법으로 연결된 목록을 고려할 수 있습니다 ...

나는 방에 [X] [X] 배열의 객체를 사용하는 것에 대해 먼저 생각했지만, 어떤 방향 으로든 성장해야하므로 "방문"된 방만 건축해야한다는 점을 피하고 싶습니다.

여전히 [] []를 사용할 수 있습니다. 특히 처음에 추가 할 때 동적 성장이 느려질 수 있지만 그다지 나쁜 것은 아닙니다. 확인하려면 프로파일해야합니다. 링크 된 목록이나 맵을 조작하려고하면 실제로는 더 나빠질 수 있으며 가끔 일정한 수준이 아닙니다.

지연 평가를 구현하여 방문한 방만 만들 수 있습니다. 방을 포함하는 객체가 제자리에있을 수 있으며 그 방이 room()호출 될 때까지 해당 방을 만들지 않습니다 . 그런 다음 매번 같은 것을 반환합니다. 어렵지 않습니다.


1
"4 웨이 링크 된 목록"의 의미를 확장 할 수 있습니까? 내가 찾을 수있는 유일한 것은 CodeProject 기사였습니다.이 기사는 인접 목록이되었습니다.
Steven Evers

0

"컴퓨터에서 모험 게임 만들기"책을 통해이 작업을 배웠습니다. 기본 코드를 기꺼이 파고 싶다면 (이 책은 오래된 것입니다) 여기를 읽으십시오.

http://www.atariarchives.org/adventure/chapter1.php

바로 가기의 경우 방의 배열이 있으며 각 배열의 요소는 갈 수있는 다른 방에 대한 포인터이며 0은 (요즘 -1을 사용합니다) 표시 할 수 없음을 나타냅니다 그렇게하세요 예를 들어, 방 구조를 만들 것입니다 (또는 수업 또는 당신이 무엇을)

room {
 int north_dir;
 int south_dir;
 int west_dir;
 int east_dir;
 // All other variables and code you care about being attached to the rooms here
}

그런 다음 배열 또는 연결된 목록 구조를 가지거나 방을 관리 할 수 ​​있습니다. 배열 스타일 (또는 C ++ 벡터)의 경우 해당 코드를 사용하고 방향은 링크 된 방의 색인을 보유합니다. 연결된 목록의 경우 다음 방에 대한 포인터를 사용할 수 있습니다.

다른 사람들이 말했듯이 사람들이 들어올 때 새로운 방을 동적으로 생성해야하는 경우에도 그렇게 할 수 있습니다.


0

하나의 구조로 모든 문제를 해결하려고 시도하지 마십시오.

다른 방과의 관계가 아닌 다른 방과 관련된 물건을 저장하도록 방 개체를 정의하십시오. 그러면 1D 배열, 목록 등이 원하는대로 커질 수 있습니다.

별도의 구조는 "접근성 (reachability)"을 보유 할 수 있습니다-어느 방에있는 방에서 접근 할 수 있는지. 그런 다음 전 이적 접근성이 빠른 계산이 필요한지 여부를 결정하십시오. 무차별 대입 계산 및 캐시를 원할 수 있습니다.

조기 최적화를 피하십시오. 정말 간단한 구조와 간단하고 이해하기 쉬운 알고리즘을 사용한 다음 사용되는 알고리즘을 최적화하십시오.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.