Kết nối phòng tròn


7

Roguelike của tôi tạo ra một số phòng tròn. Tôi khá thích kết quả hiện tại, trông giống như thế này:

nhập mô tả hình ảnh ở đây

Nó được tạo ra bằng cách khắc ra các vòng tròn ngẫu nhiên trong một không gian đầy.

Tôi có một vấn đề chính với nó: một số vòng tròn hoàn toàn không được kết nối. Tôi hoàn toàn không biết nên sử dụng thuật toán nào để kết nối chúng.

Tôi đã thử "cho mỗi vòng tròn, tìm vòng tròn gần nhất và đường hầm đến trung tâm của nó (theo chiều ngang sau đó theo chiều dọc)", nhưng tôi nhận được một cái gì đó khủng khiếp thay vào đó (cùng bản đồ như trên):

nhập mô tả hình ảnh ở đây

Những gì có vẻ phù hợp là một cái gì đó như "cho mỗi không gian tự khép kín, tìm không gian trống gần nhất và đường hầm đến đó." Điều đó có vẻ phức tạp - mặc dù (đối với mỗi không gian kín hình tròn, hãy tính khoảng cách đến mọi không gian khác) và có thể sẽ chậm (cũng chạy trên Ruby)

Có một giải pháp đơn giản cho vấn đề này?


Đó là điều bạn muốn? Để có mỗi không gian kết nối với không gian gần nhất trong cùng một khu vực?
AturSams

Tại sao không đường hầm dọc theo một đường tùy ý, không chỉ đường ngang / dọc?
dùng1095108

@ArthurWulfWhite Tôi muốn toàn bộ bản đồ được kết nối. Không gian gần nhất là một chút phức tạp (tôi chưa theo dõi gạch chu vi).
tro999

1
Lưu ý bên lề: chuyển sang Linux, nơi có các thiết bị đầu cuối đẹp và sử dụng màu sắc! Đó là bên cạnh tất cả những điều tốt đẹp dành cho một lập trình viên trong một môi trường dựa trên unix!
Shahbaz

@Shahbaz Tôi chủ yếu sử dụng Linux (ít nhất là cho nhà phát triển trò chơi). Tôi sẽ cập nhật ảnh chụp màn hình của mình với các phiên bản Linux (imo, đẹp hơn) ngay khi tôi tìm ra tích hợp bảng tạm VirtualBox ...
tro99999

Câu trả lời:


6

Ổn thỏa. Có một giải pháp khá rẻ tiền cho vấn đề này. Trước tiên hãy sử dụng Union Find để theo dõi các phòng chưa được kết nối.

Mỗi ô vuông bây giờ sẽ thuộc về một số bộ có một chỉ mục duy nhất.

  1. Khi bạn tạo một phòng hình tròn, hợp nhất tất cả các hình vuông bên trong nó vào cùng một bộ.
  2. Khi bạn tạo một phòng trùng với phòng khác, thống nhất hai bộ thành một.
  3. Khi bạn đã hoàn tất, bạn sẽ có một loạt các khoảng trắng, mỗi không gian được kết nối có ID duy nhất của riêng nó trong cấu trúc tìm dữ liệu kết hợp.
  4. Chọn không gian mở lớn nhất (bạn cần theo dõi kích thước của các bộ trong liên kết tìm).
  5. Hãy coi bản đồ là một đồ thị của các nút trong đó mỗi nút được kết nối với nó bên trái, phải, lên và xuống bởi một cạnh. Nếu một cạnh dẫn đến một bức tường, nó có giá (1), nếu không, nó có giá epsilon0,0001. Tất cả các phòng khác (không gian trống) sẽ được kết nối với nút đích ảo với cạnh miễn phí.
  6. Sử dụng Dijkstra để tìm đường đi ngắn nhất từ ​​không gian phòng hiện tại đến đích và xóa đường dẫn đó bằng cách loại bỏ các bức tường trong đường dẫn này.
  7. Bây giờ hợp nhất các bộ (các phòng đã tham gia) và tiếp tục quá trình này với Dijkstra.

Bạn có thể muốn hỏi một số câu hỏi về điều này nếu bạn không có nền tảng CS nhưng nhìn chung bạn đang tìm kiếm con đường ngắn nhất thực sự giữa các phòng (không được kết nối [cụ thể là nhìn xuyên tường]) và khi bạn tìm thấy một con đường, dọn sạch nó (loại bỏ các bức tường) và sau đó xác định chúng (hai phòng được kết nối mới) là cùng một bộ và chuyển sang kết nối khu vực chính đang phát triển với tất cả các phòng khác nhau bằng cách lặp lại quá trình với Dijkstra nhiều lần.
AturSams

Tôi có một nền tảng CS. Nước sốt bí mật ở đây là sử dụng Dijkstra (hoặc có thể A *) để đi qua các tế bào trên tường. Ý tưởng rất thú vị; cảm ơn, tôi sẽ thử nó
tro999

1

Câu trả lời của Arthur dường như giải quyết vấn đề của bạn, nhưng đây là một cách tiếp cận dễ dàng hơn:

  1. bắt đầu từ một trong các ô trống.
  2. sử dụng thuật toán BFS, đánh dấu tất cả các ô trong khu vực đó.
  3. thêm tất cả các nút được truy cập ở bước trước để bắt đầu danh sách thuật toán BFS mới và sử dụng nó khám phá qua các bức tường cho đến khi bạn tìm thấy một số khu vực trống khác chưa được truy cập
  4. xóa đường dẫn BFS thứ hai của bạn đi đến khu vực mới.
  5. lặp lại quá trình từ bước 2, cho đến khi tất cả các ô được truy cập.

