11
\$\begingroup\$

Everyone always wants to implement Conway's Game of Life. That's boring! Let's do cops and robbers instead!

You'll have two teams: the cops and the robbers. Each team has 5 members with 50 health each. The program will loop continuously. Each iteration, the following will occur:

  • For each team, print the first letter (C for the cops, R for the robbers), a space, a space-separated list of the members' HP, and a newline. This is the teams' status. After both are done, print another newline. For instance, here's what it might look like the first round:

    C 50 50 50 50 50
    R 50 50 50 50 50
    
  • Pick a random number from 1 to 10 (including both 1 and 10). We'll call the number N. If N is even, the robbers lose this round; if odd, the cops lose.

  • Pick a random member of the losing team whose HP is greater than 0 and deduct N HP. The members' HP should never appear go below 0 on the status.

  • Restart the loop.

The game ends when all the members of one team lose all their HP. Then, the following will be printed if the cops win:

C+
R-

and if the robbers win:

R+
C-

This is code golf, so the shortest number of characters wins.

Here's a sample implementation in Python 2:

import random

cops = [50]*5
robbers = [50]*5

while any(cops) and any(robbers):
    # print the status
    print 'C', ' '.join(map(str, cops))
    print 'R', ' '.join(map(str, robbers))
    print
    # pick N
    N = random.randint(1, 10)
    # pick the losing team (robbers if N is even, else cops)
    losers = robbers if N % 2 == 0 else cops
    # pick a member whose HP is greater than 0
    losing_member = random.choice([i for i in range(len(losers)) if losers[i]])
    losers[losing_member] -= N
    # make sure the HP doesn't visibly drop below 0
    if losers[losing_member] < 0: losers[losing_member] = 0

if any(cops):
    # robbers lost
    print 'C+'
    print 'R-'
elif any(robbers):
    # cops lost
    print 'C-'
    print 'R+'
\$\endgroup\$
  • \$\begingroup\$ Minor irony: out of the 3176+ questions on this site, no more than 11 are tagged game-of-life. \$\endgroup\$ – Sanchises Apr 10 '15 at 20:44
  • 3
    \$\begingroup\$ @sanchises Extended irony: and 14 are tagged cops-and-robbers! \$\endgroup\$ – Runer112 Apr 10 '15 at 20:46
  • \$\begingroup\$ @sanchises I was largely referring to programming in general (e.g. "Help me! I'm trying to implement Conway's Game of Life!")...but that's still pretty ironic. \$\endgroup\$ – kirbyfan64sos Apr 10 '15 at 20:58
  • \$\begingroup\$ @kirbyfan64sos I know (been there, done that), but this is exactly the kind of site where people go after they've implemented GoL and want more... Anyway, perhaps I'll have a go at this in ><>, lets see if I can do that. \$\endgroup\$ – Sanchises Apr 10 '15 at 21:03
  • \$\begingroup\$ I've removed the CnR tag again. Around here, this tag has a very specific meaning and describes challenges where there are actually two (not necessarily disjoint) parties competing against each other at certain tasks (have a look around the other challenges with that tag). \$\endgroup\$ – Martin Ender Apr 10 '15 at 22:46

10 Answers 10

3
\$\begingroup\$

CJam, 86 bytes

I'm a bit late to the party, but I bring the gift of CJam! ... Hey wait, where are you going?

50aA*{"CR"1$+2/zSf*Nf+oNoAmr{_AmrE&+:P2$=:H!}gPH@)-Ue>t_2/z::+0#:L)!}g;'CL'+'-?N'R2$6^

Try it online.

Explanation

As the questions asks to emulate a straightforward process, this is a relatively straightforward answer. Perhaps one interesting choice I made was to hold the health of both teams interleaved in the same list. This costs 3 bytes to convert to two separate lists, which is needed for both health displaying and checking if a team has lost. But (I think) this is made up for by the 2 bytes saved in initialization and much simpler damage-dealing logic.

50aA*           "Initialize the health list to 10 copies of 50. Even indices
                 hold the health of cops and odd indices hold the health of
                 robbers.";
{               "Do:";
  "CR"1$+2/z      "Split the health list into the two teams for output, adding
                   the corresponding team letter to the start of each.
                       [a b c d e f g h i j]
                    -> [['C a c e g i] ['R b d f h j]]";
  Sf*Nf+          "Insert a space between each element in each team health list
                   and append a newline to the end of each team health list.";
  oNo             "Print the health status for each team and an extra newline.";
  Amr             "Generate the damage amount minus one. If the damage amount is
                   even (robbers lose), then this is odd and aligns with robbers
                   being at odd indices in the health list, and vice versa.";
  {               "Do:";
    _AmrE&+:P       "Add a random even number from [0, 10) to the damage amount
                     minus one. This value modulo the size of the health list
                     (10) selects a person on the losing team to be damaged.";
    2$=:H!
  }g              "... While the selected person's health is zero.";
  PH@)-Ue>t       "Set the damaged person's new health to the maximum of their
                   current health minus the damage amount and zero.";
  _2/z::+0#:L     "Split the health list into the two teams, sum each team's
                   health, and search for a team's health equal to zero.";
  )!
}g              "... While no team's health was found equal to zero.";
;               "Discard the health list.";
'C              "Produce a 'C'.";
L'+'-?          "Produce a '+' if team 1 (robbers) lost, or '-' otherwise.";
N               "Produce a newline.";
'R              "Produce an 'R'.";
2$6^            "Produce the opposite of the sign produced before.";
                "Implicitly print these final results.";
