Mid-Autumn Festival gambling game

Tomorrow is the Mid-Autumn festival, and in the spirit of that holiday, I will introduce a gambling game that we (people from Xiamen) play during the holiday!

Rules

The game is played with six 6-sided dice. Different combinations of numbers have different ranks, with a special emphasis on fours and ones. Your job is to write a program/function that will rank the hand, given a roll of 6 dice. Here are the ranks (I've modified/simplified the rules a bit): I guess only Chinese people can do this challenge! Okay, fine, here are some English explanations.

• 0: 4 fours and 2 ones.
• 1: 6 fours.
• 2: 6 ones.
• 3: 6 of any kind except fours and ones.
• 4: 5 fours.
• 5: 5 of any kind except for fours.
• 6: 4 fours.
• 7: Straight. (1-6)
• 8: 3 fours.
• 9: 4 of any kind except 4.
• 10: 2 fours.
• 11: 1 four.
• 12: Nothing.

Input

6 numbers, an array of 6 numbers, or a string of 6 numbers that represent the values of the 6 dice rolls from 1-6

Output

Your program/function may return/output anything to indicate the rank, as long as each rank is indicated by one output and vice versa. Ex. Using the numbers 0-12, 1-13, etc.

Examples(Using 0-12 as the outputs)

[1,1,1,1,1,1]->2
[1,4,4,4,1,4]->0
[3,6,5,1,4,2]->7
[1,2,3,5,6,6]->12
[3,6,3,3,3,3]->5
[4,5,5,5,5,5]->5


This is code-golf, so shortest byte count wins!

• (I would've put this in the sandbox, but I wanted the timing to be right. I tried to be as thorough as possible, please let me know if there are any clarifications needed.) – Quintec Sep 23 '18 at 23:03
• @Shaggy So the OP says that output 0-12 or 1-13 instead – Shieru Asakoto Sep 23 '18 at 23:15
• @Shaggy As I stated in the question, the output does not necessarily have to correspond with the label number. The skipped number and random gaps in the image are to simplify the rules a bit - there really aren't definite rules for this tradition, this is just my interpretation. – Quintec Sep 23 '18 at 23:18
• Shouldn't it be [1,2,3,5,6,6]->13 ?? – J.Doe Sep 24 '18 at 13:02
• @J.Doe The examples/test-cases use the indices as result instead of the values. Unlike the values, the 10 isn't skipped. – Kevin Cruijssen Sep 24 '18 at 14:06

Charcoal, 55 bytes

≡⌈Ｅθ№θι⁶Ｉ⁻²⌕14§θχ⁵Ｉ⁻⁵⁼⁵№θ4⁴⎇⁼№θ4⁴§60⁼№θ1²9¹7Ｉ⁻¹²⌊⊘Ｘ²№θ4


Try it online! Link is to verbose version of code. Does not skip 10. Explanation:

≡⌈Ｅθ№θι


Calculate the highest frequency of any digit.

⁶Ｉ⁻²⌕14§θχ


If there is a 6 of a kind, then subtract the position of the digit in the string 14 from 2. This results in 1 for 6 4s, 2 for 6 1s, and 3 for 6 of anything else.

⁵Ｉ⁻⁵⁼⁵№θ4


If there is a 5 of a kind, then the result is 5 unless there are 5 4s, in which case 1 is subtracted.

⁴⎇⁼№θ4⁴§60⁼№θ1²9


If there is a 4 of a kind, then if there are 4 4s then the result is 6 unless there are 2 1s in which case the result is 0, otherwise the result is 9.

¹7


If all the digits are different then the result is 7.

Ｉ⁻¹²⌊⊘Ｘ²№θ4


Otherwise the result is 12 - (4 >> (3 - # of 4s)).

• It seems to be best practice to accept the shortest answer, so that's what I'll do :) Unfortunately not many people saw and upvoted your answer... – Quintec Sep 29 '18 at 19:02
• @Quintec That's not a problem; people are supposed to upvote answers based on the ingenuity of the algorithm or other factor that makes them appreciate the answer. – Neil Sep 29 '18 at 21:25

JavaScript (ES6), 88 bytes

a=>a.map(o=n=>[x=o[n]=-~o[n],6,,,21,9,8^o][a=x<a?a:x])|[,1,4,5,o&2|8,2,4][o]


Outputs an integer according to the following mapping:

 Rank | Output       Rank | Output
------+--------     ------+--------
0  |  31            7  |   7
1  |  12            8  |   5
2  |  14            9  |  21
3  |   8           10  |   4
4  |  11           11  |   1
5  |   9           12  |   0
6  |  29


How?

Method

The output is computed by performing a bitwise OR between:

• a bitmask based on $$\F\$$: the number of 4's
• a bitmask based on $$\M\$$: the maximum number of occurrences of the same dice

Exceptions:

• When $$\F=4\$$: we use a special bitmask $$\4b\$$ when the two other dice are 1's, or a default bitmask $$\4a\$$ otherwise.
• When $$\M=6\$$: we use a special bitmask $$\6b\$$ when we have a six-of-a-kind of 1's, or a default bitmask $$\6a\$$ otherwise.

Table

Valid combinations of $$\F\$$ and $$\M\$$ are highlighted in bold and blue in the following table.

$$\begin{array}{c|c|cccccccc} &F&0&1&2&3&4a&4b&5&6\\ \hline M&\text{OR}&0&1&4&5&8&10&2&4\\ \hline 1&6&\color{grey}{6}&\color{blue}{\textbf{7}}&\color{grey}{6}&\color{grey}{7}&\color{grey}{14}&\color{grey}{14}&\color{grey}{6}&\color{grey}{6}\\ 2&0&\color{blue}{\textbf{0}}&\color{blue}{\textbf{1}}&\color{blue}{\textbf{4}}&\color{grey}{5}&\color{grey}{8}&\color{grey}{10}&\color{grey}{2}&\color{grey}{4}\\ 3&0&\color{blue}{\textbf{0}}&\color{blue}{\textbf{1}}&\color{blue}{\textbf{4}}&\color{blue}{\textbf{5}}&\color{grey}{8}&\color{grey}{10}&\color{grey}{2}&\color{grey}{4}\\ 4&21&\color{blue}{\textbf{21}}&\color{blue}{\textbf{21}}&\color{blue}{\textbf{21}}&\color{grey}{21}&\color{blue}{\textbf{29}}&\color{blue}{\textbf{31}}&\color{grey}{23}&\color{grey}{21}\\ 5&9&\color{blue}{\textbf{9}}&\color{blue}{\textbf{9}}&\color{grey}{13}&\color{grey}{13}&\color{grey}{9}&\color{grey}{11}&\color{blue}{\textbf{11}}&\color{grey}{13}\\ 6a&8&\color{blue}{\textbf{8}}&\color{grey}{9}&\color{grey}{12}&\color{grey}{13}&\color{grey}{8}&\color{grey}{10}&\color{grey}{10}&\color{blue}{\textbf{12}}\\ 6b&14&\color{blue}{\textbf{14}}&\color{grey}{15}&\color{grey}{14}&\color{grey}{15}&\color{grey}{14}&\color{grey}{14}&\color{grey}{14}&\color{grey}{14}\\ \end{array}$$

All other combinations (in gray) can't possibly happen. For instance, if we have three 4's, we must have $$\M\ge3\$$. But because there are only 3 other remaining dice in such a roll, we also have $$\M\le3\$$. So, there's only one possible value of $$\M\$$ for $$\F=3\$$.

Example

If we have a straight, each dice appears exactly once. So we have $$\M=1\$$ and $$\F=1\$$. Using the bitmasks described in the above table, this leads to:

$$6 \text{ OR } 1 = 7$$

There is another $$\7\$$ in the table, but it's invalid. Therefore, a straight is uniquely identified by $$\7\$$.

Commented

a =>                   // a[] = input array, reused as an integer to keep track of the
a.map(               //       maximum number of occurrences of the same dice (M)
o =                // o = object used to count the number of occurrences of each dice
n =>               // for each dice n in a[]:
[                  //   this is the lookup array for M-bitmasks:
x =              //     x = o[n] = number of occurrences of the current dice
o[n] = -~o[n], //     increment o[n] (we can't have M = 0, so this slot is not used)
6,               //     M = 1 -> bitmask = 6
,                //     M = 2 -> bitmask = 0
,                //     M = 3 -> bitmask = 0
21,              //     M = 4 -> bitmask = 21
9,               //     M = 5 -> bitmask = 9
8 ^ o         //     M = 6 -> bitmask = 14 for six 1's, or 8 otherwise
][a =              //   take the entry corresponding to M (stored in a)
x < a ? a : x] //   update a to max(a, x)
)                 // end of map(); keep the last value
|                    // do a bitwise OR with the second bitmask
[                    // this is the lookup array for F-bitmasks:
,                  //   F = 0 -> bitmask = 0
1,                 //   F = 1 -> bitmask = 1
4,                 //   F = 2 -> bitmask = 4
5,                 //   F = 3 -> bitmask = 5
o & 2 | 8,      //   F = 4 -> bitmask = 10 if we also have two 1's, 8 otherwise
2,                 //   F = 5 -> bitmask = 2
4                  //   F = 6 -> bitmask = 4
][o]              // take the entry corresponding to F (the number of 4's)


R, 100 bytes

Encode the score as a bunch of indexed conditionals. Simpler than my first stringy-regex approach.

Edit- fixed bug and now rank all rolls.

function(d,s=sum(d<2))min(2[s>5],c(11,10,8,6-6*!s-2,4,1)[sum(d==4)],c(7,12,12,9,5,3)[max(table(d))])


Try it online!

JavaScript (Node.js), 169 bytes

a=>-~"114444|123456".search(w=a.sort().join,[l,t]=/(.)\1{3,}/.exec(w)||,l=l.length)||(l>5?3-"14".search(t):l>3?4+(5.5-l)*(t-4?4:2):[,13,12,11,9][w.split4.length])


Try it online!

Returns 1..13

Python 2,  148  119 bytes

-27 bytes thanks to ovs's (1. use of .count allowing a map to be used; 2. removal of redundant 0 in slice; 3. use of an in rather than a max; 4. shortened (F==4)*O==2 to F==4>O==2 [since golfed to F>3>O>1])

C=map(input().count,range(1,7))
O,F=C[::3]
print[F>3>O>1,F>5,O>5,6in C,F>4,5in C,F>3,all(C),F>2,4in C,F>1,F,1].index(1)


Try it online!

• @ovs oh heh .count >_< nice – Jonathan Allan Sep 24 '18 at 14:15
• One last suggestion: As d is only needed one, this is shorter as a full program. – ovs Sep 24 '18 at 14:19
• Python 3 version: 131 bytes – Adirio Sep 24 '18 at 14:31
• @ovs - thanks for all your golfing. Saved another two to boot. – Jonathan Allan Sep 24 '18 at 17:17
• Gotta love python conditional chaining! Also, the spec now doesn't skip the number 10 – Quintec Sep 24 '18 at 17:25

Pyth, 60 bytes

eS[@[ZhZ2 4*6hq2hJuXtHG1Qm0Q8hT)@J3*5hSJ*Tq6hJ*<3KeSJ@j937TK


Maps to reversed rank, 0-12. Try it online here, or verify all the test cases at once here.

The full mapping used is as follows:

12: 4 fours and 2 ones.
11: 6 fours.
10: 6 ones.
9: 6 of any kind except fours and ones.
8: 5 fours.
7: 5 of any kind except for fours.
6: 4 fours.
5: Straight. (1-6)
4: 3 fours.
3: 4 of any kind except 4.
2: 2 fours.
1: 1 four.
0: Nothing.


This works by mapping the dice values to frequencies, then calculating the value of several rules, and taking the maximum of the set.

Implicit: Q=eval(input()), Z=0, T=10

JuXtHG1Qm0Q   Input mapping
u     Q      Reduce each element of the input, as H, ...
m0Q   ...with initial value, G, as an array of 6 zeroes (map each roll to 0)
tH           Decrement the dice roll
X  G1         Add 1 to the frequency at that point
J             Store the result in J

@[ZhZ2 4*6hq2hJuXtHG1Qm0Q8hT)@J3   Rule 1 - Values for sets including 4
Z                               *0
hZ                             *1 (increment 0)
2                            *2
4                          *4
JuXtHG1Qm0Q          Input mapping (as above)
h                     First value of the above - i.e. number of 1's
q2                      1 if the above is equal to 2, 0 otherwise
*6h                       *Increment and multiply by 6
Maps to 12 if there are 2 1's, 6 otherwise
8        *8
hT      *11 (increment 10)
[                          )      Wrap the 7 starred results in an array
@J3   Get the 4th value of the input mapping - i.e. number of 4's
@                                  Get the value at that position in the array

*5hSJ   Rule 2 - Straight
hSJ   Smallest frequency in input mapping (first, sort, J)
For a straight, smallest value will be 1, 0 otherwise
*5      Multiply by 5

*Tq6hJ   Rule 3 - 6 1's
hJ   Frequency of 1's (first value from input mapping)
q6     1 if the above == 6, 0 otherwise
*T       Multiply by 10

*<3KeSJ@j937TK   Rule 4 - 4,5,6 of a kind, other than 4's
KeSJ          Get the max frequency from input mapping, store in K
j937T    [9,3,7]
@     K   Get the element at K'th position in the above (modular indexing)
<3K             1 if 3<K, 0 otherwise
*                Multiply the previous 2 results

eS[   Wrapping it all up!
[   Wrap all the above rules in an array
eS    Take the max value of the above, implicit print


Retina, 137 126 bytes

4
A
O.
^11A+
0
1+$2 ^A+ 1 (.)\1{5} 3 ^.A+ 4 .?(.)\1{4}.? 5 ^..A+ 6 12356A 7 .+AAA$
8
.*(.)\1{3}.*
9
.+AA$10 .+A$
11
.{6}
12


-11 bytes thanks to @Neil.

Output is 0-indexed (0..12).

Explanation:

Replace every 4 with an 'A':

4
A


Sort all the input-digits (the A's will be at the back):

O.


Every other two lines replaces the input with the expected output:

Regex         Replacement  Explanation

^11A+         0            starts with "11", followed by any amount of A's
1+$2 ends with any amount of 1s ^A+ 1 starts with any amount of A's (.)\1{5} 3 six of the same characters ^.A+ 4 starts with any character, followed by any amount of A's .?(.)\1{4}.? 5 five of the same characters, with optionally a leading or trailing character ^..A+ 6 starts with any two characters, followed by any amount of A's 12356A 7 literal "12356A" match .+AAA$        8            any amount of leading characters, ending with three A's
.*(.)\1{3}.*  9            four of the same characters,
with optionally any amount of leading/trailing chars
.+AA$10 any amount of leading characters, ending with two A's .+A$          11           any amount of leading characters, ending with a A
.{6}          12           any six characters

• Very clever, but I think you can go one step further and replace 4 with something outside of the range 1-6 to get it to sort to one end automatically (not sure whether it makes a difference which end you sort to). – Neil Sep 24 '18 at 16:06
• @Neil Ah, that's clever as well! Thanks. – Kevin Cruijssen Sep 24 '18 at 16:57

05AB1E, 57 55 bytes

4¢4@U.M¢©5-Di14¹нk2αë>Di5X-ë>i9X3*-¹1¢2Ê*ë®i7ë¹4¢o;ï12α


Port of @Neil's Charcoal answer, because my initial approach was already at 60 bytes and I wasn't done yet. My current answer can probably be golfed some more, though.

Input as a list of digits.

Explanation:

4¢              # Count the amount of 4s in the (implicit) input-list
4@            # Check if it's larger than or equal to 4
# (results in 1 for truthy; 0 for falsey)
U           # Pop and store that result in variable X
.M              # Push the most frequent number in the (implicit) input-list
¢             # Pop and push the count of that number in the (implicit) input-list
©            # Store it in the register (without popping)
5-Di            # If the maximum count - 5 is exactly 1 (so the maximum count is 6):
14          #  Push 14
¹н        #  Push the first digit of the input-list
k       #  Get its index in 14, resulting in -1, 0, or 1
2α     #  Take the absolute difference with 2
#  (This covers cases 1, 2, and 3)
ë>Di            # Else-if the maximum count - 5 + 1 is exactly 1 (so the maximum count is 5):
5           #  Push 5
X-         #  And subtract variable X
#  (This covers cases 4 and 5)
ë>i             # Else-if the maximum count - 5 + 2 is exactly 1 (so the maximum count is 4):
9            #  Push 9
X3*         #  Multiply variable X by 3
-        #  And subtract it from the 9
¹1¢     #  Count the amount of 1s in the input-list
2Ê   #  Check if it's NOT equal to 2 (1 if truthy; 0 if falsey)
*  #  Multiply it with the 9 or 6
#  (This covers cases 0, 6, and 9)
ë®i             # Else-if the maximum count is 1:
7            #  Push a 7
#  (This covers case 7)
ë               # Else (maximum count is 2 or 3):
¹4¢            #  Count the amount of 4s in the input-list
o           #  Take 2 to the power this count
;ï         #  Halve and floor it
12α      #  And take the absolute difference with 12
#  (This covers cases 8, 10, 11, and 12)
# (Output the top of the stack implicitly as result)


Ruby, 100 bytes

->a{%w(^2.*4$6$ ^6 6 5$5 4$ ^1*$3$ 4 2$1$ .).index{|b|/#{b}/=~([1,*a,4].map{|c|a.count(c)}*'')}}


Try it online!

How it works:

Count occurrences of every number in the array, prepend number of 1s and append number of 4s.

After that, try to match with different regex patterns.