Code files for the Byron College coding club.
# The Fibonacci sequence is a series of numbers that, when given 2 initial values,
# each value after is given by the sum of the value before and the value 2 places behind.

# For example, a fibonacci sequence of length 8 can be: 1, 1, 2, 3, 5, 8, 13, 21, 
length = 0
seq = []

seq.append(int(input("Input the first number of the sequence: ")))
seq.append(int(input("Input the second number of the sequence: ")))
length = int(input("How long will this sequence be?: "))
if length > 0:
    for i in range(length):
        nextNum = seq[i]+seq[i+1]
        seq.append(nextNum)
    print(seq)
else:
    print("The length can only be positive!")
BEGINNER/projects/fibonacci.py
zhongheng-liu/byron-college-coding-club
OP_ADD = 0
OP_SUB = 1
OP_DIV = 2
OP_MUL = 3
def program(operation: int):
    if operation == OP_ADD:
        total = 0
        acceptMoreInputs = True
        while acceptMoreInputs:
            num = input("Input number (input \"X\" to stop): ")
            if (num == "X"):
                acceptMoreInputs = False
                continue
            total += int(num) # total = total + num
        print("Your add result is: " + str(total))
    elif operation == OP_SUB:
        firstNum: float = float(input("First number: "))
        secondNum: float = float(input("Second number: "))
        return firstNum - secondNum
    elif operation == OP_DIV:
        firstNum: float = float(input("First number: "))
        secondNum: float = float(input("Second number: "))
        return firstNum / secondNum
    elif operation == OP_MUL:
        firstNum: float = float(input("First number: "))
        secondNum: float = float(input("Second number: "))
        return firstNum * secondNum
    else:
        return
program(OP_ADD)
BEGINNER/projects/high-level-calculator-project.py
zhongheng-liu/byron-college-coding-club
identifier = "@"
prompts = [
    "Proper Noun (Person's Name)",
    "Noun",
    "Adjective (feeling)",
    "Verb",
    "Adjective (feeling)",
    "Animal",
    "Adjective",
    "Verb",
    "Color",
    "Verb (ending with -ing)",
    "Adverb (ending with -ly)",
    "Number",
    "Unit of time",
    "Color",
    "Animal",
    "Number",
    "Silly word",
    "Noun"
]
sentences = [
    "This week I am going camping with @.",
    "I packed my lantern, sleeping bag, and @.", 
    "I am so @ to @ in a tent.",
    "I am @ we might see a @, they are kind of @. ",
    "We are going to hike, fish, and @. ",
    "I have heard that the @ lake is great for @. ",
    "Then we will @ hike through the forest for @ @. ",
    "If I see a @ @ while hiking, I am going to bring it home as a pet! ",
    "At night we will tell @ @ stories and roast @ around the campfire!",
]
newSentences = []
answers = []
for prompt in prompts:
    answers.append(input(prompt+": "))
print(answers)
answerIndex = 0
sentenceIndex = 0
for sentence in sentences:
    newSentence = ""
    for word in sentence.split(" "):
        if word.find("@") == -1: newSentence += word + " " # fail condition
        else:
            newSentence += word.replace("@", answers[answerIndex]) + " "
            answerIndex += 1 # next answer
    newSentences.append(newSentence)
    sentenceIndex += 1
print("".join(newSentences))
BEGINNER/projects/madlibs-project.py
zhongheng-liu/byron-college-coding-club
# There are several primitive data types in Python,
# you may encounter more if you are coding in other languages
x: int = 0
y: float = 0.112
string: str = "Hi Mom!"
print(type(x))

# These are later concepts, but you can try it yourself at home
spy_satellite_locations: list = [(0.23, 2.79), (68.0, 0.34)]
submarine_coordinates = (0, 203)

# Discussion
# Data types are less useful in Python, since it is not a strongly-typed language.
# However, it is good practice to note down what type a variable is by using the "variable: type"
# syntax. In more complex programs, you will find that type definitions are extremely helpful in 
# managing class-based inheritance and objects with tons of properties and methods. 
# You will find out more in later sessions.

