diff --git "a/\\" "b/\\" deleted file mode 100644 index 54798e320..000000000 --- "a/\\" +++ /dev/null @@ -1,272 +0,0 @@ -""" -Implementation for the 2023 CTF. - - -Scenario: - In the whimsical world of the CTF (Capture The Flag), an extraordinary - challenge awaits our protagonist, Ollie the Otter. Ollie, a brilliant otter - with a passion for education, has embarked on an academic journey like no - other. But there's a twist – Ollie's unique otter nature has led to some - special conditions on his degree planning adventure. - - Ollie's task is to navigate through a degree planner system called - "Circles," specially designed to accommodate his otter-specific - requirements. In this intriguing scenario, you must help Ollie, the otter - scholar, chart his academic path using Circles. - - When you are done, press the `Validate CTF` button on the term planner page - to recieve your flags. - -# Stage 1. The Otter's Academic Odyssey: Charting Uncharted Waters - Being the pioneer Otter in academia, Ollie faces some unique conditions - that must be fulfilled for him to successfully navigate his degree. Let's - tackle parts 0-3 to obtain the first flag. - -# Stage 2. The Kafka Quandary: Or How I Learned To Stop Worrying and Love The -# Handbook - Ollie encounters additional hurdles due to university policies, with - challenges 4 through 7 directly related to these policy-driven obstacles. - Join Ollie in overcoming these challenges and guiding him through this - academic maze - -# Stage 3: (HARD) Numbers, Notions, and the Cult of Calculus: An O-Week Odyssey - During Orientation Week (O-week), Ollie had a chance encounter with a group - of math students. Little did he know that this encounter would lead to him - being initiated into a number-centric cult. Over time, Ollie developed - superstitions about certain numbers, and the ultimate challenges he faces - revolve around aligning his degree plan with his newfound beliefs. With the - last challenges, Ollie on his quest to complete his degree while also - seeking the approval of the enigmatic number cult -""" -from typing import Callable, Optional - -from fastapi import APIRouter -from server.routers.model import PlannerData - -router = APIRouter( - prefix="/ctf", tags=["ctf"], responses={404: {"description": "Not found"}} -) - -def degree(data: PlannerData) -> bool: - return data.program == "3778" - - -def all_courses(data: PlannerData) -> set[str]: - return { - course - for year in data.plan - for term in year - for course in term - } - - -def get_code(course: str) -> int: - return int(course[4:]) - - -def get_faculty(course: str) -> str: - return course[:4] - - -def gen_eds(courses: set[str]) -> set[str]: - return set( - course - for course in courses - if not course.startswith("COMP") and not course.startswith("MATH") - ) - - -def hard_requirements(data: PlannerData) -> bool: - # NOTE: Can't check start year from this - # Frontend should handle most of this anyways - # including validity of the program - return ( - data.program == "3778" - and "COMPA1" in data.specialisations - and "MATHC2" in data.specialisations - and len(data.plan) == 3 - ) - - -def extended_courses(data: PlannerData) -> bool: - extended_courses = { - "COMP3821", - "COMP3891", - "COMP6841", - "COMP6843" - "COMP6845" - } - return len(extended_courses & all_courses(data)) >= 3 - - -def summer_course(data: PlannerData) -> bool: - return any( - course.startswith("COMP") - for year in data.plan - for course in year[0] - ) - - - -def term_sums_even(data: PlannerData) -> bool: - """ - Check that the sum of the course codes in even terms is even - """ - is_even: Callable[[int], bool] = lambda x: x % 2 == 0 - print("Checking even") - for y, year in enumerate(data.plan): - # Exclude summer term + odd terms - for i, term in enumerate(year[2::2], 2): - term_sum = sum(map(get_code, term.keys())) - print(f"{y}T{i} sum: {term_sum}") - if not is_even(term_sum): - print("failed: ", term) - return False - - return True - -# TODO -def term_sums_odd(data: PlannerData) -> bool: - """ - Check that the sum of the course codes in odd terms is odd - """ - is_odd: Callable[[int], bool] = lambda x: x % 2 == 1 - print("Checking odd") - for year in data.plan[::2]: - # Exclude summer term + even terms - for term in year[1::2]: - term_sum = sum(map(get_code, term.keys())) - if not is_odd(term_sum): - print("failed: ", term) - return False - return True - -def term_sums(data: PlannerData) -> bool: - for year in data.plan: - for i, term in enumerate(year[2::2], 1): - if sum(map(get_code, term.keys())) % 2 != i % 2: - return False - - return True - - -def comp1511_marks(data: PlannerData) -> bool: - """ - Ollie must achieve a mark of 100 in COMP1511 to keep his scholarship - """ - for year in data.plan: - for term in year: - for course in term: - _, marks = term[course] # type: ignore - if course == "COMP1511": - return marks == 100 - - return False - - -def gen_ed_sum(data: PlannerData) -> bool: - """ - The sum of GENED course codes must not exceed 2200 - """ - return sum(map(get_code, gen_eds(all_courses(data)))) <= 2200 - - -def gen_ed_faculty(data: PlannerData) -> bool: - """ - Gen-Eds must all be from different faculties - """ - gen_eds_facs = list(map(get_faculty, gen_eds(all_courses(data)))) - return len(gen_eds_facs) == len(set(gen_eds_facs)) - - -def same_code_diff_faculty(data: PlannerData) -> bool: - """ - Must take two courses with the same code but, from different faculties - """ - codes = list(map(get_code, all_courses(data))) - # Can't have duplicate of a course since it's a set - return len(codes) != len(set(codes)) - - -def math_limit(data: PlannerData) -> bool: - """ - In your N-th year, you can only take N + 1 math courses - """ - for i, year in enumerate(data.plan, 1): - num_math = len([ - course - for term in year - for course in term - if course.startswith("MATH") - ]) - if num_math > i + 1: - return False - - return True - -def six_threes_limit(data: PlannerData) -> bool: - """ - There can by at most 6 occurrences of the number 3 in the entire - planner - """ - all_codes = "".join(str(get_code(course)) for course in all_courses(data)) - return all_codes.count("3") <= 6 - -def comp1531_third_year(data: PlannerData) -> bool: - """ - COMP1531 must be taken in the third year - """ - third_year = data.plan[2] - for term in third_year: - for course in term: - if course == "COMP1531": - return True - - return False - -# (validator_func, message, Optional) -requirements: list[tuple[Callable[[PlannerData], bool], str, Optional[str]]] = [ - # Challenge 1 - (hard_requirements, "Before you can submit, you must check that you are in a 3 year CS degree and have a math minor", None), - (summer_course, "Ollie must take one summer COMP course.", None), - (comp1511_marks, "To keep their scholarship, Ollie must achieve a mark of 100 in COMP1511.", None), - (extended_courses, "Ollie must complete FOUR COMP courses with extended in the name that have not been discontinued.", "mVd3_1t_2_un1"), - # Challenge 2 - (comp1531_third_year, "Unable to find a partner earlier, Ollie must take COMP1531 in their third year.", None), - (gen_ed_faculty, "The university has decided that General Education must be very general. As such, each Gen-Ed unit that Ollie takes must be from a different faculty.", None), - (math_limit, "The university has become a big believer in spaced repetition and want to prevent students from cramming subjects for their minors. Now, in their N-th year, Ollie can only take N + 1 math courses.", None), - (gen_ed_sum, "Course codes now reflect the difficulty of a course. To avoid extremely stressful terms, the sum of Olli's Gen-Ed course codes must not exceed 2200.", "i<3TryMesters"), - # Challenge 3 - (same_code_diff_faculty, "You must take two courses from different faculties that have the same course code.", None), - (term_sums_even, "You must ensure that the sum of your course codes in even terms is even. Note that summer courses do not count towards this.", None), - (term_sums_odd, "You must ensure that the sum of your course codes in odd terms is odd. Note that summer courses do not count towards this.", None), - (six_threes_limit, "In all your course codes, there can be at most 6 occurrences of the number 3", "CU1Tur3d"), -] - -@router.post("/validateCtf/") -def validate_ctf(data : PlannerData): - """ - Validates the CTF - """ - passed: list[str] = [] - flags: list[str] = [] - for req_num, (fn, msg, flag) in enumerate(requirements): - if not fn(data): - return { - "valid": False, - "passed": passed, - "failed": req_num, - "flags": flags, - "message": msg - } - passed.append(msg) - if flag is not None: - flags.append(flag) - print("Ok: ", req_num) - return { - "valid": True, - "failed": -1, - "passed": passed, - "flags": flags, - "message": "Congratulations! You have passed all the requirements for the CTF." - }