CTO Challenge 2016 2nd LV3
from typing import List, Dict | |
from functools import reduce | |
from collections import Counter | |
from menu import Menu | |
Coupon = int | |
class CouponSelector(): | |
def __init__(self, | |
limitation_of_coupons: Dict[Coupon, int], | |
coupons_available_for_pizza: List[Coupon], | |
lowest_amount_for_using_coupon: int | |
) -> None: | |
self.limitation_of_coupons = limitation_of_coupons | |
self.coupons_available_for_pizza = coupons_available_for_pizza | |
self.lowest_amount_for_using_coupon = lowest_amount_for_using_coupon | |
def select_optimum_combination_by_menus( | |
self, | |
menus: List[Menu], | |
coupons: List[Coupon]): | |
amount = reduce(lambda amount, m: amount + m.price, menus, 0) | |
available_coupons = _filter_limited_coupons( | |
coupons, | |
self.limitation_of_coupons | |
) | |
if not _pizza_exists(menus): | |
available_coupons = [c for c in available_coupons | |
if c not in self.coupons_available_for_pizza] | |
return self.select_optimum_combination(amount, available_coupons) | |
def select_optimum_combination(self, amount: int, coupons: List[Coupon]): | |
if amount <= self.lowest_amount_for_using_coupon: | |
return [] | |
sorted_coupons = sorted(coupons, reverse=True) | |
rest = amount | |
used_coupons = [] | |
for c in sorted_coupons: | |
if rest < c: | |
break | |
rest -= c | |
used_coupons.append(c) | |
return used_coupons | |
def _pizza_exists(menus: List[Menu]): | |
return any(m.is_pizza() for m in menus) | |
def _filter_limited_coupons( | |
coupons: List[Coupon], | |
coupon2limit: Dict[Coupon, int]): | |
counter = Counter(coupons) | |
for c, n in counter.items(): | |
counter[c] = min(coupon2limit[c], n) | |
return list(counter.elements()) |
from coupon_selector import CouponSelector | |
coupon_selector = CouponSelector({}, [], 1000) | |
def test_select_optimum_combination_case1(): | |
selected = coupon_selector.select_optimum_combination( | |
1000, | |
[500, 500, 200, 100, 100, 100] | |
) | |
assert selected == [] | |
def test_select_optimum_combination_case2(): | |
selected = coupon_selector.select_optimum_combination(1210, []) | |
assert selected == [] | |
def test_select_optimum_combination_case3(): | |
selected = coupon_selector.select_optimum_combination( | |
1210, | |
[500, 500, 200, 100, 100, 100] | |
) | |
assert selected == [500, 500, 200] | |
def test_select_optimum_combination_case4(): | |
selected = coupon_selector.select_optimum_combination( | |
1530, | |
[500, 500, 200, 100, 100, 100] | |
) | |
assert selected == [500, 500, 200, 100, 100, 100] | |
def test_discounted_in_order_when_coupons_are_not_sorted(): | |
selected = coupon_selector.select_optimum_combination( | |
1530, | |
[500, 200, 100, 500, 100, 100] | |
) | |
assert selected == [500, 500, 200, 100, 100, 100] |
from coupon_selector import CouponSelector | |
from menu import PizzaMenu, SideMenu | |
coupon_selector = CouponSelector( | |
{500: 2, 200: 2, 100: 3, 400: 1}, | |
[400], | |
1000 | |
) | |
def test_select_optimum_combination_by_menu_case1_on_lv2(): | |
menus = [PizzaMenu("ジェノベーゼ", "M", 1000)] | |
selected = coupon_selector.select_optimum_combination_by_menus( | |
menus, | |
[500, 200, 100, 400] | |
) | |
assert selected == [] | |
def test_select_optimum_combination_by_menu_case2_on_lv2(): | |
menus = [PizzaMenu("マルゲリータ", "M", 1200)] | |
selected = coupon_selector.select_optimum_combination_by_menus( | |
menus, | |
[] | |
) | |
assert selected == [] | |
def test_select_optimum_combination_by_menu_case3_on_lv2(): | |
menus = [ | |
SideMenu("ポテトフライ", 400), | |
SideMenu("ポテトフライ", 400), | |
SideMenu("シーザーサラダ", 600) | |
] | |
selected = coupon_selector.select_optimum_combination_by_menus( | |
menus, | |
[500, 500, 200, 100, 100, 400] | |
) | |
assert selected == [500, 500, 200, 100, 100] | |
def test_select_optimum_combination_by_menu_case4_on_lv2(): | |
menus = [PizzaMenu("ジェノベーゼ", "L", 1400)] | |
selected = coupon_selector.select_optimum_combination_by_menus( | |
menus, | |
[500, 500, 200, 100, 100, 400] | |
) | |
assert selected == [500, 500, 400] | |
def test_select_optimum_combination_by_menu_case5_on_lv2(): | |
menus = [PizzaMenu("ジェノベーゼ", "M", 1000), PizzaMenu("マルゲリータ", "M", 1200)] | |
selected = coupon_selector.select_optimum_combination_by_menus( | |
menus, | |
[500, 500, 500, 200, 200, 200, 100, 100, 100, 100, 400, 400] | |
) | |
assert selected == [500, 500, 400, 200, 200, 100, 100, 100] |
from itertools import combinations, product | |
from datetime import datetime | |
from setmenu_discounter import SetMenuDiscount, SetMenuDiscounter | |
from menu import PizzaMenu, SideMenu | |
all_pizza = [ | |
PizzaMenu("ジェノベーゼ", "M", 1000), | |
PizzaMenu("マルゲリータ", "M", 1200), | |
PizzaMenu("ジェノベーゼ", "L", 1400), | |
PizzaMenu("マルゲリータ", "L", 1800), | |
] | |
l_pizza = [ | |
PizzaMenu("ジェノベーゼ", "L", 1400), | |
PizzaMenu("マルゲリータ", "L", 1800), | |
] | |
side_menus = [ | |
SideMenu("ポテトフライ", 400), | |
SideMenu("グリーンサラダ", 500), | |
SideMenu("シーザーサラダ", 600) | |
] | |
def is_weekday_and_from_11_to_14(d): | |
return 0 <= d.weekday() and \ | |
d.weekday() < 5 and \ | |
11 <= d.hour and \ | |
d.hour <= 14 | |
service = SetMenuDiscounter([ | |
SetMenuDiscount( | |
"ピザ2セット", | |
list(map(lambda x: list(x), combinations(all_pizza, 2))), | |
None, | |
[SideMenu("ポテトフライ", 400)], | |
None | |
), | |
SetMenuDiscount( | |
"ピザL2セット", | |
list(map(lambda x: list(x), combinations(l_pizza, 2))), | |
None, | |
[ | |
SideMenu("ポテトフライ", 400), | |
SideMenu("グリーンサラダ", 500), | |
SideMenu("シーザーサラダ", 600) | |
], | |
None | |
), | |
SetMenuDiscount( | |
"平日ランチセット", | |
list(map(lambda x: list(x), product(all_pizza, side_menus))), | |
[is_weekday_and_from_11_to_14], | |
[], | |
400 | |
) | |
]) | |
def test_pizza2_set(): | |
orders = [ | |
PizzaMenu("ジェノベーゼ", "M", 1000), | |
PizzaMenu("マルゲリータ", "M", 1200), | |
SideMenu("ポテトフライ", 400), | |
] | |
(actual, discount) = service.decide_discount(orders, datetime.now()) | |
assert type(actual) == SetMenuDiscount | |
assert actual.name == "ピザ2セット" | |
assert discount == 400 | |
def test_pizzaL2_set(): | |
orders = [ | |
PizzaMenu("ジェノベーゼ", "L", 1400), | |
PizzaMenu("マルゲリータ", "L", 1800), | |
SideMenu("グリーンサラダ", 500), | |
] | |
(actual, discount) = service.decide_discount(orders, datetime.now()) | |
assert type(actual) == SetMenuDiscount | |
assert actual.name == "ピザL2セット" | |
assert discount == 500 | |
def test_weekday_lunch_set(): | |
orders = [ | |
PizzaMenu("ジェノベーゼ", "L", 1400), | |
SideMenu("グリーンサラダ", 500), | |
] | |
(actual, discount) = service.decide_discount( | |
orders, | |
datetime(2016, 10, 26, 11, 30, 0) | |
) | |
assert type(actual) == SetMenuDiscount | |
assert actual.name == "平日ランチセット" | |
assert discount == 400 |
pip=bin/pip | |
python=bin/python | |
pyflakes=bin/pyflakes | |
pytest=bin/pytest | |
run: $(python) | |
$< main.py -c $(cs) -m $(ms) | |
test: $(pytest) | |
$< -v ./lv1_test.py ./lv2_test.py ./lv3_test.py | |
install: $(pip) | |
$< install -r requirements.txt | |
$(pytest): $(pip) | |
$(MAKE) install | |
$(pyflakes): $(pip) | |
$(MAKE) install | |
$(python): | |
virtualenv . -p python3 | |
$(pip): | |
virtualenv . -p python3 | |
clean: | |
rm -rf ./bin | |
rm -rf ./lib | |
rm -rf ./include |
pytest | |
flake8 | |
mypy-lang |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment