How can I pair up items in an array? Let\'s say I have an array of Fighters. And I want to pair them up based on their Weights. Fighters with c
This is just extension of Truth's answer based on comments:
The first thing I'd do differently is basic keeping trace players.
$unassignedPlayers = $fighterList;
Than the algorithm would work in the way: prepare list of teams (if you're using database, use SELECT DISTINCT
or GROUP BY teams.id
):
$teams = array();
foreach( $fighterList as $fighter){
$teams[] = $figter->team;
}
$teams = array_unique( $teams);
Next we'll need method that will split array of fighters (let's say, we have teams {A,A,B,B,C,C}
we want to split that into {A,A}
, {B,B,C,C}
):
// Don't use string type declaration, it's just ilustrating
function splitFighters( array $input, string $team){
$inteam = array();
$outteam = array();
foreach( $input as $fighter){
if( $figter->team == $team){
$inteam[] = $fighter;
} else {
$outteam[] = $fighter;
}
}
return array( $inteam, $outteam);
}
Now that we do have that, we may create function that will sort team members:
function assignFighters( array &$input, array $teams, array &$output){
// Nothing to work with?
if( !count( $input)){
return true;
}
// No team left and still unassigned players, that fatal error
if( !cont( $teams)){
throw new Exception( 'Unassigned players occurred!');
}
// Shift team
$team = array_shift( $teams);
// Split into in and out team
list( $inteam, $outteam) = splitFighters( $input, $team);
// Inteam is already empty (let's say all players were assigned before)
// Just go deeper (where's DiCaprio?)
if( !count( $inteam) && count( $teams)) {
return assignFighters( $input, $teams, $output)
}
// There're unassigned and nonassignable players in this team
// This is error and we'll have to deal with it later
if( !count($outteam)){
$input = $inteam; // Propagate unassigned players to main
return false;
}
// Sort both teams by fighters weight
// Uses Truth's comparison function
usort($inteam, "sortFighters");
usort($outteam, "sortFighters");
// Fig = Fighter
while( $fig1 = array_shift( $inteam)){
// Are there any players to work with
if( !count( $outteam)){
array_unshift( $inteam, $fig1);
$input = $inteam; // Propagate unassigned players to main
return false;
}
// Assign players to each other
$fig2 = array_shift( $outteam);
$fig1->paired = $fig2;
$fig2->paired = $fig1;
// This will propagate players to main nicely
$output[] = $fig1;
$output[] = $fig2;
}
// Still here? Great! $inteam is empty now
// $outteam contains all remaining players
$input = $outteam;
return assignFighters( $input, $teams,$output);
}
Until this point you could use Truth's algorithm, but this should have better weight matching and represents what you intend more clearly but anyway now comes $unassignedPlayers
into work:
$assignedPlayers = array();
$state = assignFighters( $unassignedPlayers, $teams, $assignedPlayers);
// Note:
$state === !(bool)count($unassignedPlayers)
// should evaluate as true, otherwise I'm having an error in algorithm
So what now... If you have $state === false
resp. count( $unassignedPlayers) > 0
something went wrong and we need to apply some magic. How will that magic work:
// Keep the trace of swapped players so we don't end up in endless loop
$swappedPlayers = array();
// Browse all unassigned players
while( $fig1 = array_shift( $unassignedPlayers)){
// Store as swapped
$swappedPlayers[] = $fig1;
// At first check whether there's not unassigned player in the different team
// this shouldn't occur in first iteration (all fighters should be from one team
// in the beginning) but this is most effective part of this method
foreach( $unassignedPlayers as $key => $fig2){
if( $fig2->team != $fig1->team){
$fig1->pair = $fig2;
$fig2->pair = $fig1;
continue;
}
}
// No luck, normal magic required
list( $inteam, $outteam) = splitFighters( $assignedPlayers, $fig1->team);
$fig2 = null; // I like my variables initialized, this actually quite important
// Now select someone from $outteam you will want to swap fights with.
// You may either iterate trough all players until you find best weight
// match or select it random, or whatever, I'll go with random,
// it's your job to implement better selection
$i = 1000; // Limit iterations
while(($i--) > 1){
$key1 = array_rand( $outteam, 1);
if( $outteam[$key]->team == $fig1->team){
continue; // No point in swapping fit team member
}
// No recursive swaps
if( in_array( $outteam[$key], $swappedPlayers)){
continue;
}
// This may speed things really up:
// That means we'll get rid of 2 players due to foreach loop at the beggining
// However I'm not sure how this condition will really work
if( $outteam[$key]->pair->team == $fig1->team){
continue;
}
// Store matched fighter
$fig2 = $outteam[$key];
// Unset pair from another fighter
$fig2->pair->pair = null;
// Find the pair in $assignedPlayers and move it to $unassignedPlayers
$key = array_search( $fig2->pair, $assignedPlayers);
if( $key === false){
throw new Exception( 'Cannot find pair player');
}
unset( $assignedPlayers[$key]);
$unassignedPlayers[] = $fig2->pair;
$swappedPlayers[] = $fig2->pair;
// Remove pair from self
$fig2->pair = null;
$swappedPlayers[] = $fig2;
break; // hh, try forgetting this one :)
}
// This shouldn't be happening
if( $fig2 === null){
throw new Exception( 'Didn\'t find good match in 1000 iterations.');
}
// Ok now just make matches as go to the next iteration
$fig1->pair = $fig2;
$fig2->pair = $fig1;
// And store those
$assignedPlayers[] = $fig1;
$assignedPlayers[] = $fig2;
}
I've written all this out of my head (it was challenge), test it and leave notes in comments please :)