Code Golf: Build Me an Arc
Asked Answered
S

12

20

Challenge

The shortest program by character count that accepts standard input of the form X-Y R, with the following guarantees:

  • R is a non-negative decimal number less than or equal to 8
  • X and Y are non-negative angles given in decimal as multiples of 45° (0, 45, 90, 135, etc.)
  • X is less than Y
  • Y is not 360 if X is 0

And produces on standard output an ASCII "arc" from the starting angle X to the ending angle Y of radius R, where:

  • The vertex of the arc is represented by o
  • Angles of 0 and 180 are represented by -
  • Angles of 45 and 225 are represented by /
  • Angles of 90 and 270 are represented by |
  • Angles of 135 and 315 are represented by \
  • The polygonal area enclosed by the two lines is filled with a non-whitespace character.

The program is not required to produce meaningful output if given invalid input. Solutions in any language are allowed, except of course a language written specifically for this challenge, or one that makes unfair use of an external utility. Extraneous horizontal and vertical whitespace is allowed in the output provided that the format of the output remains correct.

Happy golfing!

Numerous Examples

Input:

0-45 8

Output:

        /
       /x
      /xx
     /xxx
    /xxxx
   /xxxxx
  /xxxxxx
 /xxxxxxx
o--------

Input:

0-135 4

Output:

\xxxxxxxx
 \xxxxxxx
  \xxxxxx
   \xxxxx
    o----

Input:

180-360 2

Output:

--o--
xxxxx
xxxxx

Input:

45-90 0

Output:

o

Input:

0-315 2

Output:

xxxxx
xxxxx
xxo--
xxx\
xxxx\
Strangles answered 29/7, 2010 at 17:48 Comment(7)
If the angle is greater than 180, do we fill the angle or it's supplement? For instance what should "0-315 2" look like? "The polygonal area enclosed by the two lines" is not specific enough.Feebleminded
@Tryptich: Even if the difference between the starting and ending angles equals or exceeds 180 degrees, the region from the starting angle up to the ending angle should be filled. So 0-315 2 is (to use the technical terms) a Pac-Man, not a pie slice. See example in edit.Strangles
Seeing the title I thought the goal is to write an Arc interpreter... that would actually be in spirit with Paul Graham's "Shorter is better"!Grano
Where do you all come up with these crazy ideas from? :)Ingram
Shouldn't code golf questions be rosetta-stone and language-agnostic?Setzer
@Setzer Badia rosetta-stone, language-agnostic and bad-practices :)Bauble
Upvoted for using the proper technical term, Pac-Man.Featheredge
P
15

Perl, 235 211 225 211 207 196 179 177 175 168 160 156 146 chars

