Як я повинен побудувати структуру даних для динамічного "лабіринту" необмеженого розміру?


43

Я насправді не впевнений, що "лабіринт" - це правильний термін. В основному користувачі починаються з одного, Roomякий має 4 двері (N, S, E і W). Вони можуть їхати в будь-якому напрямку, і кожна наступна кімната містить ще одну кімнату з місцями від 1 до 4 дверних прорізів, які йдуть в інші приміщення.

"Лабіринт", як передбачається, має бути необмеженим за розміром і рости по мірі переміщення кімнат. Існує обмежена кількість Roomsдоступних, однак доступна кількість динамічна і може змінюватися.

Моя проблема полягає в тому, що я не впевнений у найкращій структурі даних для такого типу шаблонів

Я спершу подумав про те, щоб просто використовувати [X] [X] масив Roomоб’єктів, але я дійсно вважаю за краще уникати цього, оскільки річ повинна рости в будь-якому напрямку, і слід будувати лише кімнати, які "відвідуються".

Інша думка полягала в тому, щоб кожен Roomклас містив 4 пов'язані Roomвластивості для N, S, E і W і просто посилався на попередній 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 Додайте рядок / стовпчик, змістіть усі дані масиву, а потім змістіть усі позиції гравця, щоб відповідати новому масиву карт. Повільно, і це може бути важко залежно від того, наскільки потрібно зрушити, але можливо. Проте відповідь Bubblewrap набагато краща.
Ізката

Я, мабуть, помиляюся, але хіба це не було б краще на GameDev.SE?
Динамічний

1
@Dynamic Це питання структури даних, тому воно тут добре вписується.
Ізката

Відповіді:


44

Дайте кожній координаті кімнати (початок буде (0,0)) і збережіть кожну створену Кімната у словнику / хешмапі за координатами.

Неважливо визначити сусідні координати для кожної кімнати, за допомогою яких можна перевірити, чи існує кімната. Ви можете вставити нульові значення, щоб представити місця, де вже визначено, що немає кімнати. (або якщо це неможливо, я не впевнений, атм, окремий словник / мапа для координат, які не містять номер)


