First, study this puzzle to get a feel for what you will be producing.
Your challenge is to write a program or function which will output a circular graphic like the ones from the puzzle, given a (base 10) number between 1 and 100 (inclusive). This is similar to this challenge, except that you will be producing a graphic rather than roman numerals. The following circles represent the numbers 1-10, from left to right:
circle pattern
As the answer to the puzzle states, your graphic should read like a roman numeral from the inside-out, where the line thickness represents the roman numeral symbols and the entire graphic represents the number. For your reference, here are the line thicknesses which you will need. Each line should have a 3px padding between it and the next.
Number Roman Numeral Line Width
1 I 1px
5 V 3px
10 X 5px
50 L 7px
100 C 9px
Please post a sample or two of your output. Assume input is correct, standard loopholes, etc and so forth. This is code golf, so fewest bytes win. In the event of a tie, most votes win. Good luck!
-
3\$\begingroup\$ Is the correct absolute size of the image necessary, or is it sufficient to have the right relative sizes? \$\endgroup\$David Zhang– David Zhang2014年11月26日 07:43:19 +00:00Commented Nov 26, 2014 at 7:43
-
\$\begingroup\$ @DavidZhang Yes, please stick to the line and padding sizes that I listed, for fairness sake. \$\endgroup\$Rip Leeb– Rip Leeb2014年11月26日 12:38:36 +00:00Commented Nov 26, 2014 at 12:38
7 Answers 7
Mathematica - (削除) 166 (削除ここまで) 181 bytes
A bit more concise than the other Mathematica answer, thanks in part to a more point-free style.
c = Characters; r = Riffle;
Graphics[r[{0, 0}~Disk~# & /@ Reverse@Accumulate[
l = {3} ~Join~ r[2 Position[c@"IVXLC", #][[1, 1]] - 1 & /@
c@IntegerString[#, "Roman"], 3]], {White, Black}],
ImageSize -> 2 Total@l] &
All whitespace is for clarity only. This defines an anonymous function which returns the desired graphic.
Animation
Animated circles
Generating an animated GIF of the number circles is trivial in Mathematica, which has built-in functions for animating and exporting sequences of arbitrary objects. Assuming the above code has just been executed,
Table[Show[%@n, PlotRange -> {{-100, 100}, {-100, 100}},
ImageSize -> 200], {n, 1, 399, 1}];
Export["animcircles.gif", %]
Example Output
Example output
-
\$\begingroup\$ Please post a few results. Sorry for not asking this is the first place. I've also changed the question to accept functions. \$\endgroup\$Rip Leeb– Rip Leeb2014年11月26日 12:40:06 +00:00Commented Nov 26, 2014 at 12:40
-
\$\begingroup\$ Thanks for the suggestions @MartinBüttner. The code has been fixed to output correctly-sized images, and example output has been added. \$\endgroup\$David Zhang– David Zhang2014年11月26日 17:17:00 +00:00Commented Nov 26, 2014 at 17:17
-
3\$\begingroup\$ Your animation wiggles. Not that I could do better. \$\endgroup\$corsiKa– corsiKa2014年11月27日 20:35:24 +00:00Commented Nov 27, 2014 at 20:35
-
\$\begingroup\$ Hmm, you're right. I'm really not sure why it does that, considering I specified the plot range explicitly for Mathematica. \$\endgroup\$David Zhang– David Zhang2014年11月28日 19:24:29 +00:00Commented Nov 28, 2014 at 19:24
-
\$\begingroup\$ Maybe related to the wiggling: mathematica.stackexchange.com/q/134272 \$\endgroup\$coredump– coredump2016年12月26日 15:42:04 +00:00Commented Dec 26, 2016 at 15:42
Common Lisp - (削除) 376 (削除ここまで) (削除) 331 (削除ここまで) 304 bytes
(use-package(car(ql:quickload'vecto)))(lambda(n o &aux(r 3)l p)(map()(lambda(c)(setf l(position c" I V X L C D M")p(append`((set-line-width,l)(centered-circle-path 0 0,(+(/ l 2)r))(stroke))p)r(+ r l 3)))(format()"~@R"n))(with-canvas(:width(* 2 r):height(* 2 r))(translate r r)(map()'eval p)(save-png o)))
Examples
enter image description here (1) enter image description here (24)
enter image description here (104) enter image description here (1903) enter image description here (3999)
Animation
For numbers from 1 to 400:
New
NB: For the record, this animation is done as follows:
I have a modified version of the code, named rings which returns the width of the produced image.
Hence, the result of the the following loop is the maximum size, here 182:
(loop for x from 1 to 400
maximize (rings x (format nil "/tmp/rings/ring~3,'0d.png" x)))
The whole loop takes 9.573 seconds. That gives approximately 24ms for each integer. Then, in a shell:
convert -delay 5 -loop 0 -gravity center -extent 182x182 ring*png anim.gif
Ungolfed
(ql:quickload :vecto)
(use-package :vecto)
(lambda (n o)
(loop with r = 3
for c across (format nil "~@R" n)
for l = (1+ (* 2(position c"IVXLCDM")))
for h = (/ l 2)
collect `(,(incf r h),l) into p
do (incf r (+ h 3))
finally (with-canvas(:width (* 2 r) :height (* 2 r))
(loop for (x y) in p
do (set-line-width y)
(centered-circle-path r r x)
(stroke))
(save-png o))))
Explanations
The function takes an integer
Nbetween 1 and 3999 and a filenameI use
(format nil "~@R" N)to convert from decimal to roman. For example:(format nil "~@R" 34) => "XXXIV"The
~@Rformat control string is specified to work for integers between 1 and 3999. That's why there is a limitation for the range of allowed inputs.I iterate over the resulting string to build a list
Pcontaining(radius width)couples, for each numeral C.The width is a simple linear mapping: I use the constant string "IVXLCDM" to compute the position of C in it. Multiplying by two and adding one, we obtain the desired value:
(1+ (* 2 (position c "IVXLCDM")))This is however done slightly differently in the golfed version:
(position c " I V X L C D M")The computation of each radius takes into account the width of each ring as well as empty spaces between rings. Without any speed optimization, computations remain precise because they are not based on floats, but rational numbers.
Edit: I changed the parameters to comply with the padding rules.
Once this is done, I know the required size of the resulting canvas (twice the latest computed radius).
- Finally, I draw a circle for each element of
Pand save the canvas.
-
1\$\begingroup\$ "This code supports all roman numerals (IVXLCDM)". Does that mean your program takes roman numerals as input? That's not what I intended, but pretty cool. Props for the animation too. \$\endgroup\$Rip Leeb– Rip Leeb2014年11月26日 12:45:41 +00:00Commented Nov 26, 2014 at 12:45
-
1\$\begingroup\$ No, no, sorry if this is unclear: it works for any integer between 1 and 3999. In your question, you only required inputs from 1 to 100, and your table does not mention D or M... I'll edit that part. \$\endgroup\$coredump– coredump2014年11月26日 12:51:48 +00:00Commented Nov 26, 2014 at 12:51
HTML+JQuery, 288
HTML
<canvas>
JS
r=3;w=9;c=$('canvas').get(0).getContext('2d')
for(i=prompt(),e=100;e-.1;e/=10){
if((x=Math.floor(i/e)%10)==4)d(w)+d(w+2)
else if(x==9)d(w)+d(w+4)
else{if(x>4)d(w+2)
for(j=x%5;j;j--)d(w)}
w-=4}
function d(R){c.lineWidth=R
c.beginPath()
c.arc(150,75,r+=R/2,0,7)
c.stroke()
r+=R/2+3}
r=3;w=9;c=$('canvas').get(0).getContext('2d')
for(i=prompt(),e=100;e-.1;e/=10){
if((x=Math.floor(i/e)%10)==4)d(w)+d(w+2)
else if(x==9)d(w)+d(w+4)
else{if(x>4)d(w+2)
for(j=x%5;j;j--)d(w)}
w-=4}
function d(R){c.lineWidth=R
c.beginPath()
c.arc(150,75,r+=R/2,0,7)
c.stroke()
r+=R/2+3}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.0/jquery.min.js"></script>
<canvas>
Java, 565
import java.awt.*;class Z{public static void main(String[]s){int i=new Byte(s[0]),j=i/10,k=i%10;String t="",u;if(j>8)t="59";if(j>9)t="9";if(j==4)t="57";else if(j<9){t=j>4?"7":"";j-=j>4?5:0;if(j>0)t+="5";if(j>1)t+="5";if(j>2)t+="5";}if(k>8)t+="15";if(k==4)t+="13";else if(k<9){t+=k>4?"3":"";k-=k>4?5:0;if(k>0)t+="1";if(k>1)t+="1";if(k>2)t+="1";}u=t;Frame f=new Frame(){public void paint(Graphics g){g.setColor(Color.BLACK);int x=0;for(char c:u.toCharArray()){int z=c-48,q=x;for(;x<q+z;)g.drawOval(99-x,99-x,x*2,x++*2);x+=3;}}};f.setSize(200,200);f.setVisible(1>0);}}
Examples
15
15
84
84
93
93
Formatted nicely:
import java.awt.*;
class Z {
public static void main(String[] s) {
int i = new Byte(s[0]), j = i / 10, k = i % 10;
String t = "", u;
if (j > 8)
t = "59";
if (j > 9)
t = "9";
if (j == 4) {
t = "57";
} else if (j < 9) {
t = j > 4 ? "7" : "";
j -= j > 4 ? 5 : 0;
if (j > 0)
t += "5";
if (j > 1)
t += "5";
if (j > 2)
t += "5";
}
if (k > 8)
t += "15";
if (k == 4) {
t += "13";
} else if (k < 9) {
t += k > 4 ? "3" : "";
k -= k > 4 ? 5 : 0;
if (k > 0)
t += "1";
if (k > 1)
t += "1";
if (k > 2)
t += "1";
}
u = t;
Frame f = new Frame() {
public void paint(Graphics g) {
g.setColor(Color.BLACK);
int x = 0;
for (char c : u.toCharArray()) {
int z = c - 48, q = x;
for (; x < q + z;) {
g.drawOval(99 - x, 99 - x, x * 2, x++ * 2);
}
x += 3;
}
}
};
f.setSize(200, 200);
f.setVisible(1 > 0);
}
}
-
\$\begingroup\$ Please post a few results. Sorry for not asking this is the first place. \$\endgroup\$Rip Leeb– Rip Leeb2014年11月26日 12:39:44 +00:00Commented Nov 26, 2014 at 12:39
Mathematica 9 - (削除) 301 (削除ここまで) 249 bytes
:D It feels cheaty to use the built-in converting to Roman numerals, but hey.
l=Length;k=Characters;r@n_:=(w=Flatten[Position[k@"IVXLC",#]*2-1&/@k@IntegerString[n,"Roman"]];Show[Table[Graphics@{AbsoluteThickness@w[[i]],Circle[{0,0},(Join[{0},Accumulate[3+w]]+3)[[i]]+w[[i]]/2]},{i,Range@l@w}],ImageSize->{(Total@w+(l@w)*3)*2}])
(When I did this last night I didn't have much time, but I realized it could be golfed down way more. And I also took some hints from David Zhang... :D Thanks!)
A bit more clearly:
l=Length;
k=Characters;
r@n_:=
(
w=Flatten[Position[k@"IVXLC",#]*2-1&/@k@IntegerString[n,"Roman"]];
Show[Table[Graphics@{AbsoluteThickness@w[[i]],Circle[{0,0},(Join[{0},Accumulate[3+w]]+3)[[i]]+w[[i]]/2]},{i,Range@l@w}],ImageSize->{(Total@w+(l@w)*3)*2}]
)
This is a function that you can call like this:
r[144]
enter image description here
Or, you can show the results from values a to b with: Table[r[i],{i,a,b}]
Note: This only works for values up to 399.
Python 2, (削除) 322 (削除ここまで) 296
The script reads the number to be converted from stdin, and outputs the image as SVG markup.
.. I use 'red' instead of 'black', because it saves 2 chars :)
Here are some samples : for 23 : http://jsfiddle.net/39xmpq49/ for 42 : http://jsfiddle.net/7Ls24q9e/1/
i=input();r=9
def R(n,p):
global r,i;i-=n;print '<circle r="{0}" stroke-width="{1}"/>'.format(r,p);r+=p+3
print '<svg viewBox="-50 -50 99 99" fill="none" stroke="red">',[[R(n,int(p)) for p in s*int(i/n)] for n,s in zip([100,90,50,40,10,9,5,4,1],'9/59/7/57/5/15/3/13/1'.split('/'))]and'','</svg>'
JavaScript (削除) 342 (削除ここまで) (削除) 334 (削除ここまで) 308
function R(n){var v=document,o=[],x=1,c=v.body.appendChild(v.createElement('canvas')).getContext('2d')
while(n)v=n%10,y=x+2,o=[[],[x],[x,x],[x,x,x],[x,y],[y],[y,x],[y,x,x],[y,x,x,x],[x,x+=4]][v].concat(o),n=(n-v)/10
v=3
while(x=o.shift())c.lineWidth=x,c.beginPath(),c.arc(150,75,v+x/2,0,7),c.stroke(),v+=x+3}
for (var i = 1; i <= 100; i++) {
R(i);
}
Explore related questions
See similar questions with these tags.