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

 Create a function that takes a heading in degrees and returns the correct 32-point compass heading.
 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).


Notes;
 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
 The column of indices can be thought of as an enumeration of the thirty two cardinal points (see talk page)..


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