2
Зробіть так, щоб кожен об'єкт Кімнати містив інформацію з північного сходу-південного заходу для інших об'єктів Кімнати, тому ви можете використовувати словник / хешмап, а також кардинальні вказівки Кімнати для навігації по лабіринту (іноді ви хочете знайти з абсолютними координатами та іноді вам захочеться знати, що знаходиться на північ від об'єкта X).
Ніл

2
@Paul Я думаю, що ідея полягає у створенні словника кімнат, і, будуючи нові Room, шукайте сусідні кімнати у словнику та додайте їх посилання на Roomоб’єкт, перш ніж додавати нові кімнати до решти сторін. Тому єдиний раз, коли відбуватиметься пошук словника, це створювати новий Roomоб’єкт, а не під час подорожі між нимиRooms
Рейчел

7
Зовсім немає причин зберігати посилання на інші приміщення всередині Roomоб'єкта. Якщо ви знаходитесь в кімнаті (x,y)і хочете подорожувати на північ, ви шукаєте приміщення (x,y+1)в словнику, створюючи його, якщо його немає. Словник пошуки дуже швидко, O(1).
Карл Білефельдт

3
@KarlBielefeldt: Кімната @ (x,y+1)може існувати, але не (x,y)рухатися з півночі. Тобто, край може не переходити безпосередньо (x,y)до (x,y+1).
Стівен Еверс

2
Ви, хлопці, ускладнюєте це. Це в основному те саме, що і масив .. за винятком того, що ви шукаєте його в словнику, а не в двовимірному масиві. Будь-які проблеми, з якими ви зіткнетесь із масивом, також будуть там ... але він все одно буде використовувати масив.
користувач606723

15

У більшості випадків те, що ви описуєте, звучить як графік. Враховуючи деякі ваші вимоги (зростаючий аспект), я, мабуть, вибрав би список суміжності як реалізацію (іншим поширеним варіантом буде матриця примикання ).

Я не впевнений , що мова ви використовуєте, але хороший підручник / пояснення з деталями реалізації для графа реалізованого зі списком суміжності в .NET знаходиться тут .


1
Не впевнений, що графік достатній для його опису, оскільки N / E / S / W насправді не є частиною концепції графіка; є лише підключений і, можливо, вхід / вихід.
Едвард Странд

3
@CrazyEddie: Майте на увазі, що структура даних не призначена для прямого відображення в якомусь конкретному домені (адже це є їх користю). У цьому конкретному прикладі графік був би спрямованим, а краї можна тривіально позначати за допомогою перерахунку.
Стівен Еверс

Анотація може тривільно застосувати навіть відносини схід-захід, північ-південь. Це усуне проблеми переходу на захід від кімнати A до кімнати B, а потім сходу від кімнати B до кімнати C (замість кімнати A) через поганий зв’язок.
Пітер Сміт

3
Скажемо, що ми хотіли реалізувати приміщення, заселене майстром, який захищає себе, телепортуючи програвача на десять квадратів у випадковому напрямку. Виявити цю точку в структурі даних на основі графіків було б дуже дорого, особливо якщо б ця кімната не існувала, і вам довелося генерувати всі кімнати, що пов'язують цю кімнату з іншою кімнатою на графіку (оскільки структура не дозволить множинні, взаємно роз’єднані ділянки підземелля).
Марк Бут

2
@MarkBooth, але тоді ми змінили вимоги, ні?
Джошуа Дрейк

9

Пару думок про реалізацію (я думав про Каркассона, який має ряд інших складних аспектів для побудови матриці - колись мені було задано навіть питання інтерв'ю).

Є кілька питань, які задаються цій структурі:

  1. є кімната в X, Y?
  2. чи є кімната [N / S / E / W] порожнього місця X, Y?

Проблема з простими списками та графіками

Складність простих списків полягає в тому, що потрібно пройти структуру, щоб перевірити, чи можна щось розмістити в заданому місці. Системі потрібен спосіб доступу до довільного місця O (1).

Поміркуйте:

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

Щоб знайти інформацію про можливе розташування "?", Коли в 3, треба пройти весь шлях, щоб дістатися до 4. Відповідь посилання з додатковою інформацією про те, скільки вузлів вона перестрибує (щоб 3 були пов'язані з 4 з додатковою інформацією "стрибок 1" все ж вимагає прогулянок, коли знаходить сусідство "*" з 6 або A.

Прості, великі масиви

Є обмежена кількість номерів

Якщо це не велика кількість, просто створіть масив 2N x 2N і залиште його там. Масив 100 х 100 - це лише "10000 вказівників" та 50 об'єктів. Індексуйте безпосередньо в масив.

Це може бути зменшено до всього NxN, якби поза межами діяльності зміщений масив навколо був завжди в межах обмежень. Наприклад, якщо потрібно додати кімнату, яка переповнюватиме масив, матимуть сітку для переміщення кожного елемента в одну позицію, щоб більше не було переливу. На даний момент розмір світу на 50 номерів становив би 2500 покажчиків та 50 об’єктів.

Рідкі масиви та матриці

Щоб більш оптимально створити це, подивіться на розріджений масив, у якому більшість елементів дорівнює 0 або нуль. Для двох вимірів це відоме як розріджена матриця . Багато мов програмування мають різні бібліотеки для роботи з цією структурою. Якщо бібліотека обмежується цілими числами, це ціле число можна використовувати як індекс у фіксованому масиві кімнат.

Моїм переважним підходом було б мати кімнати як структуру (покажчики на сусідні кімнати та координати) і мати приміщення також вказівником із хеш-таблиці, яка хеширована по координаті. У цій ситуації, щоб запитати, яка кімната є [N / S / E / W] з нульової кімнати, можна запитати хеш-таблицю. Це, до речі, формат "DOK" для зберігання розрідженої матриці.


1
Хоча приємна відповідь, як пропонує Bubblewrap , словник із кордоном позиції як ключовим є розумною структурою, яка використовується для реалізації рідкої матриці.
Марк Бут

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
Чи можете ви розширити, що ви маєте на увазі під "чотиристоронньо пов'язаним списком"? Єдине, що я міг знайти, це стаття CodeProject, яка зводиться до списку суміжності.
Стівен Еверс

0

Я навчився це робити завдяки книзі «Створення пригодницьких ігор на своєму комп’ютері». Якщо ви готові перекопати BASIC-код (книга ця стара), прочитайте тут:

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 масив, список тощо можуть рости, як вам завгодно.

Окрема структура може містити "доступність" - які кімнати доступні з кімнати, в якій я перебуваю. Тоді вирішуйте, чи потрібно транзитивна доступність швидкого розрахунку чи ні. Можливо, ви хочете розраховувати грубу силу і кеш-пам'ять.

Уникайте ранньої оптимізації. Використовуйте дійсно прості структури та німі легко зрозумілі алгоритми, а потім оптимізуйте ті, до яких звикаєте.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.