16
\$\begingroup\$

The 32-point compass is... interesting, to say the least.

image

By Denelson83 (Own work) [GFDL or CC-BY-SA-3.0], via Wikimedia Commons

Your challenge is to take a degree measure, and convert it into a direction on the 32-point compass.

Each direction is 11.25 (360 / 32) degrees farther than the previous. For example, N (north) is 0 degrees, NbE (north by east) is 11.25 degrees, NNE (north-northeast) is 22.5 degrees, etc.

As for how you're supposed to get the directions,

  • 0 degrees is N, 90 degrees is E, 180 degrees is S, and 270 degrees is W.

    • These are called cardinal directions.
  • The halfway points between the cardinal directions are simply the cardinal directions they're between concatenated. N or S always go first, and W or E always are second.

    • These are called ordinal directions.
  • The halfway points between the cardinal and ordinal directions are the directions they're between concatenated, again, with a "-" in between. Cardinal directions go first, ordinal second.

    • These are called secondary-intercardinal directions.
  • The halfway points between secondary-intercardinal directions and other directions are the other directions "by" the cardinal direction they're closest to (other than the one directly next to them, of course).

    • I have no idea what these are called :P

If all this explanation hurts your brain as much as mine, you can refer to this chart:

1 North N 2 North by east NbE 3 North-northeast NNE 4 Northeast by north NEbN 5 Northeast NE 6 Northeast by east NEbE 7 East-northeast ENE 8 East by north EbN 9 East E 10 East by south EbS 11 East-southeast ESE 12 Southeast by east SEbE 13 Southeast SE 14 Southeast by south SEbS 15 South-southeast SSE 16 South by east SbE 17 South S 18 South by west SbW 19 South-southwest SSW 20 Southwest by south SWbS 21 Southwest SW 22 Southwest by west SWbW 23 West-southwest WSW 24 West by south WbS 25 West W 26 West by north WbN 27 West-northwest WNW 28 Northwest by west NWbW 29 Northwest NW 30 Northwest by north NWbN 31 North-northwest NNW 32 North by west NbW 

Here is a more detailed chart and possibly better explanation of the points of the compass.

Your challenge is to take input in degrees, and output the full name of the compass direction it corresponds to, along with its abbreviation.

Test cases:

Input Output 0 North N 23.97 North-northeast NNE 33.7 Northeast by north NEbN 73.12 East-northeast ENE 73.13 East by north EbN 219 Southwest by south SWbS 275 West W 276 West by north WbN 287 West-northwest WNW 

