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


MODULE BoxTheCompass;
FROM FormatString IMPORT FormatString;
FROM RealStr IMPORT RealToStr;
FROM Terminal IMPORT WriteString,WriteLn,Write,ReadChar;
 
PROCEDURE expand(cp : ARRAY OF CHAR);
VAR i : INTEGER = 0;
BEGIN
    WHILE cp[i] # 0C DO
        IF i=0 THEN
            CASE cp[i] OF
                'N': WriteString("North") |
                'E': WriteString("East")  |
                'S': WriteString("South") |
                'W': WriteString("West")  |
                'b': WriteString(" by ")
            ELSE
                WriteString("-");
            END;
        ELSE
            CASE cp[i] OF
                'N': WriteString("north") |
                'E': WriteString("east")  |
                'S': WriteString("south") |
                'W': WriteString("west")  |
                'b': WriteString(" by ")
            ELSE
                WriteString("-");
            END;
        END;
        INC(i)
    END;
END expand;
 
PROCEDURE FormatReal(r : REAL);
VAR
    buf : ARRAY[0..63] OF CHAR;
    u,v : INTEGER;
    w : REAL;
BEGIN
    u := TRUNC(r);
    w := r - FLOAT(u);
    v := TRUNC(100.0 * w);
 
    FormatString("%6i.%'02i", buf, u, v);
    WriteString(buf);
END FormatReal;
 
VAR
    cp : ARRAY[0..31] OF ARRAY[0..4] OF CHAR = {
        "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"
    };
    buf : ARRAY[0..63] OF CHAR;
    i,index : INTEGER;
    heading : REAL;
BEGIN
    WriteString("Index  Degrees  Compass point");
    WriteLn;
    WriteString("-----  -------  -------------");
    WriteLn;
    FOR i:=0 TO 32 DO
        index := i MOD 32;
        heading := FLOAT(i) * 11.25;
        CASE i MOD 3 OF
            1: heading := heading + 5.62; |
            2: heading := heading - 5.62;
        ELSE
            (* empty *)
        END;
        FormatString("%2i  ", buf, index+1);
        WriteString(buf);
        FormatReal(heading);
        WriteString("   ");
        expand(cp[index]);
        WriteLn
    END;
 
    ReadChar
END BoxTheCompass.