| Bytes | Lang | Time | Link |
|---|---|---|---|
| 374 | Python 3.8 prerelease | 250601T154945Z | Ted |
| 167 | JavaScript ES7 | 250601T183145Z | Arnauld |
| 070 | Charcoal | 250601T112728Z | Neil |
| 044 | Jelly | 250601T181332Z | Jonathan |
Python 3.8 (pre-release), 536 374 bytes
def f(b):
s,h,w,z="-\|/",len(b),len(b[0]),0
for r in range(h):
for c in range(w):
if(v:=b[r][c])in s:y,x,k,l,z=r,c,*[[0,1],[1,1],[1,0],[1,-1]][s.find(v)],5
while z:
if" "!=b[y][x]!=v or v=="\\"and"//"==b[y-1][x]+b[y][x-1]or v=="/"and"\\\\"==b[y-1][x]+b[y][x+1]:return 1
while(0in[y-h+1+k,x-w+1+l,y+k,x+l])*z:k,l=-k,-l;z-=1
b[y][x]=v;y+=k;x+=l
return 0
Explanation
s is all line characters, h and w are the height and width of the board. z is used to run the main loop, so set it to 0 to avoid triggering it until we find a line.
s,h,w,z="-\|/",len(b),len(b[0]),0
Iterate through the board
for r in range(h):
for c in range(w):
Store the value of the cell we're looking at in v. If v is a line character, set z to begin the loop to check for intersections.
Start looking at y,x which is initially set to the current cell r,c. k,l are set to the direction associated with the current line character. z keeps track of when we've finished analysing this cell.
if(v:=b[r][c])in s:y,x,k,l,z=r,c,*[[0,1],[1,1],[1,0],[1,-1]][s.find(v)],5
An intersection has occurred in any of the following conditions:
- If the currently extended point (
b[x][y]) is anything except for an empty space, or the character we're currently extending - If the character being investigated is
\, and the cell directly above, and directly to the left are both/ - If the character being investigated is
/, and the cell directly above, and directly to the right are both\
while z:
if" "!=b[y][x]!=v or v=="\\"and"//"==b[y-1][x]+b[y][x-1]or v=="/"and"\\\\"==b[y-1][x]+b[y][x+1]:return 1
This is slightly complicated. Essentially, this serves to reverse the direction we're moving along the line when we hit an edge. This is what the checks for [y-h+1+k,x-w+1+l,y+k,x+l] do. If any of them are 0, our next move would put us on an edge. In that case, we reverse the direction of k and l.
However, we also can end up in a situation where a move in either direction will put us on the edge. E.g.: if the board is 1x1. Therefore, we need to prevent the loop from continuing if this is the case. So instead of an if statement, this is a while loop, which also decrements z, which is the condition controlling the outer loop.
while(0in[y-h+1+k,x-w+1+l,y+k,x+l])*z:k,l=-k,-l;z-=1
Set the current cell to the character being investigated, move the cursor.
b[y][x]=v;y+=k;x+=l;
JavaScript (ES7), 167 bytes
Expects a matrix of ASCII codes. Returns a Boolean value.
m=>m.some((r,y)=>r.some((c,x)=>c**3&72&&L.some(([a,b,c])=>(g=(v,q)=>(v/=a*B-A*b)>.5&v<q.length-1.5)(a*C-A*c,m)&g(c*B-C*b,r),L.push([A=c%7-3,B=c/3&2,C=A*x+B*y]))),L=[])
Method
Summary
We scan the input matrix from top-left to bottom-right, storing the equation parameters of the detected lines along the way and testing if any new line intersects a previous one within the canvas.
Detecting line intersections
For each line character \$c\$ at position \$(X,Y)\$ in the input matrix, we push in the array \$L\$ the parameters \$(A,B,C)\$ of the equation of the corresponding line:
$$Ax+By=C$$
where \$C=AX+BY\$ in all cases, and \$A\$, \$B\$ are defined as follows:
| Character | A | B | Equation |
|---|---|---|---|
\ |
\$-1\$ | \$1\$ | \$-x+y=C\$ |
/ |
\$1\$ | \$1\$ | \$x+y=C\$ |
- |
\$0\$ | \$1\$ | \$y=C\$ |
| |
\$1\$ | \$0\$ | \$x=C\$ |
For each line already stored in \$L\$ with parameters \$(a,b,c)\$, we compute its intersection point \$(x,y)\$ with the new line:
$$x=\frac{cB-Cb}{aB-Ab}$$ $$y=\frac{aC-Ac}{aB-Ab}$$
and test whether we have \$x\in(1/2,W-3/2)\$ and \$y\in(1/2,H-3/2)\$, where \$H\$,\$W\$ are the dimensions of the matrix.
NB: Although the above definitions of \$x\$ and \$y\$ are valid only if \$aB-Ab\neq0\$, this will also work as-is in the JS implementation for \$aB-Ab=0\$ (we'll get either NaN or +/-Infinity, which will make the tests fail as expected).
Character processing
The table below shows the formulas that are used to determine if an ASCII code matches a line character and, if it does, which values of \$A\$ and \$B\$ must be used (note that both values are doubled, which doesn't change the definition of the line).
| Character | Type | c = ASCII code | c**3&72 | A = c%7-3 | B = c/3&2 |
|---|---|---|---|---|---|
+ |
border | 43 | 0 | n/a | n/a |
= |
border | 61 | 0 | n/a | n/a |
[ |
border | 91 | 0 | n/a | n/a |
] |
border | 93 | 0 | n/a | n/a |
|
background | 32 | 0 | n/a | n/a |
\ |
line | 92 | 64 | -2 | 2 |
/ |
line | 47 | 8 | 2 | 2 |
- |
line | 45 | 64 | 0 | 2 |
| |
line | 124 | 64 | 2 | 0 |
Charcoal, 95 70 bytes
WS⊞υιυUE¹¦¹F⊗LυF⊗LθF⁸«Jκι≔§-/|\λη¿⁼η⊟KD²✳λ«W‹!KK✳⁺⁴λ¹¿№⁻-/|\ηKK≔-ω»»⎚ω
Try it online! Link is to verbose version of code. Takes a rectangular array of newline-terminated strings as input and outputs a Charcoal boolean, i.e. - if any rays intersect, nothing if not. Explanation:
WS⊞υιυUE¹¦¹
Input the canvas, write it to Charcoal's canvas, then double-space it, using @UnrelatedString's tip to @JonathanAllan.
F⊗LυF⊗LθF⁸«
Loop over all possible cells and directions.
Jκι≔§-/|\λη
Jump to the cell and work out the ray character for the current direction.
¿⁼η⊟KD²✳λ«
If there is an appropriate ray entering this cell, then...
W‹!KK✳⁺⁴λ¹
... extend the ray though any empty spaces, and...
¿№⁻-/|\ηKK≔-ω
... check whether the ray directly intersected a ray in a different direction.
»»⎚ω
Clear the canvas and output the final result.
Jelly, 59 44 bytes
-13 bytes thanks to Unrelated String (explode the grid first so \ always overlaps / at some character).
K€Z$⁺=Ɱ“-|/\”¹ZŒdŒD4ƭoẸ$€¹ZŒḍŒḌ4ƭƊ€SZṖḊƊ4¡ỊȦ
A monadic Link that accepts a list of lines and yields 0 if the rays intersect or 1 if not (i.e. inverted).
Try it online! Or see the test-suite.
How?
Explode the canvas with space characters:
K€Z$⁺
$ - last two links as a monad - f(Canvas):
K€ - join each with space characters
Z - transpose
⁺ - repeat that -> A Canvas with space filling between rows AND columns
Create ray component masks:
=Ɱ“-|/\” - vectorised equals? mapped across "-|/\"
Extend rays within each of these four masks:
¹ZŒdŒD4ƭoẸ$€¹ZŒḍŒḌ4ƭƊ€
Ɗ€ - for each {Mask}:
4ƭ - apply each of these four in turn:
¹ - no-op ('-' uses rows)
Z - transpose ('|' uses columns)
Œd - anti-diagonals (for '/')
ŒD - diagonals (for '\')
€ - for each "Strip":
$ - last two links as a monad:
Ẹ - any? -> Strip contains ray segment(s)
o - {Strip} vectorised logical OR {that}
-> Strip of all ones if any 1s existed, else zeros
4ƭ - apply each of these four in turn:
¹ - no-op
Z - transpose
Œḍ - convert back from anti-diagonals
ŒḌ - convert back from diagonals
Check for crossing rays
SZṖḊƊ4¡ỊȦ
S - sum respective mask values together
4¡ - do this four times:
Ɗ - last three links as a monad:
Z - transpose
Ṗ - pop
Ḋ - dequeue
Ị - insignificant (vectorises) --> 1 at 0 or 1, else 0
Ȧ - any and all?