Score an asynchronous prisoner's dilemma game

In a round of the prisoner's dilemma exercise, two players each decide whether to cooperate or defect for that round. The scoring for a round is:

• Player A and Player B both cooperate: 1 point for both
• Player A and Player B both defect: 2 points for both
• Player A cooperates and Player B defects: 3 points for cooperating Player A and 0 points for defecting Player B

You don't need to worry about strategy, though: your program will merely be tabulating the score for a game. (In case you are already familiar with prisoner's dilemma, my "points" here correspond to "years in prison.")

Your challenge is to take input that represents the players' choices over several rounds and compute their respective total scores. One player submits choices in lowercase, c and d (for cooperate and defect), and the other submits choices in uppercase, C and D. These choices are supplied to your program as a string.

Normally, players in prisoner's dilemma submit their moves simultaneously and iteratively. In this challenge, however, the players may have submitted their choices for several rounds at once. If a player's move is out of sequence, the scoring program remembers it and matches it with the next available move from the opposing player.

Here's a sample input string:

cDCddDDCcCc


To show the matches that exist in this input, I'll call out the lowercase and uppercase separately, and pair them up:

cDCddDDCcCc
c  dd   c c => cddcc
DC  DDC C  => DCDDCC


These will be paired into the rounds:

c vs D (3 pts for lowercase-player, 0 pts for uppercase-player)
d vs C (0 pts for lowercase-player, 3 pts for uppercase-player)
d vs D (2 pts for both)
c vs D (3 pts for lowercase-player, 0 pts for uppercase-player)
c vs C (1 pt for both)


Which produces the score 9 (lowercase) to 6 (uppercase), so the output should be 9,6 (or any unambiguous delimiter).

To express it in yet another way, here's each pairing pulled out on its own row:

cDCddDDCcCc
cD
Cd
dD
D c
C  c


There is one unmatched C, because the uppercase player submitted more moves than the lowercase player. That is acceptable, and it is totally ignored for scoring purposes.

Here are the requirements:

• You must write a program or function that accepts a string of the regular-expression form /[cdCD]+/, through some input mechanism (STDIN, function argument, read from file, etc.). (Your program may optionally accept input with a trailing newline.)

• Your program or function must output or return the players' scores as a string. The output format must start with the lowercase player's score, followed by the uppercase player's score, separated by any non-empty, non-numeric delimiter of your choice. (A trailing newline is optional.)

• If one player has more moves than the other, the excess moves are ignored.

• If all moves in the input are from exclusively one player (that is, no rounds have been played at all), then each player's score is 0.

• The smallest submission in bytes wins.

Test cases

Input:  cDCddDDCcCc
Output: 9,6         -- or any delimiter; I chose commas here

Input:  cccDDD
Output: 9,0

Input:  DDDDDDccc
Output: 9,0

Input:  cDcDcD
Output: 9,0

Input:  dcDDC
Output: 5,2

Input:  CcdCDDcd
Output: 6,6

Input:  Ddd
Output: 2,2

Input:  ccccccccccc
Output: 0,0

• dont they normally get 2 points for cooperation and lose 1 point if both defect? – Eumel Nov 30 '15 at 16:48
• @Eumel I just copied the specs from the Wikipedia introduction, which appears to use a formulation suggested by the original authors. Also note that points here are "bad" as they correspond to years in prison. The winner is the player with the fewest points. – apsillers Nov 30 '15 at 16:54
• Is (0,0) or [0,0] OK for output? – xnor Dec 1 '15 at 2:20

Pyth, 23 bytes

jsMc2/LC,@Gz-zG"cDDCdd


Test suite

Explanation:

@Gz: Lowercase letters

-zG: Uppercase letters

C,: Pair, truncate the remainder.

: Take the string representation of the list of pairs

/L ... "cDDCdd: For each letter in "cDDCdd", count how many time it appears in the above string repr.

c2: Chop the resulting list in half.

sM: Add up each half.

j: Join on newlines and print.

 must be used instead of s to make the case where one party never plays work.

