# Prime parity peregrination

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.

• 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:

N = 3000:

N = 20000:

N = 159000:

N = 1200000:

N = 11000000:

• Is there a reason only [graphical-output] is allowed? Any reason in particular to disallowed ASCII output, like my now deleted Charcoal answer? – Kevin Cruijssen Jun 24 '19 at 12:11
• @Kevin I was advised once not to mix both in the same challenge... What do others think? – Luis Mendo Jun 24 '19 at 12:34
• Well, 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 Jun 24 '19 at 12:42
• Would 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 Jun 24 '19 at 14:31
• At N=11000000, it appears to be approximating the map of Europe. – Digital Trauma Jun 24 '19 at 18:07

# 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.

• Cool! I got down to 77 bytes by combining our solutions. Cheers! – Roman Jun 25 '19 at 6:54

# MATL, 2524 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\$$:

# 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:

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 */
}
• 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 Jun 24 '19 at 17:06
• 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 Jun 24 '19 at 17:29
• Ah okay, you're right, that makes sense. – Doorknob Jun 24 '19 at 17:33
• 162 bytes – ceilingcat Jun 28 '19 at 22:44

# Wolfram Language (Mathematica), 9896917776 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]

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.

• Thanks @lirtosiast! It's like learning a foreign language: new vocabulary every day. – Roman Jun 25 '19 at 19:45

# MATL, 313028 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

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

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

# JavaScript - 675668660632556 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"/>

• Color depends on amount of overlapping lines? Cool! – val says Reinstate Monica Jun 25 '19 at 12:18
• 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 Jun 25 '19 at 14:27

# Red, 515480 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

n = 20000

• Out 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 Jun 25 '19 at 9:30
• @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 Jun 25 '19 at 10:41
• @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 Jun 25 '19 at 10:50
• Ah 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 Jun 25 '19 at 11:02

# 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