There's no such thing as a free lunch
JavaScript (ES6), 88 85 bytes
Takes input as an array of strings. Returns 0
for not free or 1
for free.
a=>a.map(s=>([a,b]=s.split` `,t+={e:+a,c:-a,t:x=t*a/100,d:-x}[(b||'e')[0]]),t=0)|t<=0
How it works
Each line is split on the space to get a
= amount, b
= type of operation. If there's no operation at all (which is the case on the first line), b
is set by default to "e"
for "extra".
To add the correct amount to the total t
, we use an object whose keys are the first letter of the operation:
{
e: +a, // extra
c: -a, // coupon
t: t * a / 100, // tip
d: -t * a / 100 // discount
}
Note: If the bill consisted of only one element, map()
would return a single-element array which would be coerced to an integer when applied the |
operator, making the final test fail. But the OP confirmed that this can't happen. (Arrays of 2 or more elements are coerced to 0.)
Demo
let f =
a=>a.map(s=>([a,b]=s.split` `,t+={e:+a,c:-a,t:x=t*a/100,d:-x}[(b||'e')[0]]),t=0)|t<=0
console.log(f([
'12.34',
'15 tip',
'25 discount',
'1.5 extra',
'2 coupon'
]))
console.log(f([
'10',
'20 tip',
'20 discount',
'2 coupon',
'2 coupon',
'1 coupon',
'50 discount',
'2.55 coupon'
]))
Jelly, 42 39 bytes
⁾_@
⁾C×
”+
⁾‘×
ḲµṪḢO%7µĿṭ
ḢW;Ç€j”µFV>0¬
Takes a list of strings with decimal formatted numbers
(Leading zeros will work, but have the side effect of printing zeros to STDOUT prior to the final result).
Try it online! - not free; or free.
How?
⁾_@ - Link 1: a coupon
⁾_@ - literal "_@" - the Jelly code for subtraction with reversed arguments
⁾C× - Link 2: a discount
⁾C× - literal "C×" - the Jelly code for complement (1-input) then multiply
”+ - Link 3: extra cost
”+ - literal '+' - the Jelly code for add
⁾‘× - Link 4: a tip
⁾‘× - literal "‘×" - the Jelly code for increment (input+1) then multiply
ḲµṪḢO%7µĿṭ - Link 5, switch: char list
Ḳ - split on spaces (gives [amount, type] as char lists)
µ µ - monadic chain separation to get a value, say v
Ṫ - tail (get the type: "coupon", "discount", "extra", or "tip")
Ḣ - head (get the first character: 'c', 'd', 'e' or 't')
O - cast to ordinal (99, 100, 101, or 116)
%7 - mod 7 (1, 2, 3, or 4)
Ŀ - call link v as a monad
ṭ - tack to the amount char list
ḢW;Ç€j”µFV>0¬ - Main link: list of strings (char lists)
Ḣ - head - the base price char list
W - wrap in a list
Ç€ - call the last link (5) as a monad for €ach of the rest
; - concatenate
”µ - literal 'µ' - Jelly's monadic chain separator
j - join all the parts with 'µ's "10",".2 tip",".2 discount", "2 coupon","2 coupon","1 coupon",".5 discount","2.55 coupon":
F - flatten (makes a char list, for example: "10µ.20‘×µ.20C×µ2_@µ2_@µ1_@µ.50C×µ2.55_@")
V - evaluate as Jelly code (the above evaluates to -0.2499999999999991)
>0 - greater than 0?
¬ - not
CJam, 45 42 bytes
q~Sf/(sd\{L(d\~ci6%"1\-* + )* -"S/=~}fL0>!
Take input as an array of strings, and takes the tip and discount as decimals.
Try it online!
Explanation
q~ e# Read and eval the input.
Sf/ e# Split each string by spaces.
(sd e# Pull out the first element (base price) and cast it to a double.
\ e# Bring the array back to the top.
{ e# For each element L in the array:
L e# Push L.
(d e# Pop out the first element and cast it to a double.
\~ e# Bring the second element to the top of the stack.
ci6% e# Mod its first character's ASCII value by 6. (c,d,e,t) -> (3,4,5,2)
"1\-* + )* -"S/ e# Push this string and split it on spaces.
= e# Get the element given by number from the mod. CJam uses modular arrays,
e# so 4 and 5 get elements 0 and 1 respectively.
~ e# Eval whichever string was retrieved.
}fL e# (end of loop)
0>! e# Check if it's not greater than 0.
The code which is evaluated depending on the first letters:
t -> ")*" Adds 1 to the tip amount and multiplies it by the current price.
d -> "1\-*" Subtracts the discount amount from 1 and multiplies it by the current price.
e -> "+" Adds the extra amount to the current price.
c -> "-" Subtracts the coupon amount from the current price.