31
\$\begingroup\$

Go Fish!

Given a school of fish, for example:

  ><>        ><>       ><>      ><>      ><>
<><    <><            ><>    <><    ><>
 ><>      ><>     ><><><        ><>><>   <><
    ><>  ><>        <><     ><>    <><
 ><>        ><>         ><>     ><>       ><>
><>  <><<><       ><>  <><><>        ><>   ><>
   ><>     ><>        ><>        ><>    <><
 ><>  ><>       <><        <><      ><>   ><>

give an output that tells how many fish tails are in each column. Eg., in the above example, there is 1 tail in the 0 column, 3 tails in the 1 column, 2 tails in the 2 column, and so on... so the output would be:

  • 1321101102211100003000321110110131012200013200

Output can be in any (reasonable) format: a string, array of integers, etc.

I shouldn't need to clarify: the tail of the fish is this part:

><>     <><
^         ^
Input Rules:

The fish can be facing either direction:

  • <>< or ><>

and are allowed to touch each other:

  • <><<><, ><>><>, <><><>, ><><><

Only valid fish will be included in the input

There is an unlimited amount of fish per row, but never more than 15 rows. Each row has trailing whitespace stripped.

Test Cases:
><>
100
<><
001
><>><> <><
 <><<><><>
><>><><><><>
200300111200
><><><><><><><><><><>
100001100001100001100

#Standard-rules-apply #Code-Golf #Have-fun

\$\endgroup\$
10
  • 4
    \$\begingroup\$ Can we take input with the lines the same length instead of stripped? \$\endgroup\$
    – Jordan
    Commented Nov 15 at 16:59
  • 1
    \$\begingroup\$ Is a list of lines a valid input, or must we use newline characters? \$\endgroup\$ Commented Nov 15 at 18:22
  • 3
    \$\begingroup\$ @Jonathan Allan You can use either, obviously a list of lines will usually be slightly shorter, up to you. \$\endgroup\$
    – Klumpy7
    Commented Nov 15 at 18:55
  • 1
    \$\begingroup\$ @emanresuA Sorry I didn’t see that. I had it in there to make the challenge more challenging, but I guess as it doesn’t add more interesting-ness, I will take the requirement out as per popular request \$\endgroup\$
    – Klumpy7
    Commented Nov 17 at 5:45
  • 1
    \$\begingroup\$ What happens if more than 10 on one pos? \$\endgroup\$
    – l4m2
    Commented Nov 17 at 7:36

21 Answers 21

13
\$\begingroup\$

Vyxal, 8 bytes

C<v¦+3Ḋ∑

Try it Online!

I/O as a matrix of charcodes. Inspired by ovs's K answer (and then backported to that to save two bytes)

       ∑ # Over each line, sum the results of
C<       # For each number, check if it's not a space
C        # by checking if if casting it to a char
 <       # results in a larger value - ' ' < '3', '<'/'>' > '6'
  v¦     # Take the cumulative sum of these
    +    # Add to the original charcodes
     3Ḋ  # And check if it's divisible by 3

As to why this works, here's an example:

  • Given ><>><> <>< ([62 60 62 62 60 62 32 60 62 60 32 32])
  • Mask of nonspaces i.e. fish characters: [1 1 1 1 1 1 0 1 1 1 0 0]
  • Cumulative sum of that: [1 2 3 4 5 6 6 7 8 9 9 9] (modulo 3: [1 2 0 1 2 0 0 1 2 0 0 0])
    • First char of fish -> 1 (modulo 3), second char -> 2, last char -> 0, space -> 0
  • input charcodes modulo 3: [2 0 2 2 0 2 2 0 2 0 2 2]
    • > -> 2, < -> 0, -> 2
  • Add these together: [0 2 2 0 2 2 2 1 1 0 2 2] (modulo 3). The only way this will result in a 0 is if:
    • a 1 from the first char of a fish is added to a 2 from a > i.e. tail of rightwards fish
    • a 0 from the last char of a fish is added to a 0 from a < i.e. tail of a leftwards fish
  • Check divisibility by 3: [1 0 0 1 0 0 0 0 0 1 0 0] i.e. fish positions
\$\endgroup\$
8
\$\begingroup\$

