Title courtesy of Greg Martin
For this challenge, I'll define an arc of size \$k\$ as a single piece of a sine wave with a length of \$k\$ units and an height of \$\frac{k}{4}\$ units:
And I'll define a swoosh of order \1ドル\$ and \2ドル\$ as a single arc of size 1, and recursively define a swoosh of order \$k\$ as a swoosh of order \$k-1\$ followed by a flipped swoosh of order \$k-2\$, with an arc the same length as the previous two swooshes laid end-to-end.
For example, here are the swooshes of order 3, 4, 5, and 6:
You might notice that the length of a swoosh of order \$n\$ is the \$n\$th fibonacci number, hence the name.
Reference implementation:
let ctx = c.getContext('2d')
const scale = 30;
let drawArc = (length, offset = 0, amp = 1) => {
ctx.moveTo(offset * scale, c.height / 2)
for (i = 0; i <= scale * length; i++) {
ctx.lineTo(i + offset * scale, -Math.sin(Math.PI * i / (scale * length)) * amp * length * scale / 4 + c.height / 2)
}
ctx.stroke()
}
fib = n => fib[n] ||= n <= 2 ? 1 : fib(n - 1) + fib(n - 2)
let swoosh = (order, offset = 0, amp = 1) => {
if (order <= 2) {
drawArc(1, offset, amp);
} else {
swoosh(order - 1, offset, amp)
swoosh(order - 2, offset + fib(order - 1), -amp)
drawArc(fib(order), offset, amp)
}
}
swoosh(8)
<canvas width=1000 height=500 id=c></canvas>
Your challenge is to, given an integer \$n\$, draw a fibonacci swoosh of order \$n\$ as defined above, either outputting to an image file or drawing on the screen. You may use 0-indexing or 1-indexing. It's fine if the size of your answer's output is limited by screen/canvas size, but your answer should be able to handle at least \$n=6\$, and each arc should be at least 10 pixels wide onscreen. Each arc should be recognisable as a sine curve.
This is code-golf, shortest wins!
Testcases
Here's a higher-resolution image for n=6:
enter image description here And here's an image for n=10: enter image description here
5 Answers 5
JavaScript (ES6), (削除) 287 (削除ここまで) (削除) 286 (削除ここまで) (削除) 246 (削除ここまで) (削除) 235 (削除ここまで) 233 bytes
f=
n=>`<svg viewBox=-1,-${[g(n)*6+1,i*24+2,i*6+g(n-2)*6+2]}><path fill=none stroke=#000 d=M0,0${h(n,-3)}>`
g=n=>i=n<3||g(n-1)+g(n-2)
h=(n,s)=>'c'+[4,s,8,t=s*2,12,t,4,0,8,-s,12,-t].map(m=>m*g(n))+(n>2?'m'+[-i*24,0]+h(--n,s)+h(--n,-s):'')
<input type=number oninput=o.innerHTML=f(+this.value) min=1 max=20><div id=o>
Outputs an HTML5 compatible SVG image. Doesn't adjust the stroke-width for large n so the swoosh tends to fade out. n limited to 20 because it's too slow otherwise. Edit: Saved 1 byte thanks to @noodleman. Saved a few bytes thanks to @Ausername. Version with stroke-width adjustment:
f=
n=>`<svg viewBox=-1,-${[g(n)*24+1,i*96+2,i*24+g(n-2)*24+2]}><path fill=none stroke=#000 stroke-width=${g(n-2)} d=M0,0${h(n,-3)}>`
g=n=>i=n<3?1:g(n-1)+g(n-2)
h=(n,s)=>'c'+[4,s,8,t=s*2,12,t,4,0,8,-s,12,-t].map(m=>m*4*g(n))+(n>2?'m'+[-i*96,0]+h(--n,s)+h(--n,-s):'')
<input type=number oninput=o.innerHTML=f(+this.value) min=1 max=20><div id=o>
-
\$\begingroup\$ You should be able to save a byte on your fibonacci function
gwithg=n=>n<3||g(n-1)+g(n-2)\$\endgroup\$noodle person– noodle person2024年04月06日 12:07:03 +00:00Commented Apr 6, 2024 at 12:07 -
\$\begingroup\$ @noodleman Yeah originally I had a raw
g(n)somewhere but I think the only one left is in the stroke-width adjustment version. \$\endgroup\$Neil– Neil2024年04月06日 12:40:52 +00:00Commented Apr 6, 2024 at 12:40 -
\$\begingroup\$
[e=g(n)*6+1,e*4-2,e+g(n-2)*6+1]saves a few bytes \$\endgroup\$emanresu A– emanresu A2024年04月06日 20:38:36 +00:00Commented Apr 6, 2024 at 20:38 -
\$\begingroup\$ Also doing the initial call to h with s=-3 saves a few more bytes \$\endgroup\$emanresu A– emanresu A2024年04月06日 20:41:41 +00:00Commented Apr 6, 2024 at 20:41
-
\$\begingroup\$ -5 more by replacing
gwithg=n=>i=n<3?6:g(n-1)+g(n-2), removing all*6s, replacing*24with*4, and adding a/6in the map call inh(code) \$\endgroup\$emanresu A– emanresu A2024年04月06日 23:06:43 +00:00Commented Apr 6, 2024 at 23:06
Google Sheets, (削除) 499 (削除ここまで) 490 bytes
-9 bytes thanks to @doubleunary
=SORT(SPARKLINE(LET(F,LAMBDA(F,n,IF(n<3,1,F(F,n-1)+F(F,n-2))),D,LAMBDA(l,o,a,LET(s,SEQUENCE(30*l+1,1,),{s+o*30,SIN(PI()*s/(30*l))*a*l})),W,LAMBDA(W,k,o,a,IF(k<3,D(1,o,a),{W(W,k-1,o,a);W(W,k-2,o+F(F,k-1),-a);D(F(F,k),o,a)})),z,W(W,A1,,1),y,INDEX(z,,1),x,TOCOL(SCAN(,ABS({0;y}-y)>1,LAMBDA(a,c,a+c)),2),IF(A1>2,REDUCE(FILTER(z,x=0),SEQUENCE(COUNTUNIQUE(x)-1),LAMBDA(a,i,LET(k,{a;SORT(a,SEQUENCE(ROWS(a)),)},{QUERY(k,"limit "&XMATCH(VLOOKUP(i,{x,z},2,),INDEX(k,,1),,-1));FILTER(z,x=i)}))),z))))
As pointed out by @doubleunary in the comments, it turns out that wasn't a bug but just how the function is designed. When drawing across the given coordinates, it never "lifts the pen" so if it draws \$x1 \rightarrow x2 \rightarrow x3\$ and it has to go back to \$x1\$, it draws a line from \$x3 \rightarrow x1\$ ruining the figure. I addressed this issue by filling the gaps with the intermediate coordinates so \$x1 \rightarrow x2 \rightarrow x3 \rightarrow x1\$ becomes \$x1 \rightarrow x2 \rightarrow x3 \rightarrow x2 \rightarrow x1\$ but this doubled the size of the formula and it no longer works beyond order=7, unlike the previous formula that worked up to order=18.
Google Sheets, (削除) 250 (削除ここまで) 237 bytes
-13 bytes thanks to @doubleunary
=SORT(SPARKLINE(LET(F,LAMBDA(F,n,IF(n<3,1,F(F,n-1)+F(F,n-2))),D,LAMBDA(l,o,a,LET(s,SEQUENCE(30*l+1,1,),{s+o*30,SIN(PI()*s/(30*l))*a*l})),W,LAMBDA(W,k,o,a,IF(k<3,D(1,o,a),{W(W,k-1,o,a);W(W,k-2,o+F(F,k-1),-a);D(F(F,k),o,a)})),W(W,A1,,1))))
It's just an implementation of the code OP provided.
(削除) The straight line connecting the start and end of the wave seems to be a bug with the SPARKLINE function. (削除ここまで)
-
1\$\begingroup\$ When
sparkline()is used like this, it can only draw one continuous line. You could avoid the straight line by "tracking back" to the start position each time you finish a wave. See what happens if you swap the order ofsort()andsparkline()and specify a low value fororder. \$\endgroup\$doubleunary– doubleunary2024年04月06日 17:20:03 +00:00Commented Apr 6, 2024 at 17:20 -
\$\begingroup\$ ahh I see that makes sense! I've never used SPARKLINE before so I'm not very familiar with it. I'll look into it. \$\endgroup\$z..– z..2024年04月06日 17:25:39 +00:00Commented Apr 6, 2024 at 17:25
-
1\$\begingroup\$ -14 bytes:
=sort(sparkline(let(f,lambda(f,n,if(n<3,1,f(f,n-1)+f(f,n-2))),d,lambda(l,o,a,let(s,sequence(30*l+1,1),{s+o*30,sin(pi()*s/(30*l))*a*l})),w,lambda(w,k,o,a,if(k<3,d(1,o,a),{w(w,k-1,o,a);w(w,k-2,o+f(f,k-1),-a);d(f(f,k),o,a)})),w(w,A1,,1))))\$\endgroup\$doubleunary– doubleunary2024年04月06日 17:29:58 +00:00Commented Apr 6, 2024 at 17:29 -
1
-
1\$\begingroup\$ Very nice! Tried to decrease recursion with a
reduce()based Fibonacci but that did not help much. You can snip 9 bytes by replacing the secondw(w,a1,,1)withz. \$\endgroup\$doubleunary– doubleunary2024年04月07日 04:54:08 +00:00Commented Apr 7, 2024 at 4:54
K (ngn/k) (削除) 106 (削除ここまで) 95 bytes
{?|/''i=\:_0.5+w%4%1-{:[x<2;k;o[x-1+b;y+y-b]%2-4*b:_y+y],k:`[email protected]*y}[x]'(i:!1+w)%w:2/1,&3+x}
It takes one argument, the order of the swoosh, and outputs the bitmap.
How it works?
The function
{:[x<2;k;o[x-1+b;y+y-b]%2-4*b:_y+y],k:`[email protected]*y}
takes two arguments, the order and normalized x coordinate, then it outputs a list of y coordinates the swoosh of that order passes through at the given x coordinate. It calculates the coordinates recursively.
The rest of the code transforms coordinates into bitmap.
Here are the bitmaps for k from 1 to 7 (converted to png).
C (GCC), (削除) 259 (削除ここまで) 257 bytes
w;a;char*m;z(k,s,f,x){k=k>2?z(k-2,z(k-1,s,f),-f):s+100;float W=k-s,q=asin(.75);for(x=0;x<W;++x)if(m)m[(int)(w/2-f*W*(sin(x/W*(3.14-2*q)+q)-.75)+.5)*w+s+x+a]=1;return k;}main(n){w=z(n,0);a=sprintf(m=calloc(w,w+1),"P5 %d %d 1 ",w,w);z(n,0,1);write(1,m,w*w);}
Input is given as argc, outputs a PGM image.
Function z draws a swoosh of order k, starting at the x coordinate s, and returns the x coordinate at which it ends; f can be either -1 or 1, and it determines if the swoosh is flipped or not (respectively).
If the output buffer is NULL, it performs a dry run.
How it works:
- first, do a dry run of
zto compute the width of the final image; for simplicity, the height is set equal to the width; - allocate a buffer of proper size, and write the PGM header;
- then re-run
z; - output the buffer.
-
-
\$\begingroup\$ @Blue maybe, apart from the fact that it would be another language (
GCC -O0), I've just tried and I can't get it work, and I don't think it's doable without adding less then 9 bytes. \$\endgroup\$matteo_c– matteo_c2024年04月09日 10:29:30 +00:00Commented Apr 9, 2024 at 10:29 -
\$\begingroup\$ First, I have to introduce another variable (let's call it
e) in the signature, since I need the value ofkunaltered for the next calls; the same holds for the other parameters, except forxwhich is modified before the function terminates. This already adds up to +6 bytes, so... I won't try to get it work, you're free to prove me wrong :) \$\endgroup\$matteo_c– matteo_c2024年04月09日 10:33:03 +00:00Commented Apr 9, 2024 at 10:33 -
1\$\begingroup\$ Oh yeah, it took me a second to figure out what is happening, it most certainty won't be worth it. Good job! \$\endgroup\$Blue– Blue2024年04月09日 13:41:30 +00:00Commented Apr 9, 2024 at 13:41
Desmos, 153 bytes
c=180
h(n,x)=f(n,x)[1...c]
g(n)=\{n<2:n,g(n-1)+g(n-2)\}
k=g(n)
f(n,x)=\{n<3:[45\sin x\{0<x<c\}],\join(f(n-1,x),-f(n-2,x-cg(n-1)),45k\sin x/k\{0<x<ck\})\}
The fibonacci swoosh of order \$n\$ can be displayed by calling h(n,x) (x is a free variable, like in f(x)=x or f(x)=x^2). For example, h(3,x) will display the fibonacci swoosh of order 3.
Note that the graph is set in degrees mode; it will not work in radians mode.
Try It On Desmos! - Prettified
Second ever Desmos answer using recursion!!! I am still gushing over the fact that Desmos actually added recursion; it's now turing complete and that opens the door for so many challenges that Desmos previously couldn't solve. Just to think that just a couple days ago, this challenge would not be possible to do in Desmos...
Also, this does work up to \$n=6\$ (see screenshots below), but for some reason that I am unaware of, it takes an extremely long time for the graph to display anything. I didn't keep track of exactly how long it took to load the graph, but, for me, it was upwards of 30 minutes. This is why I didn't test \$n>6\$; it was taking super long for the graphs to load anything. Although in theory, it should be able to handle a couple of cases past \$n=6\$. I found that the runtime can be reduced by replacing the c in [1...c] to a smaller number (like 20), but it still takes a while to display the graph.
Also, for some reason that I am also unaware of, simply calling f(n,x) without any modifications will result in the following error: Can't plot a list whose length depends on free variable 'x'.. To fix this, f(n,x) has to be indexed by a range that is dependent on a non-free variable; in this case, it is c, which is defined to be 180. This is precisely what h(n,x) achieves.
Here are the screenshots of the graphs after they finished running.
Explore related questions
See similar questions with these tags.