Xác định xem một hình dạng được hình thành từ một loạt các hình dạng khác là 'không bị phá vỡ'


7

Tôi có một lưới các ô trong một trò chơi và mỗi ô có thể có một hình dạng đơn giản nhất định trên đó.

Tôi muốn có thể xác định xem hình dạng kết hợp từ bất kỳ sự sắp xếp nhất định nào của các ô này có dẫn đến hình dạng lớn hơn 'không bị phá vỡ' hoặc 'bị phá vỡ' hay không.

Ví dụ; đây là một ví dụ về hình dạng 'không bị phá vỡ' ...

Đây là một ví dụ về hình dạng không bị phá vỡ

Và đây là một ví dụ về hình dạng 'bị hỏng' ...

Đây là một ví dụ về hình dạng bị hỏng

Tôi sẽ có thể viết mã cần thiết một khi tôi tìm ra logic của vấn đề này, nhưng cho đến nay tôi đã phải vật lộn.

Tôi đã thử các bảng dữ liệu được liên kết với từng ô mô tả các cạnh của nó, ví dụ:

var edges = {
    top : false,
    right : true,
    bottom : true,
    left : false
}

Và từ đó tôi có thể xác định xem mỗi ô có được 'kết nối' với hàng xóm của nó không. Nhưng điều này không giúp ích cho tất cả các tình huống.

EDIT 1: Nhiều cụm hình dạng 'không bị phá vỡ' bị cô lập sẽ được coi là hình dạng 'bị hỏng' ...

Nhiều cụm hình dạng 'không bị phá vỡ' bị cô lập

EDIT 2:

Ngoài ra, hình dạng 'không bị phá vỡ' có thể đạt được nếu cạnh của hình dạng không được kết nối với mọi ô lân cận, nó có thể chỉ là một kết nối. Ví dụ: tôi sẽ coi đây là 'không bị phá vỡ' ...

hình dạng 'không bị phá vỡ' có thể đạt được nếu cạnh của ô không được kết nối với mọi ô lân cận

Bất cứ ai có bất kỳ ý tưởng về điều này?


"Nhưng điều này không giúp ích cho tất cả các tình huống." Bạn có một ví dụ về tình huống xác định kết nối với hàng xóm là không đủ? Có vẻ như một vấn đề thành phần được kết nối có thể được giải quyết bằng tìm kiếm theo chiều sâu / chiều rộng đầu tiên.
DMGregory

Câu trả lời:


8

Đây là một vấn đề thành phần được kết nối cơ bản và có thể được giải quyết bằng một lần gọi đầu tiên theo chiều rộng hoặc tìm kiếm theo chiều sâu hoặc bất kỳ thuật toán lấp đầy nào.

bool IsOneSolidShape(TileCollection tiles)
{
    // Clear visited flags on all tiles.
    // (This can also be implemented as a temporary set/map or parallel array,
    // if you prefer not to clutter your Tile type with book-keeping data)
    foreach(var tile in tiles)
        tile.IsVisited = false;

    // Prepare a container to hold tiles whose neighbours we haven't searched yet.
    // With a stack, this is depth-first search. A queue gives breadth-first.
    var pending = new stack<Tile>();

    // Seed the search with some start tile - this choice is arbitrary.
    var startTile = tiles.GetSomeNonEmptyTile();
    startTile.IsVisited = true;
    pending.Push(startTile);
    int numVisited = 1;

    // Search for as long as we have unexplored tiles in our stack:
    while(pending.Count > 0)
    {
         var tile = pending.Pop();
         foreach(var neighbour in tile.GetNeighbours())
         {
             // Skip over tiles we've already reached.
             if(neighbour.IsVisited)
                continue;

             // Check for a valid connection between this tile and this neighbour.
             if(tile.IsConnectedTo(neighbour))
             {
                // This tile is connected.
                // Mark it visited so we don't double-count it later.
                neighbour.IsVisited = true;

                // Add it to our reached set.
                numVisited++;

                // Keep note of it so we can search its neighbours.
                pending.Push(neighbour);
             }
         }
    }

    // If we have reached every non-empty tile,
    // then they are all in one connected shape.
    return numVisited == tiles.GetNonEmptyTileCount();
}

Mã ở trên chỉ cho bạn biết nếu bạn có chính xác một nhóm.

Nếu bạn muốn đếm có bao nhiêu nhóm, bạn có thể thực hiện bằng cách gói vòng lặp while bên ngoài trong một thời gian (numVisited <gạch.GetNonEmptyTileCount ()), mỗi lần gieo hạt vòng lặp bên trong bằng một ô không bị chặn mới và tăng nhóm của bạn đếm.

