Box the compass

From Rosetta Code
Task
Box the compass
You are encouraged to solve this task according to the task description, using any language you may know.

Avast me hearties!

There be many a land lubber that knows naught of the pirate ways and gives direction by degree! They know not how to box the compass!

Task description

  1. Create a function that takes a heading in degrees and returns the correct 32-point compass heading.
  2. Use the function to print and display a table of Index, Compass point, and Degree; rather like the corresponding columns from, the first table of the wikipedia article, but use only the following 33 headings as input:
[0.0, 16.87, 16.88, 33.75, 50.62, 50.63, 67.5, 84.37, 84.38, 101.25, 118.12, 118.13, 135.0, 151.87, 151.88, 168.75, 185.62, 185.63, 202.5, 219.37, 219.38, 236.25, 253.12, 253.13, 270.0, 286.87, 286.88, 303.75, 320.62, 320.63, 337.5, 354.37, 354.38]. (They should give the same order of points but are spread throughout the ranges of acceptance).

Note

  • The headings and indices can be calculated from this pseudocode:
for i in 0..32 inclusive:
    heading = i * 11.25
    case i %3:
      if 1: heading += 5.62; break
      if 2: heading -= 5.62; break
    end
    index = ( i mod 32) + 1

ALGOL 68

Works with: ALGOL 68 version Revision 1 - no extensions to language used.
Works with: ALGOL 68G version Any - tested with release 1.18.0-9h.tiny.

<lang algol68>#!/usr/local/bin/a68g --script #

[]STRING

 long  by nesw = (" by ", "North", "East", "South", "West"),
 short by nesw = ("b", "N", "E", "S", "W");

MODE MINUTES = REAL; # minutes type # INT last minute=360*60; INT point width=last minute OVER 32;

PROC direction name = (REAL direction in minutes, []STRING locale directions)STRING: (

 STRING by = locale directions[1];
 []STRING nesw = locale directions[@-1];
 PRIO MASK = 7; # same PRIOrity as * and / #
 OP MASK = (INT n, BITS lower)INT: ABS (BIN n AND NOT lower),
    DECAP = (STRING s)STRING: IF UPB s > 1 THEN REPR (ABS s[1]-ABS "A"+ABS "a")+s[2:] ELSE s FI;
 PROC point name = (INT in point)STRING: (
   INT point = in point MOD 32 # 32 points of a compass #;
   IF point MOD 8 = 0 THEN
  1. name the principle point: eg. N, E, S or W #
     nesw[point OVER 8]
   ELIF point MOD 4 = 0 THEN
  1. name the half: eg. NE, SE, SW or NW #
     point name((point+8)MASK 2r1111)+DECAP point name(point MASK 2r1111 + 8)
   ELIF point MOD 2 = 0 THEN
  1. name the quarter: eg. N-NE, E-NE, E-SE, S-SE, S-SW, W-SW, W-NW or N-NW #
     point name((point+4)MASK 2r111)+"-"+point name(point MASK 2r111 + 4)
   ELSE # Name the sixteenth point: #
  1. eg. NbE,NEbN,NEbE,EbN,EbS,SEbE,SEbS,SbE,SbW,SWbS,SWbW,WbS,WbN,NWbW,NWbN,NbW #
     INT opp point = point OVER 8 + ABS NOT ODD (point OVER 2);
     point name((point+2)MASK 2r11)+ by +nesw(opp point MOD 4)
   FI
 );
 point name(ROUND(direction in minutes/point width))

);

PROC traditional name = (MINUTES minutes)STRING: (

 INT degrees = ROUND(minutes / 60);
 degrees=0  |"Tramontana"          |:
 degrees=45 |"Greco or Bora"       |:
 degrees=90 |"Levante"             |:
 degrees=135|"Sirocco"             |:
 degrees=180|"Ostro"               |:
 degrees=225|"Libeccio"            |:
 degrees=270|"Poniente or Zephyrus"|:
 degrees=315|"Mistral"             |:
 degrees=360|"Tramontana"          |""

);

  1. First generate the test set results #

test:(

 printf($"Test:"l$);
 FOR i FROM 0 TO 32 DO
   REAL heading = i * 11.25 +
     CASE i MOD 3 IN
       +5.62,
       -5.62
       OUT 0
     ESAC;
   INT index = ( i MOD 32) + 1;
   printf(($zd" ", g23k, zzd.zzl$, index , direction name(heading*60, long by nesw), heading))
 OD

);

table:(

 OP OVER = (REAL r, INT base)INT: ENTIER ABS r OVER  base,
    MOD = (REAL r, INT base)REAL: ( INT i = ENTIER r; i MOD base + r - i);
 printf(
   $l"Table:"l
   " #|Compass point"22k"|Abbr|Traditional wind point| Lowest°′ | Middle°′ | Highest°′"l$
 );
 OP DEGMIN = (REAL minutes)STRUCT(INT d, REAL m): (minutes MOD last minute OVER 60, minutes MOD 60);
 FOR point FROM 1 TO 32 DO
   REAL centre = (point-1) * point width;
   REAL from =  centre - point width/2,
        to   =  centre + point width/2-1/120;
   printf((
           $g(-2)"|"$, point,
           $g$, direction name(centre, long by nesw),
           $22k"|"g$, direction name(centre, short by nesw),
           $27k"|"g$, traditional name(centre),
           $50k$, $"|"g(-3)"°", dd.dd"′"$, DEGMIN from, DEGMIN centre, DEGMIN to,
           $l$
         ))
 OD

)</lang> Output:

Test:
 1 North                0.00
 2 North by East       16.87
 3 North-Northeast     16.88
 4 Northeast by North  33.75
 5 Northeast           50.62
 6 Northeast by East   50.63
 7 East-Northeast      67.50
 8 East by North       84.37
 9 East                84.38
10 East by South      101.25
11 East-Southeast     118.12
12 Southeast by East  118.13
13 Southeast          135.00
14 Southeast by South 151.87
15 South-Southeast    151.88
16 South by East      168.75
17 South              185.62
18 South by West      185.63
19 South-Southwest    202.50
20 Southwest by South 219.37
21 Southwest          219.38
22 Southwest by West  236.25
23 West-Southwest     253.12
24 West by South      253.13
25 West               270.00
26 West by North      286.87
27 West-Northwest     286.88
28 Northwest by West  303.75
29 Northwest          320.62
30 Northwest by North 320.63
31 North-Northwest    337.50
32 North by West      354.37
 1 North              354.38

Table:
 #|Compass point     |Abbr|Traditional wind point| Lowest°′ | Middle°′ | Highest°′
 1|North             |N   |Tramontana            |354°22.50′|  0°00.00′|  5°37.49′
 2|North by East     |NbE |                      |  5°37.50′| 11°15.00′| 16°52.49′
 3|North-Northeast   |N-NE|                      | 16°52.50′| 22°30.00′| 28°07.49′
 4|Northeast by North|NEbN|                      | 28°07.50′| 33°45.00′| 39°22.49′
 5|Northeast         |NE  |Greco or Bora         | 39°22.50′| 45°00.00′| 50°37.49′
 6|Northeast by East |NEbE|                      | 50°37.50′| 56°15.00′| 61°52.49′
 7|East-Northeast    |E-NE|                      | 61°52.50′| 67°30.00′| 73°07.49′
 8|East by North     |EbN |                      | 73°07.50′| 78°45.00′| 84°22.49′
 9|East              |E   |Levante               | 84°22.50′| 90°00.00′| 95°37.49′
