30
\$\begingroup\$

The goal of this code golf is to create an interpreter for the programming language HQ9+
There are 4 commands in this programming language:

  • H - prints "Hello, world!"
  • Q - prints the source code of the program
  • 9 - prints the lyrics of the song "99 bottles of beer"
  • + - increments the accumulator

The rules:

  1. Because the accumulator isn't defined, you can ignore the command +
  2. Your program should prompt for input (this input will be the source code) or the source code is read from a file
  3. You are not allowed to put the lyrics of "99 bottles of beer" in a file, and read the text from the file
  4. You are not allowed to use a compression algorithm such as GZip or BZip to compress the song text: you have to create your own compression algorithm. This doesn't have to be a complex algorithm, but try to compress the text as much as possible (remember: this is a code-golf, the code with the fewest bytes wins)
  5. If the source code contains a char that isn't H, Q, 9 or +, then output "Source code contains invalid characters" before running any commands! This means that, if the source code is H1 for example, then don't output Hello, world! before you output Source code contains invalid characters. No, immediately output Source code contains invalid characters
  6. If your interpreter accepts both uppercase and lowercase characters, then you get -8 to your character count

Good luck!

\$\endgroup\$
  • 3
    \$\begingroup\$ Rule 4 doesn't make sense at the "why would he prohibit that?!" level; rule 5 doesn't make sense at the "what does he mean?!" level. \$\endgroup\$ – Peter Taylor Oct 22 '13 at 18:44
  • 4
    \$\begingroup\$ How does rule 5 not make sense? \$\endgroup\$ – Mike C Oct 22 '13 at 19:37
  • \$\begingroup\$ I think it would be better to just require it only uses uppercase, rather than arbitrarily saying you get a free 8 characters off if it accepts either. 8 may or may not cover it depending on the language. \$\endgroup\$ – Mike C Oct 22 '13 at 22:37
  • 10
    \$\begingroup\$ Rule 5 does not make sense because it breaks the HQ9+ spec. \$\endgroup\$ – boothby Oct 23 '13 at 2:27
  • 2
    \$\begingroup\$ An interpreter in HQ9+ should automatically win. \$\endgroup\$ – TheNumberOne Jan 3 '15 at 22:51

17 Answers 17

16
\$\begingroup\$

APL (Dyalog) (326 - 8 = 318)

Paste it in an editor window and call H. Input is taken from the keyboard.

H
⎕←'Source code contains invalid characters'/⍨~∧/'HhQq9+'∊⍨V←⍞
B←' of beer'
W←' on the wall'
L←⎕TC[2]
Z←{k←' bottle','s'/⍨⍵≠1⋄⍵<0:'99',k⋄⍵=0:'No',k⋄k,⍨⍕⍵}
{⍵∊'Hh':⎕←'Hello, world!'
⍵∊'Qq':⎕←V
⍵∊'9':{⎕←G,K,(G←Z⍵),B,L,(⊃'Go to the store and buy some more' 'Take one down and pass it around'[1+×⍵]),L,(Z⍵-1),K←B,W,L}¨1-⍨⌽⍳100}¨V
\$\endgroup\$
6
\$\begingroup\$

Mathematica, 349 346 341 chars

