Introduction

Last year was my birthday (really!) and sadly I had to organise my own party. Well, now you know, couldn't you at least make the cake?

Challenge

Given an integer n as input, write a full program to output a birthday cake with n candles on.

Output

A piece of cake with one candle on is:

 $
 |
---
~~~
---

And a piece of cake with three candles on is:

 $ $ $
 | | |
-------
~~~~~~~
-------

I'm sure you can work it out from that

However, for input 0, you must output the following:

Congratulations on your new baby! :D

For input less than 0, you should output a candleless cake:

---
~~~
---

Nothing is allowed to be output to STDERR.

Trailing newlines and spaces are allowed.

Winning

Shortest code in bytes wins.

Leaderboard

var QUESTION_ID=57277;OVERRIDE_USER=30525;function answersUrl(e){return"http://api.stackexchange.com/2.2/questions/"+QUESTION_ID+"/answers?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+ANSWER_FILTER}function commentUrl(e,s){return"http://api.stackexchange.com/2.2/answers/"+s.join(";")+"/comments?page="+e+"&pagesize=100&order=desc&sort=creation&site=codegolf&filter="+COMMENT_FILTER}function getAnswers(){jQuery.ajax({url:answersUrl(answer_page++),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){answers.push.apply(answers,e.items),answers_hash=[],answer_ids=[],e.items.forEach(function(e){e.comments=[];var s=+e.share_link.match(/\d+/);answer_ids.push(s),answers_hash[s]=e}),e.has_more||(more_answers=!1),comment_page=1,getComments()}})}function getComments(){jQuery.ajax({url:commentUrl(comment_page++,answer_ids),method:"get",dataType:"jsonp",crossDomain:!0,success:function(e){e.items.forEach(function(e){e.owner.user_id===OVERRIDE_USER&&answers_hash[e.post_id].comments.push(e)}),e.has_more?getComments():more_answers?getAnswers():process()}})}function getAuthorName(e){return e.owner.display_name}function process(){var e=[];answers.forEach(function(s){var r=s.body;s.comments.forEach(function(e){OVERRIDE_REG.test(e.body)&&(r="<h1>"+e.body.replace(OVERRIDE_REG,"")+"</h1>")});var a=r.match(SCORE_REG);a&&e.push({user:getAuthorName(s),size:+a[2],language:a[1],link:s.share_link})}),e.sort(function(e,s){var r=e.size,a=s.size;return r-a});var s={},r=1,a=null,n=1;e.forEach(function(e){e.size!=a&&(n=r),a=e.size,++r;var t=jQuery("#answer-template").html();t=t.replace("{{PLACE}}",n+".").replace("{{NAME}}",e.user).replace("{{LANGUAGE}}",e.language).replace("{{SIZE}}",e.size).replace("{{LINK}}",e.link),t=jQuery(t),jQuery("#answers").append(t);var o=e.language;/<a/.test(o)&&(o=jQuery(o).text()),s[o]=s[o]||{lang:e.language,user:e.user,size:e.size,link:e.link}});var t=[];for(var o in s)s.hasOwnProperty(o)&&t.push(s[o]);t.sort(function(e,s){return e.lang>s.lang?1:e.lang<s.lang?-1:0});for(var c=0;c<t.length;++c){var i=jQuery("#language-template").html(),o=t[c];i=i.replace("{{LANGUAGE}}",o.lang).replace("{{NAME}}",o.user).replace("{{SIZE}}",o.size).replace("{{LINK}}",o.link),i=jQuery(i),jQuery("#languages").append(i)}}var ANSWER_FILTER="!t)IWYnsLAZle2tQ3KqrVveCRJfxcRLe",COMMENT_FILTER="!)Q2B_A2kjfAiU78X(md6BoYk",answers=[],answers_hash,answer_ids,answer_page=1,more_answers=!0,comment_page;getAnswers();var SCORE_REG=/<h\d>\s*([^\n,]*[^\s,]),.*?(\d+)(?=[^\n\d<>]*(?:<(?:s>[^\n<>]*<\/s>|[^\n<>]+>)[^\n\d<>]*)*<\/h\d>)/,OVERRIDE_REG=/^Override\s*header:\s*/i;
body{text-align:left!important}#answer-list,#language-list{padding:10px;width:290px;float:left}table thead{font-weight:700}table td{padding:5px}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script> <link rel="stylesheet" type="text/css" href="//cdn.sstatic.net/codegolf/all.css?v=83c949450c8b"> <div id="answer-list"> <h2>Leaderboard</h2> <table class="answer-list"> <thead> <tr><td></td><td>Author</td><td>Language</td><td>Size</td></tr></thead> <tbody id="answers"> </tbody> </table> </div><div id="language-list"> <h2>Winners by Language</h2> <table class="language-list"> <thead> <tr><td>Language</td><td>User</td><td>Score</td></tr></thead> <tbody id="languages"> </tbody> </table> </div><table style="display: none"> <tbody id="answer-template"> <tr><td>{{PLACE}}</td><td>{{NAME}}</td><td>{{LANGUAGE}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table> <table style="display: none"> <tbody id="language-template"> <tr><td>{{LANGUAGE}}</td><td>{{NAME}}</td><td>{{SIZE}}</td><td><a href="{{LINK}}">Link</a></td></tr></tbody> </table>

  • 159
    Happy Birthday! – Dennis Sep 8 '15 at 6:40
  • 1
    is leading whitespace (for example 2 newlines) allowed for the candleless cake? – Level River St Sep 8 '15 at 7:16
  • 2
    @steveverrill Yes – anon Sep 8 '15 at 7:21
  • 2
    @PaŭloEbermann A cake of 3-width – anon Sep 8 '15 at 20:05
  • 4
    @jvriesem No. It's code for snippet. – sigod Sep 11 '15 at 10:54

42 Answers 42

up vote 35 down vote accepted

Pyth, 73 72 71 69 67 bytes

?Qjb+m*+\ dQ"$|"*RhyeS,1Q"-~-""Congratulations on your new baby! :D

Try it online.

Output for n < 0 contains 2 leading newlines, as permitted in comments. To get rid of them, use

?QjbfT+jR*\ hQ"$|"*RhyeS,1Q"-~-""Congratulations on your new baby! :D
  • The smallest code, ofcourse, appears to be least human readable. – Sathish Sanjeevi Oct 7 '16 at 13:39
  • WTF is this??? LOL – Starx Jan 11 '17 at 9:28

CJam, 76 75 bytes

ri_W>\_1e>)" $ |--~~--"2/f*Wf<N*"Congratulations on your new baby! :D"?_8>?

