Box the compass: Difference between revisions

From Rosetta Code
Content added Content deleted
(Perl solution edited, and output posted)
Line 995: Line 995:


=={{header|Perl}}==
=={{header|Perl}}==
{{output?|Perl}}
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.
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
<lang Perl>sub angleindex # zero-based indices for easier arithmetic
# mostly correct, but mis-named every fourth compass point.
{
return int($_[0] * 32.0 / 360.0 + 0.5) % 32;
}

my @dirs= qw(north east south west);


sub indexname # compute the names, because they're logical
sub compass_point # compute the names, because they're logical
{
{
my ($ind)= @_;
my $index = shift;
my @directions = qw(north east south west);
my $name;
my $name;


if( $ind & 1 ) {
if ( $index & 1 ) {
my $ref= ($ind + 1) & ~0x23;
my $ref = ( $index + 1 ) & ~0x23;
$name= indexname($ref) . " by " .
$name =
indexname($ref + (2 - ($ind & 3))*(8 - ($ref & 4)) & 0x1F);
compass_point($ref) . " by "
. compass_point(
( ( ( 2 - ( $index & 2 ) ) * 4 ) + ( ($index) & 0x18 ) ) & 0x1F );
}
}
elsif( $ind & 2 ) {
elsif ( $index & 2 ) {
$name =
$name= indexname(($ind + 2) & ~0x27) . "-" . indexname(($ind | 4) & ~0x23);
compass_point( ( $index + 2 ) & ~0x27 ) . "-"
. compass_point( ( $index | 4 ) & ~0x23 );
}
}
elsif( $ind & 4 ) {
elsif ( $index & 4 ) {
$name =
$name= indexname(($ind + 8) & ~0x2F) . indexname(($ind | 8) & ~0x27);
compass_point( ( $index + 8 ) & ~0x2F )
. compass_point( ( $index | 8 ) & ~0x27 );
}
}
else {
else {
$name= $dirs[$ind/8];
$name = $directions[ $index / 8 ];
}
}
return $name;
return $name;
}
}


sub heading_angle {
my @input= (0.0, 16.87, 16.88, 33.75, 50.62, 50.63, 67.5, 84.37, 84.38, 101.25,
my $index = shift;
118.12, 118.13, 135.0, 151.87, 151.88, 168.75, 185.62, 185.63,
my $heading_angle = $index * 11.25;
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);
my $offset = ( 0, 5.62, -5.62 )[ $index % 3 ];
return $heading_angle + $offset;
}


for (@input) {
for my $index ( 0 .. 32 ) {
my $ind= angleindex($_);
$index %= 32;
my $name= ucfirst indexname($ind);
my $heading_angle = heading_angle($index);
my $compass_point = ucfirst compass_point($index);
++$ind; # -> one-based index for output
my $label = $index + 1;
print "$ind\t$name\t$_\n";
printf " %2d %-21s %6.2f\n", $label, $compass_point, $heading_angle;
}</lang>
}</lang>
Output:

<pre>
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
</pre>
=={{header|PicoLisp}}==
=={{header|PicoLisp}}==
<lang PicoLisp>(scl 3)
<lang PicoLisp>(scl 3)

Revision as of 09:16, 6 July 2011

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. 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

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

<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

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

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°