g | x | w | all
Bytes Lang Time Link
051Nekomata + n250725T085202Zalephalp
419Python250722T123435ZSevC_10
219JavaScript ES11250523T220545ZArnauld
084Charcoal250525T144843ZNeil
064Jelly250524T164606ZJonathan
10405AB1E250523T141717ZKevin Cr

Nekomata + -n, 51 bytes

~q"<><>"q3L=$qŤqN:Ť#3*äLᶜ{↔Ť↔Ť}:ᵐ↔Ðᵐ{xᶻŘᵐh≡}"\\/"=?

Attempt This Online!

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

Attempt This Online!


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

Attempt This Online!

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

path

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.

Try it online.

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

Try just step 1 online.

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

Try just step 2 online.

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: