Tôi nên xây dựng cấu trúc dữ liệu như thế nào cho một mê cung năng động, không giới hạn kích thước?


43

Tôi thực sự không chắc chắn rằng "mê cung" là thuật ngữ chính xác. Về cơ bản người dùng bắt đầu trong một Roomcửa có 4 cửa (N, S, E và W). Họ có thể đi theo bất kỳ hướng nào, và mỗi phòng tiếp theo chứa một phòng khác với bất kỳ nơi nào từ 1 đến 4 ô cửa đi đến các phòng khác.

"Mê cung" được cho là có kích thước không giới hạn, và phát triển khi bạn chuyển phòng. Có một số lượng Roomscó hạn, tuy nhiên số có sẵn là động và có thể thay đổi.

Vấn đề của tôi là, tôi không chắc về cấu trúc dữ liệu tốt nhất cho kiểu mẫu này

Trước tiên tôi nghĩ về việc chỉ sử dụng một mảng các Roomđối tượng [X] [X] , nhưng tôi thực sự muốn tránh điều đó vì thứ đó đáng lẽ phải phát triển theo bất kỳ hướng nào, và chỉ nên xây dựng các phòng được "ghé thăm".

Ý nghĩ khác là mỗi Roomlớp chứa 4 Roomthuộc tính được liên kết cho N, S, E và W và chỉ liên kết với trước đó Room, nhưng vấn đề là tôi không biết cách xác định nếu người dùng đi vào phòng mà có một phòng liền kề đã được "xây dựng"

Ví dụ,

--- --- ----------
| | | |
   Bắt đầu 5 4
| | | |
--- --- --- --- ---
--- --- ---------- --- ---
| | | | | |
| 1 2 3
| | | | | |
--- --- --- --- ----------

Nếu người dùng di chuyển từ Bắt đầu> 1> 2> 3> 4> 5, thì Roomsố 5 cần biết rằng W chứa phòng bắt đầu, S là phòng số 2 và trong trường hợp này không nên có sẵn và N có thể là một mới Roomhoặc một bức tường (không có gì).

Có lẽ tôi cần một sự pha trộn của mảng và các phòng được liên kết, hoặc có thể tôi chỉ nhìn sai cách này.

Có cách nào tốt hơn để xây dựng cấu trúc dữ liệu cho loại "mê cung" này không? Hay tôi đang đi đúng hướng với quá trình suy nghĩ hiện tại của mình, và tôi chỉ thiếu một vài thông tin?

(Trong trường hợp bạn quan tâm, dự án là một trò chơi rất giống với Munchkin Quest )


Tôi không nghĩ bất kỳ loại mảng nào sẽ hoạt động vì các phòng sẽ phát triển theo bất kỳ hướng nào ... Vậy nếu bạn bắt đầu ở [0,0] và di chuyển sang trái? nó sẽ thử [-1, 0].
Paul

@Paul Nối một hàng / cột, dịch chuyển tất cả dữ liệu mảng, sau đó thay đổi tất cả các vị trí trình phát để phù hợp với mảng bản đồ mới. Chậm và có thể khó tùy thuộc vào mức độ phải thay đổi, nhưng có thể. Tuy nhiên, câu trả lời của Bubblewrap tốt hơn nhiều.
Izkata

Tôi có thể sai, nhưng điều này sẽ tốt hơn trên GameDev.SE?
Năng động

1
@Docate Đây là một câu hỏi về cấu trúc dữ liệu, vì vậy nó chỉ phù hợp ở đây.
Izkata

Câu trả lời:


44

Đưa cho mỗi tọa độ Phòng (bắt đầu sẽ là (0,0)) và lưu trữ từng Phòng được tạo trong từ điển / hashmap theo tọa độ.

Việc xác định tọa độ liền kề cho mỗi Phòng mà bạn có thể sử dụng để kiểm tra xem Phòng có tồn tại hay không. Bạn có thể chèn giá trị null để thể hiện các vị trí đã xác định rằng không có Phòng nào tồn tại. (hoặc nếu điều đó là không thể, tôi không chắc chắn, một từ điển / sơ đồ riêng biệt cho các tọa độ không chứa Phòng)


