97
\$\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
    Commented Nov 18, 2015 at 15:19
  • 26
    \$\begingroup\$ This is a subtask of the Create an HQ9+ Interpreter golf. \$\endgroup\$
    – Geobits
    Commented 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.
    Commented 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
    Commented 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
    Commented Oct 22, 2018 at 4:05

181 Answers 181

1
2
3 4 5
7
5
\$\begingroup\$

PHP, 172 bytes

<?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.";

The most significant trick is $o[21]. On the first iteration, $o will be undefined, resulting in an empty string. On each iteration after, it refers to the first s of pass.

\$\endgroup\$
1
  • \$\begingroup\$ Windows produces 99 bottlea of beer on the wall. You could go safe with 22 instead of 21. And You don´t need the <? if You use -r. \$\endgroup\$
    – Titus
    Commented Jan 5, 2017 at 0:52
5
\$\begingroup\$

Java, 274 bytes. Java 21+: 245 bytes

Java -v20:

interface B{static void main(String[]p){String a=" bottle",b=" of beer",s=a+'s'+b,x=s,c=" on the wall",n=".\n";for(int i=99;i>0;)System.out.println(i+x+c+", "+i--+x+n+(i>0?"Take one down and pass it around, "+i:"Go to the store and buy some more, 99")+(x=i==1?a+b:s)+c+n);}}

Ungolfed:

interface B{
    static void main(String[]p){
        String a=" bottle",b=" of beer",s=a+'s'+b,x=s,c=" on the wall",n=".\n";
        for(int i=99;i>0;)
            System.out.println(
                i+x+c+", "+i+x+n
                +(--i>0?"Take one down and pass it around, "+i:"Go to the store and buy some more, 99")
                +(x=i==1?a+b:s)+c+n
            );
    }
}

Java v21+:

void main(){String a=" bottle",b=" of beer",s=a+'s'+b,x=s,c=" on the wall",n=".\n";for(int i=99;i>0;)System.out.println(i+x+c+", "+i+x+n+(--i>0?"Take one down and pass it around, "+i:"Go to the store and buy some more, 99")+(x=i==1?a+b:s)+c+n);}

Ungolfed:

void main(){
    String a=" bottle",b=" of beer",s=a+'s'+b,x=s,c=" on the wall",n=".\n";
    for(int i=99;i>0;)
        System.out.println(
            i+x+c+", "+i+x+n
            +(--i>0?"Take one down and pass it around, "+i:"Go to the store and buy some more, 99")
            +(x=i==1?a+b:s)+c+n
        );
}

Built on the previous record, shortened by 6 14 16 19 25 27 21 characters by rearranging stuff.

Curious to see how much lower we can still go.

**Edit2: Added Java 21+ version which allows further reductions through the implicit class & instance main method features. Run the source directly with "java --source 21 --enable-preview B.java". (Match the version number to your Java.) "--enable-preview" won't be needed once the feature graduates from preview.

*Edit1: Previous version with length of 268 chars matched the output in the highest-voted java entry at the time (~290 chars), but it didn't fully match the punctuation in the challenge text. I updated it to match the challenge which required +6 bytes. Credit to @DLosc and @moderators .

\$\endgroup\$
9
  • 1
    \$\begingroup\$ Welcome to the site! You are always allowed to comment suggestions or post your own answer. However our site policy and culture discourages editting other people's answers with golfs unless they are a "Community wiki". \$\endgroup\$
    – Wheat Wizard
    Commented Mar 14 at 21:44
  • \$\begingroup\$ Thanks! Just to confirm, I tried to comment the existing Java entry on page 1 several times. I always get a "You must have 50 reputation to comment" popup. \$\endgroup\$
    – Rubinhood
    Commented Mar 14 at 23:39
  • \$\begingroup\$ Shortened it by 8 + 2 chars, building on the existing ideas. \$\endgroup\$
    – Rubinhood
    Commented Mar 16 at 22:11
  • \$\begingroup\$ Down another 3 bytes, by streamlining how the plural is handled. \$\endgroup\$
    – Rubinhood
    Commented Mar 17 at 19:43
  • \$\begingroup\$ Another -6 characters. \$\endgroup\$
    – Rubinhood
    Commented Mar 18 at 13:04
4
\$\begingroup\$

C, 230 bytes

My first codegolfing and learning C!

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

Compiled with gcc -std=c99 -w beer.c

Readable (sort of):

#define B "%d bottle%s of beer"

main(i,a){
  for (i = 99; i; i--) {
    a=i<2;
    printf(B" on the wall, "B".\n%s, "B" on the wall.\n\n",
      i, "s"+a, i, "s"+a,
      a? "Go to the store and buy some more":"Take one down and pass it around",
      a? 99 : i-1, "s"+(i==2));
  }
}

My first entry (293 bytes):

#define BOT "bottle%s of beer"
main(){
char *e[]={"Go to the store an buy some more","Take one down and pass it around"};char *z[]={"","s"};
for(int i=99;i>0;i--){int j=i>1?i-1:99;char* s=z[i>1];
printf("%d "BOT" on the wall, %d "BOT". %s, %d "BOT" on the wall.\n",i,s,i,s,e[i>1],j,z[j>1]);}}

