9
\$\begingroup\$

Introduction

cowsay is a Unix command made by Tony Monroe, written in Perl. It outputs a picture of a cow saying given text. For example:

 ________________________
< Typical cowsay output! >
 ------------------------
       \   ^__^
        \  (oo)\_______
           (__)\       )\/\
               ||----w |
               ||     ||

Challenge

Given a multiline string as input, output a cow saying the inputted string.

The output is made like so:

  • Start with one space
  • Append the length of the longest line of the input plus \$2\$ underscores
  • Append a newline
  • Append a < if the input is a single line, else append a /
  • Append a space
  • Append the first line of input
  • Append the length of the longest line, minus the length of the current line, plus \$1\$, spaces
  • Append a > if the input is a single line, else append a \
  • Append a newline
  • While there are still lines left in the input:
    • Append a \ if this is the last line, else append a |
    • Append a space
    • Append the current line of input
    • Append the length of the longest line, minus the length of the current line, plus \$1\$, spaces
    • Append a / if this is the last line, else append a |
    • Append a newline
  • Append a space
  • Append the length of the longest line of the input plus \$2\$ hyphens
  • Append a newline
  • Append the cow and speech bubble line:
       \   ^__^
        \  (oo)\_______
           (__)\       )\/\
               ||----w |
               ||     ||

Test cases

Input

...becomes...

Output
====================
Typical cowsay output!

...becomes...

 ________________________
< Typical cowsay output! >
 ------------------------
       \   ^__^
        \  (oo)\_______
           (__)\       )\/\
               ||----w |
               ||     ||
====================
Two
Lines

...becomes...

 _______
/ Two   \
\ Lines /
 -------
       \   ^__^
        \  (oo)\_______
           (__)\       )\/\
               ||----w |
               ||     ||
====================
This is the first line of input.
This is the second line. It is the longest, so the bubble should stretch to fit it.
This is the third line.
Hello world!
(;;"''{<<['"



Empty lines shouldn't cause any problems.

...becomes...

 _____________________________________________________________________________________
/ This is the first line of input.                                                    \
| This is the second line. It is the longest, so the bubble should stretch to fit it. |
| This is the third line.                                                             |
| Hello world!                                                                        |
| (;;"''{<<['"                                                                        |
|                                                                                     |
|                                                                                     |
|                                                                                     |
\ Empty lines shouldn't cause any problems.                                           /
 -------------------------------------------------------------------------------------
       \   ^__^
        \  (oo)\_______
           (__)\       )\/\
               ||----w |
               ||     ||

Rules

  • Standard loopholes are forbidden.
  • Any amount of trailing whitespace is allowed on any line. Trailing newlines are also allowed.
  • If possible, please link to an online interpreter (e.g. TIO) to run your program on.
  • Please explain your answer. This is not necessary, but it makes it easier for others to understand.
  • Languages newer than the question are allowed. This means you could create your own language where it would be trivial to do this, but don't expect any upvotes.
  • You may assume only printable ASCII is given as input.
  • Please, don't submit cowsay in Bash. It's boring and you'll lose rep quickly.
  • This is , so shortest code in bytes wins!

Reference implementation

Note

The cow is one space too close to the left, but I've kept it this way so people don't need to update their answers. But I will accept the correct version.

\$\endgroup\$
10
  • 3
    \$\begingroup\$ Identical but closed due to not being specified well enough \$\endgroup\$ – hyper-neutrino May 9 at 21:26
  • 1
    \$\begingroup\$ Some of those links are broken, recommend not using links at all but putting the actual test cases into the post. Makes it much clearer. \$\endgroup\$ – Noodle9 May 9 at 21:29
  • 2
    \$\begingroup\$ This seems like a combination of Print 'em goats and Surround a string with hashes tbh, and doesn't seem like it adds anything new \$\endgroup\$ – caird coinheringaahing May 9 at 21:37
  • 1
    \$\begingroup\$ @cairdcoinheringaahing Not really. A big difference is that here it's harder to generate the speech bubble, as the sides vary depending on the lines of input, and this actually needs to support multiline strings and pad the bubble to the right length \$\endgroup\$ – Makonede May 9 at 21:39
  • 5
    \$\begingroup\$ It's unfortunate that for golfing, this splits into two pretty different tasks of wrapping the text in speech bubble, and drawing the cow. And I don't think the cow has enough ASCII art structure to allow interesting techniques in most languages. Maybe you could omit the cow drawing from the task? \$\endgroup\$ – xnor May 9 at 21:49

12 Answers 12

4
\$\begingroup\$

Canvas, 92 87 86 83 81 80 bytes

l╷?/ω┤|*\∔∔]<} +↔ω┘+×L┤_×α-×└∔∔r 7ד;yB¾‾≡*hsNMnJS=f≤)g<◂AX↓ZxLh?╷r┌╴\⁵n╪.cE^‟+∔

