Golf a golf chart

Given a distance in meters as an integer $$\60\le d \le 260\$$, return the number of clubs that may be used according to the following arbitrary chart, where both $$\min\$$ and $$\max\$$ are inclusive:

 club           | min | max
----------------+-----+-----
Driver         | 200 | 260
3-wood         | 180 | 235
5-wood         | 170 | 210
3-iron         | 160 | 200
4-iron         | 150 | 185
5-iron         | 140 | 170
6-iron         | 130 | 160
7-iron         | 120 | 150
8-iron         | 110 | 140
9-iron         |  95 | 130
Pitching Wedge |  80 | 115
Sand Wedge     |  60 |  90


Notes

• The club names are given for information only.

• Of course, the choice of the club depends on several other parameters. For instance the Sand Wedge is designed to escape from a sand bunker. But for the purposes of this challenge, only the distance matters.

• This is undoubtedly a challenge.

Example

For $$\d=130\$$, we may choose 6-iron, 7-iron, 8-iron or 9-iron, so the expected answer is $$\4\$$.

Test cases

Input Output
60    1
79    1
80    2
93    1
105   2
110   3
116   2
129   3
130   4
200   4
201   3
235   2
260   1


Or as lists:

Input : 60, 79, 80, 93, 105, 110, 116, 129, 130, 200, 201, 235, 260
Output: 1, 1, 2, 1, 2, 3, 2, 3, 4, 4, 3, 2, 1

• Here and here are related, more complicated challenges. – Arnauld Aug 17 '20 at 12:52
• What do you do if you're less than 60 metres from the pin? Use your putter? – Steve Bennett Aug 19 '20 at 2:51
• Sorry, just making a golf joke. A bit confused why PW and SW have minimum ranges. – Steve Bennett Aug 19 '20 at 6:40
• @SteveBennett - I don't think any of them would have truly "minimum" ranges in real life – Kyle Delaney Aug 19 '20 at 18:32
• "undoubtedly a code-golf challenge" I loved that so much :) – merrybot Aug 21 '20 at 18:04

x86-16 machine code, 47 42 bytes

00000000: be14 01b3 01b1 0bad 3ad0 7205 3ad4 7701  ........:.r.:.w.
00000010: 43e2 f4c3 505a 5feb 6e73 78d2 8282 8c8c  C...PZ_.nsx.....
00000020: 9696 a0a0 aaaa b4b9 c8c8                 ..........


Listing:

BE 0114     MOV  SI, OFFSET CHART   ; SI point to distance chart
B3 01       MOV  BL, 1              ; start counter at 1
B1 0B       MOV  CL, 11             ; loop 11 clubs
SCORE_LOOP:
AD          LODSW                   ; load AL = min, AH = max
3A D0       CMP  DL, AL             ; is d less than min?
72 05       JB   DONE               ; if so, continue
3A D4       CMP  DL, AH             ; is d greater than max?
77 01       JA   DONE               ; if so, continue
43          INC  BX                 ; otherwise increment counter
DONE:
E2 F4       LOOP SCORE_LOOP         ; loop through end of chart

CHART   DB  80,90,95,235,110,115,120,210,130,130,140,140
DB  150,150,160,160,170,170,180,185,200,200


Callable function, input d in DX, output in BL.

No compression (the data is only 24 22 bytes in binary anyway) just a table comparison.

Edit: Huge props to @SE - stop firing the good guys for re-arranging the list and eliminating need to offset the d value, saving 5 bytes!

Test program runs:

Alternate version, 50 bytes

BB 0501     MOV  BX, 0501H          ; init counter to 1 in BL and
BF 556D     MOV  DI, 0556DH         ; magic number to 0x5556D in BH:DI
BE 011C     MOV  SI, OFFSET CHART   ; SI point to transition table
B1 16       MOV  CL, 22             ; loop 22 transitions
SCORE_LOOP:
AC          LODSB                   ; load AL = next transition
3A C2       CMP  AL, DL             ; is d less than?
77 0B       JA   EXIT               ; if not, end
D0 EF       SHR  BH, 1              ; cascade bit shift high word into CF
D1 DF       RCR  DI, 1              ; bit shift lsb into CF
43          INC  BX                 ; increment counter
72 02       JC   NEXT               ; if CF was a 1, continue to next
4B          DEC  BX                 ; otherwise subtract 2
4B          DEC  BX
NEXT:
E2 F0       LOOP SCORE_LOOP         ; keep looping
EXIT:
C3          RET