Haha, I love all these nasty tricks i applied that i never though could even exist in C!

\$\endgroup\$
6
  • \$\begingroup\$ Typo: Go to the store an buy \$\endgroup\$
    – feersum
    Commented Nov 18, 2015 at 20:05
  • \$\begingroup\$ @feersum Thanks, fixed. \$\endgroup\$
    – Arnaud
    Commented Nov 18, 2015 at 20:53
  • 1
    \$\begingroup\$ Welcome to PPCG! Lots more opportunity for more golfing this answer. Check out these tips \$\endgroup\$ Commented Nov 18, 2015 at 21:54
  • \$\begingroup\$ You're missing the newline after "beer." \$\endgroup\$
    – Ray
    Commented Nov 19, 2015 at 2:00
  • \$\begingroup\$ @DigitalTrauma Thank you! wow great link, really usefull! chopped like 15 bytes with some of those tricks! \$\endgroup\$
    – Arnaud
    Commented Nov 19, 2015 at 6:55
4
\$\begingroup\$

Beam, 1141 1109 bytes

I still have a lot of room to golf this further with all the empty spaces, but it is getting really hard to follow and breaks quite easily :) It's very similar to the one that I posted for this question, except it goes to the store before the beer reaches 1 and the cells used for the parameters have been shifted. I've also change the layout considerably. I'll try and do an explanation once I try out a couple more layouts.

