Installation
composer require avoo/elo
Ensure composer.json includes the package under require.
Basic Usage Import the core class and initialize with default settings:
use Avoo\Eloquent\EloCalculator;
$eloCalculator = new EloCalculator();
First Calculation Simulate a match between two players (e.g., Player A with 1500 Elo vs. Player B with 1400 Elo):
$result = $eloCalculator->calculate(1500, 1400, true); // true = Player A wins
echo $result['newEloA']; // Updated Elo for Player A
Key Files
src/EloCalculator.php: Core logic (K-factor, formula overrides).tests/: Unit tests for edge cases (e.g., draws, invalid inputs).Match Result Handling
true/false for the winner.null or a dedicated method if extended:
$eloCalculator->calculate(1500, 1400, null); // Draw logic (default: split points)
Custom K-Factors Override the default K-factor (e.g., 32 for beginners, 16 for experts):
$eloCalculator = new EloCalculator(16); // Initialize with custom K
Batch Processing Loop through match results for efficiency:
$matches = [
['playerA' => 1500, 'playerB' => 1400, 'winner' => 'A'],
['playerA' => 1600, 'playerB' => 1550, 'winner' => 'B'],
];
foreach ($matches as $match) {
$result = $eloCalculator->calculate($match['playerA'], $match['playerB'], $match['winner'] === 'A');
// Store $result in DB
}
Integration with Eloquent
Attach to a Player model’s afterSave or afterUpdate:
// Player.php
public function updateElo($winner, $opponentElo) {
$eloCalculator = new EloCalculator();
$newElo = $eloCalculator->calculate($this->elo, $opponentElo, $winner)->newElo;
$this->elo = $newElo;
$this->save();
}
Testing Mock the calculator in unit tests:
$eloCalculator = $this->createMock(EloCalculator::class);
$eloCalculator->method('calculate')
->willReturn(['newEloA' => 1600, 'newEloB' => 1300]);
Floating-Point Precision
Elo calculations may yield floating-point results (e.g., 1500.42). Round to integers if required:
$newElo = ceil($result['newEloA']); // or floor()/round()
K-Factor Misconfiguration
Draw Handling Default behavior splits the expected point difference equally. Override in a subclass:
class CustomEloCalculator extends EloCalculator {
protected function handleDraw($eloA, $eloB) {
return [
'newEloA' => $eloA + 0.5, // Custom logic
'newEloB' => $eloB + 0.5,
];
}
}
Negative Elo The library doesn’t enforce minimums. Validate inputs:
if ($eloA < 0 || $eloB < 0) {
throw new \InvalidArgumentException("Elo cannot be negative");
}
Thread Safety The calculator is stateless but ensure thread safety in concurrent environments (e.g., queues):
// Safe for parallel jobs
$eloCalculator = new EloCalculator();
Log Intermediate Values Extend the class to log probabilities:
class DebugEloCalculator extends EloCalculator {
public function calculate($eloA, $eloB, $winner) {
$result = parent::calculate($eloA, $eloB, $winner);
\Log::debug("Probabilities: A={$this->getProbability($eloA, $eloB)}%, B=" . (100 - $this->getProbability($eloA, $eloB)) . "%");
return $result;
}
}
Unit Test Edge Cases Test with:
1500 vs 1500).50 vs 2500).Custom Formulas
Override calculateExpectedScore() for non-standard Elo variants:
protected function calculateExpectedScore($eloA, $eloB) {
return 1 / (1 + pow(10, ($eloB - $eloA) / 400)); // Standard Elo
// OR: Custom formula (e.g., Glicko-2)
}
Event Dispatching
Trigger events after calculations (e.g., elo:updated):
use Illuminate\Support\Facades\Event;
$eloCalculator = new EloCalculator();
$result = $eloCalculator->calculate(1500, 1400, true);
Event::dispatch(new EloUpdated($result));
Database Storage
Use Laravel’s observers or accessors to auto-update Elo:
// Player.php
public function getEloAttribute($value) {
return $this->attributes['elo'] ?? 1200; // Default
}
How can I help you explore Laravel packages today?