lưu ý rằng trong mỗi lần lặp lại, bạn chỉ cần tiếp tục BFS trước đó. Ý tôi là khởi động lại thuật toán ngay từ đầu sẽ chỉ làm giảm hiệu suất của bạn và nó sẽ không tạo ra kết quả nào tốt hơn.


1
Tôi thấy những gì bạn đang làm nhưng nếu bạn không dừng lại bfsvà chỉ khởi động lại từ các bức tường bên ngoài hiện tại của chu vi thì điều đó không đảm bảo rằng những con đường ngắn nhất sẽ được thực hiện giữa các khu vực mở. Ngoài ra, nó sẽ hoạt động khá giống nhau và đơn giản hóa giải pháp và nó phức tạp.
AturSams

tốt, nó đảm bảo con đường ngắn nhất. Và ngay cả khi tôi đang phạm phải bất kỳ sai lầm nào (mà tôi thực sự nghi ngờ), các đường dẫn được tạo sẽ gần tối ưu.
Ali1S232

1

Nếu bạn muốn một giải pháp đơn giản hơn, vì các phòng đơn giản và hình tròn, bạn có thể làm điều này thay vào đó:

  1. Chọn một phòng ngẫu nhiên.
  2. Thêm nó vào danh sách (hiện đang trống) của các visitedphòng.
  3. Lặp lại một số cấu trúc dữ liệu có chứa tất cả unvisited phòng và tìm unvisitedphòng gần nhất . Nếu khoảng cách giữa chúng nhỏ hơn tổng bán kính của chúng thì có lẽ không có lý do gì để kết nối chúng nhưng trong mọi trường hợp "vẽ" một hành lang với một đường không thẳng giữa tâm của chúng (tôi sẽ mô tả thuật toán sau).
  4. Lặp lại trên tất cả unvistedvà tìm một trong những gần nhất với một trong visitednhững người.
    • min [(xu-xv)^2 + (yu-yv)^2].
  5. Kết nối hai phòng, thêm phòng mới vào visited, xóa khỏi unvisitedvà lặp lại cho đến khi tất cả các phòng đều visitedthành công.

Điều này sẽ hoạt động với một bản đồ có ít hơn một trăm phòng nhưng có thể được cải thiện về hiệu quả tính toán theo nhiều cách (không liên quan đến câu hỏi).

Để vẽ một đường thẳng không sử dụng thuật toán giả sau:

Mã được lấy từ đây

       /**
        * Draw a line
        * 
        * @param x0     first point x coord
        * @param y0     first point y coord 
        * @param x1     second point x coord
        * @param y1     second point y coord
        * @param c      color (0xaarrvvbb)
        */
        public function line ( x0:int, y0:int, x1:int, y1:int, color:uint ):void
        {   
            var dx:int;
            var dy:int;
            var i:int;
            var xinc:int;
            var yinc:int;
            var cumul:int;
            var x:int;
            var y:int;
            x = x0;
            y = y0;
            dx = x1 - x0;
            dy = y1 - y0;
            xinc = ( dx > 0 ) ? 1 : -1;
            yinc = ( dy > 0 ) ? 1 : -1;
            dx = dx < 0 ? -dx : dx;
            dy = dy < 0 ? -dy : dy;
            setPixel32(x,y,color);

            if ( dx > dy )
            {
                cumul = dx >> 1;
                for ( i = 1 ; i <= dx ; ++i )
                {
                    x += xinc;
                    cumul += dy;
                    if (cumul >= dx)
                    {
                        cumul -= dx;
                        y += yinc;
                    }
                    setPixel32(x,y,color);
                }
            }else
            {
                cumul = dy >> 1;
                for ( i = 1 ; i <= dy ; ++i )
                {
                    y += yinc;
                    cumul += dx;
                    if ( cumul >= dy )
                    {
                        cumul -= dy;
                        x += xinc ;
                    }
                    setPixel32(x,y,color);
                }
            }
        }

1

Thật không may, cả hai giải pháp của Arthur đều không chứng minh được khả năng của tôi; nó đã dẫn đến một vấn đề có thể giải quyết được, nhưng không chạy đủ nhanh (mất một giây độ lớn để chạy; tôi cần một cái gì đó gần như tức thời.)

Đây có lẽ là lỗi của tôi, vì tôi đã không thực hiện mọi thứ chính xác như anh ấy đề xuất, mà thay vào đó là mã đơn giản hơn (mã nhanh hơn).

Điều đó nói rằng, tôi tìm thấy một giải pháp đơn giản, nhanh để chạy và dễ viết mã. Các bước chính là:

  • Giả sử phòng "bắt đầu" không phải là phòng lớn nhất, nhưng phòng người chơi sinh ra ở
  • Đối với mỗi phòng không được kết nối, hãy tìm phòng được kết nối gần nhất (O (n ^ 2))
  • Tạo một đường hầm kết nối hai phòng

Đây không phải là một cây bao trùm tối thiểu, vì vậy có những trường hợp thứ tự quan trọng - tôi lặp lại các phòng theo thứ tự sai, và cuối cùng kết nối chúng một cách dài hơn thay vì một cách ngắn hơn.

Đối với trò chơi và yêu cầu của tôi, điều này đã làm việc. Trong tương lai, tôi có thể mở rộng nó bằng cách sử dụng cây bao trùm tối thiểu thay thế.

Đối với hậu thế, đây là cùng một tầng được tạo ra với giải pháp của tôi.

nhập mô tả hình ảnh ở đây

Bạn có thể thấy một ảnh chụp nhanh của mã lõi trên GitHub, bắt đầu từ đây .


Yup, một cây bao trùm tối thiểu sẽ là tối ưu ở đây. Các giải pháp khác là không tốt.
AturSams
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.