g=filter
(n!m)(a,b)=(a+n,b+m)
f s=init$tail$show$foldr id(0,0)$zipWith(#)(g(>'a')s)$g(<'E')s 'c'# 'C'=1!1 'c'#_=3!0 _# 'D'=2!2 _#_=0!3  Usage example: f "cDCddDDCcCc" -> "9,6" 15 bytes just to get the output format right, i.e. turning a pair of numbers (x,y) into a string "x,y". How it works:  g(>'a')s -- extract all lowercase letters g(<'E')s -- extract all uppercase letters zipWith(#) -- combine both lists element wise with function # -- # calls ! depending on the combination of c/d/C/D -- ! takes 2 numbers a and b and returns a function -- that takes a pair (x,y) and returns (x+a,y+b) -- now we have a list of such functions foldr id(0,0) -- apply those functions starting with (0,0) init$tail$show -- format output  Edit: @Zgarb helped me saving 5 bytes. Thanks! LabVIEW, 77 Bytes The code scans from tokens and uses those indicies to decide where the points go. Counting goes like this Python 3, 110 Saved 5 Bytes thanks to FryAmTheEggman. Saved 7 Bytes thanks to apsillers. Saved 26 Bytes thanks to DSM. x=[[],[]] a=b=0 for m in input():x[m<'E']+=m for w,p in zip(*x):d=p>'C';c=w<'d';b+=d*2+c;a+=3-d-2*c print(b,a)  I think it's finally all golfed out. It scans through each character in the input and sorts it based on being uppercase or not. It then does some fancy math that abuses Python's implicit conversion of bools to ints. JavaScript (ES6), 124 118 bytes s=>(A=B=i=0,U=(r=x=>s.replace(/c|d/g,x)),r(l=>U[i]&&(U[i++]<'D'?l<'d'?++A&++B:B+=3:l<'d'?A+=3:(A+=2,B+=2))),A+','+B)  Live demo (Expanded slightly for readability.) var f=function (s) { A=B=i=0; U=(r=function(x){return s.replace(/c|d/g,x)})(""); r(l=>U[i]&&(U[i++]<'D'?l<'d'?++A&++B:B+=3:l<'d'?A+=3:(A+=2,B+=2))); return A+','+B; } var input = ["cDCddDDCcCc","cccDDD","DDDDDDccc","cDcDcD","dcDDC","CcdCDDcd","Ddd","ccccccccccc"]; var output = ["9,6","9,0","9,0","9,0","5,2","6,6","2,2","0,0"]; var passed = true; for (var index=0;index<input.length;index++) { if (f(input[index]) !== output[index]) passed = false; } document.getElementById("result").textContent = passed ? "All tests passed." : "Some tests failed."; <div id="result"></div> Saved 6 bytes thanks to user81655. • I originally had array comprehensions, but ended up using a different method. Thanks. – intrepidcoder Dec 1 '15 at 3:45 Par, 49 bytes (lW▼·L)w▼·U))t˅y])[h7%Z2*↓″4>5*-]z2↔-″0<4*+╞)t.Σ¡  One byte is used per character. See here. Explanation ( ## Construct array l ## Read line W ## Assign to w ▼·L) ## Filter by immutable under lower-case w ## Get w ▼·U) ## Filter by immutable under upper-case ) ## t ## Transpose and truncate ˅y]) ## If empty, empty 2-D matrix [ ## Map h ## Decimal to hex 7% ## Modulo 7 Z ## Assign to z 2*↓″4>5*- ## Score of lower case ] ## Put in array z2↔-″0<4*+ ## Score of upper case ╞ ## Add to array ) ## t ## Transpose and truncate .Σ ## Map - sum ¡ ## Empty array onto stack  Outputs in the form 9 6. • As someone who has never used (or heard of) Par, I found your explanation a pleasure to read. Thanks! – apsillers Nov 30 '15 at 19:07 CJam, 9283 81 bytes This ended up longer than I thought it would... 0]K*X3tC30tG22tZ11t:L;0'a]q+{'D>}:B$_{B}%1#/z{,1>},{2<[:i:#K%L=]sY0e[{si}%}%:.+S*


Try it here.

Explanation (Dare I explain this? :O):

0]K*C3tX30tG22tZ11t:L;    e# Creates this array [0,30,0,11,0,0,0,0,0,0,0,0,3,0,0,0,22,0,0,0]
0'a]q+                    e# Creates an array that looks like [0, 'a', input string]
{'D>}:B\$                  e# Sorts the array by if the int representation of each element is greater than the int value of the character 'D' (e.g. [0,C,D,a,c,d])
_{B}%1#/                  e# Finds the index of the first value in the array that is > 'D' and splits the array at that index.
z{,1>},{                  e# Zip the two sub arrays and filter for only sub arrays with more than one element. (e.g [[0,a],[C,c],[D,d]])
{2<[:i:#K%L=]s            e# For each sub array, take the first two elements, convert each to an it, calculate n=(x[0]^x[1]) mod 20, and get the nth element in the very first array, and convert it to a string
Y0e[                      e# Pad the string with 0 so it is length 2. (e.g. [["00"],["22"],["11"]])
{si}%}%:.+                e# get the numerical representation of each digit and dot sum all of them (e.g [[0,0],[2,2],[1,1] => [3,3])
S*                        e# Join with a space (e.g "3 3")
`