Слияние подмассивов с общими элементами в многомерном массиве - PullRequest
0 голосов
/ 13 февраля 2012

Я работаю над игрой на PHP. У меня есть система, которая может случайным образом генерировать карты с путями в них. Вот пример:

http://i1244.photobucket.com/albums/gg579/Xeniar117/Map.png

(Я обновил этот пост, не имея достаточного количества повторений для этого изображения, чтобы оно оставалось непосредственно на странице, которую кто-то другой сделал возможным с помощью своего представителя, извините)

Каждой ячейке карты присвоена четырехзначная строка, обозначающая путешествие (W, N, E, S). Таким образом, ячейка 11 на карте выше будет иметь строку 1110, а ячейка 17 будет иметь строку 1001. Эта строка (она же NavString) генерируется случайным образом в рамках правил, чтобы предотвратить отправку игроков с карты.

Любые ячейки, имеющие только одно направление движения, помещаются в $ endcaps (массив). Кроме того, любые ячейки с 3 и 4 направлениями перемещения помещаются в $ endcaps, но с таким же количеством входов в $ endcaps, как и направления перемещения (например, 3-стороннее пересечение получает 3 входа в $ endcaps) Ячейки, имеющие только 2 направления, не добавляются в Это потому, что у них есть 1 вход и только 1 другой выход, так что это прямой путь внутрь и наружу; ни разветвлений, ни тупиков.

Я выполняю цикл через $ endcaps, чтобы получить начальную точку для каждого раздела пути, который я хочу записать. Используя NavString, я следую по каждому пути из ячейки, собранной из $ endcaps, пока не доберусь до другой ячейки, которая находится в $ endcaps (используя in_array ($ endcaps)), все время записывая номера ячеек, проходящих через нее, в $ paths [$ path ] [$ шаг]. Каждый новый $ endcap I $ path ++ и каждая новая ячейка между $ endcaps I $ step ++.

Вот пример того, как выглядит $ paths после того, как он выходит из цикла: [Примечание: карта на изображении - это то, откуда появился этот 2D-массив]

Array
(
[0] => Array
    (
        [0] => 4
        [1] => 9
        [2] => 14
    )

[1] => Array
    (
        [0] => 6
        [1] => 5
        [2] => 10
        [3] => 11
    )

[2] => Array
    (
        [0] => 6
        [1] => 1
        [2] => 2
        [3] => 7
    )

[3] => Array
    (
        [0] => 6
        [1] => 7
    )

[4] => Array
    (
        [0] => 6
        [1] => 11
    )

[5] => Array
    (
        [0] => 7
        [1] => 6
    )

[6] => Array
    (
        [0] => 7
        [1] => 2
        [2] => 1
        [3] => 6
    )

[7] => Array
    (
        [0] => 7
        [1] => 12
        [2] => 11
    )

[8] => Array
    (
        [0] => 8
        [1] => 13
    )

[9] => Array
    (
        [0] => 11
        [1] => 10
        [2] => 5
        [3] => 6
    )

[10] => Array
    (
        [0] => 11
        [1] => 6
    )

[11] => Array
    (
        [0] => 11
        [1] => 12
        [2] => 7
    )

[12] => Array
    (
        [0] => 13
        [1] => 8
    )

[13] => Array
    (
        [0] => 13
        [1] => 14
    )

[14] => Array
    (
        [0] => 13
        [1] => 18
        [2] => 19
        [3] => 14
    )

[15] => Array
    (
        [0] => 14
        [1] => 13
    )

[16] => Array
    (
        [0] => 14
        [1] => 9
        [2] => 4
    )

[17] => Array
    (
        [0] => 14
        [1] => 19
        [2] => 18
        [3] => 13
    )

[18] => Array
    (
        [0] => 16
        [1] => 17
        [2] => 22
        [3] => 23
    )

[19] => Array
    (
        [0] => 23
        [1] => 22
        [2] => 17
        [3] => 16
    )

)