2
Đặt từng đối tượng Phòng chứa thông tin theo hướng đông bắc-tây nam cho các đối tượng Phòng khác, do đó bạn có thể sử dụng cả từ điển / hàm băm cũng như các hướng hồng y của Phòng để điều hướng mê cung (đôi khi bạn sẽ muốn tìm với tọa độ tuyệt đối và đôi khi bạn sẽ muốn biết những gì phía bắc của đối tượng phòng X).
Neil

2
@Paul Tôi nghĩ ý tưởng là tạo ra một từ điển các phòng, và khi xây dựng một cái mới Room, hãy tìm các phòng liền kề trong từ điển và thêm các liên kết của chúng vào Roomđối tượng trước khi thêm các phòng mới vào các mặt còn lại. Vì vậy, lần duy nhất tra cứu từ điển sẽ xảy ra là khi tạo một Roomđối tượng mới , không phải khi đi giữaRooms
Rachel

7
Không có lý do để lưu trữ các liên kết đến các phòng khác bên trong một Roomđối tượng. Nếu bạn đang ở trong phòng (x,y)và muốn đi du lịch về phía Bắc, bạn hãy tra phòng (x,y+1)trong từ điển, tạo nó nếu nó không tồn tại. Tra cứu từ điển rất nhanh O(1),.
Karl Bielefeldt

3
@KarlBielefeldt: Phòng @ (x,y+1)có thể tồn tại, nhưng không thể điều hướng được (x,y)khi đi về phía Bắc. Điều đó có nghĩa là, một cạnh có thể không đi trực tiếp từ (x,y)đến (x,y+1).
Steven Evers

2
Các bạn đang làm cho điều này phức tạp. Điều này về cơ bản giống như một mảng .. ngoại trừ bạn đang tìm kiếm nó trong một từ điển chứ không phải là một mảng hai chiều. Bất kỳ vấn đề nào bạn gặp phải với một mảng cũng sẽ có ... nhưng dù sao anh ta cũng sẽ sử dụng một mảng.
dùng606723

15

Trong hầu hết các trường hợp, những gì bạn đang mô tả nghe giống như một biểu đồ. Đưa ra một số yêu cầu của bạn (khía cạnh đang phát triển) Tôi có thể chọn một danh sách kề là việc triển khai (tùy chọn phổ biến khác sẽ là ma trận kề ).

Tôi không chắc bạn đang sử dụng ngôn ngữ nào, nhưng một hướng dẫn / giải thích tốt với các chi tiết triển khai cho biểu đồ được triển khai với danh sách kề trong .NET có ở đây .


1
Không chắc chắn một biểu đồ là đủ để mô tả điều này vì N / E / S / W không thực sự là một phần của khái niệm biểu đồ; chỉ có kết nối và có thể vào / ra.
Edward Strange

3
@CrazyEddie: Hãy nhớ rằng / cấu trúc dữ liệu không có ý định ánh xạ trực tiếp đến bất kỳ miền cụ thể nào (thực sự, đó là lợi ích của chúng). Trong ví dụ cụ thể này, đồ thị sẽ có hướng và các cạnh có thể được chú thích tầm thường với một enum.
Steven Evers

Chú thích có thể thực thi một cách tầm thường mối quan hệ đông-tây, bắc-nam. Điều này sẽ loại bỏ các vấn đề về phía tây từ phòng A đến phòng B và sau đó đi về phía đông từ phòng B đến phòng C (thay vì phòng A) do liên kết xấu.
Peter Smith

3
Hãy nói rằng chúng tôi muốn thực hiện một căn phòng được tạo ra bởi một pháp sư tự bảo vệ mình bằng cách dịch chuyển mười hình vuông theo hướng ngẫu nhiên. Tìm điểm đó trong cấu trúc dữ liệu dựa trên biểu đồ sẽ rất tốn kém, đặc biệt là nếu phòng đó không tồn tại và bạn phải tạo tất cả các phòng liên kết phòng đó với phòng khác trong biểu đồ (vì cấu trúc sẽ không cho phép nhiều, các phần ngắt kết nối lẫn nhau của ngục tối).
Đánh dấu gian hàng

2
@MarkBooth nhưng sau đó chúng tôi đã thay đổi yêu cầu, phải không?
Joshua Drake

9

Một vài suy nghĩ về việc thực hiện (Tôi đã nghĩ về Carcassonne có một số khía cạnh thách thức khác để xây dựng ma trận - đó thậm chí là một câu hỏi phỏng vấn tôi từng được hỏi).

