Box the compass

From Rosetta Code
Revision as of 16:04, 28 March 2011 by Rdm (talk | contribs) (J: add another example)
Box the compass is a draft programming task. It is not yet considered ready to be promoted as a complete task, for reasons that should be found in its talk page.

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

J

<lang j>require'strings' cardinal=: ;:'N Ne E Se S Sw W Nw N' rplc;:'N North E East e east S South W West w west' tween=:1 :'[,m,tolower@]' by=:' by 'tween fixup=: (rplc |.@;{.@;:@tolower) ^:(' '&e.)L:0 points=:fixup ,2 ([;by;'-'tween;by~)&>/\ cardinal indice=: 32 | 0.5 <.@+ %&11.25 deg2pnt=: points {~ 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>

As for the required example, sometimes it's simpler to do just a small bit of "extra" work:

<lang j>((1 ":@:+ ~."1@index),.' ',.;@~."1@deg2pnt,.' ',.'7.2'8!:2]) _5.62 0 5.62 +/~11.25 * i.33

1 North                -5.62   0.00   5.62
2 North by east         5.63  11.25  16.87
3 North-northeast      16.88  22.50  28.12
4 Northeast by north   28.13  33.75  39.37
5 Northeast            39.38  45.00  50.62
6 Northeast by east    50.63  56.25  61.87
7 Northeast-east       61.88  67.50  73.12
8 East by north        73.13  78.75  84.37
9 East                 84.38  90.00  95.62

10 East by south 95.63 101.25 106.87 11 East-southeast 106.88 112.50 118.12 12 Southeast by east 118.13 123.75 129.37 13 Southeast 129.38 135.00 140.62 14 Southeast by south 140.63 146.25 151.87 15 Southeast-south 151.88 157.50 163.12 16 South by east 163.13 168.75 174.37 17 South 174.38 180.00 185.62 18 South by west 185.63 191.25 196.87 19 South-southwest 196.88 202.50 208.12 20 Southwest by south 208.13 213.75 219.37 21 Southwest 219.38 225.00 230.62 22 Southwest by west 230.63 236.25 241.87 23 Southwest-west 241.88 247.50 253.12 24 West by south 253.13 258.75 264.37 25 West 264.38 270.00 275.62 26 West by north 275.63 281.25 286.87 27 West-northwest 286.88 292.50 298.12 28 Northwest by west 298.13 303.75 309.37 29 Northwest 309.38 315.00 320.62 30 Northwest by north 320.63 326.25 331.87 31 Northwest-north 331.88 337.50 343.12 32 North by west 343.13 348.75 354.37

1 North               354.38 360.00 365.62</lang>

Here, I compute the name (and index) for each of the numbers, and then find the unique list of names (and indices) represented in each row -- which is always only one name (and only one index) -- and convert the whole thing to characters.

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°

Python

<lang python>majors = 'north east south west'.split() majors = majors + majors # 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()

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°

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
   return [string totitle [string map $unpack [lindex $dirs $dir]]]

}

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}]
   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°