Creating a HQ9+ interpreter

31

6

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!

ProgramFOX

Posted 2013-10-22T16:56:03.910

Reputation: 8 017

2An interpreter in HQ9+ should automatically win. – TheNumberOne – 2015-01-03T22:51:39.770

No because it doesn't accept input – Oliver Ni – 2015-01-04T17:25:34.393

4Rule 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. – Peter Taylor – 2013-10-22T18:44:27.590

4How does rule 5 not make sense? – Mike C – 2013-10-22T19:37:31.910

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. – Mike C – 2013-10-22T22:37:53.837

12Rule 5 does not make sense because it breaks the HQ9+ spec. – boothby – 2013-10-23T02:27:25.933

@boothby: OK, I removed that rule. – ProgramFOX – 2013-10-23T15:26:16.243

@MikeC, it said what you must not do, but it wasn't clear to me that it fully stated what you must do. – Peter Taylor – 2013-10-23T20:06:19.627

do I get extra points if it accepts uppercase +? – 12Me21 – 2017-02-11T16:49:32.443

Answers

16

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

marinus

Posted 2013-10-22T16:56:03.910

Reputation: 30 224

6

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++;}}}

Mike C

Posted 2013-10-22T16:56:03.910

Reputation: 1 169

Got it down to 487 characters by using stdin instead of a file for input. – Mike C – 2013-10-22T19:36:03.110

Make that 464 by abusing #define. – Mike C – 2013-10-22T19:47:56.177

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. – Daniel Lubarov – 2013-10-23T05:34:30.327

6

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"] &

alephalpha

Posted 2013-10-22T16:56:03.910

Reputation: 23 988

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.) – DavidC – 2013-10-23T15:18:27.307

@DavidCarraher It outputs code due to how StringMatchQ and If fails: If[StringMatchQ[3, "a"], x, y] – ssch – 2013-10-23T18:01:18.263

3

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.

undergroundmonorail

Posted 2013-10-22T16:56:03.910

Reputation: 5 897

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. – Oliver Ni – 2015-01-04T17:28:43.930

It also gives an error when I enter +. You're supposed to ignore +, not give an error – Oliver Ni – 2015-01-04T17:31:31.603

Also there's not supposed to be anything between outputs. Like if I enter HQHH it should say Hello World!HQHHHelloWorld!Hello World! – Oliver Ni – 2015-01-04T17:33:49.563

You don't exit on the invalid character, just display the message, so you can save 7 chars there. – Kyle Kanos – 2014-06-19T16:48:14.597

@KyleKanos Oh, I misunderstood that. Thanks! – undergroundmonorail – 2014-06-19T16:57:02.577

3

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
}

faubi

Posted 2013-10-22T16:56:03.910

Reputation: 2 599

2

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

Doorknob

Posted 2013-10-22T16:56:03.910

Reputation: 68 138

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... – Oliver Ni – 2015-01-04T17:44:38.540

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. – Oliver Ni – 2015-01-04T17:46:10.350

@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. – Doorknob – 2015-01-04T17:46:41.053

Ok, but also, there's not supposed to be a newline between outputs. – Oliver Ni – 2015-01-04T17:47:04.383

@Oliver Where does the challenge say that? – Doorknob – 2015-01-04T17:47:38.880

Well, ProgramFOX said that to me. – Oliver Ni – 2015-01-04T17:48:34.283

2

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

Kyle Kanos

Posted 2013-10-22T16:56:03.910

Reputation: 4 270

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. – primo – 2014-06-19T15:26:42.717

2

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

seequ

Posted 2013-10-22T16:56:03.910

Reputation: 1 714

Wait, why is this downvoted? – seequ – 2015-03-20T18:41:14.803

And no, I won't write an explanation for this. Too long. – seequ – 2014-06-20T12:38:44.183

2

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

Oliver Ni

Posted 2013-10-22T16:56:03.910

Reputation: 9 650

2

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)

proud haskeller

Posted 2013-10-22T16:56:03.910

Reputation: 5 866

How should I run it? I tried pasting it in WinGHCi, but I get <interactive>:8:5: parse error on input ‘=’. – ProgramFOX – 2015-01-02T08:47:37.103

