24
\$\begingroup\$

Can't see emoji? Click here.

You will simulate a vending machine. The items are 56 different emojis, which can be represented as two surrogate UTF-8 characters, from: 🍅(U+D83C U+DF45) to 🍼 (U+D83C U+DF7C). In HTML these emojis can be represented in UTF-16 form as &#x1F345 through &#x1F37C. In base 10 the first UTF-8 character has value 55356. The value of the second character ranges from 57157 to 57212. Here is a CJam program that generates all the relevant emojis. To avoid confusion, this challenge will be scored in characters, not bytes. Some emojis are 2 characters, some are 3. http://www.lettercount.com will give you an accurate count for this challenge.

Pricing 💵💵💵

There are 8 different price categories:

  • $1.00 : 🍅🍆🍇🍈🍉🍊🍋
  • $1.50 : 🍌🍍🍎🍏🍐🍑🍒
  • $2.00 : 🍓🍔🍕🍖🍗🍘🍙
  • $2.50 : 🍚🍛🍜🍝🍞🍟🍠
  • $3.00 : 🍡🍢🍣🍤🍥🍦🍧
  • $3.50 : 🍨🍩🍪🍫🍬🍭🍮
  • $4.00 : 🍯🍰🍱🍲🍳🍴🍵
  • $4.50 : 🍶🍷🍸🍹🍺🍻🍼

Displaying the Vending Machine

This is what the user sees when the program starts and after making a purchase. You can use regular numbers instead of emoji numbers, but emoji numbers have the benefit of being the same width as other emojis. If you use emoji numbers you can subtract 14 characters from your score, because emoji numbers are 3 characters each.

  1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣
A 🍅🍆🍇🍈🍉🍊🍋
B 🍌🍍🍎🍏🍐🍑🍒
C 🍓🍔🍕🍖🍗🍘🍙
D 🍚🍛🍜🍝🍞🍟🍠
E 🍡🍢🍣🍤🍥🍦🍧
F 🍨🍩🍪🍫🍬🍭🍮
G 🍯🍰🍱🍲🍳🍴🍵
H 🍶🍷🍸🍹🍺🍻🍼 

If the user enters G5 that selects 🍳. Entering E6 selects 🍦.

Adding Cash 💸💸💸

To add money, the user should input / and then the code for a coin or bill. The coins are (N)ickel ($0.05), (D)ime ($0.10), and (Q)uarter ($0.25). The bills are (O)ne ($1.00), (F)ive ($5.00), (T)en ($10.00). Every time the user adds a bill or coin, the program should output Balance: [New Balance]. Only one bill or coin can be added each time.Here is an example containing three lines of input:

/D
Balance: $0.10
/Q
Balance: $0.35
/F
Balance: $5.35

Selecting Items

The user selects an item by entering the item code, like B2 or C3. If the user has not added any money, the program should output Item Cost: [Cost of Item]. If the user has added money, but it is not enough to buy the item, the output should be Insufficient Funds. Item Cost: [Cost of Item]. If the user did add enough money, the program should output: Item at [Item Location] Purchased. Remaining Balance: [Balance - Cost of Item]. Then on its own line, print out the emoji of the purchased item. Then print out the entire vending machine (See "Displaying the Vending Machine") with the purchased item replaced with 🚫(U+D83D U+DEAB) Base 10: (55357 57003).

Ending the Program

If the user buys an item which brings his/her balance down to $0.00, the program should automatically terminate. Alternatively, if the user enters "Done" at any time, the program must give the user his/her remaining balance in change. Change must be in only Nickels, Dimes, and Quarters, and must be the smallest amount of coins possible. For example if the remaining balance is $1.45, the output should be QQQQQDD.

Sample Run

(Input starts with "-")

  1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣
A 🍅🍆🍇🍈🍉🍊🍋
B 🍌🍍🍎🍏🍐🍑🍒
C 🍓🍔🍕🍖🍗🍘🍙
D 🍚🍛🍜🍝🍞🍟🍠
E 🍡🍢🍣🍤🍥🍦🍧
F 🍨🍩🍪🍫🍬🍭🍮
G 🍯🍰🍱🍲🍳🍴🍵
H 🍶🍷🍸🍹🍺🍻🍼  
-E1
Item Cost: $3.00
-/F
Balance: $5.00
-E1
Item at E1 Purchased. Remaining Balance: $2.00
🍡 
  1️⃣2️⃣3️⃣4️⃣5️⃣6️⃣7️⃣
A 🍅🍆🍇🍈🍉🍊🍋
B 🍌🍍🍎🍏🍐🍑🍒
C 🍓🍔🍕🍖🍗🍘🍙
D 🍚🍛🍜🍝🍞🍟🍠
E 🚫🍢🍣🍤🍥🍦🍧
F 🍨🍩🍪🍫🍬🍭🍮
G 🍯🍰🍱🍲🍳🍴🍵
H 🍶🍷🍸🍹🍺🍻🍼
-Done
QQQQQQQQ

Invalid Codes

  • If the input starts with / but is not a valid money input, output Money not Accepted.
  • If the input is an item that has already been purchased, output Item out of stock
  • If the input is a digit-letter combination that is not in range (like A0 or J2, output "Invalid code"
  • For all other invalid input, output "Error"

Bonus (-15%) : Jam

When vending an item, there is a 10% chance the machine will jam, and the item gets stuck. Output JAM!!! one time. In this case the user must input "punch" or "kick". Each time the user does so, there is a 20% chance that the item will break free, and vend as normal. Until the jam clears, the only inputs recognized are "punch" and "kick". All other inputs do nothing. Here is a sample:

-A4
Item at A4 purchased. Remaining Balance: $1.00
JAM!!!
-punch
-kick
-kick
-punch
-punch
🍚

(The jam has been cleared. Resume as normal.)

Bonus (-10%) : ID

If the user tries to buy an alcoholic beverage (any item on the last row except the baby bottle), you must demand proof of age. To do so, you ask the user to multiply two random numbers between 13 and 19 (inclusive). Output ID required: [13-19]x[13-19] = ?. If the user answers correctly, the purchase can go through. If not, print 🔞(U+D83D U+DD1E) base 10: (55357 56606)

Alternative Challenge : Minecraft

This challenge seems like something possible in Minecraft. To successfully create a Minecraft version of this program, the player must

  • Have an 8x7 grid to select from 56 unique items
  • Be able to add money to the balance (Maybe use different types of stones for the different denominations described above.)
  • Give the user the item he/she selects if they have put in enough "money"
  • Return the user the appropriate amount of change at the end.

Compatibility Mode

  • I can only take input at the beginning of the program

    • Not to worry, you can make a program that takes all input up front. If your program has not terminated after evaluating all input, assume that "Done" is called at the end. If you do this method, you will not be eligible for any bonuses.
  • I can't view/output emojis

    • You can copy and paste your input and output into this Stack Snippet (or here). It can understand both unicode emojis and HTML codes for emojis. If your output looks correct on this page, your program passes. Try pasting in some of the sample input/output to see how it looks.

function parse() {
var o = document.getElementById("output");
var text = document.getElementById("in").value;
o.innerHTML = text.replace(/\n/g,"<br/>").replace(/ /g,"&nbsp");
twemoji.parse(o);
}
<head>
<script src="//twemoji.maxcdn.com/twemoji.min.js"></script>  
</head>
<body>
<h1>Emoji to Image</h1>
<textarea id="in" rows="10" cols="50"></textarea>
<input type="submit" onclick="parse()">
<div id="output"></div>
</body>

Here is an abbreviated compatibility-mode version of the vending machine display that you can test in the Stack Snippet:

   &#x31&#x20E3&#x32&#x20E3&#x33&#x20E3&#x34&#x20E3&#x35&#x20E3...
A&#x1F345&#x1F346&#x1F347&#x1F348&#x1F349...
B 
...
  • I can output emojis, but not to STDOUT.

    • Seemingly, the Mac OS X Terminal is the only one known to be able to output emojis. If you are not able to output emojis in your terminal, your other options are
      • Send all output to a file.
      • Display all output in a window.
      • Use the technique described in "I can't view/output emojis".
\$\endgroup\$
6
  • 5
    \$\begingroup\$ $ can be replaced with your local currency doesn't make much sense if you then require that the coins be (N)ickels (D)imes and (Q)uarters. I suggest you remove it as it adds confusion. \$\endgroup\$ Commented Dec 1, 2015 at 8:25
  • \$\begingroup\$ @steveverrill Good point. I've changed it. My question is, do non-U.S. keyboards have a $ button, or is there a code for it? \$\endgroup\$
    – geokavel
    Commented Dec 1, 2015 at 17:00
  • \$\begingroup\$ the $ symbol is part of the ASCII character set, so it's safe to assume pretty much all keyboards and languages can handle it. It is the only currency symbol in the 7-bit ASCII character set. As a result, other symbols such as the GBP symbol or the EUR symbol € can be incorrectly rendered due to incompatible string encodings, but it virtually never happens with $. \$\endgroup\$ Commented Dec 1, 2015 at 20:29
  • \$\begingroup\$ @steveverrill I know they can handle it, but is there a physical keyboard button for it? \$\endgroup\$
    – geokavel
    Commented Dec 1, 2015 at 20:32
  • \$\begingroup\$ My British and Spanish keyboards have it in addition to the relevant local currency symbols, and I think the others do too. Besides being the most widely used currency symbol in the world (including for many currencies that are not called dollars), the $ symbol has special meaning in most programming languages, so not having it available on the keyboard would be a serious limitation. I'm typing this on my Spanish keyboard and it's actually easier to get $ (shift + 4) than € (alt gr + E) so you can see that the latter symbol added as an afterthought. \$\endgroup\$ Commented Dec 1, 2015 at 20:40

2 Answers 2

9
\$\begingroup\$

PHP, (1295 - 14) - 25% = 960.75 characters

Old scores 1315 1351 1361 1295 989 977

That was fun, and it's the first answer – yeah.

$i=[A=>[🍅,🍆,🍇,🍈,🍉,🍊,🍋],B=>[🍌,🍍,🍎,🍏,🍐,🍑,🍒],C=>[🍓,🍔,🍕,🍖,🍗,🍘,🍙],D=>[🍚,🍛,🍜,🍝,🍞,🍟,🍠],E=>[🍡,🍢,🍣,🍤,🍥,🍦,🍧],F=>[🍨,🍩,🍪,🍫,🍬,🍭,🍮],G=>[🍯,🍰,🍱,🍲,🍳,🍴,🍵],H=>[🍶,🍷,🍸,🍹,🍺,🍻,🍼]];$p=[A=>1,B=>1.5,C=>2,D=>2.5,E=>3,F=>3.5,G=>4,H=>4.5];$v=[N=>.05,D=>0.1,Q=>.25,O=>1,F=>5,T=>10];a:$m='';$w=$x=0;$q=$c[0];$r=$c[1];$f=fgets;$n=rand;$u=number_format;if('Done'==$c&&$e=1)foreach([Q,D,N]as$_)while($b&&round($b-$v[$_],2)>=0&&$m.=$_)$b-=$v[$_];elseif('/'==$q){$v[$r]?$b+=$v[$r]:$m="Money not accepted
";$m.="Balance: $".$u($b,2);}elseif(in_array($q,range(A,Z))){if(0<$r&&8>$r--&&($_=$p[$q])){$b&&$b<$_?$m="Insufficient funds. ":0;if($b<$_)$m.="Item cost: $".$u($_,2);elseif(👾==$i[$q][$r])$m="Item out of stock.";else{$t=0;if(H==$q&&$r<6){$t=$n(13,19);$s=$n(13,19);echo"ID required: {$t}x$s = ?
";if($f(STDIN)!=$t*$s)$m=🔞;else$t=0;}if(!$t){$b-=$_;if(1>$n(0,9)){$w=1;}$x=$i[$q][$r];$m="Item at $c purchased. Remaining balance: $".$u($b,2).($w?"":"
$x");$i[$q][$r]=👾;if(!$b)$e=1;}}}else$m="Invalid code";}if($c&&!$m&&!$e)$m=Error;system(clear);echo"  1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣";foreach($i as$k=>$_){echo"
$k ";foreach($_ as$_)echo"$_ ";}echo"
$m
";if($w){echo"JAM!!!
";for(;$c!=kick&&$c!=punch||7>$n(0,9);$c=trim($f(STDIN)));echo"$x
";}$e?exit:$c=trim($f(STDIN));goto a;

As vending machines remind me of the 90s I used goto and for purchased items the Space Invader 👾 is shown. The machine is redrawn after every command.

Ungolfed (somehow)

$i=[A=>[🍅,🍆,🍇,🍈,🍉,🍊,🍋],B=>[🍌,🍍,🍎,🍏,🍐,🍑,🍒],C=>[🍓,🍔,🍕,🍖,🍗,🍘,🍙],D=>[🍚,🍛,🍜,🍝,🍞,🍟,🍠],E=>[🍡,🍢,🍣,🍤,🍥,🍦,🍧],F=>[🍨,🍩,🍪,🍫,🍬,🍭,🍮],G=>[🍯,🍰,🍱,🍲,🍳,🍴,🍵],H=>[🍶,🍷,🍸,🍹,🍺,🍻,🍼]];
$p=[A=>1,B=>1.5,C=>2,D=>2.5,E=>3,F=>3.5,G=>4,H=>4.5];
$v=[N=>.05,D=>0.1,Q=>.25,O=>1,F=>5,T=>10];

a:
$m='';
$w=$x=0;
$q=$c[0];
$r=$c[1];
$f=fgets;
$n=rand;
$u=number_format;

if('Done'==$c&&$e=1)
    foreach([Q,D,N]as$_)
        while($b&&round($b-$v[$_],2)>=0&&$m.=$_)
            $b-=$v[$_];
elseif('/'==$q){
    $v[$r]?$b+=$v[$r]:$m="Money not accepted
";
    $m.="Balance: $".$u($b,2);
}elseif(in_array($q,range(A,Z))){
    if(0<$r&&8>$r--&&($_=$p[$q])){
        $b&&$b<$_?$m="Insufficient funds. ":0;
        if($b<$_)$m.="Item cost: $".$u($_,2);
            elseif(👾==$i[$q][$r])
                $m="Item out of stock.";
            else{
                $t=0;
                if(H==$q&&$r<6){
                    $t=$n(13,19);
                    $s=$n(13,19);
                    echo"ID required: {$t}x$s = ?
";
                    if($f(STDIN)!=$t*$s)
                        $m=🔞;
                    else
                        $t=0;
                }
                if(!$t){
                    $b-=$_;
                    if(1>$n(0,9)){
                        $w=1;
                    }
                    $x=$i[$q][$r];
                    $m="Item at $c purchased. Remaining balance: $".$u($b,2).($w?"":"
$x");
                    $i[$q][$r]=👾;
                    if(!$b)$e=1;
                }
            }
    }else
        $m="Invalid code";
}

if($c&&!$m&&!$e)$m=Error;

system(clear);
echo"  1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣";
foreach($i as$k=>$_){
    echo"
$k ";
    foreach($_ as$_)
        echo"$_ ";
}
echo"
$m
";

if($w){
    echo"JAM!!!
";
    for(;$c!=kick&&$c!=punch||7>$n(0,9);$c=trim($f(STDIN)));
    echo"$x
";
}

$e?exit:$c=trim($f(STDIN));
goto a;

👾 Tested on OS X with PHP 5.5 and 5.6 👾


Edits

  • Added 12 bytes. Fixed exit on Done and golfed some strings.
  • Added 306 bytes. Fixed one or two bugs and added support for the bonuses. With the bonuses it's now nearly 15 bytes shorter.
  • Added 66 bytes. Had to format the money differently, also had to accept more values ([A-Z][0-9]) for Invalid code.
  • Saved 10 bytes. Found one whitespace and turned some .= into = were applicable.
  • Saved 36 bytes due to some refactoring.
  • Saved 20 bytes by refactoring some loops and assignments.
\$\endgroup\$
5
  • \$\begingroup\$ Wow, that was very fun to play! The only issue I noticed: You need to format prices like 3 and 3.5 into $3.00 and $3.50. \$\endgroup\$
    – geokavel
    Commented Dec 1, 2015 at 17:15
  • \$\begingroup\$ Also, when I type in J5 I should get the message : "Invalid code". \$\endgroup\$
    – geokavel
    Commented Dec 1, 2015 at 17:21
  • \$\begingroup\$ @geokavel Thanks for your feedback. I've added the correct money format and it also accepts more variants for Invalid code now. It's a fun challenge - even though it has lots of options … 1K characters speak for themselves … :) \$\endgroup\$ Commented Dec 1, 2015 at 20:40
  • \$\begingroup\$ Congratulations, by a narrow margin, you are the winner! Thank you to both players for being worthy competitors, and fervently trying to out-golf each other! \$\endgroup\$
    – geokavel
    Commented Dec 9, 2015 at 5:48
  • \$\begingroup\$ @geokavel It was a fun challenge and a nice ping pong between Stephan and me. If somebody will come up with a shorter solution in the future, I'll be there. ;) \$\endgroup\$ Commented Dec 9, 2015 at 9:54
6
\$\begingroup\$

C# 6 (.Net Framework 4.6), 1356 1324 1304 1311 1307 1238 1235 1231 1220 Letters - 14 Letters (Emoji Numbers) - (15% Jam Bonus + 10% ID Bonus) = 1017 993 978 972.75 969.75 918 915.75 904.5 Letters

using h=System.Console;class c{static void Main(){double b=0,z,p,x=0,i=57157,j,n,r,c,m;var a=1==0;var d=new System.Collections.Hashtable();for(;i<57213;)d.Add(x++,""+(char)55356+(char)i++);for(var e=new System.Random();;){for(w("  1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣"),x=0;x<8;x++,w(""))for(h.Write((char)('A'+x)),i=0;i<7;)h.Write(" "+d[x*7+i++]);for(;;){var k=h.ReadLine();if(k[0]==47){var l=k[1];z=b;b+=l=='N'?.05:l=='D'?.1:l=='Q'?.25:l=='O'?1:l=='F'?5:l=='T'?10:0;a=a|z!=b;w(z!=b?$"Balance {b:0.00}":"Money not Accepted");}else{if(k=="Done"){for(;b/.25>=1;b-=.25)h.Write("Q");for(;b/.1>=1;b-=.1)h.Write("D");for(;b/.05>=1;b-=.05)h.Write("N");return;}r=k[0]-65;c=k[1]-49;if(r<0|r>7|c<0|c>6)w("Invalid code");else{p=1+r/2;if(!a|p>b)w($"{(a&p>b?"Insufficient Funds. ":"")}Item Cost: ${p:0.00}");else{m=r*7+c;if(d[m]=="🚫")w("Item out of stock");else{if(r>6&c<6){j=e.Next(13,20);n=e.Next(13,20);w($"ID required: {j}x{n} = ?");if(int.Parse(h.ReadLine())!=j*n){w("🔞");break;}}b-=p;w($"Item at {k} Purchased. Remaining Balance: ${b:0.00}");if(e.Next(10)==1)for(w("Jam!!!");;){var f=h.ReadLine();if((f=="punch"|f=="kick")&e.Next(10)<2)break;}w(""+d[m]);d[m]="🚫";if(b==0)return;break;}}}}}}}static void w(string s)=>h.WriteLine(s);}

slightly more ungolfed for use in LinqPad 5 (C# 6). Use h=System.Console in F4.

void Main()
{
    double b=0,z,p,x=0,i=57157,j,n,r,c,m;
    var a = 1==0;
    var d = new Hashtable();
    for (; i < 57213;) d.Add(x++, ""+(char)55356 + (char)i++);
    for (var e = new Random(); ;)
    {
        for (w("  1️⃣ 2️⃣ 3️⃣ 4️⃣ 5️⃣ 6️⃣ 7️⃣"), x = 0; x < 8; x++, w(""))
            for (h.Write((char)('A' + x)), i = 0; i < 7;)
                h.Write(" " + d[x * 7 + i++]);

        for (; ;)
        {
            var k = h.ReadLine();
            if (k[0] == 47)
            {
                var l = k[1];
                z = b;
                b += l == 'N' ? .05 : l == 'D' ? .1 : l == 'Q' ? .25 : l == 'O' ? 1 : l == 'F' ? 5 : l == 'T' ? 10 : 0;
                a = a|z!=b;
                w(z != b ? $"Balance {b:0.00}" : "Money not Accepted");
            }
            else
            {
                if (k == "Done")
                {
                    for (; b / .25 >= 1; b -= .25) h.Write("Q");
                    for (; b / .1 >= 1; b -= .1) h.Write("D");
                    for (; b / .05 >= 1; b -= .05) h.Write("N");
                    return;
                }
                r = k[0] - 65;
                c = k[1] - 49;
                if(r<0|r>7|c<0|c>6)w("Invalid code");
                else
                {
                    p = 1 + r / 2;
                    if (!a | p > b) w($"{(a&p>b?"Insufficient Funds. ":"")}Item Cost: ${p:0.00}");
                    else
                    {
                        m = r * 7 + c;
                        if (d[m] == "🚫")
                            w("Item out of stock");
                        else
                        {
                            if (r > 6 & c < 6)
                            {
                                j = e.Next(13, 20);
                                n = e.Next(13, 20);
                                w($"ID required: {j}x{n} = ?");
                                if (int.Parse(h.ReadLine()) != j * n)
                                {
                                    w("🔞");
                                    break;
                                }
                            }

                            b -= p;
                            w($"Item at {k} Purchased. Remaining Balance: ${b:0.00}");
                            if (e.Next(10) == 1)
                                for (w("Jam!!!"); ;)
                                {
                                    var f = h.ReadLine();
                                    if ((f == "punch" | f == "kick") & e.Next(10) < 2)
                                        break;
                                }
                            w(""+d[m]);
                            d[m] = "🚫";
                            if (b == 0) return;
                            break;
                        }
                    }
                }
            }
        }
    }
}

void w(string s)=>h.WriteLine(s);

edit: thanks for the for(;;) hint ;)

edit: Strike !! Better than PHP ;-)

edit: Removed 24 Letters. Still something left for php.

edit: Removed 20 Letters by switching from decimal to double.

edit: Removed 7 Letters by switching to Emoji Numbers

edit: Removed 4 Letters by switching everything to double instead of a mix of double and ints.

edit: Removed 69 Letters - found dead code o_O Best Entry C# :D

edit: Removed 3 Letters - switched last if-else statement

edit: Removed 4 Letters - switched from short-circuit operators (&& and ||) to normal & and |

edit: Removed 11 Letters - switched from h.WriteLine to w() with conditional verbatim string.

\$\endgroup\$
8
  • \$\begingroup\$ Looks good. I was able to test it here with static input. Is there a way for me to test it online or on Mac with dynamic input? \$\endgroup\$
    – geokavel
    Commented Dec 1, 2015 at 17:31
  • \$\begingroup\$ You can test it with dynamic input here, although the emojis show up as ??. \$\endgroup\$
    – geokavel
    Commented Dec 1, 2015 at 17:37
  • \$\begingroup\$ The only issue I've found with yours is that "Invalid code" should only show up for a number-digit combo that is not in range. All unspecified errors should output "Error". \$\endgroup\$
    – geokavel
    Commented Dec 1, 2015 at 17:41
  • \$\begingroup\$ on my way @insertusernamehere :D \$\endgroup\$ Commented Dec 3, 2015 at 13:38
  • \$\begingroup\$ hey .... i've got it down to under php :) @insertusernamehere \$\endgroup\$ Commented Dec 11, 2015 at 15:55

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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