11
\$\begingroup\$

421 is a rather popular dice game in France and some other European countries. It is mostly played in bars and pubs to determine who's going to buy the next round of drinks. The full game is usually played in two rounds, with tokens that each player tries to get rid of, but this is irrelevant here. (Wikipedia page in French.)

The game is played with 3 standard cube dice.

Task

Your task is to sort a non-empty list of distinct 3-dice rolls [ X,Y,Z ] from highest to lowest, by applying the scoring rules of this game.

Basic scoring

  • 4,2,1 is the highest possible combination. Depending on the rules, it may score 8, 10 or 11 points. Because we are sorting the rolls rather than counting the points, the exact value doesn't matter.
  • Three Aces: 1,1,1 is the second highest combination and scores 7 points.
  • Two-Aces: X,1,1 (where X is 2 to 6) scores X points.
  • Three-of-a-Kind: X,X,X (where X is 2 to 6) scores X points.
  • Straights: X,X+1,X+2 scores 2 points.
  • All other rolls score 1 point.

Settling ties

Whenever two rolls give the same number of points, the following rules apply:

  • A Two-Aces is better than a Three-of-a-Kind. Example: 5,1,1 beats 5,5,5.
  • The Three-of-a-Kind 2,2,2 is better than a straight. Example: 2,2,2 beats 4,5,6.
  • Straights are ordered from lowest to highest. Example: 4,5,6 beats 2,3,4.
  • All other rolls are settled by sorting the dice from highest to lowest. Example: 6,5,2 beats 6,4,3. (Therefore, the lowest possible combination in the game is 2,2,1.)

Below are the 56 possible distinct rolls ordered from highest to lowest:

421 111 611 666 511 555 411 444 311 333 211 222 654 543 432 321
665 664 663 662 661 655 653 652 651 644 643 642 641 633 632 631
622 621 554 553 552 551 544 542 541 533 532 531 522 521 443 442
441 433 431 422 332 331 322 221

Challenge rules

  • You may take the rolls in any reasonable format, such as a list of lists [[3,2,1],[4,2,1]], a list of strings ["321","421"], a list of integers [321,421], etc. However, each die must be clearly identifiable with a value from 1 to 6.
  • For each roll, you can assume that the dice are sorted either from lowest to highest or from highest to lowest, as long as it is consistent. Please state in your answer which order you're expecting, if any.
  • The shortest answer in bytes wins!

Test cases

Using lists of strings with the dice sorted from highest to lowest:

Inputs

[ "321", "654" ]
[ "222", "321", "211" ]
[ "333", "311", "331", "111" ]
[ "111", "222", "333", "444" ]
[ "321", "421", "521", "621" ]
[ "422", "221", "442", "421", "222" ]
[ "222", "111", "421", "211", "651", "652", "543" ]

Expected outputs

[ "654", "321" ]
[ "211", "222", "321" ]
[ "111", "311", "333", "331" ]
[ "111", "444", "333", "222" ]
[ "421", "321", "621", "521" ]
[ "421", "222", "442", "422", "221" ]
[ "421", "111", "211", "222", "543", "652", "651" ]
\$\endgroup\$
  • \$\begingroup\$ Should the sort be stable? \$\endgroup\$ – Erik the Outgolfer Jul 28 '18 at 19:16
  • \$\begingroup\$ @EriktheOutgolfer All rolls are distinct and two rolls can always be sorted according to the ties rules. \$\endgroup\$ – Arnauld Jul 28 '18 at 19:20
  • \$\begingroup\$ Would it be OK to take each die as 0-5 instead of 1-6 (e.g. 012 instead of 123)? \$\endgroup\$ – wastl Jul 28 '18 at 20:38
  • \$\begingroup\$ @wastl I'm going to say no. The format is flexible but the dice values must be in [1...6]. \$\endgroup\$ – Arnauld Jul 28 '18 at 20:52
  • \$\begingroup\$ You carry dice to the pub? \$\endgroup\$ – Beta Decay Jul 29 '18 at 0:54
6
\$\begingroup\$

Python, 93 bytes