10|East by South     |EbS |                      | 95°37.50′|101°15.00′|106°52.49′
11|East-Southeast    |E-SE|                      |106°52.50′|112°30.00′|118°07.49′
12|Southeast by East |SEbE|                      |118°07.50′|123°45.00′|129°22.49′
13|Southeast         |SE  |Sirocco               |129°22.50′|135°00.00′|140°37.49′
14|Southeast by South|SEbS|                      |140°37.50′|146°15.00′|151°52.49′
15|South-Southeast   |S-SE|                      |151°52.50′|157°30.00′|163°07.49′
16|South by East     |SbE |                      |163°07.50′|168°45.00′|174°22.49′
17|South             |S   |Ostro                 |174°22.50′|180°00.00′|185°37.49′
18|South by West     |SbW |                      |185°37.50′|191°15.00′|196°52.49′
19|South-Southwest   |S-SW|                      |196°52.50′|202°30.00′|208°07.49′
20|Southwest by South|SWbS|                      |208°07.50′|213°45.00′|219°22.49′
21|Southwest         |SW  |Libeccio              |219°22.50′|225°00.00′|230°37.49′
22|Southwest by West |SWbW|                      |230°37.50′|236°15.00′|241°52.49′
23|West-Southwest    |W-SW|                      |241°52.50′|247°30.00′|253°07.49′
24|West by South     |WbS |                      |253°07.50′|258°45.00′|264°22.49′
25|West              |W   |Poniente or Zephyrus  |264°22.50′|270°00.00′|275°37.49′
26|West by North     |WbN |                      |275°37.50′|281°15.00′|286°52.49′
27|West-Northwest    |W-NW|                      |286°52.50′|292°30.00′|298°07.49′
28|Northwest by West |NWbW|                      |298°07.50′|303°45.00′|309°22.49′
29|Northwest         |NW  |Mistral               |309°22.50′|315°00.00′|320°37.49′
30|Northwest by North|NWbN|                      |320°37.50′|326°15.00′|331°52.49′
31|North-Northwest   |N-NW|                      |331°52.50′|337°30.00′|343°07.49′
32|North by West     |NbW |                      |343°07.50′|348°45.00′|354°22.49′

C

<lang C>int main() {

       int i, j;
       double degrees[] = { 0.0, 16.87, 16.88, 33.75, 50.62, 50.63, 67.5,
               84.37, 84.38, 101.25, 118.12, 118.13, 135.0, 151.87, 151.88,
               168.75, 185.62, 185.63, 202.5, 219.37, 219.38, 236.25, 253.12,
               253.13, 270.0, 286.87, 286.88, 303.75, 320.62, 320.63, 337.5,
               354.37, 354.38 };
       char * names =  "North                 "
                       "North by east         "
                       "North-northeast       "
                       "Northeast by north    "
                       "Northeast             "
                       "Northeast by east     "
                       "East-northeast        "
                       "East by north         "
                       "East                  "
                       "East by south         "
                       "East-southeast        "
                       "Southeast by east     "
                       "Southeast             "
                       "Southeast by south    "
                       "South-southeast       "
                       "South by east         "
                       "South                 "
                       "South by west         "
                       "South-southwest       "
                       "Southwest by south    "
                       "Southwest             "
                       "Southwest by west     "
                       "West-southwest        "
                       "West by south         "
                       "West                  "
                       "West by north         "
                       "West-northwest        "
                       "Northwest by west     "
                       "Northwest             "
                       "Northwest by north    "
                       "North-northwest       "
                       "North by west         "
                       "North                 ";
       for (i = 0; i < 33; i++) {
               j = .5 + degrees[i] * 32 / 360;
               printf("%2d  %.22s  %6.2f\n", i + 1, names + (j % 32) * 22,
                       degrees[i]);
       }
       return 0;

}</lang>Output:<lang>

1  North                     0.00
2  North by east            16.87
3  North-northeast          16.88
4  Northeast by north       33.75
5  Northeast                50.62
6  Northeast by east        50.63
7  East-northeast           67.50
8  East by north            84.37
9  East                     84.38

10 East by south 101.25 11 East-southeast 118.12 12 Southeast by east 118.13 13 Southeast 135.00 14 Southeast by south 151.87 15 South-southeast 151.88 16 South by east 168.75 17 South 185.62 18 South by west 185.63 19 South-southwest 202.50 20 Southwest by south 219.37 21 Southwest 219.38 22 Southwest by west 236.25 23 West-southwest 253.12 24 West by south 253.13 25 West 270.00 26 West by north 286.87 27 West-northwest 286.88 28 Northwest by west 303.75 29 Northwest 320.62 30 Northwest by north 320.63 31 North-northwest 337.50 32 North by west 354.37 33 North 354.38 </lang>

C++

Using the Boost libraries

Library: Boost

<lang cpp>#include <string>

  1. include <boost/array.hpp>
  2. include <boost/assign/list_of.hpp>
  3. include <boost/format.hpp>
  4. include <boost/foreach.hpp>
  5. include <iostream>
  6. include <math.h>

using std::string; using namespace boost::assign;

int get_Index(float angle) {

  return static_cast<int>(floor(angle / 11.25 +0.5 )) % 32 + 1;

}

string get_Abbr_From_Index(int i) {

   static boost::array<std::string, 32> points(list_of
           ("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"));
   return points[i-1];

}

string Build_Name_From_Abbreviation(string a) {

   string retval;
   for (int i = 0; i < a.size(); ++i){
       if ((1 == i) && (a[i] != 'b') && (a.size() == 3)) retval += "-";
       switch (a[i]){
           case 'N' : retval += "north"; break; 
           case 'S' : retval += "south"; break; 
           case 'E' : retval += "east";  break; 
           case 'W' : retval += "west";  break; 
           case 'b' : retval += " by ";  break;
       }
   }
   retval[0] = toupper(retval[0]);
   return retval;

}

int main() {

   boost::array<float,33> headings(list_of
           (0.0)(16.87)(16.88)(33.75)(50.62)(50.63)(67.5)(84.37)(84.38)(101.25)
           (118.12)(118.13)(135.0)(151.87)(151.88)(168.75)(185.62)(185.63)(202.5)
           (219.37)(219.38)(236.25)(253.12)(253.13)(270.0)(286.87)(286.88)(303.75)
           (320.62)(320.63)(337.5)(354.37)(354.38));
   int i;
   boost::format f("%1$4d %2$-20s %3$_7.2f");
   BOOST_FOREACH(float a, headings)
   {
       i = get_Index(a);
       std::cout << f % i %  Build_Name_From_Abbreviation(get_Abbr_From_Index(i)) % a << std::endl;
   }
   return 0;

}</lang> Output:

   1 North                   0.00
   2 North by east          16.87
   3 North-northeast        16.88
   4 Northeast by north     33.75
   5 Northeast              50.62
   6 Northeast by east      50.63
   7 East-northeast         67.50
   8 East by north          84.37
   9 East                   84.38
  10 East by south         101.25
  11 East-southeast        118.12
  12 Southeast by east     118.13
  13 Southeast             135.00
  14 Southeast by south    151.87
  15 South-southeast       151.88
  16 South by east         168.75
  17 South                 185.62
  18 South by west         185.63
  19 South-southwest       202.50
  20 Southwest by south    219.37
  21 Southwest             219.38
  22 Southwest by west     236.25
  23 West-southwest        253.12
  24 West by south         253.13
  25 West                  270.00
  26 West by north         286.87
  27 West-northwest        286.88
  28 Northwest by west     303.75
  29 Northwest             320.62
  30 Northwest by north    320.63
  31 North-northwest       337.50
  32 North by west         354.37
   1 North                 354.38

Clojure

Translation of: Tcl

<lang lisp>(ns boxing-the-compass

 (:use [clojure.string :only [capitalize]]))

(def headings

    (for [i (range 0 (inc 32))]
      (let [heading (* i 11.25)]

(case (mod i 3) 1 (+ heading 5.62) 2 (- heading 5.62) heading))))

(defn angle2compass

 [angle]
 (let [dirs ["N" "NbE" "N-NE" "NEbN" "NE" "NEbE" "E-NE" "EbN"

"E" "EbS" "E-SE" "SEbE" "SE" "SEbS" "S-SE" "SbE" "S" "SbW" "S-SW" "SWbS" "SW" "SWbW" "W-SW" "WbS" "W" "WbN" "W-NW" "NWbW" "NW" "NWbN" "N-NW" "NbW"] unpack {\N "north" \E "east" \W "west" \S "south" \b " by " \- "-"} sep (/ 360 (count dirs)) dir (int (/ (mod (+ angle (/ sep 2)) 360) sep))]

   (capitalize (apply str (map unpack (dirs dir))))))