CHART   DB 80,91,95,110,116,120,130,131,140,141,150,151,160,161,170,171,180,186,200,201,211,236


This is heavily inspired by Jonathan Allan's answer. This uses a table of values of d where the number of clubs transitions either +1 or -1, and a corresponding binary magic number of 0x5556d where a 1 indicates a positive change and 0 indicates a negative change.

Unfortunately, this doesn't help a lot here since encoding the original table is 24 bytes versus the 22 transitions plus the 3 byte magic number so really it's larger. It was fun trying though!

• You can save 2 bytes by starting the counter at 1, and then use these 11 ranges: [(80 - 90),(95 - 235),(110 - 115),(120 - 210),(130 - 130),(140 - 140),(150 - 150),(160 - 160),(170 - 170),(180 - 185),(200 - 200)]. The values are not offset by 60 yet. – SE - stop firing the good guys Aug 20 '20 at 20:23
• @SE-stopfiringthegoodguys and it's really -5 bytes because we don't need to reduce the input by 60 to do byte compare since all values over 255 are 1 anyway. Very nice! – 640KB Aug 20 '20 at 21:11

Python 3, 71 bytes

lambda n:sum(a<=n/5<=b for a,b in zip(b'($"  ',b'4/*(%" '))  Try it online! The byte strings contain some unprintables, their escaped form is b'($" \x1e\x1c\x1a\x18\x16\x13\x10\x0c' and b'4/*(%" \x1e\x1c\x1a\x17\x12'.

Python 3, 71 bytes

lambda n:sum(b>n-a*5>-1for a,b in zip(b'($"  ',b'=8))$'))


Try it online!

Python 3.8, 90 86 bytes

lambda x:-~sum([79<x<91,94<x<236,-1<(a:=x-110)<6,9<a<101,69<a<76,a/10in{2,3,4,5,6,9}])


Try it online!

The last condition can also be written as a%10<1<a/10<7,a==90 at the same length.

Jelly, 25 bytes

“Ḳœẹ“rɓ?’ḃ5×5“ZO‘;"Ä⁸>§I‘


A full program which prints the result (or a monadic Link which returns a single-element list).

Try it online! Or see the test-suite.

How?

For any valid input, in $$\[60,260]\$$ we are able to use at least one club. For any given yardage, in $$\[61,260]\$$, we are able to use either the same, one more, or one less club than we could have done for one yard less. The code below encodes the yardages at which the number of available clubs goes up, and those at which the number of available clubs goes down and uses that to calculate the result.

“Ḳœẹ“rɓ?’ḃ5×5“ZO‘;"Ä⁸>§I‘ - Main Link: integer, Y  e.g. 129
“Ḳœẹ“rɓ?’                 - list of (two) base-250 integers = [11132965,7226564]
ḃ5               - convert to bijective base five -> [[5,3,2,2,2,2,3,3,2,5],[3,3,2,2,2,2,2,2,2,4]]
×5             - multiply by five -> [[25,15,10,10,10,10,15,15,10,25],[15,15,10,10,10,10,10,10,10,20]]
“ZO‘         - list of code-page indices = [90,79]
"       - zip with:
;        -   concatenation -> [[90,25,15,10,10,10,10,15,15,10,25],[79,15,15,10,10,10,10,10,10,10,20]]
Ä      - Cumulative values -> [[90,115,130,140,150,160,170,185,200,210,235],[79,94,109,119,129,139,149,159,169,179,199]]
⁸>    - is Y greater than (those)? -> [[1,1,0,0,0,0,0,0,0,0,0],[1,1,1,1,0,0,0,0,0,0,0]]
§   - sums -> [2,4]
I  - deltas -> [2]
‘ - increment -> [3]
- implicit print -> "3"