lambda a:sorted(a,key=lambda x:(x!=421,x>111,-(x%100<12)*x-(x%111<1)*x*.9,(x%111==99)*-x,-x))

Try it online!

\$\endgroup\$
  • \$\begingroup\$ What makes this specific to Python 2? \$\endgroup\$ – Solomon Ucko Jul 29 '18 at 3:15
  • \$\begingroup\$ @SolomonUcko Nothing, but I guess it's marked as Python 2 because it was auto-generated by the Code Golf Submission template from TIO. \$\endgroup\$ – Mr. Xcoder Jul 29 '18 at 8:09
  • 1
    \$\begingroup\$ You can save 2 bytes by using the fact that 111=37*3 (and only xxx triples among all the possible triplets are divisible by 37) Try it online! \$\endgroup\$ – digEmAll Jul 29 '18 at 19:38
  • 1
    \$\begingroup\$ You can lose 2 bytes by sorting in place with the sort method. \$\endgroup\$ – Jakob Jul 30 '18 at 2:01
4
\$\begingroup\$

Retina 0.8.2, 67 bytes

O$#^`(421|111)|(\d)((11)|\2\2)|(654|543|432|321)|\d{3}
$1$2$#4$#5$&

Try it online! Link includes test cases. Explanation: The numeric sort key is generated as follows:

421     42100421
111     11100111
611-211   610611-  210211
666-222   600666-  600222
654-321    01654-   01321
653-221    00653-   00221
\$\endgroup\$
  • \$\begingroup\$ @Arnauld Sorry, I hadn't realised I'd mixed up those cases. Fortunately I think I've been able to fix it for the same byte count. \$\endgroup\$ – Neil Jul 28 '18 at 23:13
  • \$\begingroup\$ Now it's looking fine indeed. \$\endgroup\$ – Arnauld Jul 28 '18 at 23:15
3
\$\begingroup\$

Jelly,  37  34 bytes

;Ø1,ẋ3$)Ẏṙ-;‘r’Ɗ€;œċ3$Ṇ63¦
6RṚÇiµÞ

A monadic link accepting a list of lists of dice rolls (each sorted descending) which yields the sorted rolls descending.

Try it online! Or see a test-suite ...or try all rolls sorted lexicographically

How?

This method builds a list of all rolls from highest to lowest*, replacing [4,2,1] with 0 (an invalid input roll), in order to rank each roll using Jelly's first-index-of atom, i.

* The list also includes repeats and redundant entries which wont affect the behaviour:

[[1,1,1],[6,1,1],[6,6,6],[5,1,1],[5,5,5],[4,1,1],[4,4,4],[3,1,1],[3,3,3],[2,1,1],[2,2,2],[1,1,1],[7,6,5],[6,5,4],[5,4,3],[4,3,2],[3,2,1],[2,1,0],[6,6,6],[6,6,5],[6,6,4],[6,6,3],[6,6,2],[6,6,1],[6,5,5],[6,5,4],[6,5,3],[6,5,2],[6,5,1],[6,4,4],[6,4,3],[6,4,2],[6,4,1],[6,3,3],[6,3,2],[6,3,1],[6,2,2],[6,2,1],[6,1,1],[5,5,5],[5,5,4],[5,5,3],[5,5,2],[5,5,1],[5,4,4],[5,4,3],[5,4,2],[5,4,1],[5,3,3],[5,3,2],[5,3,1],[5,2,2],[5,2,1],[5,1,1],[4,4,4],[4,4,3],[4,4,2],[4,4,1],[4,3,3],[4,3,2],[4,3,1],[4,2,2],0,[4,1,1],[3,3,3],[3,3,2],[3,3,1],[3,2,2],[3,2,1],[3,1,1],[2,2,2],[2,2,1],[2,1,1],[1,1,1]]

;Ø1,ẋ3$)Ẏṙ-;‘r’Ɗ€;œċ3$Ṇ63¦ - Link 1, build rolls: descending pips, P  e.g. [6,5,4,3,2,1]
       )                   - for each:       e.g. X=5
 Ø1                        -   literal [1,1]
