1

I am trying to calculate the winning order of golfers when they are tied in a competition.

These golf competitions are using the "stableford" points scoring system, where you score points per hole with the highest points winning. Compared to normal golf "stroke play" where the lowest score wins (though this also has the countback system, only calculating the lowest score in the event of a tie...)

The rules are to use a "countback". i.e., if scores are tied after 9 holes, the best placed of the ties is the best score from the last 8 holes. then 7 holes, etc.

The best I can come up with is 2 arrays.

  1. An array with all the players who tied in a given round. ($ties)
  2. One which has the full score data in (referencing the database playerid) for all 9 holes. ($tie_perhole)

I loop through array 1, pulling data from array 2 and using the following formula to create a temporary array with the highest score:

$max = array_keys($array,max($array));

If $max only has 1 item, this player is the highest scorer. the loop through the first array is "by reference", so on the next iteration of the loop, his playerid is now longer in the array, thus ignored. this continues until there is only 1 playerid left in the first array.

However, it only works if a single player wins in each iteration. The scenario that doesn't work is if a sub-set of players tie on any iterations / countbacks.

I think my problem is the current structure I have will need the original $ties array to become split, and then to continue to iterate through the split arrays in the same way...

As an example...

The $ties array is as follows:

Array 
( 
    [18] => Array 
        ( 
            [0] => 77 
            [1] => 79 
            [2] => 76 
            [3] => 78 
        ) 
)

The $tie_perhole (score data) array is as follows:

Array 
( 
    [18] => Array 
        ( 
            [77] => Array 
                ( 
                    [9] => 18 
                    [8] => 16 
                    [7] => 14 
                    [6] => 12 
                    [5] => 10 
                    [4] => 8 
                    [3] => 6 
                    [2] => 4 
                    [1] => 2 
                ) 
            [79] => Array 
                ( 
                    [9] => 18 
                    [8] => 17 
                    [7] => 15 
                    [6] => 14 
                    [5] => 11 
                    [4] => 9 
                    [3] => 7 
                    [2] => 5 
                    [1] => 3 
                ) 
            [76] => Array 
                ( 
                    [9] => 18 
                    [8] => 16 
                    [7] => 14 
                    [6] => 12 
                    [5] => 10 
                    [4] => 8 
                    [3] => 6 
                    [2] => 4 
                    [1] => 2 
                ) 
            [78] => Array 
                ( 
                    [9] => 18 
                    [8] => 17 
                    [7] => 15 
                    [6] => 13 
                    [5] => 11 
                    [4] => 9 
                    [3] => 7 
                    [2] => 5 
                    [1] => 3 
                ) 
        ) 
) 

So in this competition, player's 78 and 79 score highest on the 8th hole countback (17pts), so 1st and 2nd should be between them. Player 79 should then be 1st on the 6th hole countback (14pts, compared to 13pts). The same should occur for 3rd and 4th place with the 2 remaining other players.

There are other scenarios that can occur here, in that within a competition, there will likely be many groups of players (of different amounts) on different tied points through the leaderboard.

Also note, there will be some players on the leaderboard who are NOT tied and stay in their current outright position.

The basics of the working code I have is:

foreach ($ties as $comparekey => &$compareval) {
$tie_loop = 0;
for ($m = 9; $m >= 1; $m--) {
    $compare = array();
    foreach ($compareval as $tie) {
        $compare[$tie] = $tie_perhole[$comparekey][$tie][$m];
    }
    $row = array_keys($compare,max($compare));

    if (count($row) == 1) {
        $indexties = array_search($row[0], $ties[$comparekey]);
        unset($ties[$comparekey][$indexties]);
        // Now update this "winners" finishing position in a sorted array
        // This is a multidimensional array too, with custom function...
        $indexresults = searchForId($row[0], $comp_results_arr);
        $comp_results_arr[$indexresults][position] = $tie_loop;
        $tie_loop++;
    }
    // I think I need conditions here to filter if a subset of players tie
    // Other than count($row) == 1
    // And possibly splitting out into multiple $ties arrays for each thread...

    if (empty($ties[$comparekey])) {
        break;
    }
}
}
usort($comp_results_arr, 'compare_posn_asc');
foreach($comp_results_arr as $row) {
    //echo an HTML table...
}

Thanks in advance for any helpful insights, tips, thoughts, etc...

Robert Cathay asked for more scenarios. So here is another...

The leaderboard actually has more entrants (player 26 had a bad round...), but the code i need help with is only bothered about the ties within the leaderboard.

