Move arrows along a contour Sandboxed

Given a set of closed non-overlapping 2d contours (separated by at least one space even on diagonals) with arrows oriented consistently in the same clockwise or counter-clockwise direction (each contour has its own direction) and a positive number n, move the arrows n steps along the contours in the respective direction. 
The arrows are represented by &gt; v &lt; ^ respectively for right, down, left and up directions. There the other characters are - (horizontal), | (vertical) and + (corner).
When an arrow is on a corner, it keeps its current direction and changes it only after the turn is taken.

There will always be a straight segment (or a space) between any two corners (like +-+ for the horizontal and similar for the vertical) - in other words the sharp U turns are forbidden. The segments between the corners are either vertical or horizontal and the bend at a corner is always 90 degree.

Input:


a positive integer - n - number of steps 
an ASCII representation of the contours - it can be a multiline string, a list of strings, a list of characters and so on.


Output:

The same contours with all arrows shifted n steps in each contour's overall direction.

Test cases:

1.

Input: 

n = 1

 +-----&gt;-&gt;            
 |       |            
 |       v---+        
 |           |        
 +---&lt;-------+      


Output:

 +------&gt;+
 |       v
 |       +&gt;--+
 |           |
 +--&lt;--------+


2.

Input: 

n = 2             

 +-----+ +---+        
 |     | |   |        
 +--&gt;--+ |   v  
         |   | 
 +---&gt;---+   |        
 |           |         
 +------&lt;&lt;---+       


Output:

 +-----+ +---+
 |     | |   |
 +----&gt;+ |   |
         |   | 
 +-----&gt;-+   v
 |           |     
 +----&lt;&lt;-----+        


3.

Input: 

n = 3

 +---+   +---+   +-------+      
 |   |   |   v   |       |      
 ^   |   |   |   +-&lt;-+   |      
 |   |   ^   |       |   v      
 |   +---+   +--&gt;----+   |      
 |                       |      
 |   +-------+   +---+   |      
 |   |       |   v   |   |      
 +---+       +---+   +---+      


Output:    

 +&gt;--+   ^---+   +-------+
 |   |   |   |   ^       |
 |   |   |   |   +---+   |
 |   |   |   |       |   |
 |   +---+   v-----&gt;-+   |
 |                       |
 |   +-------+   +---+   v 
 |   |       |   |   |   |
 +---+       +-&lt;-+   +---+  


4.

Input:

n = 1

+--+ 
|  |
|  +---+
|      |     
+----+ |
     | |
     +-+ 


Output:

+--+ 
|  |
|  +---+
|      |     
+----+ |
     | |
     +-+ 


5.

Input

n = 4 

^&gt;&gt;&gt;&gt;
^   v
^   v&gt;&gt;&gt;&gt;
^       v           
&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;v


Output:

^&gt;&gt;&gt;&gt;
^   v
^   v&gt;&gt;&gt;&gt;
^       v           
&lt;&lt;&lt;&lt;&lt;&lt;&lt;&lt;v


6.

Input:

n = 1

^-&gt;
^ v
&lt;&lt;v


Output:

^&gt;+
^ v
&lt;&lt;v


Write a function or a program solving the above task. The shortest code in bytes in every language wins. Don't be discouraged by the golfing languages. Explanation of the algorithm and the code is highly appreciated.
JavaScript (ES6), &nbsp;210 ... 182&nbsp; 180 bytes

Takes input as (m)(n), where \$m\$ is a list of lists of characters. Returns the result in the same format.



m=&gt;g=n=&gt;n?g(n-1,m=m.map((r,y)=&gt;r.map((c,x)=&gt;(i=0,h=$=&gt;~$?(m[Y=y+($-2)%2]||0)[X=x+~-$%2]&gt;h?"-|+"[n+=`;m[${Y}][${X}]=S[${$}]`,i?2:$&amp;1]:h($^++i):c)((S="&lt;^&gt;v").indexOf(c)))),eval(n)):m


Try it online!

How?

You can follow this link to see a formatted version of the source.

Wrapper

The recursive function \$g\$ is just used as a wrapper that invokes the main code to move all arrows by 1 step and keeps calling itself with \$n-1\$ until \$n=0\$.

Update method

We can't safely move each arrow one at a time because we would run the risk of overwriting non-updated arrows with updated ones. Instead, we first remove all arrows and compute their new positions. We apply the new positions in a second time.

This is done by re-using \$n\$ as a string to store the position updates as JS code.

For instance, in the first test case, \$n\$ is set to:

"1;m[0][7]=S[2];m[1][8]=S[3];m[2][9]=S[2];m[4][3]=S[0]"


(Note that the leading number -- which is the original value of \$n\$ -- is harmless.)

The new positions are applied by just doing eval(n).

Directions

Each arrow is converted into a direction \$d\$ (named $ in the code), using the following compass:

$$\begin{matrix}
&amp;1\\
0&amp;+&amp;2\\
&amp;3
\end{matrix}$$

The corresponding values of \$dx\$ and \$dy\$ are computed this way:

 d | dx = (d - 1) % 2 | dy = (d - 2) % 2
---+------------------+------------------
 0 |        -1        |         0
 1 |         0        |        -1
 2 |        +1        |         0
 3 |         0        |        +1


Corners

If the next character in the identified direction is a space or is out of bounds, it means that we're located on a corner and we need to take a 90° or 270° turn. This is why the helper function \$h\$ is testing up to 3 distinct directions: \$d\$, \$d\operatorname{xor}1\$ and \$d\operatorname{xor}3\$.

If we're located on a corner, we overwrite the cell with +. Otherwise, we overwrite it with either - or |, depending on the parity of \$d\$.

Note: The parameter of \$h\$ is not named $ just because it looks uber l33t but also because it allows us to compare a given character with \$h\$ (implicitly coerced to a string) to know if it's a space (below "$"), a contour character (above "$") or another arrow (also above "$").

Animated version



f=
m=&gt;g=n=&gt;n?g(n-1,m=m.map((r,y)=&gt;r.map((c,x)=&gt;(i=0,h=$=&gt;~$?(m[Y=y+($-2)%2]||0)[X=x+~-$%2]&gt;h?"-|+"[n+=`;m[${Y}][${X}]=S[${$}]`,i?2:$&amp;1]:h($^++i):c)((S="&lt;^&gt;v").indexOf(c)))),eval(n)):m

m = [
  [..."+---+   +---+   +-------+"],
  [..."|   |   |   v   |       |"],
  [..."^   |   |   |   +-&lt;-+   |"],
  [..."|   |   ^   |       |   v"],
  [..."|   +---+   +--&gt;----+   |"],
  [..."|                       |"],
  [..."|   +-------+   +---+   |"],
  [..."|   |       |   v   |   |"],
  [..."+---+       +---+   +---+"]
];

(F = _ =&gt; (o.innerHTML = m.map(r =&gt; r.join('')).join('\n'), m = f(m)(1), window.setTimeout(F, 100)))()
&lt;pre id=o&gt;&lt;/pre&gt;