# Theme of my birthday: up in flames

I need to light some candles. In fact, I need to light an arbitrary amount of candles. But only the good ones.

OBJECTIVE Given a block of text (containing lit, valid, and invalid candles) and a number N as input, write a program or function that lights an amount exactly equal to N, minus the number of already-lit candles. If N is greater than the number of valid candles, the program should print the number of missing, valid candles. If no candles are present, the output should be :(.

Here are some valid candles:

.
|   .       .
|   |   .   \
|   |   |   /


(ending with a ., containing only | or balanced, not necessarily adjacent \ and /, can be of any length.)

Here are some invalid candles:

.       .   .   .
\       |   |   |
|           |   |
\   .   |   !


(unbalanced \, no candle stick, disconnected, non-| characters, not planted on the ground.)

A lit candle will replace the . on a valid candle with any of the following characters (your choice):

@ ^ & " ~


You must use at least one, and you get a -10% bonus for each character used in your program, in such a way that each character can appear on a lit candle. If you use the 🔥 emoji, you get a -15 byte bonus, which is applied before the percentage bonuses, if used. THE BYTE COUNT IS ROUNDED DOWN!

This is a , so the shortest code in bytes wins.

## Example IOs

input: 8,
.         .
| . ! . . |.  . . .
| | | | | | | | | |
output:
@         @
| @ ! @ @ |.  @ @ @
| | | | | | | | | |
input: 14,
// nothing
output: :(
input: 15,
.   ..  . .  ". .
| . ||  | |  || !
output: 9 // more candles required; 15 - (6 unlit) = 9 (thanks to @AndersKaseorg for catching my mistakes (plural)!)
input: 7,
.
/        ~
|        \  .
/  &   " /  |
\  | @ | | . . . . .
\  | | | | | 1 l I |
output:
&
/        ~
|        \  .
/  &   " /  |
\  | @ | | ^ . . . 🔥
\  | | | | | 1 l I |
input: 5,
. .             |
i Q no candl es . |3-.
output: :(


## Leaderboards

Here is a Stack Snippet to generate both a regular leaderboard and an overview of winners by language.

To make sure that your answer shows up, please start your answer with a headline, using the following Markdown template:

# Language Name, N bytes


where N is the size of your submission. If you improve your score, you can keep old scores in the headline, by striking them through. For instance:

# Ruby, <s>104</s> <s>101</s> 96 bytes


If there you want to include multiple numbers in your header (e.g. because your score is the sum of two files or you want to list interpreter flag penalties separately), make sure that the actual score is the last number in the header:

# Perl, 43 + 2 (-p flag) = 45 bytes


You can also make the language name a link which will then show up in the leaderboard snippet:

# [><>](http://esolangs.org/wiki/Fish), 121 bytes


var QUESTION_ID=59664,OVERRIDE_USER=8478;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>

• Oh, and it's my Birthday. – Conor O'Brien Oct 4 '15 at 19:57
• Happy Birthday! – Level River St Oct 4 '15 at 20:08
• @steveverrill Thanks ^_^ – Conor O'Brien Oct 4 '15 at 20:25
• Can we assume the input is padded with spaces to form a rectangle? – Downgoat Oct 4 '15 at 21:16
• @vihan Like this? – Conor O'Brien Oct 4 '15 at 21:49

# Haskell, ⌊(269 bytes − 15) · 0.9⁵⌋ = 149

h('.',0)(e:f,c,o)=(f,2:c,e:o)
h(k,b)(f,c,o)|Just x<-lookup k$zip"\\|/"[4,0..]=(f,x+2*div b 2:c,k:o)|0<1=(f,2:c,k:o) g l(f,c,o)=foldr h(f,[],'\n':o)$zip l c
d("",_,o)=o
d('@':_,_,_)=":("
d(f,_,_)=show$length f f n=d.foldr g(take n$'@':cycle"🔥^&\"~",repeat 1,"").lines


Example run:

*Main> putStr s
.
/        ~
|        \  .
/  &   " /  |
\  | @ | | . . . . .
\  | | | | | 1 l I |
*Main> putStr (f 3 s)
^
/        ~
|        \  .
/  &   " /  |
\  | @ | | 🔥 . . . @
\  | | | | | 1 l I |


Assumes that each input line is at least as long as the previous line, as allowed by one of the author’s comments.

• Looks like we have a new 1st place! – Conor O'Brien Oct 6 '15 at 22:53

# Python 2, 529 bytes with bonus, 303

• Assumes that the first line has an integer on it.
• Doesn't assume consistent spacing. Doesn't assume candles' column is empty.

Strategy:

• Get input as list.
• Reverse it and map it to a list of columns.
• Test and operate.
• Map it back into rows, reverse it, join the lines.

import re;R=raw_input;C=str.count;G=lambda x:[y if y else' 'for y in x];H=lambda x:[''.join(G(v))for v in map(None,*x)];F=re.findall;t,r,i,g,d=0,u"🔥~\"&^@",[],r'^[|/\\]+[%s](?=\s|$)',R() while d:i+=[d];d=R() c=int(F('\d+',i.pop(0))[0]);i=i[::-1];m=H(i) t+=sum(1 for x in m if F(g%r,x)) for p,n in enumerate(m): try:b=F(g%'\.',n)[0] except:continue if C(b,'/')==C(b,'\\')and t<c:t+=1;m[p]=re.sub('\.',r[0],n,1) if len(r)>1:r=r[1:] m='\n'.join(H(m)[::-1]) d=":("if t<1 elsec-t+" more candles required"if t<c else m;print d  Tests: 5, * * * * * * * @ @ @ @ | | . | | | | | | |  * * * * * * * @ @ @ @ | | 🔥 | | | | | | |  3, . . . | \ |  1 more candles required  3, . . . . . . | | |  . . . 🔥 ~ " | | |  • Could you edit to clarify the score before and after bonuses, and whether or not the byte-count is with or without comments? – Conor O'Brien Oct 6 '15 at 3:08 • I'm attempting to golf further. And I want to try a Pyth version. – user193661 Oct 6 '15 at 3:15 • Cool! Thanks. Good luck with Pyth! ^_^ – Conor O'Brien Oct 6 '15 at 3:17 • Why don't you get rid of all the comments, newlines, etc.? – RK. Oct 7 '15 at 1:27 • You can get rid of the  more candles required bit; a numeric output is assumed to be thus. – Conor O'Brien Oct 9 '15 at 13:25 # JavaScript (ES6), 328 bytes (score: 184) I tried to beat the Haskell solution but that's actually a pretty competitive entry given all of the logic that needs to happen. Score calculated as:Math.floor((328-15)*Math.pow(0.9,5)), bytes counted in a file with UTF-8 encoding, tested and confirmed with io.js --harmony_arrow_functions. Solution: eval("(n,s)=>{q=x=>x[0].map((_,c)=>x#[c]));h='Q';c=5;t=n;m=q(s.split('\\n')X#.match(/^ *[Q][\\\\//|]+$/)&&xR\\L==xR/L&&t-->0?xR./,c>1?h[c--]:'@'):x);return t==n?':(':t>0?1+t:q(mX.join('\\n')R@/,'🔥')}"[k='replace'](/[A-Z]/g,x=>({X:"#.split('')))#.join(''))",R:"[k](/\\",Q:'.@^&"~',L:"/g,'').length"}[x]))[k](/#/g,'.map(x=>x'))


Requirements: the array must be space-padded to be rectangular.

Explanation: all of the eval insanity sets one variable (the variable k to the string replace to save some bytes) and shaves 11 bytes off of a 339-byte string, which I can un-golf as:

(num_candles_desired, string) => {
transpose = array => array[0].map((_, index) => array.map(row => row[index]));
candle_tips = '.@^&"~';
c = 5; // decrementing index into candle_tips when > 1.
candles_left = num_candles_desired;
transposed_normal_output = transpose(
string.split('\n').map(line => line.split(''))
).map(col_array => col_array.join(''))
// the next map does the actual logic: finds possible candles with
// a regex, checks that the \ chars match the / chars in number,
// then decrements the candles_left index while changing the . to a
// lit flame.
.map(col => col.match(/^ *[.@^&"~][\\//|]+\$/)
&& col.replace(/\\/g,'').length == col.replace(/\//g,'').length
&& candles_left-- > 0 ? x.replace(/\./, c > 1 ? candle_tips[c--]
: '~')
: x);
return candles_left == num_candles_desired ? ':('
: candles_left > 0 ? 1 + candles_left
: transpose(
transposed_normal_output.map(col => col.split(''))
).map(row_array => row_array.join('')).join('\n')
// as promised, we include the emoji at least once if we can.
// the leading backslash is unnecessary and comes from the
// above metaprogramming-compression with eval().
.replace(/\@/,'🔥')
}


Since example I/Os were requested, here was the test suite I ran,

Reference example #1, lighting 8 candles...
Input:
.         .
| . ! . . |.  . . .
| | | | | | | | | |
Output:
~         🔥
| " ! & ^ |.  @ @ @
| | | | | | | | | |
Reference example #2, lighting 14 candles...
Input:

Output:
:(
Reference example #3, lighting 15 candles...
Input:
.   ..  . .  ". .
| . ||  | |  || !
Output:
9
Reference example #4, lighting 7 candles...
Input:
.
/        ~
|        \  .
/  &   " /  |
\  | @ | | . . . . .
\  | | | | | 1 l I |
Output:
~
/        ~
|        \  .
/  &   " /  |
\  | 🔥 | | @ . . . @
\  | | | | | 1 l I |
Reference example #5, lighting 5 candles...
Input:
. .             |
i Q no candl es . |3-.
Output:
:(

• Some example I/OS? – Conor O'Brien Oct 7 '15 at 22:41
• @CᴏɴᴏʀO'Bʀɪᴇɴ added. – CR Drost Oct 7 '15 at 22:56