@ProgramFOX i have no idea... Ill check when i can – proud haskeller – 2015-01-02T09:20:10.047

@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. – proud haskeller – 2015-01-04T20:13:46.893

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. – ProgramFOX – 2015-01-05T16:46:55.490

@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? – proud haskeller – 2015-01-05T17:15:17.520

That works indeed. I was using the wrong way to execute the HQ9+ commands. Thanks for explaining me! – ProgramFOX – 2015-01-05T17:16:56.187

1

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] : ""));
    }
}

cookie

Posted 2013-10-22T16:56:03.910

Reputation: 271

437 bytes – ceilingcat – 2020-01-17T23:29:02.907

1

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.

Johannes Kuhn

Posted 2013-10-22T16:56:03.910

Reputation: 7 122

Okay, now that one's legit. Good job, you beat me. I should stop using C for golf. – Mike C – 2013-10-22T19:00:40.537

You can remove the first line and embed it at the last parameter of regexp. You can get my golfing of 99 beers on https://codegolf.stackexchange.com/a/109818/29325

– sergiol – 2017-10-28T23:34:26.267

1

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!

Dom Hastings

Posted 2013-10-22T16:56:03.910

Reputation: 16 415

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 – Oliver Ni – 2015-01-04T17:35:46.780

If you buy some more, how can there still be no more? – Oliver Ni – 2015-01-04T17:36:07.463

And also, Q doesn't work for me. – Oliver Ni – 2015-01-04T17:39:02.213

@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! – Dom Hastings – 2016-11-15T17:03:33.637

@Oliver updated the lyrics! – Dom Hastings – 2016-11-15T17:23:16.990

1

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

gggg

Posted 2013-10-22T16:56:03.910

Reputation: 1 715

1

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

Kyle Kanos

Posted 2013-10-22T16:56:03.910

Reputation: 4 270

The code to lowercase the input costs 12 characters, so the bonus is not worth it. – nyuszika7h – 2015-01-03T19:21:52.167

You don't need to lowercase the input, you can just do something like if s in'Hh' – Oliver Ni – 2015-07-30T04:40:59.057

It saves 8 chars – Oliver Ni – 2015-07-30T04:41:31.623

@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).

– Kyle Kanos – 2015-08-03T14:49:23.520

1

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!

Rohan Jhunjhunwala

Posted 2013-10-22T16:56:03.910

Reputation: 2 569

CLA are fine. Nice answer. – Rɪᴋᴇʀ – 2016-03-14T00:07:28.617

0

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

SeanC

Posted 2013-10-22T16:56:03.910

Reputation: 1 117

Thats 636 Characters – Oliver Ni – 2015-01-04T17:03:53.647

Remove all the whitespace from my indentation, @oliver – SeanC – 2015-01-04T18:39:52.693

note that excel will add spaces, and expand abbreviations, so within excel, the code is 531 chars – SeanC – 2013-10-23T19:51:33.447

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) – Taylor Scott – 2017-05-31T16:46:33.380

0

Kotlin, 1223

import java.io.File

class Main(private val Path: String) {
private fun read(path: String = Path): ArrayList<Char>{
    val fr = File(path)
    val arr = ArrayList<Char>()
    fr.forEachLine{for(i in 0 until fr.length()){arr.add(it[i.toInt()])}}
    return arr
}

private fun interpret(arr: ArrayList<Char>): ArrayList<String>{
    val output = ArrayList<String>()
    var acc=0
    for(i in 0 until arr.size){
        when(arr.get(i)){
            'H' -> output.add("Hello World")
            'Q' -> for(i in 0 until arr.size) output.add(arr[i].toString())
            '9' -> for(i in 99 downTo 1){
                if(i-1 == 0 || i==0){
                    output.add("$i bottles of beer\nyou take one down, pass it around,\nno more bottles of beer on the wall.")
                } else
                    output.add("$i bottles of beer\nyou take one down, pass it around,\n${i-1} bottles of beer on the wall.\n")
            }
            '+' -> acc++
        }
    }
    return output
}

fun main(path: String = Path){
    val arr = interpret(read(path))
    for(i in 0 until arr.size) print(arr.get(i))
}
}

Prettified version. Reads from a file. Is actually runnable.

