Box the compass: Difference between revisions
(→{{header|Tcl}}: Add Ruby.) |
|||
Line 450: | Line 450: | ||
puts "Headings = {" |
puts "Headings = {" |
||
h.each_with_index { |n, i| puts " #{i+1} => #{n.inspect}," } |
|||
puts "}"</lang> |
puts "}"</lang> |
||
Line 542: | Line 542: | ||
32 north by west 354.37 |
32 north by west 354.37 |
||
1 north 354.38</pre> |
1 north 354.38</pre> |
||
=={{header|Tcl}}== |
=={{header|Tcl}}== |
Revision as of 08:19, 4 April 2011
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
- Create a function that takes a heading in degrees and returns the correct 32-point compass heading.
- 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
Clojure
<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))))))
(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
<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
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 hy=:'-'tween&>/@(/: #@>)@; fixup=: (rplc |.@;{.@;:@tolower) ^:(' '&e.)L:0 points=: , fixup 2 ([;by;hy;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>
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
<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
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",
}
- Finds the 32-point compass heading nearest _degrees_, and
- returns an array of the index and name.
- p heading(60)
- # => [6, "northeast by east"]
def heading(degrees)
i = degrees.quo(360).*(32).round.%(32).+(1) [i, Headings[i]]
end
- 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]]]
}
- 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°