Playing Darts: become an amateur with the most efficient prioritizing

7

3

Introduction:

I think most people will know how darts work. But since some might not, here are the rules of playing Darts in a nutshell:

  • Amount of players: 2
  • Starting points: 501
  • Goal: Get your points to 0 first
  • Darts per player: 3
  • Available numbers on the board: 1 through 20, and Bulls-eye

Here is the layout of a Dartboard:

enter image description here

  • Each number is worth an equal amount of points
  • Each triple (the inner red-green circle) is 3x the number
  • Each double (the outer red-green circle) is 2x the number
  • The red center is called the Bulls-eye or Double-Bull, which is worth 50 points
  • And the outlined part of the center around the Bulls-eye (also known as Single-Bull) is worth 25 points

Some other rules:

  • You'll have to finish to 0 exactly
  • The very last dart that puts your points to 0 must be a double or the Bulls-eye
  • If a dart would leave your remaining total on 1 or less than 0, the entire set of three darts of which it forms a part is discarded (since you aren't able to finish 1 or less than 0 with a double)
  • Once you've reached (exactly) 0, you won't have to throw any remaining darts of the three you throw in a single turn

Relevant information regarding this Challenge:

Although every player has of course preferred doubles to finish with (Double-20 is preferred by some of the pros for example), in general - and for the sake of this challenge - this will be the order of prioritization on finishing doubles:

16, 8, 20, 12, 4, 18, 14, 10, 6, 2, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1, Bulls-Eye

Why? Let's compare a double-16 and double-17 finish. If you want to throw the double-16, but throw in the 16 instead by accident, you can continue to double-8. If you want to throw the double-17, but throw in the 17 instead, you must first throw an odd number (like 1 or 3), before you can try your best on a finishing double again. If we look at the following, you can see the next move in case you accidentally throw a single instead of a double:

Bulls-eye
20-10-5
19
18-9
17
16-8-4-2-1
15
14-7
13
12-6-3
11

Clearly 16 is your best bet, hence the given order of prioritizing above. (And since the Bulls-eye is the smallest, that has the lowest preference for the sake of this challenge.)

Challenge:

So, those are the rules of playing Darts. As for this challenge:

Given an input integer in the range of 2-170 (excluding 169, 168, 166, 165, 163, 162, 159; these cannot be finished within 3 darts), output the steps required to finish it following these rules in this exact order of precedence:

  1. You must try to finish it in as few darts as possible
  2. You must keep the prioritizing in mind when doing so for your finishing darts
  3. When multiple options are available with the same priority, we pick the option with the highest number. I.e. 1.) For 131 the expected result is T20, T13, D16 instead of T19, T14, D16 or T18, T15, D16 or T17, T14, D16 etc., because T20 is the highest number. 2.) For 109 the expected result is 17, T20, D16 instead of 20, T19, D16, because T20 is the highest number.
  4. If exactly three darts must be thrown to finish, sort the first two throws by Single, Triple, Double, Bulls-Eye, Single-Bull

Keeping the rules above in mind, these will be your strict input-output pairs (format of both input and output is flexible, though):

Legend: D = Double; T = Triple; BE = Bulls-eye; SB = Single-Bull