Try it online in the CJam interpreter.

How it works

ri           e# Read an integer from STDIN.
_W>          e# Check if it is greater than -1.
\_           e# Swap input and Boolean and copy the input.
1e>)         e# Take the maximum of input and 1 and increment the result.
             e# Let's call the result R.
" $ |--~~--" e# Push that string.
2/           e# Split it into [" $" " |" "--" "~~" "--"].
f*           e# Repeat each chunk R times.
Wf<          e# Discard the last character of each repeated chunk.
N*           e# Join the repreated chunks, separating by linefeeds.

"Congratulations on your new baby! :D"

?            e# If the input is non-zero, select the cake; else, keep the string.
_8>          e# Push a copy and discard the first 8 characters (single candle).
?            e# Select the unmodified cake/string if the input was non-negative,
             e# a candleless cake otehrwise.

Ruby, 120 bytes

Revision 1 (120 bytes)

18 bytes saved thanks to manatwork

n=gets.to_i
puts ['Congratulations on your new baby! :D',%w{\ $ \ | - ~ -}.map{|e|e.ljust 2*n+1,e},'---
~~~
---'][n<=>0]

Revision 0 (138 bytes)

n=gets.to_i
n>0&&[' $',' |',?-,?~,?-].each{|e|puts''.rjust(2*n+1,e)}
puts ['Congratulations on your new baby! :D','','---
~~~
---'][n<=>0]

For positive numbers: this iterates through a string corresponding to each line of the cake. These are used as pad strings to right justify the empty string to length 2*n+1. This avoids any complications with having to print an odd number of characters, when the natural repetition is equal to the pitch of the candles (i.e. 2 characters.) n>0&& is necessary to avoid outputting a single column in case of input zero.

For all numbers: "n<=>0" finds the sign of the input. The baby message is output for n=0, and an empty string for positive n (as correct output has already been given above.) For negative n, Ruby interprets the -1 as meaning the last element of the array, and outputs the candleless cake.

  • 1
    Aren't there too many puts? n=gets.to_i puts ['Congratulations on your new baby! :D',%w{\ $ \ | - ~ -}.map{|e|''.rjust 2*n+1,e},'--- ~~~ ---'][n<=>0] – manatwork Sep 8 '15 at 11:09
  • 1
    One more: ''.rjuste.ljust. – manatwork Sep 8 '15 at 11:20
  • @manatwork thanks, I should have spotted that, but I did this very quick. I didn't know about backslash space with %w. And e.just: very clever. – Level River St Sep 8 '15 at 12:18

R, 157

write(if(n<-scan()){
z=matrix(c("$","|","-","~","-"),N<-2*max(1,n)+1,5,T)
z[seq(1,N,1+(n>0)),1:2]=" "
z}else"Congratulations on your new baby! :D","",N,F,"")
  • 2
    I'm learning a lot on write parameters there. Brilliant one – Tensibai Sep 9 '15 at 7:31

R, 228 226 220 221 Bytes


Last edit to correct the candleless cake, was as wide as the positive one on negative cases, thanks @CathG and @jbaums for the feedback

n=scan()
s=strsplit
p=paste0
a=b='-~-'
if(!n)cat('Congratulations on your new baby! :D')else{
if(n>0){a=p('  ',a);b=p('$|',b)}else n=1
a=s(a,'')
b=s(b,'')
d=data.frame(rep(c(a,b),n),a)
cat(do.call(p,d),sep="\n")}
  • 1
    A couple of bytes can be saved: <- can be =, and you can use a=b='-~-'. – jbaums Sep 8 '15 at 11:37
  • Ho, thanks @jbaums forget the first = and didn' tthough of the dual assignation – Tensibai Sep 8 '15 at 11:44
  • You have a bracket issue somewhere, I get Error: unexpected '}' – flodel Sep 8 '15 at 23:48
  • @flodel corrected. Copy/paste issue – Tensibai Sep 9 '15 at 5:27

JavaScript ES6, 136

Using alert for output - bad proportional font and the result is ugly. In the snippet below the alert is redirect to the snipped body, giving a better result.
The newline inside backticks is significant and counted.

Test running the snippet in Firefox.

/* Redefine alert for testing purpose */ alert=x=>O.innerHTML=x;

alert((n=+prompt())?[...'$|-~-'].map((c,i)=>(i>1?c:' '+c).repeat(i>1?n>0?n-~n:3:n>0&&n)).join`
`:'Congratulations on your new baby! :D')
<pre id=O></pre>

Less golfed

n=+prompt(); // get input and convert to number

if (n) { // if n != 0 prepare the cake
   output = [...'$|-~-'].map( // for each char of the five lines
     (c,i) => (i>1 ? c : ' '+c) // in line 0 and 1 symbols are space separated
     // if n < 0 repeat count is 0 for line 0 and 1, 3 for the other
     // if n > 0 repeat count is n for line 0 and 1, n+n+1 for the other
     .repeat(i>1 ? n>0 ? n-~n : 3 : n>0 && n) // 
   ).join`\n`;
}
else {
    output = 'Congratulations on your new baby! :D');
}

alert(output);
  • Try this $('div pre code')[2].innerHTML ;) – Vasu Adari Sep 11 '15 at 13:34