Try it here!

Takes input surrounded by ```.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ you can take multiline strings with triple backticks \$\endgroup\$ – Razetime May 11 at 5:33
  • \$\begingroup\$ @Razetime great trick, thanks! \$\endgroup\$ – hakr14 May 11 at 16:51
10
\$\begingroup\$

Charcoal, 95 bytes

≔⌈E⪪θ¶⁺²LιηP×_ηM↙EI⁺²Xχ№θ¶§|/\<Iι η‖BOηM⁷¦¹↘²M²↗”{‴JCεχ(J7WS⊙u#←L⌈η‽=Ykc8¿№➙Q⎚⊗\`ê:VI✂e≡8s”J¹¦¹θ

Try it online! Link is to verbose version of code. Note that multiline input with empty lines is an awkward input format for Charcoal, so you'll have to use Pythonic format. Explanation:

≔⌈E⪪θ¶⁺²Lιη

Find out the length of the longest line in the text.

P×_ηM↙

Print the top row of underscores, then move down and left so that the next output appears to the left of the underscores.

EI⁺²Xχ№θ¶§|/\<Iι

Print the left-hand side of the speech bubble. Each row is coded according to whether it is the first (+1) or last (+2) row, with a row that is both first and last therefore having a value of 3, which is then indexed to find the appropriate character.

 η

Print the row of dashes aligned with the underscores.

‖BOη

Reflect the left side to complete the speech bubble.

M⁷¦¹↘²M²↗

Print the diagonal line joining the speech bubble to the cow.

”{‴JCεχ(J7WS⊙u#←L⌈η‽=Ykc8¿№➙Q⎚⊗\`ê:VI✂e≡8s”

Print the cow as a compressed string.

J¹¦¹θ

Jump back to the bubble to print the input text.

\$\endgroup\$
9
\$\begingroup\$

Python 2, 249 bytes

-1 byte thanks to @ovs
-3 bytes thanks to @Jonathan Allan

A relatively straight-forward implementation. One thing to note is that the cow contains a lot of spaces, which allows us to save a couple bytes by using replace.

s=input().split('\n')
m=max(map(len,s))+2
l=len(s)-2
z=-l*'<>'or'/\\'+'||'*l+'\/'
print'','_'*m
for c in s+['-'*m,'''? \   ^__^
?   \  (oo)\_______
?? (__)\?  )\/\\
???||----w |
???||?||'''.replace('?',' '*5)]:print z[:1],c.ljust(m-2),z[1:2];z=z[2:]

Try it online!

\$\endgroup\$
2
  • 1
    \$\begingroup\$ You can save one of the three prints (saving three bytes) like this \$\endgroup\$ – Jonathan Allan May 10 at 18:33
  • \$\begingroup\$ ...(was too long for 1 comment) - note that trailing spaces on any lines are allowed :) \$\endgroup\$ – Jonathan Allan May 10 at 18:35
6
\$\begingroup\$

05AB1E, 116 112 105 bytes

-7 bytes thanks to Kevin Cruijssen!

.BDgi„><€S`ëDgÍ'|и'\š'/ªÂ}Š)ø¬Jg'_×ð욬¤'-:ª•˜©ã€õĆWõ½*¸cÀΓý¼oº₁%<Yë;Gá“•" \^_
(o)/|-w"ÅвJ¶¡•–ãö•28вjÅ\«»

