# Chris Pine's “Deaf Grandma”

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.

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

## 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) • 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!"' – Ilmari Karonen Sep 28 '12 at 8:26
• @IlmariKaronen Integrated, thanks! – J B Sep 29 '12 at 9:50

## 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?

• 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. – Phoshi Feb 4 '11 at 14:17
• Oh! I forgot to remove the brackets. Thanks :) – fR0DDY Feb 4 '11 at 16:25
• 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()) – Matt Sep 26 '12 at 12:49

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.

• You might want to take a look here: codegolf.stackexchange.com/questions/191/… – Joey Feb 10 '11 at 13:00
• 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. – SpellingD Sep 26 '12 at 18:02 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)); } } }  • Ah missed a few simple ones in mine, and made some silly mistakes. Nice +1 – Kyle Rosendo Feb 4 '11 at 9:57 ## 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. 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. • 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). – b w Feb 4 '11 at 16:54 • 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) :-) – b w Feb 4 '11 at 17:58 • @bill Yes, I messed my testing initially. The non leaky version would be 199 chars, waaay too long :) – Richard Feb 4 '11 at 18:07 • ha. I like the Main(); solution... no sane person would use this program long enough for it to be a problem. – b w Feb 4 '11 at 18:10 • As Phoshi said in the comment to fR0DDY. The program crashes when grandma falls asleep. – Richard Feb 4 '11 at 18:19 # 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!"); } }  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)}  ## 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,

[[ ${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.) # Javascript - 133131130128127 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 • Write the ternary operator as g=/[a-z]/.test(i)?'Huh?!...':'No...' and you spare 2 characters. – manatwork Sep 26 '12 at 13:43 • Edited, thanks for the pointer. – codeporn Sep 27 '12 at 14:33 • 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). – manatwork Sep 27 '12 at 15:21 • Great, +1! I saved another two chars with changing the while to a for loop. – codeporn Sep 28 '12 at 8:15 # Jelly, 54 bytes 21X+⁽¤Ḷ“Ð€ÑƊʂ!ṡxẈ»,K”! ɠ¢“®VẹQƝɗi³ỤwṖoỴ»ŒuƑ?ṄßƊḟ⁻?“BYE  Try it online! # Jelly, 49 bytes 21X+⁽¤Ḷ“Ð€ÑƊʂ!ṡxẈ»,K”!µ“®VẹQƝɗi³ỤwṖoỴ»ŒuƑ?ḟ⁻?“BYE  Try it online! # Jelly, 51 bytes 21X+⁽¤Ḷ“Ð€ÑƊʂ!ṡxẈ»,K;”!µ“®VẹQƝɗi³ỤwṖoỴ»ŒuƑ?ḟ⁻?“BYE”  Try it online! The first one is perhaps the most faithful interpretation of the question. It is a full program which reads input line by line from STDIN and acts on it line by line, terminating either when it reaches "BYE" or the end of STDIN (at which point it exits with an error). The second is a full program which takes a single string as input (e.g. Hello, HELLO, BYE, etc.) and outputs the corresponding output (an empty output for BYE) This third is a function which can be repeatedly called with arguments in a similar version to the second, but each argument is independent - calling it with BYE will not prevent you calling it again. All three output not in uppercase, reflecting the text of the question rather than the sample program provided. +5 bytes to output in all uppercase As they all have the same basic structure, I'll only explain the first one, and leave the other two as exercises for the reader ## How it works 21X+⁽¤Ḷ“Ð€ÑƊʂ!ṡxẈ»,K”! - Helper link. This takes no arguments, outputs a string and returns "!" 21 - Yield 21 X - Choose a random integer between 1 and 21 inclusive ⁽¤Ḷ - Compressed integer: 1929 + - Add to the random integer. Call that Y “Ð€ÑƊʂ!ṡxẈ» - Compressed string: "No, not since" , - Pair; ["No, not since", Y] K - Join the two with spaces ”! - Unparseable nilad; Output the space separated string and return "!" ɠ¢“®VẹQƝɗi³ỤwṖoỴ»ŒuƑ?ṄßƊḟ⁻?“BYE - Main link f(). Takes no arguments ɠ - Read a line of STDIN, L ? - If: ⁻ “BYE - Condition: L does not equal "BYE" Ɗ - Then: ? - If: Ƒ - Condition: L does not change when: Œu - Converted to uppercase ¢ - Then: Call the helper link, yielding "!" “®VẹQƝɗi³ỤwṖoỴ» - Else: Compressed string "Huh?! Speak up, sonny!" Ṅ - Print with a trailing newline ß - Recurse, calling f() again ḟ - Else: Return the empty string and halt execution  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 # 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
'