Prime parity peregrination

44

8

The purpose of this challenge is to graphically depict a walk on the plane, where the direction of each step \$k\$ is determined by the primality of \$k\$ and the parity of its binary expansion. Specifically,

  • Initial direction is fixed, say North.
  • All steps have the same length.
  • The direction of step \$k\$ can be North, West, South or East, and is determined as follows:
    • If \$k\$ is not prime, the direction does not change.
    • If \$k\$ is prime and the binary expansion of \$k\$ has an even number of ones, turn right.
    • If \$k\$ is prime and the binary expansion of \$k\$ has an odd number of ones, turn left.

As a worked example, assume that the initial direction is North. The first steps are:

  • \$k=1\$ is not prime. So we move one step in the current direction, which is North.
  • \$k=2\$ is prime, and its binary expansion, 10, has and odd number of ones. So we turn left, and are now facing West. We move one step in that direction.
  • \$k=3\$ is prime, and its binary expansion, 11, has and even number of ones. So we turn right, and are now facing North. We move one step in that direction.
  • \$k=4\$ is not prime. So we move one step in the current direction, which is North.

The challenge

Input: positive integer \$N\$.

Output: plot of the \$N\$-step walk as defined above.

Additional rules

  • The initial direction can be freely chosen (not necessarily North), but should be the same for all \$N\$.
  • The turning rule can be the opposite to that described above, that is, turn right for odd parity and left for even; but it has to be the same for all \$N\$.
  • The output has to be a graphical depiction of the walk. For instance:
    • The walk can be drawn with line segments.
    • The visited points can be shown with a marker, such as a dot; with or without connecting line segments.
    • A two-colour raster image can be provided, with one colour corresponding to visited points and another for non-visited.
  • The scales of the horizontal and vertical axes need not be the same. Also axis labels and similar elements are optional. As long as the walk can be clearly seen, the plot is valid.
  • Note that some points are visited more than once. The plot is not sensitive to this. For instance, if line segments are shown in the plot, each unit segment is displayed the same no matter how many times it has been traversed.
  • The code should work for any N given unlimited resources. It is acceptable if in practice it fails for large N due to time, memory or data-type limitations.
  • Input and output are flexible as usual. In particular, any of the standard means for outputting images can be used.
  • The shortest code in bytes wins.

Test cases

The following plots use North as initial direction; even parity turns right; and the walk is depicted with line segments.

N = 7:

enter image description here

N = 3000:

enter image description here

N = 20000:

enter image description here

N = 159000:

enter image description here

N = 1200000:

enter image description here

N = 11000000:

enter image description here

Luis Mendo

Posted 2019-06-24T09:49:01.093

Reputation: 87 464

1Is there a reason only [graphical-output] is allowed? Any reason in particular to disallowed ASCII output, like my now deleted Charcoal answer? – Kevin Cruijssen – 2019-06-24T12:11:20.660

2@Kevin I was advised once not to mix both in the same challenge... What do others think? – Luis Mendo – 2019-06-24T12:34:53.693

1Well, I can understand the reasoning behind that advice, since output as image/graph vs ASCII art is completely different in some languages. Then again, I have seen graph outputs getting loads of upvotes in ASCII-art challenges and vice-versa, so I guess not everyone agrees. Personally I think it really depends on the challenge. In this case I personally don't see any harm in allowing both in the same challenge, but maybe I'm biased due to my now deleted answer. So I'll ask the same question as you: "What do others think?" @Arnauld Maybe you should post your ASCII Taxi Driver after all ;) – Kevin Cruijssen – 2019-06-24T12:42:41.720

@KevinCruijssen Yes, perhaps a good compromise is to post another similar challenge for ASCII art. – Luis Mendo – 2019-06-24T13:43:21.923

1Would be interesting to see this run on various OEIS sequences (true, some would just walk in a straight line or run in circles, but some could be quite something). – Draco18s no longer trusts SE – 2019-06-24T14:31:20.020

1@Draco18s Yup. I tried a few things and many give nice looking paths – Luis Mendo – 2019-06-24T14:33:15.597

@LuisMendo out of curiosity, is this your invention, or is this a studied sequence? – Jonah – 2019-06-24T16:29:57.527

