Countdown Numbers Game

Mar 28th, 2018
209
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
1. import itertools as it
2. import operator
3. import random
4.
5. from more_itertools import distinct_permutations
6.
7.
8. class FalseOperation(ValueError):
9.     pass
10.
11.
12. def is_valid(sequence):
13.     count_nums = 0
14.     for token in sequence:
15.         if not token:
16.             count_nums -= 1
17.             if count_nums < 1:
18.                 return False
19.         else:
20.             count_nums += 1
21.     return True
22.
23.
24. def valid_sequences(num_count):
25.     base = (True,) * num_count + (False,) * (num_count - 1)
26.     #     print(base)
27.     perms = distinct_permutations(base)
28.     #     perms = list(perms)
29.     #     print(f'possible permutations: {len(perms)}')
30.     for sequence in perms:
31.         #         print(sequence)
32.         if is_valid(sequence):
33.             yield sequence
34.
35.
36. def distinct_ordered_combinations(iterable, r):
37.     seen = set()
38.     for combination in it.combinations_with_replacement(iterable, r):
39.         for permutation in it.permutations(combination):
40.             if permutation not in seen:
41.                 yield permutation
43.
44.
45. def generate_sequences(numbers, operators):
46.     l = len(numbers)
47.     for sequence in valid_sequences(l):
48.         for nums, ops in it.product(
49.                 distinct_permutations(numbers),
50.                 distinct_ordered_combinations(operators, l - 1),
51.         ):
52.             nums = iter(nums)
53.             ops = iter(ops)
54.             yield tuple(next(nums) if token else next(ops) for token in sequence)
55.
56.
57. def calculate_sequence(sequence, operators):
58.     # Implement postfix algorithm
59.     stack = list(sequence[:2])
60.     for idx, token in enumerate(sequence[2:], start=3):
61.         if token not in operators:
62.             stack.append(token)
63.             continue
64.         operand1 = stack.pop()
65.         operand2 = stack.pop()
66.         try:
67.             result = operators[token](operand1, operand2)
68.         except ZeroDivisionError:
69.             raise FalseOperation('Division by zero')
70.         if int(result) != result:
71.             raise FalseOperation('non-int value')
72.         if result < 0:
73.             raise FalseOperation('negative value')
74.         yield result, sequence[:idx]
75.         stack.append(result)
76.
77.
78. def test_sequences(target, numbers, operators):
79.     for sequence in generate_sequences(numbers, operators):
80.         try:
81.             for result, seq in calculate_sequence(sequence, operators):
82.                 if result == target:
83.                     yield result, seq
84.         except FalseOperation:
85.             pass
86.
87.
88. def main():
89.     # Generate the set of possible numbers allowed by Countdown rules
90.     large_numbers = [25, 50, 75, 100]
91.     small_numbers = list(range(1, 11)) * 2
92.
93.     # Generate six number tiles according to user input for large numbers
94.     while True:
95.         try:
96.             usr_large_num = int(input("How many LARGE numbers do you want (0-4)? "))
97.         except ValueError:
98.             pass
99.         else:
100.             if 0 <= usr_large_num <= 4:
101.                 break
102.     six_tiles = random.sample(large_numbers, usr_large_num) + \
103.                 random.sample(small_numbers, 6 - usr_large_num)
104.
105.     # Generate random target output: any three-digit number
106.     target = random.randint(100, 999)
107.
108.     print(six_tiles)
109.     print(f"Target: {target}")
110.
112.                  "-": operator.sub,
113.                  "*": operator.mul,
114.                  "/": operator.truediv}
115.
116.     for operators in it.combinations_with_replacement(ops_tiles, 4):
117.         generate_sequences(six_tiles, operators)
118.         test_sequences(target, six_tiles, operators)
119.         print('hi')
120.
121.
122. if __name__ == '__main__':
123.     main()