Points left:   Darts to throw (in exact order)
2              D1
3              1, D1
4              D2
5              1, D2
6              D3
7              3, D2
8              D4
9              1, D4
10             D5
11             3, D4
12             D6
13             5, D4
14             D7
15             7, D4
16             D8
17             1, D8
18             D9
19             3, D8
20             D10
21             5, D8
22             D11
23             7, D8
24             D12
25             9, D8
26             D13
27             11, D8
28             D14
29             13, D8
30             D15
31             15, D8
32             D16
33             1, D16
34             D17
35             3, D16
36             D18
37             5, D16
38             D19
39             7, D16
40             D20
41             9, D16
42             10, D16
43             11, D16
44             12, D16
45             13, D16
46             14, D16
47             15, D16
48             16, D16
49             17, D16
50             BE
51             19, D16
52             20, D16
53             T7, D16
54             D11, D16
55             T13, D8
56             T8, D16
57             SB, D16
58             D13, D16
59             T9, D16
60             D14, D16
61             T15, D8
62             T10, D16
63             T13, D12
64             D16, D16
65             T11, D16
66             D17, D16
67             T17, D8
68             T12, D16
69             T15, D12
70             D19, D16
71             T13, D16
72             D20, D16
73             T19, D8
74             T14, D16
75             T17, D12
76             T20, D8
77             T15, D16
78             D19, D20
79             T13, D20
80             T16, D16
81             T19, D12
82             BE, D16
83             T17, D16
84             T20, D12
85             T15, D20
86             T18, D16
87             T17, D18
88             T16, D20
89             T19, D16
90             BE, D20
91             T17, D20
91             T17, D20
92             T20, D16
93             T19, D18
94             T18, D20
95             T19, D19
96             T20, D18
97             T19, D20
98             T20, D19
99             7, T20, D16
100            T20, D20
101            T17, BE
102            10, T20, D16
103            11, T20, D16
104            T18, BE
105            13, T20, D16
106            14, T20, D16
107            T19, BE
108            16, T20, D16
109            17, T20, D16
110            T20, BE
111            19, T20, D16
112            20, T20, D16
113            T20, T7, D16
114            T20, D11, D16
115            T19, D13, D16
116            T20, T8, D16
117            T20, SB, D16
118            T20, D13, D16
119            T20, T9, D16
120            T20, D14, D16
121            T19, D16, D16
122            T20, T10, D16
123            T19, D17, D16
124            T20, D16, D16
125            T20, T11, D16
126            T20, D17, D16
127            T19, D19, D16
128            T20, T12, D16
129            T19, D20, D16
130            T20, D19, D16
131            T20, T13, D16
132            T20, D20, D16
133            T17, BE, D16
134            T20, T14, D16
135            T19, D19, D20
136            T18, BE, D16
137            T20, T15, D16
138            T20, D19, D20
139            T19, BE, D16
140            T20, T16, D16
141            T17, BE, D20
142            T20, BE, D16
143            T20, T17, D16
144            T18, BE, D20
145            T20, T15, D20
146            T20, T18, D16
147            T19, BE, D20
148            T20, T16, D20
149            T20, T19, D16
150            T20, BE, D20
151            T20, T17, D20
152            T20, T20, D16
153            T20, T19, D18
154            T20, T18, D20
155            T20, T19, D19
156            T20, T20, D18
157            T20, T19, D20
158            T20, T20, D19
160            T20, T20, D20
161            T20, T17, BE
164            T20, T18, BE
167            T20, T19, BE
170            T20, T20, BE

NOTE: Test cases are done by hand, so if you see any mistakes, please comment down below.
Multiple fixed thanks to @JohanKarlsson, @JonathanAllan, and @Sebastian

Challenge rules:

  • Input and output format are flexible. Can be a list of Strings, array of Objects, single STDOUT-printline, your call. You can also choose your own formatting regarding the Triples, Doubles, Bulls-Eye and Single-Bull. Please state what you've used in your answer.

General rules:

  • This is , so shortest answer in bytes wins.
    Don't let code-golf languages discourage you from posting answers with non-codegolfing languages. Try to come up with an as short as possible answer for 'any' programming language.
  • Standard rules apply for your answer, so you are allowed to use STDIN/STDOUT, functions/method with the proper parameters and return-type, full programs. Your call.
  • Default Loopholes are forbidden.
  • If possible, please add a link with a test for your code.
  • Also, please add an explanation if necessary.

Kevin Cruijssen

Posted 2017-08-28T11:21:02.953

Reputation: 67 575

the non-double dart(s) have the following priority order (...): Single, Triple, Double, ... Not that it really matters, but just out of curiosity: I suppose a (harder) Triple is preferred over a Double in order to minimize the risk of throwing a dart outside the board? – Arnauld – 2017-08-28T11:36:51.953

@Arnauld Yes, with a Triple first there is the possibility that you miss the Triple and hit something else, but in that case your score is going down and you can adjust. When you miss a Double and it's outside the board, you basically wasted a dart completely and your score remains the same. Of course in a real life scenario there are cases where you might prefer a dart going outside the board instead of in a Single (i.e. D16 is left and you accidentally throw 7. Throwing outside the board to try D16 again would have been better, since after 7 you'd need two darts to finish 25 instead of one). – Kevin Cruijssen – 2017-08-28T11:51:08.217

The precedence order is (I believe) that "You must keep the prioritizing in mind when doing so for your finishing darts" is of greater priority than "When multiple options are available with the same priority, we pick the highest numbers" and "When you have to use 2 or 3 darts to finish, the non-double dart(s) have the following priority order (for the sake of this challenge): Single, Triple, Double, Bulls-Eye, Single-Bull" ... so for a finish of 139 (for example) wouldn't T19, BE, D16 be of higher priority than the listed T20, T19, D11? – Jonathan Allan – 2017-08-28T14:20:04.150

@JonathanAllan You're indeed right that T19, BE, D16 should be the answer for 139. We first look if we can finish it in 1 or 2 darts instead of 3; then the best double to end with (which is D16 in this case), and then others two numbers will be ordered as "Single, Triple, Double, Bulls-Eye, Single-Bull". Will edit test case 139, and if you see any other incorrect ones let me know. – Kevin Cruijssen – 2017-08-28T14:31:22.637

FYI I'm used to the 25 point ring being called the Inner. – Neil – 2017-08-28T15:06:12.020

