36
\$\begingroup\$

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!

\$\endgroup\$
  • 3
    \$\begingroup\$ Is the correct absolute size of the image necessary, or is it sufficient to have the right relative sizes? \$\endgroup\$ – David Zhang Nov 26 '14 at 7:43
  • \$\begingroup\$ @DavidZhang Yes, please stick to the line and padding sizes that I listed, for fairness sake. \$\endgroup\$ – Rip Leeb Nov 26 '14 at 12:38
15
\$\begingroup\$

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

\$\endgroup\$
  • \$\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 Nov 26 '14 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 Nov 26 '14 at 17:17
  • 3
    \$\begingroup\$ Your animation wiggles. Not that I could do better. \$\endgroup\$ – corsiKa Nov 27 '14 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 Nov 28 '14 at 19:24
  • \$\begingroup\$ Maybe related to the wiggling: mathematica.stackexchange.com/q/134272 \$\endgroup\$ – coredump Dec 26 '16 at 15:42
15
\$\begingroup\$

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 N between 1 and 3999 and a filename

  • I use (format nil "~@R" N) to convert from decimal to roman. For example:

     (format nil "~@R" 34) => "XXXIV"
    

    The ~@R format 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 P containing (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 P and save the canvas.
\$\endgroup\$
  • 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 Nov 26 '14 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 Nov 26 '14 at 12:51
8
\$\begingroup\$

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>

Fiddle

\$\endgroup\$
  • \$\begingroup\$ No stack snippet ? \$\endgroup\$ – Optimizer Nov 26 '14 at 13:27
  • \$\begingroup\$ @Optimizer Totally forgot we have that now \$\endgroup\$ – TwiNight Nov 26 '14 at 13:38
5
\$\begingroup\$

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);
    }
}
\$\endgroup\$
  • \$\begingroup\$ Please post a few results. Sorry for not asking this is the first place. \$\endgroup\$ – Rip Leeb Nov 26 '14 at 12:39
3
\$\begingroup\$

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.

\$\endgroup\$
1
\$\begingroup\$

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>'
\$\endgroup\$
1
\$\begingroup\$

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);
}

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.