BEGINNER/s1-intro-basics/datatypes.py
zhongheng-liu/byron-college-coding-club
print("Hello world")

# Declaring variables and constants
# The idea of variables are that they are "abbreviations" 
# of a result of calculation

x = 0 # integer - a whole number
print(x) # print function, outputs the value in question

calc = 6 + 3 # variables as stores of results of calculations
print(calc) # b = 9, the print will output 9

text = "And then there was light" # Strings (Text)
fruits = ["Apple", "Orange", "Watermelon"] # Arrays / lists

# Constants are variables that do not change.

# Boolean (true/false) & constant (not strict)
CAN_MODIFY_DATA = False 
# In Python, there are no rules that regulate what is a constant
# and what is a variable. But usually, we write constants with
# capital letters and underlines to hint other programmers what
# kind of variable this is.  

BEGINNER/s1-intro-basics/declarations.py
zhongheng-liu/byron-college-coding-club
# This is output
print("I am the output result, uwu")

# This is an input. An input will ALWAYS be a str type. 
# To convert it to another type, enclose it in int(), float(), str(), or other methods.
number = input("Write here any number you like: ")

print(number)

# The following 2 print statements concatenate a string with another string 
print("The number is "+ number)
print("The number is ", number)

# f-strings are basically "syntactic sugar" to make the code for concatenating strings 
# look nicer by inserting the variable in the code directly.
# Example
x = 128324 # pretend this is some kind of result from a process
print(f"The result of the calculation is {x}")


BEGINNER/s1-intro-basics/io.py
zhongheng-liu/byron-college-coding-club
test1 = 'Hello'
test2 = 'World'

test3 = test1 + test2
print(test3)
# expected result: HelloWorld

test4 = test1 + ' ' + test2
print(test4)
# expected result: Hello World

print(test1, test2)
# expected result: Hello World

BEGINNER/s2-strings/connecting_strings.py
zhongheng-liu/byron-college-coding-club
# We use an escape character, when we try to insert characters that are illegal in a string.
# An escape character is a backflash \ followed by the string you want to insert.

# An example of an illegal character is a double quote inside a string that is surrounded by double quotes:
text = 'My favourite subject is'Math'in school'

# Using escape character to avoid the issue
text1 = 'My facourite subject is \'Math\'in school'
print(text1)

BEGINNER/s2-strings/escape_character.py
zhongheng-liu/byron-college-coding-club
name = 'Steven'
age = '16'
text1 = 'My name is {} and I am {} years old'
print(text1.format(name, age))
# The curly bracket is the position where you want to put the variable
# The order of the variable in the text will be the same as it is in the format() method

# Another example
car = 'racing car'
brand = 'BMW'
price = '500k'
text2 = 'This {} from {} costs {}'
print(text2.format(car, brand, price))

# There is the other similar method which can achive the same effect
text3 = f'My name is {name} and I am {age} years old'
print(text3)
# The expected result of text3 should be the same as text1

BEGINNER/s2-strings/format_strings.py
zhongheng-liu/byron-college-coding-club
# Slicing string
test = 'Hello World!'
print(test[3:])
# expected result: lo World!
print(test[-4: -2])
# expected result: rl

BEGINNER/s2-strings/slicing_strings.py
zhongheng-liu/byron-college-coding-club
test = 'Hello World'

print(test.upper())
# print test in upper case

print(test.lower())
# print in lower case

print(len(test))
# find the length of the strings
# this works in list too which we will talk about in later sessions

print(test.strip())
# remove space

print(test.replace("d", "ddd"))
# replace d with ddd

print(test.split(' '))
# split the string according to what you entered, the output result won't contain the stirng you entered

print(test.find('Hello'))
# Return the position of the value you entered in the strings

test1 = 'oiiaioiiiai'
print(test1.count('i'))
# return the number of times of the value you entered in the strings

'''
You can check this Python official documentation link for more string methods：  
https://docs.python.org/3/library/stdtypes.html#string-methods
'''