Có một số câu hỏi được hỏi về cấu trúc này:

  1. Có phòng nào ở X, Y không?
  2. Có phòng [N / S / E / W] của vị trí trống X, Y không?

Vấn đề với danh sách và đồ thị đơn giản

Khó khăn với các danh sách đơn giản là người ta phải đi bộ cấu trúc để kiểm tra xem có thể đặt thứ gì đó tại một vị trí nhất định hay không. Hệ thống cần một cách truy cập một vị trí tùy ý O (1).

Xem xét:

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

Để tìm thông tin vị trí có thể '?', Khi lên 3, người ta phải đi bộ khắp nơi để đến 4. Câu trả lời của liên kết với thông tin bổ sung về số lần nhảy của nó (để 3 nút được liên kết với 4 với thông tin bổ sung 'nhảy 1') vẫn cần phải đi bộ khi tìm sự phụ thuộc của '*' từ 6 hoặc A.

Đơn giản, lớn, mảng

Số lượng phòng có hạn

Nếu đây là một số không lớn, chỉ cần tạo một mảng 2N x 2N và để nó ở đó. Một mảng 100 x 100 là 'chỉ' 10.000 con trỏ và 50 đối tượng. Chỉ mục trực tiếp vào mảng.

Điều này có thể được giảm xuống chỉ còn NxN nếu các hoạt động ngoài giới hạn chuyển mảng xung quanh để luôn nằm trong các ràng buộc. Ví dụ: nếu một phòng được thêm vào sẽ tràn vào mảng, có lưới để dịch chuyển mọi vị trí một phần tử để không còn dòng chảy nữa. Tại thời điểm này, kích thước của thế giới cho 50 phòng sẽ là 2500 con trỏ và 50 đối tượng.

Mảng thưa và ma trận

Để tối ưu hơn tạo điều này, hãy nhìn vào một mảng thưa thớt trong đó phần lớn các phần tử là 0 hoặc null. Đối với hai chiều, đây được gọi là ma trận thưa thớt . Nhiều ngôn ngữ lập trình có các thư viện khác nhau để làm việc với cấu trúc này. Nếu thư viện giới hạn số nguyên, người ta có thể sử dụng số nguyên này làm chỉ mục vào một mảng phòng cố định.

Cách tiếp cận ưa thích của tôi sẽ là có các phòng như một cấu trúc (con trỏ đến các phòng liền kề và tọa độ) và có phòng cũng là một con trỏ từ bảng băm được băm trên tọa độ. Trong tình huống này để hỏi phòng nào [N / S / E / W] từ phòng null, người ta sẽ truy vấn bảng băm. Điều này, tình cờ, là định dạng 'DOK' để lưu trữ một ma trận thưa thớt.


1
Câu trả lời hay, như Bubblewrap gợi ý, một từ điển với một vị trí là khóa là một cấu trúc hợp lý để sử dụng để thực hiện một ma trận thưa thớt.
Đánh dấu gian hàng

2

Còn cái này thì sao..

Cung cấp cho mỗi tế bào một liên kết đến mỗi hàng xóm của nó. Cung cấp cho mỗi ô một số loại tên (tức là "0/0", "-10/0" (Giả sử bạn bắt đầu từ 0,0)). Thêm một Hashset với tất cả các tên trong đó. Khi bạn cố gắng di chuyển đến một ô khác, chỉ cần kiểm tra xem hàng xóm chưa tồn tại trong Hashset.

Ngoài ra, nếu bạn tạo một lối mở cho một phòng khác, điều đó có nghĩa là các phòng tồn tại? Vì vậy, bạn sẽ tạo một liên kết đến một căn phòng trống không có liên kết đến bất kỳ phòng nào. Bạn có thể cũng muốn kiểm tra hàng xóm phòng mới. Nếu có, và sẽ mở ra căn phòng mới của bạn, hãy thêm một cánh cửa vào căn phòng đó.

   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 / Cửa; 1,0: N = 1,1 / Trống; E = 2,0 / Cửa; S = 1, -1 / Tường

Bạn cũng phải đảm bảo rằng bạn cung cấp cho mỗi phòng mới ít nhất một cửa không liền kề (sang phòng khác) để mê cung có thể phát triển theo hướng đó.


1