Я хочу сравнить одинаковые значения в под-массивах с другими под-массивами в $ paths. Если найдено совпадение 1+, я хочу объединить два массива вместе и продолжить поиск подмассивов с хотя бы одним общим значением, НО мне нужно разрешить добавление любых новых дополнений в сравнение.

Так что в идеале это должно объединиться в двумерный массив только с 3 подмассивами после того, как все сказано и сделано. требуемый результат вышеприведенного примера должен выглядеть следующим образом:

array (
    array(1,2,5,6,7,10,11,12)
    array(4,9,14,8,13,18,19)
    array(16,17,22,23)
)

Вышеуказанное будет отличным представлением путей, которые я затем смогу использовать, чтобы убедиться, что вход и выход соединены, и игрок не попадет в ловушку!

Итак, мой вопрос: как я могу уточнить пути $ (в первом блоке кода) до массива во 2-м блоке кода? Помните, что эти карты генерируются случайным образом, поэтому $ paths может иметь любое количество под-массивов, а под-массивы могут иметь любое количество элементов, которые могут быть любым числом ячеек (в зависимости от того, как система генерировала пути)

Надеюсь, этого достаточно, чтобы вы, ребята, помогли мне, в прошлый раз, когда я спросил, как это сделать, они сказали, что я слишком расплывчатый (и после некоторого размышления над этим я согласен). Если его недостаточно, хорошо ...


ОБНОВЛЕНИЕ То, что я пробовал до сих пор

$set=0;
foreach($paths as $loop1key=>$loop1) {
  foreach($paths as $loop2key=>$loop2) {
    if(count(array_intersect($loop1,$loop2)) > 0) {
      $consolidated = array_merge($loop1,$loop2);
      unset($paths[$loop2key],$paths[$loop1key]);
      $loop1 = $consolidated;
    }
  }
  $after[$set]=$consolidated;
  $set++;
}

И (предложено @hackerartist):

$after = array();
foreach($paths as $numArray) {
  $addedKey = -1;
  foreach ($after as $key=>$conArray) {
    if ($addedKey<0&&count(array_intersect($conArray,$numArray))){
      $a‌​fter[$key]=array_unique(array_merge($after[$key],$numArray));
      $addedKey = $key;
    }
    if ($addedKey>=0 && count(array_intersect($after[$addedKey],$numArray))) {
      $after[$addedKey] = array_unique(array_merge($after[$addedKey],$after[$key]));
      unset($after[$key]);
    }
  }
  ‌​if ($addedKey<0) {
    $after[] = $numArray;
  }
}


ОБНОВЛЕНИЕ # 2 Я подправил свою вторую попытку (та, что прямо здесь ^^^), и она, кажется, работает 99% времени, однако все еще не удается объединить некоторые участки пути в их правильную группировку. Весь мой исходный код ниже с твиком, окруженным комментариями. (Строки 141-158 в соответствии с PHP Expert Editor)
<?php
$rows = 5;//Set statically to test, will be random in final version
$cols = 5;//Same as rows, will eventually be a random value
$gridsize = $rows * $cols;