Summary leaderboard:

Points  Player
21      48
21      75
20      73
20      1
13      26

This example produces a $tie_perhole array of:

Array 
( 
    [21] => Array 
        ( 
            [75] => Array 
                ( 
                    [9] => 21 
                    [8] => 19 
                    [7] => 16 
                    [6] => 14 
                    [5] => 12 
                    [4] => 9 
                    [3] => 7 
                    [2] => 5 
                    [1] => 3 
                ) 
            [48] => Array 
            ( 
                [9] => 21 
                [8] => 19 
                [7] => 16 
                [6] => 13 
                [5] => 11 
                [4] => 9 
                [3] => 8 
                [2] => 5 
                [1] => 3 
            ) 
    ) 
[20] => Array 
    ( 
        [73] => Array 
            ( 
                [9] => 20 
                [8] => 18 
                [7] => 16 
                [6] => 13 
                [5] => 11 
                [4] => 8 
                [3] => 6 
                [2] => 5 
                [1] => 3 
            ) 
        [1] => Array 
            ( 
                [9] => 20 
                [8] => 17 
                [7] => 16 
                [6] => 14 
                [5] => 12 
                [4] => 9 
                [3] => 7 
                [2] => 4 
                [1] => 2 
            ) 
    ) 
) 

In this example, the array shows that players 75 and 48 scored 21 points that player 75 will eventually win on the 6th hole countback (14pts compared to 13pts) and player 48 is 2nd. In the next tied group, players 73 and 1 scored 20 points, and player 73 will win this group on the 8th hole countback and finishes 3rd (18 pts compared to 17 pts), with player 1 in 4th. player 26 is then 5th.

Note, the $tie_loop is added to another array to calculate the 1st to 5th place finishing positions, so that is working.

Hopefully that is enough to help.

Dan Burt
  • 47
  • 1
  • 7
  • The highest score is ranked first? It's golf. Your "other scenarios" throw me off a bit. If you can explain those a little better, I can give you an answer. Could you give an array demonstrating these other scenarios? How are the groups of players scored in with each other? Will they have their own array of placements? I guess what I'm asking is, show an example that includes all scenarios. – Evadecaptcha Aug 04 '15 at 15:59
  • sorry, golf can be a confusing world... there is normal "stroke play" which is lowest score and what most people are used to. but for amateurs, "stableford" is the most common, that converts into a points scoring system (highest points wins...) i will edit the main article to demo some other scenarios... – Dan Burt Aug 04 '15 at 18:26

1 Answers1

1

Ok, so I don't understand golf at all... hahaha BUT! I think I got the gist of this problem, so heres my solution.

<?php
/**
* Author    : Carlos Alaniz
* Email     : Carlos.glvn1993@gmail.com
* Porpuse   : Stackoverflow example
* Date      : Aug/04/2015
**/


$golfers = [
"A" => [1,5,9,1,1,2,3,4,9],
"B" => [2,6,4,2,4,4,1,9,3],
"C" => [3,4,9,8,1,1,5,1,3],
"D" => [1,5,1,1,1,5,4,5,8]
];

//Iterate over scores.
function get_winners(&$golfers, $hole = 9){
    $positions = array();                               // The score numer is the key!
    foreach ($golfers as $golfer=>$score ) {            // Get key and value
        $score_sub = array_slice($score,0,$hole);       // Get the scores subset, first iteration is always all holes
        $total_score = (string)array_sum($score_sub);   // Get the key 
        if(!isset($positions[$total_score])){
            $positions[$total_score] = array();         // Make array
        }
        $positions[$total_score][] = $golfer;           // Add Golpher to score.
    }
    ksort($positions, SORT_NUMERIC);                    // Sort based on key, low -> high
    return array(end($positions), key($positions));     // The last shall be first
}

//Recursion is Awsome
function getWinner(&$golfers, $hole = 9){
    if ($hole == 0) return;
    $winner = get_winners($golfers,$hole);              // Get all ties, if any.
    if(count($winner[0]) > 1){                          // If theirs ties, filter again!
        $sub_golfers = 
        array_intersect_key($golfers, 
            array_flip($winner[0]));                    // Only the Worthy Shall Pass.
        $winner = getWinner($sub_golfers,$hole - 1);    // And again...
    }
    return $winner;                                     // We got a winner, unless they really tie... 
}

echo "<pre>";
print_R(getWinner($golfers));
echo "</pre>";