• Nice. How does Jelly do the compression so efficiently? – Jonah Aug 17 '20 at 14:02
• @Jonah I've added a description. I've used two (relatively) small numbers to encode the differences between (a) increases and (b) decreases in club availability rather than encode the original data. – Jonathan Allan Aug 17 '20 at 16:41
• * "differences between" -> "yardages at which we get" – Jonathan Allan Aug 17 '20 at 16:58

J, 635855 50 bytes

1#.1=(59 90+/\@,|:5*2+4#.inv 2424834 3408207)I."1]


Try it online!

-5 bytes thanks to xash

Encodes lists as numbers in base 4, reconstructs, then uses interval index I. to count how many of the ranges the input falls within.

• – xash Aug 18 '20 at 13:53
• Also, base 4 is a bit shorter. – xash Aug 18 '20 at 14:00
• Of course! Thanks xash. – Jonah Aug 18 '20 at 14:35

R, 7776 72 bytes

Edit: -4 bytes thanks to Robin Ryder

sum((d=scan()/10-9)>=c(11,9:2,.5,-1,d)&d<=c(d,14.5,12,11,9.5,8:4,2.5,0))


Try it online!

Fairly naive solution, but benefits from R's automatic vectorization and its : sequence operator.

• You can save 2 bytes with -9 instead of -6: Try it online! – Robin Ryder Aug 18 '20 at 13:56
• Also, the input is guaranteed to be between 60 and 260, so you don't need to check that is ≥60 or ≤260, gaining 2 more bytes: Try it online! – Robin Ryder Aug 18 '20 at 14:03
• Thanks (twice). I especially like the second trick: I'd never have thought of using d itself in the upper/lower bound vectors to save characters! Really nice! – Dominic van Essen Aug 18 '20 at 14:12

Python 3, 180 155 153 97 88 bytes

lambda n:sum(a<=chr(n)<b for a,b in zip('<P_nx‚Œ– ª´È','[tƒ—¡«ºÉÓìą'))


Try it online!

• 155 bytes using lambda tio.run/… – Manish Kundu Aug 17 '20 at 13:59
• Change these values into byte values, to save 33 bytes. Try it online! – user96495 Aug 17 '20 at 14:24
• 111 bytes – user96495 Aug 17 '20 at 14:27
• 97 bytes – Mukundan314 Aug 17 '20 at 14:32
• 88 bytes by doing the in range manually. – ovs Aug 18 '20 at 17:01

K (oK), 85 82 bytes

Solution:

{+/z=x|y&z}.(-3 -1 .5 2 3 4 5 6 7 8 9 11;0 2.5 4 5 6 7 8 9.5 11 12 14.5 17),-9+.1*


Try it online!

Explanation:

Decidedly naive; highly likely that this is a bad approach. Although nice golf by ngn to simplify the comparison logic!.

{+/z=x|y&z}.(-3 -1 .5 2 3 4 5 6 7 8 9 11;0 2.5 4 5 6 7 8 9.5 11 12 14.5 17),-9+.1* / the solution
.1* / multiply input by 0.1
-9+    / subtract 9 from input
,        / append to
(-3 -1 .5 2 3 4 5 6 7 8 9 11;0 2.5 4 5 6 7 8 9.5 11 12 14.5 17)        / club stats
{         }.                                                                       / call lambda with multiple args
y&z                                                                         / min of input and min distance
x|                                                                            / max compared to max distance
z=                                                                              / is input the same?
+/                                                                                / sum up


Extra:

• -3 bytes thanks to ngn
• ~(z<x)|z>y -> z=x|y&z – ngn Aug 24 '20 at 15:05
• Nice. Will update when I'm on my laptop – streetster Aug 24 '20 at 18:15

Python 3, 105 bytes

