Last active
August 9, 2023 02:21
-
-
Save joefaron/b5018373830a7a140c27f90536f3da5b to your computer and use it in GitHub Desktop.
WSL CS Odds Predictions (World Surf League - Challenger Series)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<PRE><?php | |
ini_set('display_errors', 1); | |
ini_set('display_startup_errors', 1); | |
error_reporting(E_ERROR); | |
ini_set('max_execution_time', 0); | |
?> | |
__ _______ _ ___ ___ ___ ____ | |
\ \ / / ____| | |__ \ / _ \__ \|___ \ | |
\ \ /\ / / (___ | | ______ ) | | | | ) | __) | | |
\ \/ \/ / \___ \| | |______| / /| | | |/ / |__ < | |
\ /\ / ____) | |____ / /_| |_| / /_ ___) | | |
\/ \/ |_____/|______| |____|\___/____|____/ | |
____ _ _ _ _____ _ | |
/ ____| | | | | / ____| (_) | |
| | | |__ __ _| | | ___ _ __ __ _ ___ _ __ | (___ ___ _ __ _ ___ ___ | |
| | | '_ \ / _` | | |/ _ \ '_ \ / _` |/ _ \ '__| \___ \ / _ \ '__| |/ _ \/ __| | |
| |____| | | | (_| | | | __/ | | | (_| | __/ | ____) | __/ | | | __/\__ \ | |
\_____|_| |_|\__,_|_|_|\___|_| |_|\__, |\___|_| |_____/ \___|_| |_|\___||___/ | |
__/ | | |
|___/ | |
WSL CS 2023 - World Surf League - Challenger Series - Odds to make the CT based on current points and remaining events | |
View code at: <a href="https://gist.github.com/joefaron/b5018373830a7a140c27f90536f3da5b">https://gist.github.com/joefaron/b5018373830a7a140c27f90536f3da5b</a> | |
View results at: <a href="https://joefaron.com/wsl-odds-to-make-the-ct-2023/">https://joefaron.com/wsl-odds-to-make-the-ct-2023/</a> | |
This work is licensed under a Creative Commons Attribution 4.0 International License. | |
You are free to: | |
- Share — copy and redistribute the material in any medium or format | |
- Adapt — remix, transform, and build upon the material | |
for any purpose, even commercially. | |
The licensor cannot revoke these freedoms as long as you follow the license terms. | |
Under the following terms: | |
- Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. | |
Original work by Joe Faron | |
<?php | |
// event place to points array, from typical event like: https://www.worldsurfleague.com/events/2023/cs/144/ballito-pro/prizes | |
$original_place_to_points = array( | |
1 => 10000, 2 => 7800, 3 => 6085, 5 => 4745, 9 => 3320, | |
17 => 1900, 25 => 1700, 33 => 700, 49 => 600, 65 => 300, | |
73 => 250, 81=>0 | |
); | |
// data from: https://www.worldsurfleague.com/athletes/tour/mcs?year=2023 | |
$surfers_text=<<<EOM | |
Rank Name 1 2 3 4 Total Points | |
1 "Cole Houshmand | |
United States" 700 10,000 10,000 3,320 24,020 | |
2 "Jacob Willcox | |
Australia" 4,745 7,800 4,745 3,320 20,610 | |
3 "Crosby Colapinto | |
United States" 6,085 1,700 700 7,800 16,285 | |
4 "Eli Hanneman | |
Hawaii" 300 3,320 1,700 10,000 15,320 | |
5 "Frederico Morais | |
Portugal" 1,700 4,745 7,800 700 14,945 | |
6 "Imaikalani deVault | |
Hawaii" 7,800 3,320 1,700 1,700 14,520 | |
7 "Samuel Pupo | |
Brazil" 10,000 700 700 1,700 13,100 | |
8 "Morgan Cibilic | |
Australia" 1,700 4,745 3,320 3,320 13,085 | |
9 "Jackson Baker | |
Australia" 1,700 6,085 3,320 1,700 12,805 | |
10 "Kade Matson | |
United States" 600 4,745 6,085 700 12,130 | |
11 "Jett Schilling | |
United States" 6,085 1,900 700 3,320 12,005 | |
12 "Joan Duru | |
France" 700 3,320 6,085 1,700 11,805 | |
13 "Nolan Rapoza | |
United States" - - 4,745 6,085 10,830 | |
14 "Michael Rodrigues | |
Brazil" 4,745 700 1,900 3,320 10,665 | |
15 "Jake Marshall | |
United States" 700 1,700 3,320 4,745 10,465 | |
15 "Jadson Andre | |
Brazil" 4,745 1,700 700 3,320 10,465 | |
15 "George Pittar | |
Australia" 1,700 700 3,320 4,745 10,465 | |
18 "Marco Mignot | |
France" 700 6,085 1,900 600 9,285 | |
19 "Mateus Herdy | |
Brazil" 1,900 700 1,900 4,745 9,245 | |
20 "Mikey McDonagh | |
Australia" 3,320 3,320 1,900 700 9,240 | |
21 "Reef Heazlewood | |
Australia" 1,900 1,700 600 4,745 8,945 | |
22 "Kauli Vaast | |
France" 700 600 4,745 1,900 7,945 | |
23 "Joel Vaughan | |
Australia" 700 1,900 3,320 1,900 7,820 | |
24 "Jorgann Couzinet | |
France" 4,745 600 1,700 700 7,745 | |
25 "Deivid Silva | |
Brazil" 600 1,700 1,900 3,320 7,520 | |
26 "Alejo Muniz | |
Brazil" 700 700 4,745 700 6,845 | |
27 "Adin Masencamp | |
South Africa" 3,320 1,900 700 700 6,620 | |
28 "Timothe Bisso | |
France" 3,320 600* 1,900 700 6,520 | |
29 "Lucca Mesinas | |
Peru" 1,900 600 3,320 600 6,420 | |
30 "Jackson Bunch | |
Hawaii" 3,320 600 1,700 600 6,220 | |
31 "Nat Young | |
United States" 1,900 700 1,700 1,900 6,200 | |
32 "CTKanoa Igarashi | |
Japan" - - - 6,085 6,085 | |
33 "Dylan Moffat | |
Australia" 1,700 1,900 1,700 700 6,000 | |
34 "Marc Lacomare | |
France" 600 4,745 300 250 5,895 | |
35 "Maxime Huscenot | |
France" 700 3,320 700 700 5,420 | |
35 "Edgard Groggia | |
Brazil" 3,320 700 700 700 5,420 | |
37 "Eithan Osborne | |
United States" 600 3,320 700 600 5,220 | |
37 "Justin Becret | |
France" 700 3,320 600 600 5,220 | |
39 "Ezekiel Lau | |
Hawaii" 1,900 700 600 1,900 5,100 | |
40 "Lucas Silveira | |
Brazil" 1,900 1,900 600 600 5,000 | |
40 "Carlos Munoz | |
Costa Rica" 700 700 1,700 1,900 5,000 | |
42 "Luke Thompson | |
South Africa" 300 1,900 600 1,900 4,700 | |
42 "Miguel Tudela | |
Peru" 1,700 1,700 600 700 4,700 | |
44 "Alister Reginato | |
Australia" 250 700 300 3,320 4,570 | |
45 "Ian Gouveia | |
Brazil" 250 700 1,900 1,700 4,550 | |
46 "Dimitri Poulos | |
United States" 600 300 3,320 300 4,520 | |
46 "Evan Geiselman | |
United States" 3,320 600 600 -* 4,520 | |
48 "Leo Casal | |
Brazil" 1,700 250 600 1,700 4,250 | |
49 "Rafael Teixeira | |
Brazil" 300 300 1,700 1,900 4,200 | |
50 "Daniel Emslie | |
South Africa" 250 250 3,320 250 4,070 | |
51 "Kolohe Andino | |
United States" 600 700 1,900 600 3,800 | |
51 "Kalani Ball | |
Australia" 600 1,900 700 600 3,800 | |
51 "Te Kehukehu Butler | |
New Zealand" 600 1,900 600 700 3,800 | |
54 "Jarvis Earle | |
Australia" 1,900 600 600 600 3,700 | |
55 "Taj Lindblad | |
United States" - 3,320 - 250 3,570 | |
56 "CTRio Waida | |
Indonesia" 3,320 - - - 3,320 | |
56 "CTJoao Chianca | |
Brazil" 3,320 - - - 3,320 | |
58 "Hiroto Ohhara | |
Japan" 1,700 600 700 300 3,300 | |
59 "Keanu Asing | |
Hawaii" 1,900 - 600 700 3,200 | |
60 "Shion Crawford | |
Hawaii" 600 300 300 1,900 3,100 | |
60 "Ketut Agus | |
Indonesia" 700 1,700 700 - 3,100 | |
60 "Conner Coffin | |
United States" 700 700 - 1,700 3,100 | |
63 "Sheldon Simkus | |
Australia" 600 300 250 1,700 2,850 | |
64 "Brodi Sale | |
Hawaii" 700 700 600 700 2,700 | |
65 "Jordan Lawler | |
Australia" 300 300 600 700 1,900 | |
66 "Billy Stairmand | |
New Zealand" 250 600 700 300 1,850 | |
66 "Josh Burke | |
Barbados" 700 300 600 250 1,850 | |
68 "Kian Martin | |
Sweden" 300 600 600 250 1,750 | |
69 "Mihimana Braye | |
French Polynesia" - 1,700* - - 1,700 | |
70 "Oney Anwar | |
Indonesia" 700 600 - 300 1,600 | |
71 "Joshua Moniz | |
Hawaii" 300 700 250 300 1,550 | |
72 "Guillermo Satt | |
Chile" 300 250 700 250 1,500 | |
73 "John Mark Tokong | |
Philippines" 600 300 250 300 1,450 | |
73 "Tiago Carrique | |
France" 300 250 300 600 1,450 | |
75 "Connor Slijpen | |
South Africa" 250 600 300 250 1,400 | |
75 "Jabe Swierkocki | |
United States" 250 250 300 600 1,400 | |
75 "Ryan Kainalo | |
Brazil" 600 250 250 300 1,400 | |
78 "Kai Martin | |
Hawaii" - 600 700 - 1,300 | |
78 "Gatien Delahaye | |
France" - - 700 600 1,300 | |
80 "Michael Dunphy | |
United States" - 600 - 600 1,200 | |
80 "Adur Amatriain | |
Basque Country" 600 600 - - 1,200 | |
82 "Daiki Tanaka | |
Japan" 250 250 300 300 1,100 | |
83 "Sheldon Paishon | |
Hawaii" - 600 - 250 850 | |
83 "Joshe Faulkner | |
South Africa" - - 250 600 850 | |
85 "John Mel | |
United States" - - - 700* 700 | |
85 "CTRyan Callinan | |
Australia" 700 - - - 700 | |
85 "Krystian Kymerson | |
Brazil" - - 700 - 700 | |
85 "Soli Bailey | |
Australia" - 700 - - 700 | |
89 "CTConnor O'Leary | |
Australia" 600 - - - 600 | |
89 "CTCaio Ibelli | |
Brazil" 600 - - - 600 | |
89 "CTLeonardo Fioravanti | |
Italy" - - - 600 600 | |
89 "Teva Bouchgua | |
Morocco" - 600 - - 600 | |
89 "Joh Azuchi | |
Japan" - - 600 - 600 | |
89 "CTCallum Robson | |
Australia" 600 - - - 600 | |
89 "Marlon Harrison | |
Australia" - - - 600* 600 | |
96 "Luke Van Wyk | |
South Africa" - - 300 - 300 | |
96 "Kyuss King | |
Australia" - 300 - - 300 | |
98 "Saxon Reber | |
Australia" - 250 - - 250 | |
98 "Thomas Lindhorst | |
South Africa" - - 250 - 250 | |
98 "Cooper Davies | |
Australia" 250 - - - 250 | |
EOM; | |
if($_POST['surfers_text']){ | |
$surfers_text=$_POST['surfers_text']; | |
} | |
echo "<form method='post' action='?'><textarea rows='5' cols='70' name='surfers_text'>$surfers_text</textarea><BR><input type='submit' value='Submit New CSV Results'></form>"; | |
function parseSurfers($input) { | |
$stream = fopen('php://temp', 'r+'); | |
fwrite($stream, $input); | |
rewind($stream); | |
$result = []; | |
// skip the header | |
fgetcsv($stream, 0, "\t"); | |
while ($row = fgetcsv($stream, 0, "\t")) { | |
$nameAndCountry = explode("\n", $row[1]); | |
$name = trim($nameAndCountry[0]); | |
$eventScores = array_slice($row, 2, 4); // extract columns 2 through 5 | |
$eventScores = array_map(function ($value) { | |
$value=str_replace(',', '', $value); | |
if(!is_numeric($value))$value=0; | |
return $value; // remove comma for thousands for accurate arithmetic operations | |
}, $eventScores); | |
$result[$name] = $eventScores; | |
} | |
fclose($stream); | |
return $result; | |
} | |
$surfers=parseSurfers($surfers_text); | |
//take only first X surfers... (for now) | |
$take_only_top=80; | |
$num_simulations=1000; | |
$events_total=6; | |
foreach($surfers as $name=>$scores){ | |
$events_left=$events_total-count($scores);break;//1st place person should have results for all events | |
} | |
$events_left=2; | |
$start=microtime(1); | |
$surfers = array_slice($surfers, 0, $take_only_top); | |
// fill out place array, so each place has point value | |
$place_to_points = []; | |
$prev_value = end($original_place_to_points); // default to the last value | |
for ($i = count($surfers); $i >= 1; $i--) { | |
if (isset($original_place_to_points[$i])) { | |
$prev_value = $original_place_to_points[$i]; | |
} | |
$place_to_points[$i] = $prev_value; | |
} | |
ksort($place_to_points); | |
$results = runSimulation2($surfers, $place_to_points, $num_simulations,$events_left); | |
echo "\n$num_simulations simulations - took ".number_format(microtime(1)-$start,2)." secs\n"; | |
echo "percentage of time surfer (in current top $take_only_top) placed in final position, with all having equally randomized scores in $events_left more event(s).\n\n"; | |
echo "Event Place\tPoints\n"; | |
foreach ($original_place_to_points as $place => $points) { | |
echo "$place\t\t$points\n"; | |
} | |
?> | |
<link rel="stylesheet" type="text/css" href="//cdn.datatables.net/1.11.5/css/jquery.dataTables.css"> | |
<script type="text/javascript" charset="utf8" src="//code.jquery.com/jquery-3.5.1.js"></script> | |
<script type="text/javascript" charset="utf8" src="//cdn.datatables.net/1.11.5/js/jquery.dataTables.js"></script> | |
<table id="sortableTable" class="display"> | |
<thead> | |
<tr> | |
<th>Rank</th> | |
<th>Name</th> | |
<?php foreach ($results as $name => $odds): foreach ($odds as $key=>$score): ?> | |
<th>Place <?php echo $key ?></th> | |
<?php endforeach; break; endforeach; | |
for ($event_num = 0; $event_num < 6; $event_num++): ?> | |
<th>Event <?php echo $event_num + 1; ?></th> | |
<?php endfor; ?> | |
<th>Total Pts</th> | |
</tr> | |
</thead> | |
<tbody> | |
<?php | |
$rank=0;foreach ($results as $name => $odds): $rank++;?> | |
<tr> | |
<td><?php echo $rank ?></td> | |
<td><?php echo $name ?></td> | |
<?php foreach ($odds as $key=>$chance): ?> | |
<td><?php echo $chance ?></td> | |
<?php endforeach; | |
$pts=0; | |
for ($event_num = 0; $event_num < 6; $event_num++): $pts+=$surfers[$name][$event_num];?> | |
<th><?php echo number_format($surfers[$name][$event_num]) ?></th> | |
<?php endfor; ?> | |
<th><?php echo number_format($pts)?></th> | |
</tr> | |
<?php endforeach; ?> | |
</tbody> | |
</table> | |
<script> | |
$(document).ready(function() { | |
$('#sortableTable').DataTable({ | |
order: [], // Start with no sorting. | |
pageLength: 50, // Show 50 rows by default | |
columnDefs: [{ | |
targets: '_all', // Apply to all columns | |
orderSequence: ['desc', 'asc'] // First click will sort descending, second will sort ascending | |
}] | |
}); | |
}); | |
</script> | |
<?php | |
function simulateEventForAllSurfers($place_to_points_filled, $surfers) { | |
$shuffledSurfers = $surfers; | |
shuffle($shuffledSurfers); // This will randomly order the surfers | |
$eventResults = []; | |
foreach ($shuffledSurfers as $index => $surfer) { | |
$place = $index + 1; // Because array index starts at 0 | |
$eventResults[$surfer] = $place_to_points_filled[$place]; | |
} | |
return $eventResults; | |
} | |
function runSimulation2($surfers, $place_to_points, $num_simulations,$events_left) { | |
$placeCount = array_fill_keys(array_keys($surfers), array_fill(1, 10, 0)); | |
for ($i = 0; $i < $num_simulations; $i++) { | |
$tempSurfers = $surfers; | |
for ($j = 0; $j < $events_left; $j++) { | |
$eventResults = simulateEventForAllSurfers($place_to_points, array_keys($surfers)); | |
foreach ($eventResults as $name => $points) { | |
$tempSurfers[$name][] = $points; | |
} | |
} | |
foreach($tempSurfers as $name => $points){ | |
$tempSurfersSum[$name] = array_sum($points); | |
} | |
arsort($tempSurfersSum); | |
$top10 = array_slice($tempSurfersSum, 0, 10, true); | |
$position = 1; | |
foreach ($top10 as $name => $points) { | |
$placeCount[$name][$position]++; | |
if ($position === 10) { | |
$tenth_place_sum += $points; | |
} | |
$position++; | |
} | |
} | |
$odds = []; | |
foreach ($placeCount as $name => $counts) { | |
for ($position = 1; $position <= 10; $position++) { | |
$odds[$name][$position] = (number_format(($counts[$position] / $num_simulations) * 100, 3) + 0) . "%"; | |
} | |
} | |
echo "10th place average total points after all events: " . number_format($tenth_place_sum / $num_simulations,0) . "\n"; | |
return $odds; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment