Sort arrays with undefined data

I am creating a script for a game in a browser that will generate a random animal for the player to fight anywhere from 0-5 markup. The markings on this animal are randomly generated and fed into the imagick user-defined function, which adds them in order as they appear in the array.

While the marking is randomly determined, there are many rules for how they should appear on the animal, for example, marking in the "full body" area shows the marks above in the "stomach" area. To better explain, I will show a tester image:

enter image description here

Thus, in order to break 5 tags into this randomly generated animal, the eyeshadow marking belongs to the eye area, undertail belongs to the tail, streaks belongs to the fullbody, appaloosa belongs to the back and okapi belongs to the legs. Now the order is simply added when the script is looped over the database and randomly selected labels, so okapi (stripes on the legs) is on top, since it was the last in the array and the last one was added. But following the rules of the order, the last one in the array should have been stripes (horizontal stripes all over the body), since the pop-up inscriptions come from above.

Here is the code that selects the marking, this is done using the Laravel engine:

  // 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 suppose I can accomplish this with many complex if statements, but that just doesn't seem like an elegant solution for me. In addition, there is an exception: “Gradient”, a full label, falls under everything, even if it is a full body label. So far this is the only marking with a similar exception.

I tried using the various sort functions offered by PHP, but I was not very lucky. uksort seems to be the most promising, but since the value that we sort exists in the database, and not in the array that we sort (the imagick function must be supplied in the marking format => color matrix), which is difficult to work with.

Tl; dr: I need to reorder an array in which an indefinite amount of data is based on the values ​​existing in the database for keys (area for marking). What is the most elegant way to achieve this?

+7
sorting arrays php random laravel
source share
2 answers

Here are some optimizations to your code, there are comments to describe what has been done. This, obviously, is not finished, as in his answer Marcin indicated that it would be better.

  // 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; } } 
+4
source share

I will not answer your question, but looking at your code there are many opportunities for improvement.

Consider this:

 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; } 

It could be changed for something much simpler:

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

The same goes for:

 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(); } 

if it can be rewritten to:

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

Of course, above ORDER BY RAND() may not be the best solution due to performance reasons.

You really need to take care of the amount of your code and duplication, or you will soon be lost in what you do.

+2
source share

All Articles