(print

(apply str (map-indexed #(format "%2s %-18s %7.2f\n"

(inc (mod %1 32)) (angle2compass %2) %2) headings)))</lang> Output:

 1 North                 0.00
 2 North by east        16.87
 3 North-northeast      16.88
 4 Northeast by north   33.75
 5 Northeast            50.62
 6 Northeast by east    50.63
 7 East-northeast       67.50
 8 East by north        84.37
 9 East                 84.38
10 East by south       101.25
11 East-southeast      118.12
12 Southeast by east   118.13
13 Southeast           135.00
14 Southeast by south  151.87
15 South-southeast     151.88
16 South by east       168.75
17 South               185.62
18 South by west       185.63
19 South-southwest     202.50
20 Southwest by south  219.37
21 Southwest           219.38
22 Southwest by west   236.25
23 West-southwest      253.12
24 West by south       253.13
25 West                270.00
26 West by north       286.87
27 West-northwest      286.88
28 Northwest by west   303.75
29 Northwest           320.62
30 Northwest by north  320.63
31 North-northwest     337.50
32 North by west       354.37
 1 North               354.38

D

Translation of: Java

<lang d>import std.stdio, std.string, std.math, std.array;

struct boxTheCompass {

   static string[32] points;
   static this() {
       enum cardinal = ["north", "east", "south", "west"];
       enum desc = ["1", "1 by 2", "1-C", "C by 1", "C", "C by 2",
                    "2-C", "2 by 1"];
       foreach (i; 0 .. 4) {
           auto s1 = cardinal[i];
           auto s2 = cardinal[(i + 1) % 4];
           auto sc = (s1 == "north" || s1 == "south") ?
                       (s1 ~ s2) : (s2 ~ s1);
           foreach (j; 0 .. 8)
               points[i * 8 + j] = desc[j].replace("1", s1).
                                   replace("2", s2).replace("C",sc);
       }
   }
   static string opCall(double degrees) {
       double testD = (degrees / 11.25) + 0.5;
       return capitalize(points[cast(int)floor(testD % 32)]);
   }

}

void main() {

   foreach (i; 0 .. 33) {
       double heading = i * 11.25 + [0, 5.62, -5.62][i % 3];
       writefln("%s\t%18s\t%s", i % 32 + 1,
                boxTheCompass(heading), heading);
   }

}</lang> Output:

1                North  0
2        North by east  16.87
3      North-northeast  16.88
4   Northeast by north  33.75
5            Northeast  50.62
6    Northeast by east  50.63
7       East-northeast  67.5
8        East by north  84.37
9                 East  84.38
10       East by south  101.25
11      East-southeast  118.12
12   Southeast by east  118.13
13           Southeast  135
14  Southeast by south  151.87
15     South-southeast  151.88
16       South by east  168.75
17               South  185.62
18       South by west  185.63
19     South-southwest  202.5
20  Southwest by south  219.37
21           Southwest  219.38
22   Southwest by west  236.25
23      West-southwest  253.12
24       West by south  253.13
25                West  270
26       West by north  286.87
27      West-northwest  286.88
28   Northwest by west  303.75
29           Northwest  320.62
30  Northwest by north  320.63
31     North-northwest  337.5
32       North by west  354.37
1                North  354.38

Euphoria

<lang euphoria>constant names = {"North","North by east","North-northeast","Northeast by north",

   "Northeast","Northeast by east","East-northeast","East by north","East",
   "East by south","East-southeast","Southeast by east","Southeast","Southeast by south",
   "South-southeast","South by east","South","South by west","South-southwest",
   "Southwest by south","Southwest","Southwest by west","West-southwest",
   "West by south","West","West by north","West-northwest","Northwest by west",
   "Northwest","Northwest by north","North-northwest","North by west"}

function deg2ind(atom degree)

   return remainder(floor(degree*32/360+.5),32)+1

end function

sequence degrees degrees = {} for i = 0 to 32 do

   degrees &= i*11.25 + 5.62*(remainder(i+1,3)-1)

end for

integer j for i = 1 to length(degrees) do

   j = deg2ind(degrees[i])
   printf(1, "%6.2f  %2d  %-22s\n", {degrees[i], j, names[j]})

end for</lang>

Output:

  0.00   1  North
 16.87   2  North by east
 16.88   3  North-northeast
 33.75   4  Northeast by north
 50.62   5  Northeast
 50.63   6  Northeast by east
 67.50   7  East-northeast
 84.37   8  East by north
 84.38   9  East
101.25  10  East by south
118.12  11  East-southeast
118.13  12  Southeast by east
135.00  13  Southeast
151.87  14  Southeast by south
151.88  15  South-southeast
168.75  16  South by east
185.62  17  South
185.63  18  South by west
202.50  19  South-southwest
219.37  20  Southwest by south
219.38  21  Southwest
236.25  22  Southwest by west
253.12  23  West-southwest
253.13  24  West by south
270.00  25  West
286.87  26  West by north
286.88  27  West-northwest
303.75  28  Northwest by west
320.62  29  Northwest
320.63  30  Northwest by north
337.50  31  North-northwest
354.37  32  North by west
354.38   1  North

Fortran

Works with: Fortran version 90 and later

<lang fortran>Program Compass

 implicit none
 integer :: i, ind
 real :: heading
 do i = 0, 32
   heading = i * 11.25
   if (mod(i, 3) == 1) then
     heading = heading + 5.62
   else if (mod(i, 3) == 2) then
           heading = heading - 5.62
   end if
   ind = mod(i, 32) + 1
   write(*, "(i2, a20, f8.2)") ind, compasspoint(heading), heading
 end do

contains

function compasspoint(h)

 character(18) :: compasspoint
 character(18) :: points(32) = (/ "North             ", "North by east     ", "North-northeast   ", & 
            "Northeast by north", "Northeast         ", "Northeast by east ", "East-northeast    ", &
            "East by north     ", "East              ", "East by south     ", "East-southeast    ", &
            "Southeast by east ", "Southeast         ", "Southeast by south", "South-southeast   ", &
            "South by east     ", "South             ", "South by west     ", "South-southwest   ", &
            "Southwest by south", "Southwest         ", "Southwest by west ", "West-southwest    ", &
            "West by south     ", "West              ", "West by north     ", "West-northwest    ", &
            "Northwest by west ", "Northwest         ", "Northwest by north", "North-northwest   ", &
            "North by west     "  /)  
 real, intent(in) :: h
 real :: x
 x = h / 11.25 + 1.5
 if (x >= 33.0) x = x - 32.0
 compasspoint = points(int(x))

end function compasspoint end program Compass</lang> Output:

 1  North                 0.00
 2  North by east        16.87
 3  North-northeast      16.88
 4  Northeast by north   33.75
 5  Northeast            50.62
 6  Northeast by east    50.63
 7  East-northeast       67.50
 8  East by north        84.37
 9  East                 84.38
10  East by south       101.25
11  East-southeast      118.12
12  Southeast by east   118.13
13  Southeast           135.00
14  Southeast by south  151.87
15  South-southeast     151.88
16  South by east       168.75
17  South               185.62
18  South by west       185.63
19  South-southwest     202.50
20  Southwest by south  219.37
21  Southwest           219.38
22  Southwest by west   236.25
23  West-southwest      253.12
24  West by south       253.13
25  West                270.00
26  West by north       286.87
27  West-northwest      286.88
28  Northwest by west   303.75
29  Northwest           320.62
30  Northwest by north  320.63
31  North-northwest     337.50
32  North by west       354.37
 1  North               354.38

Go

<lang go>package main

import "fmt"