;                          -   concatenate        [5,1,1]
      $                    -   last two links as a monad (f(X)):
     3                     -     literal 3
    ẋ                      -     repeat           [5,5,5]
   ,                       -   pair               [[5,1,1],[5,5,5]]
        Ẏ                  - tighten (to a list of rolls rather than pairs of rolls)
          -                - literal -1
         ṙ                 - rotate left by (make rightmost [1,1,1] the leftmost entry)
               Ɗ€          - for €ach: last three links as a monad (f(X)):
            ‘              -   increment  -> X+1
              ’            -   decrement  -> X-1
             r             -   range      -> [X+1,X,X-1]
                           -   ...note: [7,6,5] and [2,1,0] are made but are redundant
           ;               - concatenate
                     $     - last two links as a monad (f(P)):
                    3      -   literal 3
                  œċ       -   combinations with replacement -> [[6,6,6],[6,6,5],...,[6,6,1],[6,5,5],...,[1,1,1]]
                 ;         - concatenate
                         ¦ - sparse application...
                       63  - ...to indices: 63
                      Ṇ    - ...what: NOT   -> replaces the [4,2,1] entry with a 0

6RṚÇiµÞ - Main Link: list of rolls
     µÞ - sort by the monadic link:
6       -   six
 R      -   range -> [1,2,3,4,5,6]
  Ṛ     -   reverse -> [6,5,4,3,2,1]
   Ç    -   call the last link as a monad -> [[1,1,1],[6,1,1],[6,6,6],[5,1,1],...]
    i   -   first index of (e.g.: [1,1,1]->1 or [6,1,1]->2 or [4,2,1]->0)
\$\endgroup\$
3
\$\begingroup\$

R, 73 bytes

(x=scan())[order(x!=421,x>111,-(x%%100<12)*x-(!x%%37)*x*.9,x%%37!=25,-x)]

Try it online!

  • Full program taking a list of integer from stdin and returning them in descending order (i.e. 421 ... 221)
  • Started as partially inspired by @Lynn answer, now is basically a porting of it... so credits to @Lynn ;)
  • Saved 2 bytes getting division remainder x % 37 instead of 111

Explanation :

For each of the numbers 5 keys are computed and used hierarchically to sort the array :

key1 = 0 if equal to 421, 1 otherwise
key2 = 0 if equal to 111, 1 otherwise
key3 = 0 
       - 1.0*x if number is x11 numbers (2 aces)
       - 0.9*x if number is xxx numbers (3 of a kind)
key4 = 0 if number is a straight, 1 otherwise
key5 = -x

Then the array is sorted by key1 first, then by key2 in case of tie and so on...
\$\endgroup\$
2
\$\begingroup\$

JavaScript (ES7), 96 bytes

d=>d.sort((a,b,g=(n,[x,y,z]=n)=>n**=n-421?y*z-1?x-y|y-z?~y%x|~z%y?1:2:3:x-1?3.1:5:5)=>g(b)-g(a))

Sorts rolls by adhering closely to the rules of scoring. Expects an array of strings with individual rolls in descending order of value, e.g. ["654"]

Try it online!

Explanation

Categories of rolls are raised to the following exponents:

421                 5
Three Aces          5
Two Aces            3.1
Three-of-a-Kind     3
Straights           2
Other               1

Ungolfed

Mentally unwrapping the conditional checks gives me a headache, and I'm certain it can somehow be further golfed....

var f =
d => d.sort(
    (
     a, b,
     g = (n, [x, y, z] = n) =>               // destructure rolls, e.g. "321" => ["3","2","1"]
        n **=                                // differentiate scores by raising to the exponent corresponding to its category in the table above
                                             // all conditionals check for a falsy value
            n - 421
                ? y * z - 1                  // ends with "11"
                    ? x - y | y - z          // three-of-a-kind
                        ? ~y % x | ~z % y    // straights, with operators chosen to correctly convert strings to numbers
                            ? 1
                            : 2
                        : 3
                    : x - 1                  // three aces
                        ? 3.1
                        : 5
                : 5
    ) =>
    g(b) - g(a)
)
\$\endgroup\$
1
\$\begingroup\$

