Write a program to play the name game.


Your program should accept a single name as input from the user in some way (e.g read from standard input or as a command-line argument). You can assume that the name is a single word consisting of an upper case letter followed by one or more lower case letters.


Your program must print the rhyme for the given name, as explained in the song, by filling out the following template:

(X), (X), bo-b(Y)
Banana-fana fo-f(Y)

Here, (X) is the original name, and (Y) is the name in lower case with any initial consonants removed.

There is one exception, however. If the original name began with m, f or b, it should be written without this letter on the corresponding line. E.g. if the name was Bob, the "b" line should end with bo-ob. Note that in this case, any other consonants are kept, so for Fred it's fo-red, not fo-ed.



Shirley, Shirley, bo-birley
Banana-fana fo-firley


Arnold, Arnold, bo-barnold
Banana-fana fo-farnold


Bob, Bob, bo-ob
Banana-fana fo-fob


Fred, Fred, bo-bed
Banana-fana fo-red


Shortest code wins.

  • 1
    \$\begingroup\$ I guess Y is handled as a vocal, so Yves is like Ives or Arnold. \$\endgroup\$ Commented Nov 7, 2011 at 2:14
  • 1
    \$\begingroup\$ But what about Yates, Yestin, Yolanda, or Yulia? \$\endgroup\$
    – ephemient
    Commented Nov 7, 2011 at 6:01
  • \$\begingroup\$ @ephemient: I guess you could treat Y as a vowel only if it's followed by a consonant. That should cover those cases at least. \$\endgroup\$
    – hammar
    Commented Nov 7, 2011 at 12:44
  • 14
    \$\begingroup\$ I feel sorry for Tucker. \$\endgroup\$ Commented Dec 3, 2011 at 17:29

18 Answers 18


vi, 118 115

Y2PA,<ESC>Ypj~Y2PIbo-b<c-o>wBanana-fana fo-f<c-o>wFee-fi-mo-m<c-o>2$!<ESC>HJJ:%s/\vo-(([bfm])\2([^aeiou]*))?([bfm]?)[^aeiou]*/o-\3\4

The code includes 5 control characters, which I've put in brackets. Each only counts as a single character towards the character count.

EDIT: Moving the first join (J) to later and changing the paste-before (P) to a paste-after (p) saved me 1 character. Also, not capturing the o- in the regex saved me 2 more characters.


SNOBOL4, 437 430 bytes

 B = "b" D
 F = "f" D
 M = "m" D
 &ANCHOR = 1
 D SPAN('bcdfghjklmnpqrstvwxyz') . I REM . R :F(Y)
 B = "b" R
 F = "f" R
 M = "m" R
 I "b" :S(U)
 I "f" :S(V)
 I "m" :S(W) F(Y)
U D "b" REM . B :(Y)
V D "f" REM . F :(Y)
W D "m" REM . M
Y OUTPUT = N ", " N ", bo-" B
 OUTPUT = "Banana-fana fo-" F
 OUTPUT = "Fee-fi-mo-" M
 OUTPUT = N "!"

Ungolfed (plus I added a prompt; the one above just waits for a name to be typed):

      OUTPUT = "Please enter your name."
      Name = TRIM(INPUT)
      LC = 'abcdefghijklmnopqrstuvwxyz'
      Low = REPLACE(Name, UC, LC)
      BName = "b" Low
      FName = "f" Low
      MName = "m" Low
      Consonants = SPAN('bcdfghjklmnpqrstvwxyz')
      &ANCHOR = 1
      Low Consonants . First REM . Rest  :F(READY)
      BName = "b" Rest
      FName = "f" Rest
      MName = "m" Rest
      First "b"                   :S(BINIT)
      First "f"                   :S(FINIT)
      First "m"                   :S(MINIT) F(READY)
BINIT Low "b" REM . BName         :(READY)
FINIT Low "f" REM . FName         :(READY)
MINIT Low "m" REM . MName
READY OUTPUT = Name ", " Name ", bo-" BName
      OUTPUT = "Banana-fana fo-" FName
      OUTPUT = "Fee-fi-mo-" MName
      OUTPUT = Name "!"