var p = []string{

   "North",
   "North by east",
   "North-northeast",
   "Northeast by north",
   "Northeast",
   "Northeast by east",
   "East-northeast",
   "East by north",
   "East",
   "East by south",
   "East-southeast",
   "Southeast by east",
   "Southeast",
   "Southeast by south",
   "South-southeast",
   "South by east",
   "South",
   "South by west",
   "South-southwest",
   "Southwest by south",
   "Southwest",
   "Southwest by west",
   "West-southwest",
   "West by south",
   "West",
   "West by north",
   "West-northwest",
   "Northwest by west",
   "Northwest",
   "Northwest by north",
   "North-northwest",
   "North by west",

}

func main() {

   for _, h := range []float32{0.0, 16.87, 16.88, 33.75, 50.62, 50.63, 67.5,
       84.37, 84.38, 101.25, 118.12, 118.13, 135.0, 151.87, 151.88, 168.75,
       185.62, 185.63, 202.5, 219.37, 219.38, 236.25, 253.12, 253.13, 270.0,
       286.87, 286.88, 303.75, 320.62, 320.63, 337.5, 354.37, 354.38} {
       i := int(h/11.25+.5) % 32
       fmt.Printf("%2d  %-18s %7.2f°\n", i+1, p[i], h)
   }

}</lang>

 1  North                 0.00°
 2  North by east        16.87°
 3  North-northeast      16.88°
 4  Northeast by north   33.75°
 5  Northeast            50.62°
 6  Northeast by east    50.63°
 7  East-northeast       67.50°
 8  East by north        84.37°
 9  East                 84.38°
10  East by south       101.25°
11  East-southeast      118.12°
12  Southeast by east   118.13°
13  Southeast           135.00°
14  Southeast by south  151.87°
15  South-southeast     151.88°
16  South by east       168.75°
17  South               185.62°
18  South by west       185.63°
19  South-southwest     202.50°
20  Southwest by south  219.37°
21  Southwest           219.38°
22  Southwest by west   236.25°
23  West-southwest      253.12°
24  West by south       253.13°
25  West                270.00°
26  West by north       286.87°
27  West-northwest      286.88°
28  Northwest by west   303.75°
29  Northwest           320.62°
30  Northwest by north  320.63°
31  North-northwest     337.50°
32  North by west       354.37°
 1  North               354.38°

Haskell

<lang haskell> import Data.Char import Data.Maybe import Text.Printf

dirs = ["N", "NbE", "N-NE", "NEbN", "NE", "NEbE", "E-NE", "EbN",

       "E", "EbS", "E-SE", "SEbE", "SE", "SEbS", "S-SE", "SbE", 
       "S", "SbW", "S-SW", "SWbS", "SW", "SWbW", "W-SW", "WbS", 
       "W", "WbN", "W-NW", "NWbW", "NW", "NWbN", "N-NW", "NbW"]

-- Given an index between 0 and 31 return the corresponding compass point name. pointName = capitalize . concatMap (fromMaybe "?" . fromChar) . (dirs !!)

 where fromChar c = lookup c [('N', "north"), ('S', "south"), ('E', "east"), 
                              ('W', "west"),  ('b', " by "),  ('-', "-")]
       capitalize (c:cs) = toUpper c : cs

-- Convert degrees to a compass point index between 0 and 31. pointIndex d = (round (d*1000) + 5625) `mod` 360000 `div` 11250

printPointName d = let deg = read d :: Double

                      idx = pointIndex deg
                  in printf "%2d  %-18s  %6.2f°\n" (idx+1) (pointName idx) deg

main = do

 input <- getContents
 mapM_ printPointName $ lines input

</lang> Output:

 1  North                 0.00°
 2  North by east        16.87°
 3  North-northeast      16.88°
 4  Northeast by north   33.75°
 5  Northeast            50.62°
 6  Northeast by east    50.63°
 7  East-northeast       67.50°
 8  East by north        84.37°
 9  East                 84.38°
10  East by south       101.25°
11  East-southeast      118.12°
12  Southeast by east   118.13°
13  Southeast           135.00°
14  Southeast by south  151.87°
15  South-southeast     151.88°
16  South by east       168.75°
17  South               185.62°
18  South by west       185.63°
19  South-southwest     202.50°
20  Southwest by south  219.37°
21  Southwest           219.38°
22  Southwest by west   236.25°
23  West-southwest      253.12°
24  West by south       253.13°
25  West                270.00°
26  West by north       286.87°
27  West-northwest      286.88°
28  Northwest by west   303.75°
29  Northwest           320.62°
30  Northwest by north  320.63°
31  North-northwest     337.50°
32  North by west       354.37°
 1  North               354.38°

Icon and Unicon

This example is incomplete. 354.38? Please ensure that it meets all task requirements and remove this message.

<lang Icon>link strings,numbers

procedure main()

every heading := 11.25 * (i := 0 to 32) do {

  case i%3 of {
     1: heading +:= 5.62
     2: heading -:= 5.62
     }
  write(right(i+1,3)," ",left(direction(heading),20)," ",fix(heading,,7,2))
  } 

end

procedure direction(d) # compass heading given +/- degrees static dirs initial {

  every put(dirs := [],
            replacem(!["N","NbE","N-NE","NEbN","NE","NEbE","E-NE","EbN",
                       "E","EbS","E-SE","SEbE","SE","SEbS","S-SE","SbE",

"S","SbW","S-SW","SWbS","SW","SWbW","W-SW","WbS", "W","WbN","W-NW","NWbW","NW","NWbN","N-NW","NbW"],

                      "N","north","E","east","W","west","S","south","b"," by "))   
  }

  return dirs[round(((((d%360)+360)%360)/11.25)%32 + 1)]

end</lang>

strings for replacem numbers for round, fix

Output:

  1 north                   0.00
  2 north by east          16.87
  3 north-northeast        16.88
  4 northeast by north     33.75
  5 northeast              50.62
  6 northeast by east      50.63
  7 east-northeast         67.50
  8 east by north          84.37
  9 east                   84.38
 10 east by south         101.25
 11 east-southeast        118.12
 12 southeast by east     118.13
 13 southeast             135.00
 14 southeast by south    151.87
 15 south-southeast       151.88
 16 south by east         168.75
 17 south                 185.62
 18 south by west         185.63
 19 south-southwest       202.50
 20 southwest by south    219.37
 21 southwest             219.38
 22 southwest by west     236.25
 23 west-southwest        253.12
 24 west by south         253.13
 25 west                  270.00
 26 west by north         286.87
 27 west-northwest        286.88
 28 northwest by west     303.75
 29 northwest             320.62
 30 northwest by north    320.63
 31 north-northwest       337.50
 32 north by west         354.37

J