1

@Jonah I came up with it (but there are similar walks that are probably well known; see for example here). I wanted a random-looking but reproducible walk. And I wanted it change direction not too often. So primes came to mind (random-looking, not too abundant). Also I wanted for it to change direction left or right with the same probability. The parity of a prime number is obviously not useful for this. So parity of the sum of binary digits was a natural choice

– Luis Mendo – 2019-06-24T16:36:35.780

16At N=11000000, it appears to be approximating the map of Europe. – Digital Trauma – 2019-06-24T18:07:02.563

1

@DigitalTrauma Also, https://chat.stackoverflow.com/transcript/message/46585109#46585109

– Luis Mendo – 2019-06-24T18:32:19.350

@DigitalTrauma This phenomenon is known as pareidolia. That said, I do agree that it looks like a map of Europe. ;)

– Arnauld – 2019-06-25T13:29:36.237

Answers

12

Sledgehammer 0.4, 22 20 bytes

⢂⡐⠥⡄⠡⢒⣩⣀⣼⡝⢄⡎⣛⠅⡉⣱⡆⢀⡠⣽

Decompresses into this Wolfram Language function:

ListPlot[AnglePath[Array[If[PrimeQ@#, ArcSin[(-1)^ThueMorse@#], 0] &, #]]]

Ungolfed

First we define a function that returns the angle to turn at each step:

If[PrimeQ[#],
    ArcSin[(-1)^ThueMorse@#],
    0
]&

ThueMorse is the parity of the sum of binary digits. We use -1^(...) rather than 2*...-1 for a slightly complicated reason: Wolfram Language automatically converts arithmetic expressions in source to a canonical form, so expressions like 2/x are stored as Times[2, Power[x, -1]]. This makes the frequency of Power very high, and thus compressing it very cheap.

(Multiplying by Boole@PrimeQ@ is slightly longer, and implicit Boole casting of Booleans had not been implemented at the time of the challenge.)

From here, Mathematica's AnglePath and ListPlot do exactly what we need:

ListPlot[AnglePath[Array[%, #]]]&

In the interactive app, output is a rescalable vector Graphics object.

enter image description here

lirtosiast

Posted 2019-06-24T09:49:01.093

Reputation: 20 331

Cool! I got down to 77 bytes by combining our solutions. Cheers! – Roman – 2019-06-25T06:54:28.790

14

MATL, 25 24 21 bytes

Q:qJyZpbB!sEq*^YpYsXG

Try it at MATL online

Thanks @LuisMendo for a nice golfing session in chat that ultimately led to this 21 byte version, by suggesting Eq*^

Explanation

Q:q % Push 0:n
J   % Push 1i for later use.
y   % Duplicate 0:n from below
Zp  % Vector result of isprime()
b   % Bubble 0:n from bottom of stack
B!s % Sum of bits in binary representation
Eq  % Double minus one to get an odd number
*   % Multiply by isprime result to get either zero or aforementioned odd number
^   % Exponentiate 1i by an odd number or zero to get -i, 1 or i (corresponding to left turn, straight ahead, right turn).
Yp  % Cumulative product to get a vector of directions
Ys  % Cumulative sum to get vector of positions
XG  % Plot

Example for \$k=12345\$: enter image description here

Sanchises

Posted 2019-06-24T09:49:01.093

Reputation: 8 530

8

Wolfram Language (Mathematica), 98 96 91 77 76 63 bytes

ListPlot@AnglePath@Array[Pi/2If[PrimeQ@#,2ThueMorse@#-1,0]&,#]&

-14 bytes: Thanks to @lirtosiast for showing me how to use AnglePath...

-13 bytes: ...and ThueMorse!

usage example:

%[20000]

enter image description here

Step-by-step explanation:

  • If[PrimeQ@#, 2 ThueMorse@# - 1, 0] & is a function that takes the step index and returns 0 for non-primes, -1 for even-binary primes, and +1 for odd-binary primes. ThueMorse@# replaces the previous solution's Total[#~IntegerDigits~2] (which is the same, modulo 2).

  • Array[Pi/2*%,#] makes a list of this function with the index going from 1 to the function argument (20000 in the example), and multiplies every element by π/2 to make it into a direction-change angle (radians). We now have 0 for non-primes, -π/2 for even-binary primes, and +π/2 for odd-binary primes.

  • AnglePath[%] converts this list of direction-change angles into a path. This instruction replaces the previous solution's double-use of Accumulate.

  • ListPlot[%] converts the list of positions into a XY dot plot. If a line is preferred, use ListLinePlot instead. These plotting functions have lots of options to make the plots look better.

Roman

Posted 2019-06-24T09:49:01.093

Reputation: 1 190

1Thanks @lirtosiast! It's like learning a foreign language: new vocabulary every day. – Roman – 2019-06-25T19:45:36.723

8

C (gcc), 179 bytes

o;i;d;k;h;f(n,p)char*p;{h=2*n+1;memset(p,0,h*h);p+=h--*n+n;*p=1;for(d=k=0;k++<n;){for(i=1;k%++i%k;);for(o=k;o/2;o=o/2^o&1);i==k?d+=o*2+3:0;p+=(d%2*h+1)*((d&2)-1);*p=1;}return++h;}

Try it online!

A function. First argument is N, second argument is an allocated buffer of size at least \$4 n^2 + 4 n + 1\$ bytes. A square image is written to this buffer, its side length is returned. In the image, 0 is a white pixel, 1 is a black pixel.

C (gcc), 219 bytes

o;i;d;k;h;f(n,p)char*p;{h=2*n+1;p+=sprintf(p,"P1 %d %d ",h,h);memset(p,48,h*h);k=h--*n+n;*(p+2*k+1)=0;p+=k;*p=49;for(d=k=0;k++<n;){for(i=1;k%++i%k;);for(o=k;o/2;o=o/2^o&1);i==k?d+=o*2+3:0;p+=(d%2*h+1)*((d&2)-1);*p=49;}}

Try it online!

A function. First argument is N, second argument is an allocated buffer of size at least \$4 n^2 + 4 n + 2 \times \lfloor log_{10}(2n+1)\rfloor + 9\$ bytes. A square image in PBM format is written to the buffer.

Cropped output for 20000:

cropped output for 20000

Both versions start with west and turn right on odd, left on even.

I tried the larger testcases with neither of them, as the output with 20000 was ~1.5 GB, and 150000 would have been ~90GB. This is all stored in memory while the program executes.

Explanation of the upper one:

o;         /* Temporary variable for calculating parity */
i;         /* Temporary variable for primality test */
d;         /* d % 4 = direction */
k;         /* Step */
h;         /* height/width of image */
f(n,p)char*p;{ /* Function taking int and char pointer */
  h=2*n+1;     /* Image sidelength = 2 * N + 1, so N in each direction */
  memset(p,0,h*h); /* Reset buffer */
  p+=h--*n+n;  /* Position p at image center; decrement h */
  *p=1;        /* Put a dot at center */
  for(d=k=0;   /* Set direction and step to 0 */
    k++<n;){   /* Loop over [1..N] */
    for(i=1;k%++i%k;); /* Primality test */
    for(o=k;o/2;o=o/2^o&1); /* Parity test */
    i==k?d+=o*2+3:0; /* Change direction if necessary */
    p+=(d%2*h+1)*((d&2)-1); /* Move according to direction */
    *p=1; /* Set pixel to black */
  }
  return++h; /* Add 1 back to h and return */
}

wastl

Posted 2019-06-24T09:49:01.093

Reputation: 3 089

1

I don't think requiring that an allocated buffer be provided as an argument is allowed - per meta policy, any extra input must be empty (which I'd interpret to mean 0 or null pointer in the case of C).

– Doorknob – 2019-06-24T17:06:35.390

3

I'm interpreting this as saying I can expect it to be allocated. This is also a pattern used in many standard library functions, such as sprintf.

– wastl – 2019-06-24T17:29:21.313

Ah okay, you're right, that makes sense. – Doorknob – 2019-06-24T17:33:18.813

162 bytes – ceilingcat – 2019-06-28T22:44:10.127

7

MATL, 31 30 28 26 bytes

J4:^0i:Zpl_G:B!s^*hYs)YsXG

3 Bytes saved thanks to @LuisMendo

2 Bytes saved thanks to @Sanchises

Try it at MATL Online

Explanation

This solution uses complex numbers to represent the X and Y components of the 2D plane

J      % Push the literal complex number 0 + 1j to the stack
4:     % Create the array [1, 2, 3, 4]
^      % Raise 0 + 1j to each power in the array, [1, 2, 3, 4]

At this point, we have four points ((0, 1), (-1, 0), (0, -1), (1, 0)) in an array represented by complex numbers. These are the four cardinal directions. Now we want to use these to "walk".

Essentially the way that this works is that we start heading in the zero'th direction (the 0'th element of the array which is (-1, 0)). For each step, we need to determine the change in this heading. We will use integers to track this change. If we want to turn "right", we increment this integer by 1 (referencing the next element in the 4-point array) and if we want to go "left", we decrement this integer by 1 (referencing the previous element in the 4-point array). If we want to continue on our path, we keep the integer value constant (referencing the same element in the 4-point array).

This part of the code creates an array of all of those 0, -1, and 1 values.

0      % Push a literal 0 to the stack (the starting point)
i      % Explicitly grab the input (N)
:      % Create an array from 1...N
Zp     % Determine if each element is a prime (1) or not (0)
l_     % Push the literal -1 to the stack
G      % Explicitly grab the input again (N)
:      % Create an array from 1...N
B      % Convert to binary representation (each row is the binary representation of
       % each element in the vector)
!      % Transpose
s      % Sum down the columns to count number of 1's
^      % Raise the -1 to each element. For odd number of 1's in the
       % binary expansion this yields -1, and even yields 1

*      % Multiply this -1 or 1 by the result of the prime check (element-wise). 
       % For non-primes this yields a 0, for primes with an even number of 1's in 
       % the binary representation this is a 1, and for primes 
       % with an odd number of 1's in

h      % Horizontally concatenate with the initial 0

Now we have an array of the differences between successive integers so we can compute the cumulative sum of those up to get the indices which we can then use to lookup the direction at each step in the original 4-element array.

Conveniently, MATL has wrap-around indexing such that index 5 wraps around to the beginning of a 4-element array. We can use this to our advantage so that we can increment and decrement this integer without worrying about the fact that the reference direction array is only 4 elements.

Ys     % Compute the cumulative sum
)      % Use this to modularly index into the original array of four points

Now we have an array of directions of steps takes, so we can compute the cumulative sum of these directions to trace out the path that was taken.

Ys     % Compute the cumulative sum
XG     % Plot as a 2D plot

Suever

Posted 2019-06-24T09:49:01.093

Reputation: 10 257

5

Perl 6, 213 182 bytes

{my @p=[\+] [\*]({{.is-prime??.base(2).comb(~1)%2??i!!-i!!1+0i}(++$)}...*)[^$_];{"<svg viewBox='{.min xx 2,.elems xx 2}'>>.&{"L{.re} {.im}"}}' fill='none' stroke='black'/>"}(minmax |@p».reals)}

{{"<svg viewBox='{{.min,.min,+$_,+$_}(.minmax)}'><path d='{"L"X~$_}' fill='none' stroke='red'/></svg>"}(([\+] [\*]({-{.is-prime*.base(2).comb(~1)R**-1||i}(++$)i}...*)[^$_])».reals)}

Try it online!

(Really managed to pare this one down!)

This function outputs in SVG format.

  • { -{ .is-prime * .base(2).comb(~1) R** -1 || i }(++$)i } ... * is an infinite sequence of direction changes for each step, in the form of complex numbers, where 1 means "continue in the same direction," i means "turn left," and -i means "turn right."
  • [^$_] limits that sequence to the number of steps provided as the function's argument.
  • [\*] scans that sequence with (complex) multiplication, turning the list of relative direction changes into a list of absolute directions.
  • [\+] scans that sequence with (complex) addition, producing a list of the visited coordinates.
  • ».reals converts that list of complex numbers into two-element lists of its real and imaginary parts.

The SVG image is just one single path element.

Output (converted to PNG) for N = 20000:

path for N = 20000

Sean

Posted 2019-06-24T09:49:01.093

Reputation: 4 136

4

LOGO, 177 171 bytes

to d:c
if :c%2[rt 180
make"c:c-1]if:c<1[stop]d:c/2
end
to p
if:c>1[make"f 2
repeat:c-2[if:c%:f<1[stop]make"f:f+1]rt 90
d:c]end
to g:n
make"c 1
repeat:n[p
fw 2
make"c:c+1]end

To use, do something like this:

reset
pu
fw 100
pd
g 3000

Sorry, but I wasn't able to capture sample output. Explanation:

to d:c
if :c%2[rt 180
make"c:c-1]if:c<1[stop]d:c/2
end

This is a recursive procedure which rotates 180° for every set bit in its parameter, which effectively computes the parity of its binary expansion.

to p
if:c>1[make"f 2
repeat:c-2[if:c%:f<1[stop]make"f:f+1]rt 90
d:c]end

This is a very basic primality test. After special-casing 1, the procedure early returns if a factor is found. If however the current value is found to be prime, it turns right, and then uses the above procedure to change that into a left turn as appropriate.

to g:n
make"c 1
repeat:n[p
fw 2
make"c:c+1]end

This is just a simple loop to test all the numbers up to n for primality and to move two pixels after each one.

Neil

Posted 2019-06-24T09:49:01.093

Reputation: 95 035

4

C, 321 bytes

a,b,A,B,k,p,e,i,t,r;g(n,c,d,x,y,X,Y){x=y=Y=r=0;X=1;for(k=0;k++<=n;){r|=x==c&y==d;a=x<a?x:a;A=x>A?x:A;b=y<b?y:b;B=y>B?y:B;for(p=1,i=k;--i;p=p*i*i%k);for(e=1,i=k;i;e=-e)i&=i-1;if(p)t=X,X=-e*Y,Y=e*t;x+=X;y+=Y;}}f(n,x,y){A=a=B=b=0;g(n);printf("P1%d %d ",A-a+1,B-b+1);for(y=b;y<=B;++y)for(x=a;x<=A;++x)g(n,x,y),putchar(48+r);}

Try it online!

I started working on this before the other C answer was posted, but I figured I might as well post mine as well anyway. This one is a lot longer, but it also crops the output image to the dimensions of the result automatically.

Function is called as f(n), and output is to stdout in netpbm format.

Example output for n=1000:

a,b,A,B,          // used to store x range [a,A] and y range [b,B]
k,p,e,i,t,        // temp variables used in g
r;g(n,c,d,        // takes n + coordinates, sets r to whether (c,d) is visited
x,y,X,Y){         // temp variables - position (x,y) and velocity (X,Y)
x=y=Y=r=0;X=1;    // initialization
for(k=0;k++<=n;){ // loops k over the step number
r|=x==c&y==d;     // set r to 1 if current coordinate is the requested one
a=x<a?x:a;A=x>A?x:A;b=y<b?y:b;B=y>B?y:B;    // update bounds
for(p=1,i=k;--i;p=p*i*i%k);                 // prime test
for(e=1,i=k;i;e=-e)i&=i-1;                  // parity test
if(p)t=X,X=-e*Y,Y=e*t;                      // if prime, turn accordingly
x+=X;y+=Y;}}      // move position in direction of velocity
f(n,x,y){         // main function; x and y are temp variables
A=a=B=b=0;g(n);   // obtain accurate bounds
printf("P1 %d %d\n",A-a+1,B-b+1);           // output netpbm header
for(y=b;y<=B;++y)for(x=a;x<=A;++x)          // loop through all coordinates
g(n,x,y),putchar(48+r);}                    // output 1 if visited, 0 otherwise

The prime test is essentially the one used in Lynn's answer to a different challenge, which relies on Wilson's theorem.

The parity test uses an adaptation of Kernighan's bit-counting method.

Since the prime test is very slow, and the algorithm re-runs the entire path generation function for each pixel drawn, any input much higher than 1000 times out on TIO.

Doorknob

Posted 2019-06-24T09:49:01.093

Reputation: 68 138

308 bytes – ceilingcat – 2019-06-28T23:46:45.873

4

Jelly, 41 bytes

B§ḂḤ’×ıµ1Ẓ?€×\ÄŻÆiZ_Ṃ$€Z‘ḞŒṬµẈḢ⁾P1,;Lṭ@FK

Try it online!

Full program that takes a single argument \$N\$ and outputs a 1-bit PBM to STDOUT. Takes some inspiration from @Sanchises MATL answer. Starts off going South and turns left on odd bit parity.

Sample output for \$N=3000\$:

Output for N=3000

If ASCII Art were permissible, here’s an alternative 29-byte variant which yields (for \$N=300\$):

0000000000000000000000111110000000000
0000000000000000000000100010000000000
0000001110000000000000100010000000000
0000001010000000000000100010000000000
0000001010000000000000100010000000000
0000001010000000000000100010000000000
0000001010000000111111111010000000000
0000001010000000100000101010000000000
0000001111111110100000101010000000000
0000000000100010100000101010000000000
0000000000111111100000101010001111111
0000000000000010000000101010001000001
0000000000000011100010101010001000001
0000000000000000100010101010001000001
0000111111100000100011111111111111111
0100100000100000100010001010001000001
0110100000111111100011111111111000111
0010100000000000000010101010000000101
1111100000000000000010101110000000101
1010000000000000000010100000000000101
1010000000000000000011111111111011101
1010000000000000000000100000001010101
1110000000000000000000111111101111101
0000000000000000000000000000100010101
0000000000000000000000000000100010101
0000000000000000000000000000100010101
0000000000000000000000000000111111111
0000000000000000000000000000000010100
0000000000000000000000000000000010100
0000000000000000000000000000000010100
0000000000000000000000000000000011100

Nick Kennedy

Posted 2019-06-24T09:49:01.093

Reputation: 11 829

4

JavaScript - 675 668 660 632 556 534 Bytes

First time here on CodeGolf, initially started with ~1500 Bytes Code. Golfed it to less than half nearly more than a third of it. Feel free to continue golfing. Bytes Counted with: this tool

Principle:
Draws into fixed size canvas with N and variable stroke length as input.

Edits:

-07 Bytes - remove of missed if
-08 Bytes - change switch to if/else
-28 Bytes - change to tenary if/else
-76 Bytes - shorter prime test (runtime / 3)
-22 Bytes - use this prime function (runtime * 4)

Golfed Code:

function f(e,r){for(var t=0,n=0,i=950,f=450,o=document.getElementById("d").getContext("2d"),a=1;a<=e;a++){n=iP(a)?iO(a)?1:2:0;var u=i,c=f;t==0?(t=0==n?(c=f-r,0):1==n?(u=i-r,1):(u=i+r,3)):t==1?(t=0==n?(u=i-r,1):1==n?(c=f+r,2):(c=f-r,0)):t==2?(t=0==n?(c=f+r,2):1==n?(u=i+r,3):(u=i-r,1)):(t=0==n?(u=i+r,3):1==n?(c=f-r,0):(c=f+r,2));o.beginPath(),o.moveTo(i,f),o.lineTo(u,c),o.stroke(),i=u,f=c}}function iP(h){for(i=n=h;n%--i;);return(1==i)}function iO(e){for(var r=(e>>>0).toString(2),t=0,n=0;n<r.length;n++)"1"==r[n]&&t++;return t%2!=0}

Ungolfed code with whitespaces:

function f(e,r){
    for(var t=0,n=0,i=950,f=450,o=document.getElementById("d").getContext("2d"),a=1;a<=e;a++){

        // prime and odd/even check
        n=iP(a)?iO(a)?1:2:0;

        var u=i,c=f;

        t==0?(t=0==n?(c=f-r,0):1==n?(u=i-r,1):(u=i+r,3)):t==1?(t=0==n?(u=i-r,1):1==n?(c=f+r,2):(c=f-r,0)):t==2?(t=0==n?(c=f+r,2):1==n?(u=i+r,3):(u=i-r,1)):(t=0==n?(u=i+r,3):1==n?(c=f-r,0):(c=f+r,2));

        o.beginPath(),o.moveTo(i,f),o.lineTo(u,c),o.stroke(),
        i=u,f=c // renew old cords
    }
}

// check prime
function iP(h){
    for(i=n=h;n%--i;);
    return(1==i)
}

// check binary expression even/odd
function iO(e){
    for(var r=(e>>>0).toString(2),t=0,n=0;n<r.length;n++)
        "1"==r[n]&&t++;
    return t%2!=0
}

Examples:

N = 7 - Length = 60

f(7, 60);
function f(e,r){for(var t=0,n=0,i=950,f=450,o=document.getElementById("d").getContext("2d"),a=1;a<=e;a++){n=iP(a)?iO(a)?1:2:0;var u=i,c=f;t==0?(t=0==n?(c=f-r,0):1==n?(u=i-r,1):(u=i+r,3)):t==1?(t=0==n?(u=i-r,1):1==n?(c=f+r,2):(c=f-r,0)):t==2?(t=0==n?(c=f+r,2):1==n?(u=i+r,3):(u=i-r,1)):(t=0==n?(u=i+r,3):1==n?(c=f-r,0):(c=f+r,2));o.beginPath(),o.moveTo(i,f),o.lineTo(u,c),o.stroke(),i=u,f=c}}function iP(h){for(i=n=h;n%--i;);return(1==i)}function iO(e){for(var r=(e>>>0).toString(2),t=0,n=0;n<r.length;n++)"1"==r[n]&&t++;return t%2!=0}
<canvas id="d" width="1900" height="900"/> 

N = 3000 - Length = 4

f(3000, 4);
function f(e,r){for(var t=0,n=0,i=950,f=450,o=document.getElementById("d").getContext("2d"),a=1;a<=e;a++){n=iP(a)?iO(a)?1:2:0;var u=i,c=f;t==0?(t=0==n?(c=f-r,0):1==n?(u=i-r,1):(u=i+r,3)):t==1?(t=0==n?(u=i-r,1):1==n?(c=f+r,2):(c=f-r,0)):t==2?(t=0==n?(c=f+r,2):1==n?(u=i+r,3):(u=i-r,1)):(t=0==n?(u=i+r,3):1==n?(c=f-r,0):(c=f+r,2));o.beginPath(),o.moveTo(i,f),o.lineTo(u,c),o.stroke(),i=u,f=c}}function iP(h){for(i=n=h;n%--i;);return(1==i)}function iO(e){for(var r=(e>>>0).toString(2),t=0,n=0;n<r.length;n++)"1"==r[n]&&t++;return t%2!=0}
<canvas id="d" width="1900" height="900"/> 

N = 20000 - Length = 2

f(20000, 2);
function f(e,r){for(var t=0,n=0,i=950,f=450,o=document.getElementById("d").getContext("2d"),a=1;a<=e;a++){n=iP(a)?iO(a)?1:2:0;var u=i,c=f;t==0?(t=0==n?(c=f-r,0):1==n?(u=i-r,1):(u=i+r,3)):t==1?(t=0==n?(u=i-r,1):1==n?(c=f+r,2):(c=f-r,0)):t==2?(t=0==n?(c=f+r,2):1==n?(u=i+r,3):(u=i-r,1)):(t=0==n?(u=i+r,3):1==n?(c=f-r,0):(c=f+r,2));o.beginPath(),o.moveTo(i,f),o.lineTo(u,c),o.stroke(),i=u,f=c}}function iP(h){for(i=n=h;n%--i;);return(1==i)}function iO(e){for(var r=(e>>>0).toString(2),t=0,n=0;n<r.length;n++)"1"==r[n]&&t++;return t%2!=0}
<canvas id="d" width="1900" height="900"/> 

N = 159000 - Length = 1

f(159000, 1);
function f(e,r){for(var t=0,n=0,i=950,f=450,o=document.getElementById("d").getContext("2d"),a=1;a<=e;a++){n=iP(a)?iO(a)?1:2:0;var u=i,c=f;t==0?(t=0==n?(c=f-r,0):1==n?(u=i-r,1):(u=i+r,3)):t==1?(t=0==n?(u=i-r,1):1==n?(c=f+r,2):(c=f-r,0)):t==2?(t=0==n?(c=f+r,2):1==n?(u=i+r,3):(u=i-r,1)):(t=0==n?(u=i+r,3):1==n?(c=f-r,0):(c=f+r,2));o.beginPath(),o.moveTo(i,f),o.lineTo(u,c),o.stroke(),i=u,f=c}}function iP(h){for(i=n=h;n%--i;);return(1==i)}function iO(e){for(var r=(e>>>0).toString(2),t=0,n=0;n<r.length;n++)"1"==r[n]&&t++;return t%2!=0}
<canvas id="d" width="1900" height="900"/> 

pixma140

Posted 2019-06-24T09:49:01.093

Reputation: 135

Color depends on amount of overlapping lines? Cool! – val says Reinstate Monica – 2019-06-25T12:18:08.570

I did not change the stroke style, this should be default black with no pattern or transparancy. Found here. Why there might happen a color change could be related to the stroke width I set in the second parameter my function is taking @val. Sorry to maybe disappointing you.

– pixma140 – 2019-06-25T14:27:39.043

3

Red, 515 480 471 bytes

-1 byte thanks to Kevin Cruijssen!

func[n][a: 270 x: t: u: v: w: 0 y: 1
b: copy[line 0x0 0x1]repeat i n - 1[p: on
j: i + 1 repeat k i / 2[if j%(k + 1)= 0[p: off]]if p[s: 0
until[if j% 2 = 1[s: s + 1](j: j / 2)< 1]a: a + pick[-90 90]s% 2 + 1]append b 'line 
append b as-pair x y x: x + cosine a y: y - sine a append b as-pair x y t: min x t
u: max x u v: min y v w: max y w]c: 500 /(max u - t w - v)view[base white 502x500
draw collect[foreach k b[keep either pair? k[as-pair k/1 - t * c k/2 - v * c][k]]]]]

A significant part of the code (~160 bytes) deals with normalizing the coordinates so that the graphics fits entirely in the canvas regardless of the size of the input.

Initial direction: South.

Here's the result for n = 3000

3000 iterations

n = 20000

20000

Galen Ivanov

Posted 2019-06-24T09:49:01.093

Reputation: 13 815

1Out of curiosity, why aren't there any spaces required for the modulos at if j%(k + 1) and if j% 2 = 1, but there are spaces required for most of the other operators (+, /, etc.). Can the space also be removed at the modulo of pick[-90 90]s% 2? Actually, why also aren't there any spaces required at as-pair k/1 - t * c k/2 - v * c for the /? – Kevin Cruijssen – 2019-06-25T09:30:18.083

1@KevinCruijssen Yes, the space can be removed for s% 2, thanks! I don't know why, but modulo % is the only operator for which the space in front of it can be dropped, if preceded by a word (variable). In as-pair k/1 - t * c k/2 - v * c the slashes / serve completely different purpose - they are paths. k is a pair and k/1 is the first element (it can be selected also by k/x, or pick k 1). Spaces are needed almost everywhere, the exceptions are around ()[]{}, beacuse there is no ambiguity. – Galen Ivanov – 2019-06-25T10:41:04.277

@KevinCruijssen Most symbols can be used in word names (Red doesn't have variables, everything is either a word or value (or some syntax block like [...] or (...)). So: a*4: 45 -> a word a*4 is assigned a value 45. % is used as a marker for the file! datatype and maybe that's why it can't be used in word names but can break the rules for the other arithmetic operators. – Galen Ivanov – 2019-06-25T10:50:10.283

1Ah ok, that makes sense that the / have a different purpose there and the symbols can be used without spaces in variables (or words as they are apparently called for Red). Thanks for the explanation. :) And glad I could (mostly accidentally) save a byte for the s% 2. :) – Kevin Cruijssen – 2019-06-25T11:02:23.210

1

Processing , 140+ bytes

void f(int N){for(int x,y,i,l,d,k=d=y=x=0;k++<N;d+=i<l?0:Integer.bitCount(k)%2*2-1,d&=3,point(x-=~-d%2,y+=(d-2)%2))for(i=1,l=k;0<l%++i%l;);}

Might not fulfill clearly seen

walk

PrincePolka

Posted 2019-06-24T09:49:01.093

Reputation: 653