for($r=0;$r<$rows;$r++){//Loop as many times as $rows
    for($c=0;$c<$cols;$c++){//Loop as many times as $cols
        $cell = ($r * $cols) + $c;//Current cell ((Current row * Total Columns) + Current Column)
        if($r == 0 && $c > 0 && $c < ($cols - 1)){//North edge cell
            $west = $cells[$cell - 1][2];
            $east = rand(0,1);
            $south = rand(0,1);
            $string = $west."0".$east.$south;
        } elseif ($r > 0 && $r < ($rows - 1) && $c == 0){//West Edge
            $north = $cells[$cell - $cols][3];
            $east = rand(0,1);
            $south = rand(0,1);
            $string = "0".$north.$east.$south;
        } elseif ($c == ($cols - 1) && $r > 0 && $r < ($rows - 1)){//East Edge
            $west = $cells[$cell - 1][2];
            $north = $cells[$cell - $cols][3];
            $south = rand(0,1);
            $string = $west.$north."0".$south;
        } elseif ($r == ($rows - 1) && $c > 0 && $c < ($cols - 1)){//South Edge
            $west = $cells[$cell - 1][2];
            $north = $cells[$cell - $cols][3];
            $east = rand(0,1);
            $string = $west.$north.$east."0";
        } else {//Either a Corner or Core Cell
            switch($cell){
                case '0'://NW Corner
                    $east = rand(0,1);
                    $south = rand(0,1);
                    $string = "00".$east.$south;
                break;
                case ($cols - 1)://NE Corner
                    $west = $cells[$cell - 1][2];
                    $south = rand(0,1);
                    $string = $west."00".$south;
                break;
                case ($gridsize - $cols)://SW Corner
                    $north = $cells[$cell - $cols][3];
                    $east = rand(0,1);
                    $string = "0".$north.$east."0";
                break;
                case ($gridsize - 1)://SE Corner
                    $west = $cells[$cell - 1][2];
                    $north = $cells[$cell - $cols][3];
                    $string = $west.$north."00";
                break;
                default://Core cell
                    $west = $cells[$cell - 1][2];
                    $north = $cells[$cell - $cols][3];
                    $east = rand(0,1);
                    $south = rand(0,1);
                    $string = $west.$north.$east.$south;
                break;
            }
        }
        for($x=0;$x<4;$x++){
            if($string[$x]==1){
                $totalnav++;
                $dirs[]=$x;
            }
        }
        if($totalnav == 1 || $totalnav > 2){
            $endcaps[$cell]=$dirs;
        }
        unset($totalnav);
        unset($dirs);
        $cells[] = $string;
    }
}


//Determine center
foreach($cells as $cellnum=>$cell){
    $center = $cell[0]+$cell[1]+$cell[2]+$cell[3];
    if($center > 0){$cell .= "10000";}
    else{$cell.="00000";}
    $blocks[]=$cell;
}

//Refine rooms
for($r=0;$r<$rows-1;$r++){
    for($c=0;$c<$cols-1;$c++){
        $nw = ($r * $cols) + $c;//Current Cell Number
        $ne = $nw + 1;
        $sw = $nw + $cols;
        $se = $ne + $cols;
        $loop = 0;
        if($blocks[$nw][2]==1&&$blocks[$nw][3]==1){$loop++;}
        if($blocks[$ne][0]==1&&$blocks[$ne][3]==1){$loop++;}
        if($blocks[$sw][1]==1&&$blocks[$sw][2]==1){$loop++;}
        if($blocks[$se][0]==1&&$blocks[$se][1]==1){$loop++;}

        if($loop == 4){//Loop detected, creating room
            $blocks[$nw][8]=1;
            $blocks[$ne][7]=1;
            $blocks[$sw][6]=1;
            $blocks[$se][5]=1;
            $loop=0;
        }
    }
}

$path=0;
$oppnodes = array(0=>array(2,-1),1=>array(3,($cols*-1)),2=>array(0,1),3=>array(1,$cols));
foreach($endcaps as $cellnumber=>$directions){
    foreach($directions as $direction){
        $step=0;
        $paths[$path][$step++]=$cellnumber;
        $nextcell = $cellnumber + $oppnodes[$direction][1];
        $nextnode = $oppnodes[$direction][0];
        $end=0;
        while($end==0){
            if(!isset($endcaps[$nextcell])){
                //Pass the torch from last to current
                $currentcell = $nextcell;
                $currentnode = $nextnode;

                $nav = substr($cells[$currentcell],0,4);//Grab nav string from current cell.
                $nav[$currentnode]=0;//Disable opposite node from direction last traveled
                for($x=0;$x<4;$x++){
                    if($nav[$x]=="1"){
                        $nextnode = $oppnodes[$x][0];
                        $nextcell = $currentcell + $oppnodes[$x][1];
                    }
                }
                $paths[$path][$step]=$currentcell;
            } else {
                $paths[$path][$step]=$nextcell;
                $end=1;
            }
            $step++;
        }
        $path++;
    }
}

