2. Hailstone numbers
A sequence is called a hailstone sequence because the values typically rise and fall, somewhat analogously to a hailstone inside a cloud.
See working app at: https://pc-hailstone-numbers.anvil.app
2.1. References
2.2. Design
2.3. Get started
Click: Blank App.
Choose: Material Design
2.4. Settings
Click on the cog icon to show the settings tab.
Enter an App name. Hailstone_numbers
Enter an App title. Hailstone_numbers
Enter an App description. Hailstone_numbers are a sequence of odd and even positive integers.
Close the settings tab.
2.5. Build interface
2.5.1. Title
2.5.2. Column panel
2.5.3. Info
Hailstone numbers are a sequence of odd and even positive integers.
The values typically rise and fall, like a hailstone inside a cloud.
e.g. 6, 3, 10, 5, 16, 8, 4, 2, 1
2.5.4. Rules
The rules for producing hailstone numbers:
* Start with a positive whole number (integer)
* If the number is even, divide by 2.
* If the number is odd, multiply by 3 and add 1.
* Repeat for each new number and continue till 1 is reached.
2.5.5. Directions
Enter the start number (positive integer) and click Generate.
2.5.6. Hailstone_start
2.5.8. Error field
2.5.9. Length_label
2.5.10. Length
2.5.11. Start_label
2.5.12. Hailstone_numbers
2.6. Initial Code
class Form1(Form1Template):
def __init__(self, **properties):
# Set Form properties and Data Bindings.
self.init_components(**properties)
# hide error field and output fields
self.error.visible = False
self.set_main_field_vis(False)
def set_main_field_vis(self, vis_bool):
self.length_label.visible = vis_bool
self.length.visible = vis_bool
self.hailstone_numbers.visible = vis_bool
2.7. Event Code
def generate_click(self, **event_args):
self.generate()
def hailstone_start_pressed_enter(self, **event_args):
self.generate()
2.8. Hailstone Code
def hailstone(self, num):
# return list of numbers
hailstone_list = [num]
while num > 1:
if hailstone_list[-1] == 1:
return hailstone_list
else:
if hailstone_list[-1] % 2 == 0:
new_num = int(hailstone_list[-1] / 2)
else:
new_num = (hailstone_list[-1] * 3) + 1
hailstone_list.append(new_num)
return hailstone_list
2.9. Checking the input
def test_integer(self):
# str(invalid entries) give the string 'None'
if str(self.hailstone_start.text) == 'None':
return "Invalid number."
# invalid entries give False, so not False is True
if not self.hailstone_start.text:
return "Not a valid start number."
# catch 0, negative ints and floats below 1
if self.hailstone_start.text < 1:
return "Enter a whole number above 0."
# floats
if self.hailstone_start.text != int(self.hailstone_start.text):
return "Postitive Integers, not floats are needed."
# have an int, no error
return None
2.10. Generate Code
def generate(self):
# hide error and clear it
self.error.visible = False
self.error.text = ""
# check for error and display it if present
error = self.test_integer()
if error:
self.error.text = error
self.error.visible = True
self.length.text = ""
self.hailstone_numbers.text = ""
self.set_output_field_vis(False)
return
# continue if no error
hns = self.hailstone(self.hailstone_start.text)
self.hailstone_numbers.text = hns
self.length.text = len(hns)
self.set_output_field_vis(True)
def test_integer(self):
# str(invalid entries) give the string 'None'
if str(self.hailstone_start.text) == 'None':
return "Invalid number."
# invalid entries give False, so not False is True
if not self.hailstone_start.text:
return "Not a valid start number."
# catch 0, negative ints and floats below 1
if self.hailstone_start.text < 1:
return "Enter a whole number above 0."
# floats
if self.hailstone_start.text != int(self.hailstone_start.text):
return "Postitive Integers, not floats are needed."
# have an int, no error
return None
def hailstone(self, num):
# return list of numbers
hailstone_list = [num]
while num > 1:
if hailstone_list[-1] == 1:
return hailstone_list
else:
if hailstone_list[-1] % 2 == 0:
new_num = int(hailstone_list[-1] / 2)
else:
new_num = (hailstone_list[-1] * 3) + 1
hailstone_list.append(new_num)
return hailstone_list
2.11. Final Code
from anvil import *
import anvil.tables as tables
import anvil.tables.query as q
from anvil.tables import app_tables
class Form1(Form1Template):
def __init__(self, **properties):
# Set Form properties and Data Bindings.
self.init_components(**properties)
# hide error field and output fields
self.error.visible = False
self.set_output_field_vis(False)
def set_output_field_vis(self, vis_bool):
self.length_label.visible = vis_bool
self.length.visible = vis_bool
self.hailstone_numbers.visible = vis_bool
def hailstone_start_change(self, **event_args):
if self.hailstone_start.text:
self.hailstone_start.text = min(100000, self.hailstone_start.text)
def generate_click(self, **event_args):
self.generate()
def hailstone_start_pressed_enter(self, **event_args):
self.generate()
def generate(self):
# hide error and clear it
self.error.visible = False
self.error.text = ""
# check for error and display it if present
error = self.test_integer()
if error:
self.error.text = error
self.error.visible = True
self.length.text = ""
self.hailstone_numbers.text = ""
self.set_output_field_vis(False)
return
# continue if no error
hns = self.hailstone(self.hailstone_start.text)
self.hailstone_numbers.text = hns
self.length.text = len(hns)
self.set_output_field_vis(True)
def test_integer(self):
# str(invalid entries) give the string 'None'
if str(self.hailstone_start.text) == 'None':
return "Invalid number."
# invalid entries give False, so not False is True
if not self.hailstone_start.text:
return "Not a valid start number."
# catch 0, negative ints and floats below 1
if self.hailstone_start.text < 1:
return "Enter a whole number above 0."
# floats
if self.hailstone_start.text != int(self.hailstone_start.text):
return "Postitive Integers, not floats are needed."
# have an int, no error
return None
def hailstone(self, num):
# return list of numbers
hailstone_list = [num]
while num > 1:
if hailstone_list[-1] == 1:
return hailstone_list
else:
if hailstone_list[-1] % 2 == 0:
new_num = int(hailstone_list[-1] / 2)
else:
new_num = (hailstone_list[-1] * 3) + 1
hailstone_list.append(new_num)
return hailstone_list
Tasks
Limit the initial input to under 100000.
Limit the initial input to under 100000.
def hailstone_start_change(self, **event_args):
if self.hailstone_start.text:
self.hailstone_start.text = min(100000, self.hailstone_start.text)
Tasks
The longest sequence is 351 for hailstone(77031) for numbers <100,000. Find another hailstone number under 100000 with a sequence length over 200.
Advanced: Create a list of multipliers to replace the 3 multiplier. Add a textbox to enable the user to enter the multiplier. Restrict the values to 1, 3, 5, 7 or 9. e.g 3, 5 or 1, 3, 7. Randomly choose form this list when generating each new number in the hailstone sequence.
The longest sequence is 351 for hailstone(77031) for numbers <100,000. Find another hailstone number under 100000 with a sequence length over 200.
Look at the sequence for hailstone(77031) and find the the next number under 100000. It has a sequence length of 206.
Advanced: Create a list of multipliers to replace the 3 multiplier. Add a textbox to enable the user to enter the multiplier. Restrict the values to 1, 3, 5, 7 or 9. e.g 3, 5 or 1, 3, 7. Randomly choose form this list when generating each new number in the hailstone sequence.
Working app at: https://pc-hailstone-random-multipliers.anvil.app
# code to allow random choice from a list
from random import choice
# code to insert in def generate(self):
def generate(self):
...
# check for error in multiplier and display it if present
error = self.test_multiplier()
if error:
self.error.text = error
self.error.visible = True
self.length.text = ""
self.hailstone_numbers.text = ""
self.set_output_field_vis(False)
return
...
# code to text multiplier input
def test_multiplier(self):
try:
multiplier_list = self.multiplier.text
multiplier_list = multiplier_list.split(",")
multiplier_list = [int(x) for x in multiplier_list if int(x) % 2 == 1 and int(x) < 10 and int(x) > 0]
if len(multiplier_list) == 0:
multiplier_list = [3]
self.multiplier_list = multiplier_list
self.multiplier.text = str(multiplier_list).strip('[]')
print(self.multiplier.text)
return None
except ValueError as error:
self.multiplier_list = None
return "multiplier requires positive integers separated by commas."
except IndexError as error:
self.multiplier_list = None
return "multiplier requires positive integers separated by commas."
# code to generate hailstone using **choice(self.multiplier_list)**
def hailstone(self, num):
# return list of numbers
hailstone_list = [num]
while num > 1:
if hailstone_list[-1] == 1:
return hailstone_list
else:
if hailstone_list[-1] % 2 == 0:
new_num = int(hailstone_list[-1] / 2)
else:
multiplier = choice(self.multiplier_list)
new_num = (hailstone_list[-1] * multiplier) + 1
hailstone_list.append(new_num)
if len(hailstone_list) > 1000:
return hailstone_list
return hailstone_list