All capitalization must be preserved, as in the test cases. Maximum number of decimal places is 2. All input numbers will be greater than or equal to 0, and less than 360. If a decimal point is present, there will be digits on both sides (you don't have to handle .1 or 1.).

This is , so the shortest code in bytes wins.

\$\endgroup\$
5
  • \$\begingroup\$ @WallyWest Hmm, this one allows arrays, has different capitalization, and has no "between," but I didn't notice that (possibly because of the... interesting title :P). I'll see what I can do to make it different enough... \$\endgroup\$ Commented Feb 23, 2014 at 22:33
  • 3
    \$\begingroup\$ @WallyWest There, now you have to output the abbreviation too. Along with all the other differences, that should be enough to make it a non-dup. (oh, also this one has dashes too) \$\endgroup\$ Commented Feb 23, 2014 at 22:36
  • \$\begingroup\$ @WallyWest There be no answers in "R" to your previous question (there weren't even any in "C"!) I hopes we'll see some this time, shipmates! \$\endgroup\$ Commented Feb 25, 2014 at 9:33
  • \$\begingroup\$ Would have been more fun if there were input from -360 to 360 degree (negative means anti-clockwise) and a bonus!. \$\endgroup\$ Commented Feb 25, 2014 at 17:05
  • \$\begingroup\$ For people not looking for a challenge, the easy solution is to find the possible output for which the distance is minimal from the input angle, using a lookup table angle <-> name. \$\endgroup\$ Commented Feb 25, 2020 at 17:16

5 Answers 5

7
\$\begingroup\$

Javascript 470 453 438 434 432 421 404

s=String;s.prototype.a=s.prototype.replace;var a=prompt()/11.25,a=a+0.5|0,b,k,c=a,d=c%8,c=c/8|0,e=["north","east","south","west"],f,g,h;f=e[c];g=e[(c+1)%4];h=f==e[0]|f==e[2]?f+g:g+f;b="1;1 by 2;1-C;C by 1;C;C by 2;2-C;2 by 1".split(";")[d].a(1,f).a(2,g).a("C",h);k=b.a(/north/g,"N").a(/east/g,"E").a(/south/g,"S").a(/west/g,"W").a(/by/g,"b").a(/[\s-]/g,"");b=b[0].toUpperCase()+b.slice(1);alert(b+" "+k) 

You can copy this code to your console and execute it. It prompts you for the degrees input and outputs the result with alert();

Ungolfed Javascript can be found at this fiddle: http://jsfiddle.net/AezL3/11

\$\endgroup\$
4
  • \$\begingroup\$ +1 Nice but be careful : "All capitalization must be preserved, as in the test cases." \$\endgroup\$ Commented Feb 25, 2014 at 10:15
  • \$\begingroup\$ @BenH Which test case fails the capitalization check? Thanks for this, btw. This came in handy for my web interface I'm writing. \$\endgroup\$ Commented Jan 5, 2016 at 5:56
  • \$\begingroup\$ By the way, this does die for 355 degrees to 360 degrees. The fix is easy. Just make calcPoint(32) do what 0 does, so you can do this with %32 or any similar. \$\endgroup\$ Commented Jan 5, 2016 at 6:01
  • \$\begingroup\$ @StevenLu it took me a while to figure out what you means, but this line var name = calcPoint(input % 32); does the trick \$\endgroup\$ Commented Oct 20, 2017 at 10:03
4
\$\begingroup\$

Perl, 250 236 231 188 187

Edit: Some bytes off exploiting symmetry (as I've seen in @bazzargh solution)

+Edit: And some evil tricks...

+Edit: Back to where I started (working with list, not string), and exploiting more symmetry = 1 byte off and a whole lot uglier.

$_=((@_=(1,@_=qw(1b3 1-13 13b1 13 13b3 3-13 3b1),3,map{y/1/2/r}reverse@_)),map{y/312/421/r}@_)[int<>/11.25+.5];print ucfirst s/\w/(' by ',north,south,east,west)[$&]/ger,' ',y/1-4-/NSEW/dr 

Pretty-printed:

$_=( (@_= ( 1, @_=qw(1b3 1-13 13b1 13 13b3 3-13 3b1), 3, map{y/1/2/r}reverse@_ ) ),map{y/312/421/r}@_ )[int<>/11.25+.5]; print ucfirst s/\w/(' by ',north,south,east,west)[$&]/ger,' ',y/1-4-/NSEW/dr 

5.014 required because of r modifier.

\$\endgroup\$
3
  • \$\begingroup\$ You have a typo in your code: sourth should be south (2nd statement that starts with s/b/ by ... \$\endgroup\$ Commented Feb 25, 2014 at 13:39
  • \$\begingroup\$ Those first 3 regexps can be replaced by y/NS/SN/; for 10 chars \$\endgroup\$ Commented Feb 25, 2014 at 17:51
  • \$\begingroup\$ @bazzargh, yes, and not only that ;-) \$\endgroup\$ Commented Feb 25, 2014 at 17:54
4
\$\begingroup\$

Haskell 415 372 347 330 317 304 301C

Ended up converging on a solution like @VadimR's (and the symmetry's back!). Usage: h 219 outputs "Southwest by south SWbS"

d"N"="north" d"S"="south" d"E"="east" d"W"="west" d"b"=" by " d"-"="-" d(x:y)=d[x]++d y e(x:y)=x:tail(d$x:y) k 'N'='S' k 'S'='N' k 'E'='W' k x=x r="N NbE N-NE NEbN NE NEbE E-NE EbN E EbS E-SE SEbE SE SEbS S-SE SbE " p=words$r++(map k r) g x=p!!mod(round$x/11.25)32 h x=e(g x)++(filter(/='-')$' ':g x) 

3 more chars gone, thanks @shiona.

\$\endgroup\$
2
  • \$\begingroup\$ drop 1 is same as tail. Also if I'm not mistaken you can do e l@(x:_)=x:tail$d l to shave still one more char. \$\endgroup\$ Commented Feb 25, 2014 at 17:17
  • \$\begingroup\$ can't believe I missed that. Thanks! \$\endgroup\$ Commented Feb 25, 2014 at 17:41
0
\$\begingroup\$

Python 3.8, 482 438 424 bytes

lambda h:' '.join([b(h),a(a(a(b(h)),1),d={' by ':'b','-':''})]) L=str.lower c={'North':'N','East':'E','South':'S','West':'W'} a=lambda t,l=0,d=c:[*(t:=t.replace([i,L(i)][l],d[i])for i in[*d])][-1] b=lambda h,k=[*c]:a('W|W by x|W-z|Z by w|Z|Z by x|X-z|X by w'.split('|')[int((q:=h*4/45+.5)%8)],d={'W':(W:=[*k][(v:=int(q//8)%4)]),'X':(X:=[*k][(v+1)%4]),'w':(w:=L(W)),'x':(x:=L(X)),'Z':(Z:=[W+x,X+w][W in'EastWest']),'z':L(Z)}) 

Try it online!

This is what I got after some golfing of tony goodwin's answer; posted at its own answer due to the TIO link being too long for a comment. If he chooses to update his answer to the above, I will delete this answer.

I am assuming that it's acceptable to submit a function as a solution rather than a full program. If not, here is a full program at 426 bytes.

I expect that much can still be done to shorten b.

Edit: Golfed off 44 bytes, courtesy of the majestic walrus. Still don't feel like b is done being golfed.

Edit2: Shaved off another 14 by unpacking dicts instead of using keys() and items().

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

Python, 2103 1647 1103 1034 924 889 848 bytes

Very late, I know. Thanks for the challenge, I am setting up a magnetometer for wind direction with my Pi, and wanted a 16 point of compass solution like this to feed into weather forecasting algorithms. All my code is in Python, so here is version of the javascript solution already posted in Python, but with an extra twist that you can specify either 32, 16, or 8 points of the compass at variable j, and I have changed the offset of degHead in the statement before it, depending on the number of points. I used a modified rename alogorithm (and used variables I could rename without corrupting the words!) to ensure I met the case requirements of the question.

I know this won't win, as Python is more wordy, and so am I.

Short version:

 def a(t,d,l): for i,j in d.items(): if l: i=i.lower() t=t.replace(i,j) return t def b(h,q): p=32 r=360 h=(h+(r/q/2))/(r/p) j=int(int(int(h %8)%8/(p/q))*p/q) h=int(h/8)%4 k=c.keys() u=['W','W by x','W-z','Z by w','Z','Z by x','X-z','X by w'] d={} d['W']=list(k)[h] d['w']=d['W'].lower() d['X']=list(k)[(h+1)%4] d['x']=d['X'].lower() if(d['W']=='North' or d['W']=='South'): d['Z']=d['W']+d['x'] else: d['Z']=d['X']+d['w'] d['z']=d['Z'].lower() return a(u[j],d,0) def g(n): n=a(n,c,0) n=a(n,c,1) d={'by':'b',' ':'','-':''} return a(n,d,0) def v(m): while True: try: return float(input(m)) except ValueError: print("?") c={'North':'N','East':'E','South':'S','West':'W'} while True: h=v("?") n=b(h,32) print(h,n,g(n)) 

Clear version

 import math import sys def calcPoint(degHead, points): maxPoints=32 if points not in(8,16,32): sys.exit("not a good question") degHead=(degHead+(360/points/2))/(360/maxPoints) j =int(int( int(degHead % 8)%8/(maxPoints/points))*maxPoints/points) degHead = int(degHead / 8) % 4 cardinal = ['North', 'East', 'South', 'West'] pointDesc = ['W', 'W by x', 'W-z', 'Z by w', 'Z', 'Z by x', 'X-z', 'X by w']#vars not compass points W = cardinal[degHead] X = cardinal[(degHead + 1) % 4] w=W.lower() x=X.lower() if (W == cardinal[0] or W == cardinal[2]) : Z =W + x else: Z =X + w z=Z.lower() return pointDesc[j].replace('W', W).replace('X', X).replace('w', w).replace('x', x).replace('Z', Z).replace('z', z); def getShortName(name): return name.replace('North', 'N').replace('East', 'E').replace('South', 'S').replace('West', 'W').replace('north', 'N').replace('east', 'E').replace('south', 'S').replace('west', 'W').replace('by', 'b').replace(' ', '').replace('-', '') def input_number(msg, err_msg=None): while True: try: return float(input(msg)) except ValueError: sys.exit("not a number") while True: headingCalib=input_number("input a number: ") print (headingCalib, end=' ') name = calcPoint(headingCalib,32) #degrees heading, points of compass 8,16 or 32) print (name, end=' ') shortName = getShortName(name) print (shortName) 
\$\endgroup\$
10
  • 4
    \$\begingroup\$ This answer shows no attempt at golfing and is therefore subject to deletion as not a serious contender for the challenge \$\endgroup\$ Commented Dec 4, 2019 at 1:24
  • \$\begingroup\$ Fair point - I've posted the attempt at golfing now. \$\endgroup\$ Commented Dec 4, 2019 at 21:25
  • \$\begingroup\$ Why do you have so much indentation in your submission? It doesn't seem to part of your answer, so I don't see the point of it. You can also golf this a lot more, just by removing all the extra whitespace, shortening variable names and declarations, and removing redundant variables altogether \$\endgroup\$ Commented Dec 4, 2019 at 22:23
  • \$\begingroup\$ Thanks Jo, I've updated the version again. Did I get it all? \$\endgroup\$ Commented Dec 4, 2019 at 22:52
  • \$\begingroup\$ reused a dictionary for cardinals much better. Out of ideas now. Hopefully enough to qualify? \$\endgroup\$ Commented Dec 4, 2019 at 23:41

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.