P'P''''>`++++++)++'''P>`+++++++++++++)'''''''''''P+++++++++++++`P```>`++\ v@@++++++++++L(`<@+p'''''''''PL<
v``P''(++++++`<P'''''''''(++++++++`<L```P'+++++P'+++P'++++++P'++++P''''(/> p++@---@``p@'''''p+++@`> `)''' 'p-@''p\
>''p:'p@'p@'\>n'   >`>`)'''p@''''p@\>n'''p@''''p@-@````p@'''''p@`>`)'''''/v  `P+p``@p'''(`<@-p''''''''P'+up(`<`@@/
^/``@@p'@p''/ >'''\ /-p'@p'@p``@p``/`>-'P''''''''p+@>`)''p`n`L++++++++++@S 'p@````p@````p@'''''p@`p@````p@'''''p@```p++@---@``p@'''''p-@+@`p@+++@``p-@``p@'p-@'''p-@```p++@`p@'p@''''p+@++++@`````p@'''''p-@`p@--@``p-@``p@''''p--@p@+++@``p-@''''p-@>`)'''p@'p+:`p@'p@'''p@'p@@``p@`p-@'''p-@`>`)'''p@''''p@``p@``p@'p@'p-@@'''p--@`>`)'''p@''''p@-@````p@'''''p@`>`)'''''p++@---@``p@'''''p+++@`>`)''''p-@''p@@'''p+@H
^\p@`p-@```p`//'''/ \@@'''p--@`>`)'p/``````@pS@++++++++++L`<vP+p`P-p`P-p`@ p'''(`<@-p''''@--p``@-p`@+p'@p`@--p''''@-p'@p`````@p'''@+++p''@p```\
^             \'p-@/v               \ p-@''p-@`p-@``p@''''p@ -@``p-@``p@'p ++@'''p@'p+++@`p-@````p@'p-@'''p-@```p++@`p@''''p+@```p-@''''p-@@``/
^                   <                                       <             <

Try it in the stack snippet here

\$\endgroup\$
4
\$\begingroup\$

PHP, 250 246 244 233 228 bytes

I believe this is smallest so far. This is based on the 247 shot, with some modifications to further minimise.

Minimised

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

Expanded

<?php

$b=99;
function x($n){return"$n bottle".($n-1?'s':'')." of beer";}
$y=" on the wall";
while(b){
    $c=x($b);
    echo"$c$y, $c.\n",--$b?"Take one down and pass it around":"Go to the store and buy some more",", ".x($b?:99)."$y.\n\n";
}
\$\endgroup\$
7
  • 1
    \$\begingroup\$ As you can run PHP from command line with php -r which is equivalent of perl -e and similar, is acceptable to omit the opening tag. See the related discussion in Running PHP with -r instead of code tags. \$\endgroup\$
    – manatwork
    Commented Nov 19, 2015 at 18:27
  • \$\begingroup\$ Ah, thanks @manatwork, I guess I could then get mine down a bit more. \$\endgroup\$
    – Phroggyy
    Commented Nov 19, 2015 at 18:29
  • \$\begingroup\$ while($b) works just as well. \$\endgroup\$
    – frnhr
    Commented Nov 21, 2015 at 18:32
  • \$\begingroup\$ There is an extra newline at the end though. \$\endgroup\$
    – frnhr
    Commented Nov 21, 2015 at 18:33
  • \$\begingroup\$ Thanks for the whole suggestion! The newline is required though, to have a line in between verses \$\endgroup\$
    – Phroggyy
    Commented Nov 21, 2015 at 18:41
4
\$\begingroup\$

Oracle SQL, 393 326 320 306 324 bytes*l

Grew a few bytes to fix error noted by C.L.

select b||decode(l,0,c||d||', '||b||c||'. Go to the store and buy some more, 99 bottle','s'||c||d||', '||b||'s'||c||'. Take one down and pass it around, '||l||' bottle')||decode(l,1,'','s')||c||d||'.'
from(select level-1 l,level||' bottle' b,' of beer' c,' on the wall' d from dual connect by level <= 99)order by l desc;

\$\endgroup\$
3
  • \$\begingroup\$ Second-to-last line ends with "1 bottles of beer". \$\endgroup\$
    – CL.
    Commented Nov 22, 2015 at 9:59
  • \$\begingroup\$ Fixed - thanks for the catch on that. \$\endgroup\$ Commented Nov 23, 2015 at 15:13
  • \$\begingroup\$ if you want to be on the leaderboard, please make your header be a markdown heading #Language Name \$\endgroup\$
    – cat
    Commented Nov 27, 2015 at 16:10
4
\$\begingroup\$

PARI/GP, 181 bytes

for(i=1,99,{G=Str("Take one down and pass it around, "b=Str(a=Str(i" bottl"e" of beer")" on the wall")".

"g=Str(b", "a".
"G));e=es);print(g"o to the store and buy some more, "b".")

Using the variable name G conveniently saves a byte. The closing brace doesn't seem to be required to end a multi-line context, strangely.

The shortest solution I know of previous to this is 32 bytes heavier.

\$\endgroup\$
4
\$\begingroup\$

R, 267 bytes

Not the shortest solution out there for R but it uses a different approach (namely dataframes).

d=data.frame(i=1:99,j=c(99,1:98));d$b=" bottles of beer";d$t="Take one down and pass it around";d[1,3:4]=c(" bottle of beer","Go to the store and buy some more");d$B=d$b[d$j];w=" on the wall";cat(rev(with(d,sprintf("\n%i%s%s, %i%s.\n%s, %i%s%s.",i,b,w,i,b,t,j,B,w))))

Indented, with newlines:

d = data.frame(i=1:99,j=c(99,1:98))
d$b = " bottles of beer"
d$t = "Take one down and pass it around"
d[1,3:4] = c(" bottle of beer","Go to the store and buy some more")
d$B = d$b[d$j]
w = " on the wall"
cat(rev(with(d,sprintf("\n%i%s%s, %i%s.\n%s, %i%s%s.",i,b,w,i,b,t,j,B,w))))

Here it is on ideone.

\$\endgroup\$
4
\$\begingroup\$

C Preprocessor, 753 bytes

#ifndef T
#define N
#define T 9
#define G(A,B) I(A,B)
#define I(A,B) A##B
#define X Take one down and pass it around,
#define Y G(bottle,s) of beer
#define Z Y on the wall
#define E(x) G(T,x) Z, G(T,x) Y.
#define F(x) X G(T,x) Z.
#endif
E(9)
F(8)
N
E(8)
F(7)
N
E(7)
F(6)
N
E(6)
F(5)
N
E(5)
F(4)
N
E(4)
F(3)
N
E(3)
F(2)
N
E(2)
#if T+0
F(1)
N
E(1)
F(0)
N
E(0)
#if T<2
#undef T
#define T
#elif T<3
#undef T
#define T 1
#elif T<4
#undef T
#define T 2
#elif T<5
#undef T
#define T 3
#elif T<6
#undef T
#define T 4
#elif T<7
#undef T
#define T 5
#elif T<8
#undef T
#define T 6
#elif T<9
#undef T
#define T 7
#else
#undef T
#define T 8
#endif
F(9)
N
#include __FILE__
#else
#define s
X 1 Z.
N
1 Z, 1 Y.
#undef s
Go to the store and buy some more, 99 Z.
#endif

Try it online!

It is my privilege to submit this C preprocessor solution to this catalog. Note that this is a pure C Preprocessor solution (or, as I prefer to call it, a C Preprocessor solution). To my knowledge this is the first such solution to be posted to a catalog (there are a few claimed "C preprocessor" solutions around, but they're really things like C programs using the C preprocessor to generate strings).

5 bytes could be saved if I exploited the file name (e.g. changing #include __FILE__ to #include "f").

If anyone can help golf this further let me know.

\$\endgroup\$
4
\$\begingroup\$

Japt, 157 127 bytes

1oL w £A=X+` Þ­¤{X-1?'s:P}  Þ8`;B=A+`  e Ø!` +".

{B}, {A}.
{´X?`Take e ܵ d ps  ÂÐ`:`Go  e ÐJe d ¿y Ñ  Ú`}, "ìéHn

Markdown hates unprintables (unlike the shoco string compression library), so here's a hexdump:

00000000: 316f 4c20 7720 a341 3d58 2b60 20de ada4 7b58    1oL w £A=X+` Þ.¤{X
00000012: 2d31 3f27 733a 507d 208f 20de 3860 203d 412b    -1?'s:P} . Þ8` =A+
00000024: 6020 8d20 9065 20d8 2160 3b42 2b22 2e0a 0a7b    ` . .e Ø!`;B+"...{
00000036: 427d 2c20 7b41 7d2e 0a7b b458 3f60 5461 6b65    B}, {A}..{´X?`Take
00000048: 208d 6520 dcb5 2084 6420 7086 7320 8a20 c2d0     .e ܵ .d p.s . ÂÐ
0000005A: 9860 3a60 476f 2091 2090 6520 d04a 6520 8464    .`:`Go . .e ÐJe .d
0000006C: 20bf 7920 d120 20da 9060 7d2c 2022 c3ac e948     ¿y Ñ  Ú.`}, "ìéH
0000007E: 6e                                              n

Try it online!

This could probably be made shorter by saving the repeated sections to variables. Absolutely correct, past me. Japt is now the shortest language without a built-in!

\$\endgroup\$
2
  • \$\begingroup\$ Seems to be missing the empty line between verses. \$\endgroup\$
    – primo
    Commented Feb 23, 2017 at 14:43
  • \$\begingroup\$ @primo Thanks, fixed at the cost of one byte. \$\endgroup\$ Commented Feb 24, 2017 at 13:04
4
\$\begingroup\$

T-SQL, 671 653 517 Bytes

Saw an Oracle one so I thought of doing it but I went for a cursor... Not competition level but still a lot of fun =)

DECLARE @q CHAR(15) = 'bottles of beer',@w CHAR(11)='on the wall',@e CHAR(33)='Take one down and pass it around,',@r INT=99,@i INT=1,@ INT=99 WHILE(@>=1)BEGIN SET @i=@-1 IF(@>1)BEGIN PRINT CONVERT(VARCHAR(2),@)+' '+@q+' '+@w+', '+CONVERT(VARCHAR(2),@)+' '+@q+'.'+CHAR(10)+@e+' '+CONVERT(VARCHAR,@i)+' '+@q+' '+@w+'.'+CHAR(13)END ELSE BEGIN PRINT CONVERT(VARCHAR(1),@)+' bottle of beer '+@w+', '+CONVERT(VARCHAR(1),@)+' bottle of beer.'+CHAR(10)+'Go to the store and buy some more, 99 '+@q+' '+@w+'.'END SET @=@-1 END

Proper indent:

DECLARE @q CHAR(15) = 'bottles of beer',
 @w CHAR(11) = 'on the wall',
 @e CHAR(33) = 'Take one down and pass it around,',
 @r INT = 99,
 @i INT = 1,
 @ INT = 99
WHILE (@ >= 1)
    BEGIN
    SET @i = @ -1
    IF (@ > 1)
        BEGIN
        PRINT
            CONVERT(VARCHAR(2), @)+' '+@q+' '+@w+', '+CONVERT(VARCHAR(2), @)+' '+@q+'.'+CHAR(10)+
            @e+' '+CONVERT(VARCHAR, @i)+' '+@q+' '+@w+'.'+CHAR(13)
        END
    ELSE
        BEGIN
        PRINT
            CONVERT(VARCHAR(1), @)+' bottle of beer '+@w+', '+CONVERT(VARCHAR(1), @)+' bottle of beer.'+CHAR(10)+
            'Go to the store and buy some more, 99 '+@q+' '+@w+'.'
        END
    SET @ = @-1
    END
\$\endgroup\$
3
  • \$\begingroup\$ Nice work. Quite a few places to save bytes: #1 Use CONCAT() instead of +, it implicitly casts the integers and you don't need CONVERT. #2: Eliminate @r (unused) and @i (just use @-1), and remove spaces around =. #3: String literals can include returns, use that instead of '+CHAR(10)+'. #4: This is more work but consider a label and a GOTO instead of a WHILE loop. #5: Include spaces at the beginning of @q and @w, and a (period return) at the beginning of @e. \$\endgroup\$
    – BradC
    Commented Aug 9, 2017 at 21:05
  • \$\begingroup\$ Today I learned: @ is a valid variable name in T-SQL. Have a +1. \$\endgroup\$
    – primo
    Commented Oct 22, 2017 at 9:03
  • 1
    \$\begingroup\$ @primo :) also: @@, @@@, @@@@... \$\endgroup\$
    – Nelson
    Commented Oct 22, 2017 at 11:49
4
\$\begingroup\$

Python, 245 223 bytes

w="bottles of beer on the wall"
[print(i,w+',',w[:15]+'.\nTake one down and pass it around,',str(i-1),w+'.\n')for i in range(99,1,-1)]
print('1 bottle',w[8:],',1 bottle',w[8:15]+'.\nGo to the store and buy some more,',99,w)

edit: thank you GamrCorps for the suggestion to cut down over 20 bytes!

\$\endgroup\$
1
  • 3
    \$\begingroup\$ Welcome to PPCG.SE! Nice first post! There are some simple golfing improvements such as single-character variables and the starting index for the slicing (wall[0:8] can be wall[:8]). Otherwise, great job, I hope to see you around! \$\endgroup\$
    – GamrCorps
    Commented Oct 22, 2017 at 20:15
4
\$\begingroup\$

Lua 5.1, 211 207 bytes

(Also works in Lua 5.2 and Lua 5.3.)

4 bytes saved thanks to primo.

B=" on the wall"P=""S="Go to the store and buy some more"for i=1,99 do
A=i.." bottle"..P.." of beer"S=([[Take one down and pass it around, AB.

AB, A.
S]]):gsub(".",_G)P="s"end
print(S:sub(68)..S:sub(33,65))

I chop up the song into chunks that have the same number of bottles of beer which simplifies the interpolation at the cost of some slicing at the end. This also allows constructing the verses backwards which simplifies the logic for pluralizing "bottle".

:gsub(".",_G) is a fun little call that replaces each character in a string with its value as a global variable (with no replacement if the global variable is not defined).

I played around with some of the tricks seen in the Python answers, but I wasn't able to beat this yet. It may still be possible to do a better in-order solution though.

Try it online

\$\endgroup\$
1
  • 1
    \$\begingroup\$ You can initialize S to S='Go to the store and buy some more'. Otherwise nice solution. The gsub on _G is brilliant. \$\endgroup\$
    – primo
    Commented Dec 17, 2018 at 17:29
3
\$\begingroup\$

PHP, 274 269 bytes

<?php $a=' bottles';for($i=99;$i>0;$i--){$d="Take one down and pass it around, ".($i-1)."$a<br><br>";if($i==1){$a=rtrim($a,'s');$d="Go to the store and buy some more, 99 bottles";}$d.=" of beer on the wall.";$s.="$i$a of beer on the wall, $i$a of beer.<br>".$d;}echo$s;
\$\endgroup\$
1
  • 1
    \$\begingroup\$ Depending on you few output, <br> could be replaced by \n, and you can replace rtrim() with trim() as there is no s in front of 'bottles' \$\endgroup\$
    – Martijn
    Commented Nov 18, 2015 at 21:22
3
\$\begingroup\$

Pyth, 157 156 150 149 148 145 bytes

L+b%" bottle%s of beer on the wall."<\stbVK99s[PyKJ", "<13yK\.)++?=tK"Take one down and pass it around""Go to the store and buy some more"Jy|K99k

Try it online.

\$\endgroup\$
1
  • \$\begingroup\$ You can shorten this a bunch using the new ." operator. I can't seem to get the online compiler to make a friendly link to a working one, but here is a paste of the hexdump. I'm not sure if it is a bug, but I couldn't get ." to work with a leading space, but I believe it still saves one byte. \$\endgroup\$ Commented Nov 19, 2015 at 16:20
3
\$\begingroup\$

Perl 5, 243 bytes

A shorter Perl 5 solution is posted as a community wiki solution here.

$}=" bottle";$s='s';$o=" of beer";$w=" on the wall";$i=99;while($i>1){say"$i$}$s$o$w, $i$}$s$o.";$s=''if--$i<2;say"Take one down and pass it around, $i$}$s$o$w.\n"}say"$i$}$s$o$w, $i$}$s$o.";say"Go to the store and buy some more, 99$}s$s$o$w."

Using the free -M5.010 switch for "say" instead of "print".

Cheers.

\$\endgroup\$
3
  • 1
    \$\begingroup\$ I think it'd be cheaper to use last if$i<2; before the $s assignment (and change the while to while($i)) than to have the extra say in the second-to-last line. \$\endgroup\$
    – msh210
    Commented Nov 18, 2015 at 21:59
  • 2
    \$\begingroup\$ (Oh, and there's a shorter version at 99-bottles-of-beer.net/language-perl-727.html. :-)) \$\endgroup\$
    – msh210
    Commented Nov 18, 2015 at 22:04
  • \$\begingroup\$ About that link. It doesn't go to the store, as the challenge requests. But thanks for the hint, the different methods used are interesting. \$\endgroup\$
    – LukStorms
    Commented Nov 18, 2015 at 22:15
3
\$\begingroup\$

C#, 274 bytes

Direct copy from my answer on another question (it was popularity, but I just treated it as golf). Never did get around to golfing it further.

class 
P{static 
void Main(){
for(int i=99;i>0;
)System.Console.Write
(i+"{1}{2}{3}, {0}{1}{2} of beer.\n"+(i>1?
"Take one down and pass it around, {4}{1}{5}{3}.\n\n":
"Go to the store and buy some more, 99{1}s{3}."
),i--," bottle",i>0?"s":"",
" of beer on the wall"
,i,i>1?"s":""
);}}
\$\endgroup\$
3
  • \$\begingroup\$ Nice one! Why you indent like this instead of one line? \$\endgroup\$
    – aloisdg
    Commented Jul 7, 2016 at 22:50
  • \$\begingroup\$ Nevermind I guess it is just for reading. \$\endgroup\$
    – aloisdg
    Commented Jul 7, 2016 at 23:04
  • 2
    \$\begingroup\$ @aloisdg I think I made a weak attempt to shape the code in some fun way (like an arrow), but failed. \$\endgroup\$ Commented Jul 11, 2016 at 6:41
3
\$\begingroup\$

PHP, 251 bytes

This accounts for the plural/singular problem.

<?php for($i=99;$i>0;$i--){$b=" of beer";$s=" bottles$b";$r=" bottle$b";$w=" on the wall";$h=$i-1;echo$h>=1?"$i$s$w, $i$s.\nTake one down and pass it around, $h".($h<2?$r:$s)."$w.\n\n":"$i$r$w, $i$r. \nGo to the store and buy some more, 99$s$w.\n\n";}

Readable:

for ($i=99; $i > 0; $i--) {
  $b = " of beer";
  $s = " bottles$b";
  $r = " bottle$b";
  $w = " on the wall";
  $h = $i - 1;
  echo $h >= 1 ? "$i$s$w, $i$s.\nTake one down and pass it around, $h" . ($h<2 ? $r : $s) . "$w.\n\n" : "$i$r$w, $i$r. \nGo to the store and buy some more, 99$s$w. \n\n";
}
\$\endgroup\$
2
3
\$\begingroup\$

Perl 5, 159

@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+/;say$'."Go to the store and buy some more, $&"

Based on the solution from eyepopslikeamosquito found here.

Requires the free -M5.010 switch to use "say".

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

Java, 290 bytes

class C{public static void main(String[]p){String b=" bottle",s=b+"s",o=" of beer",w=o+" on the wall",c=", ",n=".\n",u;for(int i=99;i>0;){u=i>2?s:b;System.out.println(i+s+w+c+i--+s+o+n+(i>0?"Take one down and pass it around, "+i+u:"Go to the store and buy some more, 99"+b+"s")+w+n);s=u;}}}

Ungolfed:

class C {
    public static void main(String[] p) {
        String b = " bottle", s = b + "s", o = " of beer", w = o + " on the wall", c = ", ", n = ".\n", u;
        for (int i = 99; i > 0;) {
           u = i > 2 ? s : b;
           System.out.println(i + s
             + w
             + c
             + i--
             + s
             + o
             + n
             + (i > 0 ? "Take one down and pass it around, " + i + u
                 : "Go to the store and buy some more, 99" + b + "s") + w + n);
           s = u;
        }
    }
}
\$\endgroup\$
4
  • 2
    \$\begingroup\$ The leading Java solution (282) bytes does not give the correct solution. I can't comment on it as I don't have 50 reputation. Is there any way to mark incorrect solutions? \$\endgroup\$
    – Minimal
    Commented Dec 24, 2015 at 15:24
  • \$\begingroup\$ What is the issue with its output? \$\endgroup\$ Commented Dec 24, 2015 at 15:49
  • \$\begingroup\$ Output of the 282 byte solution is..... 99 bottles of beer on the wall, 99 bottles of beer. Take one down and pass it around, 99 bottles of beer on the wall. 98 bottles of beer on the wall, 98 bottles of beer. Take one down and pass it around, 98 bottles of beer on the wall. ..... 2 bottles of beer on the wall, 2 bottles of beer. Take one down and pass it around, 2 bottles of beer on the wall. 1 bottles of beer on the wall, 1 bottles of beer. Take one down and pass it around, 1 bottles of beer on the wall. Go to the store and buy some more! \$\endgroup\$
    – Minimal
    Commented Dec 31, 2015 at 10:21
  • 1
    \$\begingroup\$ Issues) 1) When the bottle is taken down and passed around the number of bottles should be one less. 2) Should be a blank line after each verse 3) When mentioning 1 bottle, should say bottle, not bottles 4) The last line should read "Go to the store and buy some more, 99 bottles of beer on the wall." \$\endgroup\$
    – Minimal
    Commented Dec 31, 2015 at 10:21
3
\$\begingroup\$

05AB1E, 167 bytes

Dammit, 167 is waaayyy too long. But this gave me some new ideas to make this a bit shorter.

Code:

99L`[D?ð?“±¥“?D1Q"s "si\ð}?“€‚¬ž€‰€€íÒ, “??ð?“±¥“?D1Q"s "si\ð}?“€‚¬ž.“,Ž“Ÿ†€µ§€€ƒî倕³†, “ª?D?ð?“±¥“?D1Q"s "si\ð}?“€‚¬ž€‰€€íÒ.
“,]“‚œ€„€€›‰€ƒŽŠ€ä€£, 99Ïꀂ¬ž€‰€€íÒ.“ª,

Try it online!

Uses CP-1252 encoding.

\$\endgroup\$
3
\$\begingroup\$

PHP, 226 221 bytes

Minified

<? $w=" on the wall";for($i=99;$i>=0;$i--){if($j&&$i)echo"Take one down and pass it around, $i$j$w.

";$j=" bottle".($i!=1?'s':'')." of beer";if($i)echo"$i$j$w, $i$j.
";}echo"Go to the store and buy some more, 99$j$w.";

Expanded

<?

  $w=" on the wall";

  for($i=99;$i>=0;$i--){
    if($j && $i)
       echo "Take one down and pass it around, $i$j$w.

";

    $j=" bottle".($i!=1?'s':'')." of beer";

    if($i)
       echo "$i$j$w, $i$j.
";
  }

  echo "Go to the store and buy some more, 99$j$w.";

https://ideone.com/m89z3E

\$\endgroup\$
6
  • 1
    \$\begingroup\$ We allow the use of the short opening tag (<?) here, though my memory is slipping as to whether or not that requires another command-line argument (and thus only saving 2 bytes rather than 3). Welcome to PPCG! \$\endgroup\$
    – user45941
    Commented Feb 24, 2016 at 9:25
  • 1
    \$\begingroup\$ @mego Short open tags are enabled by default (i.e. if there were no .ini at all) and therefore, at least in my option, should also be allowed by default. relevant meta post \$\endgroup\$
    – primo
    Commented Feb 24, 2016 at 10:09
  • \$\begingroup\$ @primo That's right, thanks for refreshing my memory. \$\endgroup\$
    – user45941
    Commented Feb 24, 2016 at 10:10
  • \$\begingroup\$ thanks for the tips! it seems that many of the php answers here include the full tags, so in the spirit of consistency, I'll leave it. But thanks for posting the meta because I wasn't sure about notices (i.e. $j is uninitialized) but it seems they're ok \$\endgroup\$
    – FuzzyTree
    Commented Feb 24, 2016 at 17:31
  • 1
    \$\begingroup\$ A good way to check is to run the script with the -n option to disable the local ini: php -n script.php. If it produces the correct output, it should be considered valid. While I'm here, you can replace the \n with a literal new line to save a few bytes. You might also be able to combine the last two echo statements with a ternary, e.g. echo$i?"string if true":"string if false";. \$\endgroup\$
    – primo
    Commented Feb 25, 2016 at 2:02
3
\$\begingroup\$

T-SQL, 436 323 bytes

WITH _ AS(SELECT 99B UNION ALL SELECT B-1 FROM _ WHERE B>2)SELECT REPLACE(REPLACE(REPLACE(A,'#',' of beer'),'@',' on the wall'),'!',' bottle')FROM(SELECT REPLACE(REPLACE('^!s#@, ^!s#.
Take one down and pass it around, %!s#@.','^',B),'%',B-1)A FROM _ UNION ALL SELECT'1!#@, 1!#.
Go to the store and buy some more, 99!s#@.')A
\$\endgroup\$
7
  • \$\begingroup\$ you can save a few more by removing a couple of spaces SELECT 99B and ,B-1)FROM. Also since ` bottles of beer` is repeated a few times a replace on that will trim a few more. \$\endgroup\$
    – MickyT
    Commented May 20, 2016 at 2:24
  • \$\begingroup\$ Some of the replacements aren't going to save anything or will actually cause it to expand since replace is so many characters... I an working on using the XML contact hack to see if I can reduce it more. \$\endgroup\$ Commented May 20, 2016 at 2:29
  • \$\begingroup\$ not sure I can save enough characters to just break even on the XML hack :( \$\endgroup\$ Commented May 20, 2016 at 2:41
  • \$\begingroup\$ fixed and added 4 bytes :( \$\endgroup\$ Commented May 20, 2016 at 3:36
  • \$\begingroup\$ Using this your byte count is 338. single byte for the line feeds \$\endgroup\$
    – MickyT
    Commented May 20, 2016 at 3:47
3
\$\begingroup\$

Scratch, 252 bytes

Script
(scoring used)
Resets, repeats for 98 cycles, then a modified last block for the last line.

If you allow "1 bottles" in the list, this can be golfed down to 133 bytes.
Script Alt

\$\endgroup\$
3
  • \$\begingroup\$ Nice Scratch answers! \$\endgroup\$
    – Riker
    Commented May 20, 2016 at 13:55
  • \$\begingroup\$ Thanks, I've been warming up to it after leaving it for a year. \$\endgroup\$ Commented May 20, 2016 at 14:03
  • \$\begingroup\$ @weatherman115 I think mine is better. Also, your output is incorrect; it's one line, the output needs to be two lines per verse. Not very golfed either \$\endgroup\$
    – anna328p
    Commented Jul 7, 2016 at 17:49
3
\$\begingroup\$

Retina, 159 bytes

Byte count assumes ISO 8859-1 encoding. The leading linefeed is significant.


99$*1 bottles of beer
1
1$'W, 1$'.¶Take one down and pass it around, $'W.¶¶
1+
$.&
G-3`
W
 on the wall
T`s``\b1 .+
.+ \B
Go to the store and buy some more, 99

Try it online!

I can't be believe I (or anyone else) have ever done this in Retina...

Explanation


99$* bottles of beer

We start by replacing the empty input with 99 1s followed by bottles of beer.

1
1$'W, 1$'.¶Take one down and pass it around, $'W.¶¶

Now we replace each of those 1s with the substitution pattern on the second line. Here $' stands for the string after each match, and we use W as a placeholder for later. Note that the occurrences of $' on the first line are "incremented" by prepending a 1. We've got most of the work done already, except that the numbers are in unary instead of decimal, and we've got Ws instead of on the walls and the last verse is off.

1+
$.&

This does the unary-to-decimal conversion by matching each run of 1s and replacing it with its length.

G-3`

This is a "grep" stage. The regex is empty, so it always matches, but the -3 limit means that only lines up to the third from the end are kept, so the last two are discarded. This gets rid of the two extraneous linefeeds at the end.

W
 on the wall

Now we substitute our W placeholder. At this point, we've got everything in place except that the last verse is still wrong.

T`s``\b1 .+

This fixes the pluralisation by removing all ss from the match of the regex at the end, which matches anything after a 1 (on the same line).

Note that the last line doesn't have 0 since the unary representation of it is an empty string it wasn't matched by the conversion stage. Instead there will be two spaces in a row.

.+ \B
Go to the store and buy some more, 99

This is the only case where there is no word boundary after a space, so we detect the line like this and replace it with the correct lyrics.

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

MIT Scratch 2.0, 184 bytes

Notes: Since this language is graphical, I counted each block/element used, each byte of the strings, and variable invocations as a byte.

Screenshot

Try it online: https://scratch.mit.edu/projects/115797194/#player

Notes: This program does not automatically clear the list after it's run, you have to go to the editor and run the following block:

clear list block

I have conveniently added it into the project, however it is not part of the program and does not count towards the byte count.

\$\endgroup\$
3
\$\begingroup\$

Ruby, 237 (thanks primo!) 227 bytes

99.downto(2){|i|puts "#{i}#{$B=" bottles of beer"}#{$W=" on the wall"}, #{i}#$B,\nTake one down and pass it around, #{i-1}#{i<3?$C=$B.tr('s','') :$B}#$W.\n\n"};puts "1#$C#$W, 1#$C,\nGo to the store and buy some more, 99#$B#$W."

Mostly ungolfed: (old, outdated)

$B="bottles of beer"
$C="bottle of beer"
$W="on the wall"
99.downto(2) { |i|
  puts "#{i} #$B #$W, #{i} #$B,\nTake one down and pass it around, #{i-1} #{i==2 ? $C : $B} #$W.\n\n"}
  puts "1 #$C #$W, 1 #$C,\nGo to the store and buy some more, 99 #$B #$W."

Try it online:

http://codepad.org/ozIpcgPI

\$\endgroup\$
3
  • \$\begingroup\$ Save a few bytes my moving each string definition to its first use: #{i}#{$B=' bottles of beer'}#{$W=' on the wall'}, and #{i-1}#{i==2?$C=' bottle of beer':$B}. \$\endgroup\$
    – primo
    Commented Jul 10, 2016 at 6:51
  • 1
    \$\begingroup\$ Alternatives for $C: $C=$B.tr('s',''), or assuming you rename $B to $_: ~/s/;$C=$`+$'. \$\endgroup\$
    – primo
    Commented Jul 10, 2016 at 7:07
  • \$\begingroup\$ Also i<3 instead of i==2 and you can put actual linefeeds in your strings instead of using the \n escape sequence. \$\endgroup\$ Commented Jul 10, 2016 at 9:32
3
\$\begingroup\$

Java 8, 245 bytes

i->{String b=" bottle",o=" of beer",n=" on the wall",t=b+o,w=b+'s'+o,e=".\n";while(--i>0)out.print(i<2?i+t+n+", "+i+t+e+"Go to the store and buy some more, 99"+w+n+e:i+w+n+", "+i+w+e+"Take one down and pass it around, "+~-i+(i<3?t:w)+n+e+'\n');}

Try it online!

Outputs the whole of the lyrics with a trailing newline at the end.

Explanation (slightly ungolfed):

i->{
/*
 * t = " bottle of beer";
 * w = " bottles of beer";
 */
String b=" bottle",o=" of beer",n=" on the wall",t=b+o,w=b+'s'+o,e=".\n";
while(--i>0) // taking advantage of input from lambda statement
    out.print(i<2?i+t+n+", "+i+t+e+"Go to the store and buy some more, 99"+w+n+e
                 :i+w+n+", "+i+w+e+"Take one down and pass it around, "+~-i+(i<3?t:w)+n+e+'\n');
/*
 * if i<2:
 *   print 'n bottles' in singular form and the final part of the lyrics through simple concatenation
 * else:
 *   print 'n bottles' in plural form plus 'n-1 bottles' through the use of the ternary operator and
 *   '~' - a unary operator that, when placed in the order above, decreases the value of i by 1
 */
}
\$\endgroup\$
3
\$\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
    Commented 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\$ Commented Jan 22, 2023 at 13:16
  • 1
    \$\begingroup\$ hmmm... then something like this should work for 578 \$\endgroup\$
    – ASCII-only
    Commented Jan 22, 2023 at 14:18
  • 1
    \$\begingroup\$ 344? (full lyrics ofc) \$\endgroup\$
    – ASCII-only
    Commented 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\$ Commented Feb 17 at 15:47
3
\$\begingroup\$

Fortran (GFortran) -cpp, 409.. 266 bytes

#define w' on the wall'
#define p i,' bottle',char(n),' of beer'
#define q print"(*(g0))"
n=115;do5 i=99,1,-1;if(i<2)n=0
if(i<99)q,'Take one down, pass it around, ',p,w,'.',new_line(w)
5 q,p,w,', ',p,'.'
n=115;i=99;q,'Go to the store and buy some more, ',p,w,'.';end

Try it online!

  • -cpp allows me to #define macros
  • macro p uses ASCII conversion, so a character variable 's' isn't needed
  • macro q formats integers nicely, so a conversion of i to string isn't needed
  • loop do5 (ungolfed do 5) loops around to label 5 two lines below
\$\endgroup\$
2
  • 1
    \$\begingroup\$ This prints "Take one down, pass it around, 1 bottles of beer on the wall" where it should say "bottle" \$\endgroup\$ Commented Jul 17 at 13:49
  • 1
    \$\begingroup\$ Oops, fixed now. \$\endgroup\$
    – roblogic
    Commented Jul 17 at 13:54
1
2
3 4 5
7

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