lambda n,a=[1],b=[2],c=[3],d=[4]:(a*20+b*11+a*4+b*15+c*6+b*4+c+(c*9+d)*6+d*5+c*14+d+c*10+b*25+a*25)[n-60]


Try it online!

Explanation: A simple bruteforce to store the list of answers and print the required index.

Charcoal, 44 bytes

ＮθＩΣＥ¹²⁻›θ⁺⁵⁹×⁵Σ…”)⊞⊟‹Ｇ↔”ι›θ⁺⁹⁰×⁵Σ…”)⊟+.Ｄ↥”ι


Try it online! Link is to verbose version of code. Port of @JonathanAllen's answer. Explanation:

Ｎθ


Input d.

ＩΣＥ¹²⁻


Map over the 12 clubs and print the sum of the results cast to string of taking the differences between...

›θ⁺⁵⁹×⁵Σ…”)⊞⊟‹Ｇ↔”ι


... d compared with 59 added to 5 times the digital sum of prefix of the compressed string 43322222224, and...

›θ⁺⁹⁰×⁵Σ…”)⊟+.Ｄ↥”ι


... d compared with 90 added to 5 times the digital sum of prefix of the compressed string 53222233235.

ＮθＩΣＥＩ⪪”)¶∧!✂η}⌊⍘⪫⪫⊕#<e⌊W[qＹ9¤…”²∧›θ⁻×⁵ι﹪κ²⊖⊗﹪κ²


Try it online! Link is to verbose version of code. Explanation: The ending and starting distances of the 12 clubs are split from a compressed string of integers from 12 to 52 which are multiplied by 5. d is compared against them all, scoring 1 for greater or equal distances in odd positions and -1 for greater distances in even positions and the final total is printed.

Python 3, 62 60 bytes

