Skip to content

Instantly share code, notes, and snippets.

@joefaron
Last active August 9, 2023 02:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joefaron/b5018373830a7a140c27f90536f3da5b to your computer and use it in GitHub Desktop.
Save joefaron/b5018373830a7a140c27f90536f3da5b to your computer and use it in GitHub Desktop.
WSL CS Odds Predictions (World Surf League - Challenger Series)
<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