22
\$\begingroup\$

I'm a mentor at RubyLearning and one of the exercises we give to our students is the "Deaf Grandma" exercise from Chris Pine's book "Learn to Program". Here's the description:

Write a Deaf Grandma program. Whatever you say to grandma (whatever you type in), she should respond with: "Huh?! Speak up, sonny!", unless you shout it (type in all capitals). If you shout, she can hear you (or at least she thinks so) and yells back: "No, not since 1938!"

To make your program really believable, have grandma shout a different year each time; maybe any year at random between 1930 and 1950. (This part is optional, and would be much easier if you read the section on Ruby's random number generator at the end of the methods chapter.) You can't stop talking to grandma until you shout "BYE".

After several course iterations I tried to see how small I can get this and now have it down to 112 characters:

puts (s||='').upcase==s ? "NO, NOT SINCE #{1930+rand(21)}!":"HUH?! SPEAK UP, SONNY!" until(s=gets.chomp)=="BYE"

I'm curious to see in how few characters this can be achieved in the language of your choice, because I think Ruby is already doing really well here.

Edit: The Perl solution posted below led to

ruby -nle 'puts($_=="BYE"?exit: $_.upcase!? "HUH?! SEPAK UP, SONNY!":"NO, NOT SINCE #{1930+rand(21)}!")'

which is 92 characters for the expression + 2 more for the n and l options.

\$\endgroup\$
  • \$\begingroup\$ In a golfing context, this needs additional specification. What should happen if there's any extra output past BYE? \$\endgroup\$ – J B Feb 4 '11 at 15:27
  • \$\begingroup\$ Only "BYE" exactly terminates the program. \$\endgroup\$ – Michael Kohl Feb 4 '11 at 20:54

18 Answers 18

13
\$\begingroup\$

Perl, 85 91

Run with perl -nE '<code goes there>' (n counted in program size):

$==1930+rand 21;say/^BYE$/?last:uc eq$_?"
NO, NOT SINCE $=!":"HUH?! SPEAK UP, SONNY!"

That trailing exclamation mark is very expensive...

Edits suggested by IK:

  • Using a regexp instead of a string match spares the -l global option as well as two program characters: -3.
  • Using an actual variable to save a value and use it later for interpolation (Genius! Who'd have thought of using a variable for that?): 0.
  • Making that variable $=, constrained to be an integer: -4.

(and it still doesn't add up and I'm too sleepy to find out why. Oh well, the final count is right at least)

\$\endgroup\$
  • \$\begingroup\$ Abusing $= and using a regexp for "BYE" gets this down to 84+1: perl -nE '$==1930+rand 21;say/^BYE$/?last:uc eq$_?"NO, NOT SINCE $=!":"HUH?! SPEAK UP, SONNY!"' \$\endgroup\$ – Ilmari Karonen Sep 28 '12 at 8:26
  • \$\begingroup\$ @IlmariKaronen Integrated, thanks! \$\endgroup\$ – J B Sep 29 '12 at 9:50
6
\$\begingroup\$

Python 120 Characters

r=raw_input
s=r()
while'BYE'!=s:
 print["HUH?! SPEAK UP, SONNY!","NO, NOT SINCE %d!"%(1930+id(s)%21)][s.isupper()];s=r()

Any hints to improve?

\$\endgroup\$
  • \$\begingroup\$ You don't need the brackets around that if statement, also I'm pretty sure python has a recursion ceiling - but that could just simulate your grandma falling asleep. \$\endgroup\$ – Phoshi Feb 4 '11 at 14:17
  • \$\begingroup\$ Oh! I forgot to remove the brackets. Thanks :) \$\endgroup\$ – fR0DDY Feb 4 '11 at 16:25
  • \$\begingroup\$ You can save some characters if you remove the first line, replace the second with s='', reorder your statements in your while loop, and put the whole while loop on one line: gist.github.com/3787809 If you were really determined, you could save 2 characters by using python 3 (raw_input() -> input(), but print -> print()) \$\endgroup\$ – Matt Sep 26 '12 at 12:49
4
\$\begingroup\$

131 characters in PowerShell:

for(){$j=read-host;if($j-ceq"BYE"){break}if($j-ceq$j.ToUpper()){"No, not since 19$(10..90|random)!"}else{"Huh?! Speak up, sonny!"}}

W/ whitespace:

for(){
  $j = read-host;
  if ( $j -ceq "BYE" ) { break }
  if ( $j -ceq $j.ToUpper() ) { "No, not since 19$(10..90|random)!" }
  else { "Huh?! Speak up, sonny!" }
}

Squeezed 18 characters from Joey's suggestion.

BTW, 'Learn to Program' was the first programming book I ever read cover to cover.

\$\endgroup\$
  • \$\begingroup\$ You might want to take a look here: codegolf.stackexchange.com/questions/191/… \$\endgroup\$ – Joey Feb 10 '11 at 13:00
  • \$\begingroup\$ You can get this down to 120 by squishing the first if... into the conditional check of the for() like so: for(;($j=read-host)-cne"BYE"){if($j-ceq$j.ToUpper()){... Also, the spec says 1930-1950. \$\endgroup\$ – SpellingD Sep 26 '12 at 18:02
3
\$\begingroup\$

C# - 234 Chars

using System;class P{static void Main(){for(;;){var s=Console.ReadLine();if(s!=s.ToUpper()){Console.WriteLine("Huh?! Speak up, sonny!");continue;}if(s=="BYE")break;Console.WriteLine("No, not since 19{0}!",new Random().Next(30,51));}}}

More readable:

using System;
class P
{
    static void Main()
    {
        for(;;)
        {
            var s=Console.ReadLine();
            if(s!=s.ToUpper())
            {
                Console.WriteLine("Huh?! Speak up, sonny!");
                continue;
            }
            if(s=="BYE")
                break;
            Console.WriteLine("No, not since 19{0}!",new Random().Next(30,51));
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ Ah missed a few simple ones in mine, and made some silly mistakes. Nice +1 \$\endgroup\$ – Kyle Rozendo Feb 4 '11 at 9:57
3
\$\begingroup\$

Befunge - 27x6 = 162 characters

> ~:0`  #v _            vv<
         >:"a"`!#v  _:"z"`|
^                <       <
v"Huh?! Speak up, sonny!"0<
v"No, not since 1938!"0 <
>:# #, _@

EDIT: Completely missed the "BYE" part. New version coming soon.

EDIT 2: Actually, that makes it a bit too complex for my meager Befunge skills. I might try again later, but I can't think of any simple way to implement it at the moment.

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

C# - 194 CHARS

using System;class P{static void Main(){var s=Console.ReadLine();if(s!="BYE"){Console.Write((s==s.ToUpper()?"No, not since 19"+new Random().Next(30, 51):"Huh?! Speak up, sonny")+"!");Main();}}}

With whitespaces:

using System;
class P
{
    static void Main()
    {
        var s = Console.ReadLine();
        if (s != "BYE")
        {
            Console.Write((s == s.ToUpper() ? "No, not since 19" + new Random().Next(30, 51) : "Huh?! Speak up, sonny") + "!");
            Main();
        }
    }
}

With some inspiration from Nellius and fR0DDY.

Please let me know if it can be improved.

\$\endgroup\$
  • \$\begingroup\$ Short, but FWIW, i think this leaks (recursively calling Main()). Also, i think you want parens in the ?: expression to get the ! in both. I added an answer with this and EOL (but still leaks). \$\endgroup\$ – b w Feb 4 '11 at 16:54
  • \$\begingroup\$ i see you added the parens and removed your comment. Good job. Now my edit to add screenshots to my answer of with and without the parens is moot. (But, still leaky) :-) \$\endgroup\$ – b w Feb 4 '11 at 17:58
  • \$\begingroup\$ @bill Yes, I messed my testing initially. The non leaky version would be 199 chars, waaay too long :) \$\endgroup\$ – Richard Feb 4 '11 at 18:07
  • \$\begingroup\$ ha. I like the Main(); solution... no sane person would use this program long enough for it to be a problem. \$\endgroup\$ – b w Feb 4 '11 at 18:10
  • \$\begingroup\$ As Phoshi said in the comment to fR0DDY. The program crashes when grandma falls asleep. \$\endgroup\$ – Richard Feb 4 '11 at 18:19
3
\$\begingroup\$

D: 246 Characters

import std.random,std.stdio,std.string;void main(){auto r=rndGen();for(;;){auto t=strip(readln());if(t=="BYE")break;if(t.toupper()==t)writefln("No, not since %s!",{r.popFront();return r.front%20+1930;}());else writeln("Huh?! Speak up, sonny!");}}

More Legibly:

import std.random, std.stdio, std.string;

void main()
{
    auto r = rndGen();

    for(;;)
    {
        auto t = strip(readln());

        if(t == "BYE")
            break;

        if(t.toupper() == t)
            writefln("No, not since %s!", {r.popFront(); return r.front % 20 + 1930;}());
        else
            writeln("Huh?! Speak up, sonny!");
    }
}
\$\endgroup\$
3
\$\begingroup\$

javascript, 142 characters, 29 of them perform random year

n='a'; while((/[a-z]/.test(n)?r="HUH?! SPEAK UP, SONNY!":n=="BYE"?r='':r="NO, NOT SINCE "+Math.floor(Math.random()*21+1930))!=''){n=prompt(r)}
\$\endgroup\$
3
\$\begingroup\$

Awk: 97 characters

$0=="BYE"{exit}$0=toupper($0)==$0?"NO, NOT SINCE "int(rand()*21+1930)"!":"HUH?! SPEAK UP, SONNY!"
\$\endgroup\$
3
\$\begingroup\$

Windows PowerShell, 121 117

Due to the nature of the task this looks pretty much identical to Ty Auvil's solution, although it was written independently:

for(;($j=read-host)-cne'BYE'){if($j-cmatch'[a-z]'){'Huh?! Speak up, sonny!'}else{"No, not since 19$(30..50|random)"}}

Thanks to SpellingD for the suggestion,

\$\endgroup\$
  • \$\begingroup\$ I like your regex matching, but the switch statement is way bulky. Along with the suggestion I gave Ty, you could reduce your character count to 117 by using regex with an if instead like this: for(;($j=read-host)-cne'BYE'){if($j-cmatch'[a-z]'){'Huh?! Speak up, sonny!'}else{"No, not since 19$(30..50|random)"}} \$\endgroup\$ – SpellingD Sep 26 '12 at 18:29
2
\$\begingroup\$

Haskell (189)

import Random
import Char
main=getLine>>=g
g"BYE"=return""
g s|s/=map toUpper s=putStrLn"HUH?! SPEAK UP SONNY!">>main|4>2=randomRIO(30,50::Int)>>=putStrLn.("NO, NOT SINCE 19"++).show>>main

The weird thing is, Haskell code is usually a lot shorter than comparable C code when writing a 'serious' program.

\$\endgroup\$
  • \$\begingroup\$ You can avoid importing Char by using any(`elem`['a'..'z'])s to test for lower case letters. \$\endgroup\$ – hammar Oct 22 '11 at 3:13
2
\$\begingroup\$

APL (76)

 {'BYE'≢A←⍞:∇⎕←{∧/⍵∊⎕A:'No, not since ',⍕1938+?20⋄'Huh?! Speak up sonny!'}A}⍬
\$\endgroup\$
1
\$\begingroup\$

C# - 345 Chars

using System;class Program{static void Main(){for(;;){if(!t(Console.ReadLine()))break;}}static bool t(string s){bool b=false;if(s=="BYE")return b;int i=0;for(;i<s.Length;i++){b=(s[i]>65&&s[i]<90)?true:false;if(!b)break;}if(b) p("NO, NOT SINCE 1938!");else p("HUH?! SPEAK UP, SONNY!");return true;}static void p(string s){Console.WriteLine(s);}}

Damn verbose language ... :-)

\$\endgroup\$
  • 1
    \$\begingroup\$ You can call the class just P. And this doesn't detect uppercase correctly. I can shout at it and it still cannot hear me. You can shorten the main method to while(t(Console.ReadLine()));. You can use using C=System.Console; at the start to shorten access to ReadLine() and WriteLine() to C.ReadLine() and C.WriteLine(). \$\endgroup\$ – Joey Feb 9 '11 at 19:15
  • \$\begingroup\$ @Joey - Thanks for the tips! \$\endgroup\$ – Kyle Rozendo Feb 10 '11 at 6:43
1
\$\begingroup\$

C# - 196 chars (but leaky)

using System;class P{static void Main(){var s=Console.ReadLine();if(s!="BYE"){Console.Write((s==s.ToUpper()?"No, not since 19"+new Random().Next(30, 51):"Huh?! Speak up, sonny")+"!\n");Main();}}}

That's @Richard's (leaky) answer with two parens (see below) and a \n added in there to get the EOL in both cases. Otherwise the " + " is just wasted space.

Formatted

using System;
class P
{
    static void Main() { 
        var s = Console.ReadLine(); 
        if (s != "BYE") { 
            Console.Write((
                s == s.ToUpper() ? 
                "No, not since 19" + new Random().Next(30, 51) : 
                "Huh?! Speak up, sonny"
                ) + "!\n");
            Main(); 
        } 
    }
}

UPDATE: to clarify my comment about the parens being needed, here's what i get without the parens (i.e. with @Richard's original solution):

without parens

And with the parens:

with parens

Neither of these use my additional \n though.

\$\endgroup\$
  • \$\begingroup\$ It's 195 here, only. Did you count an unnecessary line break at the end? \$\endgroup\$ – Joey Feb 12 '11 at 12:36
1
\$\begingroup\$

Bash: 136 128 characters

while read s
do
[[ $s = BYE ]]&&break
[[ ${s^^} = $s ]]&&echo NO, NOT SINCE $[RANDOM%21+1930]!||echo HUH?! SPEAK UP, SONNY!
done

Limited alternative: 132 123 characters

f(){
read s
[[ $s = BYE ]]||{
[[ ${s^^} = $s ]]&&echo NO, NOT SINCE $[RANDOM%21+1930]!||echo HUH?! SPEAK UP, SONNY!
f
}
}
f

You can talk to a deaf infinitely, but the conversation with this later code is limited by the call stack. (In my test it gets terminated after 4989 calls.)

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

Javascript - 133 131 130 128 127 121 chars

golfed version of www0z0ks solution

g='';while((i=prompt(g))!='BYE'){/[a-z]/.test(i)?g='Huh?! Speak up, sonny!':g='No, not since '+Math.floor(Math.random()*21+1930)+'!'}

g='';while((i=prompt(g))!='BYE'){g=/[a-z]/.test(i)?'Huh?! Speak up, sonny!':'No, not since '+Math.floor(Math.random()*21+1930)+'!'}

g='';while((i=prompt(g))!='BYE'){g=/[a-z]/.test(i)?'Huh?! Speak up, sonny!':'No, not since '+Math.ceil(Math.random()*21+1929)+'!'}

for(g='';(i=prompt(g))!='BYE';g=/[a-z]/.test(i)?'Huh?! Speak up, sonny!':'No, not since '+Math.ceil(Math.random()*21+1929)+'!');

for(g='';(i=prompt(g))!='BYE';g=/[a-z]/.test(i)?'Huh?! Speak up, sonny!':'No, not since '+parseInt(Math.random()*21+1930)+'!');

for(g='';(i=prompt(g))!='BYE';g=/[a-z]/.test(i)?'Huh?! Speak up, sonny!':'No, not since '+(Math.random()*21+1930|0)+'!');

Edit: Saved another six chars with this great tip

\$\endgroup\$
  • \$\begingroup\$ Write the ternary operator as g=/[a-z]/.test(i)?'Huh?!...':'No...' and you spare 2 characters. \$\endgroup\$ – manatwork Sep 26 '12 at 13:43
  • \$\begingroup\$ Edited, thanks for the pointer. \$\endgroup\$ – codeporn Sep 27 '12 at 14:33
  • 1
    \$\begingroup\$ 1 more character I found: Math.ceil() is shorter than Math.floor(). Just change the base year to keep the interval unchanged: Math.ceil(Math.random()*21+1929). \$\endgroup\$ – manatwork Sep 27 '12 at 15:21
  • \$\begingroup\$ Great, +1! I saved another two chars with changing the while to a for loop. \$\endgroup\$ – codeporn Sep 28 '12 at 8:15
0
\$\begingroup\$

Clojure - 160 154 Characters

(#(if(= % "BYE")%(do(if(=(.toUpperCase %)%)(prn(str"No, not since "(+ 1930(rand-int 9))"!"))(prn"Huh?! Speak up, sonny!"))(recur(read-line))))(read-line))

Working on golfing it a bit more. Suggestions welcome.

Run through REPL

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

Q, 115

{while[1;v:read0 0;$[v~"BYE";'`;v~upper v;-1"No, not since ",/:(($)1?1930+(!)20),'"!";-1"Huh?! Speak up, sonny!"]]}

usage

q){while[1;v:read0 0;$[v~"BYE";'`;v~upper v;-1"No, not since ",/:(($)1?1930+(!)20),'"!";-1"Huh?! Speak up, sonny!"]]}`
Hi
Huh?! Speak up, sonny!
Hello
Huh?! Speak up, sonny!
HELLO!
No, not since 1938!
Goodbye Grandma
Huh?! Speak up, sonny!
BYE
'
\$\endgroup\$

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.