<lang j>require'strings' subs=: 'N,north,S,south,E,east,W,west,b, by ,' dirs=: subs (toupper@{., }.)@rplc~L:1 0&(<;._2) 0 :0 -. ' ',LF

 N,NbE,N-NE,NEbN,NE,NEbE,E-NE,EbN,E,EbS,E-SE,SEbE,SE,SEbS,S-SE,SbE,
 S,SbW,S-SW,SWbS,SW,SWbW,W-SW,WbS,W,WbN,W-NW,NWbW,NW,NWbN,N-NW,NbW,

) indice=: 32 | 0.5 <.@+ %&11.25 deg2pnt=: dirs {~ indice</lang>

Example use:

<lang j> i.10 0 1 2 3 4 5 6 7 8 9

  deg2pnt i.10

┌─────┬─────┬─────┬─────┬─────┬─────┬─────────────┬─────────────┬─────────────┬─────────────┐ │North│North│North│North│North│North│North by east│North by east│North by east│North by east│ └─────┴─────┴─────┴─────┴─────┴─────┴─────────────┴─────────────┴─────────────┴─────────────┘</lang>

Required example:

<lang j> (":@>:@indice,.' ',.>@deg2pnt,.' ',.":@,.)(*&11.25 + 5.62 * 0 1 _1 {~ 3&|) i.33 1 North 0 2 North by east 16.87 3 North-northeast 16.88 4 Northeast by north 33.75 5 Northeast 50.62 6 Northeast by east 50.63 7 East-northeast 67.5 8 East by north 84.37 9 East 84.38 10 East by south 101.25 11 East-southeast 118.12 12 Southeast by east 118.13 13 Southeast 135 14 Southeast by south 151.87 15 South-southeast 151.88 16 South by east 168.75 17 South 185.62 18 South by west 185.63 19 South-southwest 202.5 20 Southwest by south 219.37 21 Southwest 219.38 22 Southwest by west 236.25 23 West-southwest 253.12 24 West by south 253.13 25 West 270 26 West by north 286.87 27 West-northwest 286.88 28 Northwest by west 303.75 29 Northwest 320.62 30 Northwest by north 320.63 31 North-northwest 337.5 32 North by west 354.37 1 North 354.38</lang>

Java

Translation of: Visual Basic .NET

<lang java>public class BoxingTheCompass{

   private static String[] points = new String[32];

   public static void main(String[] args){
       buildPoints();

       double heading = 0;

       for(int i = 0; i<= 32;i++){
           heading = i * 11.25;
           switch(i % 3){
               case 1:
                   heading += 5.62;
                   break;
               case 2:
                   heading -= 5.62;
                   break;
               default:
           }

           System.out.printf("%s\t%18s\t%s°\n",(i % 32) + 1, initialUpper(getPoint(heading)), heading);
       }
   }

   private static void buildPoints(){
       String[] cardinal = {"north", "east", "south", "west"};
       String[] pointDesc = {"1", "1 by 2", "1-C", "C by 1", "C", "C by 2", "2-C", "2 by 1"};

       String str1, str2, strC;

       for(int i = 0;i <= 3;i++){
           str1 = cardinal[i];
           str2 = cardinal[(i + 1) % 4];
           strC = (str1.equals("north") || str1.equals("south")) ? (str1 + str2): (str2 + str1);
           for(int j = 0;j <= 7;j++){
               points[i * 8 + j] = pointDesc[j].replace("1", str1).replace("2", str2).replace("C", strC);
           }
       }
   }

   private static String initialUpper(String s){
       return s.substring(0, 1).toUpperCase() + s.substring(1);
   }

   private static String getPoint(double degrees){
       double testD = (degrees / 11.25) + 0.5;
       return points[(int)Math.floor(testD % 32)];
   }

}</lang> Output:

1	             North	0.0°
2	     North by east	16.87°
3	   North-northeast	16.88°
4	Northeast by north	33.75°
5	         Northeast	50.62°
6	 Northeast by east	50.63°
7	    East-northeast	67.5°
8	     East by north	84.37°
9	              East	84.38°
10	     East by south	101.25°
11	    East-southeast	118.12°
12	 Southeast by east	118.13°
13	         Southeast	135.0°
14	Southeast by south	151.87°
15	   South-southeast	151.88°
16	     South by east	168.75°
17	             South	185.62°
18	     South by west	185.63°
19	   South-southwest	202.5°
20	Southwest by south	219.37°
21	         Southwest	219.38°
22	 Southwest by west	236.25°
23	    West-southwest	253.12°
24	     West by south	253.13°
25	              West	270.0°
26	     West by north	286.87°
27	    West-northwest	286.88°
28	 Northwest by west	303.75°
29	         Northwest	320.62°
30	Northwest by north	320.63°
31	   North-northwest	337.5°
32	     North by west	354.37°
1	             North	354.38°

MUMPS

The TCL implementation was the starting point, but this isn't an exact translation. <lang MUMPS>BOXING(DEGREE)

;This takes in a degree heading, nominally from 0 to 360, and returns the compass point name.
QUIT:((DEGREE<0)||(DEGREE>360)) "land lubber can't read a compass"
NEW DIRS,UNP,UNPACK,SEP,DIR,DOS,D,DS,I,F
SET DIRS="N^NbE^N-NE^NEbN^NE^NEbE^E-NE^EbN^E^EbS^E-SE^SEbE^SE^SEbS^S-SE^SbE^"
SET DIRS=DIRS_"S^SbW^S-SW^SWbS^SW^SWbW^W-SW^WbS^W^WbN^W-NW^NWbW^NW^NWbN^N-NW^NbW"
SET UNP="NESWb"
SET UNPACK="north^east^south^west^ by "
SET SEP=360/$LENGTH(DIRS,"^")
SET DIR=(DEGREE/SEP)+1.5
SET DIR=$SELECT((DIR>33):DIR-32,1:DIR)
SET DOS=$NUMBER(DIR-.5,0)
SET D=$PIECE(DIRS,"^",DIR)
SET DS=""
FOR I=1:1:$LENGTH(D) DO
. SET F=$FIND(UNP,$EXTRACT(D,I)) SET DS=DS_$SELECT((F>0):$PIECE(UNPACK,"^",F-1),1:$E(D,I))
KILL DIRS,UNP,UNPACK,SEP,DIR,D,I,F
QUIT DOS_"^"_DS

BOXWRITE

NEW POINTS,UP,LO,DIR,P,X
SET POINTS="0.0,16.87,16.88,33.75,50.62,50.63,67.5,84.37,84.38,101.25,118.12,118.13,135.0,151.87,"
SET POINTS=POINTS_"151.88,168.75,185.62,185.63,202.5,219.37,219.38,236.25,253.12,253.13,270.0,286.87,"
SET POINTS=POINTS_"286.88,303.75,320.62,320.63,337.5,354.37,354.38"
SET UP="ABCDEFGHIJKLMNOPQRSTUVWXYZ"
SET LO="abcdefghijklmnopqrstuvwxyz"
FOR P=1:1:$LENGTH(POINTS,",") DO
. SET X=$$BOXING($PIECE(POINTS,",",P))
. ;Capitalize the initial letter of the direction
. SET DIR=$PIECE(X,"^",2)
. SET DIR=$TRANSLATE($EXTRACT(DIR,1),LO,UP)_$EXTRACT(DIR,2,$LENGTH(DIR))
. WRITE $PIECE(X,"^"),?5,DIR,?40,$JUSTIFY($PIECE(POINTS,",",P),10,2),!
KILL POINTS,UP,LO,DIR,P,X
QUIT</lang>

Output:

Debugger executing 'BOXWRITE^COMPASS'
1    North                                    0.00
2    North by east                           16.87
3    North-northeast                         16.88
4    Northeast by north                      33.75
5    Northeast                               50.62
6    Northeast by east                       50.63
7    East-northeast                          67.50
8    East by north                           84.37
9    East                                    84.38
10   East by south                          101.25
11   East-southeast                         118.12
12   Southeast by east                      118.13
13   Southeast                              135.00
14   Southeast by south                     151.87
15   South-southeast                        151.88
16   South by east                          168.75
17   South                                  185.62
18   South by west                          185.63
19   South-southwest                        202.50
20   Southwest by south                     219.37
21   Southwest                              219.38
22   Southwest by west                      236.25
23   West-southwest                         253.12
24   West by south                          253.13
25   West                                   270.00
26   West by north                          286.87
27   West-northwest                         286.88
28   Northwest by west                      303.75
29   Northwest                              320.62
30   Northwest by north                     320.63
31   North-northwest                        337.50
32   North by west                          354.37
1    North                                  354.38

Perl

This example is incorrect. Please fix the code and remove this message.

Details: Doesn't actually translate degrees to points, or use such a translation to test the test angles. The final reported angle is wrong too.

The names of the compass points can be derived from the four basic directions with the help of the bits of a zero-based index.

<lang Perl># This is a modification of a solution posted by Deepsoul, which was

  1. mostly correct, but mis-named every fourth compass point.

sub compass_point # compute the names, because they're logical {

   my $index      = shift;
   my @directions = qw(north east south west);
   my $name;
   if ( $index & 1 ) {
       my $ref = ( $index + 1 ) & ~0x23;
       $name =
           compass_point($ref) . " by "
           . compass_point(
           ( ( ( 2 - ( $index & 2 ) ) * 4 ) + ( ($index) & 0x18 ) ) & 0x1F );
   }
   elsif ( $index & 2 ) {
       $name =
             compass_point( ( $index + 2 ) & ~0x27 ) . "-"
           . compass_point( ( $index | 4 ) & ~0x23 );
   }
   elsif ( $index & 4 ) {
       $name =
             compass_point( ( $index + 8 ) & ~0x2F )
           . compass_point( ( $index | 8 ) & ~0x27 );
   }
   else {
       $name = $directions[ $index / 8 ];
   }
   return $name;

}

sub heading_angle {

   my $index         = shift;
   my $heading_angle = $index * 11.25;
   my $offset        = ( 0, 5.62, -5.62 )[ $index % 3 ];
   return $heading_angle + $offset;

}

for my $index ( 0 .. 32 ) {

   $index %= 32;
   my $heading_angle = heading_angle($index);
   my $compass_point = ucfirst compass_point($index);
   my $label         = $index + 1;
   printf "  %2d %-21s %6.2f\n", $label, $compass_point, $heading_angle;

}</lang> Output:

   1 North                   0.00
   2 North by east          16.87
   3 North-northeast        16.88
   4 Northeast by north     33.75
   5 Northeast              50.62
   6 Northeast by east      50.63
   7 East-northeast         67.50
   8 East by north          84.37
   9 East                   84.38
  10 East by south         101.25
  11 East-southeast        118.12
  12 Southeast by east     118.13
  13 Southeast             135.00
  14 Southeast by south    151.87
  15 South-southeast       151.88
  16 South by east         168.75
  17 South                 185.62
  18 South by west         185.63
  19 South-southwest       202.50
  20 Southwest by south    219.37
  21 Southwest             219.38
  22 Southwest by west     236.25
  23 West-southwest        253.12
  24 West by south         253.13
  25 West                  270.00
  26 West by north         286.87
  27 West-northwest        286.88
  28 Northwest by west     303.75
  29 Northwest             320.62
  30 Northwest by north    320.63
  31 North-northwest       337.50
  32 North by west         354.37
   1 North                   0.00

Perl 6

Translation of: Perl

<lang perl6>sub point (Int $index) {

   my $ix = $index % 32;
   if $ix +& 1
       { "&point(($ix + 1) +& 28) by &point(((2 - ($ix +& 2)) * 4) + $ix +& 24)" }
   elsif $ix +& 2
       { "&point(($ix + 2) +& 24)-&point(($ix +| 4) +& 28)" }
   elsif $ix +& 4
       { "&point(($ix + 8) +& 16)&point(($ix +| 8) +& 24)" }
   else
       { <north east south west>[$ix div 8]; }

}

sub test-angle ($ix) { $ix * 11.25 + (0, 5.62, -5.62)[ $ix % 3 ] } sub angle-to-point($𝜽) { floor $𝜽 / 360 * 32 + 0.5 }

for 0 .. 32 -> $ix {

   my $𝜽 = test-angle($ix);
   printf "  %2d %6.2f%s %s\n",
             $ix % 32 + 1,
                 $𝜽, '°',
                         ucfirst point angle-to-point $𝜽;

}</lang>

Output:

   1   0.00° North
   2  16.87° North by east
   3  16.88° North-northeast
   4  33.75° Northeast by north
   5  50.62° Northeast
   6  50.63° Northeast by east
   7  67.50° East-northeast
   8  84.37° East by north
   9  84.38° East
  10 101.25° East by south
  11 118.12° East-southeast
  12 118.13° Southeast by east
  13 135.00° Southeast
  14 151.87° Southeast by south
  15 151.88° South-southeast
  16 168.75° South by east
  17 185.62° South
  18 185.63° South by west
  19 202.50° South-southwest
  20 219.37° Southwest by south
  21 219.38° Southwest
  22 236.25° Southwest by west
  23 253.12° West-southwest
  24 253.13° West by south
  25 270.00° West
  26 286.87° West by north
  27 286.88° West-northwest
  28 303.75° Northwest by west
  29 320.62° Northwest
  30 320.63° Northwest by north
  31 337.50° North-northwest
  32 354.37° North by west
   1 354.38° North

PicoLisp

<lang PicoLisp>(scl 3)

(setq *Compass # Build lookup table

  (let H -16.875
     (mapcar
        '((Str)
           (cons
              (inc 'H 11.25)       # Heading in degrees
              (pack                # Compass point
                 (replace (chop Str)
                    "N" "north"
                    "E" "east"
                    "S" "south"
                    "W" "west"
                    "b" " by " ) ) ) )
        '("N" "NbE" "N-NE" "NEbN" "NE" "NEbE" "E-NE" "EbN"
           "E" "EbS" "E-SE" "SEbE" "SE" "SEbS" "S-SE" "SbE"
           "S" "SbW" "S-SW" "SWbS" "SW" "SWbW" "W-SW" "WbS"
           "W" "WbN" "W-NW" "NWbW" "NW" "NWbN" "N-NW" "NbW"
           "N" ) ) ) )

