There's no such thing as a free lunch

...or is there?

s/-/not /

Explanation

#!/bin/sed -fz

# Convert to dc expression (discount is just a negative tip)
s/discount/_tip/g
s/tip/.01*1+*/g
s/extra/+/g
s/coupon/-/g

# Run dc
s/.*/dc -e '& 0r-p'/e

# Convert to pretty output
s/[^-]*$/free/ s/-/not / Since the input is already very close to Reverse Polish notation, it's a simple matter to transform extra and coupon to + and -, and not much more to change the percentages into multipliers. Then invoke dc and produce a readable result depending on whether - is found (we have to negate the result, so a - implies "not free", otherwise 0 would be a special case that would need its own handling). Example The second case from the question is: 10 20 tip 20 discount 2 coupon 2 coupon 1 coupon 50 discount 2.55 coupon That becomes this dc program: 10 20 .01*1+* 20 _.01*1+* 2 - 2 - 1 - 50 _.01*1+* 2.55 - 0r-p Resulting in: free JavaScript, 173169 145 bytes i=>{w=i.split\n.map($=>$.split );t=+w.shift()[0];p=$=>t*$/100;w.map(x=>{k=+x[0];f=x[1][0];l={e:k,c:-k,t:p(k),d:-p(k)},t+=l[f]});return t<=0;} There should still be plenty of golfing to do Try it online! (145 bytes currently) Try it out: <script>var _=i=>{w=i.split('\n').map($=>$.split(' '));t=+w.shift()[0];p=$=>t*$/100;w.map(x=>{k=+x[0];f=x[1][0];t+=f=='e'&&k||f=='c'&&(-k)||f=='t'&&p(k)||f=='d'&&(-p(k))});return t<=0;}</script> <textarea oninput="document.querySelector('pre').innerText=_(this.value)"></textarea> <pre></pre> Thanks to programmer5000 for all his golfing advice • Why is node required? – programmer5000 Apr 27 '17 at 19:56 • Also, you can do {w=i.split<nl> where <nl> is a literal newline – programmer5000 Apr 27 '17 at 19:57 • Node is not required. I just used it for testing on TIO – Alberto Rivera Apr 27 '17 at 19:57 • I added a stack snippet for trying it out. Feel free to rollback if you don't like it. – programmer5000 Apr 27 '17 at 20:02 • You can remove the f= part, it is allowed by the rules, and you can replace$.split(' ') with $.split . – programmer5000 Apr 27 '17 at 20:04 JavaScript (ES6), 97 107 Input as a multiline string with a trailing newline. t=>t.replace(/(\S+) ?(.*)?\n/g,(x,d,b)=>t-=b>'t'?-t*d/100:b>'e'?d:b>'d'?t*d/100:b?-d:d,t=0)&&t>=0 The regexp splits the numeric and optional text part for each line in d and b. The calculations should be more or less obviuos. Just some note: - using -= to avoid problems mixing number with strings - the sum is negated to save 1 byte, so the last check is for >= 0 instead of <= 0 PS still way longer than @Arnauld's. Rats. Test var f= t=>t.replace(/(\S+) ?(.*)?\n/g,(x,d,b)=>t-=b>'t'?-t*d/100:b>'e'?d:b>'d'?t*d/100:b?-d:d,t=0)&&t>=0 a=12.34 15 tip 25 discount 1.5 extra 2 coupon  b=10 20 tip 20 discount 2 coupon 2 coupon 1 coupon 50 discount 2.55 coupon  console.log('Not free: '+a,f(a)) console.log('Free: '+b,f(b)) C# 324 219 bytes bool a(string[] l){var x=0f;foreach(var s in l){var b=float.Parse(s.Split(' ')[0]);if(s.EndsWith("p"))x*=b;else if(s.EndsWith("t"))x*=1-b;else if(s.EndsWith("n"))x-=b;else if(s.EndsWith("a"))x+=b;else x=b;}return x<=0;} It's not pretty, and probably not the best way, but here it is. Requires input be passed as a string array, and tips/discounts passed as floats (0.15 tip instead of 15 tip) as this has been clarified as acceptible in the comments of the spec. Explaination: bool a(string[] l){ //Define method with input string array l and bool output var x=0f; //Initialize float x foreach(var s in l){ //Iterate through lines var b=float.Parse(s.Split(' ')[0]); //Parse the number from the line and store it in float b if(s.EndsWith("p")) //If line ends with "p" then line is "tip" x*=b; //Parse number from line to float add 1 and multiply by x else if(s.EndsWith("t")) //If line ends with "t" then line is "discount" x*=1-b; //Parse number from line to float, subtract from 1 and multiply by x else if(s.EndsWith("n")) //If line ends with "n" then line is "coupon" x-=b; //Parse number from line to float and subtract from x else if(s.EndsWith("a")) //If line ends with "a" then line is "extra" x+=b; //Parse number from line to float and add to x else x=b; //Line is base price } //End foreach return x<=0; //Return x less than or equal to 0 } //End method There's gotta be a better way to do this, but this works at least • If you've been given tips/discounts as floats then you don't want that 100 in the t branch. – Wai Ha Lee Apr 28 '17 at 15:58 • @WaiHaLee oops, good point, forgot to change that to 1 – Skidsdev Apr 28 '17 at 16:01 • Tip: put float.Parse(s.Split(' ')[0]) into something to reduce duplication. That'll save about 80 characters. – Wai Ha Lee Apr 28 '17 at 16:02 • oh wow I'm a terrible golfer I didn't even remove unnecessary whitespace. I blame Visual Studio. – Skidsdev Apr 28 '17 at 16:09 • Not a bad effort at all! – Wai Ha Lee Apr 28 '17 at 16:11 PowerShell, 218156 143 bytes ($n=$args)|%{[float]$v,$w=$_-split' ';switch -w($w){"t*"{$t+=$v}"d*"{$d+=$v}"e*"{$e+=$v}"c*"{$c+=$v}}};($n[0]*(1+$t/100)*(1-$d/100)+$e-$c)-lt 0

Try it online!

EDIT Saved bytes by splitting the piped variable beforehand

EDIT 2 Stored second part of string so I could make better wildcard calls

• Seems to work, and your input format is fine. – programmer5000 Apr 27 '17 at 19:28

Python 133 bytes

def f(b):
t=float(b.pop(0))
for l in b:
v,a=l.split(' ');v=float(v);t+={'t':t*v/100,'d':-t*v/100,'c':-v,'e':v}[a[0]]
return t<=0

Similar to the JavaScript ES6 version. But type conversion is required for float values in Python.

Explanation:

Extract the first value and convert it to float.

For each other line in the bill:

1. split and convert the value to float
2. Use a dict to select the right operation according to the first letter
3. Accumulate the value

Usage:

print(f([
'12.34',
'15 tip',
'25 discount',
'1.5 extra',
'2 coupon'
]))

print(f([
'10',
'20 tip',
'20 discount',
'2 coupon',
'2 coupon',
'1 coupon',
'50 discount',
'2.55 coupon'
]))
• Welcome to the site! – James Apr 28 '17 at 21:53

Java 227 bytes

Been a while and still no Java answer that I can see, so here's my C# answer ported to Java, at the cost of 8 bytes

boolean a(String[] l){Float x=0f;for(String s:l){Float b=Float.parseFloat(s.split(" ")[0]);if(s.endsWith("p"))x*=b;else if(s.endsWith("t"))x*=1-b;else if(s.endsWith("n"))x-=b;else if(s.endsWith("a"))x+=b;else x=b;}return x<=0;}

For an explanation and such, see my C# answer

Like that answer, this answer expects that the tip and discount be passed as floats (0.15 not 15)

• Pretty good...for Java! – programmer5000 May 2 '17 at 10:51
• @programmer5000 to be fair C# is only slightly less verbose than Java, main advantages being things like C#'s support of var generic type, and lambdas (I know Java has them, but C#'s are golfier) – Skidsdev May 2 '17 at 10:55

Jq 1.5, 129119114 112 bytes

reduce (.[]/" "|.[0]|=tonumber|.[1]|=length)as[$n,$c](0;[$n,0,0,.+.*($n/100),0,.+$n,.-$n,0,.-.*($n/100)][$c])<=0

Expanded

reduce (
.[]/" "             # split each element into [value,command]
| .[0]|=tonumber      # convert value to number
| .[1]|=length        # convert command to length
) as [$n,$c]
(  0
; [ $n # "" -> set base , 0 , 0 , .+.*($n/100)       # "tip"
, 0
, .+$n # "extra" , .-$n               # "coupon"
, 0
, .-.*($n/100) # "discount" ][$c]                # ... depending on command length
) <=0                   # true if lunch was free

Try it online!