Javascript, 101 chars, 116 bytes (UTF-8)

a=>(r=d=>(i="ƥoɣʚǿȫƛƼķōÓÞʎȟưŁ".indexOf(String.fromCharCode(d)),i<0?700-d:i),a.sort((a,b)=>r(a)-r(b)))

Takes an array of numbers representing the rolls like [ 321, 654 ].

Try it online!

Explanation:

I took the first 16 of the 56 possible distinct rolls (the ones that don't really follow any order) and I encoded them as a string: "ƥoɣʚǿȫƛƼķōÓÞʎȟưŁ". Each character of this string corresponds the the first 16 possible rolls (ƥ is 421, o is 111, ...). Now for each two elements a and b of the array we just check their index from the string, if they are included the index is used, otherwise (the index is -1) we use the roll number itself (we substract it from 700 to inverse the order of the ones not included, i.e. to sort them in a decreasing order).

\$\endgroup\$
  • \$\begingroup\$ NB: Unless otherwise specified, code length is always measured in bytes. The number of chars may sometimes be relevant, but I don't think it is here. Relevant meta answer. \$\endgroup\$ – Arnauld Jul 30 '18 at 8:01
  • \$\begingroup\$ @Arnauld Yeah! BTW I'm the one who asked that meta question ;-) \$\endgroup\$ – ibrahim mahrir Jul 30 '18 at 13:58
  • 1
    \$\begingroup\$ D'oh! I missed this detail entirely... :D \$\endgroup\$ – Arnauld Jul 30 '18 at 14:03
1
\$\begingroup\$

Clean, 169 102 bytes

All octal escapes are counted as one byte, as the Clean compiler will happily take them that way, but TIO and SE won't because they aren't well-formed UTF-8.

import StdEnv
$l=[e\\c<-:"\245\157\143\232\377\53\233\274\67\115\323\336\216\37\260\101\231\230\227\226\225\217\215\214\213\204\203\202\201\171\170\167\156\155\52\51\50\47\40\36\35\25\24\23\n\11\273\272\271\261\257\246\114\113\102\335",e<-l|c==toChar e]

Try it online!

Uses the fact that all the dice rolls, as integers modulo 256, are unique.
Conveniently, Char is treated (mostly) as a modulo 256 integer.

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

Pyth, 48 bytes

oxs[]j421T]j7 2_sm+Ld,,ddjT9tS6_m_+LdS3 4.C_S6 3

Expects input as a nested array, each element ordered descending. Try it online here, or verify all the test cases at once here.

oxs[]j421T]j7 2_sm+Ld,,ddjT9tS6_m_+LdS3 4.C_S6 3)NQ   Final 3 tokens inferred from context
                                                      Implicit: Q=eval(input()), T=10
    ]j421T                                            Convert 421 to base 10, wrap in array -> [[4,2,1]]
          ]j7 2                                       Convert 7 to base 2, wrap in array -> [[1,1,1]]
                 m          tS6                       Map d in [2,3,4,5,6] using:
                      ,dd                               [d,d]
                         jT9                            Convert 10 to base 9 -> [1,1]
                  +Ld,                                  Prepend d to each of the above
               _s                                     Flatten and reverse -> [[6,1,1],[6,6,6]...[2,2,2]]
                                m       4             Map d in [0,1,2,3] using:
                                  +LdS3                 [d+1,d+2,d+3]
                                 _                      Reverse the above
                               _                      Reverse the result -> [[6,5,4]...[3,2,1]]
                                           _S6        [6,5,4,3,2,1]
                                         .C    3      All 3-element combinations of the above, respecting order
  s[                                            )     Wrap the 5 previous sections in an array, concatenate
ox                                               NQ   Order Q using each element's index in the above list
\$\endgroup\$
1
\$\begingroup\$

05AB1E, 76 48 45 bytes

421X3∍6LR©vy11«y3∍}®vy>y<ŸJ})D®3ãJsм)˜©IΣ®sk>

This turned out to be a lot longer than expected, but at least it's easy to implement. Will see if I can find a better solution.. Made the list by hand now, but still a bit long.

