| Bytes | Lang | Time | Link |
|---|---|---|---|
| 051 | Nekomata + n | 250725T085202Z | alephalp |
| 419 | Python | 250722T123435Z | SevC_10 |
| 219 | JavaScript ES11 | 250523T220545Z | Arnauld |
| 084 | Charcoal | 250525T144843Z | Neil |
| 064 | Jelly | 250524T164606Z | Jonathan |
| 104 | 05AB1E | 250523T141717Z | Kevin Cr |
Nekomata + -n, 51 bytes
~q"<><>"q3L=$qŤqN:Ť#3*äLᶜ{↔Ť↔Ť}:ᵐ↔Ðᵐ{xᶻŘᵐh≡}"\\/"=?
Takes input as a list of strings. The lengths of these strings must be the same.
This is slow for large inputs, so I did not test the last test case.
Explanation
For small fish (><> or <><), we simply check that its length is 3 and that it is a substring of <><>.
Larger fish are more interesting. We first check the size of the rectangle: the width must be 3/2 of the height. After that, we optionally rotate the rectangle by 180 degrees, so that the fish is in the correct orientation (if it really is a fish).
Then we rotate the nth column of the rectangle by n positions, and check that all characters in the first row are \. For example, let's look at this fish:
\ /\
\/ \
/\ /
/ \/
After rotating the nth column of the rectangle by n positions, it becomes:
\\\\\\
/ / /
/ / /
All characters in the first row are \, so this is potentially a fish.
We also need to check the / part. Now we take the original rectangle, reverse each column, and then rotate the nth column of the rectangle by n positions again. Now we check that all characters in the first row are /.
This is enough to determine whether the rectangle is a fish or not.
The code non-deterministically takes a rectangle from the input, and checks if it is a fish using the above method. -n counts the number of solutions.
Python, 419 bytes
from re import match as M
R=range
B=bool
s='.'
def f(l):
t=0
for w,r in enumerate(l):
for c in R(len(r)):
t+=('<><'==r[c:c+3])+('><>'==r[c:c+3])
for d in R(len(l)):
e,g,h=d+1,0,0
for i in R(e):
a,b,k=r'\\'+s*(d-i)*2,s*i*2,l+[s]*e*e
p,q,x,y=fr'{s*i}{a}/{b}\\',f'{s*(d-i)}/{b}{a}/',k[w+i][c:],k[w+i+e][c:]
g,h=g+B(M(p,x))+B(M(q,y)),h+B(M(q,x))+B(M(p,y))
t+=(g==2*e)+(h==2*e)
return t
Commented code:
from re import match as M
R=range
B=bool
# single character in the regex search pattern
s='.'
def f(l):
"""
Finds the fish in the text
params
l: list of text lines
return: total fish found
"""
# total fish found
t=0
# loop on text lines
for w,r in enumerate(l):
# loop on the (indexes of) charaters of the line
# search fish starting from this position
for c in R(len(r)):
# search fish of size 0 (height = 1 line)
t+=('<><'==r[c:c+3])+('><>'==r[c:c+3])
# loop on fish size to search on text lines
# up to the maximum number of lines of text
for d in R(len(l)):
# e = fish size (d = 0, e = 1: fish on 2 lines)
# g, h = counter of matched lines for right- and left-facing fish
e,g,h=d+1,0,0
# loop index on fish size
# to search all corresponding pattern on consecutive lines
for i in R(e):
# a, b = auxiliary strings to build regex pattern
# k = extend the original text lines list to avoid index error
# (meaningless added lines to not match any fish)
a,b,k=r'\\'+s*(d-i)*2,s*i*2,l+[s]*e*e
# p, q = pattern for regex search of fish shape
# considering spaces between symbols scaled according to fish size (d)
# and based on the line of the fish (i)
# x, y = lines of text in which to check the current patterns
# x for the upper half of the fish, y for the lower half of the fish
p,q,x,y=fr'{s*i}{a}/{b}\\',f'{s*(d-i)}/{b}{a}/',k[w+i][c:],k[w+i+e][c:]
# check the matching pattern on lines
# (for each fish check a line in upper and lower half)
# add to the counter for each right- and left-facing fish
g,h=g+B(M(p,x))+B(M(q,y)),h+B(M(q,x))+B(M(p,y))
# add to the total fish found if matched lines correspond to fish size
# (e = 1, fish on 2 lines: found if 2 lines matched)
t+=(g==2*e)+(h==2*e)
# return total number of fish
return t
JavaScript (ES11), 219 bytes
Expects a matrix of characters.
m=>m.map((r,y)=>r.map((c,x)=>m.map((_,n)=>N+=n?[1,-1].some(o=>(g=k=>k?m[Y-=-2/i|1]?.[X+=i>1?o:-o]=="\\/"[i+!~o&1]&&g(k-1):++i)(n*2,i=0,X=x+o,Y=y+1)*g(n,Y--)*g(n,X-=o)*g(n*2,Y++)):/<><|><>/.test(r[x-1]+c+r[x+1]))),N=0)|N
Commented
m => // m[] = input matrix
m.map((r, y) => // for each row r[] at index y in m[]:
r.map((c, x) => // for each character c at index x in r[]:
m.map((_, n) => // for n = 0 to m.length - 1:
N += // update the output N
n ? // if n is not 0:
[1, -1].some(o => // for o = 1 and o = -1:
( g = k => // g is a recursive function taking k
k ? // if k is not 0:
m[Y -= -2 / i | 1]?.[ // update Y (i=0,1,2,3 → -1,+1,+1,-1)
X += i > 1 ? o : -o // update X (i=0,1,2,3 → -o,-o,+o,+o)
] == "\\/"[ // test the character at M[Y][X],
i + !~o & 1 // which must be either '\' or '/'
] && g(k - 1) // if successful, do a recursive call
: // else (k = 0):
++i // stop and increment i
)( // 1st call to g with:
n * 2, i = 0, // k = 2n, i = 0
X = x + o, Y = y + 1 // and (X,Y) set to the correct
) // starting position
* g(n, Y--) // 2nd call with k = n and Y adjusted
* g(n, X -= o) // 3rd call with k = n and X adjusted
* g(n * 2, Y++) // 4th call with k = 2n and Y adjusted
) // end of some()
: // else (n = 0):
/<><|><>/.test( // look for a small fish ('<><' or '><>')
r[x - 1] + c + r[x + 1] // centered at the current position
) //
) // end of map()
), // end of map()
N = 0 // start with N = 0
) | N // end of map(); return N
Path
Below is the path that is tested with the 4 calls to the helper function \$g\$, given the current position \$(x,y)\$, the fish size \$n\geq1\$ and the horizontal direction \$o\$ (a symmetric path is also tested with \$o=-1\$ for a fish facing right).
Charcoal, 84 bytes
WS⊞υ⪫ ιUMυ⁺ι⮌§⮌υκFLυFE⊘⁻Lυι✂υι⁺ι⊗⊕κFL⌊υM⁼⭆×¹·⁵Lκ⁺§§κμ⁺λμ§§⮌κμ⁺λμ…\/׳Lκ→I⁺ⅈL⌕AΣυ><>
Try it online! Link is to verbose version of code. Takes input as a rectangular list of newline-terminated strings. Explanation:
WS⊞υ⪫ ι
Input the strings and give them extra side padding to avoid cyclic indexing wraparound false positives.
UMυ⁺ι⮌§⮌υκ
Rotate the input by 180° and append that to the right so that only right-facing fish need to be detected.
FLυFE⊘⁻Lυι✂υι⁺ι⊗⊕κ
Loop through all sets of rows of even height.
FL⌊υM⁼⭆×¹·⁵Lκ⁺§§κμ⁺λμ§§⮌κμ⁺λμ…\/׳Lκ→
Loop through all column indices and count any fish of that height found.
I⁺ⅈL⌕AΣυ><>
Add on any one-liners. (FindAll finds with overlaps, whereas Count would not.)
Jelly, 64 bytes
U,µŒd;ŒDẈ.ị$ƙ$ŒHḢ€ZẎ€Q€F)UÐeċØ^
ẆZ€$⁺€ẎµZL÷3Ḥ⁼LµƇÇ€
KOṡ3IA§=4;ÇS
A monadic Link that accepts a list of lists of \< >/ characters (lines of equal length) and yields the count of fish.
Try it online! Or see the test-suite.
How?
KOṡ3IA§=4;ÇS - Main Link: Lines
K - join with spaces
O - cast to ordinals ("\< >/" -> [92,60,32,62,47])
ṡ3 - length three slices -> [[a, b, c], ...]
I - forward differences of each -> [[b-a, c-b], ...]
A - absolute values of all
§ - sum of each
=4 - for each: equals 4? -> is tiny fish
;Ç - concatenate Link_2(Lines) (see below)
S - sum -> total count of fish
ẆZ€$⁺€ẎµZL÷3Ḥ⁼LµƇÇ€ - Link_2: Lines
ẆZ€$ - transpose each non-empty contiguous sublist
⁺€ - ...and do that again for each
Ẏ - tighten -> all rectangles
µ µƇ - keep those for which:
ZL - transpose, length (i.e. the width)
÷3Ḥ - divided by three then doubled
⁼L - equals length (i.e. 2/3 width = height)
Ç€ - call Link_3 for each such rectangle
U,µŒd;ŒDẈ.ị$ƙ$ŒHḢ€ZẎ€Q€F)UÐeċØ^ - Link_3: Rectangle
U - reverse each row
, - pair with {Rectangle}
µ ) - for each:
Œd;ŒD - antidiagonals concatenate diagonals
ƙ$ - for groups of...
Ẉ - ...equal: lengths
.ị$ - ...do: get [last, first]
ŒH - split into two halves
Ḣ€ - head of each (the parts that would constitute a full-scale fish)
Z - transpose
Ẏ€ - tighten each
Q€ - deduplicate each
F - flatten
UÐe - reverse the second
ċØ^ - count occurrences of "/\"
-> number of fish of Rectangle's size
05AB1E, 114 109 104 bytes
J»…><>º2ä¢IεN>xs3*"I€üÿø€üÿ".VεDεεRεºθ}}}«}JNU1DúεXN+4и2.ýZXĀ+N_XΘ*-.ø1šs•1ø~üû•S.Λ}θVεε»YÏ…\/\X>׺Q]˜«O
Input as a rectangular matrix of characters.
I attempted to make a test suite, but it became a complete mess with work-around fixes (mostly because the Canvas should be cleared). Nonetheless, here is one for the original 114 bytes version, with additional .BðδÜõK.B» within the code and X>4и2.ýZ>.ø1šð•1ø~üû•S.Λ\ after each test case to clear up the Canvas for the next iteration: verify all test cases.
Explanation:
Step 1: Count all small fish (that only use >< as characters):
J # Join the rows of the (implicit) input-matrix together
» # Then join this list of lines with newline delimiter
…><> # Push string "><>"
º # Mirror it to "><><><"
2ä # Split it into two equal-sized parts: ["><>","<><"]
¢ # Count how many times each of those occur in the string
Step 2: Count all larger fish (that only use \/ as characters):
I # Push the input-matrix again
ε # Map over each row:
N> # Push the 1-based row-index
x # Double it (without popping)
s # Swap so the 1-based index is at the top again
3* # Multiply it by 3
"I€üÿø€üÿ" # Push this string, where the `ÿ` are replaced with the 3(N+1)
# and 2(N+1) respectively
.V # Execute it as 05AB1E code:
I # Push the input-matrix
€ # Map over each row:
üÿ # Split it into overlapping 3(N+1)-sized sublists
ø # Zip/transpose; swapping rows/columns
€ # Map over each list again:
üÿ # Split it into overlapping 2(N+1)-sized blocks
# (we now have a list of overlapping 3(N+1) by 2(N+1) sized blocks
εDεεRεºθ}}}«} # Create a mirrored copy of each block:
ε } # Map over the list of lists of blocks
D # Duplicate the current list of blocks
εε }} # Nested map over each row of each block
R # Reverse the row
ε } # Inner map over each character within this row
º # Mirror it
θ # Pop and leave the last (mirrored) character
« # After the inner maps: merge the lists of blocks together
J # Join each inner-most row of characters together
NU # Store the current 0-based map-index in variable `X`
1Dú # Push " 1" (push 1; duplicate; pad that many spaces)
ε # Inner map over this pair of characters:
X # Push `X`
N+ # Add the inner map's 0-based index `N` to it
4и # Repeat that four times
2.ý # Intersperse with 2s
Z # Push the maximum (the `X+N`) without popping the list
XĀ+ # Add (X!=0) to it
- # And then possibly decrease it by:
N_ # (N==0)
* # AND
XΘ # (X==1)
.ø # Prepend & append that `X+N+(X!=0)-((N==0)*(X==1))`
1š # And prepend an additional 1
s # Swap so the current character (" " or "1") is at the top
•1ø~üû• # Push compressed integer 8332107655
S # Convert it to a list of digits
.Λ # Use the (modifiable) Canvas builtin with these three options
}θ # After the map: keep the last one for character "1"
# (the first iteration with " " was to clean the Canvas)
V # Pop and store this fish-shape in variable `Y`
Try this sub-step online to see the created fish-shapes.
εε # Nested map over each block:
» # Join the lines together with newline delimiter
Y # Push the fish-shape of 1s from variable `Y`
Ï # Only keep the characters at the truthy (==1) locations
…\/\ # Push string "\/\"
X>× # Repeat it `X+1` amount of times
º # Mirror that entire string
Q # Check if the two strings are equal
] # Close all maps
˜ # Flatten the list of lists of 0s and 1s
Step 3: Sum those counts together, and output the result
« # Merge this list to the pair of step 1
O # Take the sum of this list
# (after which the result is output implicitly)
See this 05AB1E tip of mine (section How to compress large integers?) to understand why •1ø~üû• is 8332107655.
See this 05AB1E tip of mine to understand how the Canvas builtin works, and why I've used the following options to draw the fish:
- Character to draw:
1 - Directions to draw in: \$[8,3,3,2,1,0,7,6,5,5]\$ aka \$RESET↘↘→↗↑↖←↙↙\$
- Lengths of each section: \$v=X+N, \\ t=v+(X\neq0)-(N=0)\times(X=1) \\ [1,t,v,2,v,2,v,2,v,t]\$
