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评级系统测试的主要内容,如果未能解决你的问题,请参考以下文章