#!/usr/bin/env python3
"""Advent of Code 2015, Day 21: RPG Simulator 20XX"""

import pytest


def item_combinations(items, combo_range):
    """Return a set of item combinations, allowing no item to be chosen

    `combo_range` is the acceptable lengths of the combinations, with None
    inserted so the length of the combination is always the upper
    limit of the range. For instance:
        [a, b], range(2)    ->      (a), (b), (None)
        [a, b], range(3)    ->      (a, b), (a, None), (b, None),
                                        (None, None)

    You can force an item to be chosen by excluding 0 from the range:
        [a, b], range(1, 2)     ->      (a), (b)
        [a, b], range(1, 3)     ->      (a, b)
        [a, b, c], range(1, 3)  ->      (a, b), (a, c), (b, c)
    """
    pass


@pytest.mark.parametrize('items,combo_range,expected', [
    (['a', 'b'], range(1), []),
    (['a', 'b'], range(2), [(None,), ('a',), ('b',)]),
    (['a', 'b'], range(1, 2), [('a',), ('b',)]),
    (['a', 'b'], range(3),
        [(None, None), ('a', None), ('b', None), ('a', 'b')]),
    (['a', 'b'], range(1, 3), [('a', 'b')]),
])
def test_combos(items, combo_range, expected):
    assert sorted(item_combinations(items, combo_range)) == sorted(expected)


def main(puzzle_input):
    pass


if __name__ == '__main__':
    # Puzzle input (boss stats):
    #     Hit Points: 104
    #     Damage: 8
    #     Armor: 1
    main()

@@ -71,16 +71,34 @@
 0XX%22%22%22%0A%0A
+import itertools%0A%0A
 import p
@@ -810,27 +810,869 @@
     %22%22%22%0A    
-pas
+if combo_range.start %3C 0 or combo_range.stop %3C 0:%0A        raise ValueError('Range must not be negative')%0A    elif (combo_range.start == combo_range.stop or%0A            combo_range.start == 0 and combo_range.stop == 1):%0A        # Choices are of length zero%0A        return %5B%5D%0A%0A    full_length_combos = %5B%5D%0A    if combo_range.start == 0:%0A        no_choice = %5BNone for _ in range(combo_range.stop - 1)%5D%0A        full_length_combos.append(tuple(no_choice))%0A        combo_range = range(1, combo_range.stop)%0A%0A    expected_length = combo_range.stop - 1%0A    for length in combo_range:%0A        combos = itertools.combinations(items, length)%0A        if length %3C expected_length:%0A            combos = %5Bc + tuple(%5BNone%5D * (expected_length - length))%0A                      for c in combos%5D%0A        full_length_combos.extend(combos)%0A%0A    return full_length_combo
 s%0A%0A%0A@pytest.
@@ -1971,16 +1971,65 @@
 range(1,
+ 2), %5B('a',), ('b',)%5D),%0A    (%5B'a', 'b'%5D, range(2,
  3), %5B('
@@ -2101,23 +2101,16 @@
  assert 
-sorted(
 item_com
@@ -2142,28 +2142,20 @@
 nge)
-)
  == 
-sorted(
 expected
 )%0A%0A%0A
@@ -2150,16 +2150,175 @@
 expected
+%0A%0A%0Adef test_combos_disallows_negative():%0A    for r in %5Brange(-2, 2), range(2, -2)%5D:%0A        with pytest.raises(ValueError):%0A            item_combinations(%5B%5D, r
 )%0A%0A%0Adef 

Implement item_combinations, test range check