Try it online!

Speech bubble:

                           # add sides:
.B                         # Squarify: split input by newlines and pad each line to the same length with spaces
  Dg                       # duplicate the list and get the number of lines
    i                      # if this equals 1:
     „><€S`                #   push [">"] and ["<"]
                           #   stack layout: ["Single line"], [">"], ["<"]
    ë                      # else (more than one line of input):
     DgÍ                   #   number of lines - 2
        '|и                #   repeat "|" this many times in a list
           '\š'/ª          #   prepend "\" and append "/"
                           #   the list is now ["\", "|", ..., "|", "/"]
                 Â         #   keep this list and push the reverse
                           #   stack layout: ["multi-line", ..., "input"], ["\", "|", ..., "|", "/"], ["/", "|", ..., "|", "\"]

                           # add top and bottom:
Š)ø                        # triple-swap, wrap stack into a list and transpose to assemble the lines
   ¬Jg                     # get the length of the first complete line (without the spaces)
      '_×                  # create a string of this many "_"
         ðì                # prepend a space
           š¬              # prepend this string to the list of lines
             ¤'-:          # push another copy of the "_" string and replace "_" with "-"
                 ª         # append to the list of lines

Cow:

•˜...á“•" \^_\n(o)/|-w"ÅвJ # base-compressed cow without leading spaces
•–ãö•28в                   # compressed list of line lengths: [15, 23, 27, 24, 24]
        jÅ\                # pad each line to desired length with spaces in the front
           «»              # concatenate the cow lines to the other lines and join the lines by newlines
\$\endgroup\$
3
  • 1
    \$\begingroup\$ -7 bytes by changing ¶¡ to .B and removing íD€gàjí \$\endgroup\$ – Kevin Cruijssen May 11 at 13:17
  • 1
    \$\begingroup\$ @KevinCruijssen thanks a lot, I guess I still don't know all builtins ;). \$\endgroup\$ – ovs May 11 at 14:11
  • \$\begingroup\$ Some builtins are barely used, so it's not too surprising. :) There are still some builtins that even I haven't used even once yet, like .o (overlap) or (cycle last element infinitely) or (0-based index of the greatest prime less than or equal to a). 🤷 \$\endgroup\$ – Kevin Cruijssen May 11 at 17:25
5
\$\begingroup\$

Jelly, 117 bytes

L⁾||ẋṫ5“/\“\/”jµ⁾<>Ḋ?⁶ẋ4¤s2¤js1s2
j@€⁾  z⁶⁾_-jⱮZj@"Ç;“¡{+Ṁ“#ßɗ⁾ŀ`m“¬ṖȥƇʂẆEẒẓ“¡ɲÇ⁹E⁸ċ⁷“¡ɲÇ⁹D⁹{Ṅ’ṃ“\ ^_(o)/|-w”¤⁶;$8¡€¤

Try it online!

-26 bytes using base + integer compression (thanks to Bubbler for the idea)
-2 bytes thanks to caird coinheringaahing
-1 byte thanks to Nick Kennedy
thanks to Jonathan Allan for finding a bug; also -4 bytes from fixing it
-1 byte thanks to Jonathan Allan

Explanation

L⁾||ẋṫ5“/\“\/”jµ⁾<>Ḋ?⁶ẋ4¤s2¤js1s2  Helper Link - generates the side borders
                    ?              If
                   Ḋ               list[1:] (basically, if the length is not 1)
L⁾||ẋṫ5“/\“\/”jµ                   Sublink; generate the side borders for the non-edge case
L                                  Take the length
 ⁾||ẋ                              Repeat "||" that many times
     ṫ5                            Tail; remove the first 4 elements
       “/\“\/”j                    Use that result to join ["/\", "\/"]
                ⁾<>                Otherwise, return "<>"
                     ⁶ẋ4¤          " " * 4
                         s2¤       split into slices of size 2
                            j      Prepend and append (Extra spaces are needed because the top and bottom borders don't have side borders)
                             s2    Slices of length 2 (separate the left and right border components)
                               s2  Slices of length 2 (group the left and right border components into pairs)

j@€⁾  z⁶⁾_-jⱮZj@"Ç;“...’ṃ“ \_(o)/|-w^”¤⁶;$8¡€¤     Main Link
j@€⁾                                               join "  " with each row (surround in spaces)
      z⁶                                           Zip, using space as filler
            Ɱ                                      For each column
        ⁾_-j                                       Join ["_", "-"] using it (prepend _ and append -)
             Z                                     Zip
              j@"Ç                                 Vectorized swapped join with the helper link (basically, for each row, prepend and append the borders)
                  ;                                Append: {{
                   “...’                           [23386201, 8811166311836860, 134563374682941922730, 101089407352370886, 101089407336781202]
                        ṃ“ \_(o)/|-w^”             Base decompressed with " \_(o)/|-w^" - this is the cow without leading spaces
                                            €      For each row
                                       ⁶;$         Prepend " "
                                          8¡       8 times
                                             ¤     }}
\$\endgroup\$
7
  • \$\begingroup\$ @NickKennedy Sorry, not sure what you mean - I managed to get it work by structuring it like “| |”ẋṫ9“/ \“\ /”j and L“< >”ÇỊ?⁶ẋ8¤s4¤js2s2 but that's -1 so I don't suppose that's what you meant \$\endgroup\$ – hyper-neutrino May 10 at 14:06
  • \$\begingroup\$ Instead of @NickKennedy's suggestion, you can use instead to make it µ“< >”Ḋ? \$\endgroup\$ – caird coinheringaahing May 10 at 14:52
  • \$\begingroup\$ @cairdcoinheringaahing ah yes good point, thanks \$\endgroup\$ – hyper-neutrino May 10 at 14:53
  • \$\begingroup\$ @NickKennedy Oh right, I was doing something else before, lol. Thanks \$\endgroup\$ – hyper-neutrino May 10 at 19:00
  • 1
    \$\begingroup\$ @JonathanAllan Oops! Thanks for noticing that! -4 bytes to fix it, lol \$\endgroup\$ – hyper-neutrino May 11 at 12:51
4
\$\begingroup\$

JavaScript, 342 299 307 bytes

n=>[' '+'_'.repeat(e=Math.max(...(s=n.split`
`).map(l=>l.length))+2),...s.map((i,l,_,x=i.padEnd(e-1))=>1 in s?l?l>s.length-2?`\\ ${x}/`:`| ${x}|`:`/ ${x}\\`:`< ${x}>`),' '+'-'.repeat(e),''].join`
`+[(n='       ')+"\\   ^__^"," \\  (oo)_______",`    (__)${n})\\/\\`,n+` ||----w |`,n+" ||     ||"].join(`
`+n)

Try it online.

Explanation

This builds an array of strings which are joined with a newline.

What each element does

  • element 1: underscore string
  • next few elements: each line of the inputted string with the added limiters
  • next element: hyphen string
  • then, add the cow string.

-43 bytes thanks to A username

+8 bytes due to empty line bug

\$\endgroup\$
4
  • \$\begingroup\$ Nice! Your approach of using a large ternary works quite well, and when combined with Redwolf's tricks of padEnd and auto-indenting the cow, gets 301 bytes \$\endgroup\$ – A username May 10 at 9:04
  • \$\begingroup\$ Make that 299 \$\endgroup\$ – A username May 10 at 9:14
  • \$\begingroup\$ @Ausername thanks for the suggestions! I wonder if the line logic can be golfed a little furhter. \$\endgroup\$ – Recursive Co. May 10 at 9:41
  • 1
    \$\begingroup\$ @Arnauld thanks for pointing that out. I will probably rewrite my ternary statement, in a few hours, since I don't have access to a computer at the moment. \$\endgroup\$ – Recursive Co. May 11 at 5:46
3
\$\begingroup\$

Jelly, 106 bytes

“¢Ʋėỵ“Œ\ė#ݲ¿“¤e+ṂTƒƑ¶d“Ṫ©Ḋ9çṀṠ“Ṫ©Ḋ87⁹Ġ’ṃ“ \_)o|w-^/(”⁶;$8¡€
Ø^⁾><Ṗ?WẋLU0¦”|JṖḊƊ¦⁾  ©jṁ€2ɓ®jⱮz⁶⁾_-jⱮZj@";¢

A monadic Link that accepts a list of the lines and yields a list of lines. (Gives eight spaces before the cow, for seven change 8¡€ to 7¡€)

Try it online!

How?

Ø^⁾><Ṗ?WẋLU0¦”|JṖḊƊ¦⁾  ©jṁ€2ɓ®jⱮz⁶⁾_-jⱮZj@";¢ - Main Link: list, Lines
     Ṗ?                                       - if popped (Lines)? (i.e. > 1 line?):
Ø^                                            -    then: "/\"
  ⁾><                                         -    else: "><"
       W                                      - wrap (that) in a list, say "Corners"
         L                                    - length (of Lines)
        ẋ                                     - repeat (Corners that many times) 
           0¦                                 - apply to rightmost:
          U                                   -   upend (reverse to "<>" or "\/")
                  Ɗ¦                          - apply to indices...
               J                              -   range of length (of Lines)
                Ṗ                             -   pop
                 Ḋ                            -   dequeue
             ”|                               - ... '|' (replaces all but first and last)
                    ⁾..                       - "  "
                       ©                      - (copy this to the register)
                        j                     - join
                         ṁ€2                  - mould each like 2 ("||" and "  " as needed)
                            ɓ                 - new dyadic chain - f(Lines,Edges=that)
                             ®                - recall from register -> "  "
                              jⱮ              - map (across Lines) with join
                                z⁶            - transpose with space as filler
                                  ⁾_-         - "_-"
                                     jⱮ       - map (across these columns) with join
                                       Z      - transpose (back to rows)
                                         @"   - zip with, with swapped arguments:
                                        j     -   (Edge-pair) join (Row)
                                            ¢ - call last Link as a nilad -> cow lines
                                           ;  - concatenate

“...’ṃ“ \_)o|w-^/(”⁶;$8¡€ - Link 1, Get cow lines: no arguments
“...’                     - list of integers (one per line of the cow)
      “ \_)o|w-^/(”       - " \_)o|w-^/(" (the cow's constituents)
     ṃ                    - base decompress
                        € - for each:
                      8¡  -   repeat this eight times:
                     $    -     last two links as a monad:
                   ⁶      -       ' '
                    ;     -       concatenate
\$\endgroup\$
4
  • \$\begingroup\$ Invalid, this prints five udders for the cow. Also, if you want you can stay with the correctly spaced version, but it's shorter if you use the fact that I made a slight mistake when making this challenge to get rid of one of the spaces on each line. \$\endgroup\$ – Makonede May 11 at 19:40
  • 1
    \$\begingroup\$ @Makonede fixed the udders, thanks. I think using eight or seven spaces makes no difference. \$\endgroup\$ – Jonathan Allan May 11 at 20:21
  • \$\begingroup\$ You forgot to change -w to w-, and to update your TIO link. \$\endgroup\$ – Makonede May 11 at 20:32
  • 1
    \$\begingroup\$ @Makonede Uh, fail ...I was just doing the same! All sorted now. \$\endgroup\$ – Jonathan Allan May 11 at 20:36
2
\$\begingroup\$

Retina, 156 144 bytes

mP`^.*
¶
 \¶\ 
1,-2%`^.|.$
|
^
 $.%'*_¶/ 
$
 /¶ $.%`*-
¶/(.+)/¶
__¶<$1>¶
$
¶7* \   ^__^¶8* \  (oo)\7*_¶11* (__)\7* )\/\¶15* ||4*-w |¶15* ||5* ||

Try it online! Explanation:

mP`^.*

Pad all lines to the same length.

¶
 \¶\ 

Add \s on all the intermediate lines...

1,-2%`^.|.$
|

... but fix interior lines to use | instead.

^
 $.%'*_¶/ 
$
 /¶ $.%`*-

Insert the leading row of _s and trailing row of -s, plus the /s at the start and end of the bubble (still assuming a multiline bubble).

¶/(.+)/¶
__¶<$1>¶

Fix up a single-line bubble, not only to change the /s to <>s, but also because not enough _s are calculated for the initial row.

$
¶7* \   ^__^¶8* \  (oo)\7*_¶11* (__)\7* )\/\¶15* ||4*-w |¶15* ||5* ||

Append the cow using run length encoding.

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

JavaScript (V8), 380 354 329 bytes

-25 thanks to @A username

t=>` ${"_".repeat(m=Math.max(...(l=t.split`
`).map(d=>d.length)))}__
${"/<"[j=+!l[1]]} ${l.shift().padEnd(m)} ${"\\>"[j]}
${j?'':l.map(d=>"|\\"[w=+!l[++j]]+' '+d.padEnd(m)+' '+"|/"[w]).join`
`+`
`} `+"-".repeat(m+2)+`
\\   ^__^
 \\  (oo)\\_______
    (__)\\${n='       '})\\/\\
 ${n}||----w |
 ${n}||     ||`.replace(/\n/g,`
`+n)

Try it online!

\$\endgroup\$
9
  • \$\begingroup\$ Invalid, as the separator for intermediate lines is not | and it outputs a trailing }. \$\endgroup\$ – Makonede May 9 at 22:06
  • \$\begingroup\$ @Makonede That's a bug. Fixed. \$\endgroup\$ – Redwolf Programs May 9 at 22:07
  • \$\begingroup\$ 329, sorry for the spam, I think this is the best \$\endgroup\$ – A username May 10 at 7:27
  • \$\begingroup\$ Can u use another char than \n in RegEx at the end? \$\endgroup\$ – l4m2 May 10 at 22:46
  • \$\begingroup\$ @l4m2 Oh, good idea. \$\endgroup\$ – Redwolf Programs May 10 at 22:50
2
\$\begingroup\$

JavaScript (ES8),  266  252 bytes

s=>` _2
 3
 -2
 5\\ 1^__^
 10\\  (oo)\\_5
 9(__)\\ 5)\\/\\
 13||-6w |
 13|| 7||`.replace(/.(\d+)/g,(s,n)=>n^3?s[i=0].repeat(n^2||w):a.map(s=>'|\\/<'[j=!i++*2|!a[i]]+s.padEnd(w)+'|/\\>'[j]).join`
`,a=s.split`
`.map(w=s=>` ${w>(v=s.length+2)?0:w=v,s} `))

Try it online!

How?

We first split the input string into lines surrounded with 2 spaces and compute the width w of the biggest line (including said spaces):

a = s.split`\n`.map(w = s => ` ${w > (v = s.length + 2) ? 0 : w = v, s} `)

Each number n in the template string is interpreted as follows:

  • 2: repeat the preceding character w times (for the top and bottom borders of the text bubble)

  • 3: insert the text bubble:

    a.map(s =>
      '|\\/<'[j = !i++ * 2 | !a[i]] +
      s.padEnd(w) +
      '|/\\>'[j]
    )
    .join`\n`
    
  • anything else: repeat the preceding character n XOR 2 times

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

Vyxal, a 194-byte mess

□⁋↵:ƛL;G:£2+\_*\ +\ $+,L1=[‛< □‛ >++,|□L(n[n›□L=[`\\ `` /`|`| `` |`]|`/ `` \\`]□ni:L¥ε\ *+$++,)]\ ¥2+\-*\ ++,`  ?\\   ^__^
   ?\\  (oo)\_______
?? (__)\\  ?)\\/\\
???||----w |
???||?||
`\?\ 5*V,

Try it Online!

Explanation tomorrow.

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

Julia 1.0, 243 240 bytes

r=readlines()[(R=end;:)]
a=' '
l=maximum(length,r)+2
println.([a*'_'^l
["|/\\<"[(j=1+(i<2)+2(i==R);)]rpad(a*r[i],l)*"|\\/>"[j] for i=1:R]
a*'-'^l
replace(raw"?  \   ^__^
?   \  (oo)\_______
?? (__)\?  )\/\
???||----w |
???||?||",'?'=>a^5)])

Try it online!

I was trying to compress the cow but dingledooper's trick was more efficient

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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