jblog
toc

99 Bottles of Beer

2016-10-22, post № 144

art, codegolf, poetry, programming, Pygame, #character-sparse code, #characters, #space-efficient, #StackExchange

I recently [1] found a five year old StackExchange thread talking about a programming challenge to recreate the marvelously imaginative lyrics of ‘99 Bottles of Beer’. The song goes as follows (whole lyrics can be viewed below).

99 bottles of beer on the wall, 99 bottles of beer.
Take one down and pass it around, 98 bottles of beer on the wall.
98 bottles of beer on the wall, 98 bottles of beer.
Take one down and pass it around, 97 bottles of beer on the wall.
97 bottles of beer on the wall, 97 bottles of beer.
Take one down and pass it around, 96 bottles of beer on the wall.
[…]
3 bottles of beer on the wall, 3 bottles of beer.
Take one down and pass it around, 2 bottles of beer on the wall.
2 bottles of beer on the wall, 2 bottles of beer.
Take one down and pass it around, 1 bottle of beer on the wall.
1 bottle of beer on the wall, 1 bottle of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.

Of course, the real challenge is not to build a program that generates the lyrics but rather to do it with the least amount of characters [2]. To achieve this goal, I tried out various strategies, all written in Python.

The first strategy is to join a string generated by a for loop (𝟤𝟨𝟥 characters).

99-bottles-of-beer_d...-function.py; Python2, 264 bytes, 3 lines
a,b=" bottles of beer"," on the wall"
c=a.replace("s","")
print"\n".join([str(n)+a+b+", "+str(n)+a+"."+"\nTake one down and pass it around, "+str(n-1)+[a,c][n<3]+b+".\n"for n in range(99,1,-1)])+"\n1"+c+b+", 1"+c+".\nGo to the store and buy some more, 99"+a+b+"."

The second, less efficient strategy is to use a function and recursion (𝟤𝟩𝟦 characters).

99-bottles-of-beer_d...-function.py; Python2, 275 bytes, 8 lines
a,b=" bottles of beer"," on the wall"
c=a.replace("s","")
def f(n):
	s=""
	if n>1:
		s="%d"%n+a+b+", %d"%n+a+".\nTake one down and pass it around, "+str(n-1)+[c,a][n>2]+b+".\n\n"+f(n-1)
	return s
print f(99)+"1"+c+b+", 1"+c+".\nGo to the store and buy some more, 99"+a+b+"."

The third is a bit more character-efficient, using lambda functions and recursion (𝟤𝟩𝟢 characters).

99-bottles-of-beer_d...gy-lambda.py; Python2, 271 bytes, 6 lines
a="%d bottle%s of beer"
b=" on the wall"
c=["","s"]
f=lambda i:a%(i,c[i>1])+b+", "+a%(i,c[i>1])+".\nTake one down and pass it around, "+a%(i-1,c[i>2])+b+".\n\n"
F=lambda i:f(i)+F(i-1)if i>0 else ""
print F(99)[:-65]+"Go to the store and buy some more, "+a%(99,"s")+b+"."

The fourth, final and most space-efficient [3] strategy is to use a while loop (𝟤𝟧𝟢 characters).

99-bottles-of-beer_d...egy-while.py; Python2, 251 bytes, 7 lines
a="%d bottle%s of beer"
b=" on the wall"
c=a+b+", "+a+"."
i=99
s=""
while i>1:s+=c%((i,"s")*2);i-=1;s+="\nTake one down and pass it around, "+a%(i,["","s"][i>1])+b+".\n\n"
print s+c%(1,"",1,"")+"\nGo to the store and buy some more, "+a%(99,"s")+b+"."

Footnotes

  1. [2020-07-21] One may note that I at the time of this post was not a codegolf.StackExchange member.
  2. [2020-07-27] N. b.: A source file’s trailing newline does not count towards the golf score.
  3. [2020-07-21] That I came up with at that time.