2015-12-30 11 views
7

Przygotowuję skrypt do gry przeglądarkowej, która wygeneruje losowe zwierzę do walki z dowolnym graczem od 0 do 5. Oznaczenia na tym zwierzęciu są losowo generowane i są podawane do niestandardowej funkcji wyobrażania, która dodaje je w kolejności, w jakiej pojawiają się w tablicy.Sortowanie tablic z niepewnymi danymi

Oznakowania są wybierane losowo, istnieje wiele zasad określających sposób ich pojawiania się na zwierzęciu, na przykład oznaczenia w obszarze "całego ciała" pokazują powyższe oznaczenia w obszarze "brzusznym". Aby lepiej wyjaśnić, będę dołączyć zdjęcie testera do tej pory:

enter image description here

więc rozbić 5 oznaczenia na tym losowo generowanych zwierzęcia, eyeshadow znakowania należy do regionu oka, undertail należący do ogona , streaks należy do fullbody, appaloosa należy do tyłu, a okapi należy do nóg. Teraz kolejność jest dodawana po skasowaniu skryptu przez bazę danych i losowo wybranych oznaczeń, więc okapi (paski na nogach) jest na górze, ponieważ był ostatnim w tablicy, a ostatni dodany. Ale zgodnie z regułami porządkowania, ostatnia w tablicy powinna być smugami (poziomymi smugami w całym ciele), ponieważ oznaczenia pełnostanowe idą na wierzch.

Oto kod oznaczenia zaznaczane, to odbywa się za pomocą silnika laravel:

// Determine number of markings 
    $num = mt_rand(1,10); 

    if ($num == 1) { 
     $markingNum = 0; 
    } elseif ($num > 1 && $num < 4) { 
     $markingNum = 1; 
    } elseif ($num > 4 && $num < 6) { 
     $markingNum = 2; 
    } elseif ($num > 6 && $num < 8) { 
     $markingNum = 3; 
    } elseif ($num > 8 && $num < 10) { 
     $markingNum = 4; 
    } else { 
     $markingNum = 5; 
    } 

    // Calculate Marking type and color 
    $markings = array(); 

    if ($markingNum > 0) { 
     for ($m = 0 ; $m < $markingNum; $m++) { 
      // Set color values (pulls from the "pallet" selected earlier in the code, which will determine the range of color that marking can be) 
      if ($m == 1) { 
       $pal = $pallet->marking1; 
      } elseif ($m == 2) { 
       $pal = $pallet->marking2; 
      } elseif ($m == 3) { 
       $pal = $pallet->marking3; 
      } elseif ($m == 4) { 
       $pal = $pallet->marking4; 
      } else { 
       $pal = $pallet->marking5; 
      } 

      // Pull previous marking info 
      if (count($markings) != 0) { 
        $previous = DataMarking::whereIn('name', array_keys($markings))->get(); 

        // This pulls the regions of the current markings in the array so it won't select a region that already has a marking. 
        foreach ($previous as $p) { 
         $regions[$p->region] = $p->name; 
        } 

        // Uncommon marking (10% chance) 
        $r = mt_rand(1, 10); 

        if ($r == 10) { 
         $marking = DataMarking::where('rarity', 1) 
               ->where('public', 1) 
               ->whereNotIn('name', array_keys($markings)) 
               ->whereNotIn('region', array_keys($regions)) 
               ->orderByRaw("RAND()") 
               ->first(); 
        // Common markings 
        } else { 
         $marking = DataMarking::where('rarity', 0) 
               ->where('public', 1) 
               ->whereNotIn('name', array_keys($markings)) 
               ->whereNotIn('region', array_keys($regions)) 
               ->orderByRaw("RAND()") 
               ->first(); 
        } 

        // Colors marking 
        if ($pal == 0) { 
         $markingColor = rand_color(); 
        } else { 
         $range = ColorRange::where('id', $pal)->firstOrFail(); 
         $markingColor = "#" . mixRange(substr($range->start_hex, 1), substr($range->end_hex, 1)); 
        } 

        $markings[$marking->name] = $markingColor; 
       } else { 
       // Uncommon marking (10% chance) 
       $r = mt_rand(1, 10); 

       if ($r == 10) { 
        $marking = DataMarking::where('rarity', 1) 
              ->where('public', 1) 
              ->orderByRaw("RAND()")->first(); 
       // Common marking 
       } else { 
        $marking = DataMarking::where('rarity', 0) 
              ->where('public', 1) 
              ->orderByRaw("RAND()")->first(); 
       } 

       // Colors marking 
       if ($pal == 0) { 
        $markingColor = rand_color(); 
       } else { 
        $range = ColorRange::where('id', $pal)->firstOrFail(); 
        $markingColor = "#" . mixRange(substr($range->start_hex, 1), substr($range->end_hex, 1)); 
       } 

       $markings[$marking->name] = $markingColor; 
      } 
     } 
    } 

I rysunek można to osiągnąć z dużym kompleksem if, ale po prostu nie wydaje się elegancki rozwiązanie dla mnie. Ponadto istnieje wyjątek: "Gradient", oznaczenie pełne, znajduje się pod wszystkim, mimo że jest oznakowaniem całego ciała. Jak dotąd jest to jedyne oznakowanie z tego rodzaju wyjątkiem.