Timotej Leginus

Posted 2013-10-22T16:56:03.910

Reputation: 9

2Welcome to the site. I don't know kotlin but certainly you could use shorter variable names or not use so much whitespace. – Post Rock Garf Hunter – 2020-01-17T22:27:18.640

Is prettified..... – Timotej Leginus – 2020-01-18T07:00:48.367

You should probably have the original version is the answer. – Post Rock Garf Hunter – 2020-01-18T13:36:42.450

What??????????? – Timotej Leginus – 2020-01-18T14:52:45.327

Is the code in your answer the code you are submitting? If so then you should shorten it because it has extra whitespace and long variable names. If it is not then you should include the code you are submitting. – Post Rock Garf Hunter – 2020-01-18T14:58:59.410

Yes. ???¿.....? – Timotej Leginus – 2020-01-18T15:10:55.320

0

GNU C, 429 423 bytes

Compiles and runs correctly on GCC-8.3, without any compiler flags. No other configuration was tested.

It considers a file to be a stream of text up to the first '\n' that it encounters (this just makes testing easier, can be fixed by replacing the 10 in (c=getchar())!=10 with a -1). However, keep in mind that newlines will not be considered valid.

char*b="bottles of beer\0on the wall\0Take one down, pass it around",s[4096];n;Q(){puts(s);}W(){puts("Hello, world!");}B(){for(n=100;--n;){printf("%d %s %s,\n%d %s.\n%s,\n%d %s %s.\n\n",n,b,b+16,n,b,b+28,n-1,b,b+16);}}N(){}l=0,c,i,(*t[])()={[43]=N,[57]=B,[72]=W,[81]=Q};main(){while((c=getchar())!=10){if(c!=43&&c!=57&&c!=72&&c!=81){exit(puts("Source code contains invalid characters"));}s[l++]=c;}for(;i<l;++i)t[s[i]]();}

Degolfed

// we stick all the strings in a single string, null separated.
// We will later index into the right indices to print the right pieces.
char*b="bottles of beer\0on the wall\0Take one down, pass it around";

// Here we store the source code. I chose 4kiB, but this was rather arbitrary.
char s[4096];

// n is the counter for the B function
n;

Q() { // Q for quine
    // puts is shorter than printf and it prints a newline for free
    puts(s);
}

W() { // W for world
    puts("Hello, world!");
}

B() { // B for bottles
    // n is the current count
    for (n = 100; --n;) {
        printf("%d %s %s,\n"    // n bottles of beer on the wall,
               "%d %s.\n"       // n bottles of beer.
               "%s,\n"          // Take one down, pass it around,
               "%d %s %s.\n\n", // n-1 bottles of beer on the wall

               n, b, b + 16, // Index into the right positions
               n, b,         // in the string.
               b + 28, n - 1, b, b + 16);
    }
}

N() {} // N for nothing, we need it for the table

// l is the length of source code
// c is the currently read character
// i is an index
l = 0, c, i;

// This array of function pointers is a bit like a jump table.
// This table tells us which function to call depending on the current char.
// It is essentially a hand-rolled switch statement, but shorter.
// NOTE: the [index]=value syntax is a GNU extension
int(*t[])()={
    [43/* ASCII + */] = N, // do nothing
    [57/* ASCII 9 */] = B, // print 99 bottles
    [72/* ASCII H */] = W, // print hello world
    [81/* ASCII Q */] = Q  // print source
};

main() {
    // consume input until we hit \n. this
    while ((c = getchar()) != 10) {
        // if we find a character not in "HQ9+", we log and exit
        if (c != 43 && c != 57 && c != 72 && c != 81) {
            exit(puts("Source code contains invalid characters"));
        }
        // populate the source code
        s[l++] = c;
    }

    // go over the source code (at this point validated)
    // and call the function that is set in the table
    // We can skip initializing i because it is a static, and statics
    // get implicitly initialized to 0. this saves 3 bytes
    for (; i < l; ++i)
        t[s[i]]();
}

I will admit I cheated a bit with the 99 bottles.

Sebastián Mestre

Posted 2013-10-22T16:56:03.910

Reputation: 71

399 bytes – ceilingcat – 2020-01-24T20:55:13.860