[Question]
  [
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](http://pine.fm/LearnToProgram/)". 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.
  
[Answer]
## 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)
[Answer]
## 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?
[Answer]
**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.
[Answer]
**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));
        }
    }
}
```
[Answer]
## 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.
[Answer]
**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.
[Answer]
# 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!");
    }
}
```
[Answer]
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)}
```
[Answer]
## Windows PowerShell, ~~121~~ 117
Due to the nature of the task this looks pretty much identical to [Ty Auvil's solution](https://codegolf.stackexchange.com/questions/475/code-golf-chris-pines-deaf-grandma/576#576), 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,
[Answer]
# Awk: ~~97~~ 95 characters
Thanks to
* [tail spark rabbit ear](https://codegolf.stackexchange.com/users/100411/tail-spark-rabbit-ear) for replacing string equality testing with regex matching (-2 characters)
```
/^BYE$/{exit}$0=toupper($0)==$0?"NO, NOT SINCE "int(rand()*21+1930)"!":"HUH?! SPEAK UP, SONNY!"
```
[Try it online!](https://tio.run/##Fc7dCoIwGADQ@z3F5xDSkrS6KhCxWhnBjMwLb4KBS4e2hc5@iJ7d6gEOHPao@t49LzNium/@FPpjer5W3e3GG8v0bN83vQDT2AEanyDZ0RUBLKS2GiZzyx5OJ6PJfObZ2MALHKVRYEByIOEe0oMDSUxpZuC@j3hdKwe2f3RlBorUY9DCS3UN5OwVoPBIIItTWJNwE6BTKVooFG9BKwUX1ozR74cSzv8EJH9q0CWTVVuIu5DF@As "AWK – Try It Online")
[Answer]
**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. 
[Answer]
## APL (76)
```
 {'BYE'≢A←⍞:∇⎕←{∧/⍵∊⎕A:'No, not since ',⍕1938+?20⋄'Huh?! Speak up sonny!'}A}⍬
```
[Answer]
**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 ... :-)
[Answer]
### 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](https://i.stack.imgur.com/bykmP.jpg)
And with the parens:
![with parens](https://i.stack.imgur.com/KRUAR.jpg)
Neither of these use my additional `\n` though.
[Answer]
# 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.)
[Answer]
# 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](https://codegolf.stackexchange.com/questions/2682/tips-for-golfing-in-javascript/2686#2686)
[Answer]
# [Jelly](https://github.com/DennisMitchell/jelly), 54 bytes
```
21X+⁽¤Ḷ“Ð€ÑƊʂ!ṡxẈ»,K”!
ɠ¢“®VẹQƝɗi³ỤwṖoỴ»ŒuƑ?ṄßƊḟ⁻?“BYE
```
[Try it online!](https://tio.run/##AYsAdP9qZWxsef//MjFYK@KBvcKk4bi24oCcw5DigqzDkcaKyoIh4bmheOG6iMK7LEvigJ0hCsmgwqLigJzCrlbhurlRxp3Jl2nCs@G7pHfhuZZv4bu0wrvFknXGkT/huYTDn8aK4bif4oG7P@KAnEJZRf//SGVsbG8KSEVMTE8KQllFCklnbm9yZSB0aGlz "Jelly – Try It Online")
# [Jelly](https://github.com/DennisMitchell/jelly), 49 bytes
```
21X+⁽¤Ḷ“Ð€ÑƊʂ!ṡxẈ»,K”!µ“®VẹQƝɗi³ỤwṖoỴ»ŒuƑ?ḟ⁻?“BYE
```
[Try it online!](https://tio.run/##AWoAlf9qZWxsef//MjFYK@KBvcKk4bi24oCcw5DigqzDkcaKyoIh4bmheOG6iMK7LEvigJ0hwrXigJzCrlbhurlRxp3Jl2nCs@G7pHfhuZZv4bu0wrvFknXGkT/huJ/igbs/4oCcQllF////QllF "Jelly – Try It Online")
# [Jelly](https://github.com/DennisMitchell/jelly), 51 bytes
```
21X+⁽¤Ḷ“Ð€ÑƊʂ!ṡxẈ»,K;”!µ“®VẹQƝɗi³ỤwṖoỴ»ŒuƑ?ḟ⁻?“BYE”
```
[Try it online!](https://tio.run/##y0rNyan8/9/IMEL7UePeQ0se7tj2qGHO4QmPmtYcnnis61ST4sOdCyse7uo4tFvH2/pRw1zFQ1uBCg6tC3u4a2fgsbknp2ce2vxw95Lyhzun5T/cveXQ7qOTSo9NtH@4Y/6jxt32QKVOka5Abf8PtwONjPz/X8kDaGO@ko6Ckoerj48/iAFUoQQA "Jelly – 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](https://tio.run/##y0rNyan8/9/IMEL7UePeQ0se7tj2qGHOocWHlgH5ux/uXG1/bE2U9eEdh3breD9qmKvIdXLBoUUgFevCHu7aGXhs7snpmYc2P9y9pPzhzmn5D3dvObT76KTSQ0uAxLGJ9g93thyef6zr4Y75QMPsgdqcIl2Bpvz/7wG0Np/Lw9XHx58rqTKVCyjO5Zmel1@UqlCSkVkMAA) 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
```
[Answer]
# [Vyxal](https://github.com/Vyxal/Vyxal), 57 bytes
```
{:‛Ṗ⊍⇧≠|aA[`No, λ† ∧Ṡ %!`»÷ḟ»21℅+%,|`Huh?! ∞⇧ up, ½Ŀny!`,
```
[Try it Online!](https://vyxal.pythonanywhere.com/#WyIiLCIiLCJ7OuKAm+G5luKKjeKHp+KJoHxhQVtgTm8sIM674oCgIOKIp+G5oCAlIWDCu8O34bifwrsyMeKEhSslLHxgSHVoPyEg4oie4oenIHVwLCDCvcS/bnkhYCwiLCIiLCJIZWxsb1xuSEVMTE9cbkJZRSJd)
Vyxal's not particularly good at English compression.
```
{                                # While...
 :   ≠|                          # Take an input, is it not equal to...
  ‛Ṗ⊍⇧                           # "BYE"
       aA[                       # If all uppercase
          `...`        %,        # Print "No, not since" formatted by
               »÷ḟ»21℅+          # 1929 + random(1..21)
                          `...`, # Otherwise print "Huh?! Speak up, sonny!"
```
[Answer]
**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
[Answer]
# 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
'
```
]