Posted By

kurokikaze on 07/30/09


Tagged

rating Elo chess PerformanceRating


Versions (?)

Who likes this?

1 person have marked this snippet as a favorite

umang_nine


Elo ratings system test


 / Published in: PHP
 

URL: http://en.wikipedia.org/wiki/Elo_rating_system

  1. <style>
  2. div.perfrating {
  3. padding: 10px;
  4. background-color: #DDD;
  5. border: 1px solid #444;
  6. }
  7.  
  8. span.rating_change {
  9. display: block;
  10. padding: 5px;
  11. background-color: light-blue;
  12. border: 1px solid blue;
  13. }
  14. </style>
  15. <?php
  16.  
  17. define('NUM_TOURNAMENTS', 1000);
  18. define('NUM_PLAYERS', 64);
  19. define('NUM_PARTICIPANTS', 32);
  20. define('K_KOEFF', 16);
  21. define('START_RATING', 1600);
  22.  
  23. // We need even number of participants to do pairing
  24. assert('NUM_PARTICIPANTS % 2 == 0');
  25.  
  26. class player {
  27. public $strength;
  28. public $rating;
  29. public $games;
  30.  
  31. /**
  32.   * Constructor
  33.   *
  34.   * @param int $rating Start rating
  35.  
  36.   * @return player
  37.   */
  38. public function player($rating) {
  39. $this->rating = $rating;
  40. $this->strength = rand(0, 100);
  41. }
  42.  
  43. /**
  44.   * Calculate K-factor for player
  45.   *
  46.   * @return int K-factor
  47.   */
  48. public function get_k_factor() {
  49.  
  50. $factor = 25;
  51.  
  52. if ($this->games > 30) {
  53. $factor = 22;
  54. }
  55.  
  56. if ($this->rating > 1700) {
  57. $factor = 20;
  58. }
  59.  
  60. if ($this->rating > 1900) {
  61. $factor = 15;
  62. }
  63.  
  64. if ($this->rating > 2100) {
  65. $factor = 10;
  66. }
  67.  
  68. return $factor;
  69. }
  70. }
  71. function connect() {
  72. $host="localhost";
  73. $login="root";
  74. $pass="";
  75. $dname="elo";
  76. mysql_connect($host,$login,$pass) or die("Problem with Database Connection");
  77. mysql_select_db("$dname");
  78.  
  79. // Create the table in case someone wants to check the script
  80. "CREATE TABLE IF NOT EXISTS `deltas` (
  81. `value` decimal(11,4) NOT NULL
  82. ) ENGINE=MyISAM DEFAULT CHARSET=utf8;"
  83. );
  84. }
  85.  
  86. function random_pairing($pairs) {
  87. $out = array();
  88.  
  89. // Fail if number of items is odd
  90. $first = array();
  91. $second = array();
  92.  
  93. for($i = 0; $i < $pairs * 2; $i++){
  94.  
  95. $nums[] = $i;
  96.  
  97. }
  98.  
  99. shuffle($nums);
  100.  
  101. $first = array_slice($nums, 0, $pairs);
  102. $second = array_slice($nums, $pairs, $pairs);
  103.  
  104. shuffle($second);
  105.  
  106. return array_combine($first, $second);
  107. }
  108.  
  109. function play_game($player1, $player2, &$participants2) {
  110. global $players;
  111.  
  112. // Fetch ratings
  113. $str1 = $players[$player1]->strength;
  114. $str2 = $players[$player2]->strength;
  115.  
  116. // Throw a dice here
  117. // $points = rand(1, $rating1 + $rating2);
  118. if ($str2 <= $str1) {
  119. // player 1 won
  120. $winner = $player1;
  121. $loser = $player2;
  122. } else {
  123. $winner = $player2;
  124. $loser = $player1;
  125. }
  126.  
  127. $participants2[] = $winner;
  128.  
  129. echo '' . $player1 . '[' . $players[$player1]->rating . '] vs ' . $player2 . '[' . $players[$player2]->rating . '], winner is <b>' . $winner . '</b><br/>';
  130.  
  131. $status = 'normal';
  132. // 5% of games end in draw.
  133. // User that go in next tier determined by VP
  134. if (rand(0,100) <= 5) {
  135. $status = 'draw';
  136. }
  137.  
  138. // Add log record
  139. $log_record = array(
  140. 'player1' => $player1,
  141. 'player2' => $player2,
  142. 'winner' => $winner,
  143. 'loser' => $loser,
  144. 'status' => $status
  145. );
  146.  
  147. return $log_record;
  148.  
  149. }
  150.  
  151. function play_tier($participants, &$tourlog) {
  152. global $players;
  153. ksort($participants);
  154. // Decide the pairing
  155. $pairs = random_pairing(count($participants) / 2);
  156.  
  157. $subtour = 0;
  158.  
  159. // Second round
  160. $participants2 = array();
  161. foreach ($pairs as $player1 => $player2) {
  162.  
  163. $tourlog[] = play_game($participants[$player1], $participants[$player2], $participants2);
  164.  
  165. $subtour++;
  166. }
  167.  
  168. return $participants2;
  169. }
  170.  
  171. /**
  172.   * Performance Rating is a hypothetical rating that would result from the games
  173.   * of a single event only. Some chess organizations use the "algorithm of 400"
  174.   * to calculate performance rating. According to this algorithm, performance rating
  175.   * for an event is calculated by taking (1) the rating of each player beaten and
  176.   * adding 400, (2) the rating of each player lost to and subtracting 400, (3)
  177.   * the rating of each player drawn, and (4) summing these figures and dividing by
  178.   * the number of games played.
  179.   *
  180.   * @param mixed $tourlog
  181.   * @param mixed $participants
  182.   */
  183. function display_performance_rating($tourlog, $participants) {
  184. global $players;
  185. //
  186. echo '<div class="perfrating">';
  187. foreach ($participants as $participant) {
  188. $Prating = 0;
  189. $games_played = 0;
  190. // Process tour logs
  191. foreach($tourlog as $tour_record) {
  192.  
  193. if ($tour_record['status'] == 'draw' && ($participant == $tour_record['player1'] || $participant == $tour_record['player2']) ) {
  194. if ($participant == $tour_record['player1']) {
  195. $Prating += $players[$tour_record['player2']]->rating;
  196. } else {
  197. $Prating += $players[$tour_record['player1']]->rating;
  198. }
  199. } elseif ($participant == $tour_record['winner']) {
  200. $Prating += $players[$tour_record['loser']]->rating + 400;
  201. $games_played++;
  202. } elseif($participant == $tour_record['loser']) {
  203. $Prating += $players[$tour_record['winner']]->rating - 400;
  204. $games_played++;
  205. }
  206.  
  207. }
  208.  
  209. // Calculate performance ratings:
  210.  
  211. if ($games_played > 0) {
  212. $perfRating[$participant] = round($Prating / $games_played);
  213. // echo 'PerformanceRating of player ' . $participant . ' on event #'. $tour .' is ' . IntVal($perfRating[$participant]) . '<br/>';
  214. }
  215.  
  216. arsort($perfRating);
  217.  
  218.  
  219. }
  220.  
  221. foreach ($perfRating as $player => $p_rating) {
  222. echo ' PerformanceRating of player ' . $player . ' on event is ' . $p_rating . '<br/>';
  223. }
  224.  
  225. echo '</div>';
  226.  
  227. }
  228.  
  229. function process_ratings($tourlog) {
  230. global $players;
  231.  
  232. // Expected scores
  233. $exp = array();
  234. // Actual scores
  235. $s = array();
  236. // Games played
  237. $games_count = array();
  238.  
  239. foreach ($tourlog as $tour_record) {
  240.  
  241. // Expected scores
  242. $exp[$tour_record['player1']] += 1 / (1 + pow(10, (($players[$tour_record['player2']]->rating - $players[$tour_record['player1']]->rating)/400)));
  243. $exp[$tour_record['player2']] += 1 / (1 + pow(10, (($players[$tour_record['player1']]->rating - $players[$tour_record['player2']]->rating)/400)));
  244.  
  245. $games_count[$tour_record['player1']]++;
  246. $games_count[$tour_record['player2']]++;
  247.  
  248. if ($tour_record['status'] == 'draw') {
  249. $s[$tour_record['player1']] += 0.5;
  250. $s[$tour_record['player2']] += 0.5;
  251. } elseif ($tour_record['winner'] == $tour_record['player1']) {
  252. $s[$tour_record['player1']] += 1;
  253. // $s[$tour_record['player2']] -= 1;
  254. } else {
  255. $s[$tour_record['player2']] += 1;
  256. // $s[$tour_record['player1']] = 1;
  257. }
  258.  
  259. }
  260.  
  261. // Do calculations only for those in the current log
  262. $participants = array_keys($games_count);
  263. foreach ($participants as $player) {
  264. $newrating = round($players[$player]->rating + $players[$player]->get_k_factor() * ($s[$player] - $exp[$player]));
  265. echo '<div>Player ' . $player . ' have expected score ' . $exp[$player] . ' and actual ' . $s[$player] . '</div>';
  266. if ($players[$player]->rating != $newrating) {
  267. 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>';
  268. "INSERT INTO `elo`.`deltas` (
  269. `value`
  270. )
  271. VALUES (
  272. '" . ($s[$player] - $exp[$player]). "'
  273. );"
  274. );
  275.  
  276. // New rating
  277. $players[$player]->rating = $newrating;
  278. $players[$player]->games += $games_count[$player];
  279. }
  280.  
  281. }
  282.  
  283. }
  284.  
  285. // BEGIN
  286.  
  287. global $players;
  288. $players = array();
  289.  
  290. // MYSQL
  291. connect();
  292.  
  293. for ($i = 0; $i <= NUM_PLAYERS; $i++){
  294. $players[$i] = new player(START_RATING); // Beginners rating
  295. }
  296.  
  297. for ($tour = 1; $tour <= NUM_TOURNAMENTS; $tour++){
  298.  
  299. echo '<h3>Tournament '. $tour . '</h3>';
  300. // Choose 20 participants at random
  301. if (count($players) > NUM_PARTICIPANTS) {
  302. $mob = array_keys($players);
  303. shuffle($mob);
  304. $participants = array_combine(range(0,NUM_PARTICIPANTS - 1), array_slice($mob, 0, NUM_PARTICIPANTS));
  305. } elseif (count($players) == NUM_PARTICIPANTS) {
  306. $participants = array_combine(range(0,NUM_PARTICIPANTS - 1), array_keys($players));
  307. } else {
  308. echo 'Too few players'.
  309. }
  310.  
  311. // new log every tournament
  312. $tourlog = array();
  313.  
  314. echo '<h4>Tier 0</h4>';
  315. $participants1 = play_tier($participants, $tourlog);
  316.  
  317. // TIER 1
  318. echo '<h4>Tier 1</h4>';
  319. $participants2 = play_tier($participants1, $tourlog);
  320.  
  321. // TIER 2
  322.  
  323. echo '<h4>Tier 2</h4>';
  324. $participants3 = play_tier($participants2, $tourlog);
  325.  
  326. // TIER 3
  327.  
  328. echo '<h4>Tier 3</h4>';
  329. $participants4 = play_tier($participants3, $tourlog);
  330.  
  331. // FINALS
  332.  
  333. echo '<h4>Finals</h4>';
  334. $winner = play_tier($participants4, $tourlog);
  335.  
  336.  
  337. echo '<span>Tour #' . $tour . ' games are ended, tourlog has ' . count($tourlog) .' records</span>';
  338.  
  339. // RATINGS CALCULATION
  340. display_performance_rating($tourlog, $participants);
  341.  
  342. process_ratings($tourlog);
  343. }
  344.  
  345. // arsort($players;
  346.  
  347. echo '<table>';
  348. echo '<tr><th>Player</th><th>Rating</th><th>Strength</th></tr>';
  349. foreach ($players as $playerid => $player) {
  350. echo '<tr><td>' . $playerid . '</td><td>' . $player->rating . '</td><td>' . $player->strength . '</td></tr>';
  351. }
  352. echo '</table>';
  353. ?>

Report this snippet  

You need to login to post a comment.