Skip to content

Commit 551a49f

Browse files
authored
Merge pull request #448 from CnCNet/feature/qm-color-selection
Feature/qm color selection
2 parents d00b5c0 + b96b1dd commit 551a49f

File tree

2 files changed

+169
-11
lines changed

2 files changed

+169
-11
lines changed

cncnet-api/app/Http/Services/QuickMatchService.php

Lines changed: 143 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,16 @@ public function createQMPlayer($request, $player, $history)
6161

6262
$qmPlayer->chosen_side = $request->side;
6363

64+
// Store preferred colors. If not set, players get yellow/red like usual.
65+
if (isset($request->colors) && is_array($request->colors))
66+
{
67+
$qmPlayer->colors_pref = json_encode(array_values($request->colors));
68+
}
69+
if (isset($request->colors_opponent) && is_array($request->colors_opponent))
70+
{
71+
$qmPlayer->colors_opponent_pref = json_encode(array_values($request->colors_opponent));
72+
}
73+
6474
if ($request->map_sides)
6575
{
6676
$qmPlayer->map_sides_id = \App\Models\MapSideString::findValue(join(',', $request->map_sides))->id;
@@ -951,6 +961,8 @@ private function set1v1QmSpawns($otherQmQueueEntries, $qmMatch, $qmPlayer, $expe
951961
$numSpawns = $qmMap->map->spawn_count;
952962
$spawnArr = [];
953963

964+
// Assign colour & spawn locations for current QM player
965+
// Then again for other players below
954966
for ($i = 1; $i <= $numSpawns; $i++)
955967
{
956968
$spawnArr[] = $i;
@@ -963,20 +975,77 @@ private function set1v1QmSpawns($otherQmQueueEntries, $qmMatch, $qmPlayer, $expe
963975
Log::debug("QuickMatchService ** Random spawns selected for qmMap: '" . $qmMap->description . "', " . $spawnOrder[0] . "," . $spawnOrder[1]);
964976
}
965977

966-
967-
# Assign colour & spawn locations for current QM player
968-
# Then again for other players below
969-
$colorsArr = $this->getColorsArr(8, false);
978+
// Find the opponent player and check if both players submitted their preferred colors and make sure their is no observers.
979+
// Streamers might not be prepared for other colors than yellow/red. With someone observer and color info missing, we stick
980+
// to the old logic.
970981
$i = 0;
982+
$colorsArr = null;
971983

972-
if ($qmPlayer->isObserver() == false)
984+
foreach ($otherQmQueueEntries as $otherQmQueueEntry)
973985
{
974-
$qmPlayer->color = $colorsArr[$i];
975-
$qmPlayer->location = $spawnOrder[$i] - 1;
976-
$qmPlayer->save();
977-
$i++;
986+
$candidate = \App\Models\QmMatchPlayer::where("id", $otherQmQueueEntry->qmPlayer->id)->first();
987+
if ($candidate && !$candidate->isObserver())
988+
{
989+
$opponentPlayer = $candidate;
990+
$opponentsCount++;
991+
}
992+
}
978993

979-
Log::debug("QuickMatchService ** Assigning Spot for " . $qmPlayer->player->username . "Color: " . $qmPlayer->color . " Location: " . $qmPlayer->location);
994+
$bothHaveColorPrefs = false;
995+
$p1Colors = null;
996+
$p2Colors = null;
997+
if ($opponentPlayer && $opponentsCount === 1)
998+
{
999+
$p1Colors = isset($qmPlayer->colors_pref) ? json_decode($qmPlayer->colors_pref, true) : null;
1000+
$p2Colors = isset($opponentPlayer->colors_pref) ? json_decode($opponentPlayer->colors_pref, true) : null;
1001+
$p1OppColors = isset($qmPlayer->colors_opponent_pref) ? json_decode($qmPlayer->colors_opponent_pref, true) : null;
1002+
$p2OppColors = isset($opponentPlayer->colors_opponent_pref) ? json_decode($opponentPlayer->colors_opponent_pref, true) : null;
1003+
1004+
$qmPlayerIsAnonymous = $qmPlayer->player->user->userSettings->getIsAnonymous();
1005+
$opponentPlayerIsAnonymous = $opponentPlayer->player->user->userSettings->getIsAnonymous();
1006+
1007+
if ($qmPlayerIsAnonymous && !$opponentPlayerIsAnonymous)
1008+
{
1009+
// Do what opponent wants.
1010+
$p1Colors = $p2OppColors;
1011+
$p1OppColors = $p2Colors;
1012+
}
1013+
else if (!$qmPlayerIsAnonymous && $opponentPlayerIsAnonymous)
1014+
{
1015+
// Do what player 1 wants.
1016+
$p2Colors = $p1OppColors;
1017+
$p2OppColors = $p1Colors;
1018+
}
1019+
1020+
$bothHaveColorPrefs = is_array($p1Colors) && is_array($p2Colors) && is_array($p1OppColors) && is_array($p2OppColors);
1021+
}
1022+
1023+
if (!$matchHasObserver && $bothHaveColorPrefs)
1024+
{
1025+
// Determine best matching colors.
1026+
[$p1Color, $p2Color] = $this->setColors($p1Colors, $p1OppColors, $p2Colors, $p2OppColors);
1027+
$colorsArr = [$p1Color, $p2Color];
1028+
if ($qmPlayer->isObserver() == false)
1029+
{
1030+
$qmPlayer->color = $colorsArr[$i];
1031+
$qmPlayer->location = $spawnOrder[$i] - 1;
1032+
$qmPlayer->save();
1033+
$i++;
1034+
Log::debug("QuickMatchService ** Assigning Spot (prefs) for " . $qmPlayer->player->username . " Color: " . $qmPlayer->color . " Location: " . $qmPlayer->location);
1035+
}
1036+
}
1037+
else
1038+
{
1039+
// No preferred colors. Used old logic.
1040+
$colorsArr = $this->getColorsArr(8, false);
1041+
if ($qmPlayer->isObserver() == false)
1042+
{
1043+
$qmPlayer->color = $colorsArr[$i];
1044+
$qmPlayer->location = $spawnOrder[$i] - 1;
1045+
$qmPlayer->save();
1046+
$i++;
1047+
Log::debug("QuickMatchService ** Assigning Spot for " . $qmPlayer->player->username . " Color: " . $qmPlayer->color . " Location: " . $qmPlayer->location);
1048+
}
9801049
}
9811050

9821051
foreach ($otherQmQueueEntries as $otherQmQueueEntry)
@@ -1022,7 +1091,7 @@ private function set1v1QmSpawns($otherQmQueueEntries, $qmMatch, $qmPlayer, $expe
10221091
$otherQmPlayer->tunnel_id = $qmMatch->seed + $otherQmPlayer->color;
10231092
$otherQmPlayer->save();
10241093

1025-
if (!$otherQmPlayer->isObserver())
1094+
if (!$otherQmPlayer->isObserver() && $opponentPlayer === null)
10261095
{
10271096
$opponentPlayer = $otherQmPlayer;
10281097
$opponentsCount++;
@@ -1542,6 +1611,69 @@ private function getColorsArr($numPlayers, $randomize)
15421611
return array_slice($possibleColors, 0, $numPlayers);
15431612
}
15441613

1614+
/**
1615+
* Selects final colors for player 1 and 2 based on their preferences.
1616+
* @param int[] $prefColorsP1 The preferred colors of player 1
1617+
* @param int[] $prefOpponentColorsP1 What player 1 prefers for their opponent.
1618+
* @param int[] $prefColorsP2 The preferred colors of player 2.
1619+
* @param int[] $prefOpponentColorsP2 What player 2 prefers for their opponent.
1620+
* @return array{int,int} [colorPlayer1, colorPlayer2]
1621+
*/
1622+
private function setColors(array $prefColorsP1, array $prefOpponentColorsP1, array $prefColorsP2, array $prefOpponentColorsP2): array
1623+
{
1624+
// Penalty mapping: position in preference array -> penalty points.
1625+
$penalties = [0, 2, 5, 10];
1626+
1627+
// Generate all possible color combinations (0-3).
1628+
$bestCombinations = [];
1629+
$lowestPenalty = PHP_INT_MAX;
1630+
1631+
for ($colorP1 = 0; $colorP1 < 4; $colorP1++)
1632+
{
1633+
for ($colorP2 = 0; $colorP2 < 4; $colorP2++)
1634+
{
1635+
// Cannot assign same color to both players.
1636+
if ($colorP1 === $colorP2)
1637+
{
1638+
continue;
1639+
}
1640+
1641+
// Calculate penalty for player 1's color choice.
1642+
$posP1 = array_search($colorP1, $prefColorsP1);
1643+
$penaltyP1Color = $penalties[$posP1];
1644+
1645+
// Calculate penalty for player 1's opponent color preference.
1646+
$posP1Opponent = array_search($colorP2, $prefOpponentColorsP1);
1647+
$penaltyP1Opponent = $penalties[$posP1Opponent];
1648+
1649+
// Calculate penalty for player 2's color choice.
1650+
$posP2 = array_search($colorP2, $prefColorsP2);
1651+
$penaltyP2Color = $penalties[$posP2];
1652+
1653+
// Calculate penalty for player 2's opponent color preference.
1654+
$posP2Opponent = array_search($colorP1, $prefOpponentColorsP2);
1655+
$penaltyP2Opponent = $penalties[$posP2Opponent];
1656+
1657+
// Total penalty for this combination.
1658+
$totalPenalty = $penaltyP1Color + $penaltyP1Opponent + $penaltyP2Color + $penaltyP2Opponent;
1659+
1660+
// Track best combinations.
1661+
if ($totalPenalty < $lowestPenalty)
1662+
{
1663+
// Replace.
1664+
$lowestPenalty = $totalPenalty;
1665+
$bestCombinations = [[$colorP1, $colorP2]];
1666+
}
1667+
elseif ($totalPenalty === $lowestPenalty)
1668+
{
1669+
// Add.
1670+
$bestCombinations[] = [$colorP1, $colorP2];
1671+
}
1672+
}
1673+
}
1674+
1675+
return $bestCombinations[array_rand($bestCombinations)];
1676+
}
15451677

15461678
/**
15471679
*
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
use Illuminate\Database\Migrations\Migration;
4+
use Illuminate\Database\Schema\Blueprint;
5+
use Illuminate\Support\Facades\Schema;
6+
7+
return new class extends Migration
8+
{
9+
public function up(): void
10+
{
11+
Schema::table('qm_match_players', function (Blueprint $table)
12+
{
13+
$table->json('colors_pref')->nullable()->after('chosen_side');
14+
$table->json('colors_opponent_pref')->nullable()->after('colors_pref');
15+
});
16+
}
17+
18+
public function down(): void
19+
{
20+
Schema::table('qm_match_players', function (Blueprint $table)
21+
{
22+
$table->dropColumn('colors_opponent_pref');
23+
$table->dropColumn('colors_pref');
24+
});
25+
}
26+
};

0 commit comments

Comments
 (0)