\$\endgroup\$
3
\$\begingroup\$

R - 201

S=sum
Z=sample
C=R=rep(50,5)
while(S(R)*S(C)){cat("C",C,"\nR",R,"\n\n")
N=Z(10,1)
F=function(x,i=Z(rep(which(x>0),2),1)){x[i]=max(0,x[i]-N);x}
if(N%%2)R=F(R)else C=F(C)}
cat(c("R+\nC-\n","C+\nR-\n")[1+!S(R)])
\$\endgroup\$
  • \$\begingroup\$ Also why the rep(which(x>0),2) as opposed to just which(x>0)? \$\endgroup\$ – MickyT Apr 16 '15 at 3:02
  • \$\begingroup\$ 1) I am counting the EOL characters, not the last one though. 2) sum(R*C) and sum(R)*sum(C) are not the same thing. For example, you would not want to exit if C=c(0,0,0,10,10) and R = c(10, 10, 10, 0, 0). In that case, I do save by assigning S=sum. 3) The problem with sample is that if the first argument is a single number, e.g. sample(5, 1), then it will be the same as doing sample(1:5, 1): instead of always returning 5, it will return any number from 1 to 5. So sample(rep(x, 2), 1) is my trick for always picking a number among x even in the case when length(x) is 1. \$\endgroup\$ – flodel Apr 16 '15 at 3:22
  • \$\begingroup\$ Sorry my bad ... Obviously not enough coffee. Thanks for the explanation on the rep() trick. I thought there must be a reason, just couldn't see it \$\endgroup\$ – MickyT Apr 16 '15 at 3:32
2
\$\begingroup\$

APL (Dyalog) (101)

∇K
S←2 5⍴50
→6/⍨~∧/J←∨/S>0
⎕←3↑'CR',0⌈S
S[L;M[?⍴M←(0<S[L←1+~2⊤N;])/⍳5]]-←N←?10
→2
⎕←'CR',⍪'+-'⌽⍨J⍳0
∇

Explanation:

  • S←2 5⍴50: at the beginning, set S to a 5-by-2 matrix where each value is 50. The top row of the matrix represents the cops, the second row represents the robbers.
  • J←∨/S>0: for each row of the matrix, store in J whether any of the HPs are larger than zero.
  • →6/⍨~∧/J: if not both teams have living members, jump to line 6. (end)
  • ⎕←3↑'CR',0⌈S: for each value in the matrix, output the maximum of it and 0, prepend a 'C' to the first row and an 'R' to the second, and add a third (empty) line.
  • N←?10: get a random number in the interval [1,10] and store it in N.
  • L←1+~2⊤N: set L (the losing team) to 1 if the number was odd and to 2 if it was even.
  • M←(0<S[L...;])/⍳5: get the indices of the living members of that team, and store them in M
  • M[?⍴M...]: select a random value from M
  • S[L;M...]-←N: subtract N from the selected team member's value
  • →2: jump to line 2 (the test for living members)
  • ⎕←'CR',⍪'+-'⌽⍨J⍳0: output the final status, putting the + in front of the winning team and the - in front of the losing team.

Sample output

\$\endgroup\$
1
\$\begingroup\$

Ruby, 184

c,r=[p,p].map{('50 '*5).split}
puts([?C,*c]*' ',[?R,*r]*' ')while (u,v=[r,c].map{|a|a.shuffle.find{|x|x>?0}}).all?&&[u,v][rand(1..10)%2].sub!(/.+/){eval"#$&-1"}
puts u ?'R+
C-':'C+
R-'
\$\endgroup\$
1
\$\begingroup\$

Mathematica, 246 241 bytes

Could probably be golfed further...