Lưu ý rằng tile.IsConnecedTo(neighbour)sẽ trả về true khi và chỉ khi cả hai tileneighbourkết nối dọc theo cạnh được chia sẻ của chúng. (ví dụ: nếu tilekết nối với cạnh được chia sẻ, nhưng neighbourkhông, nó sẽ trả về false)

Chỉnh sửa: đây là phiên bản đệ quy:

bool IsOneSolidShape(TileCollection tiles)
{
    // Clear visited flags on all tiles.
    // (This can also be implemented as a temporary set/map or parallel array,
    // if you prefer not to clutter your Tile type with book-keeping data)
    foreach(var tile in tiles)
        tile.IsVisited = false;

    // Seed the search with some start tile - this choice is arbitrary.
    var startTile = tiles.GetSomeNonEmptyTile();

    // Recursively search all tiles reachable from startTile.
    int numVisited = CountConnected(startTile);

    // If we have reached every non-empty tile,
    // then they are all in one connected shape.
    return numVisited == tiles.GetNonEmptyTileCount();
}

int CountConnected(Tile tile)
{
   // Initialize connected set to include "myself."
   tile.IsVisited = true;
   int connected = 1;

   // Check for connections to neighbours.
   foreach(Tile neighbour in tile.GetNeighbours())
   {
      // Skip tiles we've already visited, to avoid double-counting.
      if(neighbour.isVisited)
         continue;

      // Recursively search any neighbour we're connected to)
      if(tile.IsConnectedTo(neighbour))
         connected += CountConnected(neighbour);
   }

   return connected;
}

Điều này có vẻ đầy hứa hẹn. Tôi chỉ đang cố gắng tách nó ra và dịch nó sang mã LUA của mình để kiểm tra nó ...
juliusbangert

Bạn cũng có thể thực hiện điều này theo cách đệ quy, thay vì sử dụng ngăn xếp. Tôi đã tham gia khóa học thuật toán trong đó chúng tôi đang xử lý các bộ dữ liệu trong hàng nghìn và hàng triệu, vì vậy tôi có thói quen sử dụng các hình thức lặp để tránh giới hạn đệ quy, nhưng đó không phải là vấn đề đối với các trường hợp bạn ' xử lý lại. ;)
DMGregory

Bạn có một ví dụ về việc thực hiện đệ quy này không? Nó sẽ giúp tôi trong việc chuyển LUA / Corona của tôi. Một phần trong tôi nghĩ rằng nó sẽ không hoạt động với một cái gì đó giống như ví dụ cuối cùng mà tôi đã đưa vào EDIT 2 cho câu hỏi của mình. Nhưng tôi rất muốn thử nó.
juliusbangert

1
@juliusbangert, tôi đã thêm một ví dụ đệ quy. Tôi cũng có thể xác nhận rằng thuật toán tìm kiếm các thành phần được kết nối sẽ phân loại chính xác ví dụ bạn đã chia sẻ trong EDIT 2. Chỉ cần đảm bảo IsConnectedTo () kiểm tra cả hai ô dọc theo cạnh được chia sẻ, nếu không bạn sẽ gặp một số lỗi.
DMGregory

1
+1 để đề cập đến loại thuật toán sẽ sử dụng (biểu đồ truyền tải) thay vì chỉ đưa ra giải pháp cho tình huống cụ thể của OP.
Kevin

3

Ghi lại các đỉnh như các đường trên các cạnh nơi một điểm hình dạng nằm. Chúng sẽ được sử dụng để khớp với các điểm / đường trên các ô khác mỗi khi thay đổi ô.

Ví dụ: nhập mô tả hình ảnh ở đây

Phía trên các đường màu xanh lam trên hai ô trên cùng, A và B, đã được tính / lưu cho mỗi cạnh của ô theo hình dạng mà nó chứa (lưu ý ngay cả khi chúng ở các cạnh khác nhau, bạn có thể trích xuất dòng để so sánh bằng số nguyên đơn giản các giá trị nằm trong khoảng từ 0 -> X.)

Trong ví dụ bị hỏng, các dòng không khớp nhau để bạn có thể nói hình ảnh bị hỏng. Lưu trữ dữ liệu dưới dạng thông tin bổ sung trong ô để sử dụng để so sánh. Nếu bạn có một số loại cạnh được đặt (cạnh đầy đủ, nửa cạnh, v.v.) thì bạn thậm chí có thể tạo một bảng liệt kê cho các cạnh A, B, C và D trên mỗi ô và so sánh một cách thích hợp.

