Beta
Self Checkout Stand
Loading description...
Puzzles
View
This comment has been reported as {{ abuseKindText }}.
Show
This comment has been hidden. You can view it now .
This comment can not be viewed.
- |
- Reply
- Edit
- View Solution
- Expand 1 Reply Expand {{ comments?.length }} replies
- Collapse
- Spoiler
- Remove
- Remove comment & replies
- Report
{{ fetchSolutionsError }}
-
-
Your rendered github-flavored markdown will appear here.
-
Label this discussion...
-
No Label
Keep the comment unlabeled if none of the below applies.
-
Issue
Use the issue label when reporting problems with the kata.
Be sure to explain the problem clearly and include the steps to reproduce. -
Suggestion
Use the suggestion label if you have feedback on how this kata can be improved.
-
Question
Use the question label if you have questions and/or need help solving the kata.
Don't forget to mention the language you're using, and mark as having spoiler if you include your solution.
-
No Label
- Cancel
Commenting is not allowed on this discussion
You cannot view this solution
There is no solution to show
Please sign in or sign up to leave a comment.
Random tests: the threshold for
assert_approx_equals
is the bigger of either absolute or relative difference:So currently the random tests are not really testing anything well, because a 10% threshold is too lenient for the price ranges that happen in the random tests.
But it doesn't matter anyway, because afterwards when
cash
is tested it usesassert_equals
, so any non-exact values will fail the test.I have changed tests to use strict equals.
I'm still getting small discrepancies in the payment function, for example:
I don't know how to debug this.
If the test for total() already passed, why wouldn't this work?
I just noticed that this kata was tagged "puzzle". Maybe the puzzle is to figure out why there are discrepancies in a small number of random tests. LOL
As usual, "puzzle" tag is used as a poor excuse of "I can't figure out how to make my kata logically sound and not insane, and would like to frustrate other users by subjecting them to torments of trying to read my mind"...
Hello mortonfox, The discrepancy is due to
assert_approx_equals
accidentally being left in after converting all the floats to use ints. Let me know if this solves your problem.I chose the puzzles tag because I don't know what other category this falls under. I figured a vague over-used tag would be better than no tag. If there's one that's more applicable, or if no tag is actually better, I'd be happy to change it.
Sample tests need to be updated: it's still expecting the values when
1
equals to a dollar rather than a cent.Fixed!
Reference solution fails to handle coupons that applies to an item with space in its name:
Reference solution fails to handle the case where the first applied coupon results in no discount (when the coupon's either
BOGO
or2 for $5
, and only 1 item is bought), which will then apply the second coupon:Fixed. Simple typo... thanks for your keen eye!
Fixed. Logic for this check has been removed from the lambda function and placed at the beginning of coupon evaluation.
This description is just plain wrong. In fact these are 3 different kinds of coupons, and the accompanied functions all have different signatures:
BOGO
(Buy One Get One) takes the item's quantity and calculates the new quantityn for x$
takes the item's quantity and calculates the new pricen% off
takes the item's price and calculates the new priceReference solution is inconsistent over itself on this matter too:
n% off order
/n% off x$ or larger order
can only be applied once for each coupon with the same string. So10% off order
,20% off order
and20% off 0$ or larger order
can be stacked on each other, but not multiple10% off order
sReward points
order can only be applied once. Period. Not even if multiple reward points coupon exist with different valuesRelated issue: reward points coupons are always the same inside each test. This is defintely unexpected.
Some coupons are incorrectly implemented:
n% off order
:lambda x: x - (x * 0.1)
this returns the after discount value (90%), not the discount (10%)2 for 5$
:lambda x: 5 * (x // 2) if x >= 2 else 0
this meanssave 5$ every 2 item
, notevery 2 item is priced 5$
I agree with your observation about how the coupons function. I've double checked that the logic works correctly with this usage, and it can be correctly calculated as described, as long as you consider how and why these functions return the number that they do. However, I've changed the descriptions of coupons drastically to account for some of the other issues raised. This specific wording is no longer in the description.
I believe this is incorrect.
n% off order
andn% off x$ or larger order
do not exist in the actual testing. Much like actual coupons, these two discounts are predetermined and will not change. The random coupons cover a pretty wide variety of items, which is the main contributor to the total. I didn't really see the need to go too overboard with sub-total manipulation type coupons since this value is fixed after items are scanned.I really hate saying this, but I cannot replicate the issue. I added a print statment each time a rewards coupon is generated, and then ran the kata about 10 times. I copied the logs into a pretty simple parser that isolated the numbers, and I couldn't find any that were duplicated. I'm using
import random
for the random tests, and I'm not changing the seed anywhere, so it should be automatically updated, right? If you can replicate, or have something I could try, just let me know and I'll jump on it asap.10% off order
was fixed.2 for $5
can be correctly implimented using the modified lambda. If I need to update the description to indicate that the lambda functions are not all encompasing, but simply one option to calculate the correct discount, let me know. From the current description:The problem is, you've basically implemented a completely wrong version of Strategy Pattern. The point of providing a lambda function for each coupon is so that you can calculate the discount for all coupons without having to handle specific implementations one by one on the consuming side. What you've done is the opposite: you've provided a function for every coupon, but every coupon's function are used differently, so the coupon-consuming code still has to pattern every coupon type. This is an anti-pattern.
.
This has been fixed. Those lambda functions now require two variables, but they have been adjusted to fix the anti-pattern. I'll do some reading on that link you provided to make sure everything else is working right.
The implementation is wrong again:
BOGO
expects to pass inf(quantity, price per item)
,x for y$ item
expects to pass inf(quantity, quantity * price per item)
,x% off item
expects to pass inx(quantity * price per item)
,x% off order
andx$ off y$ or larger order
coupons expects to pass inf(subtotal)
.2 for 5$
coupons is again implemented incorrectly:lambda x, y: y - (500 * (x // 2))
This would claim that 3 items costs only500
, but the lone 1 item should cost the price of an item itself.Also,
BOGO
and2 for 5$
coupons are straight out ignored if there are only 1 item. This is not mentioned at all.Also,
BOGO
forgot to count the item as already scanned.I have to say, I didn't expect this to get worse than before. Now coupons don't even expect the same amount of arguments (and yet still expects all kinds of different arguments), and the implementation is still wrong. It looks you haven't tested your changes at all before making the edits, or actually understand what was the anti-pattern.
Have you actually tested your kata and logged your random inputs? I'm very dissatisfied with this response, because it certainly looks like you haven't actually tested anything: Logging
coupons
array generated by your random tests already tells easily the reward coupons in anycoupons
array contain the same value.I understand your frustration, but yes. To the best of my ability, I have tested that this works the way it was intended. I see the issue you're describing now, and I'm not sure if it matters. If given a coupon array of one reward points coupon, you'll get a random number that should be given to the coupon for the discount amount. If you recieve two or more rewards coupons, while, yes they all have the same number currently, it doesn't matter. That coupon should only be processed one time anyway. If it's that important to add the appearance of randomness, I will happily do this. I just don't see the reason given that the rules were pretty explicit about how to process duplicate coupons.
It matters from the standpoint of de-duplicating coupons: de-duplicating by the entire coupon name rather than reward coupons will fail tests where multiple reward coupons of different values exist.
This isn't ruled out by the description, so there are no reasons to arbitrarily remove some of the possible inputs. Lack of test coverage (testing the possible kind of scenarios fully) is a serious testing issue: you can't just say "I don't need to test it because the rules explained this pretty clearly". Code doesn't care about your description; it only cares about your tests.
Speaking of which, where are the explanations of reward points coupons? It used to be in the description but now it's gone, so the meaning of it is unclear.
Updated:
Let me know if that's a clear enough description for the rewards coupons.
I see your point now. My appologies for not understanding.
It's not specified which item type coupon should be applied when multiple of them are given. By first occurrence? Biggest deduction?
Fixed. Updated the description to clarify that the first occurrence should be applied.
Kata design issue: when payment is done in cash, it is expected the change to be returned. When payment is done via bank only
ACCEPTED
needs to be returned; but the cost are not deducted from the bank? So we're not actually paying for the products? This is very unrealistic.preloaded.COUPONS
is completely useless: it's a string of newline-delimited key-values which is a hassle to parse, and this is not needed anyway since the coupons pass in are already the parsed form of the key-value pairs.2 for $5
coupons for the majority of the items are actually pointless because most items are priced below2.5
, in which case buying 2 for $5 would be paying more than the normal price.Currently coupons are applied with no regards of this possibility, so in certain cases it can result in a negative discount, or a increased total from sub-total.
(Not like this can be tested anyway, since
2 for $5
coupons are incorrectly implemented right now: see comments in another issue above)Thanks for this feedback. I want to ask about why this is unrealistic though. In my experience, a self checkout machine is only responsible for soliciting sales of items for the store. If cash is given, any change owed is returned to the customer. If a card is given, the card reader checks with the bank if the funds are available, and then the machine will send an invoice to the bank to withdraw the money. From the perspective of an average user buying groceries, I think this is a more accurate system. While yes, there are more things that happen on the backend with the actual account, this seems unrelated. If it were to be fully implemented, the only safe way to do it (since BANK is in preloaded right now) would be to ask warriors to implement a seperate function to manage this extra logic. With other kata covering this in spades, it seems like an unnecessary way to bloat the logic of what we're trying to do here.
I have removed this. I'm unsure about how parsing works, but when I was testing this myself, I could only manage to see the memory key that the lambda was held in. If this is something that is easy to print, or easy to test, that's all that matters to me. I just didn't want to pass the user a cryptic function and say "eh, figure it out".
I will add a check to the coupon randomizer to account for the price of an item.
This is happening all the time in the random tests at the moment: coupons like
2 for $5 soda
generates negative discounts.There are no sample tests.
Fixed.
Random tests don't have any allowance for floating point error?
@pxsonger: you can refer to this page to implement tests with floats: https://docs.codewars.com/languages/python/authoring (search for
assert_approx_equals
).Note how
the articleanother article points out that floats and money don't mix.@mortonfox Thanks for the heads up. I've upded the tests to use 0.001 accuracy with approx_equals. I'm pretty sure your solution will work now. Sorry for the trouble.
@hobovsky and @akar-0 Thanks for the links. I've thought about having the tests use rounding to 2 decimal places (since we're representing money after all), but since there are issues with floats generally, I didn't want to make the requirements to difficult for rounding errors. I've never used the decimal module, but I'd be happy to do some digging and implement that into the tests if you think that would make things better and more consistent?
alternatively, I could change the way we're calculating sales tax, since (at least in my code) that's the only place a float is used. I could change it to use
max(1, subtotal // 10))
which would be able to return a clean integer for tax, but then we get into the potential for rounding errors.Any direction is appreciated. I'll wait to resolve the issue until I either come up with something, or we decide how it should be changed.
I have changed the kata to use only integers.