27
\$\begingroup\$

Yahtzee is a game played with five six-sided dice and a score sheet with thirteen different boxes to fill a score in. Each box has its own scoring rules:

  • 1s, 2s, 3s, 4s, 5s, 6s all score points equal to the sum of the respective dice (that is, a roll of [3, 2, 3, 1, 5] scored as 3s would be awarded 6 points: 3 for each 3).
  • 3-of-a-kind and 4-of-a-kind (as they sound, three or four dice rolled the same) score points equal to the sum of all five dice.
  • Full house (two dice show one value, the other three show another) scores 25 points
  • Small straight (four consecutive values) scores 30 points
  • Large straight (all consecutive values) scores 40 points
  • Yahtzee (all dice show the same value) scores 50 points

The thirteenth (chance) makes sense in-game, but not so much for this challenge; additionally the game has bonuses for extra Yahtzees which make no sense here. Because the challenge is...

Given five dice as input (five integers 1-6, input however is convenient, you can assume input is always valid), output the highest score possible for that 'hand'. For the purposes of this challenge, only the scoring methods in the list above are valid (specifically, chance is not a valid score box for this challenge). The score should be output as its decimal numeric value, whether that's an integer or a string representation thereof, whatever. It should be immediately recognizable as a number. Leading/trailing whitespace is fine, this is about getting the score and not presentation.

Code golf, so the answer with the fewest bytes in a given language wins. Standard loopholes forbidden.

Test cases

(Note that these are all independent, the challenge is to score one 'hand' of dice):

in: 1 5 4 3 2
out: 40
in: 1 1 4 3 1
out: 10
in: 2 2 6 5 3
out: 6
in: 2 4 2 4 6
out: 8
in: 1 1 1 1 1
out: 50
in: 5 2 5 3 6
out: 10
in: 1 6 3 4 2
out: 30
in: 1 3 1 1 3
out: 25
in: 6 5 5 6 6
out: 28
in: 1 2 3 5 6
out: 6
\$\endgroup\$
7
  • 3
    \$\begingroup\$ should we close the older question instead? IMO this is a better question than that one... \$\endgroup\$
    – Giuseppe
    Dec 12, 2017 at 22:50
  • 5
    \$\begingroup\$ IMO this is not at all a duplicate of score a game of yahtzee This explicitly states it is the highest score for one hand, where the other question is asking for an entire score from a list of die rolls. Lastly, and most importantly, I do not see any answers from the possible dupe being able to be used here in a "copy-paste" scenario. Please consider reopening. \$\endgroup\$ Dec 12, 2017 at 23:15
  • \$\begingroup\$ meta post about this question \$\endgroup\$
    – Giuseppe
    Dec 12, 2017 at 23:17
  • 2
    \$\begingroup\$ FWIW, I was aware of the older question when I put this one together. My thoughts echoed what @DevelopingDeveloper said. Having done this as an exercise once before, I found some interesting opportunities to optimize this process. I also just think this is a tidier challenge. \$\endgroup\$
    – brhfl
    Dec 12, 2017 at 23:38
  • \$\begingroup\$ "The thirteenth (chance) makes sense in-game, but not so much for this challenge" So is it counted? \$\endgroup\$
    – Unihedron
    Dec 14, 2017 at 18:11

8 Answers 8

6
\$\begingroup\$

R, 146 141 bytes

function(d)max(unique(d<-sort(d))*(g=table(d)),any(g>2)*sum(d),all(2:3%in%g)*25,(s=sum((R=diff(d))==1))<4&all(R<2)*30,(s>3)*40,(0==sd(d))*50)

Try it online!

Outgolfed by plannapus

Takes input as a list, and returns the score.

ungolfed a bit:

function(d){
 d <- sort(d)
 u <- unique(d)                  # unique dice
 g <- table(d)                   # table of counts
 Ns <- u*g                       # scores as 1s, 2s, ... etc.
 NKind <- any(g>2)*sum(d)        # 3 or 4 of a kind if any counts are at least 3
 FHouse <- all(2:3%in%g)*25      # full house is 25 if 2 & 3 are in counts
 R <- diff(d)                    # consecutive differences
 s <- sum(R==1)                  # sum of differences equal to 1
 sStraight <- s<4 &              # if the number of 1s is 3 and
               all(R<2)*30       # no consecutive numbers are 2 apart
 bStraight <- (s>3)*40           # all 1s means big straight
 Yahtzee <- sd(d)==0             # sd = 0 means all are equal
 max(Ns,NKind,FHouse,sStraight,bStraight,Yahtzee)
}

