Tennis scores meets Code Golf

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.

• I really don't think we need a new tag called sports; game should work just fine. – Justin Jan 30 '14 at 0:48
• What will be the result? Just the end score or every step along the way? – Teun Pronk Jan 30 '14 at 8:17
• @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 Jan 30 '14 at 14:18
• @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 Jan 30 '14 at 16:41
• @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 Jan 31 '14 at 2:58

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"


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


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

• Isn't 11-9 only meant to be valid as a fifth set? – Volatility Jan 30 '14 at 23:54
• @Volatility actually, you were right. I'll need to update. – primo Jan 31 '14 at 2:58

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)}


// 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);
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
Djokovic 0 - 40

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

Test 9
121212121
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

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


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) {
} 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;
}

{
var points = ++game[who];

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

{
set[who]++;
game = [0,0];

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

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

// Play game
for(var i in input) {