-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
125 lines (105 loc) · 3.82 KB
/
main.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import random
import itertools
import time
import datetime as dt
BIG_NUMBERS = [25, 50, 75, 100]
MAX_NUMBER = max(BIG_NUMBERS)
MIN_NUMBER = 1
def main():
starting_numbers = generate_starting_numbers()
target = random.randint(100, 999)
start_time = time.time()
steps = get_steps(starting_numbers, target)
end_time = time.time()
elapsed_time = end_time-start_time
print("\n" * 100)
print("Time:", str(dt.timedelta(seconds=elapsed_time)))
if elapsed_time > 30:
print("Fail.")
print("Starting numbers:", ", ".join(str(n) for n in starting_numbers))
print("Target:", target)
if steps is None:
print("Unsolvable!")
else:
print("\nSolution:")
print(parse_steps(steps))
print("Solution is valid." if verify_steps(
starting_numbers, target, steps) else "Solution is invalid!")
def get_steps(remaining_numbers, target, current=None, steps=None, closest=None):
if current is None:
steps = []
for operand_pair in itertools.permutations(remaining_numbers, 2):
(operand1, operand2) = operand_pair
for operation in ['+', '-', '*', '/']:
if operation == '/' and (operand2 == 0 or not is_integer_result(operand1, operand2)):
continue
elif operation == '-' and operand2 > operand1:
continue
current = operate(operand1, operand2, operation)
offby = abs(current - target)
if current == 0 or len(str(abs(current))) > 4:
continue
steps.append([operand1, operation, operand2, current])
if offby < 10:
print(f"Off by {offby}:")
print(parse_steps(steps))
# print(f'{operand1} {operation} {operand2} = {current}')
if current == target:
return steps
remaining_numbers_copy = remaining_numbers.copy()
remaining_numbers_copy.remove(operand1)
remaining_numbers_copy.remove(operand2)
remaining_numbers_copy.append(current)
new_steps = get_steps(remaining_numbers_copy,
target, current, steps, closest)
if new_steps is None:
steps.pop()
else:
return steps
def generate_starting_numbers():
starting_numbers = []
big_count = 0
for i in range(6):
if random.randint(0, 1) and not big_count >= 4:
starting_numbers.append(random.choice(BIG_NUMBERS))
big_count += 1
else:
starting_numbers.append(random.randint(1, 10))
return starting_numbers
def operate(operand1, operand2, operation):
if operation == '+':
return operand1 + operand2
if operation == '-':
return operand1 - operand2
if operation == '*':
return operand1 * operand2
if operation == '/':
if not is_integer_result(operand1, operand2):
raise Exception(
f"Result of {operand1} / {operand2} is not an integer")
return operand1 // operand2
def is_integer_result(operand1, operand2):
return (operand1 / operand2).is_integer()
def verify_steps(starting_numbers, target, steps):
result: int
for step in steps:
(operand1, operation, operand2, r) = step
if r != operate(operand1, operand2, operation):
return False
try:
starting_numbers.append(r)
starting_numbers.remove(operand1)
starting_numbers.remove(operand2)
except ValueError:
return False
result = r
if result != target:
return False
return True
def parse_steps(steps):
final_string = ""
for step in steps:
final_string += f"{step[0]} {step[1]} {step[2]} = {step[3]}\n"
return final_string
if __name__ == '__main__':
main()