Những gì bạn đang thiết kế nghe có vẻ giống như một biểu đồ.

Một đồ thị của mê cung

Chúng thường được thể hiện dưới dạng một tập các trạng thái Q , một trạng thái ban đầu q 0Q , và một số hiệu ứng chuyển tiếp Δ . Vì vậy, tôi khuyên bạn nên lưu trữ nó như thế này.

  • Một bộ Q : {s, 1, 2, 3, 5, 6}
  • Trạng thái ban đầu q 0 : s
  • Một bản đồ của quan hệ chuyển tiếp delta : {s → 1, s → 5, 1 → 2, 2 → 3, 3 → 4, 4 → 5}

Hầu hết các ngôn ngữ hợp lý có cả bản đồ và bộ.


0

Bạn có thể xem xét danh sách liên kết 4 chiều ...

Trước tiên tôi nghĩ về việc chỉ sử dụng một mảng [X] [X] của các đối tượng Phòng, nhưng tôi thực sự muốn tránh điều đó vì thứ đó đáng lẽ phải phát triển theo bất kỳ hướng nào và chỉ nên xây dựng các phòng được "truy cập".

Bạn vẫn có thể sử dụng [] [] cho điều đó. Sự phát triển năng động có thể chậm, đặc biệt là khi thêm vào lúc đầu, nhưng có thể không tệ lắm. Bạn nên hồ sơ để kiểm tra. Cố gắng thao túng một số danh sách hoặc bản đồ được liên kết thực sự có thể tồi tệ hơn và ở mức độ không đổi thay vì thỉnh thoảng.

Bạn chỉ có thể xây dựng các phòng đã ghé thăm bằng cách thực hiện đánh giá lười biếng. Một đối tượng có thể ở vị trí chứa một căn phòng và không xây dựng căn phòng đó cho đến khi room()được gọi vào đó. Sau đó, nó chỉ trả lại cùng một lần sau đó. Không khó để làm.


1
Bạn có thể mở rộng ý nghĩa của "danh sách liên kết 4 chiều" không? Điều duy nhất tôi có thể tìm thấy là một bài viết CodeProject, được coi là một danh sách kề.
Steven Evers

0

Tôi đã học cách làm điều này thông qua cuốn sách "Tạo trò chơi phiêu lưu trên máy tính của bạn". Nếu bạn sẵn sàng tìm hiểu mã BASIC (cuốn sách đã cũ), hãy đọc tại đây:

http://www.atariarchives.org/adventure/ch CHƯƠNG1.php

Đối với phím tắt, những gì họ làm là có một mảng các phòng, với một yếu tố trong mỗi mảng là một con trỏ đến một phòng khác mà bạn có thể đến, với 0 (tôi sẽ sử dụng -1 ngày nay) để cho biết rằng bạn không thể đi theo cách đó Ví dụ: bạn tạo cấu trúc phòng (hoặc lớp hoặc những gì bạn có)

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
}

Sau đó, bạn có thể có một mảng hoặc cấu trúc danh sách được liên kết hoặc tuy nhiên bạn muốn quản lý phòng của mình. Đối với kiểu mảng (hoặc vectơ C ++) tôi sẽ sử dụng mã đó và các hướng sẽ giữ chỉ mục của phòng mà chúng liên kết đến; đối với một danh sách được liên kết, bạn có thể sử dụng con trỏ đến phòng tiếp theo.

Như những người khác đã nói, nếu bạn cần tự động tạo phòng mới khi mọi người vào chúng, bạn cũng có thể làm điều đó.


0

Đừng cố gắng và giải quyết tất cả các vấn đề với một cấu trúc.

Xác định đối tượng phòng của bạn để lưu trữ những thứ về phòng, không quan hệ với các phòng khác. Sau đó, một mảng 1D, danh sách vv có thể phát triển như bạn muốn.

Một cấu trúc riêng biệt có thể chứa "khả năng tiếp cận" - phòng nào có thể truy cập được từ phòng tôi đang ở. Sau đó, quyết định xem khả năng tiếp cận bắc cầu có cần phải tính toán nhanh hay không. Bạn có thể muốn tính toán vũ phu và bộ đệm.

Tránh tối ưu hóa sớm. Sử dụng các cấu trúc thực sự đơn giản và các thuật toán dễ hiểu câm sau đó tối ưu hóa các thuật toán được sử dụng.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.