Projektowanie stron WWW od podszewki
Losowy artykuł: Bug YouTuba

Artykuły na każdy temat

[PHP] Jak obliczyć WN8?

Dodano 13.10.2015r. o 10:10
Jeżeli nie wiesz czym jest World of Tanks i WN8 to oznacza, że ten artykuł nie jest dla Ciebie. Dla jednych WN8 to wszystko, a dla drugich to tylko liczba, która nie odzwierciedla poziomu gracza. Faktem jest, że dość spora ilość graczy zwraca szczególną uwagę na ten czynnik/prekursor. Przed obliczeniem WN8 potrzebna jest nam tabela z tzw. oczekiwanymi wartościami (Expected Tank Values). Więcej informacji na ten temat można znaleźć tutaj i tutaj. W tym celu wykorzystamy mały fragment kodu robota sieciowego, którego jakiś czas temu napisałem.

Plik config.php wygląda tak:
Kod:
<?php
# MySQL
$config['mysql']['host'] = 'localhost'// Host of database
$config['mysql']['database'] = 'wotstats'// Name of database
$config['mysql']['login'] = ''// Login to database
$config['mysql']['password'] = ''// Password to database
# Other informations
$config['send_request']['protocol'] = 'http://';
$config['send_request']['resource_path'] = 'wot';
?>
Natomiast plik update_wnexpected_tank_values.php wygląda tak:
Kod:
<?php
// Include necessary things
require './config.php';
require './wot.class.php';
// Execute the application
$wot = new wot($config'eu');
var_dump($wot -> update_wnexpected_tank_values_table());
?>
Najlepszym rozwiązaniem aby dane były aktualne jest uruchamianie tego skryptu cyklicznie przy pomocy CRON'a. Aby całość funkcjonowała potrzebujemy oczywiście także wot.class.php:
Kod:
<?php
/**
 * @package Wotstats
 * @subpackage Core
 * @author CapaciousCore
 * @copyright Copyright (C) 2014-2015 CapaciousCore
 */

class wot
{
 private $db;
 private $config;
 private $wnexpected_tank_values;

 public function wot($config$server)
 {
  // Set up config
  $this -> config $config;
  $this -> config['server'] = $server;
  $this -> set_server_domain();
  $this -> set_api_url();
  $this -> db = new mysqli($this -> config['mysql']['host'], $this -> config['mysql']['login'], $this -> config['mysql']['password'], $this -> config['mysql']['database']);
  unset($this -> config['mysql']);

  if($this -> db -> connect_errno !== 0)
  {
   throw new Exception('Could not connect to database');
  }
  else
  {
   $this -> db -> set_charset('utf8');
  }
 }

 public function update_wnexpected_tank_values_table($force_update false)
 {
  $stmt $this -> db -> prepare('SELECT MAX(version) as version FROM wnexpected_tank_values');
  $stmt -> execute();
  $stmt -> store_result();
  $stmt -> bind_result($current_version);
  $stmt -> fetch();
  $wnexpected_tank_values_table json_decode(file_get_contents('http://www.wnefficiency.net/exp/expected_tank_values_latest.json'));
  $source_version  $wnexpected_tank_values_table -> header -> version;

  if($wnexpected_tank_values_table !== null)
  {
   if($force_update === true || $source_version $current_version)
   {
    $stmt $this -> db -> prepare('INSERT INTO wnexpected_tank_values VALUES (?, ?, ?, ?, ?, ?, ?)');
    $this -> db -> autocommit(false);

    foreach($wnexpected_tank_values_table -> data as $tank_values_table)
    {
     $stmt -> bind_param('iiddddd'$source_version$tank_values_table -> IDNum$tank_values_table -> expFrag$tank_values_table -> expDamage$tank_values_table -> expSpot$tank_values_table -> expDef$tank_values_table -> expWinRate);
     $stmt -> execute();

     if($stmt -> errno !== && $stmt -> errno !== 1062)
     {
      // Log this fail!
      // var_dump($stmt -> errno, $stmt -> error);
      $is_failed true;

      break;
     }
    }

    if($this -> db -> commit() === false)
    {
     $this -> db -> rollback();
     $is_failed true;
    }

    $this -> db -> autocommit(true);
   }
  }
  else
  {
   $is_failed true;
  }

  return $is_failed === null;
 }

 private function get_wnexpected_tank_values_table($version null)
 {
  // $version = null means current version
  $stmt $this -> db -> prepare('SELECT tank_id, ROUND(expected_kills, 2), ROUND(expected_damage, 2), ROUND(expected_detections, 2), ROUND(expected_defense, 2), ROUND(expected_winrate, 2) FROM wnexpected_tank_values WHERE version = '.($version === null '(SELECT MAX(version) FROM wnexpected_tank_values)' $version).' ORDER BY tank_id');
  $stmt -> execute();
  $stmt -> bind_result($tank_id$expected_kills$expected_damage$expected_detections$expected_defense$expected_winrate);

  while($stmt -> fetch())
  {
   $wnexpected_tank_values[$tank_id] = array($expected_kills$expected_damage$expected_detections$expected_defense$expected_winrate);
  }

  return $wnexpected_tank_values;
 }