Ok... Now ill explain my method... Since we need to know the highest score and it might be ties, it makes no sense to me to maintain all that in separate arrays, instead I just reversed the

golfer => scores to Tota_score => golfers

That way when we can sort the array by key and obtain all the golfers with the highest score.

Now total_score is the total sum of a subset of the holes scores array. So... the first time this function runs, it will add all 9 holes, in this case theres 3 golfers that end up with the same score.

Array
(
    [0] => Array
        (
            [0] => A
            [1] => B
            [2] => C
        )

    [1] => 35
)

Since the total count of golfers is not 1 and we are still in the 9th hole, we run this again, but this time only against those 3 golfers and the current hole - 1, so we are only adding up to the 8th hole this time.

Array
(
    [0] => Array
        (
            [0] => B
            [1] => C
        )

    [1] => 32
)

We had another tie.... this process will continue until we reach the final hole, or a winner.

Array
(
    [0] => Array
        (
            [0] => C
        )

    [1] => 31
)

EDIT

<?php
/**
* Author    : Carlos Alaniz
* Email     : Carlos.glvn1993@gmail.com
* Porpuse   : Stackoverflow example
**/


$golfers = [
"77" => [2,4,6,8,10,12,14,16,18],
"79" => [3,5,7,9,11,14,15,17,18],
"76" => [2,4,6,8,10,12,14,16,18],
"78" => [3,5,7,9,11,13,15,17,18]
];

//Iterate over scores.
function get_winners(&$golfers, $hole = 9){
    $positions = array();                               // The score numer is the key!
    foreach ($golfers as $golfer => $score) {            // Get key and value
        //$score_sub = array_slice($score,0,$hole);     // Get the scores subset, first iteration is always all holes
        $total_score = (string)$score[$hole-1];         // Get the key 
        if(!isset($positions[$total_score])){
            $positions[$total_score] = array();         // Make array
        }
        $positions[$total_score][] = $golfer;           // Add Golpher to score.
    }
    ksort($positions, SORT_NUMERIC);                    // Sort based on key, low -> high
    return [
            "winner"=> end($positions),
            "score" => key($positions),
            "tiebreaker_hole"  => [
                "hole"=>$hole,
                "score"=> key($positions)],
            ];                                          // The last shall be first
}

//Recursion is Awsome
function getWinner(&$golfers, $hole = 9){
    if ($hole == 0) return;
    $highest = get_winners($golfers,$hole);             // Get all ties, if any.
    $winner = $highest;
    if(count($winner["winner"]) > 1){                   // If theirs ties, filter again!
        $sub_golfers = 
        array_intersect_key($golfers, 
            array_flip($winner["winner"]));             // Only the Worthy Shall Pass.
        $winner = getWinner($sub_golfers,$hole - 1);    // And again...
    }
    $winner["score"] = $highest["score"];
    return $winner;                                     // We got a winner, unless they really tie... 
}

echo "<pre>";
print_R(getWinner($golfers));
echo "</pre>";

Result:

Array
(
    [winner] => Array
        (
            [0] => 79
        )

    [score] => 18
    [tiebreaker_hole] => Array
        (
            [hole] => 6
            [score] => 14
        )

)
  • 1
    Hi Carlos. Thanks for the example, but the arrays you have are incorrect. They are the scores each golfer gets per hole, but "stableford" scoring is a cumulative points scoring system that players score points per hole based on their score. Will this work with the example leaderboards (arrays) I provided? Otherwise, I dont think you got the jist of it... – Dan Burt Aug 05 '15 at 06:58
  • The result is the same. for player 1: cumulative: 1,2,3,4 Sum = 10 non cumulative: 1, 1 ,1,1 = 4 for player 2: cumulative: 2,4,6,8 Sum = 20 non cumulative: 2,2,2,2 = 8 So i wont matter, the result is going to be the same. I didn't get the cumulative part of it, if you want to get the highest score, this method works as well, just get rid of the array_sum() part and take $hole-1 position of the array as the second array key. Method is the same, keys are different, let me test it and I'll edit the code, to reflect this changes. – Carlos Arturo Alaniz Aug 05 '15 at 13:20
  • Check Edit. now, this will only help you to get the winner, if you want to sort the players, btw position on the board, you could iterate over using my method, excluding the winners on each iteration, (Think bubble sort). But this will be incredibly inefficient hahahaha. Maybe using a graph IDK... :p – Carlos Arturo Alaniz Aug 05 '15 at 13:54