Trong ví dụ của chúng tôi, các cạnh của chúng tôi có thể được so sánh từ các giá trị 0 -> 10.

var exampleTileA = {
    top : undefined,
    right : {0,10},
    bottom : {0,10},
    left : undefined
}

var exampleTileB = {
    top : undefined,
    right : undefined,
    bottom : {0,10},
    left : {0,10}
}

var exampleTileC = {
    top : {5,10},
    right : {5,10},
    bottom : {0,5},
    left : {0,5}
}

function compareLeftRight(leftTile, rightTile){
    return leftTile.right === rightTile.left;
}

console.log(compareLeftRight(exampleTileA, exampleTileB));
console.log(compareLeftRight(exampleTileA, exampleTileC));

//// Output
//  true
//  false

Để tiếp cận các cụm hình dạng không bị phá vỡ, bạn có thể chỉ cần lặp qua từng ô và kiểm tra xem nó có được kết nối với bất kỳ ô lặp nào trước đó không:

var tiles = [tileA, tileB, ...];
var tempTiles = tiles;

// Loop through each tile and remove it from the temp
// tile array if it is connected to any of the previous tiles
for (var tile in tiles) {
     var currentIndex = tiles.indexOf(tile);

     for(var i = 0; currentIndex > i; i++) {
         if ( tileA.connectedTo(tile)) {             
             // remove from temp tiles
             var index = tempTiles.indexOf(tile);
             if (index > -1) {
                 tempTiles.splice(index, 1);
             }
         }
     }
}

if (tempTiles.length > 0) {
    // Some tile was not connected to the group
    console.log("There are " + tempTiles.length + " tiles not connected to the group");
}

Cảm ơn Blue, tôi đã làm điều này nhưng điều này chỉ xác định nếu một ô được kết nối với hàng xóm của nó. Khi có nhiều 'cụm' hình dạng 'không bị phá vỡ' bị cô lập, tôi sẽ coi đó là hình dạng 'bị hỏng'. Nhưng tôi không chắc làm thế nào để nhặt cái này lên ...
juliusbangert

Thay vì lưu trữ thông tin đỉnh cạnh, bạn cũng có thể lưu trữ một số nguyên mô tả loại cạnh đó - về cơ bản là một loại cạnh enum. Đi theo tuyến đường này, bạn có thể kiểm tra các loại cạnh tương thích với ít bộ nhớ hơn và bạn có thể có các quy tắc khớp phức tạp hơn bao gồm màu sắc, họa tiết, hình động, độ dốc của đường tại điểm giao nhau, v.v. Bạn thậm chí có thể làm cho nó có cạnh phù hợp thay vì chỉ một trận đấu duy nhất trên mỗi cạnh.
Alan Wolfe

@AlanWolfe Tôi đã làm chính xác điều đó nhưng với các giá trị từ 0 đến 1: local edges = { top = nil, right = nil, bottom = { 0, 1 }, left = { 0, 0.5 } }(bằng lua) Nhưng vấn đề tôi gặp phải là xác định xem trên tổng thể, tôi có một hoặc nhiều hình dạng 'không bị phá vỡ'
juliusbangert

Được cập nhật với một số logic giả để thêm chức năng cho nhóm gạch.
Tom 'Blue' Piddock

0

Có vẻ như tất cả những gì bạn đang tìm kiếm là để xem liệu có BẤT K edge cạnh nào trong gạch của bạn và nhận lại được đúng hay sai.

Nếu đó là trường hợp, tất cả những gì bạn cần làm là nhìn vào mọi vị trí gạch nối với nhau (trên trục x và trục y) và xem các cạnh có tương thích không.

Bạn sẽ lặp cho đến khi bạn tìm thấy một cạnh bị hỏng (và bạn sẽ trả về true) hoặc cho đến khi bạn hết các cạnh để kiểm tra (và bạn sẽ trả về false).

Về cơ bản, nó sẽ là một vòng lặp for trong một vòng lặp for (một vòng sẽ là trục X, vòng kia sẽ là trục Y) vì vậy sẽ là thuật toán N ^ 2.

Có phải đó là những gì bạn đang theo đuổi? Không giúp đỡ à?


Vì không phải tất cả các cạnh của một ô cần được kết nối (một là đủ) câu trả lời của bạn sẽ không hoạt động.
zoran404
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.