BEGINNER/s2-strings/string_methods.py
zhongheng-liu/byron-college-coding-club
# Functions are objects that take a number of inputs and spit out an output
def do_something(arg1: str, arg2: str):
    print(f"I am doing something with {arg1} and {arg2}...")
    result = arg1.capitalize()[2:-1]
    result = result + arg2[0]
    print("I have finished. Now I will return the result of my doing something")
    return result # Returns a value to be used by another process

# Functions allow us to reuse code that has been written once already to 
# avoid repeating yourself. Why? Because programmers are lazy and don't 
# want to write things twice.

# Sometimes you won't define a function yourself, but instead use a 
# function from another piece of code. This can be from a standard library,
# the usage of which we will talk about in the future.

print(do_something("Apple", "Banana"))
print(do_something("China", "America"))

# Discussion
# Usually, it is good practice to explicitly define the types for your 
# function's parameters.This lets them know what values to input that
# won't cause the program to break down. 
# In languages with higher type safety (Java, TypeScript), it is
# mandatory that you label every value with a type definition.  
BEGINNER/s3-functions-loops/functions.py
zhongheng-liu/byron-college-coding-club
# for loop
# Iterate over a sequence
fruits = ["apple", "banana", "cherry"]
for fruit in fruits:
    print(fruit)
   
# Print numbers from 1 to 5
for i in range(1, 6):
    print(i)

# while loop
# Print numbers from 1 to 5 using a while loop
i = 1
while i <= 5:
    print(i)
    i += 1

# nested loop
# Multiplication table using nested loops
for i in range(1, 9):
    for j in range(1, 9):
        print(i * j, end=" ")
    print()

#loop control statement
# Break statement
for i in range(1, 11):
    if i == 5:
        break
    print(i)
   
# Continue statement
for i in range(1, 11):
    if i % 2 == 0:
        continue
    print(i)
   
# Pass statement
for i in range(1, 6):
    if i == 3:
        pass  # the pass statement is a placeholder statement that does nothing
    else:
        print(i)
# When the loop variable i is equal to 3, the pass statement is encountered. 
# The execution simply continues to the next iteration of the loop. 
# For all other values of i, the else block is executed, and the value of i is printed.

BEGINNER/s3-functions-loops/loops.py
zhongheng-liu/byron-college-coding-club
list = ["apple", "banana", "peach", "watermelon"]
num = 1
print(list[num])
# in python, everything starts from 0
# so print(list[1]) will be the second element in the list
# and print(list[0]) will be the first element in the list
print(list[-1])
# this will return the last element in the list as what we've metioned in string
print(list[1:3])
# will start at index 1 (included) and end at index 3 (not included)
print(list[:1])
# print to the index 1 element (not included)
print(list[2:])
# print from the index 2 element (included)
# all of them work in the same way in negative number

'''
# The other example (original file BEGINNER/s4-list/list.py)
# An array is a list of objects that can be manipulated
data = [0, 56, 12, 3556, 6787, 114514, 1919810]

print(data)

# display data points
print(data[0])
print(data[2])
print(data[-1])

# Discussion TODO
'''

BEGINNER/s4-list/access_list.py
zhongheng-liu/byron-college-coding-club
list1 = [1, 2, 3]
list2 = [4, 5, 6]

# add item to the end of the list
list1.append(4)
print(list1)

# extend the list
list1.extend(list2)
print(list1)
# two lists are going to be combined together
# you can extend thw list with tuple, sets, dictionaries etc. too
# Eg:
tuple = ("hello", "world")
newlist = [1, 2, 3, 4]
newlist.extend(tuple)

BEGINNER/s4-list/add_list_item.py
zhongheng-liu/byron-college-coding-club
# An array is a list of objects that can be manipulated
data = [0, 56, 12, 3556, 6787, 114514, 1919810]

print(data)

# display data points
print(data[0])
print(data[2])
print(data[-1])

# array comprehension and manipulation
print(data.sort())

data[5] = 60
print(data)

# Discussion TODO

BEGINNER/s4-list/list.py
zhongheng-liu/byron-college-coding-club
data = [0, 56, 12, 3556, 6787, 114514, 1919810]

data[0] = 100
print(data)

data[1:3] = [1, 2]
print(data)