 private function send_request($address$query = array(), $stop_upon_crash false)
 {
  // It's just a prototype method that should be improved about error handling even
  $url $this -> config['api_url'].$address.'/?application_id=demo&'.http_build_query($query);

  while(1)
  {
   // I recommend use curl library
   $data file_get_contents($url);

   if($data !== false)
   {
    $data json_decode($data);

    if($data -> status === 'ok')
    {
     return $data;
    }
    else if($stop_upon_crash === true && $data -> status === 'error')
    {
     return $data;
    }
   }

   sleep(1);
  }
 }

 private function set_server_domain()
 {
  $this -> config['domain'] = ($this -> config['server'] === 'na' 'com' $this -> config['server']);
 }

 private function set_api_url()
 {
  $this -> config['api_url'] = $this -> config['send_request']['protocol'].'api.worldoftanks.'.$this -> config['domain'].'/';
 }

 public function wn8($account_id$as_fraction false$calculate_for_each_tank false$list_of_tanks null)
 {
  if($this -> wnexpected_tank_values === null)
  {
   $this -> wnexpected_tank_values $this -> get_wnexpected_tank_values_table();
  }

  $data $this -> send_request('wot/tanks/stats', array('account_id' => $account_id'fields' => 'tank_id,all.battles,all.frags,all.damage_dealt,all.spotted,all.dropped_capture_points,all.wins')) -> data -> {$account_id};

  if($data !== null)
  {
   // Step 1
   foreach($data as $vehicle_info)
   {
    if($this -> wnexpected_tank_values[$vehicle_info -> tank_id] !== null)
    {
     // Forewarned is forearmed
     if($vehicle_info -> all -> battles 0)
     {
      // Calculate it!
      $battles += $vehicle_info -> all -> battles;
      $exp_frag[$vehicle_info -> tank_id] = $this -> wnexpected_tank_values[$vehicle_info -> tank_id][0] * $vehicle_info -> all -> battles;
      $exp_dmg[$vehicle_info -> tank_id] = $this -> wnexpected_tank_values[$vehicle_info -> tank_id][1] * $vehicle_info -> all -> battles;
      $exp_spot[$vehicle_info -> tank_id] = $this -> wnexpected_tank_values[$vehicle_info -> tank_id][2] * $vehicle_info -> all -> battles;
      $exp_def[$vehicle_info -> tank_id] = $this -> wnexpected_tank_values[$vehicle_info -> tank_id][3] * $vehicle_info -> all -> battles;
      $exp_wins[$vehicle_info -> tank_id] = $this -> wnexpected_tank_values[$vehicle_info -> tank_id][4] * $vehicle_info -> all -> battles 0.01;
      // Additional necessary statistics
      $account_statistics['frags'][$vehicle_info -> tank_id] = $vehicle_info -> all -> frags;
      $account_statistics['damage_dealt'][$vehicle_info -> tank_id] = $vehicle_info -> all -> damage_dealt;
      $account_statistics['spotted'][$vehicle_info -> tank_id] = $vehicle_info -> all -> spotted;
      $account_statistics['dropped_capture_points'][$vehicle_info -> tank_id] = $vehicle_info -> all -> dropped_capture_points;
      $account_statistics['wins'][$vehicle_info -> tank_id] = $vehicle_info -> all -> wins;
     }
    }
    // Tank missing in expected tank values table so add it to the list
    else
    {
     // Log this ids!
     // $missing_tanks_ids[] = $vehicle_info -> tank_id;
    }
   }

   if($battles 0)
   {
    if($calculate_for_each_tank === true XOR $list_of_tanks !== null)
    {
     foreach(($list_of_tanks !== null $list_of_tanks array_keys($exp_frag)) as $tank_id)
     {
      if($list_of_tanks === null || $list_of_tanks !== null && $exp_frag[$tank_id] !== null)
      {
       // Step 2
       $r_frag $account_statistics['frags'][$tank_id] / $exp_frag[$tank_id];
       $r_dmg $account_statistics['damage_dealt'][$tank_id] / $exp_dmg[$tank_id];
       $r_spot $account_statistics['spotted'][$tank_id] / $exp_spot[$tank_id];
       $r_def $account_statistics['dropped_capture_points'][$tank_id] / $exp_def[$tank_id];
       $r_wins $account_statistics['wins'][$tank_id] / $exp_wins[$tank_id];
       // Step 3
       $r_dmg_c max(0, ($r_dmg 0.22) / (0.22));
       $r_frag_c max(0min($r_dmg_c 0.2, ($r_frag 0.12) / (0.12)));
       $r_spot_c max(0min($r_dmg_c 0.1, ($r_spot 0.38) / (0.38)));
       $r_def_c max(0min($r_dmg_c 0.1, ($r_def 0.10) / (0.10)));
       $r_wins_c max(0, ($r_wins 0.71) / (0.71));
       // Discovered the holy grail! Calculate the WN8
       $wn8[$tank_id] = 980 $r_dmg_c 210 $r_dmg_c $r_frag_c 155 $r_frag_c $r_spot_c 75 $r_def_c $r_frag_c 145 min(1.8$r_wins_c);
       $wn8[$tank_id] = round($wn8[$tank_id], ($as_fraction === true 0));

       if($as_fraction === false)
       {
        $wn8[$tank_id] = (int)$wn8[$tank_id];
       }
      }
     }
    }

    // Step 2 (overall)
    $r_frag array_sum($account_statistics['frags']) / array_sum($exp_frag);
    $r_dmg array_sum($account_statistics['damage_dealt']) / array_sum($exp_dmg);
    $r_spot array_sum($account_statistics['spotted']) / array_sum($exp_spot);
    $r_def array_sum($account_statistics['dropped_capture_points']) / array_sum($exp_def);
    $r_wins array_sum($account_statistics['wins']) / array_sum($exp_wins);
    // Step 3 (overall)
    $r_dmg_c max(0, ($r_dmg 0.22) / (0.22));
    $r_frag_c max(0min($r_dmg_c 0.2, ($r_frag 0.12) / (0.12)));
    $r_spot_c max(0min($r_dmg_c 0.1, ($r_spot 0.38) / (0.38)));
    $r_def_c max(0min($r_dmg_c 0.1, ($r_def 0.10) / (0.10)));
    $r_wins_c max(0, ($r_wins 0.71) / (0.71));
    // Discovered the holy grail! Calculate the WN8 (overall)
    $wn8['overall'] = (980 $r_dmg_c) + (210 $r_dmg_c $r_frag_c) + (155 $r_frag_c $r_spot_c) + (75 $r_def_c $r_frag_c) + (145 min(1.8$r_wins_c));
    $wn8['overall'] = round($wn8['overall'], ($as_fraction === true 0));

    if($as_fraction === false)
    {
     $wn8['overall'] = (int)$wn8['overall'];
    }

    // Return our mighty number
    return ($calculate_for_each_tank === false && $list_of_tanks === null $wn8['overall'] : $wn8);
   }
  }

  return null;
 }
}
?>
Dodatkowo potrzebna będzie nam tabela wnexpected_tank_values, którą zbudujemy przy pomocy poniższego zapytania:
Kod:
CREATE TABLE IF NOT EXISTS `wnexpected_tank_values` (
  `version` tinyint(1) unsigned NOT NULL,
  `tank_id` smallint(1) unsigned NOT NULL,
  `expected_kills` float unsigned NOT NULL,
  `expected_damage` float unsigned NOT NULL,
  `expected_detections` float unsigned NOT NULL,
  `expected_defense` float unsigned NOT NULL,
  `expected_winrate` float unsigned NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;

ALTER TABLE `wnexpected_tank_values` ADD PRIMARY KEY (`version`,`tank_id`);
Szukając po sieci rozwiązań odnośnie obliczania WN8 natrafiałem jedynie na karykatury lub mizerne i nieoptymalne próby obliczania tego współczynnika. W związku z tym postanowiłem napisać to osobiście. Efekt mojej pracy można zobaczyć w listingu powyżej. Aby obliczyć WN8 danego gracza wywołujemy calculate_wn8.php, który wygląda następująco:
Kod:
<?php
// Include necessary things
require './config.php';
require './wot.class.php';
// Execute the application
$wot = new wot($config'eu');
var_dump($wot -> wn8(509459275)); // Pinkman_Jesse - supposedly the best player on the european server World of Tanks
?>
Oczywiście prezentowany kod jest tylko przykładem jak i fragmentem większej układanki. Układanki na którą składa się wiele plików i klas robota sieciowego jednak ja uznałem, że podam tylko to co niezbędne. Polecam również poeksperymentować z argumentami przy metodzie wn8(). A jako ciekawostkę powiem, że deweloperzy pracują już formułą WN9. Ponadto znając algorytm i będąc rozgarniętą osobą można manipulować tą statystyką.

Komentarze

Brak komentarzy

Dodaj komentarz

Zostaw komentarz jeżeli możesz! Nie bądź przysłowiowym botem! Nie bądź obojętny! Ciebie to nic nie kosztuje, a mi sprawi uśmiech na twarzy.
Zezwolono używać: BBCode
Zabroniono używać:
znaczników HTML

(Wymagany)

(Wymagany, niepublikowany)

(Nie wymagana)

Token:

Obrazek dla bota

(Przepisz tylko cyfry!)

(Wymagana)