96
\$\begingroup\$

Write a program that outputs the lyrics to 99 Bottles of Beer, in as few bytes as possible.

Lyrics:

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.

96 bottles of beer on the wall, 96 bottles of beer.
Take one down and pass it around, 95 bottles of beer on the wall.

95 bottles of beer on the wall, 95 bottles of beer.
Take one down and pass it around, 94 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.

Rules:

  • Your program must log to STDOUT or an acceptable alternative, or be returned from a function (with or without a trailing newline).
  • Your program must be a full, runnable program or function.
  • Languages specifically written to submit a 0-byte answer to this challenge are allowed, just not particularly interesting.

    Note that there must be an interpreter so the submission can be tested. It is allowed (and even encouraged) to write this interpreter yourself for a previously unimplemented language.

  • This is different from the output by HQ9+ or 99. Any answers written in these languages will be deleted.

As this is a catalog challenge, this is not about finding the language with the shortest solution for this (there are some where the empty program does the trick) - this is about finding the shortest solution in every language. Therefore, no answer will be marked as accepted.

Catalogue

The Stack Snippet at the bottom of this post generates the catalogue from the answers a) as a list of shortest solution per language and b) as an overall leaderboard.

To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:

## Language Name, N bytes

where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

## Ruby, <s>104</s> <s>101</s> 96 bytes

If there you want to include multiple numbers in your header (e.g. because your score is the sum of two files or you want to list interpreter flag penalties separately), make sure that the actual score is the last number in the header:

## Perl, 43 + 2 (-p flag) = 45 bytes

You can also make the language name a link which will then show up in the snippet:

## [><>](http://esolangs.org/wiki/Fish), 121 bytes

<style>body { text-align: left !important} #answer-list { padding: 10px; width: 290px; float: left; } #language-list { padding: 10px; width: 290px; float: left; } table thead { font-weight: bold; } table td { padding: 5px; }</style><script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="language-list"> <h2>Shortest Solution by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr> </thead> <tbody id="languages"> </tbody> </table> </div> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr> </thead> <tbody id="answers"> </tbody> </table> </div> <table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr> </tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr> </tbody> </table><script>var QUESTION_ID = 64198; var ANSWER_FILTER = "!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe"; var COMMENT_FILTER = "!)Q2B_A2kjfAiU78X(md6BoYk"; var OVERRIDE_USER = 36670; var answers = [], answers_hash, answer_ids, answer_page = 1, more_answers = true, comment_page; function answersUrl(index) { return "//api.stackexchange.com/2.2/questions/" + QUESTION_ID + "/answers?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + ANSWER_FILTER; } function commentUrl(index, answers) { return "//api.stackexchange.com/2.2/answers/" + answers.join(';') + "/comments?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + COMMENT_FILTER; } function getAnswers() { jQuery.ajax({ url: answersUrl(answer_page++), method: "get", dataType: "jsonp", crossDomain: true, success: function (data) { answers.push.apply(answers, data.items); answers_hash = []; answer_ids = []; data.items.forEach(function(a) { a.comments = []; var id = +a.share_link.match(/\d+/); answer_ids.push(id); answers_hash[id] = a; }); if (!data.has_more) more_answers = false; comment_page = 1; getComments(); } }); } function getComments() { jQuery.ajax({ url: commentUrl(comment_page++, answer_ids), method: "get", dataType: "jsonp", crossDomain: true, success: function (data) { data.items.forEach(function(c) { if (c.owner.user_id === OVERRIDE_USER) answers_hash[c.post_id].comments.push(c); }); if (data.has_more) getComments(); else if (more_answers) getAnswers(); else process(); } }); } getAnswers(); var SCORE_REG = /<h\d>\s*([^\n,<]*(?:<(?:[^\n>]*>[^\n<]*<\/[^\n>]*>)[^\n,<]*)*),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/; var OVERRIDE_REG = /^Override\s*header:\s*/i; function getAuthorName(a) { return a.owner.display_name; } function process() { var valid = []; answers.forEach(function(a) { var body = a.body; a.comments.forEach(function(c) { if(OVERRIDE_REG.test(c.body)) body = '<h1>' + c.body.replace(OVERRIDE_REG, '') + '</h1>'; }); var match = body.match(SCORE_REG); if (match) valid.push({ user: getAuthorName(a), size: +match[2], language: match[1], link: a.share_link, }); else console.log(body); }); valid.sort(function (a, b) { var aB = a.size, bB = b.size; return aB - bB }); var languages = {}; var place = 1; var lastSize = null; var lastPlace = 1; valid.forEach(function (a) { if (a.size != lastSize) lastPlace = place; lastSize = a.size; ++place; var answer = jQuery("#answer-template").html(); answer = answer.replace("{{PLACE}}", lastPlace + ".") .replace("{{NAME}}", a.user) .replace("{{LANGUAGE}}", a.language) .replace("{{SIZE}}", a.size) .replace("{{LINK}}", a.link); answer = jQuery(answer); jQuery("#answers").append(answer); var lang = a.language; lang = jQuery('<a>'+lang+'</a>').text(); languages[lang] = languages[lang] || {lang: a.language, lang_raw: lang.toLowerCase(42), user: a.user, size: a.size, link: a.link}; }); var langs = []; for (var lang in languages) if (languages.hasOwnProperty(lang)) langs.push(languages[lang]); langs.sort(function (a, b) { if (a.lang_raw > b.lang_raw) return 1; if (a.lang_raw < b.lang_raw) return -1; return 0; }); for (var i = 0; i < langs.length; ++i) { var language = jQuery("#language-template").html(); var lang = langs[i]; language = language.replace("{{LANGUAGE}}", lang.lang) .replace("{{NAME}}", lang.user) .replace("{{SIZE}}", lang.size) .replace("{{LINK}}", lang.link); language = jQuery(language); jQuery("#languages").append(language); } }</script>