R, 279 bytes

Interactive version (286 bytes):

b<-function(){
n=scan()
if(n==0)cat("Congratulations on your new baby! :D\n")
if(n>0){k=2*n+1;m=rep("-",k);cat(paste(c(rep(c(" ","$"),l=k),"\n",rep(c(" ","|"),l=k),"\n",m,"\n",rep("~",k),"\n",m,"\n"),collapse=""))}
if(n<0){m=rep("-",3);cat(paste(c(m,"\n",rep("~",3),"\n",m,"\n"),collapse=""))}
}

Not interactive version (279 bytes):

b<-function(n){
if(n==0)cat("Congratulations on your new baby! :D\n")
if(n>0){k=2*n+1;m<-rep("-",k);cat(paste(c(rep(c(" ","$"),l=k),"\n",rep(c(" ","|"),l=k),"\n",m,"\n",rep("~",k),"\n",m,"\n"),collapse=""))}
if(n<0){m=rep("-",3);cat(paste(c(m,"\n",rep("~",3),"\n",m,"\n"),collapse=""))}
}

MATLAB / Octave, 194 198 195 189 171 167 bytes

Happy birthday to you Beta Decay! :)

Thanks to HamtaroWarrior for shaving off 4 bytes!


n=input('');m='$|'.';d='  '.';if(n==0)disp('Congratulations on your new baby! :D'),break;elseif(n<0)m=d;n=1;end,disp([[d repmat([m d],1,n)];repmat('-~-'.',1,2*n+1)]);

Sample Runs

I placed this into a script file called happy_birthday.m, then ran it a few times in the command prompt. Take note that when you enter in a negative number, there are two leading carriage returns, but that's allowed in this challenge:

>> happy_birthday
-1


---
~~~
---
>> happy_birthday
0
Congratulations on your new baby! :D
>> happy_birthday
1
 $ 
 | 
---
~~~
---
>> happy_birthday
2
 $ $ 
 | | 
-----
~~~~~
-----
>> happy_birthday
3
 $ $ $ 
 | | | 
-------
~~~~~~~
-------
>> happy_birthday
4
 $ $ $ $ 
 | | | | 
---------
~~~~~~~~~
---------
>> happy_birthday
5
 $ $ $ $ $ 
 | | | | | 
-----------
~~~~~~~~~~~
-----------

Code with spacing and explanations

% Get the input number from the user
n=input('');

% If the number is positive, the core candle sequence is going to be a column vector of a $ followed by a | character
m='$|'.';    

%// Array of one column and it has two spaces - going to use more than once
d = '  '.';

% If the number is 0, display the congratulations message and get out
if(n==0)
    disp('Congratulations on your new baby! :D')
    break;

% m stores the core candle sequence for displaying on the screen
% If the number is negative, the core candle sequence is going to be a column of two blank spaces
elseif(n<0)
    m=d; 
    n=1; % n is set to 1 as this is the number of "blank candles" we want to display
end

% This displays candles and the base together
% The top half is the displaying of the candles
% It is a column vector of spaces, followed by pairs of $,| in a column
% and another column of spaces - repeated n times
% The bottom half is the displaying of the cake
% The bottom half is a column vector of -,~,- for the base of the cake
% and is repeated 2*n + 1 times to match the candle display
disp([[d repmat([m d],1,n)];repmat('-~-'.',1,2*n+1)]);

The displaying part at the end is probably the most obfuscated part of the code. This is going to display a 5 row character matrix where the first two rows consists of the candles and the last three rows consists of the base of the cake.

The basis for the top half of the display is either two spaces in a column followed by another two spaces in another column in the case that the age is negative, or a $,- in a column followed by two spaces in another column. This is a 2 x 2 character matrix. The basis for the bottom half of the display is a single column vector of -,~,- which is a 3 x 1 character matrix.

The display command first tackles the first two rows of the cake by placing two spaces in the first column, followed pairs of a column of $,- or a column of spaces if n is negative, which gets changed to n=1, and another column of two spaces repeated for a total of n times. The next three rows simply replicate the -,$,- column vector for 2*n + 1 times to align the candles together with the base, thus completing the picture.

Try it online!

You can try this online using IDEOne's Octave compiler: http://ideone.com/4qXDdJ - however, there is a slight bug when reading in values from standard input. As such, the script is slightly modified where you have to change the value of n at the beginning of the code. Fork a new version of the script and change this to whatever integer value suits you in order to see what the output looks like.

  • 1
    Pity you can't save the dot in ' '.' ! – Luis Mendo Sep 8 '15 at 21:54
  • 1
    @LuisMendo - I know!... though I'm sure you don't object to the use of the transpose that way :) – rayryeng Sep 8 '15 at 21:55
  • 1
    Hahaha. I was once told here that this site is for abusing the language. And I follow that rule faithfully! – Luis Mendo Sep 8 '15 at 21:56
  • 1
    @LuisMendo - I really wanted to get rid of the dot... but couldn't because MATLAB interprets that as a single quotation in the string :(. Would have saved me a few bytes... but after many iterations, this was the best I could come up with. – rayryeng Sep 8 '15 at 22:00
  • @LuisMendo - managed to shrink it down to 171! – rayryeng Sep 9 '15 at 2:05

JavaScript, 143 153 Bytes

for(v in k=' $ 0 | 0---0~~~0---'.split(+!(n=+prompt(c=''))))c+=k[v].repeat(n<0?1:n)+'\n';alert(n>0?c:n?c.slice(8):'Congratulations on your new baby! :D')

To see output in mono space font replace 'alert' by 'console.log'

  • Welcome to PPCG! :) – anon Sep 8 '15 at 13:47
  • 2
    Sadly this displays the congratulation message for negative input too. – manatwork Sep 8 '15 at 13:50
  • @BetaDecay tnx :-) – Nainemom Sep 8 '15 at 13:57
  • Manatwork's right :/ I don't know enough JS to help – anon Sep 8 '15 at 14:00
  • @manatwork yes you right! i misunderstood! – Nainemom Sep 8 '15 at 14:01

Moonscript, 141 bytes

n=0+io.read!
m=3
f=(s='-')->print s\rep m
if n>0
 m=n
 m=1+2*m,f' $',f' |'
if n==0 print"Congratulations on your new baby! :D"else f f!,f'~'
  • 3
    Welcome to Programming Puzzles and Code Golf! – Dennis Sep 8 '15 at 22:39

rs, 117 bytes

^0$/Congratulations on your new baby! :D
-.+/-1
$$r=)^^(\1)\n
$$m=-(--)^^(\1)\n
(\d+)/ ($ $r (| $r$m~(~~$r$m
^-[^-]+/

Live demo and test cases.

JavaScript ES6, 154 chars

alert((n=+prompt())?((n>0?` $
 |
`:(n=1)&&"")+`--
~~
--`).replace(/../gm,x=>x.repeat(n)).replace(/(.).*/gm,"$&$1"):"Congratulations on your new baby! :D")

And one more (154 too)

alert((n=+prompt())?` $
 |
--
~~
--`.slice(n<0?(n=1)-9:0).replace(/../gm,x=>x.repeat(n)).replace(/(.).*/gm,"$&$1"):"Congratulations on your new baby! :D")

To see output in monospace font (and move ouptut to the console) use

alert=x=>console.log(x)

Mouse, 164 161 bytes

?N:N.0=["Congratulations on your new baby"33!'" :D"$]N.0>[#P,32!'36,N.;#P,32!'124,N.;]N.0<[1N:]2N.*1+D:#P,45,D.;#P,126,D.;#P,45,D.;$P0I:(I.2%=0=^1%!'I.1+I:)"!"@

Mouse is clearly not an ideal choice for this task, but it was fun.

Ungolfed:

? N.                               ~ Read N from STDIN
N. 0 = [                           ~ Have a baby for N == 0
  "Congratulations on your new baby" 33 !' " :D" $
]
N. 0 > [                           ~ If N > 0...
  #P, 32 !' 36, N.;                ~ Print the candle flames
  #P, 32 !' 124, N.;               ~ Print the candle sticks
]
N. 0 < [                           ~ If N < 0...
  1 N:                             ~ Set N = 1
]
2 N. * 1 + D:                      ~ Assign D = 2N + 1
#P, 45, D.;                        ~ Print the top cake layer
#P, 126, D.;                       ~ Print the middle layer
#P, 45, D.;                        ~ Print the bottom
$P                                 ~ Define the printing macro...
  0 I:                             ~ Initialize I to 0
  ( I. 2% = 0 = ^                  ~ While I != the second input
    1% !'                          ~ Print the first input
    I. 1 + I:                      ~ Increment I
  )
  "!"                              ~ Print a newline
@

The stack can contain only integers. !' takes an integer off the stack and prints the ASCII character with that code.

CoffeeScript, 160 bytes

f=(i,l=[" $ "," | ",_="---","~~~",_])->if!i then"Congratulations on your new baby! :D"else (if i<0then[_,l[3],_]else i++;(Array(i).join r for r in l)).join "\n"

Ungolfed:

f=(i)->
  l = [" $ "," | ","---","~~~","---"] # The layers
  if i == 0
    return "Congratulations on your new baby! :D"
  else if i < 0
    return [l[2], l[3], l[2]].join("\n")
  else
    i++
    return (Array(i).join(r) for r in l).join("\n")

Use it like:

f(10) # In the CoffeeScript console
alert(f(~~prompt("Y"))) # Browser, alert popup
console.log(f(~~prompt("Y"))) # Browser, log to console, and thus has monospace font

Try it online: link (Contains some custom display code, so everything looks soo nice...)

Ooops, almost forgot it! Happy birthday, @BetaDecay!

C, 392 bytes

(known segmentation fault if no arguments are given)

#include <stdio.h>
#define P(s) printf(s);
#define F for(i=0;i<n;i++)
#define A(s, e) F{P(s)}P(e "\n")
int main(int c, char**v){int i,n=atoi(v[1]);if(n<0){n=3;A("-",)A("~",)A("-",)}else if(!n)P("Congratulations on your new baby! :D\n")else{A(" $",)A(" |",)A("--","-")A("~~","~")A("--","-")}}

Unminified and copiously spaced

#include <stdio.h>
#define P(s) printf ( s ) ;
#define F for ( i = 0 ; i < n ; i++ )
#define A(s, e) F { P ( s ) } P ( e "\n" )
int main ( int c, char ** v )
{
        int i, n = atoi ( v [ 1 ] ) ; 
        if ( n < 0 ) 
        {   
                n = 3 ; 
                A ( "-", )
                A ( "~", )
                A ( "-", )
        }   
        else if ( ! n ) 
                P ( "Congratulations on your new baby! :D\n" )
        else
        {   
                A ( " $", )
                A ( " |", )
                A ( "--", "-" )
                A ( "~~", "~" )
                A ( "--", "-" )
        }   
}
  • Without C boilerplate the #defines's and main body total 247. – Funmungus Sep 10 '15 at 23:00
  • Welcome to PPCG! This is a really good answer--I especially like the macro abuse with A. I do see a couple more spaces that could be removed, and you can get rid of i by using c for the loop variable instead. Also, at least with gcc, including stdio.h or specifying the return type of main() isn't necessary: the compiler complains about it, but hey, this is code golf. ;) – DLosc Sep 10 '15 at 23:39

Powershell, 139 134 132 126 bytes

$n=$args[0];if($n){$d=3;if($n-gt0){' $'*$n;' |'*$n;$d=$n*2+1}'-'*$d;'~'*$d;'-'*$d}else{'Congratulations on your new baby! :D'}
  • You can save 5 bytes by using $input instead of $args[0] (question doesn't say to use arguments instead of stdin), and you can use unary + to convert to a number: $n=+$input; – briantist Sep 12 '15 at 1:59
  • +$input did not seem to work - it complained that $input is an enumerator and does not have an addition operator. However, $args[0] can and should be an actual int, allowing us to at least drop the [int]. – Justin Dunlap Sep 14 '15 at 6:57
  • 1
    So, this is really old, but you can save a couple bytes by moving the input into the if and swapping the else for an ;exit -- 122 bytes -- if($n=$args[0]){$d=3;if($n-gt0){' $'*$n;' |'*$n;$d=$n*2+1}'-'*$d;'~'*$d;'-'*$d;exit}'Congratulations on your new baby! :D' – AdmBorkBork Oct 12 '16 at 16:46

Ceylon, 322 307 300 282 278 260 bytes

shared void run(){if(exists t=process.readLine(),exists n=parseInteger(t)){String r(String s)=>s.repeat(n);print(n>0thenr(" $")+"\n"+r(" |")+"\n"+r("--")+"-\n"+r("~~")+"~\n"+r("--")+"-"else(n<0then"---\n~~~\n---"else"Congratulations on your new baby! :D"));}}

The not yet golfed original (assuming negative cakes have width 3 instead of –2·n+1):

shared void birthdayCake() {
    if (exists text = process.readLine(), exists number = parseInteger(text)) {
        switch (number <=> 0)
        case (equal) {
            print("Congratulations on your new baby! :D");
        }
        case (smaller) {
            print("---\n~~~\n---");
        }
        case (larger) {
            print(" $".repeat(number));
            print(" |".repeat(number));
            print("--".repeat(number) + "-");
            print("~~".repeat(number) + "~");
            print("--".repeat(number) + "-");
        }
    }
}

This features the condition list in the if statement, each condition defining a value usable in the following conditions and in the body. Because they have the exist, the condition is only fulfilled when the values is not null, and thus the compiler knows the values is not null for the following code. (If nothing is entered (EOF), readline returns null. If parseInteger hits a non-integer, it also returns null. Our program then does nothing. As the behavior for those cases was not defined, I guess this is okay.)

Also we have the <=> operator, which maps to the Comparable.compare method, and returns a Comparison object, i.e. one of equal, smaller and larger. The compiler knows that those exhaust the Comparison type, so no else clause is needed in our switch statement.

The repeat method of class String does what one would expect. It is actually inherited from the same-named method in interface Iterable (as a string is, beside other stuff, just a list of characters).

Replacing my identifiers by one-letter ones and removing unneeded white space gives 322 characters:

shared void b(){if(exists t=process.readLine(),exists n=parseInteger(t)){switch(n<=>0)case (equal){print("Congratulations on your new baby! :D");}case(smaller){print("---\n~~~\n---");}case(larger){print(" $".repeat(n));print(" |".repeat(n));print("--".repeat(n)+"-");print("~~".repeat(n)+"~");print("--".repeat(n)+"-");}}}

Replacing the series of print by explicit \ns (and one single print) brings it down to 307:

shared void b(){if(exists t=process.readLine(),exists n=parseInteger(t)){switch(n<=>0)case(equal){print("Congratulations on your new baby! :D");}case(smaller){print("---\n~~~\n---");}case(larger){print(" $".repeat(n)+"\n"+" |".repeat(n)+"\n"+"--".repeat(n)+"-\n"+"~~".repeat(n)+"~\n"+"--".repeat(n)+"-");}}}

I tried alias-importing of repeat as r, but it doesn't help (the import declaration adds 40 characters, and we can only save 25 by replacing repeat with r).

What slightly helps, is using n.sign instead of n<=>0. While these two expressions have the same textual length, they have different types: the latter one is of type Comparison mentioned before (which has the three values smaller, larger and equal), the former one has type Integer, with the values -1, 1, 0 ... and because Integer has many more values, we also need an else clause. This is 300 characters long:

shared void b(){if(exists t=process.readLine(),exists n=parseInteger(t)){switch(n.sign)case(0){print("Congratulations on your new baby! :D");}case(-1){print("---\n~~~\n---");}case(1){print(" $".repeat(n)+"\n"+" |".repeat(n)+"\n"+"--".repeat(n)+"-\n"+"~~".repeat(n)+"~\n"+"--".repeat(n)+"-");}else{}}}

Here with whitespace:

shared void b() {
    if (exists t = process.readLine(), exists n = parseInteger(t)) {
        switch (n.sign)
        case (0) {
            print("Congratulations on your new baby! :D");
        }
        case (-1) {
            print("---\n~~~\n---");
        }
        case (1) {
            print(" $".repeat(n) + "\n" +
                        " |".repeat(n) + "\n" +
                        "--".repeat(n) + "-\n" +
                        "~~".repeat(n) + "~\n" +
                        "--".repeat(n) + "-");
        }
        else {}
    }
}

We can safe some more by resigning about our switch statement and using if, coming to 282 characters(=bytes):

shared void b(){if(exists t=process.readLine(),exists n=parseInteger(t)){if(n==0){print("Congratulations on your new baby! :D");}else if(n<0){print("---\n~~~\n---");}else{print(" $".repeat(n)+"\n"+" |".repeat(n)+"\n"+"--".repeat(n)+"-\n"+"~~".repeat(n)+"~\n"+"--".repeat(n)+"-");}}}

Formatted:

shared void b() {
    if (exists t = process.readLine(), exists n = parseInteger(t)) {
        if (n == 0) {
            print("Congratulations on your new baby! :D");
        } else if (n < 0) {
            print("---\n~~~\n---");
        } else {
            print(" $".repeat(n) + "\n" +
                        " |".repeat(n) + "\n" +
                        "--".repeat(n) + "-\n" +
                        "~~".repeat(n) + "~\n" +
                        "--".repeat(n) + "-");
        }
    }
}

We can safe another byte by swapping the cases around, since > is shorter than ==. Another "annoyance" is the repeated repeat(n) – we can define a local function (a closure, it remembers n from the defining block) with a shorter name:

String r(String s) => s.repeat(n);

This is a shorter way of writing this:

String r(String s) {
    return s.repeat(n);
}

We could use function instead of the return type for type inference, but this is not shorter. This gives us 278 bytes:

shared void b(){if(exists t=process.readLine(),exists n=parseInteger(t)){if(n>0){String r(String s)=>s.repeat(n);print(r(" $")+"\n"+r(" |")+"\n"+r("--")+"-\n"+r("~~")+"~\n"+r("--")+"-");}else if(n<0){print("---\n~~~\n---");}else{print("Congratulations on your new baby! :D");}}}

Formatted:

shared void b() {
    if (exists t = process.readLine(), exists n = parseInteger(t)) {
        if (n > 0) {
            String r(String s) => s.repeat(n);
            print(r(" $") + "\n" + r(" |") + "\n" + r("--") + "-\n" + r("~~") + "~\n" + r("--") + "-");
        } else if (n < 0) {
            print("---\n~~~\n---");
        } else {
            print("Congratulations on your new baby! :D");
        }
    }
}

Actually, using the then and else operators instead of the if statements allows us to save some calls of print (and some braces):

shared void run() {
    if (exists t = process.readLine(), exists n = parseInteger(t)) {
        String r(String s) => s.repeat(n);
        print(n > 0 then r(" $") + "\n" +
                        r(" |") + "\n" +
                        r("--") + "-\n" +
                        r("~~") + "~\n" +
                        r("--") + "-"
                    else (n < 0
                        then "---\n~~~\n---"
                        else "Congratulations on your new baby! :D"));
    }
}

This is just 261 bytes:

shared void run(){if(exists t=process.readLine(),exists n=parseInteger(t)){String r(String s)=>s.repeat(n);print(n>0thenr(" $")+"\n"+r(" |")+"\n"+r("--")+"-\n"+r("~~")+"~\n"+r("--")+"-"else(n<0then"---\n~~~\n---"else"Congratulations on your new baby! :D"));}}

(I used run instead of b for the function name because this way it can be run with ceylon run without passing a function name.)

My Github repository has a commented version of this.

Python 2, 158 bytes


i=input()
l='\n'
s=''
if i==0:s='Congratulations on your new baby! :D'
elif i<0:s='---\n~~~\n---'
else:n=i*2+1;a=l+'-'*n;s=' $'*i+l+' |'*i+a+l+'~'*n+a
print s

golflua, 113 characters

\p(c)w(S.t(c,n))~$n=I.r()+0?n==0w"Congratulations on your new baby! :D"!??n>0p" $"p" |"n=n*2+1!?n=3$p"-"p"~"p"-"$

Sample run:

bash-4.3$ golflua -e '\p(c)w(S.t(c,n))~$n=I.r()+0?n==0w"Congratulations on your new baby! :D"!??n>0p" $"p" |"n=n*2+1!?n=3$p"-"p"~"p"-"$' <<< 5
 $ $ $ $ $
 | | | | |
-----------
~~~~~~~~~~~
-----------

bash-4.3$ golflua -e '\p(c)w(S.t(c,n))~$n=I.r()+0?n==0w"Congratulations on your new baby! :D"!??n>0p" $"p" |"n=n*2+1!?n=3$p"-"p"~"p"-"$' <<< 0
Congratulations on your new baby! :D

bash-4.3$ golflua -e '\p(c)w(S.t(c,n))~$n=I.r()+0?n==0w"Congratulations on your new baby! :D"!??n>0p" $"p" |"n=n*2+1!?n=3$p"-"p"~"p"-"$' <<< -5
---
~~~
---

Python 2, 150 bytes

m=input()
n=m-1
p="---"+"--"*n
g="\n~~~"+"~~"*n+"\n"
if m>0:print" $"*m,"\n"," |"*m
s=p+g+p
print s if m!=0 else"Congratulations on your new baby! :D"

Close to author's Python :(

Perl, 139 127 117 bytes

Does not require '-n' or '-p' options.

Revision 3 (with thanks to Dom Hastings below):

$==<>;map{print$_ x(!$=||(/ /?$=:($=<1||$=)*2+1)).$/}!$=?'Congratulations on your new baby! :D':split 0,' $0 |0-0~0-'

Revision 2:

$n=<>;map{print$_ x($n==0?1:(/ /?$n:($n<1?1:$n)*2+1)).$/}$n==0?('Congratulations on your new baby! :D'):(' $',' |','-','~','-')

Revision 1:

$n=<>;map{print$_ x(($l=length())>2?1:($l==2?$n:($n<1?1:$n)*2+1)).$/}$n==0?('Congratulations on your new baby! :D'):(' $',' |','-','~','-')

Here's a version of revision 3 that doesn't have the leading blank new lines on negative input - 132 bytes.

$==<>;map{print$_ x(!$=||(/ /?$=:($=<1||$=)*2+1)).(/ /&&$=<0?'':$/)}!$=?'Congratulations on your new baby! :D':split 0,' $0 |0-0~0-'
  • Hey there, welcome! Thought I'd share a couple of ways you can save some bytes! You can drop the brackets around "Congratulations..." and if you replace ' $',' |','-','~','-' with split 0,' $0 |0-0~0-' you can drop those brackets too. Another save is replacing $n=<>;$n==0 with ($n=<>)==0. Also your ?1: could be || as you'd be saying $n==0 (which is 1) or your calculation. Hope that helps! – Dom Hastings Sep 8 '15 at 19:56
  • Hey there, thanks for that. Those brackets are a good one, and reminding me of the semantics of || is very helpful - it reminded me of $= to basically chomp the input, eliminating the requirement for specific 0 value tests. I'm not sure what you mean by $n=<>;$n==0 - my golf doesn't have that, and given I handle the input=0 case as input into the map{}(), I'm not sure how this would apply? Anyway, taking your comments and using $= as my variable, this has been golfed down to 117 parbytes. Thanks very much! – phillipo Sep 9 '15 at 11:13
  • No problem at all! Glad to have helped! What I mean was that you have $n=<>; at the beginning and then $n==0 at the back of the map{}... so I think you could have the check as ($n=<>)==0 but if you're using $= instead you might be able to shrink it more! I always forget which of $- or $= can't be negative so I didn't want to mention it and be wrong! :) – Dom Hastings Sep 9 '15 at 11:17

Pip, 74 + 1 = 75 bytes

Takes the age as a command-line argument. Requires the -n flag.

Y^"-~-"a?a<0?yX3(s.^"$|")XaALyX2*a+1"Congratulations on your new baby! :D"

Github repository for Pip

The command-line argument is stored in a. We split "-~-" into a list of characters and Yank it into the variable y. The rest of the program is a big ternary expression:

  • a?
    If a is truthy (i.e. not zero):
    • a<0?yX3
      If a is negative, return y with each element repeated 3 times: ["---";"~~~";"---"]
    • Else (a is positive):
      • (s.^"$|")Xa
        Split "$|" into a list of characters, prepend a space (s) to each, and repeat each resulting element a times
      • yX2*a+1
        Repeat each element of y 2*a+1 times
      • AL
        Append the two lists
  • Else (a is zero), return the congratulations string

At the end of the program, the -n flag ensures that lists are printed with elements on separate lines, thus displaying a properly layered cake.

Here are the steps for an input of 2:

Candles
["$";"|"]
[" $";" |"]
[" $ $";" | |"]

Cake
["-";"~";"-"]
["-----";"~~~~~";"-----"]

Put it together
[" $ $";" | |";"-----";"~~~~~";"-----"]

Final output
 $ $
 | |
-----
~~~~~
-----

Happy birthday!

Perl, 144 bytes

143 bytes of code, plus one extra byte for the -n switch to capture stdin.

if($_!=0){print$_>0?" \$"x$_.$/." |"x$_.$/:""x($_=1);$_=$_*2+1;print"-"x$_.$/."~"x$_.$/."-"x$_;exit}print"Congratulations on your new baby! :D"
  • 1
    print"-~-"=~s!.!$&x($_*2+1).$/!ger for the cake? – manatwork Sep 8 '15 at 15:36
  • If you change the switch to -p this will also work: $_=$_!=0?($_>0?' $'x$_.$/." |"x$_:""x($_=1))."-~-"=~s!.!$/.$&x($_*2+1)!ger:"Congratulations on your new baby! :D" – manatwork Sep 8 '15 at 15:49

SpecBAS, 164

Uses the apostrophe shortcut to move to new line

INPUT n: IF n=0 THEN PRINT "Congratulations on your new baby! :D" ELSE IF n<0 THEN PRINT "---"'"~~~"'"---" ELSE PRINT " $"*n'" |"*n'"-";"--"*n'"~";"~~"*n'"-";"--"*n

Formatted for easier reading

INPUT n
IF n=0 THEN PRINT "Congratulations on your new baby! :D" 
ELSE IF n<0 THEN PRINT "---"'"~~~"'"---" 
ELSE PRINT " $"*n'" |"*n'"-";"--"*n'"~";"~~"*n'"-";"--"*n

Python 3, 169 bytes

n=int(input())
m=max(n*2+1,3)
f=' {}'*n+'\n'+' {}'*n+'\n'+'-'*m+'\n'+'~'*m+'\n'+'-'*m
if n==0:f='Congratulations on your new baby! :D'
print(f.format(*['$']*n+['|']*n))

Julia, 143 bytes

n=int(readline())
p=println
l="\n"
n>0&&p(" \$"^n*l*" |"^n)
d=2(n<0?1:n)+1
p(d>1?"-"^d*l*"~"^d*l*"-"^d:"Congratulations on your new baby! :D")

Pretty straightforward. Ungolfed:

# Read n from STDIN and convert to an integer
n = int(readline())

# Print the candles for positive n
n > 0 && println(" \$"^n * "\n" * " |"^n)

# Define d as the width of the cake
d = 2(n < 0 ? 1 : n) + 1

# Newborns can't eat cake
if d > 1
    println("-"^d * "\n" * "~"^d * "\n" * "-"^d)
else
    println("Congratulations on your new baby! :D")
end

Lua, 299 Bytes

a=0+io.read() b=string.rep p=print if a==0 then p("Congratulations on your new baby! :D") else p(b(" ",a)..b("$ ",a)..("\n")..b(" ",a)..b("| ",a)) if a<0 or a==1 then p("---\n~~~\n---") else p(b(" ",a-1).."-"..b("-",2*a).."\n"..b(" ",a-1).."~"..b("~",2*a).."\n"..b(" ",a-1).."-"..b("-",2*a))end end
  • 1
    Welcome to PPCG! Your code as it stands seems to give an error (attempt to compare string with number), but adding 0+ before io.read() fixes it for me. You can also save quite a few bytes by assigning print and string.rep to single-character variables. – DLosc Sep 9 '15 at 1:04
  • @DLosc thanks good idea :) and yeah u were right with the error sorry about that – FabiF Sep 9 '15 at 8:28

Mathematica, 164 Bytes

Completely missed the candle-less cakes for n < 0, adding an additional 15 characters

r[a_,b_]:=StringRepeat[a,Abs@b];c=" $ ";t="---";m="~~~";f[n_]:=If[n>0,r[c,n]~~"\n",""]~~r[t,n]~~"\n"~~r[m,n]~~"\n"~~r[t,n];f[0]:="Congratulations on your new baby! :D"
  • 1
    Welcome to PPCG, this challenge is known as code golf, where you try and make your code as short as possible. This can be done by removing unnecessary whitespace and shortening variable names. – anon Sep 9 '15 at 14:38
  • Thanks @BetaDecay I was getting a fast version up.. minimising now. Cheers :D – Martin John Hadley Sep 9 '15 at 14:41
  • No problem. Looks good :) – anon Sep 9 '15 at 14:41

pb, 567 bytes

^w[B=45]{vb[-1]^b[0]>w[B!0]{t[B]b[0]<b[T]>>}<}w[B!0]{>}b[65]w[X!0]{<b[B-48]}>w[B!65]{t[B]<t[B*10+T]b[0]>b[T]>}b[0]<t[B]b[0]<[X]>b[T]<b[T]vw[B=0]{b[1]}t[B]b[0]^b[B*T]vw[X!3]{b[45]vb[126]vb[45]^^>}^<<t[B]<b[B+T]w[B!0]{<vw[X!3]{b[0]vb[0]vb[0]^^>}^<<t[B]vvw[T!0]{b[124]^b[36]v>>t[T-1]}v<w[X!-1]{b[45]vb[126]vb[45]^^<}}^[Y]^<[X]>w[B=0]{>vvw[X!-1]{b[0]vb[0]^<}^>}t[111]b[67]>b[T]>b[110]>b[103]>b[114]>b[97]>b[116]>b[117]>b[108]>b[97]>b[116]>b[105]>b[T]>b[110]>b[115]>>b[T]>b[110]>>b[121]>b[T]>b[117]>b[114]>>b[110]>b[101]>b[119]>>b[98]>b[97]>b[98]>b[121]>b[33]>>b[58]>b[68]

I have some regrets.

There are some ways that this could be better. For example, upon figuring out that the input is negative, I then proceed to deduce which negative number it is. This is unnecessary, and maybe one day I'll dive back into this code to stop it.

Don't try to run this in pbi's "watch" mode, if you input a negative it'll crash when it tries to print chr(-1).

Here's the explained version version where I took notes while writing the program. If you'd like to better understand how it works... good luck, and let me know if you figure it out.

# parse number

^w[B=45]{vb[-1]^b[0]>     # if negative, put a -1 at (0, 0)
    w[B!0]{t[B]b[0]<b[T]>>} # move all digits left
<}
w[B!0]{>}b[65]        # put an "A" at the end of the number
w[X!0]{<b[B-48]}      # subtract 48 from each byte to get the digits of the number
>w[B!65]{             # while there's no "A" at the second digit
    t[B]<t[B*10+T]      # set T to (first digit * 10) + second digit
    b[0]                # destroy first digit
    >b[T]               # override 2nd digit with T
    >}                  # point at second digit to restart loop
b[0]<t[B]b[0]<[X]>b[T]<b[T] # move number to (0, -1) and (1, -1)
vw[B=0]{b[1]}t[B]b[0]^b[B*T] # multiply (0, -1) by -1 if required
vw[X!3]{b[45]vb[126]vb[45]^^>} # draw the negative cake
^<<t[B]<b[B+T]w[B!0]{ # if it's positive
    <vw[X!3]{b[0]vb[0]vb[0]^^>} # erase the cake
    ^<<t[B]vvw[T!0]{b[124]^b[36]v>>t[T-1]} # draw the candles
    v<w[X!-1]{b[45]vb[126]vb[45]^^<}       # draw the cake
}^[Y]^<[X]>w[B=0]{>vvw[X!-1]{b[0]vb[0]^<}^>} # erase the part of the cake that would show up
# hardcoded string for input 0
t[111]b[67]>b[T]>b[110]>b[103]>b[114]>b[97]>b[116]>b[117]>b[108]>b[97]>b[116]>b[105]>b[T]>b[110]>b[115]>>b[T]>b[110]>>b[121]>b[T]>b[117]>b[114]>>b[110]>b[101]>b[119]>>b[98]>b[97]>b[98]>b[121]>b[33]>>b[58]>b[68]

ScriptCS (C# script), 221 bytes

Obviously this won't win any prizes, but...

var n=int.Parse(Env.ScriptArgs[0]);Console.WriteLine(n==0?"Congratulations on your new baby! :D":string.Join("\n",new[]{" $"," |","-","~","-"}.Select((s,i)=>string.Concat(Enumerable.Repeat(s,i>1?(n>0?n:1)*2+1:n>0?n:0)))))

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.