data[4:6] = [343]
print(data)
# If you insert less items than you replace,
# the new items will be inserted where you specified, 
# and the remaining items will move accordingly

data[1:2] = [5, 6, 7, 8]
print(data)
# If you insert more items than you replace, 
# the new items will be inserted where you specified,
# and the remaining items will move accordingly

# insert item
data.insert(0, '90')
print(data)
# the position of inserted element is before the index that it is inputted

BEGINNER/s4-list/modify_list.py
zhongheng-liu/byron-college-coding-club
fruits = ["Apple", "Banana", "Peach"]
fruits1 = fruits

fruits.remove("Peach")
print(fruits)
# This remove a specific element that you want

fruits.pop(0)
print(fruits)
# This will delete the element with coresponding index

del fruits1[0]
# This will delete the element with coresponding index too

del fruits1
# This will delete the whole list

BEGINNER/s4-list/remove_list_item.py
zhongheng-liu/byron-college-coding-club
# An array is a list of objects that can be manipulated
data = [0, 56, 12, 3556, 6787, 114514, 1919810]

print(data)

# display data points
print(data[0])
print(data[2])
print(data[-1])

# array comprehension and manipulation
print(data.sort())

data[5] = 60
print(data)

# Discussion TODO

HIGHER/s1-intro/arrays_sets.py
zhongheng-liu/byron-college-coding-club
# Classes are fundamental to modern programming development. 
# They allow for multiple objects to be created from the same
# template, and therefore access their internal properties.

# More often than not, classes primarily act as collections of
# data that can be read from and written to. However, class based
# inheritance allows for a top-down programming structure to be 
# created in order to allow for multiple data structures that can 
# be used when needed. 

# The clever thing about classes is how they manage data dynamically.
# Previously, you can only define a set of variables that changes 
# globally, or in the scope of functions. Now you can alter the 
# properties of an object basically anywhere! You will see applications
# of this in many instances, and you will see why it is extremely useful.

class Employee:
    def __init__(self, salary: int, name: str, level: int, moneyInAccountCents: int, profession: str) -> None:
        self.salaryCents = salary
        self.name = name
        self.level = level
        self.moneyInAccountCents = moneyInAccountCents
        self.profession = profession
    def pay(self, days: int):
        self.moneyInAccountCents += self.salaryCents * days
        print(f"{self.name} was paid {days} days of salary of {self.salaryCents/100} dollars. Now they have {self.moneyInAccountCents / 100} dollars in their account")
    def buy(self, amountCents: int):
        self.moneyInAccountCents -= amountCents
        print(f"{self.name} bought something that cost him {amountCents/100} dollars. Now they have {self.moneyInAccountCents / 100} dollars in account.")
    def introduce(self):
        print(f"Hi, I am {self.name}. I work as a {self.profession} at level {self.level} with a salary of {self.salaryCents/100} dollars per day. I have {self.moneyInAccountCents / 100} dollars in my bank account.")

john = Employee(10000, "John Doe", "0", 45942, "Programmer")

john.introduce()
john.buy(30000)
john.pay(4)
john.introduce()
HIGHER/s1-intro/classes.py
zhongheng-liu/byron-college-coding-club
# You will come to scenarios where you want to handle an Exception 
# to modify how your program works under known scenarios

# In these scenarios, use the try-except syntax
try:
    raw_input = input("Input literally anything: ")

    # Converts the input into an integer type
    # Throws ValueError if the input is not a number
    number = int(raw_input)
    print("Your program didn't produce an exception. You deserve a lolipop!")
    print(f"Your input was {number}, what would happen if you put something that wasn't a number?")
except ValueError as error:
    print(error)
    print("ValueError has occured. What you entered is not a number :(")