Try it online or verify all test cases.

Explanation:

421             # Push 421 to the stack
X3∍             # ('1' lengthened to size 3) Push 111 to the stack
6LR             # Take the range [6, 1]
   ©            # Save this range in the register so we can re-use it
   v    }       # Loop over this range:
    y11«        #  Merge '11' with the current number `y`, and push it to the stack
    y3∍         #  Lengthen the current number `y` to size 3, and push it to the stack
   ®v      }    # Load the range [6, 1] from the register, and loop over it again:
     y>y<Ÿ      #  Take the range [`y+1`, `y-1`]
          J     #  And join them together to single 3-digit numbers
)               # Push everything that's now on the stack to a single list
 D              # Duplicate this list
  ®             # Load the range [6, 1] from the register again
   3ã           # Take the cartesian repeated three times
     R          # Reverse this list
      J         # Join every inner list to a single 3-digit number
 s              # Swap, so the duplicate list of above is at the top of the stack again
  м             # And remove all those items from the cartesian list
   )            # Combine both lists (as list of lists)
    ˜           # Flatten it
     ©          # Store this full completed list (now equal to the challenge description,
                #  with some additional values we can ignore) in the register
I               # Take the input-list
 Σ              # Sort it by:
  ®             #  The list we generated above
   sk           #  Take the 0-indexed indices
     >          #  And increase it by 1 (because apparently 0 is placed at the back,
                #  so '421' would always be at the end)

Here is the actual list the first part of the code generates:

['421','111','611','666','511','555','411','444','311','333','211','222','111','111','765','654','543','432','321','210','','665','664','663','662','661','656','655','','653','652','651','646','645','644','643','642','641','636','635','634','633','632','631','626','625','624','623','622','621','616','615','614','613','612','','566','565','564','563','562','561','556','','554','553','552','551','546','545','544','','542','541','536','535','534','533','532','531','526','525','524','523','522','521','516','515','514','513','512','','466','465','464','463','462','461','456','455','454','453','452','451','446','445','','443','442','441','436','435','434','433','','431','426','425','424','423','422','','416','415','414','413','412','','366','365','364','363','362','361','356','355','354','353','352','351','346','345','344','343','342','341','336','335','334','','332','331','326','325','324','323','322','','316','315','314','313','312','','266','265','264','263','262','261','256','255','254','253','252','251','246','245','244','243','242','241','236','235','234','233','232','231','226','225','224','223','','221','216','215','214','213','212','','166','165','164','163','162','161','156','155','154','153','152','151','146','145','144','143','142','141','136','135','134','133','132','131','126','125','124','123','122','121','116','115','114','113','112','']

Try it online.

As you can see it contains empty items; numbers like 765 or 210; and numbers in the range [216, 112]. But we can all ignore these, because the items we want to actually sort are in the correct order.


Original 76 bytes solution:

Σ•X˜„Éε´õñ€9Ú ù?ä0₅9úd5àPÎøŒccuĆYÑ,o¾eíË¿¼À8@gID=vÆOΣxjF¨O‡J₃^εR™\èv•667вsk>

Try it online or verify all test cases (no test suite because there is still a bug with sort-by with compressed numbers not seeing the closing bracket to stop the sort).

Explanation:

Σ             # Sort by:
 •...•667в    #  The correctly sorted list of all possible rolls from the challenge
 sk           #  Take the 0-indexed indices
   >          #  And increase it by 1 (because apparently 0 is placed at the back,
              #  so '421' would always be at the end)

Explanation •X˜„Éε´õñ€9Ú ù?ä0₅9úd5àPÎøŒccuĆYÑ,o¾eíË¿¼À8@gID=vÆOΣxjF¨O‡J₃^εR™\èv•667в:

Everything between the two is a compressed number that is generated with the following program:

Z>β 255B

Try it online.

  • Z>: Take the max of the list + 1 (667 in this case)
  • β: Convert the list from Base-667 to a single number
  • 255B: And convert that single number to base 255 (using 05AB1E's code page), so we have our compressed number.

By using 667в it converts this compressed number back to the original list.

\$\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.