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

173 Answers 173

2
\$\begingroup\$

PHP, 267 bytes

<?php $i=99;$t="Take one down and pass it around, ";$b=" bottles of beer";$o=" on the wall";echo"99$b$o, 99$b.\n";while(--$i>1)echo"$t$i$b$o.\n\n$i$b$o, $i$b.\n";$B=str_replace('s','',$b);echo"$t$i$B$o.\n\n$i$B$o, $i$B.\nGo to the store and buy some more, 99$b$o.";

Readable version, with comments:

<?php

$i=99;
$t="Take one down and pass it around, ";
$b=" bottles of beer";
$o=" on the wall";

// first one
echo"99$b$o, 99$b.\n";

// decrement from 98 to 2
while(--$i>1) echo"$t$i$b$o.\n\n$i$b$o, $i$b.\n"; 

// ^^ "Take one down and pass it around, $i bottles of beer on the wall.\n\n ^^
// $i bottles of beer on the wall, $i bottles of beer.\n"

// last one
$B=str_replace('s','',$b); // change "bottles" to "bottle"
echo"$t$i$B$o.\n\n$i$B$o, $i$B.\nGo to the store and buy some more, 99$b$o.";
\$\endgroup\$
2
\$\begingroup\$

PHP, 248 bytes (with pluralisation)

$b=99;$x=function($n){return"$n bottle".($n!=1?'s':'')." of beer";};$y=" on the wall";do{echo"{$x($b)}$y, {$x($b)}.\n";echo--$b?"Take one down and pass it around":"Go to the store and buy some more";$b||$b=99;echo", {$x($b)}$y.\n\n";}while($b<99);

readable:

<?php

$b=99;
$x=function($n){return"$n bottle".($n!=1?'s':'')." of beer";};
$y=" on the wall";
do{
    echo"{$x($b)}$y, {$x($b)}.\n";
    echo--$b?"Take one down and pass it around":"Go to the store and buy some more";
    $b||$b=99;
    echo", {$x($b)}$y.\n\n";
}while($b<99);

NOTE: previoius version was 197 bytes but didnt correctly show singular "bottle". see history if interested

\$\endgroup\$
5
  • \$\begingroup\$ See whether string interpolation instead of concatenation would help: echo $b.$c.", ".$b.$d.".\n";echo "$b$c, $b$d.\n";. \$\endgroup\$
    – manatwork
    Nov 19, 2015 at 11:58
  • \$\begingroup\$ @manatwork: cool didnt think of that! \$\endgroup\$ Nov 19, 2015 at 12:07
  • \$\begingroup\$ Just some quick changes, trying to keep the base idea: $b=99;$d=" bottles of beer";$c="$d on the wall";do{echo"$b$c, $b$d.\n",--$b?"Take one down and pass it around":"Go to the store and buy some more";$b||$b=99;echo", $b$c.\n\n";}while($b<99);. (You can find more tricks to apply in Tips for golfing in PHP.) \$\endgroup\$
    – manatwork
    Nov 19, 2015 at 12:30
  • \$\begingroup\$ Sorry dude but at the end this still says "bottles" i.e. 1 bottles of beer on the wall - should be "bottle" \$\endgroup\$ Nov 19, 2015 at 12:36
  • \$\begingroup\$ There is an extra newline at the end. \$\endgroup\$
    – frnhr
    Nov 21, 2015 at 18:26
2
\$\begingroup\$

Go, 287

package main
import."fmt"
func main(){p,s,n:=Printf,"%[1]d bottle%[2]s of beer",`.
`
o:=s+" on the wall"
a,b,c,i,k:=o+`, `+s+n,`Take one down and pass it around, `+o+n+`
`,`Go to the store and buy some more, `+o+n,99,"s"
for 1<i{p(a,i,k)
if i--;i<2{k=""}
p(b,i,k)}
p(a,i,k)
p(c,99,"s")}

There is probably room for further optimization

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

Ceylon 1.2, 359

alias S=>String;alias I=>Integer;shared void run(){value[b,o,w,t,d]=[" bottle"," of beer"," on the wall","Take one down and pass it around, ",".\n"];S l="1"+b+o;S h(I n)=>"``n``"+b+"s"+o;S f(I n)=>h(n)+w+", "+h(n);S c(I n)=>t+h(n)+w+d+"\n"+f(n);print(d.join{f(99),*(98..2).map(c)}+d+t+l+d+"\n"+l+w+", "+l+d+"Go to the store and buy some more, "+h(99)+w+".");}

Some golfing tricks: Shorter aliases for String and Integer, and the tuple-decomposition-declaration which saves us some S and = (that was only introduced in Ceylon 1.2). The rest is straightforward extraction of common parts to variables/functions.

Formatted:

// 99 bottles of beer
//
// Question:  http://codegolf.stackexchange.com/q/64198/2338
// My answer: http://codegolf.stackexchange.com/a/64522/2338
alias S => String;
alias I => Integer;
shared void run() {
    value [b, o, w, t, d] = [
        " bottle",
        " of beer",
        " on the wall",
        "Take one down and pass it around, ",
        ".\n"
    ];
    S l = "1" + b + o;
    S h(I n) => "``n``" + b + "s" + o;
    S f(I n) => h(n) + w + ", " + h(n);
    S c(I n) => t + h(n) + w + d + "\n" + f(n);
    print(d.join { f(99), *(98..2).map(c) }
                + d + t + l + d + "\n" + l + w + ", " + l + d +
                "Go to the store and buy some more, " + h(99) + w + ".");
}
\$\endgroup\$
2
\$\begingroup\$

Microsoft Small Basic, 313 bytes

g=" of beer"
a=" bottles"+g
b=" on the wall"
c="Take one down and pass it around, "
d="."
e="1 bottle"+g
f=", "
i=99
n=Text.GetCharacter(010)
While i>1
TextWindow.WriteLine(i+a+b+f+i+a+d+n+c+(i-1)+a+b+d+n)
i=i-1
EndWhile
TextWindow.WriteLine(e+b+f+e+d+n+"Go to the store and buy some more, 99"+a+b+d)
\$\endgroup\$
2
\$\begingroup\$

Grond, 226 chars

p=".";b=" bottle";B=b+'s';o=" of beer";w=" on the wall";i=100;w(--i>0){T=i<3?b:B;h=B+o+w;u=i<2?b+o:B+o;k=i+u;a(k+w+', '+k+p);a(i>1?"Take one down and pass it around, "+(i-1)+T+o+p:"Go to the store and buy some more, 99"+h+p);}

Grond is a new programming languege that I am currently developing. It aims to make Javascript shorter to write, while still being "Javascript". I'm pretty mad that I didn't beat any of the Javascript answers, but the languege is still beta and I am still a n00b. Oh and buy the way, I might not call it grond in the future, but it is a reference to the hammer of the underworld.

Actually, Grond can beat some of the JS answers @ 179 chars, as follows:

f(x=99,z=(a=' on the wall')=>`${x||99} bottle${1-x?'s':''} of     beer`+a;x;)a(z()+', '+z`.
`+(--x?'Take one down and pass it around, ':'Go to the store and buy   some more, ')+z()+'.')

But that is copying Adam Dally's answer, so I won't do it (unless you all want me to).

Anyway, here is the compiler:

String.prototype.reverse = function() {
  return this.split("").reverse().join("");
}
var source = "p='.';b=' bottle';B=b+'s';o=' of beer';w=' on the wall';i=100;w(--i>0){T=i<3?b:B;h=B+o+w;u=i<2?b+o:B+o;k=i+u;a(k+w+', '+k+p);a(i>1?'Take one down and pass it around, '+(i-1)+T+o+p:'Go to the store and buy some more, 99'+h+p);}";
var wc = source; // wc stands for working copy 
var lastWC;
while (true) {
  lastWC = wc;
  wc = wc.reverse();
  wc = wc.replace(/\(a(?!\w)/, "(trela"); //alert ,a
  wc = wc.replace(/\(f(?!\w)/, "(rof"); // for, f
  wc = wc.replace(/\(w(?!\w)/, "(elihw"); //while,w
  wc = wc.replace(/\(p(?!\w)/, "(tpmorp"); //prompt,p
  wc = wc.replace(/\(vr\./, "(esrever."); //reverse, rv
  wc = wc.replace(/\(tm\./, "(chtam."); // match, mt
  wc = wc.replace(/\(nj\./, "(nioj."); //join, .jn
  wc = wc.replace(/\(pr\./, "(ecalper."); //replace, .rp
  wc = wc.replace(/\(ls\./, "(.ecils"); //slice, sl
  wc = wc.replace(/\(lt\./, "(esaCrewoLot."); //toLowerCase, tl
  wc = wc.replace(/\(ut\./, "(esaCreppUot."); //toUpperCase, tu
  wc = wc.reverse();
  wc = wc.replace(/\.l(?!\w)/, ".length"); // .lenght, .l
  if (lastWC === wc)
    break;
}

// quine builtin commented out because it messes things up
wc = " String.prototype.reverse=function(){return this.split('').reverse().join('');} \n var h = ['hello, world', 'Hello, World', 'Hello, World!', 'hello, world!', 'hello world', 'hello world!', 'Hello World', 'Hello World!', 'Hello, world.', 'Hello, World.' ,'hello world.' , 'Hello World.']; /* var q ='" + source + "';*/ " + wc;

eval(wc);

\$\endgroup\$
2
  • \$\begingroup\$ This reminds me a lot of goruby. If you could generalize this to expand any missing function/keyword to its closest match, I think you might really have something. \$\endgroup\$
    – primo
    Feb 23, 2016 at 14:40
  • \$\begingroup\$ @primo that is what I plan on doing \$\endgroup\$ Feb 23, 2016 at 18:17
2
\$\begingroup\$

S.I.L.O.S, 360 bytes

a=97
def q print z bottle v beer L Line g wall x IntNoLine h the c around
qL 99 zs of v on h g, 99 zs of v.
lbls
q Take one down and pass it c, 
a+1
qx a
qL  zs of v on h g.
qx a
q  zs of v on h g, 
qx a
qL  zs of v.
a-2
if a s
qL Take one down and pass it c, 1 z of v on h g.
qL 1 z of v on h g, 1 z of v.
q Go to h store and buy some more, 99 zs of v on h g.

Try It Online!

\$\endgroup\$
1
  • 1
    \$\begingroup\$ love it! Have an <s>upvote</s> upgoat! \$\endgroup\$ Aug 29, 2016 at 1:04
2
\$\begingroup\$

SOGL 0.8.2, 123 bytes

³0=?"¡θ9‘}"⁵εα⁸‘⁴1≠? s+}+→@MHM{@⁽t⁾"¹u?«◄/⅞┐⌡u¹E³‘oo"ā)≡⁰≤τ¹‘o"5|░H׀│P¬2h⁶─#‘⁴0=?M"Zø‼;3σΠg℮╥╝ņΟXž∆‘}⁽OH@o"Θ┘θΖγ┘ψψ╚⅛╔⁷‘oøO

First of all, the function:

                             this function is ran with a number on the stack - the bottle count and pushes "x/no more bottle(s)"
³0=?"¡θ9‘}"⁵εα⁸‘⁴1≠? s+}+→@  full; example here is 0
                         →@  replace "@" in the further code with the code below              [0]
³                            triplicates the number                                           [0, 0, 0]
 0=?     }                   if equal to 0                                          stack ->  [0, 0, 1]
    "¡θ9‘                      push "no more" (this can leave garbage)                        [0, 0, "no more"] 
          "⁵εα⁸‘             push " bottle"                                                   [0, 0, "no more", " bottle"]
                ⁴            duplicate the 2nd from top of the stack (number or "no more")    [0, 0, "no more", " bottle", "no more"]
                 1≠?   }     if not equal to 1 then                                           [0, 0, "no more", " bottle"]
                     s+        append "s"                                                     [0, 0, "no more", " bottles"]
                        +    add together                                                     [0, 0, "no more bottles"]

And the rest of the code:

MHM{@⁽t⁾"¹u?«◄/⅞┐⌡u¹E³‘oo"ā)≡⁰≤τ¹‘o"5|░H׀│P¬2h⁶─#‘⁴0=?M"Zø‼;3σΠg℮╥╝ņΟXž∆‘}⁽OH@o"Θ┘θΖγ┘ψψ╚⅛╔⁷‘oøO
MH                                                                                                push 100-1 = 99 (bottle count)
  M{                                                                                              repeat 100 times
    @                                                                                             the function defined previously
     ⁽t⁾                                                                                          output with the 1st letter uppercase (doesn't pop, newline)
        "¹u?«◄/⅞┐⌡u¹E³‘o                                                                          append " of beer on the wall, "
                        o                                                                         append the previous functions result
                         "ā)≡⁰≤τ¹‘o                                                               append " of beer."
                                   "5|░H׀│P¬2h⁶─#‘                                                push "take one down and pass it around, "
                                                  ⁴0=?                   }                        if the original bottle count = 0
                                                      M                                           push 100 (new bottle count)
                                                       "Zø‼;3σΠg℮╥╝ņΟXž∆‘                         push "go to the store and buy some more, "
                                                                          ⁽O                      output with the 1st letter uppercase (hardcoding would've been longer)
                                                                            H                     decrease the bottle count
                                                                             @o                   run that function and output its result
                                                                               "Θ┘θΖγ┘ψψ╚⅛╔⁷‘o    append " of beer on the wall."
                                                                                              øO  output an empty line

could probably be golfed 1-3 bytes removing repetition like " of beer on the wall, " and " of beer on the wall."

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

PHP, 269 263 260 bytes

function f($p){return" bottle".($p-1?"s":"");}function b($b){$o=" of beer";$w=" on the wall";while($b){$n=f($b);echo"<p>$b$n$o$w, $b$n$o.<br>";echo--$b?"Take one down and pass it around, $b":"Go to the store and buy some more, 99";$n=f($b);echo"$n$o$w.</p>";}}

Readable version:

function f($p) {
    return " bottle" . ($p-1 ? "s" : "");
}

function b($b) {
    $o = " of beer";
    $w = " on the wall";
    while ($b) {
        $n = f($b);
        echo "<p>$b$n$o$w, $b$n$o.<br>";
        echo --$b ? "Take one down and pass it around, $b"
                  : "Go to the store and buy some more, 99";
        $n = f($b);
        echo "$n$o$w.</p>";
    }
}

Call b(99) and away you go! Almost certainly could use some more optimization.

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

Prolog (SWI), 312 bytes

w(X):-write(X).
e:-w('.'),nl.
t(X):-w(X),b(X).
b(B):-B>1,w(' bottles of beer').
b(B):-B=1,w(' bottle of beer').
c(B):-t(B),w(' on the wall').
r(N):-N=0,w('Go to the store and buy some more, '),c(99),e.
r(N):-N>0,w('Take one down and pass it around, '),c(N),e,nl.
s(X):-X>0,c(X),w(', '),t(X),e,N is X-1,r(N),s(N).

Try it online!

\$\endgroup\$
2
  • 2
    \$\begingroup\$ Welcome to the site! Couild you maybe create an online demo of your answerr using Try it Online? \$\endgroup\$
    – Luis Mendo
    Oct 22, 2017 at 18:27
  • 3
    \$\begingroup\$ Why does your output end with false.\n\n\n? \$\endgroup\$ Oct 22, 2017 at 18:48
2
\$\begingroup\$

PowerShell, 228 227 220 221 bytes

$a,$l,$e,$s=" bottles of beer"," bottle of beer"," on the wall","Take one down and pass it around, "
99..3|%{"$_$a$e, $_$a.
$s$($_-1)$a$e.
"}
"2$a$e, 2$a.
$s`1$l$e.

1$l$e, 1$l.
Go to the store and buy some more, 99$a$e."

Try it online!

Psssh, who needs fancy-pants logic. (Spoilers: Better answers do) This just manually builds the last two lines instead of doing clever stuff. Also, saved 7 bytes by replacing `n with an actual line break. Lost a byte because I forgot to punctuate.

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

C++, 288 289 282 bytes

#import<iostream>
int i,v;main(){for(i=99;i>0;i--)printf("%1$d%6$s%2$s%7$s%8$s, %1$d%6$s%2$s%7$s.\n%3$s, %4$d%6$s%5$s%7$s%8$s.\n\n",i,i-1?"s":"",i-1?"Take one down and pass it around":"Go to the store and buy some more",v=i-1?:99,v-1==1?"":"s"," bottle"," of beer"," on the wall");}

Requires no additional compile flags.

Ideone: https://ideone.com/fhL9Lv

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

Assembly (MIPS, SPIM), 676 623 575 570 bytes

.data
a: .asciiz " bottle"
c: .asciiz " bottles"
e: .asciiz " of beer.\n"
f: .asciiz " of beer on the wall, "
g: .asciiz " of beer on the wall.\n"
h: .asciiz "Take one down and pass it around, "
i: .asciiz "Go to the store and buy some more, "
.text
.globl main
main:
li $8 99
li $11 1
y:
la $9 f
jal t
la $9 e
jal t
addi $8 -1
beq $8 $0 v
la $4 h
syscall
la $9 g
jal t
li $2 11
li $4 10
syscall
j y
t:
move $4 $8
li $2 1
syscall
li $2 4
beq $8 $11 q
la $4 c
j r
q:
la $4 a
r: 
syscall
move $4 $9
syscall
jr $ra
v:
la $4 i
syscall
li $8 99
la $9 g
jal t
li $2 10
syscall

Try it online!

Turns out it's actually cheaper to declare "bottle" and "bottles", as well as strings with newlines and commas directly in place, and just select between them. Also, using all the syscalls is actually cheaper than the alternative, though only because the the call pointer register is hidden. MIPS' need for newlines actually makes this surprisingly readable, if you know the opcodes. For those of you trying to decipher it, t is a function to print the variations of " bottle[s] of beer...," and $9 stores the address of the string to finish that phrase. One annoying caveat was the fact that all the verses require a newline between them, but the end cannot have two newlines (afaik). So theres a section at the end of the loop that prints a single character, which happens to be newline (decimal 10).
Edit: saved 5 bytes by using $9 instead of $10. I had originally used $9, but optimized it out, and forgot to shift the registers optimally. Also, this submission took forever because I was editing it in place.

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

Wren, 246 bytes

System.print((99..1).map{|i|"~ on the wall, ~.\n%(i>1?"Take one down and pass it around":"Go to the store and buy some more"), %(i>1?i-1:99) bottle%(i!=2?"s":"") of beer on the wall.".replace("~","%(i) bottle%(i>1?"s":"") of beer")}.join("\n\n"))

Try it online!

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

C#, 205 bytes

()=>{for(int i=299;i-->2;)System.Console.Write(@$"{i/3:0;;99} bottl{i/3^1:es;;e} of beer{i%3-3/i: on the wall;\.
Go to the store and buy some more;\.
Take one down and pass it around}{~i%3:;\, ;\.

;}");};

Try it online!

As an anonymous lambda. A full program scores 227.

Putting custom numeric formats to good use. I'm particularly satisfied with i%3-3/i to select between Take one down, Go to the store, and on the wall.

\$\endgroup\$
2
+200
\$\begingroup\$

APL (Dyalog Unicode), 200 bytes

-18 thanks to Razetime

d←'.'
q←'s',⍨w←'bottle'
x←∊a z←' of beer' ' on the wall'
{⎕←∊⍵q x','⍵q a d⋄⎕←∊'Take one down and pass it around,'(⍵-1)w('s'/⍨⍵≠2)x d}¨⌽1↓⍳99
∊1w x','1w a d
∊'Go to the store and buy some more,'99q x d

Try it online!

\$\endgroup\$
6
  • \$\begingroup\$ "98 bottle of beer"? \$\endgroup\$
    – Adám
    Dec 13, 2020 at 16:30
  • \$\begingroup\$ @Adám I messed up not equals and equals; it should work now \$\endgroup\$ Dec 13, 2020 at 16:39
  • \$\begingroup\$ You can golf a lot more here. x←∊a z←' of beer' ' on the wall' and use in place of many concatenations. Also explore the default output format when you mix raw numbers and text (it'll put spaces around the numbers ― just what you need). \$\endgroup\$
    – Adám
    Dec 13, 2020 at 16:48
  • \$\begingroup\$ @Adám thanks for the tip, I'll look into it tomorrow \$\endgroup\$ Dec 13, 2020 at 17:00
  • \$\begingroup\$ @Adám I attempted some golfs; it seems a whole lot smaller. \$\endgroup\$ Dec 16, 2020 at 7:21
2
\$\begingroup\$

Vyxal j, 145 bytes

2₁rƛ«ƛ
÷v≬ẏẆ:βĠPd⁰1Ȧ∩_«£¥:½h\r+WḢnð+vJ‛, j\.¶`¬Ȧ λḭ ∧ċ λ¬ ∆¶ it ⟑Ṡ, `n1-ð¥\.Wṅ;Ṙ`1 ḋ¶ `¥⌈ḢṄ+:½h`er. `+"‛, j`Go to λλ ¬↓ λ¬ by λ⋎ λƈ, 99 `¥+\.+"⁋J

Try it Online!

\$\endgroup\$
2
  • 4
    \$\begingroup\$ There are seven answers above you already. Please don't include any comments like "beats all other answers" or "ties with X/Y/Z", which can be invalidated at any moment, and do not align well with our view of intra-language competitions. \$\endgroup\$
    – Bubbler
    Jun 7, 2021 at 5:49
  • \$\begingroup\$ @Bubbler very sorry \$\endgroup\$
    – Wasif
    Jun 7, 2021 at 5:57
2
\$\begingroup\$

Regenerate, 170 bytes

((99( bottle)s( of beer))( on the wall)), $2.\n(Take one down and pass it around, (((${$9-1}|98)$3s{1-1/$9}$4)$5).\n\n$7, $8.\n){98}Go to the store and buy some more, $1.

Try it here!

This is basically just a bunch of capturing groups and backreferences repeated 98 times, with two interesting parts:

  • Group 9, which holds the number at each iteration, is (${$9-1}|98). The first time through, since group 9 hasn't been matched yet, the $9 backreference fails and we use the other option, 98. Afterwards, $9 contains the previous number, so we use that value minus 1: ${$9-1}.
  • In order not to pluralize bottle when the number hits 1, we use s{1-1/$9}. Since / is integer division, 1/$9 will be 0 if group 9 is greater than 1 and 1 if group 9 equals 1. Then {1-1/$9} repeats the s 1 time if the number is over 1 and 0 times if the number equals 1.
\$\endgroup\$
2
\$\begingroup\$

Excel, 198 209 206 bytes

+9 bytes for correcting 1 bottle error

-3 bytes for tweaks

=LET(q,99-ROW(1:99),b,q+1&" bottle"&REPT("s",q>0)&" of beer",w,b&" on the wall",w&", "&b&".
"&IF(q,"Take one down and pass it around, "&INDEX(w,100-q),"Go to the store and buy some more. "&INDEX(w,1))&".
")

Link to Spreadsheet

This version shows 3 lines per cell. Would be slightly longer to put it all in one cell and wouldn't be completely readable on screen.

Original

=LET(q,100-ROW(1:98),b," bottles of beer",w,b&" on the wall",CONCAT(q&w&", "&q&b&".
Take one down and pass it around, "&(q-1)&w&".

")&"1"&b&", 1"&b&".
Go to the store and buy some more, 99"&w &".")
\$\endgroup\$
5
  • \$\begingroup\$ This can be made 3 bytes shorter ((q-1)q-1 and &w &&w&) but it also has an error on the end: The last line starts "1 bottles of beer," instead of "1 bottle of beer on the wall," Try this as the 4th line in the formula: ")&"1"&SUBSTITUTE(w,"s",)&", 1"&b&". I count 212 bytes with all those changes. \$\endgroup\$ Jun 17, 2021 at 19:21
  • \$\begingroup\$ (Scratch that last line suggestion. I only fixed the first instance of 1 bottles but you get the idea.) \$\endgroup\$ Jun 17, 2021 at 19:28
  • \$\begingroup\$ Well that's unfortunate. I rather liked my code being a multiple of 99. \$\endgroup\$
    – Axuary
    Jun 18, 2021 at 13:46
  • 1
    \$\begingroup\$ Using Rept instead of If here is a wonderful trick - you should add that to the tips page \$\endgroup\$ Jan 10, 2022 at 16:44
  • 1
    \$\begingroup\$ @TaylorAlexRaine I don't get how REPT is a good trick when IF(q,"s","") is one byte shorter than REPT("s",q>0). Also, @Axuary, there's a small error in the last line. It has buy some more. but it should be buy some more, with a comma. \$\endgroup\$ Apr 14, 2023 at 13:29
2
\$\begingroup\$

TypeScript’s type system, 278 bytes

This was the first golf I ever submitted using TS types! It's been over a year since I made my original 591 byte solution, and I'm glad to say I have gotten way better at TS types golf. Revisiting the 99 bottles of beer problem, I came up with the following solution, which is 60 bytes shorter than the solution ASCII-only wrote based on mine, and over half the size of the original solution I had when I first approached the problem.

//@ts-nocheck
type F<N=[1],S="Go to the store and buy some more, 99 bottles of beer on the wall.",L=N["length"],X=`${L} bottle${L extends 1?"":"s"} of beer`,Z=" on the wall",V=`${X}${Z}, ${X}.
${S}`>=L extends 99?V:F<[...N,1],`Take one down and pass it around, ${X}${Z}.

${V}`>

Try it at the TypeScript playground!

This is a type F which is generic, but all the generic type parameters have defaults so no input types are needed.

Explanation: Start with the last line of the text, and N = 1:

Go to the store and buy some more, 99 bottles of beer on the wall.

Until N = 99, increment N and prepend:

Take one down and pass it around, {N} bottles of beer on the wall.

{N} bottles of beer on the wall, {N} bottles of beer.

When N = 99, prepend:

99 bottles of beer on the wall, 99 bottles of beer.

and return the final string.

In the actual program, repeated parts of the text are extracted into types that are then embedded into the string type.

For posterity's sake, here's my old solution, although I'm a bit embarassed about how bad I used to be :)

TypeScript Type System, 591 344 bytes

344 bytes thanks to @ASCII-only

type m<P=-1,A=[]>=A['length']extends 100?A:m<A['length'],[...A,P]>
type n=m
type d<N>=n[N]
type a<N>=`${N} bottle${N extends 1?"":"s"} of beer`
type _<N extends 0=99>=`${a<N>} on the wall, ${a<N>}.
${N extends 1?`Go to the store and buy some more, ${a<99>} on the wall.`:`Take one down and pass it around, ${a<d<N>>} on the wall.

${_<d<N>>}`}`

TS Playground link

Also I'm not sure this is valid, since it doesn't output the type to STDOUT, since that's not a thing with TS types.

\$\endgroup\$
10
  • \$\begingroup\$ 308? \$\endgroup\$
    – ASCII-only
    Jan 22, 2023 at 9:42
  • \$\begingroup\$ @ASCII-only I initially thought of something like that when I posted the answer, but the problem is that the type isn't the full lyrics, it ends with 51 bottles of beer... it around, ${..., while the version I posted has the full lyrics in the type. \$\endgroup\$
    – noodle man
    Jan 22, 2023 at 13:16
  • 1
    \$\begingroup\$ hmmm... then something like this should work for 578 \$\endgroup\$
    – ASCII-only
    Jan 22, 2023 at 14:18
  • 1
    \$\begingroup\$ 344? (full lyrics ofc) \$\endgroup\$
    – ASCII-only
    Jan 22, 2023 at 14:27
  • 1
    \$\begingroup\$ @ASCII-only I revisited this problem today and came up with a much shorter solution :D \$\endgroup\$
    – noodle man
    Feb 17 at 15:47
1
\$\begingroup\$

Matlab, 254

b=@(n)[' bottle','r'+eye(n~=1),' of beer'];o=' on the wall';f=@fprintf;
for k=99:-1:1;f(['%d',b(k),o,', %d',b(k),'.\n'],k,k);
if k>1;f('Take one down and pass it around, %d',k-1);
else;f('Go to the store and buy some more, 99');
end;f([b(k-1),o,'.\n\n']);end
\$\endgroup\$
1
\$\begingroup\$

Groovy, 281 bytes

def s=" of beer on the wall";def b=" bottles";for(int i=99;;)(i>1)?{println i+b+s+", "+i+b+" of beer.\nTake one down and pass it around, "+--i+(i==1?" bottle":b)+s+".\n"}():{println "1 bottle"+s+", 1 bottle of beer.\nGo to the store and buy some more, 99"+b+s+".";System.exit(0)}()

Try it online.

dis is best idea

\$\endgroup\$
4
  • \$\begingroup\$ Vitsy is still winning, though. ;) Keep golfing. +1 \$\endgroup\$ Nov 18, 2015 at 21:57
  • \$\begingroup\$ Nice, but this is 213 bytes. (Yes, I know it's a bit late, so what?) \$\endgroup\$
    – J Atkin
    Feb 13, 2016 at 18:08
  • \$\begingroup\$ @JAtkin This was an incredibly lazy reference implementation to my sandboxed version of this challenge. \$\endgroup\$ Feb 13, 2016 at 18:28
  • \$\begingroup\$ Oh well ;) I can see it now that I am looking at the code. (redundant def, ...) \$\endgroup\$
    – J Atkin
    Feb 13, 2016 at 18:28
1
\$\begingroup\$

C, 244 242 240 bytes

k;main(i){char*B=" bottle",*G="s of beer",*W=" on the wall";for(i=99;i;k=--i<2)printf("\n%d%s%s%s, %d%s%s.\n%s, %d%s%s%s.\n",i,B,G+k,W,i,B,G+k,k?"Go to the store and buy some more":"Take one down and pass it around",k?99:i-1,B,G+!(i-2),W);}

Compiled as:

gccw -std=c99 -o party party.c

Multiline:

k;
main(i)
{
    char *B=" bottle",
         *G="s of beer",
         *W=" on the wall";

    for(i = 99; i; k = --i < 2)
        printf("\n%d%s%s%s, %d%s%s.\n%s, %d%s%s%s.\n",
               i,B,G+k,W,
               i,B,G+k,
               k ? "Go to the store and buy some more":
                   "Take one down and pass it around",
               k ? 99 : i-1, B, G + !(i-2), W
        );
}
\$\endgroup\$
0
1
\$\begingroup\$

C, 249 bytes

#define A(N)printf(N<2?"%s%d bottle%s of beer%s":N^2?N^4?", ":n?"Take one down and pass it around, ":"Go to the store and buy some more, ":".\n\n","",n?n:99,n>9|!n?"s":"",N ?" on the wall":".\n"),
n=99;main(){while(n)A(1)A(3)A(0)--n,A(4)A(1)A(2)0;}

Ungolfed:

#define A(N) \
    printf( \
        /* format string */ \
        N<2 \
            ? "%s%d bottle%s of beer%s" \
            : N^2 \
                ? N^4 \
                    ? ", "  \
                    : n ? "Take one down and pass it around, ":"Go to the store and buy some more, " \
                : ".\n\n", \
        /* arguments (ignored if N >= 2) */ \
        "", \
        (n ? n : 99), \
        (n > 9 | !n) ? "s":"", \
        N ?" on the wall":".\n"),

n=99;
main() {
    while(n) A(1) A(3) A(0) --n, A(4) A(1) A(2) 0;
}
\$\endgroup\$
1
\$\begingroup\$

Swift 2.0, 208 239 bytes

After fixing the bottles thing, it's a little longer:

let a:Int->String={"\($0) bottle\($0==1 ?"":"s") of beer"},b=" on the wall"
(1...99).reverse().forEach{print("\(a($0)+b), \(a($0)).\n\(($0==1 ?"Go to the store and buy some more, "+a(99):"Take one down and pass it around, "+a($0))+b).\n")}

Here's the old, shorter version too without handling the s:

let a=" bottles of beer",b=" on the wall",c="Go to the store and buy some more, 99"
(1...99).reverse().forEach{print("\($0)\(a+b), \($0)\(a).\n\(($0==1 ?c:"Take one down and pass it around, \($0)")+a+b).\n")}
\$\endgroup\$
0
1
\$\begingroup\$

Bash, 235 bytes

e=of\ beer
d="Take one down and pass it around"
c=" on the wall"
b=" bottles "
f=$b
for n in {99..2};{
((n==2))&&f=${f/s}
echo "$n$b$e$c, $n$b$e.
$d, $[n-1]$f$e$c.
"
}
echo "1$f$e$c, 1$f$e.
Go to the store and buy some more, 99$b$e$c."

Readable

e="of beer"
d="Take one down and pass it around"
c=" on the wall"
b=" bottles "
f=$b
for n in {99..2}; {
  ((n==2)) && f=" bottle "
  echo -e "$n$b$e$c, $n$b$e.\n$d, $[n-1]$f$e$c.\n"
}
echo -e "1$f$e$c, 1$f$e.\nGo to the store and buy some more, 99$b$e$c."

As @mego mentions in the comments below, you can easily try it and see the result here: http://ideone.com/6088aJ

Update: I have used @manatwork hints. The readable version is a little bit different from the short version for better understanding.

\$\endgroup\$
9
  • \$\begingroup\$ This fails to print "bottles" in "Take one down and pass it around, n bottles of beer on the wall." for n > 1. \$\endgroup\$
    – user45941
    Nov 19, 2015 at 13:35
  • 1
    \$\begingroup\$ Looks good now, +1. I'd recommend adding this link to your answer so everyone can easily try it. \$\endgroup\$
    – user45941
    Nov 19, 2015 at 14:24
  • 1
    \$\begingroup\$ A few minor things: s=s seems completely unused; e="of beer"e=of\ beer; $((n-1))$[n-1]; instead of \n use a literal line wrap in the string literal, so you can remove the -e too; [ $n = 2 ]((n==2)). \$\endgroup\$
    – manatwork
    Nov 19, 2015 at 17:05
  • 1
    \$\begingroup\$ Calculating again, there is a waste of space. Literally. I mean, you should move the spaces from the echo parameters to the text pieces declarations. I mean like this: pastebin.com/TGsH5QVZ \$\endgroup\$
    – manatwork
    Nov 19, 2015 at 17:14
  • 1
    \$\begingroup\$ Sorry, but this throws syntax error. There must be a command separator in front of }. Either ; or CR. \$\endgroup\$
    – manatwork
    Nov 19, 2015 at 17:45
1
\$\begingroup\$

BitShift, 342372 bytes

Pre-note: This is probably sub-optimal...

BitShift is a language created by me, which only supports bit-shifting operations on a single value. It is not great for :')

Since the program is too long to include in this post, it is provided here.
It can be tested here. Be aware that it will take a while to finish.

I am hoping for this challenge to provide a better solution to this problem.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ if you click "make readable" it gets mad because newlines aren't 1 or 0 haha \$\endgroup\$ Nov 22, 2015 at 4:50
1
\$\begingroup\$

PHP, 256 bytes

<?php $b=' bottle';$o=' of beer';$t=' on the wall';$i=99;while($i>=1){$s=$i!=1?'s':'';echo "$i$b$s$o$t, $i$b$s$o.\n".($s?"Take one down and pass it around":"Go to the store and buy some more").", ".($i==1&&--$i>-3?99:--$i)."$b".($i!=1?'s':'')."$o$t.\n\n";}
\$\endgroup\$
1
\$\begingroup\$

R, 233 Bytes

f=function(n){b="bottles of beer";p=paste0;w="on the wall";cat(n,b,p(w,","),n,p(b,"."),"\n");if(n>1){cat("Take one down and pass it around,",n-1,b,p(w,".\n"));f(n-1)}else cat("Go to the store and buy some more, 99",b,p(w,"."))}
f(99)

Ideone as a proof!

longer (non-golf) version

Same functions, same syntax; added only breaks and brackets

f <- function(n) { 
  b <- "bottles of beer"
  p <- paste0
  w <- "on the wall"
  cat(n, b, p(w, ","), n, p(b, "."), "\n")
  
  if (n > 1) {
    cat("Take one down and pass it around,", n - 1, b, p(w, ".\n"))
    f(n - 1)
  } else {
    cat("Go to the store and buy some more, 99", b, p(w, "."))
  }
}

f(99)
\$\endgroup\$
1
  • 4
    \$\begingroup\$ It writes 1 bottles of beer during the last two rounds. \$\endgroup\$
    – plannapus
    Feb 24, 2016 at 8:30
1
\$\begingroup\$

golflua, 206 bytes

\b(t)~i.." bottle"..(i>1&"s"|"").." of beer"..t$\c(t)~b" on the wall"..t$i=99@i>0w(c", "..b".")?i<2 ~~$i=i-1w("Take one down and pass it around, "..c".\n")$i=99w("Go to the store and buy some more, "..c".")

Sample run:

bash-4.3$ golflua 99.golflua | tail
Take one down and pass it around, 3 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.
\$\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.