I need your help. I'm currently developing application which will keep track of Warhammer 40k ligue :-)
I'm struggling with creating parings for current round. In general: there will be 12 rounds where every player will play one game against drawn opponent. Players can't play twice against the same opponent if they hasn't played all other possible players. What is more if the count of players is odd in every round different player is pausing current round (so players count is even).
So, what I've got is this:
Create permutation for all possible pairs. In SQL table parings
I store every possible pair. As you see there are two "helper" columns = isUsed
which I use to keep track if this paring was played, and column round_num
which is foreign key for round column (to keep track of parings in every round). Method below is rather simple recursive.
private function drawAllParings($players, $parings = [])
{
global $parings;
$playerA = $players->shift();
foreach ($players as $item) {
$paring = new Paring;
$paring->PlayerA_id = $playerA->id;
$paring->PlayerB_id = $item->id;
$parings[] = $paring;
}
if ($players->count() != 1) {
$this->drawAllParings($players, $parings);
}
return $parings;
}
Create new round. What is the most important part here is assignig paused player if players count is odd. I assign it to pausing_player_id
column. I also give player a flag, that he had paused. If all players have paused I simply reset column players.PlayerPause
public function store(Request $request)
{
$lastRound = Round::orderBy('id', 'desc')->first();
if (!empty($lastRound) && $lastRound->parings->count() == 0) {
return Redirect::back()->withErrors(['Round could not be created because your previous round has no parings.']);
}
$round = new Round;
//Get number of all players in db
$playersCount = Player::count();
if ($playersCount % 2 != 0) {
$playerToPause = $this->getPausingPlayer();
$round->pausing_player_id = $playerToPause->id;
$player = Player::where('id', $round->pausing_player_id)->first();
$player->PlayerPause = 1;
$player->save();
}else{
$round->pausing_player_id = NULL;
}
if (empty($lastRound)) {
$round->round_num = 1;
}else{
$round->round_num = $lastRound->round_num + 1;
}
if ($round->save()) {
Session::flash('success', 'Round was added');
return redirect()->route('rounds.index');
}else{
return Redirect::back()->withErrors(['Round could not be created. Please contact administrator.']);
}
}
public function getPausingPlayer()
{
$playerToPause = Player::where('PlayerPause', 0)->first();
// If there are no players that can pause, reset them.
if (empty($playerToPause)) {
$this->resetPause();
$playerToPause = Player::where('PlayerPause', 0)->first();
}
return $playerToPause;
}
Select parings from parings
table for this round. Keep in mind that one player can't play twice this round, and can't play twice against the same opponent if hasn't played all other opponents. What I achived so far is createing 3 rounds, so 15 parings. After that moment script can't choose more than 3 parings from parings
table. I guess the problem is pausing player, because in every round we have different player, and it "takes" player that would fit to another player as paring. Can you please explain to me algorithm which should help me?
Here is my draw method:
public function draw()
{
$round = Round::orderby('id', 'desc')->first();
if (empty($round)) {
return new ErrorResource(['error' => 'Create Round to make parings']);
}
if (Player::all()->isEmpty()) {
return new ErrorResource(['error' => 'Add Players to make parings']);
}
if (Round::all()->isEmpty()) {
return new ErrorResource(['error' => 'Create Round to make parings']);
}
if ($round->parings->count() != 0) {
return new ErrorResource(['error' => 'You can not generate parings for this round. Create new round']);
}
//Get players to play
if (isset($round->pausing_player_id)) {
$players = Player::where('id', '!=', $round->pausing_player_id)->get();
}else{
$players = Player::all();
}
// $parings is output container
$parings = [];
// Select first player
$playerA = $players->shift();
// Container for players that has been used in this round
$usedPlayersId = [];
// Loop that selects paring from database.
while ($players->count()) {
$paring = Paring::where('isUsed', 0)
->where('PlayerA_id', '!=', $round->pausing_player_id)
->where('PlayerB_id', '!=', $round->pausing_player_id)
->whereNotIn('PlayerA_id', $usedPlayersId)
->whereNotIn('PlayerB_id', $usedPlayersId)
->where(function($query) use($playerA) {
$query->where('PlayerA_id', $playerA->id)
->orWhere('PlayerB_id', $playerA->id);
})
->first();
$usedPlayersId[] = $playerA->id;
$playerB = '';
$players = $players->reject(function($item, $key) use(&$paring, &$usedPlayersId){
if ($item->id == $paring->PlayerA_id || $item->id == $paring->PlayerB_id) {
$usedPlayersId[] = $item->id;
return true;
}
});
$playerA = $players->shift();
$parings[] = $paring;
}
return new ParingCollection(collect($parings));
}
Please help :-)