6
\$\begingroup\$

A centered polygonal number is able to be put into an arrangement of multiple concentric polygons, each with a side length of one more than the inner one. For example (image from linked page), enter image description here

the above image has 31 dots, so 31 is a centered pentagonal number.

Write a program which receives a number as input (any typical input method is fine), and displays a picture similar to the above.

In particular:

  • There must be a centered dot.
  • Each concentric polygon contains an increasing number of dots, all connected through a line.
  • The angles must be correct for the appropriate regular polygon.
  • The distance between connected dots must be the same throughout the picture.
  • Each polygon must be centered inside the surrounding polygon.

The input will be between 2 and 1000, and is guaranteed to be a centered polygonal number of some kind.

If there are multiple possible solutions, display them all. For example, an input of 181 should display both a centered square (of 9 "rings") and a centered pentagon (of 8 "rings").

No hardcoding answers.

\$\endgroup\$
  • 2
    \$\begingroup\$ 181 is also the count of a single-ringed henahectaoctacontagon (i.e. a 180-gon). In fact, any integer x > 3 is the count of a single-ringed (x-1)-gon. 181 is also the count of a two-ringed hexacontagon, a three-ringed triacontagon, a four-ringed octadecagon and a five-ringed dodecagon. (If it's all greek to you, see faculty.kutztown.edu/schaeffe/Tutorials/General/Polygons.html). Some of these, particularly the first one, are very big. \$\endgroup\$ – rici Sep 3 '14 at 4:42
6
\$\begingroup\$

HTML + JS, 913 857 675 bytes

(Line breaks are added for readability)

<body><script>
function O(X,x,y,R){X.beginPath();X.arc(x,y,R,0,M.PI*2,true);X.closePath();X.fill()}
Z=300,P=3,H=Z/2,$=+prompt(),M=Math;for(S=3;S<$;S++){for(C=1;C<30;C++){if(S*C*C+S*C+2==$*2){
V=document.createElement("canvas");V.width=V.height=Z;document.querySelector("body").appendChild(V);X=V.getContext("2d");
O(X,H,H,P);for(R=1;R<=C;R++){
D=[],A=R*(H-P)/C,G=0;X.beginPath();X.moveTo(H+A,H);D.push([H+A,H]);for(s=1;s<S;s++){x=M.cos(G+=M.PI*2/S)*A,y=M.sin(G)*A;X.lineTo(H+x,H+y);D.push([H+x,H+y])}X.closePath();X.stroke();
for(j=0;j<D.length;j++){x=D[j][0],y=D[j][1],q=D[(j+1)%S][0]-x,w=D[(j+1)%S][1]-y;O(X,x,y,P);for(k=0;k<R-1;k++)O(X,x+=q/R,y+=w/R,P)}
}break}}}
</script></body>

This is working jsfiddle example, and ungolfed source. It gets input from prompt(). You can adjust output size by adjusting sz = 300, and dot size by pt = 3 (in pixels).

Example (31 dots)

31_1 31_2 31_3 31_4

\$\endgroup\$
  • \$\begingroup\$ Being code golf, you can cut most of html and still having a working page (doctype, meta, head and so on) \$\endgroup\$ – edc65 Sep 3 '14 at 8:22
  • \$\begingroup\$ body and script are required here, but removing the html tags will save you 13 bytes. \$\endgroup\$ – Sean Latham Sep 3 '14 at 8:52
3
\$\begingroup\$

Python - 189, 258, 251, 249, 239, 222 219

from pylab import*
m=input()-1;R=arange
for j in R(1,m):
 i=2.*m/(j*j+j);f=2*pi/i
 if i%1==0:
  figure();axis('equal')
  for r in R(j+1):o=f*R(i);s=R(r+1)[:,None];t=r-s;plot(t*cos(o)+s*cos(o+f),t*sin(o)+s*sin(o+f),'.-')

Example output for n = 31:

(Not sure if the first one is valid...)

\$\endgroup\$
  • 4
    \$\begingroup\$ This seems to be missing some dots. \$\endgroup\$ – Peter Taylor Sep 3 '14 at 21:56
  • \$\begingroup\$ @PeterTaylor: Oh sorry, I totally forgot to draw the smaller line segments. Fixed it at the cost of 69 characters. :-/ \$\endgroup\$ – Falko Sep 4 '14 at 6:22
2
\$\begingroup\$

HTML5 + JS (378 364 chars)

<script>P='<svg><path stroke=red fill=none d='
C=''
N=prompt()-1
M=Math
for(i=t=1;N>t*2;t+=i)for(r=148/i,j=++i;N%t<1&&j--;)for(A=2*M.PI*t/N,y=150,x=j*r+310*i,P+='M'+x+','+y,a=s=0;s<N;s+=t)for(a+=A,k=-!j;k++<j;P+='L'+x+','+y,C+='<circle fill=red r=2 cx='+x+' cy='+y+' />')if(j)x+=(M.cos(a)-M.cos(a-A))*r,y+=(M.sin(a)-M.sin(a-A))*r
document.write(P+' />'+C)</script>

Online demo. Sorry about the long line, but it cannot be split without increasing the character count. Every semicolon which could be replaced by a newline courtesy of JavaScript's semicolon insertion has been replaced. Every space character is inside a string literal.

As with my previous "drawing in HTML" answer, SVG is slightly shorter than canvas.

Since the question doesn't say anything about positioning, I've saved a couple of characters by not guaranteeing that the diagrams will be evenly spaced.

\$\endgroup\$
  • 1
    \$\begingroup\$ I may be doing something wrong, but the fiddle doesn't seem to work for me. I can enter input numbers (e.g. 31 or 181), but conversely to the other answer, I don't see the output drawn anywhere. I tried with firefox and chrome. \$\endgroup\$ – Digital Trauma Sep 3 '14 at 19:47
  • \$\begingroup\$ @DigitalTrauma, there's about a 330px margin on it. Here's a screenshot taken in an old version of Chrome. (Edit: that's an old version of the fiddle, but the same thing applies). \$\endgroup\$ – Peter Taylor Sep 3 '14 at 20:54
  • \$\begingroup\$ Does the fiddle need updating? Here's what I see \$\endgroup\$ – Digital Trauma Sep 3 '14 at 21:11
  • \$\begingroup\$ Is there any error message in the console? \$\endgroup\$ – Peter Taylor Sep 3 '14 at 21:25
  • \$\begingroup\$ No, the console is empty \$\endgroup\$ – Digital Trauma Sep 3 '14 at 21:54
1
\$\begingroup\$

BBC BASIC, 281 ASCII characters, tokenised filesize 236

Emulator at http://www.bbcbasic.co.uk/bbcwin/bbcwin.html

Newlines are required and included in the score.

  c=400INPUTa:n=1t=0REPEATt+=n:s=(a-1)/t:r%=c/n/2CLS:MOVEc,c:s%=s:IFs%=s u=r%*2:v=0FORp=1TOs%q%=r%*COS(p/s%*2*PI)x=q%*2q%=r%*SIN(p/s%*2*PI)y=q%*2q=FNt(n)u=x:v=y:NEXT:q=GET
  n+=1UNTILs<=3END
  DEFFNt(d)PLOT153,8,0MOVEBY-8,0IFd MOVEBY u,v:q=FNt(d-1)DRAWBY x-u,y-v:q=FNt(d-1)MOVEBY-x,-y
  =0

A centered s-gonal number is of the form (triangular number t[n])*s+1. I take an input a, then iterate through the triangular numbers until a-1 is not greater than 3*t[n]. If s is an integer, we clear the screen and plot the drawing. After plotting, we wait for the user to press a key (the screen will be cleared again for the next drawing if there is one.) To check if s s an integer, we store it in an integer variable s% (integer variables are identified by the % suffix) and compare the two. In this program I use casting to integer variables several times as a means of converting reals to integers.

The polygon is plotted wedge by wedge. The radial vectors for the first two points (u,v) and (x,y) are calculated. All the code for this must be on the same line as the IF statement. Unfortunately the resolution of the plotting of this BBC basic emulator is only to the nearest multiple of 2, so in order to avoid problems we ensure that the vector parameters are multiples of 2 (by storing the values of the trig calculations in an integer variable q% then multiplying by 2. (the vectors themselves are real variables, to save characters.) There is also a variable q which serves only to accept return values of functions (required for syntax reasons.)

In order to avoid trying to do too much in the first IF statement, it is natural to put the plotting operation in a function, and having done that it may as well be recursive. The function plots the wedge as a binary tree with overlapping branches. This is short, but highly inefficent (exponential time in number of rings.) With the largest sizes some points are plotted literally millions of times. The worst case is 976, where there are 25 triangular rings, which is expected to take several hours to run :-) This only happens for a very small number of inputs and is fixable for a few extra characters (currently 15) but I don't see speed as essential to the rules.

Ungolfed Code

  c=400                        :REM Approx half the height of the screen area 
  INPUTa
  n=1                          :REM Number of rings
  t=0                          :REM t=triangular number n
  REPEAT
    t+=n                       :REM Update t to the next triangular number
    s=(a-1)/t                  :REM Number of sides of polygon
    r%=c/n/2                   :REM Choose a good length for (half)the radius of the first ring.
    CLS:MOVEc,c                :REM Clear screen and move graphics cursor to middle
    s%=s                       :REM Store the number of sides in an integer variable 
    IFs%=s                     :REM If real variable and integer variable are equal (all the following indent MUST be on the same line as the IF)
      u=r%*2:v=0:              :REM initialise (u,v) to 360 deg.
      FORp=1TOs%:              :REM Loop through the sides. 
        q%=r%*COS(p/s%*2*PI):  :REM Calculate x/2, assign to integer variable
        x=q%*2:                :REM assign to x, ensuring x is a multiple of 2. 
        q%=r%*SIN(p/s%*2*PI):  :REM ditto y.
        y=q%*2:                :REM ditto y.
        q=FNt(n):              :REM Plot the wedge from n down to 0 (the first row is the centre dot, which will be overdrawn s times)
        u=x:v=y:               :REM Reinitialise u and v with the last values of x and y 
      NEXT:                    :REM in order to plot the next wedge.
      q=GET                    :REM Allow user to view output, wait for keypress.
    n+=1                       :REM increment n ready to try the next triangular number
  UNTILs<=3                    :REM If number of sides already <=3
  END                          :REM exit program.

  DEFFNt(d)                    :REM BBC basic puts the nut & bolt functions at the end!
    PLOT153,8,0:MOVEBY-8,0     :REM Draw a filled circle radius 8 centred at the current graphics cursor position. After drawing, return cursor to centre of circle. 
    IFd                        :REM if this is not the final iteration (all the following indent MUST be on the same line as the IF)
      MOVEBY u,v:              :REM move to the point where the first circle of the next ring is to be drawn
      q=FNt(d-1):              :REM draw the circle and the rest of the branch
      DRAWBY x-u,y-v:          :REM draw the concentric line to the second circle of the next ring
      q=FNt(d-1):              :REM draw the circle and the rest of the branch
      MOVEBY-x,-y              :REM return cursor to where it was before calling the function
  =0                           :REM supply a return value to exit the function.

Output, 31 dots

I know the dots are small, but that comes in handy for the larger numbers.

enter image description here

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

Python 2.7.6, 272 bytes

I'm still learning python, so I'm sure there is more room to golf this. This is more of a challenge to myself than a competitive entry:

from turtle import*
from math import*
def f(n):pu();fd(20*n);pd()
P=input()
for n in range(2,P):
 a=2*P-2;b=n*n-n;m=a/b
 if a%b==0:
    f(n);dot(5)
    for r in range(1,n):
     f(1);rt(90-180/m)
     for s in [1]*m:
        rt(360/m)
        for t in [1]*r:dot(5);fd(40*sin(pi/m))
     lt(90-180/m)

Add exitonclick() to the end if you want the display to stay open after rendering.

Output for input of 31:

enter image description here

\$\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.