http://www.perlmonks.org?node_id=903641

In my opinion, its simplicity and directness reflects the basic nature of existence. Eventually, we all will have passed around 99 bottles of beer and there will be no more bottles on the wall.

-- Modern Drunkard Magazine (broken link - top 86 alcohol songs of all time, featuring 99 bottles of beer at #1)

🥴 🍻

Apart from its popularity as a drinking song, the classic 99 Bottles of Beer is ideal for singing to while away the hours on long trips because it takes a long time to sing and its repetitive format is easily memorized. It also pops up often in popular culture, especially film and TV. I remember the late Patrick Swayze, for example, singing it over and over again to Whoopi Goldberg in the movie Ghost in order to get her to go downtown (which she would never normally do).

The article capitalizes on the tendency of popular songs to evolve from long and content-rich ballads to highly repetitive texts with little or no meaningful content. Knuth writes that "our ancient ancestors invented the concept of refrain" to reduce the space complexity of songs, which becomes crucial when a large number of songs is to be committed to one's memory ... more ingenious approaches yield songs of complexity O(logN), a class known as "m bottles of beer on the wall".

-- The Complexity of Songs by Donald Knuth (1977)

While the affection for this song among computer programmers may have begun in 1977, with the publication of Knuth's seminal paper, it didn't become wildly popular until 1994, when some nitwit posted the entire lyrics of the song to a humour mailing list, provoking a BASIC version to be written:

10 REM BASIC Version of 99 Bottles of beer 20 FOR X=100 TO 1 STEP -1 30 PRINT X;"Bottle(s) of beer on the wall,";X;"bottle(s) of beer" 40 PRINT "Take one down and pass it around," 50 PRINT X-1;"bottle(s) of beer on the wall" 60 NEXT
to "save mailing list bandwidth". It snowballed from there until today we can now choose from more than 1400 programming language variations.

Over the years, many different Perl solutions have been proposed. On December 25 1998, for instance, Damian Conway composed a version using his Lingua::EN::Inflect module:

use Lingua::EN::Inflect 'inflect'; $n=shift||99; print inflect<<BURP while $n; NO(bottle of beer,$n) on the wall, NO(bottle of beer,$n)! Take one down, pass it around, NO(bottle of beer,@{[--$n]}) on the wall. BURP

Finally, we come to the reason for this node. Despite being the most popular code golf game of all time, precious few quality bottle golf solutions have ever been published. While there are vast hordes of really awful bottle golf solutions out there, the only half way decent one I'm aware of was whipped up in a few hours by Ton Hospel and Mtv Europe way back in 2003:

sub b{[@b=(abs||No,bottle."s"x!!++$_,of,beer),on,the,wall]}print "@{+b},\n@b,\nTake one down, pass it around,\n@{+b}.\n" for-pop||-99..-1

Many top quality golf solutions have been concocted though. For example, the 99 Bottles of Beer challenge at codegolf.com attracted over 1000 entries, more than twice as many as the next most popular game. And the leading scoreboard entries there are very short. It's just that none of them are available for public viewing.

Being a repeat codegolf vandal, I'm not shy about publishing my codegolf entries here, especially given the codegolf site seems to have been abandoned, and their beer bottle challenge has been running for over five years now. There is one caveat though: after discussions with the founders of this new PHP golf site, I won't be discussing my PHP solutions in this node, so as not to spoil their most popular game. Apart from that, I won't hold back, analysing general approaches to writing the shortest possible code to emit the lyrics of the classic drinking song and revealing all interesting Perl, Ruby and Python solutions I found while playing this game.

Rules of the game

In this 99 Bottles of Beer challenge, your program takes no input and must emit the entire lyrics of the song to stdout as follows:

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.
Your program's stdout must match the above output exactly, with one exception: the codegolf.com entry checking program strips trailing whitespace, so emitting any amount of trailing whitespace is permitted. Note that anything written to stderr is ignored, as is the program's exit code. So terminating your program by crashing it (dividing by zero, for example) is legitimate ... and surprisingly common and strangely satisfying.

Getting Started

🏌 🍺

The five times this game has been played here at Perl Monks:

did not turn up anything useful. Perhaps because of its popularity and low barrier of entry, if you randomly google for "99 bottles of beer golf", you'll be overwhelmed by some mind-bogglingly awful golf. Given the high number of hits, it's possible I overlooked some gems, but the only useful prior art I found for this game was from a Korean (kldp) web site where two different 174 stroke solutions had been posted:
sub b{[@b=(abs||99,bottle."s"x!!++$_,of,beer),on,the,wall]}print"@{+b} +, @b. ",$_?"Take one down and pass it around":"Go to the store and buy some +more",", @{+b}. "for-99..-1
and:
sub b{@b=(-$_++||99,bottl.($_?es:e),of,beer);"@b on the wall"}print+b, +", @b. ",$_?"Take one down and pass it around, ":"Go to the store and buy som +e more, ",b,". "for-99..-1
If you squint, you'll see that these two are based on the venerable 2003 solution of Hospel and Europe.

Given that the leader at this time, Polish golfing veteran "0xF", had already posted a 165-stroker, I was not hopeful that this approach could seriously threaten this experienced Polish golfing maestro. Still, I had to try at least.

First, I noticed that Ton and Mtv's snippet can be further shortened by changing "s" to 's' and embedding the sub inside the first @{} block like so:

@{sub b{[@b=(abs||No,bottle.'s'x!!++$_,of,beer),on,the,wall]}b}
This saves a single stroke because the first +b in the original is replaced by a bald b.

I then stared at the kldp solution, looking for anything unsightly. Well, that -99..-1 is an eyesore ... so I just hacked it out, reducing this solution from 174 to 168 strokes:

print"@{sub b{[@y=($n||=99,bottle.'s'x1!~$n,of,beer),on,the,wall]}b}, +@y. $_, @{b--$n}. "for("Take one down and pass it around")x98,"Go to the store and buy s +ome more"
Notice that this solution exploits undeclared Perl variables (i.e. $n above) being initialized to undef. I employed this tactical trick time and time again in this game in both Perl and PHP ... though not in Python, where it can't be done. You can pull it off in Ruby too, though you must scour RubyDoc for any "Perl-compatible" Ruby special variables (e.g. $., $_, $&, $', $`, ...) that might be gainfully employed.

Around this time, I also composed Python (195) and Ruby (191) equivalents of my early Perl subroutine-based solution.

Note that in the Python solution:

n=99 z=lambda:`n or 99`+" bottle"+"s of beer on the wall"[n==1:] while n:y=z();n-=1;print"%s, %s.\n"*2%(y,y[:-12],n and"Take one down a +nd pass it around"or"Go to the store and buy some more",z())
string slices are employed, which is almost mandatory when golfing in Python.

Though my first Ruby attempt:

def z "#{N<1?99:N} bottle#{'s'if N!=1} of beer"end (N=99).times{puts"%s, %s. "*2%[z+w=" on the wall",z,1>(N-=1)?"Go to the store and buy some more" +:"Take one down and pass it around",z+w],""}
is ungainly in the extreme, notice the curious expression:
z+w=" on the wall"
This syntactic quirk, where an assignment expression, sans parentheses, can be placed at the end of an expression, is used ad nauseam in both Ruby and PHP golf ... though not in Perl or Python, where it produces a syntax error.

I felt quite hungover around this time because these solutions were over ten strokes behind in Python and nearly twenty behind in Ruby! It was becoming increasingly obvious that using a function was consuming far too many strokes and therefore had to be eliminated. Less obvious in Perl perhaps, given I was only three behind, but my golfing instincts were telling me that a Perl solution without a sub should be considerably shorter than my 168-stroke subroutine-based solution.

Though I reached this conclusion some years ago, I didn't have the time to pursue it back then, and so put this game on hold for a little while. Actually, I forgot about it for several years until "reminded" of it by approaches from the founders of a new PHP golf site, where they were hosting the identical game.

When I returned to this game earlier this year, I noticed that "Rhebus", a young whipper snapper from London, had posted a sensational 162 strokes, thus snatching the Perl lead from the grasp of grizzled Polish expert 0xF.

Bottle Golf Tip No 1: Don't use a function

To eliminate the function, you need somehow to set up the loop so that the count of beer bottles is the same for each loop iteration. That is, instead of the "natural":

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.
where each paragraph must emit both n bottles and n-1 bottles, you might try breaking up the song as shown 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, 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.
That's better! Note that, crucial for a function-free solution, the two lines between the lines of dashes above now contain the same beer bottle count. There is a catch though: you must deal somehow with the annoyance of the first iteration, which does not contain a Take one down... line. The last line is also a pest, but can be dealt with simply by printing it after the main loop has terminated. As a variation, you can place the pestiferous last line first:
Go to the store and buy some more, 99 bottles of beer on the wall. 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, 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.
which nicely clarifies the essentially circular nature of the ditty. The "uniformity" of this output is attractive for a golfer because uniformity often means shorter code. To exploit this uniformity, however, you cannot just print the song on the fly; you must instead build a string or list in a loop, then print two separate bits of it at the end.

Let's see how this might look in Perl:

join("Take one down and pass it around",map{@c=(@b=(99-$_,bottle.'s'x! +/98/,of,beer),on,the,wall);", @c. @c, @b. "}0..98)=~/ /;print$'."Go to the store and buy some more$`"
Hmmm, 173 strokes. Not bad for a first attempt!

Regexes always win (Mtv's law of golf)

As you can see, the regex "side-effect variables", $' and $`, look very promising in this game -- as they so often are in golf.

It seems that Mtv's law applies to Ruby too, because this algorithm is actually better suited to Ruby than Perl due to some quirky Ruby syntactic sugar. You see, in Ruby:

somearray.join("string")
can be equivalently expressed as:
somearray*"string"
So, in Ruby, this new function-free approach effortlessly shaved eleven strokes from my earlier function-based solution:
"Go to the store and buy some more"+(-99..-1).map{|n|", #{c="#{b=-n," +bottle"+"s of beer"[1/-n,9]} on the wall"}. #{c}, #{b}. "}*"Take one down and pass it around"=~/ /;puts$'+$`
180 strokes! Though hardly the shortest, this is one of my favourite solutions because it directly expresses the (circular) idea of putting the last line first.

Update: Much later I reduced this by three strokes to 177:

"Go to the store and buy some more"+(-99..-1).map{|n|", #{b=-n," bottl +es"[0,6-n]+" of beer"," on the wall"}. #{b}, %s%s. "%b}*"Take one down and pass it around"=~/ /;puts$'+$`

What about Python? Alas, Python places many obstacles in the way of this approach:

  • Python does not have a short 0..98 range operator. The best you can do is range(99), which generates the sequence 0..98.
  • Unlike Perl and Ruby, regexes are not built into the Python language, usually making them too long for golf.
  • While map in Perl and Ruby takes a block/closure, Python map takes a function or lambda (both too long for golf) while list comprehensions/generator expressions take an expression, which nicely encourages side-effect-free functional purity, while being an utter nuisance for golfers.
  • Finally, and worst of all, notice that assignment is an operator in Perl and Ruby and may therefore be used as part of larger expressions. Not so in Python, where assignment is a statement and cannot be used as part of a larger expression. This cruel design decision has inflicted incalculable pain on Python golfers over a period of many years.
In any case, I couldn't make this approach short in Python. For example, though this one does work:
r="Take one down and pass it around".join(", %s.\n\n"%c+c+", %s.\n"%c[ +:-12]for c in[`99-z`+" bottle"+"s of beer on the wall"[z/98:]for z in + range(99)]) print r[35:]+"Go to the store and buy some more"+r[:34]
it's 207 strokes in length. Ouch. Of course, it's perfectly possible that I've overlooked some more golfish Python list comprehension syntax here. If so, please respond away.

After giving up on a functional approach, the shortest way I could find to implement this idea in Python was the following 193-stroker:

n=99 y="Go to the store and buy some more" while n:c=`n`+" bottle"+"s of beer on the wall"[1/n:];y+=", %s.\n\n"%c ++c+", %s.\nTake one down and pass it around"%c[:-12];n-=1 print y[68:-32]+y[:66]
Though admittedly two strokes better than my original function-based entry, this solution is a gaping eleven strokes behind the lead, 182 strokes, held by Norwegian teetotaler "hallvabo" -- who I contend has nowadays taken over the mantle of No. 1 Python golfer from Mark Byers.

In desperation, I tried a completely different line-at-a-time function-free approach in Python:

c=198 while c:y=`c/2or 99`+" bottle"+"s of beer on the wall"[3/c%3:];print[y +,"Take one down and pass it around","Go to the store and buy some mor +e"][1/c+c%2]+",",[y[:-12]+".",y+".\n"][c%2];c-=1
To my enormous frustration, this solution, in addition to being unspeakably ugly, turned out to have exactly the same score, 193 strokes. No improvement. At all. Aaargh!

The 187-stroke Ruby translation below is presented as an example of an accepted solution that "cleanly terminates" by dividing by zero:

n=198 loop{$><<[y=[n/2%-99+99," bottle"+"s of beer"[3/n%3,9]," on the wall"] +,"Take one down and pass it around","Go to the store and buy some mor +e"][j=1/n+1&~n-=1]<<", #{y[0,2+j]}. "+$/*j}
Note that the three stroke 198 above is routinely replaced by the two stroke ?ascii-char-with-ord-198 when golfing in Ruby.

Bottle Golf Tip No 2: Consider counting from 1 up to 99 not 99 down to 1

Though counting down from 99 to 1 seems "natural", golfers should always experiment with switching things around, to see if doing so shortens the code.

General golfing tip: Look for the part of your solution that makes you pull a face and try to shorten it.

With the possible exception of Python, all the above attempts at solving the crucial "1 bottle" versus "2-99 bottles" plural inflection problem have caused me to pull a face. Here is the parade of horrors seen so far:

bottle."s"x!!++$_,of,beer # Perl bottl.($_?es:e),of,beer # Perl bottle.'s'x1!~$n,of,beer # Perl bottle.'s'x!/98/,of,beer # Perl "bottle#{'s'if N!=1} of beer" # Ruby " bottle"+"s of beer"[1/-n,9] # Ruby " bottles"[0,6-n]+" of beer" # Ruby "bottle"+"s of beer"[3/n%3,9] # Ruby "bottle"+"s of beer"[1/n:] # Python

As you might expect, I was eager to eliminate all this inflective claptrap, replacing it with something simple and short like:
"bottle$x of beer"
where $x is the empty string for n==1 and "s" for n>1. Can this be achieved? Well, in languages where uninitialized variables display an empty string, it's easily pulled off simply by counting upwards from 1 to 99 ... so long as you can find a short way to set $x to "s" right after the first iteration. Of course, you'd need to build the output string in a loop and emit it at the end, yet the regex solutions described above already do that, courtesy of the $' and $` special variables.

Instead of $x, how about applying Mtv's law yet again and (indirectly) exploit a regex variable? As is common in golf, once I'd thought of the basic idea, a short solution materialized pretty quickly:

@c=(@b=(++$n,bottle.$&,of,beer),on,the,wall),s/^/Take one down and pas +s it around, @c. @c, @b. /,/s/for($_)x99;/,.* /;print$'."Go to the store and buy some more$&"
165 strokes! Caught 0xF at last!

Here we're building the string from the end to the beginning, from 1 to 99, in $_ by using the regex substitution s/^/.../. We're also exploiting a starting value of zero for the uninitialized $n. Finally, note that we're further exploiting an aliasing quirk of the perl for loop by building a single $_ value via a ($_)x99 list. I remembered this surprising trick from the 2002 Cantor game in TPR03 (see Mtv's pdf book of Golf), where a winning Cantor solution was:

s/./$& $&/gfor($_="- ")x pop;print

After a further period of shuffling things around, I finally tied for the lead at 162 strokes by replacing for with until like so:

/s/until@c=(@b=(++$n,bottle.$&,of,beer),on,the,wall),s/^/Take one down + and pass it around, @c. @c, @b. /,/, 99.* /;print$'."Go to the store and buy some more$&"
Note that the /, 99.*\n\n/ expression serves a dual purpose in that, in addition to terminating the until loop, this regex sets the $' and $& special variables to the desired values. Note further that if leading whitespace were allowed (as it is at phpgolf.org), this regex can be shortened by two strokes to /, 99.*/.

Further 162-stroke variations are:

/s/until@c=($b=++$n." bottle$& of beer",on,the,wall),s/^/Take one down + and pass it around, @c. @c, $b. /,/, 99.* /;print$'."Go to the store and buy some more$&"
and:
/s/until@c=(++$n." bottle$& of beer",on,the,wall),s/^/Take one down an +d pass it around, @c. @c, $c[0]. /,/99.* /;print$'."Go to the store and buy some more, $&"

I have an uneasy feeling that this solution is not the end of the road and that a future Perl golfer may further shorten it.

Can we translate this approach into Ruby? Yes. It's not as effective as the Perl version because Ruby lacks Perl's built-in s/^/.../ substitution operator. But it still shaves two strokes off our previous best Ruby solution:

1.upto(99){|n|~/s/;$_="Take one down and pass it around, #{c="#{b=n," +bottle#$& of beer"} on the wall"}. #{c}, #{b}. #$_"} ~/,.* /;puts$'+"Go to the store and buy some more"+$&
178 strokes. And here's an alternative 178-stroker using until:
n=0 ~/s/ until/, 99.* /=~$_="Take one down and pass it around, #{c="#{b=n+=1," bottle#$& of +beer"} on the wall"}. #{c}, #{b}. #$_";puts$'+"Go to the store and buy some more"+$&
The need for a space before until is unfortunate. As is the need for the n=0 initialization, which is not required in Perl.

As already discussed, I saw no hope of translating this approach into Python, where short "special variables" (e.g. $_, $&) do not exist and where regexes are not built into the language.

Bottle Golf Tip No 3: Unearth language-specific tactical tricks

My previous golfing articles focused mostly on the dark art of magic formulae. As you may have noticed above, magic formulae hardly feature in this game. You must rely instead on old fashioned classical golf technique, especially language-specific tactical tricks.

Of course. The true&tested method of "Can't possibly work, let's try it anyway."

-- Eugene van der Pijll

One of the best ways to find tactical tricks in the dusty corners of a programming language is to simply try ridiculous things and see what happens. Modern languages are so large and complex that it's not practical to precisely specify every little detail in the core language and libraries ... which offers opportunities for golfers to exploit implementation quirks.

Desperate to save a stroke or two, I experimented with how the % printf-like operator behaves when given more arguments than expected:

b=[99," bottles of beer"," on the wall"] print "%s%s\n"%b
Running this little test program produces:
99 bottles of beer
in Ruby, but:
TypeError: not enough arguments for format string
in Python.

In case you're interested, running this little test program in Perl:

@b=(99," bottles of beer"," on the wall"); printf "%s%s\n",@b;
produces the same result as Ruby. That is, Ruby and Perl silently ignore extra arguments to printf. They also automatically "flatten" the array. Python does not. Python also checks the number of printf arguments more strictly; that is, this Python program:
b=[99," bottles of beer"," on the wall"] print "%s%s\n"%tuple(b)
fails with:
TypeError: not all arguments converted during string formatting
whereas this one:
b=[99," bottles of beer"," on the wall"] print "%s%s%s\n"%tuple(b)
works, printing:
99 bottles of beer on the wall

I was able to exploit Ruby's printf behaviour, described above, to shave two precious strokes and so move into outright second place, three strokes behind "Ruby golfing god", flagitious:

1.upto(99){|n|~/s/;$_="Take one down and pass it around, #{c=n," bottl +e#$& of beer"," on the wall"}. #{c}, %s%s. #$_"%c} ~/,.* /;puts$'+"Go to the store and buy some more"+$&
176 strokes! Cheers! Staggering into second place in Ruby was a pleasant surprise after languishing twenty strokes behind the leaders early in this game.

I hope you've enjoyed my long journey through this game. It's time for me to sober up now and give the game of golf a rest for a while.

Leaderboards, May 2011

All languages (1237 entries):

1st 162 rhebus Perl 2nd 162 eyepopslikeamosquito Perl 3rd 165 0xF Perl 4th 166 dgzj001 Perl 5th 167 shinh Perl 6th 170 komondorok Perl 7th 170 ySas Perl 8th 171 flagitious Perl 9th 171 o0lit3 Perl 10th 171 gorash Perl

Perl (273 entries):

1st 162 rhebus 2nd 162 eyepopslikeamosquito 3rd 165 0xF 4th 166 dgzj001 5th 167 shinh 6th 170 komondorok 7th 170 ySas 8th 171 flagitious 9th 171 o0lit3 10th 171 gorash 11th 173 jgoon 12th 173 volte 13th 174 g.allen 14th 174 terjek 15th 174 bearstearns 16th 174 raymundo 17th 174 ott 18th 174 ozy4dm 19th 174 sixmen 20th 174 Aidy 21st 174 szeryf 22nd 174 Shuman 23rd 174 stressedlemming 24th 175 Ciaran 25th 175 arpad 26th 175 ergasun 27th 175 Nisse 28th 176 jojo 29th 176 Zeithase 30th 177 moq 31st 177 olivier 32nd 178 leus 33rd 179 kounoike 34th 179 yojeb 35th 180 Bryan 36th 181 lima1 37th 181 agenticarus 38th 181 pace_t_zulu 39th 181 zkhr 40th 182 fluffle ... 60th 193 grizzley 65th 195 Jasper 89th 204 chargrill 98th 209 yanick

Ruby (338 entries):

1st 173 flagitious 2nd 176 eyepopslikeamosquito 3rd 177 eban 4th 177 bitsweat 5th 177 leonid 6th 180 tal 7th 180 emiltin 8th 181 yowa 9th 182 oxy4dm 10th 182 shinh 11th 182 noop 12th 182 J-_-L 13th 183 kik 14th 183 yvl 15th 184 tobyaw 16th 185 ksk 17th 186 jojo 18th 186 carldr 19th 186 pace_t_zulu 20th 187 adel

Python (423 entries):

1st 182 hallvabo 2nd 183 logan 3rd 183 uli 4th 189 Mark Byers 5th 190 hiro.suzuki 6th 192 primo 7th 193 eyepopslikeamosquito 8th 195 recursive 9th 195 tryeng 10th 195 etaronis 11th 196 kt3k 12th 197 gtalpo 13th 197 ProfessorO 14th 198 lifthrasiir 15th 198 Jay 16th 198 SergeantPepper 17th 199 hirose 18th 199 max 19th 200 Dilb 20th 200 yonilevy

PHP (264 entries):

1st 172 ToastyX 2nd 173 eyepopslikeamosquito 3rd 175 alab 4th 175 Morgil 5th 178 jln 6th 178 cms 7th 179 hamori 8th 180 hiro.suzuki 9th 183 eyepopper 10th 185 Leafy 11th 186 carldr 12th 191 shinh 13th 193 heavyware 14th 197 Shadlan 15th 198 davidpogson 16th 198 lxlh 17th 199 echofish 18th 200 phoe 19th 201 underdonges 20th 203 Theory

References

Added later:

Update: As described in detail in a three part series Compression in Golf: Part I, this 160-stroke Perl solution was later whittled to around 150 strokes, by using pack u compression.

Replies are listed 'Best First'.
Re: Drunk on golf: 99 Bottles of Beer
by Limbic~Region (Chancellor) on May 08, 2011 at 23:46 UTC
    eyepopslikeamosquito,
    t also pops up often in popular culture, especially film and TV. I remember the late Patrick Swayze, for example, singing it over and over again to Whoopi Goldberg in the movie Ghost in order to get her to go downtown (which she would never normally do).

    It has been a while but I think you are wrong. I remember the song he sang as "I'm Henery The Eighth, I am I am". On a side note, I do believe there was a reference in a ghost show. According to 99 Bottles of Beer, it appeared on an episode of the Ghostbuster's cartoon.

    I always enjoy your posts and this is no exception - I just seldom have anything to contribute :-)

    Cheers - L~R

      Yes, you are right re "Henery the Eighth, I am". I found a youtube clip which shows Patrick Swayze singing Henry the Eighth to Whoopi Goldberg, as you say. Ghost is one of my all-time favourite movies, and I've seen it many times, but don't own a copy. It's been a while, but I remember a later street scene, where Whoopi tries to renege on going downtown and Patrick responds by singing 99 bottles of beer. Whoopi then quickly changes her mind and goes downtown as agreed. I could be mistaken though, and can't find a supporting youtube clip. Update: Found a supporting youtube clip.

Re: Drunk on golf: 99 Bottles of Beer
by eyepopslikeamosquito (Archbishop) on May 25, 2011 at 11:47 UTC

    Despite being shocked that Jasper and I had totally different 101 stroke solutions to the Saving Time challenge, I had a feeling that Rhebus and I would have similar solutions to this one. There just seemed to be much less scope for magic formulae and general weirdness in the beer bottle game. I've contacted Rhebus and am pleased to report that he was as curious as I was and happy to compare our solutions in the hope of unearthing an even shorter one. Here is his 162 stroke winning entry:

    @a=(++$a." bottle$& of beer",on,the,wall),s/^/ Take one down and pass it around, @a. @a, $a[0]./,/s/until/ 99/;print"99$' Go to the store and buy some more, @a."

    If you compare his with mine:

    /s/until@c=(@b=(++$n,bottle.$&,of,beer),on,the,wall),s/^/Take one down + and pass it around, @c. @c, @b. /,/, 99.* /;print$'."Go to the store and buy some more$&"
    you'll see some similarities and some differences. The major difference is that Rhebus used a five stroke shorter terminating regex, namely /\n99/ instead of my /, 99.*\n\n/, that unfortunately cost five strokes elsewhere: two strokes for the extra 99 at the front of the printed string and three for the terminating , @a. (instead of $&) at its end.

    Can these two solutions be combined somehow to produce a shorter one? I can assure you that many variations are possible but I couldn't find any reductions. So if you can further shorten it, you can claim to be the inventor of the shortest solution to the most popular golf game of all. :)

      In the middle of the night it occurred to me that a further stroke can be shaved by changing the terminating regex from /, 99.*\n\n/ to /, 99\D+/, producing the following 161 stroker:

      /s/until@c=(@b=(++$n,bottle.$&,of,beer),on,the,wall),s/^/Take one down + and pass it around, @c. @c, @b. /,/, 99\D+/;print$'."Go to the store and buy some more$&"
      The same trick can be applied to Ruby also.

        After applying the new terminating regex to Ruby via the following 175 stroker:

        n=0;~/s/ until/, 99\D+/=~$_="Take one down and pass it around, #{c=n+= +1," bottle#$& of beer"," on the wall"}. #{c}, %s%s. #$_"%c;puts$'+"Go to the store and buy some more"+$&
        I could not restrain myself from trying to reduce it further. Two eyesores that I found extremely annoying were:
        • The space between the ~/s/ and until.
        • The n=0; initialization, which is not needed in Perl.
        Can anything be done about them? To get rid of the wretched space, I tried reorganizing via:
        n=0;$_="Take one down and pass it around, #{~/s/;c=n+=1," bottle#$& of + beer"," on the wall"}. #{c}, %s%s. #$_"%c until~/, 99\D+/;puts$'+"Go to the store and buy some more"+$&
        Still a space before the until and still 175 strokes. Rats. Chanting Eugene's "can't possibly work, try it anyway" mantra, I changed ~/, 99\D+/ to /, 99\D+/ and it worked! Though it now emits a "warning: regex literal in condition" message to stderr, that does not matter to codegolf. Another stroke shaved. Not as I intended, but no matter. Only one more needed to catch flagitious.

        What about the n=0; initialization? At this point, I remembered the Roman to Decimal challenge, where I was able to avoid an initialization by hijacking the $. variable. Indeed, if you want a numeric variable initialized to a known value, that is the only built-in variable Ruby makes available. Given my program is four lines in length, $. is initialized to the value four. So I scribbled this down during my lunchtime walk to Neutral Bay today:

        n=0;n+=1 4-$.-=1
        Hmmm, well that's clearly one stroke less. But will it work? Or will Ruby complain when the $. "line number" becomes negative? Nope, not a whimper about a negative line number. So, much to my surprise, I've now tied flagitious for the Ruby lead on 173 strokes:
        $_="Take one down and pass it around, #{~/s/;c=4-$.-=1," bottle#$& of +beer"," on the wall"}. #{c}, %s%s. #$_"%c until/, 99\D+/;puts$'+"Go to the store and buy some more"+$&

        Update: an alternative 173 using Ruby symbols:

        $_="Take one down and pass it around, #{c=4-$.-=1," bottle#{c&&:s} of +beer"," on the wall"}. #{c}, %s%s. #$_"%c until/, 99\D+/;puts$'+"Go to the store and buy some more"+$&

        Update: Note that dmd compressed my 173 stroke solution above to 168 by using Ruby compression ... and -- as indicated at Re^3: Compression in Golf: Part III -- it seems highly likely that Ruby golf maestro flagitious used Ruby compression in his 173 stroke solution! ... which, after years of painstaking effort, makes my uncompressed 173-stroker above my best ever Ruby golf performance.

Re: Drunk on golf: 99 Bottles of Beer
by eyepopslikeamosquito (Archbishop) on Jun 12, 2011 at 07:25 UTC

    Python Update

    It disturbed me that the 99 bottles of beer analysis in the root node was incomplete without a short Python solution. Unable to concoct one myself, I contacted hallvabo, who was delighted to share his winning solution and the circumstances behind it.

    If I just reveal his solution now, however, you might just exclaim "What the f*#?! is that?" and move on. Armed with an understanding of Python string slices though, hallvabo's wacky-looking concoction becomes logical and comprehensible. So I'll start with an overview of Python slices so as to make his solution more accessible.

    Introduction to Python Slices

    "Regexes always win" -- Mtv's law of Perl golf

    "String bitwise operators always win" -- ToastyX's law of PHP golf

    "Slices always win" -- Hallvabo's law of Python golf

    When playing golf, it's vital to know which language features tend to produce the shortest code. While the above "laws" are admittedly outrageous oversimplifications, they are catchy and easy to remember. Perl regexes frequently trump alternative approaches. As do Python slices.

    The Python slicing operator s[i:j:stride] extracts subsequences, where [i,j) is a semi-open range of indices in sequence s. All slicing arguments are optional. Negative indices (relative to the end of the string) and negative strides (reverse direction) are supported. Some examples should clarify:

    x = "012345" s = x[0:3] # s contains "012" s = x[:3] # same thing (using default) s = x[4:] # s contains "45" s = x[1:4] # s contains "123" s = x[-5:-2] # same thing using negative indices s = x[-2:99] # s contains "45" - negative index from end of # string allowed, as is index outside string (99) s = x[::2] # s contains "024" s = x[1::2] # s contains "135" s = x[1:4:2] # s contains "13" s = x[1:5:2] # s contains "13" s = x[1:6:2] # s contains "135" s = x[-1::-2] # s contains "531" s = x[::-2] # same thing (using default) s = x[::-1] # s contains "543210" (i.e. string reverse) s = x[::0] # "ValueError: slice step cannot be zero"
    If you want to be competitive at Python golf, you must master all the intricacies and defaults of string slicing.

    Ruby also supports complex and powerful string slicing, but not strides. Though Perl supports array and hash slices, when it comes to slicing and dicing strings, Perl's substr function is nowhere near as concise as Python and Ruby slices and, accordingly, is rarely sighted in a winning Perl golf entry. By the way, I'd love to see Perl support more concise string slicing.

    The "Slice and Dice" Golfing Manoeuvre

    From my early 195-stroke function-based Python solution:

    n=99 z=lambda:`n or 99`+" bottle"+"s of beer on the wall"[n==1:] while n:y=z();n-=1;print"%s, %s.\n"*2%(y,y[:-12],n and"Take one down a +nd pass it around"or"Go to the store and buy some more",z())
    notice the expression:
    n and"Take one down and pass it around"or"Go to the store and buy some + more"
    Shortening the two strings above to "Take" and "Go to" for clarity, let's consider the many and varied ways of doing this in Python (TMTOWTDI):
    "Take"if n else"Go to" (n>0)*"Take"or"Go to" ["Go to","Take"][n>0] ("Go to","Take")[n>0] n and"Take"or"Go to" "GToa kteo"[n>0::2] # "Slice and Dice" wins this golf!
    As you can see, I missed the winning Python "Slice and Dice" tactical trick in this game.

    Good Things Come in Threes

    Three quarks for Muster Mark!
    Sure he hasn't got much of a bark
    And sure any he has it's all beside the mark.

    -- James Joyce in Finnegans Wake (inspiration for naming the quark)

    In addition to missing the "Slice and Dice" tactical trick, I overlooked a crucial strategic idea, namely to break up each verse into threes, like so:

    99 bottles of beer on the wall,<space> 99 bottles of beer.\nTake one down and pass it around,<space> 98 bottles of beer on the wall.\n\n
    Yet again in golf, uniformity pays dividends. Notice that each and every print statement now begins in the same way, with "n bottles of beer". Not only do you avoid the dreaded function call, but you can now print the "n bottles of beer" refrain on the fly, without needing to store it in a variable. There is a price to pay however. Instead of looping 99 times, you must now loop three times longer, 297 times. In turns out in Python that this is a bargain, as demonstrated by hallvabo's winning entry.

    Dissecting Hallvabo's 182 Stroke Python Solution

    With the key tactical and strategic ideas explained, I hope you'll find hallvabo's winning Python entry easier to follow:

    i=298 while~-i:print i/3or 99,'bottle'+'s of beer on the wall.\n\n'[2<i<6:9+ +i%3*12]+'..\n\nGToa kteo otnhee dsotwonr ea nadn dp absusy isto ma +er omuonrde,,'[(i>3)+i%3*68::2],;i-=1

    To generate the required numeric beer bottle sequence, notice that hallvabo employed a while loop, very common in Python golf, counting down from 298 to 2, as illustrated in the table below:

    ii%3i/3Part of song
    298199bottles of beer on the wall,
    297099bottles of beer. Take one down and pass it around,
    296298bottles of beer on the wall.
    295198bottles of beer on the wall,
    294098bottles of beer. Take one down and pass it around,
    293297bottles of beer on the wall.
    ............
    ............
    ............
    712bottles of beer on the wall,
    602bottles of beer. Take one down and pass it around,
    521bottle of beer on the wall.
    411bottle of beer on the wall,
    301bottle of beer. Go to the store and buy some more,
    220 => 99bottles of beer on the wall.

    As you can see, his 2<i<6 boolean expression is used as the starting offset in a "s of beer..." string slice thus ensuring "bottle" rather than "bottles" is displayed when i has the values 3, 4 and 5 (corresponding to one bottle). The 9+i%3*12 ending offset concisely trims the "s of beer on the wall.\n\n" string to the required length.

    The odd-looking ~-i while loop expression is just the Inchworm-On-A-Stick "secret operator", first employed by Perl golfer Ton Hospel in 2002, and nowadays routine in code golf across all languages. It's effectively the same as i-1 but with different precedence. Here it's employed simply to avoid a space after the while keyword. By the way, the converse inchwormy -~i secret operator for i+1 is routinely played in Python and Ruby golf ... but not Perl because it does not work for positive numbers -- for example, -~1 produces 2 in Ruby and Python, but -4,294,967,294 in Perl.

    Update: As noted in the "Inchworm on a stick" section at perlsecret, the high precedence decrement ~-$x inchworm-on-a-stick only works for positive $x while the high precedence increment -~$x inchworm-on-a-stick only works for negative $x ... though with use integer they work for all $x (as they do in Ruby and Python golf).

    The hardest part of hallvabo's masterwork to understand is probably the odd-looking string slice at the end:

    '..\n\nGToa kteo otnhee dsotwonr ea nadn dp absusy isto maer omuonr +de,,'[(i>3)+i%3*68::2]
    This complex "slice and dice" serves a dual purpose:
    • To switch from "Take one down and pass it around" to "Go to the store and buy some more" at the end of the song.
    • To deal with the (fiddly) song punctuation. Note, by the way, that the (quirky) Python print statement thoughtfully does exactly what we wish for: a space is obligingly inserted after each comma, even the trailing one, yet is suppressed if what is printed already ends in a newline (as our i%3==2 case does).
    Instructively, notice that hallvabo's earlier (longer) submissions used a number of different strings; he found that reducing the number of strings lowered his golf score. To illustrate, here is his earlier 187-stroke entry:
    i=298 while~-i:print i/3or 99,'bottle'+'s of beer on the wall'[2<i<6:9+i%3*1 +4]+('.\n'+'GToa kteo otnhee dsotwonr ea nadn dp absusy isto maer o +muonrde,,'[i>3::2],',','.\n\n')[i%3],;i-=1

    I have to give due credit here to "Logan", a remarkable golfing "beginner". Logan turned up on the codegolf IRC channel one day and the very experienced hallvabo gave him quite a bit of help to get him started. Within a few days, and much to hallvabo's surprise and chagrin, the pupil overtook the master, even stealing the outright Python lead from "uli"! To repay hallvabo, Logan gave him a slight hint to "stop being line oriented". As I well remember from the early Perl golfing days, giving even the slightest of hints to an expert golfer is fatal and so it proved here. Hallvabo fought back and eventually overtook his pupil Logan to win the game by a single stroke.

    Reducing Hallvabo's 182 Stroke Python Solution

    Of course, I tried to reduce hallvabo's winning entry further. When I first sighted his solution, I pulled a face the instant I caught sight of the ugly parentheses in (i>3)+i%3*68. Surely something can be done to eliminate them! Much to my surprise, I trimmed a stroke at my first attempt:

    i=298 while~-i:print i/3or 99,'bottle'+'s of beer on the wall.\n\n'[2<i<6:9+ +i%3*12]+'.X\n.G\noT atkoe tohnee sdtoowrne aanndd pbausys siotm +ea rmoourned,,'[3%i+i%3*68::2],;i-=1
    Notice that replacing (i>3) with 3%i saves two strokes, while costing only one, namely the wasted X above in the string being sliced and diced.

    Alas, I couldn't shave any more, even after running some brute force search programs over hallvabo's string slice magic formulae. I uncovered some interesting new variations though. For example, 2<i<6 can equivalently be replaced by 5/i%2 or 5/i%i ... which is essential when translating to Ruby because, unlike Python, Ruby does not allow booleans in numeric expressions.

    Update: It is just possible that changing hallvabo's 298..2 range to 297..1 or 299..3 or 296..0 or some such might produce a shorter solution. For example, here is a 184-stroker using 297..1:

    i=297 while i:print-~i/3or 99,'bottle'+'s of beer on the wall.\n\n'[1<i<5:i% +3*14^21]+'..\n\nGToa kteo otnhee dsotwonr ea nadn dp absusy isto m +aer omuonrde,,'[i%3-2^-72|2%i::2],;i-=1
    Having said that, hallvabo's 298..2 does seem to be the most "natural" and I'd be surprised if a different range produced a shorter solution. Writing and running a program to exhaustively search all possible string slice magic formulae for a variety of ranges should settle this issue.

    A Perl Translation

    I didn't see much hope of adapting this approach to Perl due to the length of Perl's dreaded substr function, 191 strokes being the best I could find:

    print~$_/3|0||99," bottle",substr("s of beer on the wall. X",6/$_%2,-4**($_%3)),(", ",$_<-4?". Take one down and pass it around, ":". Go to the store and buy some more, ")[$_%3-3]for-299..-3
    Because substr takes a length parameter, rather than a Python slice index, I was forced to concoct a different (negative) magic formula (-4**($_%3)) to count from the end of the string, which explains the wasted X above. Nasty.

    Replacing substr with an array slice didn't help:

    print-$_/3|0||99," bottle",("s"," of beer"," on the wall",". ")[5/$_%2..-$_%3+1],($_<-3?". Take one down and pass it around, ":". Go to the store and buy some more, ",", ")[-$_%3]for-298..-2

    A Ruby Translation

    I also wrote a couple of quick Ruby translations, a 183-stroke:

    298.downto(2){|i|$><<i/3%-99+99<<" bottle"+"s of beer on the wall. "[5/i%i..8+i%3*12]<<[i>3?". Take one down and pass it around, ":". Go to the store and buy some more, ",", "][i%3]}
    and a 184-stroke:
    298.downto(2){|i|$><<i/3%-99+99<<" bottle"+"s of beer on the wall. "[5/i%i..8+i%3*12]<<". Go to the store and buy some more, . Take one down and pass it around, "[3%i*25/2+i%3*34,37]}
    Though these two can surely be further reduced, 173 strokes looks out of reach.

    Ruby Update

    Much later, I was surprised to learn that emiltin and J-_-L had both independently concocted an eval approach that never occurred to me. Emiltin's original 180-stroke solution was:

    eval"$><<%{#{a=b='#{n} bottl#{"es"[0,n]} of beer',' on the wall'}, #{b +}. \#{0<(n-=1)?'Take one down and pass it around':(n=99;'Go to the store +and buy some more')}, #{a}. };"*n=99
    while J-_-L's original 182-stroker was:
    eval %{puts"#{a='#{n} bottle#{n>1?:s:p} of beer on the wall'}, #{a[0,3 +0]}. \#{(n-=1)<1?(n=99;'Go to the store and buy some more'):'Take one down +and pass it around'}, #{a}. " }*n=99

    So Ruby wins the TMTOWTDI prize in this game given that three completely different approaches all yield sub-180 solutions.

    Regex (173)

    $_="Take one down and pass it around, #{~/s/;c=4-$.-=1," bottle#$& of +beer"," on the wall"}. #{c}, %s%s. #$_"%c until/, 99\D+/;puts$'+"Go to the store and buy some more"+$&

    Map (177)

    "Go to the store and buy some more"+(-99..-1).map{|n|", #{b=-n," bottl +es"[0,6-n]+" of beer"," on the wall"}. #{b}, %s%s. "%b}*"Take one down and pass it around"=~/ /;puts$'+$`

    Eval (178)

    # Based on emiltin. eval"$><<%{#{a=b='#{n} bottl#{"es"[0,n]} of beer',' on the wall'}, #{b +}. \#{0<(n-=1)?'Take one down and pass it around':'Go to the store and bu +y some more'%n=99}, #{a}. };"*n=99

    # Based on J-_-L. eval"$><<%{#{a=b='#{n} bottle#{n>1?:s:p} of beer',' on the wall'}, #{b +}. \#{(n-=1)<1?'Go to the store and buy some more'%n=99:'Take one down an +d pass it around'}, #{a}. } "*n=99

    Shortest Perl, Ruby and Python Solutions

    To sum up, as at June 2011, the shortest known solutions to this game are:

    Perl (161 strokes):

    /s/until@c=(@b=(++$n,bottle.$&,of,beer),on,the,wall),s/^/Take one down + and pass it around, @c. @c, @b. /,/, 99\D+/;print$'."Go to the store and buy some more$&"

    Ruby (173 strokes):

    $_="Take one down and pass it around, #{~/s/;c=4-$.-=1," bottle#$& of +beer"," on the wall"}. #{c}, %s%s. #$_"%c until/, 99\D+/;puts$'+"Go to the store and buy some more"+$&

    Python (181 strokes):

    i=298 while~-i:print i/3or 99,'bottle'+'s of beer on the wall.\n\n'[5/i%i:i% +3*28^9]+'.X\n.G\noT atkoe tohnee sdtoowrne aanndd pbausys siotm +ea rmoourned,,'[i%3*69^3%i::2],;i-=1

    Given that over a thousand golfers have hacked on this game for more than five years now, further reductions won't come easy, though they're certainly possible. So if you can further reduce any of these solutions ... well ... kudos. And please post them here. :)

    Acknowledgement: I'd like to thank hallvabo for his help in preparing this node. Update: I'd like to thank emiltin and J-_-L for their help in preparing the Ruby update.

Re: Drunk on golf: 99 Bottles of Beer
by eyepopslikeamosquito (Archbishop) on Feb 16, 2012 at 12:09 UTC

    Perl Update

    I contacted experienced Polish golfer 0xF mentioning my article and asking if he'd like to share his original 165-stroke solution -- which led this competition for over a year in its early stages.

    Instead of sharing, however, he wanted to keep on competing! Divulging nothing, he snatched the lead once again by posting a 160-stroker.

    What on earth was 0xF up to? Had he shortened his original 165-stroker? Or my 161-stroker? Or found a completely new approach? I had no way of knowing yet could hardly ignore this provocation.

    Exploring All Possible Variations

    I'd been quite lazy with my 161-stroker in that I had neglected to explore all possible variations. Laziness may be one of the three virtues of the Perl programmer but definitely not of the Perl golfer. No, you have to work hard, exploring every possible variation, just in case one of them allows a surprising tactical trick.

    So I started by reorganizing my 161-stroke solution like so:

    @c=(@b=(++$n,bottle.$&,of,beer),on,the,wall),s/^/Take one down and pas +s it around, @c. @c, @b. /,/s/until/, 99\D+/;print$'."Go to the store and buy some more$&"
    This is nothing more than a simple rearrangement. However, on seeing it written that way I immediately noticed that the /, 99\D+/ regex was false all the time within the loop, becoming true only after loop termination ... which means that the default regex (i.e. //) is not affected within the loop because that very short // notation represents the last successful match.

    Hmmm. That means if I can eliminate the annoying /s/, I can shorten s/^// by one stroke to s///. Yet if I eliminate the /s/ I must also delete the ridiculously short $&. That is like losing an old friend. And what are the chances of finding a short enough replacement? Looks impossible, but I have to try at least.

    Without the /s/, all we have left to distinguish the first time through the loop from subsequent iterations is the s/// expression. What changes after this expression is executed for the first time? Well, as far as I can tell only $_ and the regex "side-effect" variables $&, $', $`, @+, @-. So we need to find a short expression using one of those six. Since $_, $&, $' and $` all look hopeless, that only leaves @+ and @-.

    ySas' Device

    I remember some very happy golfing moments with good ol' @-: I used it in my winning Roman to Decimal solution and fondly remember a marvellous stunt pulled by ySas where he used @- as a multiplier in s/(M)|D/4x@-x5/eg thus distinguishing a matching M from a matching D.

    Can I mimic ySas here? Yes! You see, there are zero elements in @- before the first s/// and one element thereafter (corresponding to the offset of the start of the (always) successful match, there being no sub-patterns). So @-, somewhat flukily, has exactly the right values for a multiplier -- 's'x@- -- which shortens my solution by a single stroke:

    @c=(@b=(++$n,bottle.'s'x@-,of,beer),on,the,wall),s//Take one down and +pass it around, @c. @c, @b. /until/, 99\D+/;print$'."Go to the store and buy some more$&"
    160 strokes!

    What next? Well, I can't see anything more. However, if 0xF found a different improvement, and reads this node, we may finally see the magical 160-stroke barrier broken.

    Update: The 160-stroke barrier was shattered down to 149 by utilising "pack u" compression, as detailed in this series of nodes.

Re: Drunk on golf: 99 Bottles of Beer
by primo (Scribe) on May 23, 2016 at 07:15 UTC

    phpGolf seems to have reached the end of its life cycle. My 172 byte solution was the following:

    <?for(;100>$a=1+$a." bottle$o[21] of beer";$o=" Take one down and pass it around, $b. ".$g="$b, $a.$o")$b="$a on the wall";echo"$g Go to the store and buy some more, $b.";

      Thanks for sharing your solution, and for reminding me of the good old days on the PHP golf course. After digging out my old notes, I can finally share my PHP solutions too.

      I had many different 173 strokers, such as (without bothering with the routine PHP bitwise ~ string inversion golfing trick to reduce the " on the wall" strings below by one char - see here or the "Quoted Strings" section at The golf course looks great, my swing feels good, I like my chances (Part VI) for the gory details of this PHP trick):

      <?for(;$c=($b=++$n." bottle$s of beer")." on the wall",99^$x="$c, $b. $z$x";$s=s)$z="Take one down and pass it around, $c. ";echo$x."Go to the store and buy some more, $c."; <?for(;$c=($b=++$n." bottle$z[20] of beer")." on the wall",99^$x="$c, +$b. $z$x";)$z="Take one down and pass it around, $c. ";echo$x."Go to the store and buy some more, $c."; <?for(;99^$x=($c=($b=++$n." bottle$x[21] of beer")." on the wall").", +$b. $x";$x="Take one down and pass it around, $c. $x")?><?=$x,"Go to the store and buy some more, $c.";
      I had a mental block and just could not get to 172!

      After I had given up, ToastyX mercifully gave me a hint, and then I finally did get to 172 -- but did not submit either of these two below because I would never have thought of them without ToastyX's hint:

      <?for(;99^$c=($b=++$n." bottle$x[21] of beer")." on the wall";$x=" Take one down and pass it around, $c. $c, $b.$x")?><?="$c, $b.$x Go to the store and buy some more, $c."; <?for(;99^$c=($b=++$n." bottle$s of beer")." on the wall";$s=s)$x=" Take one down and pass it around, $c. $c, $b.$x";echo"$c, $b.$x Go to the store and buy some more, $c.";

Re: Drunk on golf: 99 Bottles of Beer
by Anonymous Monk on Jun 01, 2012 at 21:03 UTC
    Ruby (168) Utilizing pack compression:
    eval ['u\x0f\x04\xfd\xd0\xbez\x88\xbb\xb2\xb8\xbb\x86\xcd+\xed\xea\xe1 +\xee\xd7\xb0\xc3\xb9\xb1\xed\xeb\xec\xca\xb8L\xec>5v"\xddD\xc0\xbb~\x +ccq\xa6 \xf8\xd4a\x9a\xc3\xae\xec\x8f\xb7\xe2\x8a\xf0\x8c\x0b\xbb+\xe +f\x19b\xefG\xa9\xa4.\x8e\x0f\x8dz3\xb1p\x17\x03\xbc\x0cO\xc2\x17^\xf2 +\xaf\x19\xa9=\x96N+\xcf\x0f\xf2\xdc\xb1\xc0A\xcb\x0b\xdb;\xc6\xce\xf1 +\x96.\xf0\xc6\xcb\xe2\xed\xea\xe1\xed\xfc\xb6\xef\x0b*\x8b\xba\xac\xb +e#;\x08\xb1\x06'].pack('u69').tr'>-^','a-} GT'