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


; List of abbreviated compass point labels
make "compass_points [ 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 ]
 
; List of angles to test
make "test_angles [  0.00  16.87  16.88  33.75  50.62  50.63  67.50
                    84.37  84.38 101.25 118.12 118.13 135.00 151.87
                   151.88 168.75 185.62 185.63 202.50 219.37 219.38
                   236.25 253.12 253.13 270.00 286.87 286.88 303.75
                   320.62 320.63 337.50 354.37 354.38 ]
 
; make comparisons case-sensitive
make "caseignoredp "false
 
; String utilities: search and replace
to replace_in :src :from :to
  output map [ ifelse equalp ? :from [:to] [?] ] :src
end
 
; pad with spaces
to pad :string :length
  output cascade [lessp :length count ?] [word ? "\ ] :string
end
 
; capitalize first letter
to capitalize :string
  output word (uppercase first :string) butfirst :string
end
 
; convert compass point abbreviation to full text of label
to expand_point :abbr
  foreach [[N north] [E east] [S south] [W west] [b \ by\ ]] [
    make "abbr replace_in :abbr (first ?) (last ?)
  ]
  output capitalize :abbr
end
 
; modulus function that returns 1..N instead of 0..N-1
to adjusted_modulo :n :d
  output sum 1 modulo (difference :n 1) :d
end
 
; convert a compass angle from degrees into a box index (1..32)
to compass_point :degrees
  make "degrees modulo :degrees 360
  output adjusted_modulo (sum 1 int quotient (sum :degrees 5.625) 11.25) 32
end
 
;  Now output the table of test data
print (sentence (pad "Degrees 7) "\| (pad "Closest\ Point 18) "\| "Index )
foreach :test_angles [
  local "index
  make "index compass_point ?
  local "abbr
  make "abbr item :index :compass_points
  local "label
  make "label expand_point :abbr
  print (sentence (form ? 7 2) "\| (pad :label 18) "\| (form :index 2 0) )
]
 
; and exit
bye
 