K (ngn/k), 18 bytes

+/{x=62-3!2+\~^x}'

Try it online!

  {             }'   / for each row:
   x=62-3!2+\~^x     /   mark the tails
+/                   / add the rows to get column count
                     / example: "><>><> <><  "
             ~^x     / is not null (not space)
                     / 1 1 1 1 1 1 0 1 1 1 0 0
          2+\        / cumulative sums, starting at 2
                     / 3 4 5 6 7 8 8 9 10 11 11 11
        3!           / modulo 3. maps each fish to 0 1 2
                     / 0 1 2 0 1 2 2 0 1 2 2 2
     62-             / subtract from 62 (>). Maps each fish to ">=<"
                     / ">=<>=<<>=<<<"
   x=                / character-wise equal to original row
                     / 1 0 0 1 0 0 0 0 0 1 0 0
\$\endgroup\$
2
  • 1
    \$\begingroup\$ 16: f:+/{0=3!x++\~^x}', might be possible to save a byte \$\endgroup\$
    – emanresu A
    Commented Nov 16 at 0:29
  • 1
    \$\begingroup\$ f:+/{~3!x++\~^x}' for 15, it'd help if I actually read the ngn/k docs \$\endgroup\$
    – emanresu A
    Commented Nov 16 at 0:52
6
\$\begingroup\$

Jelly, 14 bytes

>⁶Ä3ḍk⁸Ġ€F=2)S

A monadic Link that accepts a list of lists of < > characters and yields a list of the column-wise tail counts.

Try it online!

How?

Partition each row after either the right-hand side of each fish (whether a head or a tail) or a space character like so:

  ><>   ><><><
00111000111111    <- greater than space?
00123333456789    <- cumulative sums
11001111001001    <- is divisible by three?
ww[f]www[f][f][]  <- identified "parts" (by partitioning after 1s) 
                        shown here as: water (w);
                                       fish ([f]); and
                                       a trailing empty list ([])

Then identify the "tails" of each "part" using the group-indices-by-value monad, Ġ:

Part Type Part Ġ(Part) flatten(Ġ(Part))=2?
[f] "<><" [[1,3],[2]] [0,0,1]
[f] "><>" [[2],[1,3]] [1,0,0]
w " " [[1]] [0]
[] "" [] []
>⁶Ä3ḍk⁸Ġ€F=2)S - Link: list of lists of characters, School
            )  - for each (Row of the School):
>⁶             -    {Row} greater than the space character? (vectorises)
  Ä            -    cumulative sums of {that}
   3ḍ          -    three divides {that}? (vectorises)
     k⁸        -    partition the Row after truthy indices of {that}
       Ġ€      -    group the indices of each of {those} by their values
         F     -    flatten {that}
          =2   -    {that} equals two? (vectorises)
             S - sum {that} (column-wise)
\$\endgroup\$
5
\$\begingroup\$

Perl 5 -F, 57 50 bytes