@Neil Hmm, never heard anyone regering to it as Inner tbh. It's usually Single-Bull (or Outer Bull), and the center is known as Bulls-Eye or Double-Bull. Here is a Glossary of darts from Wikipedia.

– Kevin Cruijssen – 2017-08-28T15:10:09.557

@JonathanAllan Actually, that one is correct. 99 should be 7, T20, D16 (I know actual darters would never throw like this and always start with the T20, but in the challenge rules I've specified the following rule: "When you have to use 2 or 3 darts to finish, the non-double dart(s) have the following priority order (for the sake of this challenge): Single, Triple, Double, Bulls-Eye, Single-Bull" (because singles are easier to hit). Also fixed some other test cases order regarding rule 3. – Kevin Cruijssen – 2017-08-28T15:18:28.127

Oh, that's what that means! Why is "double" in that list? (or maybe that should be why does it read "non-double dart(s)" rather than "non-final dart(s)"?) – Jonathan Allan – 2017-08-28T15:22:14.147

Three more: 133 -> T17, BE, D16; 136 -> T18, BE, D16; and 146 -> T20, T18, D16 – Jonathan Allan – 2017-08-28T15:26:35.697

1So, could (4) just be rephrased as "If [exactly] three darts must be thrown, sort the first two throws by Single, Triple, Double, Bulls-Eye, Single-Bull", or have I still misinterpreted it? – Jonathan Allan – 2017-08-28T15:40:15.957

101 -> T17, BE, 104 -> T18, BE, 107 -> T19, BE – Johan Karlsson – 2017-08-28T15:42:02.803

142 -> T20, BE, D16 – Johan Karlsson – 2017-08-28T15:55:36.747

Ah, you're right that the 25 is actually the Outer, not the Inner. My mistake. – Neil – 2017-08-28T17:02:29.723

Answers

3

Python 3, 987 1,003 586 bytes

Edit: Fixed problem with T20's using some hacky code.

Edit: Added in golfed version.

from itertools import*
def d(i):
 f={v*2:"D"+str(v) for v in [16,8,20,12,4,18,14,10,6,2,19,17,15,13,11,9,7,5,3,1]}
 f[50]="BE"
 t={3*i:"T"+str(i) for i in range(20,6,-1)}
 t.update({i:str(i) for i in range(20,0,-1)})
 t.update({2*i:"D"+str(i) for i in range(20,10,-1) if i%3!=0})
 t.update({50:"BE", 25:"SB"})
 if i in f:
  return f.get(i)
 else:
  for n,x in product(f,t):
   r=i-n
   if r in t:
    return ' '.join((t.get(r),f.get(n)))
  for n,x,y in product(f,t,t):
   r=i-n
   if x+y==r:
    b=x!=60 or y>20
    return ' '.join((t.get(x if b else y),t.get(y if b else x), f.get(n)))

Try it online! Ungolfed version:

from itertools import product
def darts(inp):
    list_finishes = [16, 8, 20, 12, 4, 18, 14, 10, 6, 2, 19, 17, 15, 13, 11, 9, 7, 5, 3, 1]
    finishes = {finish*2:"D"+str(finish) for finish in list_finishes}
    finishes.update({50:"BE"})
    throws = {3*i:"T"+str(i) for i in range(20,6,-1)}
    throws.update({i:str(i) for i in range(20,0,-1)})
    throws.update({2*i:"D"+str(i) for i in range(20,10,-1) if i%3!=0})
    throws.update({50:"BE", 25:"SB"})

    if inp in finishes:
        return finishes.get(inp)
    else:
        for finish, x in product(finishes, throws):
            remaining = inp - finish
            if remaining in throws:
                return ', '.join((throws.get(remaining), finishes.get(finish)))
        for finish, x, y in product(finishes, throws, throws):
            remaining = inp - finish
            if x + y == remaining:
                    return ', '.join((throws.get(x if x!= 60 or y>20 else y), throws.get(y if x!= 60 or y>20 else x), finishes.get(finish)))

Little effort has gone into golfing this code, as I intended it to be a reference solution more than anything. Anyone who wants to improve this code is free to post it as their own solution or edit my answer. I have also created a function to test your function against the given values (Try it online with my function):

def test_func(func, has_comma=True):
    answers  =  """
2              D1
3              1, D1
4              D2
5              1, D2
6              D3
7              3, D2
8              D4
9              1, D4
10             D5
11             3, D4
12             D6
13             5, D4
14             D7
15             7, D4
16             D8
17             1, D8
18             D9
19             3, D8
20             D10
21             5, D8
22             D11
23             7, D8
24             D12
25             9, D8
26             D13
27             11, D8
28             D14
29             13, D8
30             D15
31             15, D8
32             D16
33             1, D16
34             D17
35             3, D16
36             D18
37             5, D16
38             D19
39             7, D16
40             D20
41             9, D16
42             10, D16
43             11, D16
44             12, D16
45             13, D16
46             14, D16
47             15, D16
48             16, D16
49             17, D16
50             BE
51             19, D16
52             20, D16
53             T7, D16
54             D11, D16
55             T13, D8
56             T8, D16
57             SB, D16
58             D13, D16
59             T9, D16
60             D14, D16
61             T15, D8
62             T10, D16
63             T13, D12
64             D16, D16
65             T11, D16
66             D17, D16
67             T17, D8
68             T12, D16
69             T15, D12
70             D19, D16
71             T13, D16
72             D20, D16
73             T19, D8
74             T14, D16
75             T17, D12
76             T20, D8
77             T15, D16
78             D19, D20
79             T13, D20
80             T16, D16
81             T19, D12
82             BE, D16
83             T17, D16
84             T20, D12
85             T15, D20
86             T18, D16
87             T17, D18
88             T16, D20
89             T19, D16
90             BE, D20
91             T17, D20
91             T17, D20
92             T20, D16
93             T19, D18
94             T18, D20
95             T19, D19
96             T20, D18
97             T19, D20
98             T20, D19
99             7, T20, D16
100            T20, D20
101            T17, BE
102            10, T20, D16
103            11, T20, D16
104            T18, BE
105            13, T20, D16
106            14, T20, D16
107            T19, BE
108            16, T20, D16
109            17, T20, D16
110            T20, BE
111            19, T20, D16
112            20, T20, D16
113            T20, T7, D16
114            T20, D11, D16
115            T19, D13, D16
116            T20, T8, D16
117            T20, SB, D16
118            T20, D13, D16
119            T20, T9, D16
120            T20, D14, D16
121            T19, D16, D16
122            T20, T10, D16
123            T19, D17, D16
124            T20, D16, D16
125            T20, T11, D16
126            T20, D17, D16
127            T19, D19, D16
128            T20, T12, D16
129            T19, D20, D16
130            T20, D19, D16
131            T20, T13, D16
132            T20, D20, D16
133            T17, BE, D16
134            T20, T14, D16
135            T19, D19, D20
136            T18, BE, D16
137            T20, T15, D16
138            T20, D19, D20
139            T19, BE, D16
140            T20, T16, D16
141            T17, BE, D20
142            T20, BE, D16
143            T20, T17, D16
144            T18, BE, D20
145            T20, T15, D20
146            T20, T18, D16
147            T19, BE, D20
148            T20, T16, D20
149            T20, T19, D16
150            T20, BE, D20
151            T20, T17, D20
152            T20, T20, D16
153            T20, T19, D18
154            T20, T18, D20
155            T20, T19, D19
156            T20, T20, D18
157            T20, T19, D20
158            T20, T20, D19
160            T20, T20, D20
161            T20, T17, BE
164            T20, T18, BE
167            T20, T19, BE
170            T20, T20, BE
"""
    if not has_comma:
        answers = answers.replace(',','')
    answers_dict = {int(answer.split('  ')[0]):answer.split('  ', 1)[1].lstrip() for answer in answers.split('\n')[1:-1]}
    all_valid = True
    for key in answers_dict.keys():
        try:
           assert func(key)==answers_dict.get(key)
        except:
            print("Failure on {}: Your answer '{}' != Given answer '{}'".format(key, func(key), answers_dict.get(key)))
            all_valid = False
    if all_valid:
        print("All tests completed successfully.")

Sebastian

Posted 2017-08-28T11:21:02.953

Reputation: 146

Great answer, and welcome to PPCG! I would suggest to golf it at least somewhat (variable/method names and spaces and such), since this is a code-golf challenge. :) I've fixed the test cases for 55, 119, 122, 127 and 131 in the challenge description, since you were given the correct output for those. As for the other ones, I've clarified rule number 3 a bit. Since rule 3 takes priority over rule 4, you first take the highest possible number, and then look at the others. So 109 should indeed be 17, T20, D16, because T20 is the highest possible number with the D16 finish. – Kevin Cruijssen – 2017-08-29T20:39:31.927

1Ah I understand now. Unfortunately, I couldn't think of an elegant way to implement that change, so I resorted to a hacky method. Oh well, all of the tests pass now. – Sebastian – 2017-08-30T00:32:15.100

Great, +1 from me! :) And I see a lot of things you could still golf in your golfed answer, but it's a bit too much to name. Tips for golfing in Python might be interesting to read through, to see if you can apply any to your code to golf it further. Thanks for being the first to answer my challenge, and enjoy your stay on PPCG!

– Kevin Cruijssen – 2017-08-30T06:36:06.527

You're still the only answer, so I've accepted it for now. If a shorter answer comes along the check might change again as is the standard here on PPCG. – Kevin Cruijssen – 2017-11-13T14:20:05.747