I am crafting a script for a browser game that will generate a random animal for the player to battle with anywhere from 0-5 markings. The markings on that animal are randomly generated and are fed into a custom imagick function, which will add them in order as they appear in the array.
While the markings are randomly decided, there are a lot of rules to how they are supposed to appear on an animal, for instance markings in the "full body" region show above markings in the "belly" region. To better explain, I'll attach an image of the tester so far:
So to break down the 5 markings on this randomly generated animal, the eyeshadow
marking belongs to eye region, undertail
belongs to tail, streaks
belongs to fullbody, appaloosa
belongs to back, and okapi
belongs to legs. The order right now is just added as the script looped through the database and randomly selected markings, so okapi (the stripes on the legs) is on top since it was the last on in the array, and the last one added. But following the order rules, the last one in the array should have been streaks (the horizontal streaks across the body), since fullbody markings go on top.
Here is the code the selects markings, 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 figure I can accomplish this with a lot of complex if statements but it just doesn't seem like an elegant solution to me. In addition, there is an exception: 'Gradient', a fullbody marking, goes underneath everything even though it is a full body marking. So far it is the only marking with this sort of exception to it, though.
I have tried using various sort
functions offered by PHP but I am not having much luck. uksort
seems the most promising, but since the value we are sorting by exists in the database and not in the array we are sorting (the imagick function has to be fed a marking => color array format), it's proving difficult to work with.
Tl;dr: I need to reorder an array that an uncertain amount of data based off of values that exist in the DB for the keys (the region for the markings). What's the most elegant way to accomplish this?