# Discussion
# The program branches into 2 paths, one that is the normal execution logic, 
# another with an exception occuring. This is usually seen in file handling,
# where the file handler may produce an exception when the file is not found.
# This doesn't mean the code has a bug, instead it means the program needs 
# an alternative method to consider when the file has not been created yet.
# Perhaps you will need to create a new file, or overwrite an existing one.
# 
# It is important to remember that Exceptions != Bugs. It is commonplace for
# Standard libraries to use exceptions as branches for certain conditions, or
# to filter out unreasonable values. More often than not, you use exception 
# handling to
HIGHER/s1-intro/error_handling.py
zhongheng-liu/byron-college-coding-club
from __future__ import annotations
from math import sqrt
# In Session 1, we talked about having classes as templates of objects.
# What if objects can EVOLVE and MORPH into other types?

# If I define a Truck. A race truck and a freight truck that both belong
# to a Truck parent class is not going to have the same usage. 
# This is why we use inheritance.
class Truck:
    def __init__(
            self, 
            weightKg: float, 
            manufacturer: str, 
            fuel_type: str,
            lifeKm: float = 0
            ) -> None:
        self.weightKg = weightKg
        self.manufacturer = manufacturer
        self.fuel_type = fuel_type
        self.lifeKm = lifeKm
        return
    def beep(self):
        print(f"Beep beep! I am a {self.manufacturer} truck that consumes {self.fuel_type}! I have done {self.lifeKm} Km!")

# RaceTruck inherits Truck, 
# and hence it contains all of Truck's methods and properties, 
# but also has its own unique method, race(), and property, accelerationMetersPerSec
class RaceTruck(Truck):
    def __init__(
            self, 
            weightKg: float, 
            manufacturer: str, 
            fuel_type: str,
            accelerationMetersPerSec: float,
            lifeKm: float = 0
            ) -> None:
        # Here below, the parent class is defined first
        super().__init__(weightKg, manufacturer, fuel_type, lifeKm)

        # Additional properties here
        self.accelerationMetersPerSec = accelerationMetersPerSec
        return
    # New method, unique to RaceTruck
    def race(self, otherRaceTruck: RaceTruck, distanceMeters: float):

        # Physics stuff... Don't worry about it too much
        selfTimeTakenSec = sqrt(2*distanceMeters/self.accelerationMetersPerSec)
        otherTimeTakenSec = sqrt(2*distanceMeters/otherRaceTruck.accelerationMetersPerSec)
        
        # Here, the property of the parent is modified 
        self.lifeKm += distanceMeters
        otherRaceTruck.lifeKm += distanceMeters
        
        # If the first truck uses less time to traverse the distance, it wins
        return selfTimeTakenSec < otherTimeTakenSec
    
# 2 different RaceTruck's that are based off the same class, but have different parameters
truck1 = RaceTruck(9000, "Mercedes-Benz", "Diesel", 0.8, 230)
truck2 = RaceTruck(9000, "Ford", "Petrol", 1.0, 67)

# Race time! Vroom vroom
truck1Wins = truck1.race(truck2, 1000)

if truck1Wins: 
    print("Truck 1 wins!")
    truck1.beep() # Methods from the parent class can still be referenced 
else: 
    print("Truck 2 wins!")
    truck2.beep() # Same here
HIGHER/s2-advanced-concepts/inheritance.py
zhongheng-liu/byron-college-coding-club
from random import randint
from time import sleep

# Generating some random data for testing.
def generate_random_data(lower: int, upper: int, length: int):
    data = []
    for _ in range(length):
        data.append(randint(lower, upper))
    return data

# This is an example of Bubble Sort, one of the most basic examples of an algorithm
# It functions by selecting 2 items each time, compares if they are in the correct position
# If they are not, they are swapped. If they are, continue to the next position (indicated by i)
def bubble_sort(arr: list):
    checks = 0
    while checks < len(arr):
        print(f"Checks={checks}")
        i = 0
        while i < len(arr) - 1:
            # sleep(1)
            if arr[i] < arr[i+1]: 
                checks += 1
            else: 
                tmp = arr[i+1]
                arr[i+1] = arr[i]
                arr[i] = tmp
                print(arr)
            i += 1
    return arr
narr = generate_random_data(0, 100, 10)
narr = [i for i in range(100, 0)]
print(narr)
print(bubble_sort(narr))
HIGHER/s2-advanced-concepts/writing_algorithms.py
zhongheng-liu/byron-college-coding-club