This is the first SNOBOL program I've ever written.

SNOBOL is a line-oriented language, like FORTRAN, COBOL, or BASIC. Each line consists of an optional label starting in column 1, the code for the line which can involve assignments and pattern matching, and an optional branch. Yes, lines end with (optional) GOTOs. They come in two forms:


Branches to label TARGET, while


Branches to SUCCESS if the pattern match succeeded, or FAILURE otherwise. You can also just branch on success and fall through to the next line on failure, or vice versa.

Continuation lines begin with a + or .. Comments begin with a *.

How does it work?

Read in a name, convert it to lower case. Set up the B-, F-, and M-names assuming it begins with a vowel. Then check if it begins with a span of consonants. If not, we're ready to go! If so, strip the leading consonants and set up the B-, F-, and M-names assuming it doesn't begin with any of those letters. Finally, check if it starts with each of those letters in turn, fixing up the names as needed. Then we're ready to play the name game!

Sample run:

 # $RUN *SNOBOL4 5=GOLF.SNO+*SOURCE* 6=*DUMMY*(1,28)+*SINK*(1,4)+*DUMMY*
 # Execution begins   16:57:25
   Snowman, Snowman, bo-bowman
   Banana-fana fo-fowman
 # Execution terminated   16:57:30  T=0.013

I ran this on the Hercules S/370 mainframe emulator running the 6.0a release of the Michigan Terminal System using SNOBOL4 version 3.10 from April 1, 1973 built for MTS on May 1, 1975, but there are probably easier ways to run SNOBOL4 on a modern system. :)

Edit: Removed a redundant success branch that was equivalent to a fallthrough (I didn't realize I could put just a failure branch by itself) which eliminates an unneeded branch label, and turned an unconditional goto into a failure branch on the prior line, for a savings of 7 bytes.

Now that TIO has SNOBOL4 support you can Try it online! Note: It shows the size as 429 rather than 430 because when I pasted it there, the final linefeed got removed. I tried changing the continuation line (the one beginning with +) to a single line, which wasn't legal on the mainframe version because the line was too long, and it worked and brought it down to 427. Obviously CSNOBOL4 allows longer lines. I'm going to leave my score at 430, though, because that's how many bytes the script was on my machine, and besides, SNOBOL is pretty non-competitive.

  • \$\begingroup\$ You can now run SNOBOL4 on Try it online \$\endgroup\$
    – Giuseppe
    Commented Apr 26, 2018 at 16:59
  • \$\begingroup\$ @Giuseppe Wow, that's cool! Thanks for letting me know. \$\endgroup\$ Commented Apr 26, 2018 at 23:17
  • \$\begingroup\$ CSNOBOL4 has a couple other differences, as assignment only requires the leading space, so all your lines like N = TRIM(INPUT) can be N =TRIM(INPUT). \$\endgroup\$
    – Giuseppe
    Commented Apr 26, 2018 at 23:39

J, 149 characters

1!:2&2>|.(n,'!');('Fee-fi-mo-';'Banana-fana fo-';n,', ',n,', bo-'),&.>;/'mfb'(,`(}.@])`($:}.)@.((=+.2*5='aeiou'i.]){.)"0 _)a.{~32(23 b.)a.i.n=.1!:1]3

Javascript, 115 Bytes

r=x=>x.match(/[aeiou]\w*/i)[0];f=x=>`${x}, ${x}, bo-b${r(x)}\nBanana-fana fo-f${r(x)}\nFee-fi-mo-m${r(x)}\n${x}!`


this function returns the name without the initial consonants


Then the rest is a function returning the string complete string.

f=x=>`${x}, ${x}, bo-b${r(x)}\nBanana-fana fo-f${r(x)}\nFee-fi-mo-m${r(x)}\n${x}!`

Edit: from 119 to 115 bytes thanks to @Martin Ender

  • 1
    \$\begingroup\$ Welcome to PPCG! I believe you can shorten that regex to /[aeiou]\w*/i. \$\endgroup\$ Commented Apr 2, 2017 at 19:28
  • 1
    \$\begingroup\$ You can replace the original version with the shortened one. The old one will be preserved in the edit history of the post. If you want to you can include previous scores like JavaScript, <s>119</s> 115 bytes in the header to give people a hint that there was originally a longer version if they are interested. \$\endgroup\$ Commented Apr 2, 2017 at 20:24
  • \$\begingroup\$ Ah, cool. Editted again :) \$\endgroup\$
    – eniallator
    Commented Apr 2, 2017 at 20:30

