Re: Factor

Factor: the language, the theory, and the practice.

Cardinal Direction

Wednesday, November 19, 2025

#math

Cardinal direction describes points on a compass — North (N), East (E), South (S), and West (W).

In addition to those, there are also 4 intercardinal directions (Northwest or NW, NE, SW, SE), 8 secondary intercardinal directions (North-Northwest or NNW, WNW, NNE, ENE, WSW, SSW, ESE, SSE), as well as 16 tertiary intercardinal directions (Northwest-by-North or NWbN, etc.).

In fact, you can see all the points of the compass divided into 32 textual directions:

I like puzzles and sometimes those come from code golfing. I stumbled across two symmetric challenges on the Code Golf and Coding Challenges Stack Exchange:

I thought it would be a good task to code in Factor.

We start by enumerating the descriptive names for all 32 points of the compass. For convenience we use the quoted words vocabulary.

CONSTANT: directions qw{
    N NbE NNE NEbN NE NEbE ENE EbN
    E EbS ESE SEbE SE SEbS SSE SbE
    S SbW SSW SWbS SW SWbW WSW WbS
    W WbN WNW NWbW NW NWbN NNW NbW
}

Then, parsing a compass degrees to a textual name involves rounding to the nearest 32-point:

: compass>string ( compass -- str )
    360/32 / round >integer 32 mod directions nth ;

We can use some test cases from the challenges to check that it works:

{ "N"    } [ 0     compass>string ] unit-test
{ "NNE"  } [ 23.97 compass>string ] unit-test
{ "NEbN" } [ 33.7  compass>string ] unit-test
{ "ENE"  } [ 73.12 compass>string ] unit-test
{ "EbN"  } [ 73.13 compass>string ] unit-test
{ "SWbS" } [ 219   compass>string ] unit-test
{ "W"    } [ 275   compass>string ] unit-test
{ "WbN"  } [ 276   compass>string ] unit-test
{ "WNW"  } [ 287   compass>string ] unit-test

And the reverse converts the name back to compass degrees by grabbing the index and multiplying:

: string>compass ( str -- compass )
    directions index 360/32 * ;

It might not be the shortest solution, but it works and it was fun to build!