lambda d:sum(b//25<=b%25+23-d/5<=7for b in b'BUNSWYQ+-}')+1


Try it online!

There's an invisible (on Stack Exchange) \x18 character at the end of the string.

• Nicely done! As I was expecting, using only one character per club is a good strategy for 'standard' languages. – Arnauld Aug 19 '20 at 6:38

I think it's possible to achieve more aggressive compression ratios in most non-esolangs.

As a little incentive, here are my own scores in Python and Node.

I will unveil both code snippets below as soon as a shorter or equally long answer is posted (or updated) in either language, or at 2PM UTC on Friday August 21, 2020 in the unlikely event that no such answer is published by then.

EDIT (2020-08-19): Congratulations to @flornquake for being the first to post a Python answer below 70 bytes, using an idea similar to mine but pushing it a step further for a total of 62 bytes!

Python 3.8 (pre-release), 70 bytes

lambda d,n=12:sum((n:=n+x//8-4)<=d/5<=n+x%8+6for x in b' A980001225F')


Try it online!

JavaScript (Node.js), 74 bytes

d=>Buffer(' A980001225F').map(x=>t-=d/5<(n+=x-32>>3)|d/5>n+x%8+6,n=t=12)|t


Try it online!

Perl 5, 70 bytes

The first byte in the string is added in \x## notation for easy testing, but is a literal byte.

$_=grep"@F">=($k+=(ord>>4)*5)&"@F"<=$k+(15&ord)*5,"\xc6G76&&&'((+L"=~/./g  Stores the details for each club (divided by 5) as a byte where the first four bits are the difference from start of the previous range and the start of this one, and the second four bits are the difference between the start of this range and the end. start end => / 5 => binary # -------------------------------------------- 60 90 => 12 6 => 11000110 # counter starts at 0 80 115 => 4 7 => 00100100 # counter is 12 from before so we only need an extra 4 95 130 => 3 7 => 00110111 110 140 => 3 6 => 00110110  Try it online! C (gcc), 83 75 bytes (not all characters show correctly on stack exchange, but are correct in TIO) *i;c;f(d){c=0;for(i=L"ÈĄ´ëªÒ È–¹Œª‚ x–nŒ_‚Ps<Z";*i;c+=d/ *i++&*i++/d);d=c;}  Try it online! Io, 89 bytes method(a,"  \"$("asList select(i,v,v at(0)<=a/5and a/5<=" \"%(*/4"at(i))size)


Try it online!

Pyth, 36 bytes

s/RC-QTrV"2FUdnx‚Œ– ª¾""Qjyƒ—¡°¿Éâû


Try it online!

Explanation

              "...   # String literal with end of ranges minus 10 as characters
"..."       # String literal with start of ranges minus 10 as characters
rV            # Generate the ranges
/R                  # Count occurrences of
C-QT              # input minus 10 converted to a characters (based on ascii value)
# in each of the ranges
s                    # sum


JavaScript (V8), 142 132 117 113 bytes

-10 bytes: applied -9 to ranges and input after division rather than only dividing by 10 (inspired by other answers, understood why it was worth it after observing my range/10 numbers)

-15 bytes thanks to Arnauld's improvements

-5 bytes thanks to Shaggy's further improvements

d=>[11,17,9,14.5,8,12,7,11,6,9.5,...'58473625',.5,4,-1,2.5,-3,t=0].map((e,i,r)=>t+=++i%2&d>=e&d<=r[i],d=d/10-9)|t


Try it online!

Pretty naive solution but I wasn't comfortable attempting more complex methods used in other answers (not to mention I'm not sure if they're even possible/worth golfing with in JS!). I'll happily take advice/improvements, though.

Unminified & explained (slightly outdated but still explains the overall process):

f = (distance) => {
// divide input by 10 and subtract 9 since the hardcoded ranges are shorter when those operations are done.
distance = distance / 10 - 9

// hardcoded ranges divided by 10 then subtracted 9 to save bytes (probably can be done better).
// Will be used in pairs, only processing even indexes and using i & i+1
//ranges = [20,26,18,23.5,17,21,16,20,15,18.5,14,17,13,16,12,15,11,14,9.5,13,8,11.5,6,9] // /10
//ranges = [14,20,12,17.5,11,15,10,14,9,12.5,8,11,7,10,6,9,5,8,3.5,7,2,5.5,0,3] // /10 -6
ranges = [11,17,9,14.5,8,12,7,11,6,9.5,5,8,4,7,3,6,2,5,0.5,4,-1,2.5,-3,0] // /10 -9 (winner! inspired by other answers)

// .map used as .reduce
ranges.map((e, i)=> { // e: current element, i: current index
totalValidClubs += ( // increment total 'valid' counter if within range
i%2 == 1 ? 0 : // skip odd indexes, will use i & i+1 on even indexes only
distance>=e && distance<=ranges[i+1] ? 1 : 0) // if even index and distance is between ranges[i] & [i+1] (inclusive), increment by 1.
}, totalValidClubs=0); // initialize valid club counter as 0
}

• Some tips: you can remove ==1 and ?1:0; you can get rid of the other ternary operator by doing ++i%2&d>=e&d<=r[i], where i is now initialized outside the callback function; 0.5 can be written as .5; it is worth converting a sequence of 7 or more digits into a split string. This gives 117 bytes. – Arnauld Aug 18 '20 at 8:17
• 113 bytes, building on @Arnauld's suggestions. – Shaggy Aug 18 '20 at 12:09
• Thanks for the suggestions! Very interesting improvements, will have to keep these tricks in mind for future challenges. Shaggy, I don't fully understand the initialization of t in your solution, how it's done in the array. Is that simply creating a global variable by doing that assignment within a global statement (the array init)? That's what it looks like from testing, but the syntax is so unfamiliar I feel it might be doing more than meets the eye. It's also added to the array as idx=23, val=0, but that's not a problem for this solution since odd indexes are ignored and it's at the end. – Matsyir Aug 18 '20 at 13:35

05AB1E, 38 36 bytes

38 Bytes

"ÈĄ´ëªÒ È–¹Œª‚ x–nŒ_‚Ps<Z"Ç2ôε.SOÄ2‹}O


I'm bad at compressing :( The best I could have think of is converting each number to an ASCII character.

Explanation:

"ÈĄ´ëªÒ È–¹Œª‚ x–nŒ_‚Ps<Z"Ç2ôε.SOÄ2‹}O
"ÈĄ´ëªÒ È–¹Œª‚ x–nŒ_‚Ps<Z"                Ranges of clubs as ASCII chars
Ç               Convert to values
2ô             Split into chunks of two
ε       }    Map on pairs
.S          -1 if lower than input, 1 if greater, 0 it equal
O         Sum the result of the pair
Ä        Absolute value
2‹      Is it lower than 2? (The only cases the absolute value is 2 are when the input is out of range)
O   Now we have list of 0 and 1 for each range. Sum it up :)