(de heading (Deg)

  (rank (% Deg 360.00) *Compass) )

(for I (range 0 32)

  (let H (* I 11.25)
     (case (% I 3)
        (1 (inc 'H 5.62))
        (2 (dec 'H 5.62)) )
     (tab (3 1 -18 8)
        (inc (% I 32))
        NIL
        (cdr (heading H))
        (round H 2) ) ) )</lang>

Output:

  1 north                 0.00
  2 north by east        16.87
  3 north-northeast      16.88
  4 northeast by north   33.75
  5 northeast            50.62
  6 northeast by east    50.63
  7 east-northeast       67.50
  8 east by north        84.37
  9 east                 84.38
 10 east by south       101.25
 11 east-southeast      118.12
 12 southeast by east   118.13
 13 southeast           135.00
 14 southeast by south  151.87
 15 south-southeast     151.88
 16 south by east       168.75
 17 south               185.62
 18 south by west       185.63
 19 south-southwest     202.50
 20 southwest by south  219.37
 21 southwest           219.38
 22 southwest by west   236.25
 23 west-southwest      253.12
 24 west by south       253.13
 25 west                270.00
 26 west by north       286.87
 27 west-northwest      286.88
 28 northwest by west   303.75
 29 northwest           320.62
 30 northwest by north  320.63
 31 north-northwest     337.50
 32 north by west       354.37
  1 north               354.38

PureBasic

<lang PureBasic>DataSection

 Data.s "N", "north", "E", "east", "W", "west", "S", "south", "b", " by "   ;abbreviations, expansions
 Data.s "N NbE N-NE NEbN NE NEbE E-NE EbN E EbS E-SE SEbE SE SEbS S-SE SbE" ;dirs
 Data.s "S SbW S-SW SWbS SW SWbW W-SW WbS W WbN W-NW NWbW NW NWbN N-NW NbW"

EndDataSection

initialize data

NewMap dirSubst.s() Define i, abbr.s, expansion.s For i = 1 To 5

 Read.s abbr
 Read.s expansion
 dirSubst(abbr) = expansion

Next

Dim dirs.s(32) Define j, s.s For j = 0 To 1

 Read.s s.s
 For i = 0 To 15
   abbr.s = StringField(s, i + 1, " ")
   dirs(j * 16 + i) = abbr
 Next

Next

expand abbreviated compass point and capitalize

Procedure.s abbr2compassPoint(abbr.s)

 Shared dirSubst()
 Protected i, compassPoint.s, key.s
 
 For i = 1 To Len(abbr)
   key.s = Mid(abbr, i, 1)
   If FindMapElement(dirSubst(), key)
     compassPoint + dirSubst(key)
   Else
     compassPoint + key
   EndIf
 Next
 ProcedureReturn UCase(Left(compassPoint, 1)) + Mid(compassPoint, 2)

EndProcedure

Procedure.s angle2compass(angle.f)

 Shared dirs()
 Static segment.f = 360.0 / 32 ;width of each compass segment
 Protected dir
 
 ;work out which segment contains the compass angle
 dir = Int((Mod(angle, 360) / segment) + 0.5)
 
 ;convert to a named direction
 ProcedureReturn abbr2compassPoint(dirs(dir))

EndProcedure

box the compass

If OpenConsole()

 Define i, heading.f, index 
 For i = 0 To 32
   heading = i * 11.25
   If i % 3 = 1
     heading + 5.62
   EndIf 
   If i % 3 = 2
     heading - 5.62
   EndIf 
   index = i % 32 + 1
   
   PrintN(RSet(Str(index), 2) + " " + LSet(angle2compass(heading), 18) + RSet(StrF(heading, 2), 7))
 Next 
 
 Print(#CRLF$ + #CRLF$ + "Press ENTER to exit"): Input()
 CloseConsole()

EndIf</lang> Sample output:

 1 North                0.00
 2 North by east       16.87
 3 North-northeast     16.88
 4 Northeast by north  33.75
 5 Northeast           50.62
 6 Northeast by east   50.63
 7 East-northeast      67.50
 8 East by north       84.37
 9 East                84.38
10 East by south      101.25
11 East-southeast     118.12
12 Southeast by east  118.13
13 Southeast          135.00
14 Southeast by south 151.87
15 South-southeast    151.88
16 South by east      168.75
17 South              185.62
18 South by west      185.63
19 South-southwest    202.50
20 Southwest by south 219.37
21 Southwest          219.38
22 Southwest by west  236.25
23 West-southwest     253.12
24 West by south      253.13
25 West               270.00
26 West by north      286.87
27 West-northwest     286.88
28 Northwest by west  303.75
29 Northwest          320.62
30 Northwest by north 320.63
31 North-northwest    337.50
32 North by west      354.37
 1                    354.38

Prolog

Part 1 : The following knowledge base takes a heading in degrees and returns the correct 32-point compass heading. It can also go in the other direction. <lang prolog> compassangle(1, 'North',n, 0.00). compassangle(2, 'North by east', nbe, 11.25). compassangle(3, 'North-northeast', nne,22.50). compassangle(4, 'Northeast by north', nebn,33.75). compassangle(5, 'Northeast', ne,45.00). compassangle(6, 'Norteast by east', nebe,56.25). compassangle(7, 'East-northeast', ene,67.50). compassangle(8, 'East by North', ebn,78.75). compassangle(9, 'East', e,90.00). compassangle(10, 'East by south', ebs, 101.25). compassangle(11, 'East-southeast', ese,112.50). compassangle(12, 'Southeast by east', sebe, 123.75). compassangle(13, 'Southeast', se, 135.00). compassangle(14, 'Southeast by south', sebs, 146.25). compassangle(15, 'South-southeast',sse, 157.50). compassangle(16, 'South by east', sbe, 168.75). compassangle(17, 'South', s, 180.00). compassangle(18, 'South by west', sbw, 191.25). compassangle(19, 'South-southwest', ssw, 202.50). compassangle(20, 'Southwest by south', swbs, 213.75). compassangle(21, 'Southwest', sw, 225.00). compassangle(22, 'Southwest by west', swbw, 236.25). compassangle(23, 'West-southwest', wsw, 247.50). compassangle(24, 'West by south', wbs, 258.75). compassangle(25, 'West', w, 270.00). compassangle(26, 'West by north', wbn, 281.25). compassangle(27, 'West-northwest', wnw, 292.50). compassangle(28, 'Northwest by west', nwbw, 303.75). compassangle(29, 'Northwest', nw, 315.00). compassangle(30, 'Northwest by north', nwbn, 326.25). compassangle(31, 'North-northwest', nnw, 337.50). compassangle(32, 'North by west', nbw, 348.75). compassangle(1, 'North', n, 360.00). compassangle(Index , Name, Heading, Angle) :- nonvar(Angle), resolveindex(Angle, Index),

                                              compassangle(Index,Name, Heading, _).

resolveindex(Angle, Index) :- N is Angle / 11.25 + 0.5, I is floor(N),Index is (I mod 32) + 1. </lang> Part 2 : The following rules print a table of indexes. <lang prolog> printTableRow(Angle) :- compassangle(Index, Name, _, Angle),

                       write(Index), write('    '),
                       write(Name), write('   '),
                       write(Angle).

printTable([X|Xs]) :- printTableRow(X), nl, printTable(Xs),!. printTable([]). </lang> The following query prints the required table. <lang prolog> ?- printTable([0.0, 16.87, 16.88, 33.75, 50.62, 50.63, 67.5, 84.37, 84.38, 101.25, 118.12, 118.13, 135.0, 151.87, 151.88, 168.75, 185.62,

                      185.63, 202.5, 219.37, 219.38, 236.25, 253.12, 253.13, 270.0, 286.87, 286.88, 303.75, 320.62, 320.63, 337.5, 354.37, 354.38]).

1 North 0.0 2 North by east 16.87 3 North-northeast 16.88 4 Northeast by north 33.75 5 Northeast 50.62 6 Norteast by east 50.63 7 East-northeast 67.5 8 East by North 84.37 9 East 84.38 10 East by south 101.25 11 East-southeast 118.12 12 Southeast by east 118.13 13 Southeast 135.0 14 Southeast by south 151.87 15 South-southeast 151.88 16 South by east 168.75 17 South 185.62 18 South by west 185.63 19 South-southwest 202.5 20 Southwest by south 219.37 21 Southwest 219.38 22 Southwest by west 236.25 23 West-southwest 253.12 24 West by south 253.13 25 West 270.0 26 West by north 286.87 27 West-northwest 286.88 28 Northwest by west 303.75 29 Northwest 320.62 30 Northwest by north 320.63 31 North-northwest 337.5 32 North by west 354.37 1 North 354.38 true. </lang>

Python

<lang python>majors = 'north east south west'.split() majors *= 2 # no need for modulo later quarter1 = 'N,N by E,N-NE,NE by N,NE,NE by E,E-NE,E by N'.split(',') quarter2 = [p.replace('NE','EN') for p in quarter1]

def degrees2compasspoint(d):

   d = (d % 360) + 360/64
   majorindex, minor = divmod(d, 90.)
   majorindex = int(majorindex)
   minorindex  = int( (minor*4) // 45 )
   p1, p2 = majors[majorindex: majorindex+2]
   if p1 in {'north', 'south'}:
       q = quarter1
   else:
       q = quarter2
   return q[minorindex].replace('N', p1).replace('E', p2).capitalize()

if __name__ == '__main__':

   for i in range(33):
       d = i * 11.25
       m = i % 3
       if   m == 1: d += 5.62
       elif m == 2: d -= 5.62
       n = i % 32 + 1
       print( '%2i %-18s %7.2f°' % (n, degrees2compasspoint(d), d) )</lang>
Output
 1 North                 0.00°
 2 North by east        16.87°
 3 North-northeast      16.88°
 4 Northeast by north   33.75°
 5 Northeast            50.62°
 6 Northeast by east    50.63°
 7 East-northeast       67.50°
 8 East by north        84.37°
 9 East                 84.38°
10 East by south       101.25°
11 East-southeast      118.12°
12 Southeast by east   118.13°
13 Southeast           135.00°
14 Southeast by south  151.87°
15 South-southeast     151.88°
16 South by east       168.75°
17 South               185.62°
18 South by west       185.63°
19 South-southwest     202.50°
20 Southwest by south  219.37°
21 Southwest           219.38°
22 Southwest by west   236.25°
23 West-southwest      253.12°
24 West by south       253.13°
25 West                270.00°
26 West by north       286.87°
27 West-northwest      286.88°
28 Northwest by west   303.75°
29 Northwest           320.62°
30 Northwest by north  320.63°
31 North-northwest     337.50°
32 North by west       354.37°
 1 North               354.38°

Ruby

First I want a hash Headings = {1 => "north", 2 => "north by east", ...}. This program outputs the hash so that I can skip typing all 32 pairs.

<lang ruby>h = [] ["north", "east", "south", "west", "north"].each_cons(2) do |a, b|

 c = if ["north", "south"].include? a then "#{a}#{b}" else "#{b}#{a}" end
 h << a
 h << "#{a} by #{b}"
 h << "#{a}-#{c}"
 h << "#{c} by #{a}"
 h << "#{c}"
 h << "#{c} by #{b}"
 h << "#{b}-#{c}"
 h << "#{b} by #{a}"

end

puts "Headings = {" h.each_with_index { |n, i| puts " #{i+1} => #{n.inspect}," } puts "}"</lang>

I paste the output from the first program into a second program, then add a method to find a compass heading from degrees, and some code to output the table.

<lang ruby>Headings = {

 1 => "north",
 2 => "north by east",
 3 => "north-northeast",
 4 => "northeast by north",
 5 => "northeast",
 6 => "northeast by east",
 7 => "east-northeast",
 8 => "east by north",
 9 => "east",
 10 => "east by south",
 11 => "east-southeast",
 12 => "southeast by east",
 13 => "southeast",
 14 => "southeast by south",
 15 => "south-southeast",
 16 => "south by east",
 17 => "south",
 18 => "south by west",
 19 => "south-southwest",
 20 => "southwest by south",
 21 => "southwest",
 22 => "southwest by west",
 23 => "west-southwest",
 24 => "west by south",
 25 => "west",
 26 => "west by north",
 27 => "west-northwest",
 28 => "northwest by west",
 29 => "northwest",
 30 => "northwest by north",
 31 => "north-northwest",
 32 => "north by west",

}

  1. Finds the 32-point compass heading nearest _degrees_, and
  2. returns an array of the index and name.
  3. p heading(60)
  4. # => [6, "northeast by east"]

def heading(degrees)

 i = degrees.quo(360).*(32).round.%(32).+(1)
 [i, Headings[i]]

end

  1. an array of angles, in degrees

angles = (0..32).map { |i| i * 11.25 + [0, 5.62, -5.62][i % 3] }

angles.each do |degrees|

 index, name = heading degrees
 printf "%2d %20s %6g\n", index, name.center(20), degrees

end</lang>

The second program outputs this table:

 1        north              0
 2    north by east      16.87
 3   north-northeast     16.88
 4  northeast by north   33.75
 5      northeast        50.62
 6  northeast by east    50.63
 7    east-northeast      67.5
 8    east by north      84.37
 9         east          84.38
10    east by south     101.25
11    east-southeast    118.12
12  southeast by east   118.13
13      southeast          135
14  southeast by south  151.87
15   south-southeast    151.88
16    south by east     168.75
17        south         185.62
18    south by west     185.63
19   south-southwest     202.5
20  southwest by south  219.37
21      southwest       219.38
22  southwest by west   236.25
23    west-southwest    253.12
24    west by south     253.13
25         west            270
26    west by north     286.87
27    west-northwest    286.88
28  northwest by west   303.75
29      northwest       320.62
30  northwest by north  320.63
31   north-northwest     337.5
32    north by west     354.37
 1        north         354.38

Tcl

<lang tcl>proc angle2compass {angle} {

   set dirs {

N NbE N-NE NEbN NE NEbE E-NE EbN E EbS E-SE SEbE SE SEbS S-SE SbE S SbW S-SW SWbS SW SWbW W-SW WbS W WbN W-NW NWbW NW NWbN N-NW NbW

   }
   set unpack {N "north" E "east" W "west" S "south" b " by "}
   # Compute the width of each compass segment
   set sep [expr {360.0 / [llength $dirs]}]
   # Work out which segment contains the compass angle
   set dir [expr {round((fmod($angle + $sep/2, 360) - $sep/2) / $sep)}]
   # Convert to a named direction, capitalized as in the wikipedia article
   return [string totitle [string map $unpack [lindex $dirs $dir]]]

}

  1. Box the compass, using the variable generation algorithm described

for {set i 0} {$i < 33} {incr i} {

   set heading [expr {$i * 11.25}]
   if {$i % 3 == 1} {set heading [expr {$heading + 5.62}]}
   if {$i % 3 == 2} {set heading [expr {$heading - 5.62}]}
   set index [expr {$i % 32 + 1}]
   # Pretty-print the results of converting an angle to a compass heading
   puts [format "%2i %-18s %7.2f°" $index [angle2compass $heading] $heading]

}</lang> Output:

 1 North                 0.00°
 2 North by east        16.87°
 3 North-northeast      16.88°
 4 Northeast by north   33.75°
 5 Northeast            50.62°
 6 Northeast by east    50.63°
 7 East-northeast       67.50°
 8 East by north        84.37°
 9 East                 84.38°
10 East by south       101.25°
11 East-southeast      118.12°
12 Southeast by east   118.13°
13 Southeast           135.00°
14 Southeast by south  151.87°
15 South-southeast     151.88°
16 South by east       168.75°
17 South               185.62°
18 South by west       185.63°
19 South-southwest     202.50°
20 Southwest by south  219.37°
21 Southwest           219.38°
22 Southwest by west   236.25°
23 West-southwest      253.12°
24 West by south       253.13°
25 West                270.00°
26 West by north       286.87°
27 West-northwest      286.88°
28 Northwest by west   303.75°
29 Northwest           320.62°
30 Northwest by north  320.63°
31 North-northwest     337.50°
32 North by west       354.37°
 1 North               354.38°

Visual Basic .NET

<lang vbnet>Module BoxingTheCompass

   Dim _points(32) As String
   Sub Main()
       BuildPoints()
       Dim heading As Double = 0D
       For i As Integer = 0 To 32
           heading = i * 11.25
           Select Case i Mod 3
               Case 1
                   heading += 5.62
               Case 2
                   heading -= 5.62
           End Select
           Console.WriteLine("{0,2}: {1,-18} {2,6:F2}°", (i Mod 32) + 1, InitialUpper(GetPoint(heading)), heading)
       Next
   End Sub
   Private Sub BuildPoints()
       Dim cardinal As String() = New String() {"north", "east", "south", "west"}
       Dim pointDesc As String() = New String() {"1", "1 by 2", "1-C", "C by 1", "C", "C by 2", "2-C", "2 by 1"}
       Dim str1, str2, strC As String
       For i As Integer = 0 To 3
           str1 = cardinal(i)
           str2 = cardinal((i + 1) Mod 4)
           strC = IIf(str1 = "north" Or str1 = "south", str1 & str2, str2 & str1)
           For j As Integer = 0 To 7
               _points(i * 8 + j) = pointDesc(j).Replace("1", str1).Replace("2", str2).Replace("C", strC)
           Next
       Next
   End Sub
   Private Function InitialUpper(ByVal s As String) As String
       Return s.Substring(0, 1).ToUpper() & s.Substring(1)
   End Function
   Private Function GetPoint(ByVal Degrees As Double) As String
       Dim testD As Double = (Degrees / 11.25) + 0.5
       Return _points(CInt(Math.Floor(testD Mod 32)))
   End Function

End Module </lang> Output:

 1: North                0.00°
 2: North by east       16.87°
 3: North-northeast     16.88°
 4: Northeast by north  33.75°
 5: Northeast           50.62°
 6: Northeast by east   50.63°
 7: East-northeast      67.50°
 8: East by north       84.37°
 9: East                84.38°
10: East by south      101.25°
11: East-southeast     118.12°
12: Southeast by east  118.13°
13: Southeast          135.00°
14: Southeast by south 151.87°
15: South-southeast    151.88°
16: South by east      168.75°
17: South              185.62°
18: South by west      185.63°
19: South-southwest    202.50°
20: Southwest by south 219.37°
21: Southwest          219.38°
22: Southwest by west  236.25°
23: West-southwest     253.12°
24: West by south      253.13°
25: West               270.00°
26: West by north      286.87°
27: West-northwest     286.88°
28: Northwest by west  303.75°
29: Northwest          320.62°
30: Northwest by north 320.63°
31: North-northwest    337.50°
32: North by west      354.37°
 1: North              354.38°