<>=~/-\d+/;for$y(@a=-$'..$'){print+(map$_|$y?!($t=8*($y>0)+atan2(-$y,$_)/atan2 1,1)&-$&/45==8|$t>=$`/45&$t<=-$&/45?qw(- / | \\)[$t%4]:$":o,@a),$/}


Perl using say feature, 161 149 139 chars

$ echo -n '<>=~/-\d+/;for$y(@a=-$'"'"'..$'"'"'){say map$_|$y?!($t=8*($y>0)+atan2(-$y,$_)/atan2 1,1)&-$&/45==8|$t>=$`/45&$t<=-$&/45?qw(- / | \\)[$t%4]:$":o,@a}' | wc -c
139
$ perl -E '<>=~/-\d+/;for$y(@a=-$'"'"'..$'"'"'){say map$_|$y?!($t=8*($y>0)+atan2(-$y,$_)/atan2 1,1)&-$&/45==8|$t>=$`/45&$t<=-$&/45?qw(- / | \\)[$t%4]:$":o,@a}'


Perl without trailing newline, 153 143 chars

<>=~/-\d+/;for$y(@a=-$'..$'){print$/,map$_|$y?!($t=8*($y>0)+atan2(-$y,$_)/atan2 1,1)&-$&/45==8|$t>=$`/45&$t<=-$&/45?qw(- / | \\)[$t%4]:$":o,@a}


Original version commented:

$_=<>;m/(\d+)-(\d+) (\d+)/;$e=$1/45;$f=$2/45; # parse angles and radius, angles are 0-8
for$y(-$3..$3){                               # loop for each row and col
    for$x(-$3..$3){
            $t=atan2(-$y,$x)/atan2 1,1;   # angle of this point
            $t+=8if($t<0);                # normalize negative angles
            @w=split//,"-/|\\"x2;         # array of ASCII symbols for enclosing lines
            $s.=!$x&&!$y?"o":$t==$e||$t==$f?$w[$t]:$t>$e&&$t<$f?"x":$";
            # if it's origin -> "o", if it's enclosing line, get symbol from array
            # if it's between enclosing angles "x", otherwise space
    }
    $s.=$/;
}
print$s;


EDIT 1: Inlined sub, relational and equality operators return 0 or 1.
EDIT 2: Added version with comments.
EDIT 3: Fixed enclosing line at 360º. Char count increased significantly.
EDIT 4: Added a shorter version, bending the rules.
EDIT 5: Smarter fix for the 360º enclosing line. Also, use a number as fill. Both things were obvious. Meh, I should sleep more :/
EDIT 6: Removed unneeded m from match operator. Removed some semicolons.
EDIT 7: Smarter regexp. Under 200 chars!
EDIT 8: Lots of small improvements:

  • Inner for loop -> map (1 char)
  • symbol array from split string -> qw (3 chars)
  • inlined symbol array (6 chars, together with the previous improvement 9 chars!)
  • Logical or -> bitwise or (1 char)
  • Regexp improvement (1 char)
  • Use arithmethic for testing negative angles, inspired by Jacob's answer (5 chars)


EDIT 9: A little reordering in the conditional operators saves 2 chars.
EDIT 10: Use barewords for characters.
EDIT 11: Moved print inside of loop, inspired by Lowjacker's answer.
EDIT 12: Added version using say.
EDIT 13: Reuse angles characters for fill character, as Gwell's answer does. Output isn't as nice as Gwell's though, that would require 5 additional chars :) Also, .. operator doen't need parentheses.
EDIT 14: Apply regex directly to <>. Assign range operator to a variable, as per Adrian's suggestion to bta's answer. Add version without the final newline. Updated say version.
EDIT 15: More inlining. map{block}@a -> map expr,@a.

Plasmasol answered 29/7, 2010 at 17:48 Comment(2)
I meant I do not draw correctly the enclosing line if it's at 360º.Plasmasol
Right, I had responded just before testing it; that's why my comment is removed. Good job getting it under 200! :)Strangles
C
12

Lua, 259 characters

Slightly abuses the non-whitespace character clause to produce a dazzling display and more importantly save strokes.

m=math i=io.read():gmatch("%d+")a=i()/45 b=i()/45 r=i()for y=r,-r,-1 do for x=-r,r do c=m.atan2(y,x)/m.pi*4 c=c<0 and c+8 or c k=1+m.modf(c+.5)io.write(x==0 and y==0 and'o'or c>=a and c<=b and('-/|\\-/|\\-'):sub(k,k)or c==0 and b==8 and'-'or' ')end print()end

Input: 45-360 4

\\\|||///
\\\|||// 
\\\\|//  
--\\|/   
----o----
--//|\\--
////|\\\\
///|||\\\
///|||\\\

Able to handle odd angles

Input: 15-75 8

           |/////
          |//////
          |//////
          |//////
          ///////
         |//////-
         ////--- 
         //-     
        o        
                 
                 
                 
                 
                 
                 
                 
                 
Custommade answered 29/7, 2010 at 17:48 Comment(1)
Very nice! I was waiting for someone to take advantage of that wording. The support for non-multiple-of-45-degree angles is great, too, though mine and perhaps others at least degrade gracefully.Strangles
H
8

Mathematica 100 Chars

Out of competition because graphics are too perfect :)

  f[x_-y_ z_]:=Graphics@Table[
                 {EdgeForm@Red,Disk[{0,0},r,{x °,y °}],{r,z,1,-1}]
                 SetAttributes[f,HoldAll]

Invoke with f[30-70 5]

Result

alt text http://a.imageshack.us/img80/4294/angulosgolf.png

alt text http://a.imageshack.us/img59/7892/angulos2.png

Note

The SetAttributes[f, HoldAll];

is needed because the input

    f[a-b c] 

is otherwise interpreted as

    f[(a-b*c)]
Heptachord answered 29/7, 2010 at 17:48 Comment(2)
@Alessandro Stamatto That was for the sake of sane competition ... and before someone complains that there is no "o" at the vertex :) ... Auguri!Bauble
@Gabe Lol ... I have the output here en.wikipedia.org/wiki/File:Diophantus-II-8-Fermat.jpg ... but it's too large for the margin of this site :)Bauble
C
8

MATLAB, 188 chars :)

input '';[w x r]=strread(ans,'%d-%d%d');l='-/|\-/|\-';[X Y]=meshgrid(-r:r);T=atan2(-Y,X)/pi*180;T=T+(T<=0)*360;T(T>w&T<x)=-42;T(T==w)=-l(1+w/45);T(T==x)=-l(1+x/45);T(r+1,r+1)=-'o';char(-T)

Commented code:

%%# Get the string variable (enclose in quotes, e.g. '45-315 4')
input ''
%%# Extract angles and length
[w x r]=strread(ans,'%d-%d%d');
%%# Store characters
l='-/|\-/|\-';
%%# Create the grid
[X Y]=meshgrid(-r:r);
%%# Compute the angles in degrees
T=atan2(-Y,X)/pi*180;
%%# Get all the angles
T=T+(T<=0)*360;
%# Negative numbers indicate valid characters
%%# Add the characters
T(T>w&T<x)=-42;
T(T==w)=-l(1+w/45);
T(T==x)=-l(1+x/45);
%%# Add the origin
T(r+1,r+1)=-'o';
%%# Display
char(-T)
Cateran answered 29/7, 2010 at 17:48 Comment(13)
@Jon- I've wondered the same thing. Given how much space you save using MATLAB's vector operations, you'd think you would see it more often.Sexuality
@Bta/John: True, but Perl has an insane advantage with those chained ternary conditionals or whatever they're called --- it's like they're built for golf!Cateran
If only the problem didn't require ASCII output, this could be cut down even more if a MATLAB plot would suffice.Sexuality
input evaluates the text you give it unless you specify a second parameter, 's'. So you need input '' s; But I think you should change T=T+(T<=0)*360 to T=mod(T,360), which would give you your two characters back :-)Nyctalopia
@SCFrench: I think asking the user to add quotes to the input string is not unreasonable :)Cateran
Any idea for condensing T(T==w) ..?Cateran
@SCFrench: Also the mod idea is great but I need to set T(T==0) to 360 as well to solve the enclosing line problem when Y=360 (or x in my code)Cateran
I can accept quoting the input string. But unfortunately when I try your code with the 0-45 8 case, I don't see the bottom line of hyphens, I think because w is 0 but the corresponding values in T are 360. You might need to mod both w and T with 360 to get both cases to work.Nyctalopia
I spent a long time last night looking for ways to condense your code further but I couldn't find anything else. You've got some pretty tight MATLAB code there.Nyctalopia
I managed to save three chars: try input '';[w x r]=strread(ans,'%d-%d%d');l='-/|\-/|\-';[X Y]=meshgrid(r:-1:-r,-r:r);T=180+atan2(Y,X)*180/pi;T(T>w&T<x)=-42;T(T==w)=-l(1+w/45);T(T==x)=-l(1+x/45);T(r+1,r+1)=-'o';char(-T) this would build the same matrix T (except the center value which doesn't really matter since its always replaced with 'o')Sarre
you can save one additional character if capital 'O' is used instead of small 'o': T(r+1,r+1)=-79Sarre
Couldn't help but try this myself. I trimmed a MATLAB solution down to 168 characters, and I still think I could get more.Tupper
input '';[w x r]=strread(ans,'%d-%d%d'); can become [w x r]=strread(input(''),'%d-%d %d'); to save 2 characters.Jemima
T
7

MATLAB 7.8.0 (R2009a) - 168 163 162 characters

Starting from Jacob's answer and inspired by gwell's use of any non-whitespace character to fill the arc, I managed the following solution:

[w x r]=strread(input('','s'),'%d-%d%d');
l='o -/|\-/|\-';
X=meshgrid(-r:r);
T=atan2(-X',X)*180/pi;
T=T+(T<=-~w)*360;
T(T>x|T<w)=-1;
T(r+1,r+1)=-90;
disp(l(fix(3+T/45)))

And some test output:

>> arc
0-135 4
\||||////
 \|||///-
  \||//--
   \|/---
    o----

I could reduce it further to 156 characters by removing the call to disp, but this would add an extra ans = preceding the output (which might violate the output formatting rules).

Even still, I feel like there are some ways to reduce this further. ;)

Tupper answered 29/7, 2010 at 17:48 Comment(5)
Damn. That is some tight MATLAB, sir. And rather readable, I might add.Strangles
T=atan2(-X',X)*180/pi;T=T+(T<=-~w)*360; can become T=180*(atan2(-X',X)/pi+(X'>=0)*2); to save 5 characters.Jemima
@Geoff: That looks like it has definite promise. I'll have to try it out tomorrow.Tupper
@gnovice: You could write the script as a function that is invoked f(w,x,r) to save a few characters from the first line.Orpington
If equality is not an issue in this part: T(T>x|T<w) you can save 1 more by using %d%d%d combined with T(T<x|T<w).Ayotte
P
7

GNU BC, 339 chars

Gnu bc because of read(), else and logical operators.

scale=A
a=read()/45
b=read()/45
c=read()
for(y=c;y>=-c;y--){for(x=-c;x<=c;x++){if(x==0)if(y<0)t=-2else t=2else if(x>0)t=a(y/x)/a(1)else if(y<0)t=a(y/x)/a(1)-4else t=a(y/x)/a(1)+4
if(y<0)t+=8
if(x||y)if(t==a||t==b||t==b-8){scale=0;u=(t%4);scale=A;if(u==0)"-";if(u==1)"/";if(u==2)"|";if(u==3)"\"}else if(t>a&&t<b)"x"else" "else"o"};"
"}
quit
Plasmasol answered 29/7, 2010 at 17:48 Comment(1)
Nice! I would never have thought of using 'bc'.Sexuality
S
6

Ruby, 292 276 186 chars

x,y,r=gets.scan(/\d+/).map{|z|z.to_i};s=(-r..r);s.each{|a|s.each{|b|g=Math::atan2(-a,b)/Math::PI*180/1%360;print a|b==0?'o':g==x||g==y%360?'-/|\\'[g/45%4].chr: (x..y)===g ?'*':' '};puts}

Nicer-formatted version:

x, y, r = gets.scan(/\d+/).map{|z| z.to_i}
s = (-r..r)
s.each {|a|
    s.each {|b|
        g = (((Math::atan2(-a,b) / Math::PI) * 180) / 1) % 360
        print ((a | b) == 0) ? 'o' :
            (g == x || g == (y % 360)) ? '-/|\\'[(g / 45) % 4].chr :
                ((x..y) === g) ? '*' : ' '
    }
    puts
}

I'm sure someone out there who got more sleep than I did can condense this more...

Edit 1: Switched if statements in inner loop to nested ? : operator

Edit 2: Stored range to intermediate variable (thanks Adrian), used stdin instead of CLI params (thanks for the clarification Jon), eliminated array in favor of direct output, fixed bug where an ending angle of 360 wouldn't display a line, removed some un-needed parentheses, used division for rounding instead of .round, used modulo instead of conditional add

Sexuality answered 29/7, 2010 at 17:48 Comment(4)
Nice! Should probably accept parameters from standard input instead of ARGV, but I'm not picky.Strangles
You could save three characters by assigning -r..r to a variable.Piercing
I definitely can't compete with lowjacker's answer. Some of those Ruby 1.9 changes are very useful here.Sexuality
You could also save characters if you changed s=(-r..r) to s=-r..r.Piercing
A
4

Ruby, 168 characters

Requires Ruby 1.9 to work

s,e,r=gets.scan(/\d+/).map &:to_i;s/=45;e/=45;G=-r..r;G.map{|y|G.map{|x|a=Math.atan2(-y,x)/Math::PI*4%8;print x|y!=0?a==s||a==e%8?'-/|\\'[a%4]:a<s||a>e ?' ':8:?o};puts}

Readable version:

start, _end, radius = gets.scan(/\d+/).map &:to_i
start /= 45
_end /= 45

(-radius..radius).each {|y|
    (-radius..radius).each {|x|
        angle = Math.atan2(-y, x)/Math::PI * 4 % 8
        print x|y != 0 ? angle==start || angle==_end%8 ? '-/|\\'[angle%4] : angle<start || angle>_end ? ' ' : 8 : ?o
    }
    puts
}
Abampere answered 29/7, 2010 at 17:48 Comment(0)
E
3

C# - 325 319 chars

using System;class P{static void Main(){var s=Console.ReadLine().Split(' ');
var d=s[0].Split('-');int l=s[1][0]-48,x,y,r,a=int.Parse(d[0]),b=int.Parse(d[1]);
for(y=l;y>=-l;y--)for(x=-l;x<=l;)Console.Write((x==0&&y==0?'o':a<=(r=((int)
(Math.Atan2(y,x)*57.3)+360)%360)&&r<b||r==b%360?
@"-/|\"[r/45%4]:' ')+(x++==l?"\n":""));}}

Newlines not significant.

Sample input/output

45-180 8
\||||||||////////
\\|||||||///////
\\\||||||//////
\\\\|||||/////
\\\\\||||////
\\\\\\|||///
\\\\\\\||//
\\\\\\\\|/
--------o
135-360 5
\
\\
\\\
\\\\
\\\\\
-----o-----
----/|\\\\\
---//||\\\\
--///|||\\\
-////||||\\
/////|||||\
Expertism answered 29/7, 2010 at 17:48 Comment(2)
+1 for showing that anything can take as long to read as Perl if you just remove all the whitespace.Sublapsarianism
Replacing 3.14*180 with the result is like saying (3.14/180), that is, you should take *(3.14/180) instead.Strangles
S
3

Perl - 388 characters

Since it wouldn't be fair to pose a challenge I couldn't solve myself, here's a solution that uses string substitution instead of trigonometric functions, and making heavy use of your friendly neighbourhood Perl's ability to treat barewords as strings. It's necessarily a little long, but perhaps interesting for the sake of uniqueness:

($x,$y,$r)=split/\D/,<>;for(0..$r-1){$t=$r-1-$_;
$a.=L x$_.D.K x$t.C.J x$t.B.I x$_."\n";
$b.=M x$t.F.N x$_.G.O x$_.H.P x$t."\n"}
$_=$a.E x$r.o.A x$r."\n".$b;$x/=45;$y/=45;$S=' ';
sub A{$v=$_[0];$x==$v||$y==$v?$_[1]:$x<$v&&$y>$v?x:$S}
sub B{$x<=$_[0]&&$y>$_[0]?x:$S}
@a=!$x||$y==8?'-':$S;
push@a,map{A$_,'\\'.qw(- / | \\)[$_%4]}1..7;
push@a,!$x?x:$S,map{B$_}1..7;
eval"y/A-P/".(join'',@a)."/";print

All newlines are optional. It's fairly straightforward:

  • Grab user input.
  • Build the top ($a) and bottom ($b) parts of the pattern.
  • Build the complete pattern ($_).
  • Define a sub A to get the fill character for an angle.
  • Define a sub B to get the fill character for a region.
  • Build an array (@a) of substitution characters using A and B.
  • Perform the substitution and print the results.

The generated format looks like this, for R = 4:

DKKKCJJJB
LDKKCJJBI
LLDKCJBII
LLLDCBIII
EEEEoAAAA
MMMFGHPPP
MMFNGOHPP
MFNNGOOHP
FNNNGOOOH

Where A-H denote angles and I-P denote regions.

(Admittedly, this could probably be golfed further. The operations on @a gave me incorrect output when written as one list, presumably having something to do with how map plays with $_.)

Strangles answered 29/7, 2010 at 17:48 Comment(0)
E
2

C (902 byte)

This doesn't use trigonometric functions (like the original perl version), so it's quite ``bloated''. Anyway, here is my first code-golf submission:

#define V(r) (4*r*r+6*r+3)
#define F for(i=0;i<r;i++)
#define C ;break;case
#define U p-=2*r+2,
#define D p+=2*r+2,
#define R *++p=
#define L *--p=
#define H *p='|';
#define E else if
#define G(a) for(j=0;j<V(r)-1;j++)if(f[j]==i+'0')f[j]=a;
#define O(i) for(i=0;i<2*r+1;i++){
main(int i,char**v){char*p,f[V(8)];
int j,m,e,s,x,y,r;p=*++v;x=atoi(p);while(*p!=45)p++;
char*h="0123";y=atoi(p+1);r=atoi(*++v);
for(p=f+2*r+1;p<f+V(r);p+=2*r+2)*p=10;
*(p-2*r-2)=0;x=x?x/45:x;y/=45;s=0;e=2*r;m=r;p=f;O(i)O(j)
if(j>e)*p=h[0];E(j>m)*p=h[1];E(j>s)*p=h[2];else*p=h[3];p++;}
if(i+1==r){h="7654";m--;e--;}E(i==r){s--;}E(i>r){s--;e++;}
else{s++;e--;}p++;}for(p=f+V(r)/2-1,i=0;i<r;i++)*++p=48;
for(i=0;i<8;i++)if(i>=x&&i<y){G(64);}else G(32);
y=y==8?0:y;q:p=f+V(r)/2-1;*p='o';switch(x){
C 0:F R 45 C 1:F U R 47 C 2:F U H C 3:F U L 92
C 4:F L 45 C 5:F D L 47 C 6:F D H C 7:F D R 92;}
if(y!=8){x=y;y=8;goto q;}puts(f);}

also, the #defines look rather ugly, but they save about 200 bytes so I kept them in, anyway. It is valid ANSI C89/C90 and compiles with very few warnings (two about atoi and puts and two about crippled form of main).

Echikson answered 29/7, 2010 at 17:48 Comment(0)
P
2

Java - 304 chars


class A{public static void main(String[]a){String[]b=a[0].split("-");int e=new Integer(b[1]),r=new Integer(a[1]),g,x,y=r;for(;y>=-r;y--)for(x=-r;x<=r;)System.out.print((x==0&y==0?'o':new Integer(b[0])<=(g=((int)(Math.atan2(y,x)*57.3)+360)%360)&g<e|g==e%360?"-/|\\".charAt(g/45%4):' ')+(x++<r?"":"\n"));}}

More readable version:

class A{
 public static void main(String[]a){
  String[]b=a[0].split("-");
  int e=new Integer(b[1]),r=new Integer(a[1]),g,x,y=r;
  for(;y>=-r;y--)for(x=-r;x<=r;)System.out.print((
   x==0&y==0
    ?'o'
    :new Integer(b[0])<=(g=((int)(Math.atan2(y,x)*57.3)+360)%360)&g<e|g==e%360
     ?"-/|\\".charAt(g/45%4)
     :' '
   )+(x++<r?"":"\n"));
 }
}
Phosphoric answered 29/7, 2010 at 17:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.