h = If[StringMatchQ[#, ("H" | "Q" | "9" | "+") ...], 
   b = If[# > 0, ToString@#, "No"] <> " bottle" <> 
      If[# == 1, "", "s"] <> " of beer" &; 
   w = b@# <> " on the wall" &; 
   Print /@ 
    StringCases[#, {"H" -> "Hello, world!", "Q" -> #, 
      "9" -> "" <> 
        Table[w@n <> ",\n" <> b@n <> 
          ".\ntake one down, pass it around,\n" <> w[n - 1] <> 
          If[n == 1, ".", ".\n\n"], {n, 99, 1, -1}]}];, 
   "Source code contains invalid characters"] &
\$\endgroup\$
  • \$\begingroup\$ Very impressive but there appear to be some minor gliches.'h["Q"] outputs "Q". h["9"] works, but h[9] outputs the whole program (I don't understand how.) \$\endgroup\$ – DavidC Oct 23 '13 at 15:18
  • \$\begingroup\$ @DavidCarraher It outputs code due to how StringMatchQ and If fails: If[StringMatchQ[3, "a"], x, y] \$\endgroup\$ – ssch Oct 23 '13 at 18:01
6
\$\begingroup\$

C, 599 587 487 481 467 characters

I'm sure this can be beaten. I'm using C after all. There's a reason you don't see a lot of winning golf entries written in C. This is now 467 characters thanks to criminal abuse of #define.

Specify the HQ9+ source file as a command line argument.

EDIT: Now accepts the source from stdin, not a file. Start the program, start typing the code, CTRLC when done to run it.

It compiles at least in MinGW/GCC with: gcc -o hq9+.exe hq9+.c

Should work in MSVC, but I didn't feel like creating a whole solution just for this. :)

#define B "bottles of beer"
#define C case
#define P printf
#define R break
a,i,j,s;
main(){
char p[9999],c;
for(;;) {
    switch(c=getch()){
    C 'H':C 'Q':C '9':C '+': C 3: R;
    default:P("Source code contains invalid characters\n");
    }
    if (c==3) R;
    p[s++]=c;
}
for(i=0;i<s;i++){
    c = p[i];
    switch(c){
    C 'H':
        P("Hello world!");
        R;
    C 'Q':
        for(j=0;j<s;j++)putchar(p[j]);
        R;
    C '9':
        j=99;
        while(j){
            P("%d "B" on the wall,\n%d "B".\nTake one down, pass it around,\n%d "B".\n",j,j,j-1);
            j--;
        }
        R;
    C '+':
        a++;
    }
}
}

Or:

#define B "bottles of beer"
#define C case
#define P printf
#define R break
a,i,j,s;main(){char p[9999],c;for(;;){switch(c=getch()){C 'H':C 'Q':C '9':C '+': C 3: R;default:P("Source code contains invalid characters\n");}if (c==3) R;p[s++]=c;}for(i=0;i<s;i++){c = p[i];switch(c){C 'H':P("Hello world!");R;C 'Q':for(j=0;j<s;j++)putchar(p[j]);R;C '9':j=99;while(j){P("%d "B" on the wall,\n%d "B".\nTake one down, pass it around,\n%d "B".\n",j,j,j-1);j--;}R;C '+':a++;}}}
\$\endgroup\$
  • \$\begingroup\$ Got it down to 487 characters by using stdin instead of a file for input. \$\endgroup\$ – Mike C Oct 22 '13 at 19:36
  • \$\begingroup\$ Make that 464 by abusing #define. \$\endgroup\$ – Mike C Oct 22 '13 at 19:47
  • \$\begingroup\$ You could get rid of spaces, like the ones after C and a couple before R;. Could also shave off 8 chars with p[s++]=0; after the input loop to simplify printing p. \$\endgroup\$ – Daniel Lubarov Oct 23 '13 at 5:34
3
\$\begingroup\$

Python 2 - 452 453 443 bytes

q=raw_input()
if set(q)-set('HQ9+'):print'Source code contains invalid characters'
b=' bottles of beer'
b=[b,b.replace('s','')]
w=[a+' on the wall'for a in b]
t='Take one down, pass it around,\n'
c={'H':'Hello, world!','Q':q,'9':''.join(`i`+w[i<2]+',\n'+`i`+b[i<2]+'.\n'+t+(`i`if i>1 else'No')+w[0]+'.\n'for i in range(1,100)[::-1])+'No'+w[0]+',\nNo'+b[0]+'.\n'+'Go to the store, buy some more,\n99'+w[0]+'.'}
for d in q:
 if d in c:print c[d]

Bleh. It was already bleh, but then I discovered a bug that cost me a byte to fix. Bleh.

(I used to not include + in the output for Q. An input of Q+++ gave the output Q, for example.)

Saved some characters by not exiting on invalid input like I thought you were supposed to.

Explained version coming shortly. I gave up. I barely even understand how this monstrosity works any more. If there's demand I'll give it another go but until then it's not happening.

\$\endgroup\$
  • \$\begingroup\$ You don't exit on the invalid character, just display the message, so you can save 7 chars there. \$\endgroup\$ – Kyle Kanos Jun 19 '14 at 16:48
  • \$\begingroup\$ @KyleKanos Oh, I misunderstood that. Thanks! \$\endgroup\$ – undergroundmonorail Jun 19 '14 at 16:57
  • \$\begingroup\$ Your lyrics aren't right- currently it says: 99 bottles of beer on the wall, 99 bottles of beer. Take one down, pass it around, *99* bottles of beer on the wall. It's supposed to say: 99 bottles of beer on the wall, 99 bottles of beer. Take one down, pass it around, *98* bottles of beer on the wall. \$\endgroup\$ – Oliver Ni Jan 4 '15 at 17:28
  • \$\begingroup\$ It also gives an error when I enter +. You're supposed to ignore +, not give an error \$\endgroup\$ – Oliver Ni Jan 4 '15 at 17:31
  • \$\begingroup\$ Also there's not supposed to be anything between outputs. Like if I enter HQHH it should say Hello World!HQHHHelloWorld!Hello World! \$\endgroup\$ – Oliver Ni Jan 4 '15 at 17:33
3
\$\begingroup\$

Perl, 325 - 8 = 317

sub p{print@_}$_=<>;$a=' on the wall';$b=' bottle';$e=' of beer';$c='Take one down, pass it around';if(!/^[hqHQ9+]+$/){p"Source code contains invalid characters";exit}$k=$_;for(/./g){/H|h/&&p"Hello, World!";if(/9/){$i=99;$d=$b."s$e";while($i>0){p"$i$d$a
$i$d
$c
";$q=--$i==1?'':'s';$d="$b$q$e";$i||=No;p"$i$d$a
"}}/Q|q/&&p$k}

Expanded:

sub p{print@_}
$_=<>;
$a=' on the wall';
$b=' bottle';
$e=' of beer';
$c='Take one down, pass it around';
if(!/^[hqHQ9+]+$/){
    p"Source code contains invalid characters";
    exit
}
$k=$_;
for(/./g){
    /H|h/&&p"Hello, World!";
    if(/9/){
        $i=99;
        $d=$b."s$e";
        while($i>0){
            p"$i$d$a
$i$d
$c
";
            $q=--$i==1?'':'s';
            $d="$b$q$e";
            $i||=No;
            p"$i$d$a
"
        }
    }
    /Q|q/&&p$k
}
\$\endgroup\$
2
\$\begingroup\$

Ruby, 364 360 - 8 = 352

Still has lots of room for improvement. 99 bottles code stolen from here.

p=gets.upcase
if p=~/[^HQ9+
]/
puts"Source code contains invalid characters"else
p.each_char{|x|case x
when ?H
puts"Hello, world!"
when ?Q
puts p
when ?9
def c;"#{$n} bottle#{'s'if$n>1} of beer on the wall"end
($n=99).times{puts"#{c}, #{c[0..-13]}.
#{$n<2?"Go to the store and buy some more":"Take one down and pass it around"}, #{$n=($n-2)%99+1;c}.

"}end}end
\$\endgroup\$
  • \$\begingroup\$ There's supposed to be No more bottles of beer on the wall after the 1 bottle section. then it's Go to the store... \$\endgroup\$ – Oliver Ni Jan 4 '15 at 17:44
  • \$\begingroup\$ 1 bottle of beer on the wall, 1 bottle of beer on the wall, take one down, pass it around, no more bottles of beer on the wall. No more bottles of beer on the wall, no more bottles of beer, go to the store, buy some more, 99 bottles of beer on the wall. \$\endgroup\$ – Oliver Ni Jan 4 '15 at 17:46
  • \$\begingroup\$ @Oliver The question does not specify the exact lyrics of the song. In no place does it require the "no more" section to be included. \$\endgroup\$ – Doorknob Jan 4 '15 at 17:46
  • \$\begingroup\$ Ok, but also, there's not supposed to be a newline between outputs. \$\endgroup\$ – Oliver Ni Jan 4 '15 at 17:47
  • \$\begingroup\$ @Oliver Where does the challenge say that? \$\endgroup\$ – Doorknob Jan 4 '15 at 17:47
2
\$\begingroup\$

Haskell, 298

main=interact g
g s|all(`elem`"HQ9+")s=s>>=(%s)|0<1="Source code contains invalid characters"
'H'%_="hello World!"
'Q'%s=s
'9'%_=drop 32$d 99
_%_=""
k=" bottles of beer on the wall"
d 0="No more"++k++"."
d n|a<-shows n k=[a,".\n\n",a,",\n",take 18a,".\ntake one down, pass it around\n",d$n-1]>>=id

this is quite straightforward. % returns a command's output (given the source cod for use on Q). d returns the 99 bottles song with a junk line at the start for golfing reasons. everything is wrapped in an interact (you might want to use g instead of main when testing)

\$\endgroup\$
  • \$\begingroup\$ How should I run it? I tried pasting it in WinGHCi, but I get <interactive>:8:5: parse error on input ‘=’. \$\endgroup\$ – ProgramFOX Jan 2 '15 at 8:47
  • \$\begingroup\$ @ProgramFOX i have no idea... Ill check when i can \$\endgroup\$ – proud haskeller Jan 2 '15 at 9:20
  • \$\begingroup\$ @ProgramFOX sorry it took long :-). you're not using GHCi properly. in GHCi you can't irectly input definitions as it expects expressions. you can either use a let to insert definitions (this breaks when inserting multiple definitions not separated by ;) or run the code from a file. \$\endgroup\$ – proud haskeller Jan 4 '15 at 20:13
  • \$\begingroup\$ I see. I still fail at trying to run it though; when I load the file and call main, it says that the source code contains invalid characters when I try to execute H. When I try to run g instead of main, it immediately gives an error. \$\endgroup\$ – ProgramFOX Jan 5 '15 at 16:46
  • \$\begingroup\$ @ProgramFOX main doesn't work because the newline appended is not a legal command. That's why i recommended using g instead. As for g, does putStrLn $ g "H" not work properly? \$\endgroup\$ – proud haskeller Jan 5 '15 at 17:15
2
\$\begingroup\$

J - 444 bytes

I liked the number, so stopped golfing. Here 'ya go, single expression function!

f=:'Source code contains invalid characters'"_`('Hello, world!'"_`[`((((s=:(<:@[s],L,(v,LF,'Take one down and pass it around, '"_,b@<:,' of beer on the wall.'"_)@[,''"_)`(],(L=:LF,LF),(v=:1&b,' of beer on the wall, '"_,b,' of beer.'"_)@[)@.([<1:))''"_),LF,'Go to the store and buy some more, '"_,(b=:({&'Nn'@([=0:),'o more'"_)`(":@])@.(]>0:),{.&' bottles'@(8:-=&1@])),' of beer on the wall.'"_)@(99"_))`]@.('HQ9+'&i.@])"0 1 0])@.(*./@e.&'HQ9+')

Examples:

   hq9 '9QHHQ+'
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.

...

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

No more bottles of beer on the wall, no more bottles of beer.
Go to the store and buy some more, 99 bottles of beer on the wall.
9QHHQ+
Hello, world!
Hello, world!
9QHHQ+
+
   hq9 '9QHHaQ'
Source code contains invalid characters
\$\endgroup\$
  • \$\begingroup\$ And no, I won't write an explanation for this. Too long. \$\endgroup\$ – seequ Jun 20 '14 at 12:38
  • \$\begingroup\$ Wait, why is this downvoted? \$\endgroup\$ – seequ Mar 20 '15 at 18:41
2
\$\begingroup\$

Fortran 528 470 481

It requires compiling with -fpp flag (+3 to score)1 to use preprocessing directives (which saves way more than 3 chars, so I'm totally okay with that). It is also case insensitive, so there's -8 :D. Saved 5 chars by not preprocessing the endif as that is used once anyway.

Requires the file to have a .F90 extension (makes sense to call it hq9+.F90) so that the compiler forces preprocessing. The code is case-sensitive; making it case-insensitive adds something like 16 characters, so it's not really worth it to save 8 characters. My previous answer did not account for the changing plurals in bottles for 9; this version corrects it (and sadly adds a lot more characters).

#define P print*,
#define W " of beer on the wall"
#define N print'(x,i0,a,a)',
#define e enddo
#define S case
#define J len(trim(c))
character(len=99)::c,b=" bottles";read*,c;do i=1,J;if(all(c(i:i)/=["H","Q",'9',"+"])) then;P"Source code contains invalid characters";exit;endif;e;do i=1,J;select S(c(i:i));S("H");P"Hello, world!";S("Q");P c;S("9");l=8;do k=99,1,-1;N k,b(1:l),W;N k,b(1:l)," of beer";P "Take one down, pass it around";if(k==2)l=l-1;if(k==1)exit;N k-1,b(1:l),W;P"";e;P"No more",trim(b),W;S default;endselect;e;end

Looks a lot better ungolfed & non-preprocessed (probably because you can see what's going on):

program hq9
   character(len=99)::c,b=" bottles"
   read*,c
   do i=1,len(trim(c))
! change the below to ["H","h","Q","q","9","+"] to be case-insensitive
      if(all(c(i:i)/=["H","Q","9","+"]))then
         print*,"Source code contains invalid characters"
         exit
      endif
   enddo
   do i=1,len(trim(c))
      select case(c(i:i))
        case("H")                ! change to case("H","h") for case-insensitive
           print*,"Hello, world!"
        case("Q")                ! change to case("Q","q") for case-insensitive
           print*, c
        case("9")
           l=8
           do k=99,1,-1
              print'(x,i0,a,a)', k,b(1:l)," of beer on the wall"
              print'(x,i0,a)', k,b(1:l)," of beer"
              print*,"Take one down, pass it around"
              if(k==2) l=l-1
              if(k==1) exit
              print'(x,i0,a)', k-1,b(1:l)," of beer on the wall"
              print*,""
           enddo
           print*,"No more",trim(b)," of beer on the wall"
        case default
           ! do nothing
      endselect
   enddo
end program hq9
\$\endgroup\$
  • \$\begingroup\$ I think it's acceptable to require a specific extension to let the compiler know what it's compiling. An analog might be requiring a C++ file to be named .cc to avoid needing the -lstdc++ flag. \$\endgroup\$ – primo Jun 19 '14 at 15:26
2
\$\begingroup\$

Python 2, 340 - 8 = 332

s,o=raw_input(),''
for z in s:
 if z in'Hh':o+='Hello World!'
 elif z in'Qq':o+=s
 elif'9'==z:
  i=298
  while~-i:print i/3or 99,'bottle'+'s of beer on the wall.\n'[2<i<6:9+i%3*12]+'..\nGToa kteo  otnhee  dsotwonr,e ,p absusy  isto maer omuonrde,,'[(i>3)+i%3*68::2];i-=1
 elif'+'!=z:o='Source code contains invalid characters';break
print o
\$\endgroup\$
1
\$\begingroup\$

Lua 443 - 8 = 435 464 - 8 = 456

I managed to save 21 characters by using multiple if-ends instead of if-elseif-end. I also had some extra white spaces floating around after a few ).

p=print o=" of beer"t=" on the wall"B=" bottle"b=" bottles"l=io.read("*l");g=l:lower()if g:match"[^hq9+]"then p("Source code contains invalid characters")end for i=1,#g do s=g:sub(i,i)if s=='h'then p("Hello, world")end if s=='q'then p(l)end if s=='9'then n=99 repeat p(n..b..o..t..", "..n..b..o)n=n-1 p("Take one down, pass it around, "..n..b..o..t..".")p()until n==1 p("1"..B..o..t..", 1"..B..o)p("No more"..b..o..t..", no more"..b..o)end end

I'm fairly happy with this one, even though it's not much shorter than my Fortran answer. The 99 bottles of beer code has been modified from this answer by Alessandro. Ungolfed, we have

-- reuse stuff
p=print
o=" of beer"
t=" on the wall"
B=" bottle"
b=" bottles"
-- read the line & then lowercase it for case insensitivity
l=io.read("*l");g=l:lower()
if g:match"[^hq9+]" then -- horray for basic regex
   p("Source code contains invalid characters")
end
for i=1,#g do
   s=g:sub(i,i)               -- take substring
   if s=='h' then p("Hello, world") end
   if s=='q' then p(l) end
   if s=='9' then
      n=99
      repeat
         p(n..b..o..t..", "..n..b..o)
         n=n-1
         p("Take one down, pass it around, "..n..b..o..t..".")
         p()
      until n==1
      p("1"..B..o..t..", 1"..B..o)
      p("No more"..b..o..t..", no more"..b..o)
   end
end
\$\endgroup\$
  • \$\begingroup\$ The code to lowercase the input costs 12 characters, so the bonus is not worth it. \$\endgroup\$ – nyuszika7h Jan 3 '15 at 19:21
  • \$\begingroup\$ You don't need to lowercase the input, you can just do something like if s in'Hh' \$\endgroup\$ – Oliver Ni Jul 30 '15 at 4:40
  • \$\begingroup\$ It saves 8 chars \$\endgroup\$ – Oliver Ni Jul 30 '15 at 4:41
  • \$\begingroup\$ @MCParadox: Er, that's Python you're thinking of, Lua's in is for for-loop iterators and not comparisons; you'll get an error if you stick that in there. There are three choices here: (1) use match at each line, (2) user s=='h'or s=='H' at each line, (3) do it the way I did it. Clearly (3) is shorter than (1) and (2). \$\endgroup\$ – Kyle Kanos Aug 3 '15 at 14:49
1
\$\begingroup\$

Julia, 362

s = chomp(readline(STDIN))
l=""
z=" of beer"
q,r,w,t=" bottles$z"," bottle$z"," on the wall.\n","take one down, pass it around,\n"
for j=99:-1:2
b="$j$q"
l*="$b$w$b.\n$t"
end
l*="1$r$(w)1$r.\n$(t)No$q$w"
p=println
all(c->c in "HQ9+", s)||p("Source code contains invalid characters")
for c in s
    c=='Q'&&p(s)
    c=='H'&&p("Hello, world!")
    c=='9'&&p(l)
end
\$\endgroup\$
1
\$\begingroup\$

Tcl, 515

set d [read stdin]
if {![regexp {^[hq9\+HQ]*$} $d]} {puts "Source code contains invalid characters"}
lmap c [split $d {}] {set b { bottles of beer}
switch -- $c H {puts "Hello, world"} Q {puts $d} 9 {for {set i 99} {$i>2} {incr i -1} {puts "$i$b on the wall,
$i$b.
Take one down, pass it around,
[expr $i-1]$b on the wall.
"}
puts "2$b on the wall,
2$b.
Take one down, pass it around,
1 bottle of beer on the wall.

1 bottle of beer on the wall,
1 bottle of beer.
Take one down, pass it around,
No$b on the wall."}}

Just a bit golfed, still smaller than C and the correct 99 Bottles of beer end verse.

\$\endgroup\$
  • \$\begingroup\$ Okay, now that one's legit. Good job, you beat me. I should stop using C for golf. \$\endgroup\$ – Mike C Oct 22 '13 at 19:00
  • \$\begingroup\$ You can remove the first line and embed it at the last parameter of regexp. You can get my golfing of 99 beers on codegolf.stackexchange.com/a/109818/29325 \$\endgroup\$ – sergiol Oct 28 '17 at 23:34
1
\$\begingroup\$

JavaScript (ES6), 385

s=>{n=99,b=' bottle',j=' of beer',d=' on the wall',e='Take one down, pass it around',k='Go to the store, buy some more',l='No',o='s',f=', ';for(i=s.split(m=v='');~n;)v+=[n||l,b,n-1?o:m,j,d,f,n||l,b,n-1?o:m,j,f,n?e:k,f,--n<1?99:n,b,n-1?o:m,j,d,'! '].join(m);return s.match(/[^HQ9\+]/,r='')?'Source code contains invalid characters':[...s].map(c=>({H:'Hello World!',9:v,Q:s})[c]).join``}

Didn't opt for the case-insensitive, would have cost too many characters. Not even close to some of the other entries, but was fun nonetheless!

JavaScript, 344

I made a version with a less complete version of the song:

(function(i){if(s.match(/[^HQ9\+]/)){m='Source code contains invalid characters'}else{n=99,b=' bottles of beer ',d='on the wall',e='take one down, pass it around',f=', ';for(;n;)v+=[n,b,d,f,n,b,f,e,f,--n||'no more',b,d,'! '].join(m);h={H:'Hello World!',Q:arguments.callee+m,9:v};for(;c=i[n++];)m+=h[c]||''}alert(m)})((s=prompt()).split(m=v=''))

but after seeing the other entries (and looking at the actual lyrics, who knew!), I thought it was a bit of a cop out!

\$\endgroup\$
  • \$\begingroup\$ It should be Go to the store, buy some more, *99* bottles of beer on the wall, not *No more* bottles of beer on the wall \$\endgroup\$ – Oliver Ni Jan 4 '15 at 17:35
  • \$\begingroup\$ If you buy some more, how can there still be no more? \$\endgroup\$ – Oliver Ni Jan 4 '15 at 17:36
  • \$\begingroup\$ And also, Q doesn't work for me. \$\endgroup\$ – Oliver Ni Jan 4 '15 at 17:39
  • \$\begingroup\$ @Oliver didn't notice you comment on this back then... I totally misunderstood when the Q was supposed to do that the time and provided the source code of the function instead of the input... That was originally my reason for doing it in JS, that part would be easy, however I now know that isn't what's required at all! \$\endgroup\$ – Dom Hastings Nov 15 '16 at 17:03
  • \$\begingroup\$ @Oliver updated the lyrics! \$\endgroup\$ – Dom Hastings Nov 15 '16 at 17:23
1
\$\begingroup\$

C, 562 bytes

char*c="%1d %3$s of %4$s on the %5$s, %1d %3$s of %4$s.\n\0Take one down and pass it around, %2d %3$s of %4$s on the %5$s.\n\0Go to the store and buy some more, %1d %3$s of %4$s on the %5$s.\n";main(int a,char**b){int x=0;for(int i=0;i<strlen(b[1]);i++){if(b[1][i]=='+')x++;else if(b[1][i]=='9'){int k=99;while(1){printf(&c[0],k,k,k==1?"bottle":"bottles","beer","wall");if(k!=1){printf(&c[49],k,k,"bottles","beer","wall");k--;}else{k=99;printf(&c[114],k,k,"bottles","beer","wall");break;}}}else printf("%s",b[1][i]=='H'?"Hello, world!":(b[1][i]=='Q'?b[1]:""));}}

As a full program. First argument is the HQ9+ program. With an actual accumulator. I challanged myself not to use define statements.

Ungolfed version:

char* c = "%1d %3$s of %4$s on the %5$s, %1d %3$s of %4$s.\n\0Take one down and pass it around, %2d %3$s of %4$s on the %5$s.\n\0Go to the store and buy some more, %1d %3$s of %4$s on the %5$s.\n";
main (int a, char** b) {
    int x = 0;
    for (int i = 0; i < strlen(b[1]); i++) {
        if (b[1][i] == '+')
            x++;
        else if (b[1][i] == '9') {
            int k = 99;
            while (1) {
                printf(&c[0], k, k, k == 1 ? "bottle" : "bottles", "beer", "wall");
                if (k != 1) {
                    printf(&c[49], k, k, "bottles", "beer", "wall");
                    k--;
                } else {
                    k=99;
                    printf(&c[114], k, k, "bottles", "beer", "wall");
                    break;
                }
            }
        } else
            printf("%s",b[1][i] == 'H' ? "Hello, world!" : (b[1][i] == 'Q' ? b[1] : ""));
    }
}
\$\endgroup\$
0
\$\begingroup\$

Java, 546 bytes

This is my first code golf submission. I am sure we could do more with it. It reads the input as the command line argument. Beer code "borrowed" from "99 Bottles of Beer" java answer (creative commons)

class a{public static void main(String[] a){if(a[0].matches("^[HQ9\\Q+\\E]+$")){for(char c:a[0].toCharArray()){if(c=='H')p("Hello, world!");if(c=='Q')p(a[0]);if(c=='9')b();}}else{System.out.println("Source code contains invalid characters");}}static void p(String s){System.out.println(s);}static void b(){String b=" of beer",c=" on the wall",n=".\n",s;for(int i=100;i-->1;){s=" bottle"+(i>1?"s":"");p(i+s+b+c+", "+i+s+b+n+(i<2?"Go to the store and buy some more, 99":"Take one down and pass it around, "+(i-1))+" bottle"+(i!=2?"s":"")+b+c+n);}}}

Let me know if command line args is not acceptable. This was a lot of fun!

\$\endgroup\$
  • \$\begingroup\$ CLA are fine. Nice answer. \$\endgroup\$ – Rɪᴋᴇʀ Mar 14 '16 at 0:07
0
\$\begingroup\$

Excel VBA, 489 Bytes

Unindented:

Sub h(s)
For c=1 To Len(s)
Select Case Mid(s,c,1)
Case "H"
Debug.? "Hello,World!"
Case "Q"
Set v=ActiveWorkbook.VBProject.VBComponents("M").CodeModule:For n=1 To v.countoflines:Debug.? v.Lines(n,1):Next
Case "+"
a=a+1
Case "9"
s=" Bottles of Beer":o=" Bottle of Beer":w=" on the wall":t=". Take 1 down pass it around,":p=",":d=".":For n=99 To 3 Step -1:Debug.? n;s;w;p;n;s;t;n-1;s;w;d:Next:Debug.? 2;s;w;p;2;s;t;1;o;w;d:Debug.? 1;o;w;p;1;o;t;"No";s;w;d
End Select
Next
End Sub

(indented for readability)

Sub h(s)
For c=1 To Len(s)
Select Case Mid(s,c,1)
    Case "H"
        Debug.? "Hello,World!"
    Case "Q"
        Set v=ActiveWorkbook.VBProject.VBComponents("M").CodeModule
        For n=1 To v.countoflines
            Debug.? v.Lines(n,1)
        Next
    Case "+"
        a=a+1
    Case "9"
        s=" Bottles of Beer"
        o=" Bottle of Beer"
        w=" on the wall"
        t=". Take 1 down pass it around,"
        p=","
        d="."
        For n=99 To 3 Step -1
            Debug.? n;s;w;p;n;s;t;n-1;s;w;d
        Next
        Debug.? 2;s;w;p;2;s;t;1;o;w;d
        Debug.? 1;o;w;p;1;o;t;"No";s;w;d
End Select
Next
End Sub

Rename default Module1 to M
call with h "+++++++++Your Code"
This will also work on other office applications, changing ActiveWorkbook to the appropriate document type

\$\endgroup\$
  • \$\begingroup\$ note that excel will add spaces, and expand abbreviations, so within excel, the code is 531 chars \$\endgroup\$ – SeanC Oct 23 '13 at 19:51
  • \$\begingroup\$ Thats 636 Characters \$\endgroup\$ – Oliver Ni Jan 4 '15 at 17:03
  • \$\begingroup\$ Remove all the whitespace from my indentation, @oliver \$\endgroup\$ – SeanC Jan 4 '15 at 18:39
  • \$\begingroup\$ You can loose 3 Bytes by converting all instances of For n=1 To to For n=1To (removing whitespace before To) and 4 bytes by converting all instances of Debug.? 2; to Debug.?2; (removing whitespace after ? keyword) \$\endgroup\$ – Taylor Scott May 31 '17 at 16:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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