//Problem area start ===================================================
$after = array();
foreach($paths as $arrayOfNumbers) {
    $addedKey = -1;
    foreach($after as $key=>$newArrayOfNumbers){
        if($addedKey < 0 && count(array_intersect($arrayOfNumbers,$newArrayOfNumbers))){
            $after[$key] = array_unique(array_merge($arrayOfNumbers,$newArrayOfNumbers));
            $addedKey = $key;
        } elseif($addedKey >= 0 && count(array_intersect($after[$key],$newArrayOfNumbers))){
            $after[$key] = array_unique(array_merge($after[$key],$newArrayOfNumbers));
            unset($paths[$key]);
        }
    }
    if($addedKey<0){
        $after[] = $arrayOfNumbers;
    }
}
//Problem area end ===================================


echo"<table>Array Representation of Paths In Map
";print_r($after);echo"
"; echo "
$ cellnum
"; echo ""; следующие ($ блоки); foreach ($ directions как $ direction) {unset ($$ direction);} } echo ""; } echo ""; переменные не установлены ($ блоки); echo ""; снята с охраны ($ после); ?>

Я не прошу вас проверить весь мой код, а просто проблемную область, чтобы узнать, сможете ли вы выяснить, почему он так часто пропускает консолидацию. Размещение всего источника кажется наиболее логичным способом, позволяющим вам увидеть реальные результаты, которые я получаю. Источник не требует никаких внешних источников и не требует специального имени файла. Я просто не могу понять, почему он работает в 99% случаев без каких-либо проблем, но в 1/100 раза он не сможет объединить раздел пути в свою правильную группу. Конечно, я продолжу исследовать самостоятельно, но любой совет будет признателен. Спасибо!


ОБНОВЛЕНИЕ № 3 Так близко!

Я возился с некоторым кодом, предложенным хакерартистом здесь, в StackOverflow, который с некоторыми изменениями работает так, как я хочу, в 99% случаев. К сожалению, 99% не приемлемо. Предполагается, что код объединяет подмассивы с любыми общими элементами с любыми другими подмассивами в многомерном массиве, например так:

$array = array(
    array(6,5,0,1,6),
    array(6,11),
    array(11,10,15,16,17,12),
    array(11,12),
    array(12,13),
    array(18,19,14),
    array(7,8,3,4)
);

В такой массив:

$array = array(
    array(0,1,5,6,11,10,15,16,17,12,13),
    array(18,19,14),
    array(7,8,3,4)
);

Вот код, который я использую, чтобы попытаться выполнить это: $ paths будет $ массивом выше.

for($x=0;$x<count($paths);$x++){//Loop through paths
                $paths = array_values($paths);//Reset keys
                for($y=0;$y<count($paths);$y++){//Loop through again
                    if($x != $y){//If we arent on the same array
                        if(count(array_intersect($paths[$x],$paths[$y]))){//If there is even 1 matching element
                            $paths[$x] = array_unique(array_merge($paths[$x],$paths[$y]));//Merge arrays
                            unset($paths[$y]);//Remove array just merged
                        }
                    }

                }
            }

Существует также измененная версия предложенного хакерартистом решения:

$ paths2 будет массивом, подобным $ array выше

foreach($paths2 as $arrayOfNumbers) {
                $addedKey = -1;
                foreach($after as $key=>$newArrayOfNumbers){
                    if($addedKey < 0 && count(array_intersect($arrayOfNumbers,$newArrayOfNumbers))){
                        $after[$key] = array_unique(array_merge($arrayOfNumbers,$newArrayOfNumbers));
                        $addedKey = $key;
                    } elseif($addedKey >= 0 && count(array_intersect($after[$key],$newArrayOfNumbers))){
                        $after[$key] = array_unique(array_merge($after[$key],$newArrayOfNumbers));
                        unset($paths2[$key]);
                    }
                }
                if($addedKey<0){
                    $after[] = $arrayOfNumbers;
                }
            }

