PHP Elo评级系统测试

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了PHP Elo评级系统测试相关的知识,希望对你有一定的参考价值。

<style>
    div.perfrating {
        padding: 10px;
        background-color: #DDD;
        border: 1px solid #444;
    }

    span.rating_change {
        display: block;
        padding: 5px;
        background-color: light-blue;
        border: 1px solid blue;
    }
</style>
<?php
    set_time_limit(120);

    define('NUM_TOURNAMENTS', 1000);
    define('NUM_PLAYERS', 64);
    define('NUM_PARTICIPANTS', 32);
    define('K_KOEFF', 16);
    define('START_RATING', 1600);

    // We need even number of participants to do pairing
    assert('NUM_PARTICIPANTS % 2 == 0');

    class player {
        public $strength;
        public $rating;
        public $games;

        /**
        * Constructor
        *
        * @param int $rating Start rating

        * @return player
        */
        public function player($rating) {
            $this->rating = $rating;
            $this->strength = rand(0, 100);
        }

        /**
        * Calculate K-factor for player
        *
        * @return int K-factor
        */
        public function get_k_factor() {

            $factor = 25;

            if ($this->games > 30) {
                $factor = 22;
            }

            if ($this->rating > 1700) {
                $factor = 20;
            }

            if ($this->rating > 1900) {
                $factor = 15;
            }

            if ($this->rating > 2100) {
                $factor = 10;
            }

            return $factor;
        }
    }
    function connect() {
        $host="localhost";
        $login="root";
        $pass="";
        $dname="elo";
        mysql_connect($host,$login,$pass) or die("Problem with Database Connection");
        mysql_select_db("$dname");

        // Create the table in case someone wants to check the script
        mysql_query(
            "CREATE TABLE IF NOT EXISTS `deltas` (
                `value` decimal(11,4) NOT NULL
            ) ENGINE=MyISAM DEFAULT CHARSET=utf8;"
        );
    }

    function random_pairing($pairs) {
        $out = array();

        // Fail if number of items is odd
        $first = array();
        $second = array();

        for($i = 0; $i < $pairs * 2; $i++){

            $nums[] = $i;

        }

        shuffle($nums);

        $first = array_slice($nums, 0, $pairs);
        $second = array_slice($nums, $pairs, $pairs);

        shuffle($second);

        return array_combine($first, $second);
    }

    function play_game($player1, $player2, &$participants2) {
        global $players;

        // Fetch ratings
        $str1 = $players[$player1]->strength;
        $str2 = $players[$player2]->strength;

        // Throw a dice here
        // $points = rand(1, $rating1 + $rating2);
        if ($str2 <= $str1) {
            // player 1 won
            $winner = $player1;
            $loser  = $player2;
        } else {
            $winner = $player2;
            $loser  = $player1;
        }

        $participants2[] = $winner;

        echo '' . $player1 . '[' . $players[$player1]->rating . '] vs ' . $player2 . '[' . $players[$player2]->rating . '], winner is <b>' . $winner . '</b><br/>';

        $status = 'normal';
        // 5% of games end in draw.
        // User that go in next tier determined by VP
        if (rand(0,100) <= 5) {
            $status = 'draw';
        }

        // Add log record
        $log_record = array(
            'player1' => $player1,
            'player2' => $player2,
            'winner'  => $winner,
            'loser'  => $loser,
            'status' => $status
        );

        return $log_record;

    }

    function play_tier($participants, &$tourlog) {
        global $players;
        ksort($participants);
        // Decide the pairing
        $pairs = random_pairing(count($participants) / 2);

        $subtour = 0;

        // Second round
        $participants2 = array();
        foreach ($pairs as $player1 => $player2) {

            $tourlog[] = play_game($participants[$player1], $participants[$player2], $participants2);

            $subtour++;
        }

        return $participants2;
    }

    /**
    * Performance Rating is a hypothetical rating that would result from the games
    * of a single event only. Some chess organizations use the "algorithm of 400"
    * to calculate performance rating. According to this algorithm, performance rating
    * for an event is calculated by taking (1) the rating of each player beaten and
    * adding 400, (2) the rating of each player lost to and subtracting 400, (3)
    * the rating of each player drawn, and (4) summing these figures and dividing by
    * the number of games played.
    *
    * @param mixed $tourlog
    * @param mixed $participants
    */
    function display_performance_rating($tourlog, $participants) {
        global $players;
        //
        echo '<div class="perfrating">';
        foreach ($participants as $participant) {
            $Prating = 0;
            $games_played = 0;
            // Process tour logs
            foreach($tourlog as $tour_record) {

                if ($tour_record['status'] == 'draw' && ($participant == $tour_record['player1'] || $participant == $tour_record['player2']) ) {
                    if ($participant == $tour_record['player1']) {
                        $Prating += $players[$tour_record['player2']]->rating;
                    } else {
                        $Prating += $players[$tour_record['player1']]->rating;
                    }
                } elseif ($participant == $tour_record['winner']) {
                    $Prating += $players[$tour_record['loser']]->rating + 400;
                    $games_played++;
                } elseif($participant == $tour_record['loser']) {
                    $Prating += $players[$tour_record['winner']]->rating - 400;
                    $games_played++;
                }

            }

            // Calculate performance ratings:

            if ($games_played > 0) {
                $perfRating[$participant] = round($Prating / $games_played);
                // echo 'PerformanceRating of player ' . $participant . ' on event #'. $tour .' is ' . IntVal($perfRating[$participant]) . '<br/>';
            }

            arsort($perfRating);


        }

        foreach ($perfRating as $player => $p_rating) {
            echo ' PerformanceRating of player ' . $player . ' on event is ' . $p_rating . '<br/>';
        }

        echo '</div>';

    }

    function process_ratings($tourlog) {
        global $players;

        // Expected scores
        $exp = array();
        // Actual scores
        $s = array();
        // Games played
        $games_count = array();

        foreach ($tourlog as $tour_record) {

            // Expected scores
            $exp[$tour_record['player1']] += 1 / (1 + pow(10, (($players[$tour_record['player2']]->rating - $players[$tour_record['player1']]->rating)/400)));
            $exp[$tour_record['player2']] += 1 / (1 + pow(10, (($players[$tour_record['player1']]->rating - $players[$tour_record['player2']]->rating)/400)));

            $games_count[$tour_record['player1']]++;
            $games_count[$tour_record['player2']]++;

            if ($tour_record['status'] == 'draw') {
                $s[$tour_record['player1']] += 0.5;
                $s[$tour_record['player2']] += 0.5;
            } elseif ($tour_record['winner'] == $tour_record['player1']) {
                $s[$tour_record['player1']] += 1;
                // $s[$tour_record['player2']] -= 1;
            } else {
                $s[$tour_record['player2']] += 1;
                // $s[$tour_record['player1']] = 1;
            }

        }

        // Do calculations only for those in the current log
        $participants = array_keys($games_count);
        foreach ($participants as $player) {
            $newrating = round($players[$player]->rating + $players[$player]->get_k_factor() * ($s[$player] - $exp[$player]));
            echo '<div>Player ' . $player . ' have expected score ' . $exp[$player] . ' and actual ' . $s[$player] .  '</div>';
            if ($players[$player]->rating != $newrating) {
                echo '<span class="rating_change">Player ' . $player . ' has rating change from <em class="rating">' . $players[$player]->rating . '</em> to <em class="rating">' . $newrating . '</em> (delta is ' . ($s[$player] - $exp[$player]) .' and k-factor is ' . $players[$player]->get_k_factor() . ')</span>';
                mysql_query(
                    "INSERT INTO  `elo`.`deltas` (
                        `value`
                    )
                    VALUES (
                        '" . ($s[$player] - $exp[$player]). "'
                    );"
                );

                // New rating
                $players[$player]->rating = $newrating;
                $players[$player]->games += $games_count[$player];
            }

        }

    }

    //                              BEGIN

    global $players;
    $players = array();

    //                              MYSQL
    connect();
    ob_start();

    for ($i = 0; $i <= NUM_PLAYERS; $i++){
        $players[$i] = new player(START_RATING); // Beginners rating
    }

    for ($tour = 1; $tour <= NUM_TOURNAMENTS; $tour++){

        echo '<h3>Tournament '. $tour . '</h3>';
        // Choose 20 participants at random
        if (count($players) > NUM_PARTICIPANTS) {
            $mob = array_keys($players);
            shuffle($mob);
            $participants = array_combine(range(0,NUM_PARTICIPANTS - 1), array_slice($mob, 0, NUM_PARTICIPANTS));
        } elseif (count($players) == NUM_PARTICIPANTS) {
            $participants = array_combine(range(0,NUM_PARTICIPANTS - 1), array_keys($players));
        } else {
            echo 'Too few players'.
            exit;
        }

        // new log every tournament
        $tourlog = array();

        echo '<h4>Tier 0</h4>';
        $participants1 = play_tier($participants, $tourlog);

        // TIER 1
        echo '<h4>Tier 1</h4>';
        $participants2 = play_tier($participants1, $tourlog);

        // TIER 2

        echo '<h4>Tier 2</h4>';
        $participants3 = play_tier($participants2, $tourlog);

        // TIER 3

        echo '<h4>Tier 3</h4>';
        $participants4 = play_tier($participants3, $tourlog);

        // FINALS

        echo '<h4>Finals</h4>';
        $winner = play_tier($participants4, $tourlog);


        echo '<span>Tour #' . $tour . ' games are ended, tourlog has ' . count($tourlog) .' records</span>';

        // RATINGS CALCULATION
        display_performance_rating($tourlog, $participants);

        process_ratings($tourlog);
    }

    // arsort($players;

    echo '<table>';
    echo '<tr><th>Player</th><th>Rating</th><th>Strength</th></tr>';
    foreach ($players as $playerid => $player) {
        echo '<tr><td>' . $playerid . '</td><td>' . $player->rating . '</td><td>' . $player->strength . '</td></tr>';
    }
    echo '</table>';
    ob_end_flush();
?>

以上是关于PHP Elo评级系统测试的主要内容,如果未能解决你的问题,请参考以下文章

php和MySQL中简单的喜欢/不喜欢评级系统

使用 PHP、MySQL、Jquery 和 Ajax 创建 5 星评级系统

如何存储评级系统的 ID?

如何使用 Python/Pandas 测量预测的准确性?

php提交评级数据而不重新加载

php 产品评级