\$\endgroup\$
25
  • 17
    \$\begingroup\$ Whoever closevoted this, the other one is a popularity contest... \$\endgroup\$
    – TheDoctor
    Nov 18, 2015 at 15:19
  • 26
    \$\begingroup\$ This is a subtask of the Create an HQ9+ Interpreter golf. \$\endgroup\$
    – Geobits
    Nov 18, 2015 at 15:25
  • 15
    \$\begingroup\$ Note that quartata had already sandboxed a 99BB catalog several weeks ago and was planning to post it. \$\endgroup\$
    – Alex A.
    Nov 18, 2015 at 17:07
  • 16
    \$\begingroup\$ @GamrCorps Please do not unnecessarily change the rules after the challenge has started. None of the characters in the output should be optional. \$\endgroup\$
    – feersum
    Nov 18, 2015 at 18:42
  • 8
    \$\begingroup\$ To try and head off any newcomers who can't see deleted answers, if you're thinking of using a language with a 99 Bottles song built-in, double check the output matches the desired output of the question. There have already been half a dozen deleted HQ9+ answers, as well as other languages with incorrect outputs. \$\endgroup\$
    – Jo King
    Oct 22, 2018 at 4:05

175 Answers 175

1
\$\begingroup\$

Python 3, 243 bytes

b=lambda x,y: f'{x} bottle{("","s")[x>1]} of beer{(""," on the wall")[y]}'
p=print
for i in range(99,0,-1):
 p(f'{b(i,1)}, {b(i,0)}.')
 p((f'Go to the store and buy some more, {b(99,1)}.',f'Take one down and pass it around, {b(i-1,1)}.')[i>1])

Try it online!

\$\endgroup\$
3
  • \$\begingroup\$ You should include the code in your answer. TIO has a nifty auto-formatter for PPCG if you click the link icon at the top of the page \$\endgroup\$
    – Jo King
    Oct 23, 2018 at 23:14
  • 1
    \$\begingroup\$ 225 bytes \$\endgroup\$
    – Jo King
    Oct 23, 2018 at 23:40
  • \$\begingroup\$ Hah - yeah, this is tougher than it looks :) \$\endgroup\$
    – dana
    Oct 23, 2018 at 23:51
1
\$\begingroup\$

Stax, 82 bytes

äÇEà╧<à■≤ÄâDφl%H/╢S▌╙♂Å5ε─U¥ΦioJ╦D╖↨{)X╗√╛ⁿ?fJ∞L^╧êτ╝♪p1j■GTq⌂ÿ█♀├=δMséaΦ≡&NQ▌←²╠↕

Run and debug it

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

Powershell, 208 207 bytes

-1 byte, thanks @Veskah

inspired by Veskah's answer.

$l,$e,$o=" bottles of beer"," bottle of beer"," on the wall"
99..1|%{"$_$l$o, $_$l."
if($_---le2){$l,$e=$e,$l}"$(("Take one down and pass it around, $_","Go to the store and buy some more, 99")[!$_])$l$o.
"}

Psssh, is a swap a clever stuff? :)

\$\endgroup\$
3
  • \$\begingroup\$ It's about 19 bytes more clever than mine. \$\endgroup\$
    – Veskah
    Oct 24, 2018 at 20:28
  • 1
    \$\begingroup\$ Make that 12, you reminded me that strings can be multi-lined for free \$\endgroup\$
    – Veskah
    Oct 24, 2018 at 20:38
  • 1
    \$\begingroup\$ Looks like you can save a byte by doing if(){}"stuff". Seems if blocks don't need a newline or semi-colon after to work. \$\endgroup\$
    – Veskah
    Oct 25, 2018 at 21:10
1
\$\begingroup\$

Python 3, 288 266 253 (credit: Jo King) 251 242 bytes

b=lambda x:"%s bottle%s of beer"%(x,"s"*(x>1));o="on the wall"
for i in range(99,0,-1):print("\n%s %s, %s.\n%s"%(b(i),o,b(i),["Take one down and pass it around, %s %s."%(b(i-1),o),"Go to the store and buy some more, %s %s."%(b(99),o)][i==1]))
\$\endgroup\$
5
  • \$\begingroup\$ There is already a shorter Python 3 answer :P \$\endgroup\$
    – ASCII-only
    Feb 28, 2019 at 3:38
  • \$\begingroup\$ it doesn't work in python 3, and fails with a TypeError: 'NoneType' object is not subscriptable \$\endgroup\$ Feb 28, 2019 at 3:44
  • 1
    \$\begingroup\$ 223 \$\endgroup\$
    – ASCII-only
    Feb 28, 2019 at 4:25
  • \$\begingroup\$ 210 bytes \$\endgroup\$
    – Jo King
    Feb 28, 2019 at 4:48
  • 1
    \$\begingroup\$ except " instead of ' in ' on the wall' for consistency :P \$\endgroup\$
    – ASCII-only
    Feb 28, 2019 at 5:01
1
\$\begingroup\$

Whitespace, 1977 1342 1013 877 bytes

(very much whitespace)

Try it online!

Finally, under 1000 bytes.

-635 bytes by manually correcting the automatic whitelips labels

-329 bytes by offsetting the letters and using 0 for spaces

-136 bytes by pushing whole sentences in one big integer and using divmod to calculate the letters.

visible code:

sssttsssttl push 99

lssssl      label main:
lsttl       call wall
lststl      call comma_space
lsttsl      call beer
lstsl       call stop_nl
ssstl       push 1
tsst        sub
lstttl      call take
lststl      call comma_space
lsttl       call wall
lstsl       call stop_nl
ssststsl    push newline (10)
tlss        printc
lslssl      jump main

lssttl      label take:
sls         dup
ltssssl     jz store
ssstststssl "T"
tlss        printc
ssstsstsstttssttsssttsststtstststststssststssttsttttttstsssstttstsssttstttssstssssttsssssssssttsstttststttsssttsssttststtstsssttttstssssttsstssssttl
            "ake one down and pass it around"
lsll        jmp print

