Skip to content

Commit

Permalink
pylint
Browse files Browse the repository at this point in the history
  • Loading branch information
imagine-hussain committed Sep 25, 2023
1 parent 6a6feb3 commit 0ca2ac7
Show file tree
Hide file tree
Showing 2 changed files with 377 additions and 72 deletions.
272 changes: 272 additions & 0 deletions \
Original file line number Diff line number Diff line change
@@ -0,0 +1,272 @@
"""
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<flag>)
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."
}
Loading

0 comments on commit 0ca2ac7

Please sign in to comment.