Try it online!

36 Bytes (thanks to @ovs)

"ÈĄ´ëªÒ È¹ª xn_Ps<Z"Ç2ôε-P(d}O


Using -P(d inside map, which will subtract the pair with the input, product it (out of range values will be positive), then apply negative with ( and check if the value is non-negative using d.

Try it online!

• I think -P(d should work inside the map. If the value is outside of a range, -P will be positive. – ovs Aug 18 '20 at 6:46
• That's another great option! Added it. – SomoKRoceS Aug 18 '20 at 16:29
• I'm afraid this is more than 36 bytes. A lot of those characters in the string aren't in 05AB1E's codepage, which means you aren't allowed to count each character as 1 byte each and should use another encoding (for example UTF-8, which would be 61 bytes). Changing "ÈĄ´ëªÒ È¹ª xn_Ps<Z"Ç to •L´½‚ã5·“yQΣÙoy₃@Ü#ć!dÖZ•Ƶ&в60+ would fix that issue, making it 40 (05AB1E) bytes. After that, ε-P(d}O can be golfed to €Ÿ˜QO, ending at 38 bytes. – Kevin Cruijssen Aug 20 '20 at 10:07
• Also, here is a 05AB1E tip I wrote on how to compress. The •L´½‚ã5·“yQΣÙoy₃@Ü#ć!dÖZ•, Ƶ&, and •L´½‚ã5·“yQΣÙoy₃@Ü#ć!dÖZ•Ƶ&в are generated with the compression programs at the How to compress large integers? and How to compress integer lists? sections. – Kevin Cruijssen Aug 20 '20 at 10:09
• hmm I get your point, seems fair to me. I'll change it with your compressing. – SomoKRoceS Aug 20 '20 at 13:26

><>, 51 bytes

"ÇÉ³º©«Ÿ¡•—‹ƒwÓmt^ìO["1&{:})\${:}(*&+&55*0l3)?.&n;


(contains 7 unprintables)

Try it online!

Since there's at least 1 club for every input, one may re-arrange the ranges to get rid of one range, which has the added benefit of removing the "260" part which is just barely outside the range of a byte.

Desmos, 106 bytes:

f(d)=total(\left\{join([18...11],[9.5,8,6,20])*10<=d<=[47,42,40,37,34,32,30,28,26,23,18,52]*5:1,0\right\})


View graph online

Delete f(d)= and subtract 5 bytes if you're cool with using a slider as input.

• I don't think you need the \left and \right – user Nov 5 '20 at 23:04

APL, 5245 50 bytes:

{+⌿1=(↓12 2⍴⎕ucs'Èą´ìªÓ Éº«¡xn_Pt<[')∘.⍸⍵}


Try it online!

• I fixed the formatting for you. But the scoring for code golf should be in bytes (not characters), and it looks like the current solution doesn't work. – Bubbler Aug 21 '20 at 7:26
• Thanks - but what do you mean with "doesn't work"? The TIO-Link shows (I think) the correct solution - or did I miss anything? – MBaas Aug 21 '20 at 11:14
• If you use this template then it is easier to discern your character/byte count. – Adám Aug 24 '20 at 9:58
• What encoding are you using to justify that each character here is counted as a single byte? – Adám Aug 24 '20 at 10:00
• Full program at 46, while allowing you to claim the "Classic" character set. – Adám Aug 24 '20 at 10:15