Clojure, 292 characters after minimizing

Here's a first attempt, almost positive I can widdle it down further:

(defn name-game
  (let [[b f m] (map (fn [x] (if (some #(= % (first n) (last x)) "bfm")
                              (str (apply str (butlast x)) (apply str (rest n)))
                              (str x (apply str (drop-while (fn [x] (not (some #(= % x) "aeiou"))) n))))) [", bo-b" "\nBanana-fana-fo-f" "\nFee-fi-mo-m"])]
    (str n ", " n b f m "\n" n "!")))

I'm just learning clojure and thought it'd be fun to give this a shot. Here's my reasoning:

- To strip off consonants from beginning of string: (drop-while (fn [x] (not (some #(= % x) "aeiou"))) name)

- To handle the extra rules for "b", "f" and "m", I broke text into list of phrases: [", bo-b" "\nBanana-fana-fo-f" "\nFee-fi-mo-m"]

- Then, I applied a function that asks whether the phrase ends with the same letter that the name starts with, and used that to transform those 3 phrases based on the rules of the puzzle

- Final step is to build a string with results


Scala 281

I replaced (X) and (Y) in the pattern with # and 012. S is just a new name for String and a(b,c,d) is shorthand definition for b.replaceAll(c,d)

val v="""#, #, bo-b0
Banana-fana fo-f1
type S=String
def a(b:S,c:S,d:S)=b.replaceAll(c,d)
def r(t:S,n:S,i:Int)=if(n(0)=="bfm"(i).toUpper)a(t,"."+i,n.tail)else
def x(n:S)=a(r(r(r(v,n,0),n,1),n,2),"#",n)

Test invocation:

val l = List ("Shirley", "Arnold", "Bob", "Fred") 
for (n <- l) println (x (n) + "\n")

And ungolfed:

val templ = """#, #, bo-b0
Banana-fana fo-f1

val names = List ("Shirley", "Arnold", "Bob", "Fred") 
val keys = "bfm"

def recode (template: String, n: String, i: Int) = 
 if (n(0) == keys(i).toUpper)
   template.replaceFirst ("." + i, n.tail) else 
 template.replaceAll ("" + i, (n.replaceAll ("^[^AEIOUYaeiouy]*([aeiou].*)", "$1").toLowerCase))

for (name <- names)
  println (recode (recode (recode (templ, name, 0), name, 1), name, 2).replaceAll ("#", name) + "\n")

Python 3, 148 145 142 bytes

Yes I know it's a little late but...

while n[i]not in'aeiouAEIOU':i+=1;r=n[i:]
print(f'{n}, {n}, bo-b{r}\nBanana-fana fo-f{r}\nFee-fi-mo-m{r}\n{n}!')

It uses the new f-strings to format the resulting string.

I don't think TIO supports f-strings yet, unfortunately.

Old Version

def f(n,i=0):
 while n[i].lower()not in'aeiou':i+=1;r=n[i:]
 return f'{n}, {n}, bo-b{r}\nBanana-fana fo-f{r}\nFee-fi-mo-m{r}\n{n}!'

Saved 3 bytes thanks to @officialaimm

  • \$\begingroup\$ save three bytes by using 'AEIOUaeiou' literal and avoiding lower() in 3rd line \$\endgroup\$
    – 0xffcourse
    Commented Apr 2, 2017 at 16:09

Sed, 162 bytes

sed 'h;G;G;G' |sed '1s/.*/&, &, bo-b\L&/i;2s/^.*/Banana-fana fo-f\L&/;3s/^.*/Fee-fi-mo-m\L&/;4s/$/!/;tx;:x;s/o-\([bfm]\)\1/o-/i;tz;s/\(o-[bfm]\)[^aeiou]\+/\1/;:z'

I did not know sed very well before I did this. I, uh, know it a lot better, now. The first sed in the pipeline duplicates the name three times so it becomes "Bob\nBob\nBob\nBob" instead of just "Bob". The next sed does the heavy lifting.

Expects input on stdin like echo Name |sed ...


sed 'h                           ;# copy to hold buffer
G                                ;# append newline + hold buffer to pattern
G                                ;# ditto for next two G's
G' |sed '1s/.*/&, &, bo-b\L&/i   ;# 1st line -> X, X bo-bx (lowercase)
2s/^.*/Banana-fana fo-f\L&/      ;# 2nd line -> Banana-fana fo-fx
3s/^.*/Fee-fi-mo-m\L&/           ;# 3rd line -> Fee-fi-mo-mx
4s/$/!/                          ;# bang the 4th line!
tx                               ;# jump to :x if any s/// has matched
:x                               ;# spoiler alert: it has! reset t-flag
s/o-\([bfm]\)\1/o-/i             ;# match some o-cc where c = [bfm]
tz                               ;# if that matched, branch to :z
s/\(o-[bfm]\)[^aeiou]\+/\1/      ;# replace o-[bfm] plus consonants with o-[bfm]
:z                               ;# target of tz, skips over previous s///'

A couple of notes. The first four matches, 1s, 2s, 3s, 4s, transform the output into something not quite correct. Bob has become bo-bbob, Fred has become fo-ffred, and Mike has become mo-mmike. Kay would become mo-mkay, mkay?

Then, we need to replace either bo-bbob with bo-ob, or bo-bkay with bo-bay. To do that, we can use a feature where we do one s/// substitution, and then branch if it succeeded, jumping over the second one that we now want to skip. But if it missed, we want to fall through the branch and do the next substitution.

The t[label] command does that, branching only if a previous s/// matched. But at the beginning of the script I already did one s/// for each line (the leading numbers in 1s, 2s, etc. are addresses; they mean the command is only performed if the address matches). So no matter what line we're on, 1, 2, 3, or 4, at least one s/// has matched. (I tried doing it the other way around, massaging the names and then adding the "Banana-etc." after, but got stuck that way, and trying to do it all at once would cause some repetition.) Fortunately, the flag can be cleared by taking a branch, so we do that with "tx ; :x". tx branches to the x label, and :x is the x label.

Whew! That clears the palate for the final two substitutions. We try one, and if it succeeds we branch over the other, otherwise we do the second one. Either way we end up at the :z label and the pattern buffer contains one line of lyrics which is printed to stdout.

Thanks for tricking me into spending enough time with the sed man page and Texinfo manual to finally understand how do to more than sed s/foo/bar/

  • \$\begingroup\$ Oh, I'm counting the whole length of the script with the sed command and the | and all. Maybe I should call this bash / sed, or count it differently? I don't know. I'm happy with it as-is but let me know if it should be different by the standards of PPCG. \$\endgroup\$ Commented Apr 3, 2017 at 1:13

Go, 193 186 173 bytes

func f(s string){y:=MustCompile(`^[^AEIOUaeiou]+`).ReplaceAllString(s,"")
Printf(`%s, %s, bo-b%s
Banana-fana fo-f%s

Attempt This Online!

Prints to STDOUT.

  • -7 bytes by not using explicit indexes
  • -13 bytes by @The Thonnu by not using the strings package
  • \$\begingroup\$ Your current version doesn't actually work (mismatched parentheses), but here's a fix for 173 bytes \$\endgroup\$
    – The Thonnu
    Commented May 17, 2023 at 15:00

Python, 161

if b[0] in c:b=b[1:]
d="X, X, bo-@\nBanana-fana fo-@\nFee-fi-mo-@\nX!".replace('X',a)
for y in c:d=d.replace('@',y+b,1)
print d

Just realised my code fails...
- Doesn't remove initial constonants.
- Doesn't manage the 'bo-ob' business.

It's the furthest I got, maybe somebody can finish it.


Groovy, 146

r={n->m={n[0]==it[0]?n[1..-1]:it[1]+(n.toLowerCase()=~'[aeiou].*')[0]};"$n, $n, bo-${m 'Bb'}\nBanana-fana fo-${m 'Ff'}\nFee-fi-mo-${m 'Mm'}\n$n!"}

assert ['Shirley', 'Arnold', 'Bob', 'Fred'].collect { r(it) } == [
'''Shirley, Shirley, bo-birley
Banana-fana fo-firley
'''Arnold, Arnold, bo-barnold
Banana-fana fo-farnold
'''Bob, Bob, bo-ob
Banana-fana fo-fob
'''Fred, Fred, bo-bed
Banana-fana fo-red

R, 189 chars

x=scan(,'');f=function(b)if(grepl(b,x))sub('.','',x)else tolower(sub('^[^aoueiy]*',b,x,i=T));cat(sprintf('%s, %1$s, bo-%s\nBanana-fana fo-%s\nFee-fi-mo-%s\n%1$s!\n',x,f('B'),f('F'),f('M')))

But with just one more character, you can entry many names in one go:

x=scan(,'');f=function(b)ifelse(grepl(b,x),sub('.','',x),tolower(sub('^[^aoueiy]*',b,x,i=T)));cat(sprintf('%s, %1$s, bo-%s\nBanana-fana fo-%s\nFee-fi-mo-%s\n%1$s!\n',x,f('B'),f('F'),f('M')))

Pyth, 111 bytesSBCS

V"BFM"IqhzN=aYtrz0.?=aY+rN0:rz0"^[^aeiou]+"k))+%." s­ÞXY:lÍ"*]z2@Y0+." o1}1ÃЬÛJî½"@Y1+."-o Âkq°ë¹è"@Y2+z\!

Test suite

Code uses unprintable characters, and as such does not display properly on Stack Exchange. The link provided contains these characters and is the correct version of the program.


Perl 5 -na, 93 bytes

s/^[FMB]//||s/^[^aeiou]*//i;$,=$_.$/;say"@F, @F, bo-b","Banana-fana fo-f","Fee-fi-mo-m","@F!"

Try it online!


JavaScript, 100 bytes

s=>s+`, ${s}, bo-b${t=/[aeiou].+/.exec(s.toLowerCase())}
Banana-fana fo-f${t}

Try it online!


Japt, 55 54 bytes

Ba-fa fo-f
{U}!`rRRiUv e"^%c

Try it

+Si,)²+`¾...{U}!`rRRiUv e"^%c     :Implicit input of string U
+                                 :Append
 S                                :  Space
  i,                              :  Prepend ","
    )                             :End append
     ²                            :Duplicate
      +                           :Append
       `¾...   !`                 :Compressed string "bo-b\nBanana-fana fo-f\nFee-fi-mo-m\n...!"
            {U}                   :Interpolate U
                 r                :Replace
                  R               :  Newline with
                   Ri             :    Prepend newline with
                     Uv           :      U lowercased
                        e"^%c     :      Recursively replace RegEx /^[^aeiou]/i

Haskell, 151 bytes

z x|let y=last$(snd$break(`elem`"AEIOUaeiou")x):[tail x|elem(x!!0)"BFM"]=unlines[x++", "++x++", bo-b"++y,"Banana-fana fo-f"++y,"Fee-fi-mo-m"++y,x++"!"]

Try it online!

Haskell, 152 bytes

z x|let y=last$dropWhile(`notElem`"AEIOUaeiou")x:[tail x|elem(x!!0)"BFM"]=unlines[x++", "++x++", bo-b"++y,"Banana-fana fo-f"++y,"Fee-fi-mo-m"++y,x++"!"]

Try it online!



if n[0].lower() in ("m", "f", "b"): r=n[1:]
    i = iter(n.lower())
    for a in range(len(n)):
        if i.next() in ("a","e","i","o","u"):
            r = n[a:]
print "%s, %s, bo-b%s\nBanana-fana fo-f%s\nFee-fi-mo-m%s\n%s!" %(name,name,rhyme,rhyme,rhyme,name)

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.