Working on Qualification Pairings

This commit is contained in:
Torsten 2017-09-22 17:40:48 +02:00
parent 9c0f94758d
commit a66189ca00
19 changed files with 9005 additions and 237 deletions

View file

@ -1,10 +1,17 @@
<?php
namespace App\leagues;
namespace App\Leagues;
use Illuminate\Database\Eloquent\Model;
use App\leagues\League;
use App\Leagues\League;
use App\Leagues\LeagueMember;
use App\User;
use App\Round;
use App\Season;
use Log;
use DB;
class Match extends Model
{
@ -13,106 +20,246 @@ class Match extends Model
'id','league_id','round_id','user1_id','user2_id'
];
public static function createMissingMatchups() {
// https://en.wikipedia.org/wiki/Round-robin_tournament
public static function createMissingMatchups()
{ Log::info("Match::Creating missing matchups()");
$currentSeason = Season::getCurrent();
$leagues = League::where('season_id','=',$currentSeason->id);
$leagues = League::where('season_id', '=', $currentSeason->id)->get();
// TODO: Differentiate Matchup selection by League Type
$roundNames=Round::getNames();
$leagueNames=League::getNames();
// Liga 1
// Liga 2
// Ligen 3 1,2,3,...n
foreach ($leagues as $league) {
// Liga 4 (Qualifikation)
// Liga 5: Champions League
// Liga 7: Achtelfinale
// Liga 8: Viertelfinale
// Liga 9: Halbfinale
// Liga 10: Finale
foreach($leagues as $league) {
// Qualifikation
if ($league->rank == 4) {
if ($league->rank == League::RANK_QUALIFICATION) {
$matches = array();
// Bereits bestehende Matches laden
DB::table('leagues_matches')->where('league_id','=',$league->id)->get();
$users = User::getActives();
foreach($users as $user1) {
$counter_users=getActives();
foreach($counter_users as $user2) {
if ($user1->id == $user2->id) continue;
if (!User::hasMatched($user1->id, $user2->id, $league->id)) {
$members = LeagueMember::getMembers($league->id);
foreach ($members as $member) {
$counterMembers = LeagueMember::getMembers($league->id);
foreach ($counterMembers as $counterMember) {
if ($member->user_id == $counterMember->user_id) continue;
// Die Paarung darf nicht schon in der DB existieren, egal welche Runde
// hasMatched bezieht sich auf bereits eingetragene matches
// Spieler darf gegen den Gegner in der Saison nicht schon gespielt haben
$exists = false;
// Prüfung ob Paarung bereits in matches abgespeichert ist
if (array_key_exists($league->id, $matches)) {
foreach ($matches[$league->id] as $match) {
if ($match[0] == $member->user_id && $match[1] == $counterMember->user_id) {
$exists = true;
}
if ($match[1] == $member->user_id && $match[0] == $counterMember->user_id) {
$exists = true;
}
}
}
// hasMatched prüft abgespeicherte Matches in DB (leagues_matches)
if (!Match::hasMatched($member->user_id, $counterMember->user_id, $league->id) && !$exists ) {
if (!array_key_exists($league->id, $matches)) {
$matches[$league->id] = array();
}
// add Match
array_push($matches, [$user1, $user2]);
array_push($matches[$league->id], [$member->user_id, $counterMember->user_id,0]); // 0 = unplatziert
Log::info("ADD MATCH PAIRING: Liga " . $league->id . " => " . $member->user_id . " vs " . $counterMember->user_id);
}
}
}
Log::info("Open Matches:");
foreach ($matches as $match) {
// Oh je, jetzt noch die Runde festlegen
// Wir haben jetzt in $matches die nach Liga unterteilten noch zu platzierenden Matches
//Log::info("Starting placement");
// Auffüllen der noch freien Runden mit den verfügbaren matches.
$rounds = Round::where('id','>=',$league->start_round_id)->get();
/*
*
* Bestimmen, in welcher Runde das Spiel stattfinden soll.
* Es darf kein Heim & Rückspiel in der gleichen Runde sein,
* d.h. ein Spieler kommt in einer Runde nur einmal zu einem Match
*
* Stehen keine Runden mehr zur Verfügung (der Standardfall)
* muss die Vergabe beendet werden. Neue Runden kommen dann
* später im Verlauf der Halbsaison ins System.
*
*/
// In der Runde darf es nicht schon ein bestehendes Match mit dem User geben
// $round = Match::getFreeRound($user_id, $league_id)
foreach ($rounds as $round) {
if ($leagueNames[$league->id]=="Qualifikationsgruppe 4" && $roundNames[$round->id]=="Runde 2") { Log::info("Analyzing Round 2");}
//Log::info("LeagueID=".$league->id.", Trying to fill roundid ".$round->id);
$count=0;
if ($leagueNames[$league->id]=="Qualifikationsgruppe 4" && $roundNames[$round->id]=="Runde 2") {
Log::info("Available Matchups:");
}
shuffle($matches[$league->id]);
foreach($matches[$league->id] as $key => $m) {
if ($m[2]==0) { $count += 1;
if ($leagueNames[$league->id]=="Qualifikationsgruppe 4" && $roundNames[$round->id]=="Runde 2") {
Log::info($m[0]." vs ".$m[1]);
}
}
}
//Log::info("Unplaced matches:".$count);
$existingRoundMatches = Match::where('league_id','=',$league->id)->where('round_id','=',$round->id)->get();
//Log::info("round ".$round->id." Existing matchCount:".sizeof($existingRoundMatches));
$membercount = League::getMemberCount($league->id);
$maxMatchCount = intdiv($membercount,2); //TODO: Ungerade Anzahl = +1
// Gibt es noch Einträge in $matches[$league->id] ?
$hasUnplacedMatches=false;
foreach($matches[$league->id] as $m) {
if ($m[2]==0) { $hasUnplacedMatches=true; break;}
}
if ( !$hasUnplacedMatches ) {
if ($leagueNames[$league->id]=="Qualifikationsgruppe 4" && $roundNames[$round->id]=="Runde 2") {
Log::info("Ending Placement for Round 2 because roster is full");
}
break;
}
foreach ($matches[$league->id] as $index => $match) {
if ($match[2]==1) continue;
// Anzahl an Matches pro Runde schon erreicht ?
$existingRoundMatches = Match::where('league_id','=',$league->id)->where('round_id','=',$round->id)->get();
//Log::info("Existing matches in roundid ".$round->id.": ".sizeof($existingRoundMatches));
if (sizeof($existingRoundMatches) > $maxMatchCount) {
//Log::info("Skipping to next round, because Placement roster is full");
continue;
}
$conflict=false;
foreach ($existingRoundMatches as $exMatch) {
$mName=$matches[$league->id][$index][0]." vs ".$matches[$league->id][$index][1];
if ($exMatch->user1_id == $match[0] ) { $conflict=true; }
else if ($exMatch->user2_id == $match[0]) { $conflict=true; }
else if ($exMatch->user1_id == $match[1]) { $conflict=true; }
else if ($exMatch->user2_id == $match[1]) { $conflict=true; }
}
if (!$conflict) {
$matches[$league->id][$index][2]=1;
Log::info("Saving match. ".$matches[$league->id][$index][0]." vs ".$matches[$league->id][$index][1]);
$newMatch = Match::create([
'league_id' => $league->id,
'round_id' => $round->id,
'user1_id' => $matches[$league->id][$index][0],
'user2_id' => $matches[$league->id][$index][1]
]);
$newMatch->save();
}
}
}
$count=0;
foreach($matches[$league->id] as $key => $m) {
if ($m[2]==0) { $count += 1; }
}
Log::info("Exiting LeagueId ".$league->id. " with ".$count." unplaced matches");
}
}
}
public static function hasMatched($user1_id, $user2_id, $league_id)
{
// Wieviele Spiele finden pro Runde statt ? => Jeder Spieler spielt, also GruppenGröße/2, bei ungerader Gruppengröße gibt es spielfreie Tage.
$matches = DB::table('leagues_matches')
->where('league_id', '=', $league_id)
->where('user1_id', '=', $user1_id)
->where('user2_id', '=', $user2_id)
->get();
if (sizeof($matches)) {
return true;
}
public static function getFreeRound($user_id, $league_id) {
$round_matches = Match::where('league_id','=',$league_id)->orderBy('round_id')->get();
$membercount = League::getMemberCount($league_id);
$max_size = divint($membercount,2);
$matches = DB::table('leagues_matches')
->where('league_id', '=', $league_id)
->where('user1_id', '=', $user2_id)
->where('user2_id', '=', $user1_id)
->get();
if (sizeof($matches)) {
return true;
}
$current_round_id=-1;
$current_member_count=0;
$is_round_usable=true;
return false;
}
foreach($round_matches as $round_match) {
$current_member_count+=1;
// 1.) Runde darf nicht schon Gruppengröße/2 Matches enthalten (siehe $max_size)
// 2.) Spieler darf in der Runde nicht schon spielen (als user1_id enthalten sein)
if ($current_member_count>$max_size) { $is_round_usable=false; continue; }
if ($round_match->user1_id == $user_id) { $is_round_usable=false; continue; }
if ($round_match->round_id != $current_round_id) {
if ($is_round_usable && $current_round_id!=-1) { return $round_match->round_id; }
$current_round_id=$round_match->match_id;
$current_member_count=0;
$is_round_usable = true;
}
// Selektiert nur die "Heimspiele"
public static function getMatches($user_id, $league_id) {
$matches = DB::table('leagues_matches')->where('league_id', '=', $league_id)->where('user1_id','=',$user_id)->get();
return $matches;
}
public static function getLeagueMatches($league_id) {
//$matches = Match::where('league_id','=',$league_id)->orderBy('round_id')->get();
$matches = DB::table('leagues_matches')->where('league_id','=',$league_id)->orderBy('round_id')->get();
$season = Season::getCurrent();
$roundnames = Round::getNames();
$usernames = User::getNames();
foreach($matches as $match) {
$match->round_name = $roundnames[$match->round_id];
$match->user1_name = $usernames[$match->user1_id];
$match->user2_name = $usernames[$match->user2_id];
// Hier könnte man die Punkte auswerten.
}
return -1; // No suitable/free round found for Player Match.
return $matches;
}
public static function getPairings($leagueId, $roundNr) {
$members = LeagueMember::getMembers($leagueId);
$pairingCount = intdiv(sizeof($members),2);
$roundRobin=array();
foreach($members as $member) {
array_push($roundRobin, $member->id);
}
while ($roundNr>1) {
// Round-Robin shift: First element stays, rest rotates
$first = array_shift($roundRobin); // Erstes Element sichern & entfernen
$last = array_pop($roundRobin); // Letztes Element sichern & entfernen
array_unshift($roundRobin, $last); // Vorne einfügen
array_unshift($roundRobin, $first); // Vorne einfügen
$roundNr--;
}
error_log("Members: ".sizeof($roundRobin));
foreach ($roundRobin as $index => $userId) {
error_log($index." => ".$userId);
}
$idx=0;
$slot=0;
$pairings=array();
foreach ($roundRobin as $index => $userId) {
$idx=$index;
if ($index >= $pairingCount){ $idx -= $pairingCount; $slot=1;}
$pairings[$idx][$slot]=$userId;
error_log("pairings[".$idx."][".$slot."]=".$userId);
}
return $pairings; // Array, der User1/User2 Tupel enthält
}
}