a=ConstantArray[50,{2,5}];b=Or@@(#<1&)/@#&;c=Print;d=StringJoin@Riffle[IntegerString/@#," "]&;e=RandomInteger;Label@f;Which[b@a[[1]],c@"R+\nC-",b@a[[2]],c@"C+\nR-",True,c["C "<>d@a[[1]]<>"\nR "<>d@a[[2]]];a[[Mod[g=e@9+1,2]+1,e@4+1]]-=g;Goto@f]
\$\endgroup\$
1
\$\begingroup\$

PHP - 416 bytes

I'm new to golfing and though this challenge would be easy enough to try it out. So here is what I came up with.

<?$c=[50,50,50,50,50];$r=[50,50,50,50,50];while((array_sum($c)!=0)&&(array_sum($r)!=0)){$a="C ".join(" ",$c)."\n";$b="R ".join(" ",$r)."\n";echo$a,$b;$n=rand(1,10);$m=rand(0,4);if($n %2==0){while($r[$m]==0){$m=rand(0,4);}$r[$m]=$r[$m]-$n;if($r[$m]<0){$r[$m]=0;}}else{while($c[$m]==0){$m=rand(0,4);}$c[$m]=$c[$m]-$n;if($c[$m]<0){$c[$m]=0;}}if(array_sum($r)==0){echo"C+\nR-\n";}if(array_sum($c)==0){echo"R+\nC-\n";}}?>

With explanation:

<? 
$c=[50,50,50,50,50];$r=[50,50,50,50,50];                       populate Arrays
while((array_sum($c) != 0) && (array_sum($r) != 0)){           loop until on array sums up to 0
    $a="C ".join(" ",$c)."\n";                                 set cops health to a
    $b="R ".join(" ",$r)."\n";                                 set robbers health to b
    echo$a,$b;                                                 print cop and robber health
    $n=rand(1,10);                                             chose random n
    $m=rand(0,4);                                              chose random member
    if($n % 2 == 0){                                           check if n is even
        while($r[$m] == 0){ $m=rand(0,4); }                    loop until value m of array r is not 0
        $r[$m]=$r[$m]-$n;                                      lower health of member m
        if($r[$m] < 0){ $r[$m]=0; }                            if health goes below 0 set it to 0
    }else{
        while($c[$m] == 0){ $m=rand(0,4); }                    same as above
        $c[$m]=$c[$m] - $n;
        if($c[$m] < 0){$c[$m]=0;}
    }
    if(array_sum($r) == 0){ echo"C+\nR-\n"; }                  check if r array sums up to 0 and print that cops won
    if(array_sum($c) == 0){ echo"R+\nC-\n"; }                  check if c array sums up to 0 and print that robbers won
}
?>
\$\endgroup\$
  • \$\begingroup\$ I'm not a PHP user, but I'd guess that maybe you'd cut a few chars by removing != 0 and replacing the check for being equal to zero with the not operator (!array_sum($r)). \$\endgroup\$ – kirbyfan64sos Apr 14 '15 at 15:39
  • \$\begingroup\$ @kirbyfan64sos that doesn't work \$\endgroup\$ – Timo Apr 14 '15 at 16:41
  • \$\begingroup\$ Oh. In most languages, it would. \$\endgroup\$ – kirbyfan64sos Apr 14 '15 at 16:47
1
\$\begingroup\$

C, 390 384 371 bytes

My first golf, if there are any possible improvements, just tell me :)

golfed version:

#include <time.h>
#include <stdio.h>
int p[10],j,r,c,w,N,x;int s(){r=c=0;for(j=5;j--;){c+=p[5+j];r+=p[j];}return !!r-!!c;}void t(){for(j=10;j--;)printf("%s %d",j-4?j-9?"":"\n\nC":"\nR",p[j]*=p[j]>0);}main(){srand(time(0));for(j=10;j--;)p[j]=50;t();while(!(w=s())){N=rand()%10+1;while(!p[x=N%2*5+rand()%5]);p[x]-=N;t();}N=(x=w<1?'C':'R')-w*15;printf("\n\n%c+\n%c-",x,N);}

somewhat ungolfed version:

#include <time.h>
#include <stdio.h>
int p[10],j,r,c,w,N,x;

int s(){
    r=c=0;
    for(j=5;j--;){
        c+=p[5+j];
        r+=p[j];
    }
    return !!r-!!c;
}

void t(){
    for(j=10;j--;)printf("%s %d",j-4?j-9?"":"\n\nC":"\nR",p[j]*=p[j]>0);
}

main(){
    srand(time(0));
    for(j=10;j--;)p[j]=50;
    t();
    while(!(w=s())){
        N=rand()%10+1;
        while(!p[x=N%2*5+rand()%5]);
        p[x]-=N;
        t();
    }
    //w=-1 if cops won, w=1 if robbers won
    N=(x=w<1?'C':'R')-w*15;
    printf("\n\n%c+\n%c-",x,N);
}

edit: I found a way to shorten it a bit and fixed a small bug

