Tennis scores meets Code Golf

25

3

In light of the 2014 Australian Open in Melbourne, and the win by Stanislas Wawrinka against Novak Djokovic, I propose the following code golf challenge!

Wawrinka and Djokovic are at it again for the basis of this CGC. Your challenge is to take a string consisting of 1's and 2's and determine the tennis scores based on the tennis scoring system. A "1" indicates that Wawrinka has scored a point, whereas a "2" indicates that Djokovic has scored a point.

For the sake of example: 1211222122111122111 will generate a two-line output:

Wawrinka 1 - 40
Djokovic 1 - 30

The longer the string, the further into the game the score is.

Rules:

  • Shortest code wins, pure and simple.
  • The scores when displayed must be right aligned and also column aligned; e.g.
  • Sample alignment of output:

    Wawrinka 7 5 3 -  0
    Djokovic 6 7 4 - 15
    
  • If a game is in progress, the score must be displayed after the set scores. Game scores must follow set scores with a space-dash-space separator. If this happens to be the first game, a 0-0 set score must be displayed.

    Wawrinka 0 -  0
    Djokovic 0 - 15
    
  • A game will go in order of 0, 15, 30, 40, and game point. If a game reaches 40-40, a deuce is declared, and the score must display the deuce on the player row scoring the point:

    Wawrinka 7 5 3 - 40
    Djokovic 6 7 4 - 40 Deuce
    
  • If a deuce is reached, the next point will be an advantage to the player scoring the point:

    Wawrinka 7 5 3 - Ad
    Djokovic 6 7 4 - 40
    

    The point after that, if scored by the advantaged player, wins the game, else the game goes back to deuce.

  • If more than one deuce is scored in between advantage points, the number of the deuce will be indicated by a number in parentheses:

    Wawrinka 7 5 3 - 40
    Djokovic 6 7 4 - 40 Deuce (2)
    
  • A set is won if a player reaches 6 games or more and with a two game margin in lead, e.g. 7-5, 6-4... 9-7 (In the case of last night's fifth set)

  • Consecutive sets must be separated with a space.

  • There are tie breaks in the Australian Open, therefore if a 6-6 set is reached, a tie-break is established by the first player reaching seven points with a two point margin lead. Display of tie breaks are shown within square brackets as follows:

    Wawrinka 7 6 [6]
    Djokovic 6 6 [4]
    

    The winner of the tie-break is said to have won the previous set 7-6.

  • If the fifth set reaches 6-6, play is continued without tie-break until a two game margin is reached with one player, that player being the winner of that set. There are NO tie-breaks in the fifth set.

  • If a player has won three sets, a third line will be displayed saying Wawrinka wins or Djokovic wins depending on who has won and the code then terminates.

    Wawrinka 2 6 6 3 9
    Djokovic 6 4 2 6 7
    Wawrinka wins
    
  • If the string ends such that the last point has won a game, set or match, the game score is not displayed...

    Wawrinka 7 5 3
    Djokovic 6 7 4
    
  • If the string goes beyond a match being won, the rest of the string is ignored, the score displayed and the winner declared.

WallyWest

Posted 2014-01-29T23:53:33.647

Reputation: 6 949

3I really don't think we need a new tag called [tag:sports]; [tag:game] should work just fine. – Justin – 2014-01-30T00:48:25.823

@Quincunx, good point... – WallyWest – 2014-01-30T04:34:33.457

1What will be the result? Just the end score or every step along the way? – Teun Pronk – 2014-01-30T08:17:30.720

1@TeunPronk depending on the score, the match will be finished or not. The idea seems to be: Given the string of points, what is the current score. – Tim Seguine – 2014-01-30T14:18:30.613

@TimSeguine ah okay, couldnt quite figure out if you should display just the current score or everytime a player gets points. – Teun Pronk – 2014-01-30T14:20:34.447

According to your rules "A set is won if a player reaches 6 games or more and with a two game margin in lead, e.g. 7-5, 6-4... 9-7 (In the case of last night's fifth set)" But it looks like the first set wasn't finished "7-6" because there wasn't a 2 game margin? – McKay – 2014-01-30T14:34:25.063

@McKay I think 7-6 is a finished set, but I agree that the description of the is problematic – Tim Seguine – 2014-01-30T15:05:09.120

Then what's the set win condition. It isn't stated? – McKay – 2014-01-30T15:05:45.020

@McKay I am not an expert in the rules of Tennis, but I think if the set comes to 6-6, then a tie break happens. Whoever wins the tie break wins the set 7-6. That is the only exception to the rule mentioned in the post. The OP did mention tie breaks but didn't specify how they work in that regard. – Tim Seguine – 2014-01-30T15:07:49.333

Then how does a score get to be 9-7? – McKay – 2014-01-30T15:10:21.933

@McKay because that is the final set. Okay, so I think it is fairly obvious that the OP needs to explain the scoring rules for tennis a bit better. There are no tiebreaks in the final set of a match. – Tim Seguine – 2014-01-30T15:13:45.233

The tie-breaker for the final set is often played as an Advantage Set, which can run on ridiculously long. The OP makes no mention of this, though. – primo – 2014-01-30T15:16:47.730

To the OP: I think some more test cases would be helpful. Also I think you need to clarify the scoring on some of the edge cases McKay has pointed out. I think this is a good question that wasn't quite ready to come out of the sandbox. – Tim Seguine – 2014-01-30T15:17:56.903

1@McKay - you shouldn't be able to reach 9-7 in the third set of the Australian Open; the tiebreak should apply in every set except the fifth. – Chowlett – 2014-01-30T16:41:18.407

My bad, guys, @Chowlett, you're right... I should have realized that tiebreaks should apply except in set 5... I'll update the description – WallyWest – 2014-01-31T00:41:00.450

you still say "•A set is won if a player reaches 6 games or more and with a two game margin in lead, e.g. 7-5, 6-4... 9-7 (In the case of last night's fifth set)" but yet have as example scoring 6-7. – McKay – 2014-01-31T02:34:43.963

1@WallyWest you were correct with your previous edit; only the fifth set has no tie-breaker. I fixed your example to make this more clear. – primo – 2014-01-31T02:58:03.957

@primo Thanks for the edit! – WallyWest – 2014-02-02T11:18:00.587

the tie break scores still don't make sense. "The winner of the tie-break is said to have won the previous set 7-6." So, what does it look like if it is 6-6, and someone wins a set? Wouldn't that be 7-6? – McKay – 2014-02-10T13:24:47.673

@McKay the tie-break is played as a separate set altogether (denoted by square brackets). After that set is won, the previous set is marked 7-6. – primo – 2014-02-11T04:14:38.300

You didn't directly answer the question. What does it look like if the score is 6 6, and someone wins a game? – McKay – 2014-02-11T13:45:32.560

Answers

5

Perl - 424 bytes

*1=a;*2=b;@1=(Wawrinka,0);@2=(Djokovic,0);
$$_++>${$o=S^$_}&&$$_>3and$1=$2=0,
$w=$w[$_]+=($$_[$.]++>$$o[$.]||(($t=$$_[$.]==$$o[$.])&&!$%&&$.<5))&&
$$_[$.]>5+$%&&!($1[$.]=$2[$.+=!$%]=$w<2&&0,$$_[$.-1]+=$%,$%=$t)for<>=~/./g;
@s=(' 0',15,30,(40)x($e=$$o<3||$$o-2),Ad);
$%and$_="[$_]"for@1[-1],@2[-1];$d[$&]=$1>2&&$1==$2&&' Deuce'.($1>3&&" ($e)");
print"@1",$w<3&&" - $s[$1]$d[1]","\n@2",$w<3&&" - $s[$2]$d[2]",$w>2&&"\n${$&}[0] wins"

Newlines have been added for readability horizontal sanity.

I believe this to be a complete solution, according to the scoring of the Australian Open:

  • Best of five sets (a.k.a. race to three).
  • Sets 1-4 are played as a 6-6 tie-breaker.
  • Set 5 is played as an advantage set.

Test Cases


1211222122111122111

Wawrinka 1 - 40
Djokovic 1 - 30

12112221221111221112

Wawrinka 1 - 40
Djokovic 1 - 40 Deuce

121122212211112211122

Wawrinka 1 - 40
Djokovic 1 - Ad

1211222122111122111221

Wawrinka 1 - 40 Deuce (2)
Djokovic 1 - 40

22111111212122221122111212212112121221212211221121222222112112221121121122221122221211111222121222122211212122111212112211222121211212211212211122121211112222222212211121122

Wawrinka 6 [5] - 30
Djokovic 6 [6] - 40

221111112121222211221112122121121212212122112211212222221121122211211211222211222212111112221212221222112121221112121122112221212112122112122111221212111122222222122111211222

Wawrinka 6 0 -  0
Djokovic 7 0 -  0

1122222211121211121211111121111211221222212212112221211222211222112212211121122122212122212222122212212211221111121222111221211111211112222212122122112111212121221221212211112122212211111111112111212222221112212121122212121111122111222222111212221121221111222122122222111212111111221121122111122122111222222121122221112221221122221121211212111122111121212112112121222122

Wawrinka 5 4 6 6 5 - 15
Djokovic 7 6 4 4 6 - 40

11222222111212111212111111211112112212222122121122212112222112221122122111211221222121222122221222122122112211111212221112212111112111122222121221221121112121212212212122111121222122111111111121112122222211122121211222121211111221112222221112122211212211112221221222221112121111112211211221111221221112222221211222211122212211222211212112121111221111212121121121212221222

Wawrinka 5 4 6 6 5
Djokovic 7 6 4 4 7
Djokovic wins

222221112112212212222111222211111111121111121112211221221211212121122211222112111112122122212222211112122212221111121111121211212112112112221221121122121121112221221222122122211222212121212112112111221221121112222212122222221111112222222221221122211221121111221121222222111111122221122111211121222112112122212122221121222221222121212111121221221112111212212222122212212212112111112112112121112221111221221221121222122211221212211111111222222121221112221212

Wawrinka 6 7 6 4 7 - 40
Djokovic 3 5 7 6 8 - Ad

2222211121122122122221112222111111111211111211122112212212112121211222112221121111121221222122222111121222122211111211111212112121121121122212211211221211211122212212221221222112222121212121121121112212211211122222121222222211111122222222212211222112211211112211212222221111111222211221112111212221121121222121222211212222212221212121111212212211121112122122221222122122121121111121121121211122211112212212211212221222112212122111111112222221212211122212122

Wawrinka 6 7 6 4 7 
Djokovic 3 5 7 6 9 
Djokovic wins

All intermediate scores for the last test case can be seen here: http://codepad.org/FzDIcf0W

primo

Posted 2014-01-29T23:53:33.647

Reputation: 30 891

Isn't 11-9 only meant to be valid as a fifth set? – Volatility – 2014-01-30T23:54:01.023

@Volatility actually, you were right. I'll need to update. – primo – 2014-01-31T02:58:38.223

2

ECMAScript 6 - 635 Characters

f=(p,q,C)=>{B='';T=' ';L=(x)=>(B+x).length;M=(x,y)=>x>y?x:y;E=(a)=>{var x=L(a[0]),y=L(a[1]),q=M(x,y);for(;x++<q;)a[0]=T+a[0];for(;y++<q;)a[1]=T+a[1]};E(a=[p,q]);g=[m=n=o=i=t=z=0,0];s=[0,0];S=[0,0];w=2;O=(i)=>a[i]+(n+o>0?T+s[i]:B)+(o>0?(t?' ['+g[i]+']':' - '+[' 0',15,30,40,'Ad'][g[i]]+(z>0&&g[0]+g[1]==6&&i==w?' Deuce'+(z>1?' ('+z+')':B):B)):B)+'\n';while(W=C[i++]){w=--W;++o;if((d=++g[w])>M(t?6:3,(e=g[l=1-w])+1)){g=[o=z=0,0];j=++s[w];k=s[l];t=++n>11;if(j>M(5,m>4||k<6?k+1:6)){E(s);a[0]+=T+s[0];a[1]+=T+s[1];s=[n=0,0];++m;if(++S[w]>2)break}}else if(!t&&d+e>7){--g[w];--g[l];++z}}E(s);E(g);return O(0)+O(1)+(S[w]>2?(w?q:p)+' wins':B)}

With comments:

// Function f takes arguments:
//   p - Player 1 name
//   q - Player 2 name
//   C - String of 1s and 2s representing points won by players.
f=(p,q,C)=>{
    /* Empty String          */ B='';
    /* Space                 */ T=' ';
    /* String Length Func.   */ L=(x)=>(B+x).length;
    /* Max Function          */ M=(x,y)=>x>y?x:y;
    /* Equalize Length Func. */ E=(a)=>{var x=L(a[0]),y=L(a[1]),q=M(x,y);for(;x++<q;)a[0]=T+a[0];for(;y++<q;)a[1]=T+a[1]};
    /* No. of sets           */ m=0;
    /* No. games in set      */ n=0;
    /* No. points in game    */ o=0;
    /* Input Index           */ i=0;
    /* Output String         */ E(a=[p,q]);
    /* Current Game's Points */ g=[0,0];
    /* Current Set's Games   */ s=[0,0];
    /* No. sets won          */ S=[0,0];
    /* Is a tiebreaker       */ t=0;
    /* No. of deuces         */ z=0;
    /* Current match result  */ w=2;
    /* Output Fnctn  */ O=(i)=>a[i]+(n+o>0?T+s[i]:B)+(o>0?(t?' ['+g[i]+']':' - '+[' 0',15,30,40,'Ad'][g[i]]+(z>0&&g[0]+g[1]==6&&i==w?' Deuce'+(z>1?' ('+z+')':B):B)):B)+'\n';
    while(W=C[i++]){
        w=--W;
        // w - index of winner of current game
        // l - index of loser of current game
        // d - winner of current point's game score
        // e - loser of current point's game score
        ++o;
        if((d=++g[w])>M(t?6:3,(e=g[l=1-w])+1)){ 
            g=[0,0];  // Reset the game score.
            o=z=0;
            j=++s[w]; // j = Increment the winner's set score
            k=s[l];   // k = Loser's set score
            t=++n>11; // Is a tiebreak?
            if(j>M(5,m>4||k<6?k+1:6)){
                E(s);
                a[0]+=T+s[0]; // Add to output
                a[1]+=T+s[1]; // Add to output
                s=[n=0,0];  // Reset current set's no. of games & no. of deuces.
                ++m;        // Increment no. of sets.
                if(++S[w]>2)break;     // Increment winners no. sets won and check if match won.
            }
        }
        else if(!t&&d+e>7){--g[w];--g[l];++z}// Check if deuces increased.
    }
    E(s); // Format sets strings.
    E(g); // Format games strings.
    return O(0)+O(1)+(S[w]>2?(w?q:p)+' wins':B);
}

Testing

var tests=[
            '',
            '1',
            '2',
            '11',
            '222',
            '1111',
            '2222',
            '1112221',
            '11122212',
            '121212121',
            '1212121212',
            '1211222122111122111',
            '12112221221111221112',
            '121122212211112211122',
            '1211222122111122111221',
            '1111222211112222111122221111222211112222111122221212121212121212121',
            '11111111111111111111111111111111111111111111111111111111111111111111111',
            '111111111111111111111111111111111111111111111111111111111111111111111111',
            '1111111111111111111111111111111111111111111111111111111111111111111111111'
        ];
for(var ex in tests) console.log('Test '+ex+'\n'+tests[ex]+'\n'+f('Wawrinka','Djokovic',tests[ex]));

Test Outputs

Test 0

Wawrinka
Djokovic


Test 1
1
Wawrinka 0 - 15
Djokovic 0 -  0


Test 2
2
Wawrinka 0 -  0
Djokovic 0 - 15


Test 3
11
Wawrinka 0 - 30
Djokovic 0 -  0


Test 4
222
Wawrinka 0 -  0
Djokovic 0 - 40


Test 5
1111
Wawrinka 1
Djokovic 0


Test 6
2222
Wawrinka 0
Djokovic 1


Test 7
1112221
Wawrinka 0 - Ad
Djokovic 0 - 40


Test 8
11122212
Wawrinka 0 - 40
Djokovic 0 - 40 Deuce


Test 9
121212121
Wawrinka 0 - Ad
Djokovic 0 - 40


Test 10
1212121212
Wawrinka 0 - 40
Djokovic 0 - 40 Deuce (2)


Test 11
1211222122111122111
Wawrinka 1 - 40
Djokovic 1 - 30


Test 12
12112221221111221112
Wawrinka 1 - 40
Djokovic 1 - 40


Test 13
121122212211112211122
Wawrinka 1 - 40
Djokovic 1 - Ad

Test 14
1211222122111122111221
Wawrinka 1 - 40 Deuce
Djokovic 1 - 40


Test 15
1111222211112222111122221111222211112222111122221212121212121212121
Wawrinka 6 [10]
Djokovic 6 [ 9]


Test 16
11111111111111111111111111111111111111111111111111111111111111111111111
Wawrinka 6 6 5 - 40
Djokovic 0 0 0 -  0


Test 17
111111111111111111111111111111111111111111111111111111111111111111111111
Wawrinka 6 6 6
Djokovic 0 0 0
Wawrinka wins

Test 18
1111111111111111111111111111111111111111111111111111111111111111111111111
Wawrinka 6 6 6
Djokovic 0 0 0
Wawrinka wins

MT0

Posted 2014-01-29T23:53:33.647

Reputation: 3 373

1

Javascript - 743 bytes

var b=process.argv[2],c=[0,0],e=[],g=[0,0],h=[0,0],k=["0","15","30","40"],l=["Wawrinka","Djokovic"];function m(a){var d="",f;for(f in e)d+=e[f][a]+" ";3<=c[0]||3<=c[1]||(d+=g[a]+" - ",f=h[a],a=h[(a+1)%2],d=n()?d+("["+f+"]"):3>f||3>a?d+k[f]:f>a?d+"Ad":d+k[3]);return d}function n(){return 6<=g[0]&&6<=g[1]}function p(){var a=q;g[a]++;h=[0,0];7<=g[a]?(e.push(g),g=[0,0],c[a]++):6<=g[a]&&g[a]>g[(a+1)%2]+1&&(e.push(g),g=[0,0],c[a]++)} for(var r in b){var q=parseInt(b[r])-1,s=++h[q];n()?7<=s&&(4>e.length?p():s>h[(q+1)%2]+1&&p()):4<=s&&s>h[(q+1)%2]+1&&p()}console.log(l[0]," ",m(0));console.log(l[1]," ",m(1),n()?"":2<h[0]&&2<h[1]&&h[0]==h[1]?"Deuce"+(3<h[0]?" ("+(h[0]-2)+")":""):"");console.log(3<=c[0]||3<=c[1]?(3<=c[0]?l[0]:l[1])+" wins":"");

Without closure compiler:

var input = process.argv[2];

var score = [0,0];
var match = [];
var set = [0,0];
var game = [0,0];
var gameScore = [ '0', '15', '30', '40' ];
var names = ['Wawrinka', 'Djokovic'];

function printScores(who)
{
    var out = '';
    for (var i in match) {
        out += match[i][who] + ' ';
    }

    if (!isDone()) {
        out += set[who] + ' - ';

        var point = game[who], otherPoint = game[(who+1)%2];

        if (isTieBreaker()) { // Tie breaker
            out += '['+point+']';
        } else {
            if (point < 3 || otherPoint < 3) {
                out += gameScore[point];
            } else if (point > otherPoint) {
                out += 'Ad';
            } else {
                out+= gameScore[3];
            }
        }
    }

    return out;
}

function printDeuce()
{
    if (isTieBreaker()) {
        return '';
    } else {
        return (game[0] > 2 && game[1] > 2 && game[0] == game[1]) ? ('Deuce' + (game[0] > 3 ? ' (' + (game[0] - 2) + ')' : '')) : '';
    }

}

function isDone()
{
    return score[0] >= 3 || score[1] >= 3;
}

function isTieBreaker()
{
    return set[0] >= 6 && set[1] >= 6;
}

function getOther(who)
{
    return (who + 1) % 2;
}

function addPoint(who)
{
    var points = ++game[who];

    if (isTieBreaker()) {
        if (points >= 7) {
            if (match.length < 4) {
                addGame(who);
            } else if (points > (game[getOther(who)]+ 1)) {
                addGame(who);
            }
        }
    } else {
        if (points >= 4 && points > (game[getOther(who)] + 1)) {
            addGame(who);
        }
    }
}

function addGame(who)
{
    set[who]++;
    game = [0,0];

    if (set[who] >= 7) { // Won Tiebreaker
        addSet(who);
    } else if (set[who] >= 6 && set[who] > (set[getOther(who)] + 1)) {
        addSet(who);
    }
}

function addSet(who)
{
    match.push(set);
    set = [0,0];
    score[who]++;
}

// Play game
for(var i in input) {
    addPoint(parseInt(input[i]) - 1);
}

console.log(names[0], ' ', printScores(0));
console.log(names[1], ' ', printScores(1), printDeuce());
console.log(isDone() ? ((score[0] >= 3 ? names[0] : names[1]) + ' wins') : '');

Populus

Posted 2014-01-29T23:53:33.647

Reputation: 111