Теперь они могут работать для приведенного мною примера, но когда вы используете систему, которую я создал, для генерации массивов, с которыми должны работать эти коды, как я уже сказал, они пропускают один или два массива, которые должны быть объединены в еще один с общим элементом (ами) каждые 1 из 100 раз (оценка !!!) Я передаю другой массив с разными элементами в каждом подмассиве через код.

Кто-нибудь знает, что я делаю неправильно и как исправить этот недостаток?

1 Ответ

0 голосов
/ 16 февраля 2012

Возможно, это не самое элегантное решение, но оно работает 100% времени. Проще говоря, я дважды провожу его через уточняющий код, используя выходные данные первого раза, второй раз и затем возвращаю его из функции во второй раз. Повторный вызов встроен в саму функцию (так что я предполагаю, что это означает, что это рекурсивная функция, верно?) В любом случае, вот пример массива, который не будет детализирован полностью (он пропускает слияние):

$array= Array
(
    [0] => Array(
            [0] => 1,
            [1] => 0,
            [2] => 5,
            [3] => 6,
            [4] => 11),
   [3] => Array(
            [0] => 10,
            [1] => 11),
    [4] => Array(
            [0] => 11,
            [1] => 12),
    [5] => Array(
            [0] => 12,
            [1] => 17),
    [6] => Array(
            [0] => 15,
            [1] => 16)
    [7] => Array(
            [0] => 17,
            [1] => 18,
            [2] => 23,
            [3] => 22),
    [8] => Array(
            [0] => 17,
            [1] => 22),
    [9] => Array(
            [0] => 21,
            [1] => 22));

Пройдя один раз, произвел:

$array = Array(
    [0] => Array(
            [0] => 21,
            [1] => 22,
            [2] => 17,
            [4] => 18,
            [5] => 23,
            [6] => 12,
            [7] => 11,
            [8] => 10,
            [9] => 1,
            [10] => 0,
            [11] => 5,
            [12] => 6),

    [1] => Array(
            [0] => 7,
            [1] => 12,
            [2] => 2,
            [3] => 3,
            [4] => 4,
            [5] => 9,
            [6] => 8),

    [2] => Array(
            [0] => 15,
            [1] => 16));

Это не совсем правильно, так как $ array [0] [6] и $ array [1] [1] равны 12, и массивы, в которых они находятся, должны были быть объединены, но это не так. Я не уверен, почему это не получается, но я полагаю, что простое решение - снова запустить его, и это работает. Вот выполненная функция:

function linkSegments($paths=array(),$ranthru=1){
               if(is_empty($paths)){
                 echo "No segments to link!";die;
               } else {
            for($x=0;$x<count($paths);$x++){//Loop through paths
                $paths = array_values($paths);//Reset keys
                for($y=0;$y<count($paths);$y++){//Loop through again
                    if($x != $y){//If we arent on the same array
                        if(count(array_intersect($paths[$x],$paths[$y]))){//If there is even 1 matching element
                            $paths[$x] = array_unique(array_merge($paths[$x],$paths[$y]));//Merge arrays
                            unset($paths[$y]);//Remove array just merged
                        }
                    }

                }
            }
            if($ranthru == 1){
                $paths = linkSegments($paths,$ranthru=2);
                return $paths;//Set path data to class
            } else {
                return $paths;
            }
               }
        }

Вызов $ refinedArray = linkSegments ($ array) [используя $ array из первого блока кода] даст правильный результат:

$array = Array(
    [0] => Array(
            [0] => 21,
            [1] => 22,
            [2] => 17,
            [4] => 18,
            [5] => 23,
            [6] => 12,
            [7] => 11,
            [8] => 10,
            [9] => 1,
            [10] => 0,
            [11] => 5,
            [12] => 6,
            [13] => 7,
            [14] => 8,
            [15] => 9,
            [16] => 2,
            [17] => 3,
            [18] => 4),

    [1] => Array(
            [0] => 15,
            [1] => 16));

И если вам известен более эффективный способ сделать это, пожалуйста, скажите мне! Это для игры, так что чем быстрее я смогу это сделать, тем лучше: D

...