s/(>)<>|<></$1?100:aa1/ge;s/./$;[pos]+=$&/ge}{say@

Try it online!

\$\endgroup\$
4
\$\begingroup\$

Japt, 24 21 16 bytes

Takes input as a 2D array of codepoints.

Ëí+DËgHÃå+ÃÕËxv3

Try it

Ëí+DËgHÃå+ÃÕËxv3     :Implicit input of 2D array of codepoints
Ë                    :Map each D
 î+                  :  Interleave with, reducing each pair by addition
   DË                :    Map D
     g               :      Sign of difference with
      H              :        32
       Ã             :    End map
        å+           :    Cumulatively reduce by addition
          Ã          :End map
           Õ         :Transpose
            Ë        :Map
             x       :  Reduce by addition of
              v3     :    Divisible by 3?
\$\endgroup\$
4
\$\begingroup\$

Python + NumPy, 79 bytes

lambda s:sum((cumsum((X:=c_[s].view("i4")%-8)<0,1)+X)%3>1,0)
from numpy import*

Attempt This Online!

Takes a list of rows which are strings.

Or

Python + NumPy, 77 bytes (@emanresu A)

assuming input lines are equal length.

lambda s:sum((cumsum((X:=c_[s].view("i4"))>32,1)+X)%3<1,0)
from numpy import*

Attempt This Online!

How?

Using numpy is convenient here, because the array constructor pads strings to equal length (with zeros, so normally you wouldn't necessarily notice). The view cast to integer does roughly the same as ord in standard python; it also reveals the padding.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ 77 with a slightly shorter formula, though it only works if the rows are space-padded which hasn't yet been clarified \$\endgroup\$
    – emanresu A
    Commented Nov 16 at 8:33
  • 1
    \$\begingroup\$ @emanresuA to me it seems rather clear that OP wants it ragged. We'll see, I suppose. \$\endgroup\$ Commented Nov 16 at 14:03
3
\$\begingroup\$

Retina, 97 bytes

N$^`
$.&
(\S)(\S)\1
 $2 
 <
1 
> 
 1
+`(?<=^(.)*)(.)((.*¶)+(?<-1>.)*)1(?(1)^)
$.(_$2*)$3 
 
0
0G`

Try it online! Explanation:

N$^`
$.&

Sort the rows in descending order of length.

(\S)(\S)\1
 $2 

Chop the heads and tails off all the fish.

 <
1 
> 
 1

Place 1s where the tails used to be.

+`(?<=^(.)*)(.)((.*¶)+(?<-1>.)*)1(?(1)^)
$.(_$2*)$3 

Accumulate the column totals.

 
0

Replace any remaining spaces with zeros.

0G`

Keep just the totals.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ @JonathanAllan Sorting the rows seems to do the trick here. \$\endgroup\$
    – Neil
    Commented Nov 15 at 18:49
3
\$\begingroup\$

JavaScript (ES6), 69 bytes

-2 thanks to @l4m2

Expects an array of arrays of characters (which may be of different lengths). Returns an array of integers.

a=>a.map(r=>r.map((c,i)=>b[i]=~~b[i]+(c=="<>"[a=c+3|-~a%3])),b=[])&&b

Try it online!

Commented

a =>                // a[] = input array
a.map(r =>          // for each row r[] in a[]:
  r.map((c, i) =>   //   for each character c at position i in r[]:
    b[i] =          //     update b[i]:
      ~~b[i] + (    //       coerce it to an integer in case it's still undefined
        c == "<>"[  //       test whether c is a fish tail:
          a =       //         update a:
            c + 3 | //           this gives 3 if c is a space (which forces the
                    //           test to fail and resets a to 3) or NaN otherwise
            -~a % 3 //           computes (a + 1) mod 3; if the result is 2, this
                    //           is the middle part of the fish (never a tail)
        ]           //       end of lookup
      )             //
  ),                //   end of inner map()
  b = []            //   start with b = []
) && b              // end of outer map(); return b[]
\$\endgroup\$
3
  • 1
    \$\begingroup\$ Do n need reset? it's 0 after reading fishes \$\endgroup\$
    – l4m2
    Commented Nov 17 at 7:39
  • 1
    \$\begingroup\$ @l4m2 No we don't need to reset n, but then we have to initialize it explicitly to 0 rather than b. All in all, rearranging the code saves 2 bytes. 👍 \$\endgroup\$
    – Arnauld
    Commented Nov 17 at 9:17
  • 1
    \$\begingroup\$ 69 \$\endgroup\$
    – l4m2
    Commented Nov 17 at 11:28
3
\$\begingroup\$

Ruby, 83 79 bytes

->s{a=[c=x=0]*w=s=~/\n/
s.bytes{|i|c+=i/60
i>10?a[(i-62+x+=1)%w]+=c%3/2:x=0}
a}

Try it online!

Function that takes a newline separated string as an argument and returns an array.

Assumes there is no line longer than the first line.

c is a count of the number of < and > (characters over ascii 60) found. When we are at the middle character of a fish c%3 reaches its max value of 2. The direction of the character (ascii 60 or 62) points toward the tail, and the appropriate adjacent column a[x+1] or a[x-1] is incremented.

The magic constant might be expected to be -61, based on the average of 60 and 62, but another 1 must be subtracted to compensate for the increment of x.

\$\endgroup\$
3
\$\begingroup\$

R, 95 94 bytes

-1 thanks to pajonk

\(x)Reduce(\(v,m)v+`[<-`(!1:max(nchar(x)),(m*attr(m,"m"))[-1,],1),gregexec("<>(<)|(>)<>",x),0)

Attempt This Online!

Ungolfed:

\(x) {
  Reduce(
    # for each vector of fish tail indices, increment the correct slots in the output 
    #   vector
    \(v,m) {
      v +
        # assigning 1's to the indices of fish tails in a vector of zeroes
        # (this works because the assignment form of [] silently returns the new vector
        `[<-`(
          0*1:max(nchar(x)),
          # multiply match indices by match lengths so that zero-length matches 
          #   aren't included, and exclude row 1 (total match)
          # (this works since assigning to index 0 leaves the vector unchanged)
          (m*attr(m,"m"))[-1,],
          1
         )
    },
    # find the fish tails and return a list of integer vectors, where each vector is
    #   the indices of the fish tails
    gregexec("<>(<)|(>)<>",x),
    # initial value for `Reduce`
    0
  )
}
\$\endgroup\$
2
  • \$\begingroup\$ -1 byte by replacing 0* with ! \$\endgroup\$
    – pajonk
    Commented Nov 17 at 8:39
  • \$\begingroup\$ @pajonk Oh neat \$\endgroup\$
    – Eonema
    Commented Nov 17 at 17:08
3
\$\begingroup\$

Scala 3, 178 161 bytes

Saved 17 bytes thanks to @corvus_192


Golfed version. Attempt This Online!

s=>{val z=s split "\n"flatMap(l=>"><>|<><".r.findAllMatchIn(l)map(m=>m.start+(if(m.matched=="<><")2 else 0)));if(z.isEmpty)z else(0to z.max)map(i=>z.count(i==))}

Ungolfed version. Attempt This Online!

import scala.util.matching.Regex

object Main {
  def main(args: Array[String]): Unit = {
    val inputString = """  ><>        ><>       ><>      ><>      ><>
<><    <><            ><>    <><    ><>
 ><>      ><>     ><><><        ><>><>   <><
    ><>  ><>        <><     ><>    <><
 ><>        ><>         ><>     ><>       ><>
><>  <><<><       ><>  <><><>        ><>   ><>
   ><>     ><>        ><>        ><>    <><
 ><>  ><>       <><        <><      ><>   ><>"""

    val positions = findPatternPositions(inputString)
    val result = countPositions(positions)
    val outputString = result.mkString("")
    println(outputString)
  }

  def findPatternPositions(s: String): List[Int] = {
    val pattern = "><>|<><".r
    s.split("\n").flatMap { line =>
      pattern.findAllMatchIn(line).map { m =>
        m.start + (if (m.matched == "<><") 2 else 0)
      }
    }.toList
  }

  def countPositions(positions: List[Int]): List[Int] = {
    if (positions.isEmpty) List()
    else {
      val maxPosition = positions.max
      (0 to maxPosition).map { i =>
        positions.count(_ == i)
      }.toList
    }
  }
}
\$\endgroup\$
1
  • \$\begingroup\$ 161 bytes: s=>{val z=s split "\n"flatMap(l=>"><>|<><".r.findAllMatchIn(l)map(m=>m.start+(if(m.matched=="<><")2 else 0)));if(z.isEmpty)z else(0to z.max)map(i=>z.count(i==))} (change the return type to Seq[Int]) \$\endgroup\$
    – corvus_192
    Commented Nov 22 at 21:28
2
\$\begingroup\$

Charcoal, 40 35 28 26 bytes

WSP⭆ι⁺¬﹪⁺℅κL⁻…ι⊕λ ³Σ⊟KD⊕λ→

Try it online! Link is to verbose version of code. Takes input as a list of newline-terminated strings. Explanation: Now inspired by @Ausername's version of @ovs' answer.

WS

Loop through each row of fish.

P⭆ι⁺¬﹪⁺℅κL⁻…ι⊕λ ³Σ⊟KD⊕λ→

For each character, count the number of non-spaces so far including the current character, add on the current character's ordinal, and check whether the result is divisible by 3. If so, then increment the appropriate digit of the result, otherwise replace blanks with zeros. Replace the current result with the new computed values. (Unfortunately Charcoal has no way of adding the values in two arrays to each other so it has to peek each digit of the result in turn.)

\$\endgroup\$
1
  • 1
    \$\begingroup\$ @JonathanAllan I came up with a new technique which is both shorter and also automatically includes trailing zeros. \$\endgroup\$
    – Neil
    Commented Nov 15 at 18:45
2
\$\begingroup\$

Ruby, 92 bytes

Takes an array of lines as input.

->a{h=Hash.new 0
a.map{_1.scan(/(><>)|<></){h[$`.size+($1?0:2)]+=1}}
(0...a[0].size).map &h}

Attempt This Online!

\$\endgroup\$
2
\$\begingroup\$

C++ (clang), 191 124 bytes

[](auto&r,auto&v){for(auto&s:r)for(int i=0;i+2<s.size();i++)v.resize(max(v.size(),s.size())),s[i]>59?v[i+62-s[i]]++,i+=2:0;}

Try it online!

Reduction thanks to jdt and ceilingcat

\$\endgroup\$
0
1
\$\begingroup\$

Python, 142 126 122 bytes

lambda s:[*map([m.start()+2*(m[0]<'>')for r in s for m in re.finditer(r'\S..',r)].count,range(max(map(len,s))))]
import re

Attempt This Online!

  • Input: list of strings
  • Output: list of integers
\$\endgroup\$
0
1
\$\begingroup\$

05AB1E, 12 bytes

ðÊ€ηOIÇ+3ÖøO

Similar as a bunch of other answers.

Takes the input as a list of lists of characters, including trailing spaces to make all lines the same length.

Try it online or verify all test cases.

Explanation:

ðÊ           # Check for each inner-most character in the (implicit) input-matrix
             # that it's NOT equal to a space " "
  €η         # Take the prefixes of each inner list of 0s/1s
    O        # Sum each inner-most prefix
     I       # Push the input-matrix again
      Ç      # Convert each character to its codepoint-integer
       +     # Vectorized add the values at the same positions
        3Ö   # Check for each whether it's divisible by 3
          ø  # Zip/transpose; swapping rows/columns
           O # Sum each inner list (the columns before the zip) together
             # (after which the resulting list of sums is output implicitly)
\$\endgroup\$
1
\$\begingroup\$

Uiua, 30 bytes

+@0/+⬚0⊜(=-:@>◿3+2\+≠@ .)⊸≠@\n

Try it online!, inspired by ovs's K solution

Explanation:

+@0/+⬚0⊜(=-:@>◿3+2\+≠@ .)⊸≠@\n
       ⊜(               )⊸≠@\n => split string by newlines
                    ≠@         => non-whitespace indexes
                +2\+           => accumulative addition starting at 2
              ◿3               => modulo 3
          -:@>                 => subtract 62 (>)
         =             .       => compare with original row
     ⬚0                        => fill 0s if needed
+@0/+                          => row addition and then stringify (optional)
\$\endgroup\$
1
\$\begingroup\$

Pip -r, 22 bytes

$+^gR`\S`*3{'<Q@a?Rhh}

Takes input from stdin; requires it to be padded to a rectangle with spaces. Attempt This Online!

Explanation

$+^gR`\S`*3{'<Q@a?Rhh}
   g                    ; List of lines of stdin (-r flag)
    R                   ; In each line, replace
     `\S`*3             ; regex match of three non-space characters (guaranteed
                        ; to be a fish because only well-formed fish are present)
           {         }  ; with this callback function:
            '<Q@a?      ;   If the fish starts with <
                  Rh    ;   Reverse of h (001)
                    h   ;   Else, h (100)
  ^                     ; Split into a 2D matrix of characters
$+                      ; Sum (treating spaces as 0s)
\$\endgroup\$
1
\$\begingroup\$

C# (.NET Core), 189 bytes

int[]c(string f){var p=f.Split('\n');var t=new int[p.Max(s=>s.Length)];foreach(var x in p)for(int m=0;m<2;m++)foreach(Match n in Regex.Matches(x,m>0?"<><":"><>"))t[n.Index+m*2]++;return t;}

Try it online!

Full code:

    
using System;
using System.Text.RegularExpressions;

public class Program
{
    public static void Main()
    {
        string r = "<>< ><> ><>><>" + Environment.NewLine +
                          "  <>< <><     ><>    ><>" + Environment.NewLine +
                          " <>< <><" + Environment.NewLine;

        int[] c (string f)   
        {
            var p = f.Split('\n');
            int m = 0;

            foreach (var x in p)
                m = Math.Max(m, x.Length);

            var t = new int[m];

            foreach (var x in p)
            {
                foreach (Match n in Regex.Matches(x, "<><"))
                    t[n.Index + 2]++;
                foreach (Match n in Regex.Matches(x, "><>"))
                    t[n.Index]++;
            }
            
            return t;
        }
        
        Console.WriteLine(string.Join(",", c(r)));
    }
}



```
\$\endgroup\$
4
  • 4
    \$\begingroup\$ Welcome to Code Golf, and nice first answer! Answers on this site must be either functions that take in input and return output, or full programs that use STDIN/STDOUT - I/O via variables is not allowed - so you should either write this to use STDIN/STDOUT or wrap it in a function that takes f and returns t. Additionally, we require answers to make a serious attempt at being golfed, which in this case means removing unnecessary whitespace. Also, you might want to add a TIO link so others can test your answer. \$\endgroup\$
    – emanresu A
    Commented Nov 19 at 2:42
  • \$\begingroup\$ Go it. Latest edit reflect (hopefuly satisfy) the rulles. \$\endgroup\$
    – Pepik
    Commented Nov 21 at 14:57
  • \$\begingroup\$ 189 bytes \$\endgroup\$
    – jdt
    Commented Nov 21 at 16:23
  • \$\begingroup\$ TX, corrected, 189 bytes \$\endgroup\$
    – Pepik
    Commented Nov 21 at 17:37
1
\$\begingroup\$

Haskell + hgl, 36 bytes

ca"1"<<tx<m(gkY$rX">{1_<>|<><{1_|.")

Attempt This Online!

Explanation

  • rX">{1_<>|<><{1_|." regex to replace the tails with 1s.
  • tx transpose
  • ca"1" count the number of 1s in each row (previously columns).

No parser, 37 38 bytes

cn by3<<tx<m(zwp^.m Or~<cna" "<<pxx)

Attempt This Online!

Explanation

  • pxx get non-empty prefixes
  • cna" " count the number of non-spaces in each prefix.
  • m Or convert characters of the original list to code points.
  • zwp add pairwise to the original list of code points.
  • m map over each row
  • tx transpose
  • cn by3 count the number of entries in each row divisible by 3.

Parser no regex, 47 bytes

ca"1"<<tx<m(gkY$on(hh=#+)cx3"><><>< ""1000010")

Attempt This Online!

Reflection

This is ok. I'm a little disappointed by the fact the parser-no-regex version is so much longer than the no parser version when the shortest solution is the regex version. I want parsers to be more efficient than they are. It would even help regex parser answers. I don't really have a way to fix the thing I am frustrated with, but I have some thoughts in general:

  • There should be a combined version of gkY<rX. There's one for gky<rX already, I just got a bit unlucky. Turns out I already implemented this but forgot to export it. Very unlucky. Would have saved 3 bytes.
  • Map and then transpose seems to be a common operation for some reason. Should add a built-in for it.
  • pM and hd should be combined
  • Wr is great for making lists of strings, unless you want a space in one of the strings. Then the alternative is Ln, but newlines cost two bytes to write in strings. There should be an alternative that splits on some other character, and if I have any foresight I will make two of them.
  • There should be builtins for converting to and from binary Strings. It would be cheaper to make "1000010" if I had one.
  • There should be a combined version of m Or.
  • There should be a function for cumulatively counting the number of values.
\$\endgroup\$
0
\$\begingroup\$

APL+WIN, 70 bytes

Prompts for school as a 2 dimension array.

m←((i←(⍴i)÷3),3)⍴i←(n←v≠' ')/v←,j←⎕⋄+⌿(⍴j)⍴n\,(m[;1]='>'),0,m[;,3]='<'

Try it online! Thanks to Dyalog Classic

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.