Help, I'm trapped in an infinite factory!
Julia - 394 300 246 214 bytes
f(A,x,y)=(Z=sign;(S,T)=size(A);X=x+1;Y=y+1;G=fill(5,S+2y,T+2x);G[Y:S+y,X:T+x]=A;C=0G;D=1;while C[Y,X]!=D C[Y,X]=D;i,j=sum(i->1-[19 8]i%82%3,G[Y:Y+y-1,X:X+x-1]);D=Z(2i^2-2j^2+(i!=0)D);X+=Z(i+D*i);Y+=Z(j-D*j)end;D^2)
Returns 1 if the cargo loops, and 0 if it comes to a stop. It's not "strictly" truthy/falsy, in that Julia doesn't permit 0 and 1 in boolean contexts... but I consider values x
for which bool(x)==true
are truthy and bool(x)==false
are falsy.
Input A
should be of the form of a character array. If you're copy/pasting the conveyor grid, you'll need to get it into the appropriate form. To save you from having to manually handle it, use the following:
A=foldl(hcat,map(collect,split("""(PASTE GRID HERE)""","\n")))'
Where obviously, (PASTE GRID HERE)
should be replaced with the grid itself. Double-check the spaces at the end of each line, to make sure it actually has all of the spaces (it doesn't check to make sure that all lines are equal length). Note that this line isn't part of the actual solution code, just a convenient piece of code to make using the solution code a bit easier.
Ungolfed:
function f(A,x,y)
# Determine size of grid for use later
(S,T)=size(A)
# Initialise starting position (performed here to save characters)
X=x+1
Y=y+1
# Create an expanded field that is functionally "spaces" (to provide
# spaces at edges for the cargo to stop in)
G=fill(5,S+2y,T+2x)
# Put the conveyor grid into centre of the expanded field
G[Y:S+y,X:T+x]=A
# Create an array storing the most recent movement direction:
# will use 1=horizontal, -1=vertical, 0=stopped
C=0G
# Initialise current direction (same system as C)
D=1
# Loop until it finds itself repeating a coordinate/direction pair
while C[Y,X]!=D
# Mark current coordinate/direction pair in array
C[Y,X]=D
# Determine the net coordinate pairing, stored in two variables
# for golf purposes *SEE NOTE*
i,j=sum(i->1-[19 8]i%82%3,G[Y:Y+y-1,X:X+x-1])
# Determine new movement axis (if D=0, cargo stopped)
D=sign(2i^2-2j^2+(i!=0)D)
# Update X or Y depending on signs of D and the appropriate direction
X+=sign(i+D*i)
Y+=sign(j-D*j)
end
# if D=±1, return 1 (cargo is still moving), otherwise return 0
return D^2
end
Note: 1-[19 8]i%82%3
has been chosen to map the five possible characters to the appropriate coordinate pairs by the most efficient method I could find. This is also the reason for using 5 to fill the spaces when creating G
- it's a single-digit character that maps to [0 0]
.
Example of usage:
julia> A=foldl(hcat,map(collect,split(""">v>v>v
^v^v^v
^v^v^v
^>^>^v
^<<<<<""","\n")))';
julia> f(A,2,1)
true
julia> f(A,3,3)
true
julia> f(A,5,2)
false
Python 2, 207 bytes
def f(L,w,h,u=0,v=0,D=1,S=[]):a,b,c,d=map(`[r[u*(u>0):u+w]for r in L[v*(v>0):v+h]]`.count,"v^><");D=cmp(abs(a-b),abs(c-d))<D;T=u,v,D;return T in S or a-b|c-d and f(L,w,h,u+cmp(c,d)*D,v+cmp(a,b)*0**D,D,S+[T])
Input the grid as a list of rows, e.g.
['>v>v>v', '^v^v^v', '^v^v^v', '^>^>^v', '^<<<<<']
followed by the width and height. Returns 0
or True
accordingly.
Explanation
def f(L, # Grid
w,h, # Width, height of cargo
u=0,v=0, # Position of top-left of cargo, initially (0, 0)
D=1, # Moving left/right = 1, up/down = 0
S=[] # Seen (pos, axis) pairs, initially empty
):
# Arrows under cargo - no need for "".join since we only need to count v^<>
A = `[r[u*(u>0):u+w]for r in L[v*(v>0):v+h]]`
# Count for each arrow
a,b,c,d=map(A.count,"v^><")
# Golfed form of abs(a-b) < abs(c-d) or (abs(a-b) == abs(c-d) and D == 1)
D=cmp(abs(a-b),abs(c-d))<D
T=u,v,D
return (T in S # Return True if (pos, axis) previously seen
or a-b|c-d # Return 0 if all conveyors cancel
and f(L,w,h, # Otherwise, recurse
u+cmp(c,d)*D, # Update u if moving left/right
v+cmp(a,b)*0**D, # Update v if moving up/down
D,
S+[T] # Add (pos, axis) to seen
)
)
Ruby, 306 298 251 204 198
->g,w,h{m=->y,x,d,v=[]{q=y,x
r=->s{([""]*h+g)[y+h,h].map{|l|(?x*w+l)[x+w,w]}.join.count s}
z=k=r[?v]-r[?^],j=r[?>]-r[?<]
q[d=[d,1,0][j*j<=>k*k]]+=z[d]<=>0
v&[q<<d]!=[]?q!=v[-1]:m[*q,v<<q]}
m[0,0,1]}
Edit: Thanks a lot to Ventero who shortened the code a lot by applying some amazing tricks!
Input and output
The code represents a ruby function that takes three parameters:
- the grid, represented as an array of strings (each row is a different string)
- the width of the cargo
- the height of the cargo
It returns 1
(truthy) in case there's a loop, or nil
(falsy) in case the cargo rests.
Tests
Here it is passing all of Martin's tests: http://ideone.com/zPPZdR
Explanation
There are no clever tricks in the code; it's a pretty straightforward implementation of the rules.
In the code below, move
is a recursive function that makes a move according to the rules, and:
- returns truthy in case of a loop
- returns falsy in case of a rest
- otherwise calls itself to execute the next move
A more readable version is available here.
Note: the golfed code has undergone several modifications and is no longer similar to the readable version.