\$\endgroup\$
  • \$\begingroup\$ A small improvement: you can replace loops (e.g. for(j=0;j<10;j++)) with a shorter version (for(j=10;--j;)). \$\endgroup\$ – kirbyfan64sos Apr 14 '15 at 15:41
  • \$\begingroup\$ You are totally right, "fixed" this and a few minor other things, thanks. \$\endgroup\$ – Metaforce Apr 14 '15 at 19:23
0
\$\begingroup\$

Batch - 396 Bytes

I don't know if this technically counts - as it doesn't actually select a random member of the team who's health is greater than 0. It just selects a random member, and if the health subtraction generates a number less than 0, then the number becomes 0..

@echo off&setLocal enableDelayedExpansion&for %%a in (C R)do for %%b in (1 2 3 4 5)do set %%a%%b=50
:a
set/aN=%RANDOM%*10/32768+1
set/ac=%N%/2*2
if %c%==%N% (set T=C&set L=R)else set T=R&set L=C
set/aG=%RANDOM%*5/32768+1
set/a%T%%G%-=%N%
for %%a in (C R)do set %%a=0&for %%b in (1 2 3 4 5)do (if !%%a%%b! LEQ 0 set %%a%%b=0
set/a%%a+=!%%a%%b!)
if %C% NEQ 0 if %R% NEQ 0 goto :a
echo !T!+&echo !L!-
\$\endgroup\$
  • \$\begingroup\$ The requirement is that the value never shows below 0 on the printed status. I did the same thing in the example I showed. \$\endgroup\$ – kirbyfan64sos Apr 14 '15 at 0:19
0
\$\begingroup\$

Javascript: 410

function x(l){var t=this,o=t.p={n:l||"C",h:[50,50,50,50,50],s:function(){return o.h.reduce(function(a,b){return a+b})},r:function(){console.log(o.n+' '+o.h.join(' '))},d:function(m){while(o.h[z=~~(Math.random()*5)]<1){}o.h[z]=m>o.h[z]?0:o.h[z]-m}};o.r()}q=[new x(),new x('R')];while((c=q[0].p.s()>0)&&q[1].p.s()>0){q[(z=~~(Math.random()*10))%2].p.d(z);q[0].p.r();q[1].p.r()}console.log(c?'C+\r\nR-':'R+\n\rC-')
\$\endgroup\$
0
\$\begingroup\$

Octave, 182 177 158 145 bytes

145:

t=repmat(50,5);while prod(any(t))d=ceil(rand*10);c=2-mod(d,2);r=ceil(rand*5);t(r,c)-=d;t.*=t>0;end;p=2*any(t,1);['C-';'R+';'C+';'R-'](1+p:2+p,:)

I gave up checking if the character shoot is above zero - this would only be significant if we were forced to display the state in each turn - here we're just randomly skipping one random number from RNG, making it more random.

Also, replaced

t=max(0,t)

with shorter

t.*=t>0


[note - it is printing 'C+R-' without the newline - it's fixed in 145 byte version]

158:

t=repmat(50,5);while prod(any(t))d=ceil(rand*10);c=2-mod(d,2);do r=ceil(rand*5);until t(r,c);t(r,c)-=d;t=max(0,t);end;p=4*any(t,1);disp('C-R+C+R-'(1+p:4+p))

Degolfed:

t=repmat(50,5);               #only first two columns (cops, robbers) relevant
while prod(any(t))
    d=ceil(rand*10);
    c=2-mod(d,2);
    do r=ceil(rand*5);until t(r,c);
    t(r,c)-=d;
    t=max(0,t);
end;
p=4*any(t,1);
disp('C-R+C+R-'(1+p:4+p))

I changed repmat(50,5,2) to repmat(5) - so we have 5x5 matrix instead of 5x2 now (the additional 3 columns don't affect the algorithm). I also found a way to compress the output.

177:

t=repmat(50,5,2);while prod(sum(t))d=ceil(rand*10);c=2-mod(d,2);do r=ceil(rand*5);until t(r,c);t(r,c)-=d;t=max(0,t);end;if sum(t)(1)printf "C+\nR-\n";else printf "C-\nR+\n";end

Degolfed:

t=repmat(50,5,2);
while prod(sum(t))
    d=ceil(rand*10);
    c=2-mod(d,2);                  #cops or robbers affected?
    do r=ceil(rand*5);until t(r,c);
    t(r,c)-=d;
    t=max(0,t);
end
if sum(t)(1)
    printf "C+\nR-\n"
else
    printf "C-\nR+\n"
end

Basically, we create a 5x2 matrix, where the first column are cops and the second column are robbers:

t =
50     50
50     50
50     50
50     50
50     50
[cops] [robbers]

The sum function when one argument applied makes a sum by columns, so it's initially:

250    250

When one of them reaches zero, the prod(sum(t)) evaluates to zero breaking the loop. Then we can examine who won checking whose column sums to zero.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.