Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
CRAP
0.00% covered (danger)
0.00%
0 / 1
BradleyTerry
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
56
0.00% covered (danger)
0.00%
0 / 1
 rating
0.00% covered (danger)
0.00%
0 / 20
0.00% covered (danger)
0.00%
0 / 1
56
1<?php
2/**
3 * Jingga
4 *
5 * PHP Version 8.1
6 *
7 * @package   phpOMS\Algorithm\Rating
8 * @copyright Dennis Eichhorn
9 * @license   OMS License 2.0
10 * @version   1.0.0
11 * @link      https://jingga.app
12 */
13declare(strict_types=1);
14
15namespace phpOMS\Algorithm\Rating;
16
17/**
18 * Calculate rating strength using the Bradley Terry model
19 *
20 * @package phpOMS\Algorithm\Rating
21 * @license OMS License 2.0
22 * @link    https://jingga.app
23 * @see     https://en.wikipedia.org/wiki/Bradley%E2%80%93Terry_model
24 * @since   1.0.0
25 */
26final class BradleyTerry
27{
28    /**
29     * Rate the strongest to the weakest team based on historic performances (wins/losses)
30     *
31     * The following example contains match results (matrix) of teams A-D facing each other (each point is a victory).
32     * @example rating(
33     *     [
34     *          'A' => ['A' => 0, 'B' => 2, 'C' => 0, 'D' => 1],
35     *          'B' => ['A' => 3, 'B' => 0, 'C' => 5, 'D' => 0],
36     *          'C' => ['A' => 0, 'B' => 3, 'C' => 0, 'D' => 1],
37     *          'D' => ['A' => 4, 'B' => 0, 'C' => 3, 'D' => 0],
38     *      ],
39     *      10
40     *  ) // [0.640, 1.043, 0.660, 2.270] -> D is strongest
41     *
42     * @param array[] $history    Historic results
43     * @param int     $iterations Iterations for estimation
44     *
45     * @return float[] Array of "strength" scores (highest = strongest)
46     *
47     * @since 1.0.0
48     */
49    public function rating(array $history, int $iterations = 20) : array
50    {
51        $keys = \array_keys($history);
52        $pOld = [];
53        foreach ($keys as $key) {
54            $pOld[$key] = 1;
55        }
56
57        $p = $pOld;
58        for ($i = 0; $i < $iterations; ++$i) {
59            foreach ($history as $idx => $row) {
60                $W = \array_sum($row);
61
62                $d = 0;
63                foreach ($history as $idx2 => $_) {
64                    if ($idx === $idx2) {
65                        continue;
66                    }
67
68                    $d += ($history[$idx][$idx2] + $history[$idx2][$idx])
69                        / ($pOld[$idx] + $pOld[$idx2]);
70                }
71
72                $p[$idx] = $W / $d;
73            }
74
75            $norm = \array_sum($p);
76            foreach ($p as $idx => $_) {
77                $p[$idx] /= $norm;
78            }
79
80            $pOld = $p;
81        }
82
83        return $p;
84    }
85}