lsssssl     label store:
sll         drop
sssttsssttl push 99
ssstssstttl "G"
tlss        printc
ssstsstststttttsttssssststtstttttssttsstsststttssssttsssttsssssttstssttstststtsttsttstssssstsssttstsstttttttssssttsttsttstststsststtssttsstssttttsttstttl
            "o to the store and buy some more"
lstl        call print
lststl      call comma_space
lsttl       call wall
lstsl       call stop_nl
lll         end

lsssl       label stop_nl:
ssststttsl  "."
tlss        printc
ssststsl    <lf>
tlss        printc
ltl         return

lssstl      label comma_space:
ssststtssl  ","
tlss        printc
sssl        0
lsll        jmp print

lsstl       label wall:
lsttsl      call beer
ssststssststssssttttttsstttsstststttstssttsstsssssstttttttsl
            " on the wall"
lsll        jmp print

lsstsl      label beer:
lststtl     call bottles
ssstssstssssstsssttsttsttstttssstttsttttsl
            " of beer"
lsll        jmp print

lsssttl     label bottles:
sls         dup bottlecount
tlst        printi
sssttsststsstssssstttsttssttttssssl
            " bottle"
lstl        call print
sls         dup bottlecount
ssstl       push 1
tsst        sub
ltststl     jz return
ssstssttl   's'

lssl        label print:
sssttstsl   26
stsstl      copy 1
stsstl      copy 1
tstt        mod
sls         dup
ltstssl     jz space
ssstssssssl push 64
tsss        add
lsstssl     label space:
ssstsssssl  push 32
tsss        add
tlss        printc
tsts        div
sls         dup
ltsstsl     jz end_print
lsll        jump print
lssstsl     label end_print:
sll         drop
lsststl     label return:
ltl         return
\$\endgroup\$
1
\$\begingroup\$

Keg, 222 bytes