\$\endgroup\$
2
  • \$\begingroup\$ f(c(1,2,3,5,6)) fails — it should yield 6 and instead yields 30. It seems this is because you're counting how many pairs (post-sorting) differ by one, which is in fact four for the above sequence, even though it isn't a straight of four. I think I ran into this when I did this as an exercise a while back, and I should probably add that as a test case... \$\endgroup\$
    – brhfl
    Dec 13, 2017 at 2:17
  • \$\begingroup\$ @brhfl this is fixed now. \$\endgroup\$
    – Giuseppe
    Dec 13, 2017 at 2:39
6
\$\begingroup\$

Python 2, 187 184 167 165 bytes

-17 bytes thanks to @mypetlion
-2 bytes thanks to @chrstphrchvz

def f(d):r=range(1,7);c=d.count;m=map(c,r);s=`m`[1::3];return max([i*c(i)for i in r]+[sum(d)*(max(m)>2),25*(set(m)>{2,3}),30*(4*'1'in s),40*(5*'1'in s),50*(5 in m)])

Try it online!

\$\endgroup\$
0
4
\$\begingroup\$

R, 136 134 bytes

function(n,y=table(factor(n,1:6)),z=sum(!diff(diff(sort(n)))))max(1:6*y,c(25,sum(n),10*3:5)[c(all(y<4&y-1),any(y>2),z>1,z>2,any(y>4))])

Golfed down 2 bytes thanks to @Giuseppe!

Indented,

function(n, #vector of die scores
         y=table(factor(n,1:6)), #Contingency table
         z=sum(!diff(diff(sort(n))))) #Diff of diff of ordered scores
    max(1:6*y,
        c(25,sum(n),10*3:5)*c(all(y<4&y-1), #Full house
                            any(y>2), #3 and 4 of a kind
                            z>1, #Small straight
                            z>2, #Long straight
                            any(y>4))] #Yahtzee

A few test cases:

> f=function(n,y=table(factor(n,1:6)),z=sum(!diff(diff(sort(n)))))max(1:6*y,c(25,sum(n),10*3:5)*c(all(y<4&y-1),any(y>2),z>1,z>2,any(y>4)))
> f(c(2,4,2,4,6))
[1] 8
> f(c(1,2,3,5,6))
[1] 6
> f(c(6,5,5,6,6))
[1] 28
> f(c(6,5,3,1,4))
[1] 30
> f(c(6,5,3,2,4))
[1] 40
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Huh, I considered factor for a hot second before I got distracted. But I think if I use your approach with z (s in my answer), I can golf mine down to 134... \$\endgroup\$
    – Giuseppe
    Dec 13, 2017 at 19:17
  • \$\begingroup\$ Additionally, you can save three bytes by using all(y<4&y-1) and using * instead of [, and setting y inline rather than as a function argument, and it still passes all test cases: Try it online! \$\endgroup\$
    – Giuseppe
    Dec 13, 2017 at 19:24
  • \$\begingroup\$ also, I restructured the max and I think it saved the byte from setting y inline. \$\endgroup\$
    – Giuseppe
    Dec 13, 2017 at 19:28
3
\$\begingroup\$

Batch, 359 bytes

@echo off
set/at=s=m=r1=r2=r3=r4=r5=r6=0
for %%r in (%*)do set/a"m+=!(m-r%%r),r%%r+=1,t+=%%r,p=s-r%%r*%%r,p&=p>>9,s-=p
goto %m%
:1
if %r1% neq %r6% set s=40&goto g
:2
set/at=r3*r4*(r2*(r1+r5)+r5*r6)
if %t% gtr 0 set s=30
goto g
:3
set/a"s=r1^r2^r3^r4^r5^r6"
if %s%==1 if %t% lss 25 set s=25&goto g
:4
set/as=t
goto g
:5
set s=50
:g
echo %s%

Explanation:

@echo off
set/at=s=m=r1=r2=r3=r4=r5=r6=0
for %%r in (%*)do set/a"m+=!(m-r%%r),r%%r+=1,t+=%%r,p=s-r%%r*%%r,p&=p>>9,s-=p
goto %m%

Calculate the number of dice for each number, plus the maximum, plus the total of all the dice, plus the highest total of dice of the same number.

:1
if %r1% neq %r6% set s=40&goto g

If all the dice are different, this might be a long straight, but that needs there to be either no 1 or no 6.

:2
set/at=r3*r4*(r2*(r1+r5)+r5*r6)
if %t% gtr 0 set s=30
goto g

Otherwise, or if if at most two dice are the same, then this could still be a short straight. There must be at least a 3 and a 4 and also a combination of the other four numbers.

:3
set/a"s=r1^r2^r3^r4^r5^r6"
if %s%==1 set s=25&goto g

If there are three dice the same, check for a full house, since 3^2==1. However, some full houses, such as 6s and 5s, score higher as 3-of-a-kind.

:4
set/as=t
goto g

Otherwise, or if there are four the same, then score the total.

:5
set s=50

And if there are five the same, then Yahtzee!

:g
echo %s%

Output the best score.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Thanks for reminding me of the potential [5,5,6,6,6]-scored-as-full-house pitfall - I have added this as a test case. I knew there were a couple of weird fringe cases I was forgetting. \$\endgroup\$
    – brhfl
    Dec 13, 2017 at 16:50
3
\$\begingroup\$

Jelly, 58 bytes

ċЀ`Ṁ>2ȧS
ṢI=1Ạµ-ƤẸ,E;ṢI$E$;⁸Lƙ`Ṣ⁼2,3¤a3,5,4,2.Ṁ×⁵»Ç»Sƙ`Ṁ$

Try it online!

\$\endgroup\$
4
  • \$\begingroup\$ It's perfectly valid, but I'm curious and don't know Jelly well enough to suss it out on my own... why does full house return 25.0 while no other case has the trailing .0? \$\endgroup\$
    – brhfl
    Dec 13, 2017 at 19:28
  • \$\begingroup\$ @brhfl Well, because it's inferred as 2.5 × 10 = 25.0 (float arithmetic), while the others such as 30 are inferred as 3 × 10 = 30 (integer arithmetic). \$\endgroup\$ Dec 13, 2017 at 19:36
  • 2
    \$\begingroup\$ Thanks! I didn't really word my question well; I was more curious what method you're using to detect a full house that results in doing the math differently - but now that I think about it, I'm guessing it's just golfier to do 2.5, 3, 4, 5 * 10 vs 25, 30, 40, 50. Think I answered my own question. \$\endgroup\$
    – brhfl
    Dec 13, 2017 at 20:16
  • \$\begingroup\$ @brhfl Exactly, since × 10 is 2 bytes, 2.5 is 2 bytes just like 25, and 3,5,4 saves 3 bytes over 30,50,40, so 3 + 0 - 2 = 1 byte saved. \$\endgroup\$ Dec 13, 2017 at 20:30
2
\$\begingroup\$

Perl 6, 159 bytes

->\b{max map {$_(b)},|(1..6).map({*{$_}*$_}),{.kxxv.sum*?.values.grep(*>2)},{25*(6==[*]
.values)},30*?*{3&4&(1&2|2&5|5&6)},40*?*{2&3&4&5&(1|6)},50*(*.keys==1)}

Try it online!

Since input may be accepted "however is convenient," my function takes it as an instance of the Bag class, which is a container with multiplicity. A Bag is also an associative container; $bag{$key} returns how many times $key occurs in the bag.

The bulk of the function is just a list of functions that evaluate each possible Yahtzee hand, returning the score for that hand or zero if the conditions for the hand are not met.

  • |(1..6).map({ *{$_} * $_ }) is a list of six functions that evaluate hands based on repeated runs of the numbers 1-6. The leading | flattens this list into the surrounding list.
  • {.kxxv.sum * ?.values.grep(* > 2) } evaluates the 3- and 4-of-a-kind hands. .kxxv on a Bag returns the keys repeated with each's multiplicity, recovering the original list of die rolls, and .sum of course sums the dice. That sum is multiplied by a boolean value (?) which is true if the bag's .values (ie, the multiplicities) contain a value larger than 2.
  • { 25 * (6 == [*] .values) } evaluates the full house hand. 25 is multiplied by a boolean value which is true if the product of the multiplicities is 6, which for five dice can only happen if one is 3 and the other is 2.
  • 30 * ?*{ 3 & 4 & (1 & 2 | 2 & 5 | 5 & 6) } evaluates the small straight hand. It's a WhateverCode function; the second star * is the Bag. The expression between the braces is the junction of values 3 and 4, and either 1 and 2, or 2 and 5, or 5 and 6. Looking up this junction in the Bag results in a junction of the corresponding multiplicities. If the mulitplicities of 3 and 4, and at least one of 1 and 2, or 2 and 5, or 5 and 6, are nonzero, the junction is true when coerced to a boolean (with ?), and this boolean is multiplied by 30 to get the score.
  • 40 * ?*{ 2 & 3 & 4 & 5 & (1 | 6) } similarly evaluates the large straight hand. It's simpler because the dice must include each of the numbers 2-5, and either 1 or 6.
  • 50 * (*.keys == 1) evaluates the Yahtzee hand. It's simply 50 times a boolean value which is true if the number of distinct dice is one.
\$\endgroup\$
2
\$\begingroup\$

Pip, 65 63 bytes

n:_NgM\,6MXn*\,6AL[2<MXn23=JSNn3<Y#MX Jn^0MXn=5]*[$+g25--y*t50]

Takes the dice as five command-line arguments. Try it online!

Ungolfed + explanation

(This is the original version.)

                    g is list of cmdline args; t is 10 (implicit)

Y                   Yank into y:
  _Ng                function that counts occurrences of its argument in g
 M                   mapped to
  \,6                inclusive range from 1 to 6
                    This gives us how many dice are showing each number 1-6

s:                  Assign to s:
  # MX               length of max of
      Jy ^ 0         join y into string and split on zeros
                    This gives us the length of the longest straight

MX                  Max of
   y * \,6           each die frequency in y, times its value
 AL                  to which list append
   [                 this list:
                      3- and 4-of-a-kind:
    MXy > 2 & $+g      If max frequency is 3 or more, sum of g (else 0)
                      Full house:
    23 = J SNy & 25    Sort y and join into string; if it's 000023, 25 (else 0)
                      Straights:
    s > 3 & --s*t      If s is 4 or more, (s-1)*10 (else 0)
                      Yahtzee:
    MXy = 5 & 50       If max frequency is 5, 50 (else 0)
   ]
                    The result of the last expression is autoprinted
\$\endgroup\$
1
\$\begingroup\$

Ruby, 184 bytes

Full program. To make it easier to test input, add $/=' ' on top to read in the "digit separated by spaces" format. (191 chars)

a=$<.map &:to_i
b=a.|c=[]
d=(1..6).map{|x|e=a.count x
c<<x*e
e}
e=a.sum
p !b[1]?50:b[4]&&!(a&[1,6])[1]?40:(1..3).any?{|x|(a&[*x..x+3])[3]}?30:(d-[0,2,3])[0]?d.max>2?e:c.max: [25,e].max

I set out the break the 200 bytes barrier and I managed to destroy it with a dozen bytes left, with ease!

Try it online!

Explanation

Not a very good one though. Hope you have some Ruby knowledge~

a=$<.map &:to_i # a: input as [number]*5
b=a.|c=[]       # c: [], b: a.uniq
d=(1..6).map{|x|
    e=a.count x # e: occurrence count in a 
    c<<x*e      # add (number * occurrence count) to c
    e           # return value in d
}
e=a.sum         # e: sum of input
p !b[1] ? 50 :  #   condition to print 50 <= if a.uniq has length 0 (el 1 is nil)
  b[4] &&       #   condition to print 40 <= if a.uniq has length 5 (el 4 exists)
  !(a&[1,6])[1] ? 40 : # <- arr & [mask]  # and a does not have both 1 and 6
  (1..3).any?{|x| # condition to print 30 <= if any of 1..4, 2..5, 3..6
  (a&[*x..x+3])[3]} ? 30 : # [3] to assert entire mask is found in a
  (d-[0,2,3])[0] ? # if, after removing 0 (not found) 2 (a pair) 3 (a triple)
                   # and something is found, this is not full house
  d.max > 2 ?   # is triple / quadruple ?
     e :        # weakly dominating alternatives
     c.max      # choose best by-suit
  : [25,e].max  # choose best by-score
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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