Próbowałem używać różnych funkcji sort oferowanych przez PHP, ale nie mam dużo szczęścia. uksort wydaje się być najbardziej obiecująca, ale ponieważ wartość, którą sortujemy, istnieje w bazie danych, a nie w tablicy, którą sortujemy (funkcja imagick musi być podawana jako znak => tablica kolorów), z czego trudno jest pracować.

Tl; dr: Muszę zmienić kolejność tablica, która niepewna ilość danych na podstawie wartości, które istnieją w DB dla kluczy (region dla oznaczeń). Jaki jest najbardziej elegancki sposób na osiągnięcie tego?

+0

Podoba mi się obraz z psem, nawet jeśli jest to dziwnie wyglądający pies. – Phiter

+0

Haha! To dziwny pies, prawda? –

+0

Wygląda na to, że potrzebujesz kolumny 'z-index' (int) w tabeli' DataMarking'. Ta właściwość działa jako głębokość. Więc '' cień do powiek' 'z-index' może wynosić na przykład 1, a' smugi' 'z-index' może wynosić 5. Im wyższy indeks' z-index', tym później zostanie narysowane znakowanie, co spowoduje "smugi" narysowane na wszystko inne. Po pobraniu tablicy oznaczeń, uporządkuj ją według "z-index" w porządku rosnącym. Następnie przeprowadź przez niego pętlę i narysuj wszystkie oznaczenia. – Alexander

Odpowiedz

4

Oto niektóre optymalizacje kodu, istnieje inline komentarze do opisania tego, co zostało zrobione. Oczywiście nie jest to skończone, ponieważ niektóre rzeczy, które Marcin wskazał w swojej odpowiedzi, byłyby lepsze.

// Determine number of markings 
    $num = mt_rand(1,10); 

    // Removed redundent $num > X as the conditions were already meet that it was > X by the previous if statement 
    if ($num == 1) { 
     $markingNum = 0; 
    } else if ($num < 4) { 
     $markingNum = 1; 
    } else if ($num < 6) { 
     $markingNum = 2; 
    } else if ($num < 8) { 
     $markingNum = 3; 
    } else if ($num < 10) { 
     $markingNum = 4; 
    } else { 
     $markingNum = 5; 
    } 

    // Calculate Marking type and color 
    $markings = array(); 

    if ($markingNum > 0) { 
     for ($m = 1 ; $m <= $markingNum; $m++) { // incrimented to 1 and <= so we can dynamically select elements 
      // Set color values (pulls from the "pallet" selected earlier in the code, which will determine the range of color that marking can be) 
      $pal = $pallet->{'marking' . $m}; // Removed if/else and replaced with a dynamic variable 


      // Uncommon marking (10% chance) 
      $r = mt_rand(1, 10); 

      // removed duplicate database selections for a simple $rarity variable that accomplishes the same task 
      if ($r == 10) { 
       $rarity = 1; 
      } else { 
       $rarity = 0; 
      } 

      $marking = DataMarking::where('rarity', $rarity) 
            ->where('public', 1) 
            ->whereNotIn('name', array_keys($markings)) 
            ->whereNotIn('region', $regions) 
            ->orderByRaw("RAND()") 
            ->first(); 

      // Colors marking 
      if ($pal == 0) { 
       $markingColor = rand_color(); 
      } else { 
       $range = ColorRange::where('id', $pal)->firstOrFail(); 
       $markingColor = "#" . mixRange(substr($range->start_hex, 1), substr($range->end_hex, 1)); 
      } 

      $markings[$marking->name] = $marking; // adds all of the marking data, this is where you could have a z-index in the database 
      $markings[$marking->name] = $markingColor; // add your color to your marking data 
      $regions[] = $marking->region; 

     } 
    } 
2

Nie odpowiem na twoje pytanie, ale patrząc na twój kod, jest dużo miejsca na ulepszenia.

Rozważ to:

if ($m == 1) {  
    $pal = $pallet->marking1; 
} elseif ($m == 2) { 
    $pal = $pallet->marking2; 
} elseif ($m == 3) { 
    $pal = $pallet->marking3; 
} elseif ($m == 4) { 
    $pal = $pallet->marking4; 
} else { 
    $pal = $pallet->marking5; 
} 

To może być zmieniony na coś znacznie prostszego:

$pa1 = (in_array($m,range(1,4))) ? $pallet->marking{$m} : $pallet->marking5; 

sama dla:

if ($r == 10) { 

    $marking = DataMarking::where('rarity', 1) 
          ->where('public', 1) 
          ->whereNotIn('name', array_keys($markings)) 
          ->whereNotIn('region', array_keys($regions)) 
          ->orderByRaw("RAND()") 
          ->first(); 
// Common markings 
} else { 
    $marking = DataMarking::where('rarity', 0) 
          ->where('public', 1) 
          ->whereNotIn('name', array_keys($markings)) 
          ->whereNotIn('region', array_keys($regions)) 
          ->orderByRaw("RAND()") 
          ->first(); 
} 

czy może być przepisany do:

$marking = DataMarking::where('rarity', ($r == 10) ? 1 : 0) 
         ->where('public', 1) 
         ->whereNotIn('name', array_keys($markings)) 
         ->whereNotIn('region', array_keys($regions)) 
         ->orderByRaw("RAND()") 
         ->first(); 

Oczywiście powyżej ORDER BY RAND() może nie być najlepszym rozwiązaniem ze względu na wydajność.

Należy naprawdę dbają o ilości kodu i powielanie lub wkrótce zostaną utracone w to, co robisz

+0

Bardzo dziękuję za porady! –