c&(c|&:.& bottle&:&1>[s] of beer on the wall\, ^(,)&:.& bottle&:&1>[s] of beer\.
^(,)&:&1=[Go to the store and buy some more|Take one down\, pass it around]\, ^(,)&:&1=[\9\9|&;:.&] bottle&:&1>[s] of beer on the wall\.

^(,

Try it online!

Explanation

  • First it stores the current counter into the accumulator
  • Then it prints all verses accordingly until the last verse
  • Finally this hard-codes the final verse

Let's see if I can get this any shorter.

\$\endgroup\$
2
  • \$\begingroup\$ Consider adding a short explanation to your answer. Code-only answers may be automatically flagged as low-quality. See other answers for examples. \$\endgroup\$
    – mbomb007
    Jun 6, 2019 at 19:16
  • \$\begingroup\$ This can easily be shortened using the register commands. But I'm on mobile right now, so I'll probably give a shortened version later. About 12 bytes can be golfed. \$\endgroup\$
    – lyxal
    Jan 3, 2020 at 22:01
1
\$\begingroup\$

Unix TMG, 231 byte

o:parse(v)[n<143?]\o;v:b l b={3< on the wall, >3<.>*2 1< on the wall.>**};b:decimal(n)([n>1?]={<s>}|={})={2< bottle>1< of beer>};l:[--n?]={<Take one down and pass it around, >}|[n=143]={<Go to the store and buy some more, >};n:143;

Expanded:

o: parse(v) [n<143?]\o;
v: b l b = {3 < on the wall, > 3 <.> * 2 1 < on the wall.> * * };
b: decimal(n) ([n>1?]={<s>}|={}) = {2 < bottle> 1 < of beer>};
l: [--n?] = {<Take one down and pass it around, >}
 | [n=143] = {<Go to the store and buy some more, >};
n: 143;

The fact that this 1972 language uses octal instead of decimal doesn't help in this challenge 😂

\$\endgroup\$
3
  • 1
    \$\begingroup\$ If it can't be written without the newlines then the newlines should be counted. \$\endgroup\$
    – S.S. Anne
    Feb 2, 2020 at 22:21
  • \$\begingroup\$ This seems to be 235 bytes, not 231 \$\endgroup\$
    – Jo King
    Feb 3, 2020 at 1:30
  • \$\begingroup\$ @JoKing, deleted the newlines. Thanks, it works without them. \$\endgroup\$ Feb 3, 2020 at 3:57
1
\$\begingroup\$

Python, 357 339 310 bytes

After shaving off the fat to meet the expected output it didn't save as many bytes as I had expected:

def B(n):A='bottle';return A if n==1 else A+'s'
A=99
while A>=1:F='of';D=F+' beer';E='more';C=D+' on the wall';G='no '+E;H=f"Take one down and pass it around, {A-1 if A>1 else G} {B(A-1)} {C}\n"if A!=1 else f"Go to the store and buy some {E}, 99 {B(2)} {C}.";print(f"{A} {B(A)} {C}, {A} {B(A)} {D}.\n"+H);A=A-1

The following code is for code-golf output:

This is the lowest I can hit so far

def B(n):A='bottle';return A if n==1 else A+'s'
A=99
while A>=0:G='of';D=G+' beer';E='more';C=D+' on the wall';F='no '+E;print(f"{A} {B(A)} {C}, {A} {B(A)} {D}.\nTake one down and pass it around, {A-1 if A>1 else F} {B(A-1)} {C}.\n"if A!=0 else f"No {E} {B(2)} {C}, {F} {B(2)} {D}.\nGo to the store and buy some {E}, 99 {B(2)} {C}.");A=A-1

Here's the sort of bigger version:

def get_bottles(num_bottle):
    p = "bottle"

    return p if num_bottle == 1 else p + "s"


bottleNo = 99

while bottleNo >= 0:
    of = "of"
    beer = of + " beer"
    more = "more"
    botw = beer + " on the wall"
    noMore = "no " + more

    print(f"{bottleNo} {get_bottles(bottleNo)} {botw}, {bottleNo} {get_bottles(bottleNo)} {beer}.\nTake one down and "
          f"pass it around, {bottleNo - 1 if bottleNo > 1 else noMore} {get_bottles(bottleNo - 1)} {botw}.\n" if
          bottleNo != 0
          else f"No {more} {get_bottles(2)} {botw}, {noMore} {get_bottles(2)} {beer}.\nGo to the store "
               f"and buy some {more}, 99 {get_bottles(2)} {botw}.")

    bottleNo = bottleNo - 1
\$\endgroup\$
2
  • 1
    \$\begingroup\$ You went through the trouble of making all your variables into one character, but forgot num_bottle. If you do that there as well, you could shave off a few bytes \$\endgroup\$
    – GamrCorps
    May 31, 2020 at 14:21
  • \$\begingroup\$ Good catch, that saved me 18 chars. Thanks! \$\endgroup\$
    – SZT
    May 31, 2020 at 14:26
1
\$\begingroup\$

Plain TeX (237 characters)

\input tikz\let~\def~\1#1{ bottle#1 of beer}~\7{~ on the wall}~\9#1#2{\def~{ #1\1#2}Take one down and pass it around,\7.\bigskip\par\7,~.\par}~~{ 99\1s}\7,~.\par\foreach\2in{98,...,2}{\9\2s}{\91 }Go to the store and buy some more,\7.\bye

Try the Code on Overleaf


Any Comments Appreciated

Please help me golf this code to an unhealthy extent.

\$\endgroup\$
1
  • \$\begingroup\$ @AdHocGarfHunter Would you want to accept a challenge? Do you want to find out whether you can minify this further down? \$\endgroup\$
    – Joe85AC
    Jun 5, 2020 at 14:53
1
\$\begingroup\$

AWK, 198 bytes

a=100{d=" on the wall";e=" of beer";for(f=" bottle";a--;$0=g)g=g (a?(c=a f (a-1?"s":b)e)d", "c".\nTake one down and pass it around, "c d".\n\n":"Go to the store and buy some more, 99"f"s"e d".\n")}1

Try it online!

I didn't see AWK on the last yet, so here's one example.

This code will print the whole "99 bottles..." dialogue for every line of input fed into the code. I'm not sure how best to break this apart, but maybe this helps?

a=100{ ... }

Something truthy to trigger the code block, also sets the loop variable a to 100.

d=" on the wall";e=" of beer";

Store some re-used strings in variables.

for(f=" bottle";a--;$0=g)

The loop control, first set f in the loop init to save a ;. Then stop/go based on a after decrementing. At the end of the each iteration, set $0 to the accumulated output in g.

g=g ( ... )

Append the text for the next number of bottes to the accumulating output. Sadly the space is needed to avoid AWK parsing g( as a function call.

a?

Ternary for "is there more bottles?"

(c=a f (a-1?"s":b)e)

When there is more than one bottle... Generate "NN bottle of beer" or "NN bottles of beer" to be appended to g and also store that string in c. Using b in the ternary instead of "" saves a character and works since using an uninitialized variables as a string in AWK are treated like empty strings.

d", "c".\nTake one down and pass it around, "c d".\n\n"

Also append the rest of the text we need, making use of c set previously

:"Go to the store and buy some more, 99"f e d".\n"

When at zero bottles, append the last bit of text.

1

A truthy value with no code block causes the default action, which is to print the value of $0

<style>body { text-align: left !important} #answer-list { padding: 10px; width: 290px; float: left; } #language-list { padding: 10px; width: 290px; float: left; } table thead { font-weight: bold; } table td { padding: 5px; }</style><script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="language-list"> <h2>Shortest Solution by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr> </thead> <tbody id="languages"> </tbody> </table> </div> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr> </thead> <tbody id="answers"> </tbody> </table> </div> <table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr> </tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr> </tbody> </table><script>var QUESTION_ID = 64198; var ANSWER_FILTER = "!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe"; var COMMENT_FILTER = "!)Q2B_A2kjfAiU78X(md6BoYk"; var OVERRIDE_USER = 36670; var answers = [], answers_hash, answer_ids, answer_page = 1, more_answers = true, comment_page; function answersUrl(index) { return "//api.stackexchange.com/2.2/questions/" + QUESTION_ID + "/answers?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + ANSWER_FILTER; } function commentUrl(index, answers) { return "//api.stackexchange.com/2.2/answers/" + answers.join(';') + "/comments?page=" + index + "&pagesize=100&order=desc&sort=creation&site=codegolf&filter=" + COMMENT_FILTER; } function getAnswers() { jQuery.ajax({ url: answersUrl(answer_page++), method: "get", dataType: "jsonp", crossDomain: true, success: function (data) { answers.push.apply(answers, data.items); answers_hash = []; answer_ids = []; data.items.forEach(function(a) { a.comments = []; var id = +a.share_link.match(/\d+/); answer_ids.push(id); answers_hash[id] = a; }); if (!data.has_more) more_answers = false; comment_page = 1; getComments(); } }); } function getComments() { jQuery.ajax({ url: commentUrl(comment_page++, answer_ids), method: "get", dataType: "jsonp", crossDomain: true, success: function (data) { data.items.forEach(function(c) { if (c.owner.user_id === OVERRIDE_USER) answers_hash[c.post_id].comments.push(c); }); if (data.has_more) getComments(); else if (more_answers) getAnswers(); else process(); } }); } getAnswers(); var SCORE_REG = /<h\d>\s*([^\n,<]*(?:<(?:[^\n>]*>[^\n<]*<\/[^\n>]*>)[^\n,<]*)*),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/; var OVERRIDE_REG = /^Override\s*header:\s*/i; function getAuthorName(a) { return a.owner.display_name; } function process() { var valid = []; answers.forEach(function(a) { var body = a.body; a.comments.forEach(function(c) { if(OVERRIDE_REG.test(c.body)) body = '<h1>' + c.body.replace(OVERRIDE_REG, '') + '</h1>'; }); var match = body.match(SCORE_REG); if (match) valid.push({ user: getAuthorName(a), size: +match[2], language: match[1], link: a.share_link, }); else console.log(body); }); valid.sort(function (a, b) { var aB = a.size, bB = b.size; return aB - bB }); var languages = {}; var place = 1; var lastSize = null; var lastPlace = 1; valid.forEach(function (a) { if (a.size != lastSize) lastPlace = place; lastSize = a.size; ++place; var answer = jQuery("#answer-template").html(); answer = answer.replace("{{PLACE}}", lastPlace + ".") .replace("{{NAME}}", a.user) .replace("{{LANGUAGE}}", a.language) .replace("{{SIZE}}", a.size) .replace("{{LINK}}", a.link); answer = jQuery(answer); jQuery("#answers").append(answer); var lang = a.language; lang = jQuery('<a>'+lang+'</a>').text(); languages[lang] = languages[lang] || {lang: a.language, lang_raw: lang.toLowerCase(42), user: a.user, size: a.size, link: a.link}; }); var langs = []; for (var lang in languages) if (languages.hasOwnProperty(lang)) langs.push(languages[lang]); langs.sort(function (a, b) { if (a.lang_raw > b.lang_raw) return 1; if (a.lang_raw < b.lang_raw) return -1; return 0; }); for (var i = 0; i < langs.length; ++i) { var language = jQuery("#language-template").html(); var lang = langs[i]; language = language.replace("{{LANGUAGE}}", lang.lang) .replace("{{NAME}}", lang.user) .replace("{{SIZE}}", lang.size) .replace("{{LINK}}", lang.link); language = jQuery(language); jQuery("#languages").append(language); } }</script>

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

Pxem, Filename: 131 bytes + Content: 61 bytes = 192 bytes.

I escaped spaces too (\040).

  • Filename (escaped): c.z.e,.o\040.oq.e.s..o\n.o\001.-.c\001.y.sGo\040to\040the\040store\040and\040buy\040some\040more,\040.pc.\n.e.d.a.tTake\040one\040down\040and\040pass\040it\040around,\040.p.m.e..o\n.o\n.o.a
  • Content (escaped): .cd.y.t\040on\040the\040wall.mXXq.a.s.c.n.t\040of\040beer.m\001.zXXs.a\040bottle.p

Actual source

Filename

c.z.e,.o .oq.e.s..o
.o.-.c.y.sGo to the store and buy some more, .pc.
.e.d.a.tTake one down and pass it around, .p.m.e..o
.o
.o.a

Content

.cd.y.t on the wall.mXXq.a.s.c.n.t of beer.m.zXXs.a bottle.p

With comments

Inspired by Dorian's answer in Whitespace at first, then golfed well. Though both Whitespace and Pxem are stack-oriented programming language, their features are different, as in these:

  • Whitespace has label and (conditional) go-to commands; Pxem has while-done loop.
  • Both of them have subroutine, but Pxem can have only one subroutine. Also that of Pxem works as a function rather than a subroutine.
  • Whitespace has commands that accesses to non-top items of its stack; Pxem can reverse items on stack.
  • And more, I think.

Overall of the program

  • Iteration from 99 to 1 on filename.
  • Content stands for a subroutine to output strings that are frequently recycled.   - It depends on top of stack. If the item is more than 99, it outputs " on the wall" in addition to "[0-9]+ bottles? of beer".

Filename

XX.z
# push 99;
# while size<2||[ $(pop) -ne $(pop) ]; do
.ac.zXX.z
  # for i in $(reverse $(call $stack)); do
  #   push $i; done
  ## NOTE let it be "call" next time
  .a.eXX.z
  # push 44; putc $(pop); push 32; putc $(pop)
  ## NOTE DOTo is actually:
  ## if !empty; then : do above; fi
  .a,.o .oXX.z
  # push 113; call; pop
  ## NOTE 113>=100
  .aq.e.sXX
  # push 46; putchar $(pop);
  # push 10; putchar $(pop)
  .a..o\n.oXX.z
  # push 1;
  # push $(( (x=$(pop)-$(pop))<0?-x:x ))
  ## NOTE arithmetic operators actually:
  ## check if size>=2 before;
  ## NOP if size<2
  .a\001.-XX.z
  # dup; push 1;
  # while size<2||[ $(pop) -gt $(pop) ]; do
  .a.c\001.yXX.z
    # pop;
    ## NOTE literal is pushed from backwards
    # for i in 32 44 --- 71; do push $i; done
    .a.sGo to the store and buy some more, XX.z
    # while !empty; do putchar $(pop); done
    .a.pXX.z
    # push 10; push 46; push 99;
    # call
    .ac.\n.eXX.z
    # exit
    .a.dXX.z
  # done
  .a.aXX.z
  # if !empty; do heap=$(pop); fi
  # for i in 32 44 --- 84; do push $i; done
  .a.tTake one down and pass it around, XX.z
  # while !empty; do putc $(pop); done
  .a.pXX.z
  # case ${heap:+y} in (y)
  #   push $heap;; esac;
  # call; push 46; putc $(pop);
  # push 10; putc $(pop)
  # push 10; putc $(pop)
  .a.m.e..o\n.o\n.oXX.z
# done
.a.a

Content

XX.z
## NOTE subroutine stack is initialized
## to be same as that of its caller
## but its heap is initialized to be empty,
## which is not shared to its caller
## So, implicitly: stack="$*"
## NOTE when returning to subroutine,
## heap is discarded but items on its
## stack is pushed to its caller, from
## bottom to top
# dup; push 100;
# while size<2||[ $(pop) -gt $(pop) ]; do
.a.cd.yXX.z
  # heap=$(pop)
  .a.tXX.z
  # for i in 108 108 --- 32; do push $i; done;
  # push $heap;
  # push 113; push 88; push 88
  .a on the wall.mXXqXX.z
# done
.a.aXX.z
# pop; dup; printf %d $(pop)
.a.s.c.nXX.z
# heap=$(pop)
.a.tXX.z
# for i in 114 101 --- 32; do push $i; done;
# push $heap; push 1;
# while size<2||[ $(pop) -ne $(pop) ]; do
.a of beer.m\001.zXX.z
  # push 115; push 88; push 88
  .aXXXXs.z
# done
.a.aXX.z
# for i in 101 --- 32; do push $i; done;
# while !empty; do putc $(pop); done
.a bottle.pXX.z
# (* implicitly *) return $stack (* to its caller *)
.a

Try it online!

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

M4, 224 bytes

define(b,`$1 bottle`'ifelse($1,1,,s) of beer')define(w,`b($1) on the wall')define(f,`w($1), b($1).
ifelse($1,1,`Go to the store and buy some more, w(99).',`Take one down and pass it around, w(decr($1)).

f(decr($1))')')f(99)

If I learn GNU extensions, perhaps I would shorten this code.

Try it online!

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

Vyxal Hmj, 128 bytes

ƛ‛ḋ¶øP«lT…|«"Ṅ£¥:«∧*JEp
'«\,+"Ṅ$"Ṅ‛.
+n‹[`¬Ȧ λḭ ∧ċ λ¬ ∆¶ it ⟑Ṡ`|«¢ṁĖ↲Ǐ¶QṪD£y∩i› kḭS√⁰«¡]‛, ++n‹[n‹|99]‛ḋ¶øP«∪ṠEȧ₆›λ*ĖC9«"Ṅ\.++;Ṙ

I was going to put this in a comment on @Wasif's answer, but the link was too long, so here's my Vyxal answer, with a -17 byte golf.

Try it Online!

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

Knight, 170 169 bytes

;=r 99WrO++++++""r+C=sB=bS" bottles of beer"7?1r""=e" on the wall"+", "r+bI=r-rT+".
Take one down and pass it around, "r".
Go to the store and buy some more, 99"Cs+e".
"

Try it online!

  • 1 byte: inline CALL

Ungolfed but still barely understandable

# set our counter to 99 (r)
; = counter 99
# We call this inline in the golfed code
# getbottle(s):
# returns pluralized " bottle(s) of beer" depending
# on counter's value, also storing it in beer
; = getbottles BLOCK {
    : = beer 
      : SUBSTITUTE (
        : " bottles of beer"
        : 7
        # when counter == 1, remove the s
        : ? 1 counter 
        # replace with empty string
        : ""
      : )
: }
# Loop while counter != 0
: WHILE counter {
    # basically concatenate everything
    # still better than StringBuilder
    : OUTPUT 
     : ++++++
       : ""
       : counter
        : + (CALL getbottles)
          : (= wall " on the wall")
         : + ", "
           : counter
          : + beer
            : IF (= counter - counter 1) {
            :   # note: \n doesn't actually work, it is just for clarity
            :   + ".\nTake one down and pass it around, " counter
            : } {
            :   ".\nGo to the store and buy some more, 99"
            : }
           : + (CALL getbottles)
             : wall
            : ".\n"
: }
\$\endgroup\$
1
\$\begingroup\$

Japt, 116 bytes

Needs more golfing or, perhaps, a completely different approach. Borrows a couple of tricks from ETH' original version.

Includes 2 trailing newlines.

´Lõ Ô˱` Þ­¤{´LÎçs}  Þ8`".

{=D+`  e Ø!`}, {D}.
{LÎg`Go ‘ e ÐJe „d ¿y Ñ  Ú
Take e ܵ „d p†s Š ÂИ`·}, "iUìé~H

Test it

´Lõ Ô˱`...{´LÎçs}...`"...{=D+`...`}...{D}...{LÎg`...`·}..."iUìé~H
´L                                                                      :Prefix decrement L (initially 100)
  õ                                                                     :Range [1,L]
    Ô                                                                   :Reverse
     Ë                                                                  :Map each D
      ±                                                                 :  Append
       `                                                                :  Compressed string
        ...                                                             :    " bottle"
           {                                                            :    Interpolate
            ´LÎ                                                         :      Prefix decrement L and get its sign
               çs                                                       :      Repeat "s" that many times
                 }                                                      :    End interpolate
                  ...                                                   :    " of beer"
                     `                                                  :  End compressed string
                      "...                                              :  Literal string (the return value)
                          {                                             :    Interpolate
                           =                                            :      Assign to variable U
                            D+`...`                                     :      D & the compressed string " on the wall"
                                   }                                    :    End interpolate
                                    ...                                 :    Literals
                                       {D}                              :    Interpolate D
                                          ...                           :    Literals
                                             {                          :    Interpolate
                                              LÎg                       :      Get the sign of L and index (0-based) into
                                                 `...`                  :        Compressed string "Go to the store and buy some more\nTake one down and pass it around"
                                                      ·                 :        Split on newlines
                                                       }                :    End interpolate
                                                        ..."            :  End of string
                                                            iU          :  Prepend U
                                                              Ã         :End map
                                                               ¬        :Join
                                                                é       :Rotate right by
                                                                 ~H     :  The bitwise NOT of 32 (i.e., -33)
\$\endgroup\$
1
\$\begingroup\$

Oracle SQL, 254 247 285 Bytes

Golfed:

SELECT REGEXP_REPLACE(n||b||w||', '||n||b||p||CHR(10)||DECODE(n,1,'Go to the store and buy some more, 99',t||(n-1))||b||w||p,'(^1| 1).*s','1 bottle') FROM(SELECT 100-LEVEL n,' bottles of beer'b,' on the wall'w,'Take one down and pass it around, 't,'.'p FROM DUAL CONNECT BY LEVEL<=99);

Ungolfed:

SELECT regexp_replace(n || b || w || ', ' || n || b || p || chr(10) || decode(n, 1, 'Go to the store and buy some more, 99', t || (n - 1)) || b || w || p,
                      '(^1| 1).*s',
                      '1 bottle')
FROM (SELECT 100 - LEVEL n,
            ' bottles of beer' b,
            ' on the wall' w,
            'Take one down and pass it around, ' t,
            '.' p
      FROM dual
      CONNECT BY LEVEL <= 99);

Basically, I get the row number via the hierarchical query and build a small library in the inner query. Then I put it all together in the outer query.

Edit 1: -7 Bytes: Used the "distributive" property on common string within the DECODE function. Edit 2: +38 bytes: Oops, missed the s on 1 bottle. Fixed with regular expression string replacement.

Try it out here: DBFiddle

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

nroff, 259 bytes

.de B
\\$1
bottle
.if \\$1>1 \h'-1's
of beer
..
.de F
.B \\nX
on the wall,
.B \\nX
\h'-1'.
.sp 0
.nr X \\nX-1
.ie \\nX \\{Take one down and pass it around,
.B \\nX
\\}
.el \\{Go to the store and buy some more,
.B 99
\\}
on the wall.

..
.nr X 99
.while \nX .F

Attempt This Online!

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

Rust, 215 bytes

fn main(){for a in!297..{print!("{} bottle{}{}",99-a%!1%2383/3,&"s of beer on the wall"[a%!1/!4..87>>a%3&29],[", ",".
Take one down and pass it around, ",".

",".
Go to the store and buy some more, "][a%(6-3%!a)])}}

This is the smallest I could get with Rust... As for the standard version, it's 259 bytes (No solution, just proof). Both versions error with addition overflow... There's quite a bit of messy logic to filter the integer mess, still being smaller than a 0-300 loop.

It seems pretty competitive, even beating a Javascript answer?

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

Rattle, 201 200 bytes

99& bottles of beer& on the wall&Take one down and pass it around, &, &.&1 bottle of beer&Go to the store and buy some more, 99|IPg[bb1b2b4bb1b5B-sb3[1f0]bb1b2b5Bbb^B]0;b6b2b5Bbb^Bb6b2b4b6b5Bb7b1b2b5q

Try it Online!

This basically takes the lyrics chopped up then pieces them together. It's likely that this can be shortened a bit

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

Uiua, 192 bytes

o←:" of beer on the wall"
a←⊂:⊂" bottle"⟨""|@s⟩⊃(>1|°⋕)
b←⊂⟨"Take one down and pass it around, "a-1|"Go to the store and buy some more, "a99⟩=1.
l←&p$"__, _ of beer.\n__.\n"o⊃(.a|b)o
⍚l⇌+1⇡99
\$\endgroup\$
1
  • \$\begingroup\$ 158 \$\endgroup\$
    – noodle man
    Mar 17 at 12:41
1
\$\begingroup\$

Uiua, 157 bytes

&p⊂⊂:↙30.↘67∧(⊂$"Take one down and pass it around, __.\n\n__, _.\n",,," on the wall"$"_ bottle_ of beer":▽:@s≠1.)+1⇡99"Go to the store and buy some more, "@.

Try it

Explanation:

+1⇡99"Go to the store and buy some more, "@.

Set up the stack with (bottom to top) a . character, the last part of the text, and the list of numbers 1 to 99 inclusive.

∧( ... )

Fold the range using that string as the initial value by the following function:

  • $"_ bottle_ of beer":▽:@s≠1.
    

    Repeat s by whether the number is 1 or not, then put that in a format string with the number to get the "[N] bottle[s] of beer" part.

  • ,,," on the wall"
    

    Push "on the wall". The commas reorient the stack so that it will have (from bottom to top) "n bottles of beer" " on the wall" "n bottles of beer" " on the wall" "n bottles of beer".

  • $"Take one down and pass it around, __.\n\n__, _.\n"
    

    Fill these into the format string. Every _ is where a stack item goes, starting from the left. Note that this starts with the "Take one down and pass it around" bit, so we'll have to remove that from the first line later.

  • Prepend this string to the fold value.

↘67

Drop 67 characters from the start of the string, removing the first "Take one down..." line.

↙30.

Push a copy of the whole string and take 30 characters from the start, yielding "99 bottles of beer on the wall"

&p⊂⊂:

Join this and the big string together, and append the . from earlier. Finally print this whole thing to STDOUT.

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

Pip, 147 bytes

L DhP[V Y{[h|99" bottle"h=1?x's" of beer"]}w:" on the wall"k;Vy'.nDh?"Take one down and pass it around""Go to the store and buy some more"k;Vyw'.n]

Attempt This Online!

Explanation

We use h as the loop counter. It is a variable predefined to 100, so the first thing we do is Decrement it to 99 and then Loop that many times.

Each time through the loop, we Print the contents of a big list (automatically concatenated together, with a trailing newline):

  • V Y{[h|99" bottle"h=1?x's" of beer"]}: the appropriate variation on "N bottle(s) of beer" based on the current value of h (see below)
  • w: " on the wall"
  • k: ", " (a predefined variable)
  • Vy: evaluate the "N bottle(s) of beer" code again
  • '.: "."
  • n: newline (a predefined variable)
  • Dh?"Take one down and pass it around""Go to the store and buy some more": decrement h; "Take one down and pass it around" if it's still truthy (nonzero), otherwise "Go to the store and buy some more"
  • k, Vy, w, '., n: as above
{[h|99" bottle"h=1?x's" of beer"]}
 [                              ]  ; Return this list (which gets concatenated when output):
  h|99                             ; h, or else 99 if h is 0
      " bottle"                    ; That string
               h=1?x's             ; If h is 1, empty string; otherwise, "s"
                      " of beer"   ; That string
\$\endgroup\$
0
\$\begingroup\$

R, 297 bytes

b='bottles of beer'
w='on the wall'
p=paste
g=gsub
s='$'
x='.'
d=p(g(s,',',b),w)
j=99
i=j:1
l=g(s,x,p(i,d,i,b))
l[j]=g('s','',l[j])
m=p('Take one down and pass it around,',i,d)
o=p(l,m,sep="\n")
o[j]=g('\n.*',g(s,x,p('\nGo to the store and buy some more,',j,d)),o[j])
write(o,"")

Ideone link to test

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

Dart, 218 bytes

int i=99;b(i)=>'$i bottle${i<2?'':'s'} of beer';w(i)=>b(i)+' on the wall';main(){while(i>0){print(w(i)+', ${b(i)}.\n${--i>0?'Take one down and pass it around, '+w(i):'Go to the store and buy some more, '+w(99)}.\n');}}
\$\endgroup\$
0
\$\begingroup\$

Scala, 313 298 271 268 bytes

I think I may have reached the limit on this without resorting to string compression algorithms.

object m extends App{val(f,c)=((x:Int)=>s"$x bottle${if(x>1)"s" else ""} of beer"," on the wall");99 to 1 by -1 map(a=>println(s"${f(a)}$c, ${f(a)}.\n${if(a>1)"Take one down and pass it around" else "Go to the store and buy some more"}, ${f(if(a>1)a-1 else 99)}$c."))}

(Semi-)readable version:

object m extends App{
  val(f,c)=((x:Int)=>s"$x bottle${if(x>1)"s" else ""} of beer"," on the wall");
  99 to 1 by -1 map(a=>println(s"${f(a)}$c, ${f(a)}.\n${if(a>1)"Take one down and pass it around" else "Go to the store and buy some more"}, ${f(if(a>1)a-1 else 99)}$c."))
}
\$\endgroup\$
0
\$\begingroup\$

CJam, 257 229 bytes

229 Bytes

99:A;"bottles of beer":B;{A" "B" on the wall, "A" "B"."NA(:A;"Take one down and pass it around, "A" "B" on the wall."NN}98*"1 bottle of beer on the wall, 1 bottle of beer."N"Go to the store and buy some more, 99 "B" on the wall."

257 Bytes

99:A;{A" bottles of beer on the wall, "A" bottles of beer."NA(:A;"Take one down and pass it around, "A" bottles of beer on the wall."NN}98*"1 bottle of beer on the wall, 1 bottle of beer."N"Go to the store and buy some more, 99 bottles of beer on the wall."
\$\endgroup\$
0
\$\begingroup\$

Loader, 515 bytes

Run from a module named m.

~I:set I,B =99
set P =B
decr P
~P:printf "1 bottle of beer on the wall, 1 bottle of beer."
~P:printf "You go to the store and you buy some more, 99 bottles of beer on the wall."
~P:exit
P:printf "%n bottles of beer on the wall, %n bottles of beer." B,B
~P:printf "1 bottle of beer on the wall!, 1 bottle of beer."
decr P
decr B
P:printf "You take one down and you pass it around, %n bottles of beer on the wall." B
~P:printf "You take one down and you pass it around, 1 bottle of beer on the wall."
printf ""
load m
\$\endgroup\$
0
\$\begingroup\$

C++, 335 bytes

Golfed:

#include <stdio.h>
int main(){for (int c = 99; c >= 2; c--){printf("%i bottles of beer on the wall, %i bottles of beer.\nTake one down and pass it around, %i bottles of beer on the wall.", c, c, c-1);}printf("1 bottle of beer on the wall, 1 bottle of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.");return 0;}

Ungolfed:

#include <stdio.h>

int main()
{
    for (int c = 99; c >= 2; c--) {
        printf("%i bottles of beer on the wall, %i bottles of beer.\nTake one down and pass it around, %i bottles of beer on the wall.", c, c, c-1);
    }
    printf("1 bottle of beer on the wall, 1 bottle of beer.\nGo to the store and buy some more, 99 bottles of beer on the wall.");

    return 0;

} 
\$\endgroup\$
4
  • \$\begingroup\$ This does not have valid output for the last line. \$\endgroup\$ Nov 18, 2015 at 18:22
  • \$\begingroup\$ Welcome to PPCG! I've done a bit of formatting, hope you don't mind. Unfortunately though, as VoteToClose has kindly pointed out, the last line is incorrect (and also two other lines before that, which require 1 bottle of beer rather than bottles). \$\endgroup\$
    – Sp3000
    Nov 18, 2015 at 18:24
  • \$\begingroup\$ Oh, thank you. I will fix as necessary. \$\endgroup\$ Nov 18, 2015 at 18:25
  • \$\begingroup\$ This is still not valid. The last line is "Go to the store and buy some more, 99 bottles of beer on the wall.". \$\endgroup\$ Nov 18, 2015 at 18:43
0
\$\begingroup\$

Python 3, 210 bytes

b=" of beer"
s="bottles"+b
t=s[:6]+b
q="on the wall."
p=print
x=99
while x>1:p(x,s,q,x,s+".\nTake one down and pass it around,",x-1,(s,t)[x>1],q);x-=1
p(1,t,q,1,t+".\nGo to the store and buy some more,",99,s,q)

Try it here: https://ideone.com/iX4byJ

\$\endgroup\$
1
  • 1
    \$\begingroup\$ This doesn't separate the verse by newlines, and it has a period instead of a comma after the wall on the first line \$\endgroup\$
    – Jo King
    Feb 28, 2019 at 4:34
0
\$\begingroup\$

Python 3, 234 bytes

i=99
s='s'
b='%s bottle%s of beer'
o=' on the wall'
y=b%(i,s)
while i:print(y+o+', '+y+'.');i-=1;s=('',s)[i>1];y=b%(i,s);print(('Take one down, pass it around, '+y+o+'.\n','Go to the store and buy some more, 99'+b%(s,'s')+o+'.')[i<1])

with newlines for some more readability

i=99;
s='s';
b='%s bottle%s of beer';
o=' on the wall';
y=b%(i,s)
while i:
  print(y+o+', '+y+'.');
  i-=1;
  s=('',s)[i>1];
  y=b%(i,s);
  print(('Take one down, pass it around, '+y+o+'.\n','Go to the store and buy some more, 99'+b%(s,'s')+o+'.')[i<1])

makes use of a quirk of booleans to evaluate to zero or one; this allows them to be used as index in two value tuples or lists, and are shorter than the 'proper' equivalent. These pseudo ternaries are used to keep all the loop code on one line.

\$\endgroup\$

Your Answer

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

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