import random from turtle import * import time names = ["Jeff", "Fred", "Alice", "Xin", "Max", "Brad"] racers = [] hurdles = [] questions_answered = 0 player_prediction = "" def Instructions(): # a screen that introduces the game, game objective, and hints. Start button allows the player to continue to the next screen screen = Screen() screen.bgcolor("#4D6D96") global pen, start pen = Turtle() pen.hideturtle() pen.color("white") pen.penup() pen.goto(0,100) pen.write("!!!Welcome to the TURTLYMPICS 100m Hurdles!!!", align="center", font=("Arial", 35, "bold")) pen.goto(0,60) pen.write("Objective", align="center", font=("Arial", 15)) pen.goto(0,40) pen.write("PREDICT the winner turtle in a 100m race filled with hurdles", align= "center", font=("Arial", 15)) pen.goto(0,20) pen.write("Answer MATH questions to help your turtle JUMP over hurdles and get BOOSTS!", align="center", font=("Arial", 15)) pen.goto(0,-10) pen.write("Click START to begin", align ="center", font=("Arial",15)) startButton() def startButton(): # used in the instructions(). The player clicks the "start" and the game starts by calling GameStart() button123("Start") button.onclick(GameStart) def GameStart(x, y): #used for startButton(). A transition function that initiates the game by calling IntroScreen(), then RacerIntro() start.hideturtle() pen.clear() IntroScreen() clearScreen() RacerIntro() def IntroScreen(): # sets a clean blank reddish-brown background for the racer introduction screen = Screen() screen.bgcolor("#B05858") def clearScreen(): #clears all turtles and hurdles from screen. preserces RACER turtles, but clears everything else for t in Screen().turtles(): if t in racers: continue t.hideturtle() t.clear() global hurdles for hurdle in hurdles: if hurdle: hurdle.clear() hurdle.hideturtle() hurdles.clear() def RacerIntro(): #Introduces 6 racers on the screen with their names. The continue button pops up and clicking it starts the race by calling StartRace() global racers racers = [] for i in range(6): t = Turtle() t.shape("turtle") t.penup() t.nickname = names[i] racers.append(t) pen = Turtle() pen.hideturtle() pen.color("white") pen.penup() pen.goto(0, 120) pen.write("Meet the Racers!", align="center", font=("Arial", 30, "bold")) for i in range(6): racers[i].goto(-150, 60 - i * 50) pen.goto(-80, 60 - i * 50) pen.write(racers[i].nickname) pen.penup() button123("Continue") button.onclick(StartRace) def getPrediction(): #Get player's prediction on which turtle will win. Stores the prediction as p and returns it so that it can be used in StartRace(). displayRacers() p = textinput("Racers Prediction", "Which racer will win? (Enter name)") return p def displayRacers(): #Shows all racer names and their track lane and asks for player's prediction pen = Turtle() pen.hideturtle() pen.color("white") pen.penup() for i in range(6): pen.goto(-250, -210+(i*15)) pen.write(f"Lane {i+1} : {racers[i].nickname}", font=("Arial", 12)) pen.penup() def setup(): #Positions the turtles on the start line and makes the predicted turtle into blue turtle. Used in drawTrack() after track has been drawn for i in range(6): racers[i].shape("turtle") racers[i].penup() racers[i].goto(-300, i*40) if racers[i].color()[0] != "blue": racers[i].color("black") def drawTrack(): # Draws the race track. Creates background, track, lanes, start/finish, labels. Once track is finished, setup() is called and createHurdles() is also called screen = Screen() screen.bgcolor("#7CBD64") screen.tracer(0) #AI. Asked AI how to speed up the drawing process of the track. this code turns off screen updates so that all drawings happen behind the scenes track = Turtle() track.hideturtle() track.speed(0) #AI. Also asked how to speed up the drawing. This code draws the elements instantly without showing it line by line. leads to faster execution. # background track.penup() track.goto(-400, -100) track.color("#C9452E") track.begin_fill() for _ in range(2): track.forward(900) track.left(90) track.forward(400) track.left(90) track.end_fill() # lanes track.color("white") track.pensize(3) #start track.penup() track.goto(-300, -20) track.pendown() track.goto(-300, (6 * 40) - 20) #finish track.penup() track.goto(300, -20) track.pendown() track.goto(300, (6 * 40) - 20) #Horizontal lane lines track.pensize(1) for i in range(7): y = (i * 40) - 20 track.penup() track.goto(-320, y) track.pendown() track.forward(640) #checker pattern square_size = 10 track.color("black") for row in range(24): for col in range(2): x = 300 + col * square_size y = -20 + row * square_size track.penup() track.goto(x, y) if (row + col) % 2 == 0: track.color("white") else: track.color("black") track.begin_fill() for _ in range(4): track.forward(square_size) track.left(90) track.end_fill() screen.update() #labels track.color("white") track.penup() track.goto(-300, (6 * 40) + 10) track.write("START", align="center", font=("Arial", 18, "bold")) track.goto(300, (6 * 40) + 10) track.write("FINISH", align="center", font=("Arial", 18, "bold")) #lane numbers track.color("white") for i in range(6): track.goto(-350, i * 40 - 10) track.color("white") track.write(f"Lane {i+1}", font=("Arial", 12, "normal")) screen.tracer(1) setup() createHurdles() def createHurdles(): #generate 3 or 4 random positions to place hurdles on the track. white vertical lines as hurdles. stores positions to use for collision detection. global hurdles hurdles = [] num_hurdles = random.randint(3, 4) hurdle_positions = [] for i in range(num_hurdles): x_pos = random.randint(-250, 250) while any(abs(x_pos - pos) < 100 for pos in hurdle_positions): #AI. Wanted to know how to detect any hurdles that were within 100 pixels of other turtles. "any" returns true if any hurdle is within 50 pixels. It stops checking as soon as it finds a "true". x_pos = random.randint(-250, 250) hurdle_positions.append(x_pos) for x_pos in hurdle_positions: for lane in range(6): hurdle = Turtle() hurdle.hideturtle() hurdle.speed(0) hurdle.penup() hurdle.color("white") hurdle.pensize(3) hurdle.goto(x_pos, lane * 40 - 15) hurdle.pendown() hurdle.goto(x_pos, lane * 40 + 15) hurdle.penup() hurdle.hideturtle() hurdles.append((hurdle, x_pos, lane)) def StartRace(x=None, y=None): # a fucntion dedicated to starting the race. drawTrack() to draw a race track with hurdles. Sets the number of questions answered as 0. Runs getPrediction(). Sends player prediction to displayResults() as a parameter clearScreen() drawTrack() global questions_answered questions_answered = 0 global player_prediction player_prediction = getPrediction() for i, racer in enumerate(racers): #Used W3Schools.com. Basically, "enumerate()" assigns an index/number for each item of the loop. This way, this programm can determine which lane/number the player's chosen turtle is in. if racer.nickname.lower() == player_prediction.lower(): racer.color("blue") break displayResult(player_prediction) def race(): # main race loop with hurdle features. when predicted turtle comes near hurdle, the player must answer the math question as generate_math_question() is called. other turtles jump automatically through auto_jump_turtle(). tracks the hurdles that the turtle has passed. correct aswer-> boost. wrong answer -> slow pace. Constantly checks for win global questions_answered global player_prediction player_turtle_index = -1 for i, racer in enumerate(racers): if racer.nickname.lower() == player_prediction.lower(): player_turtle_index = i break hurdles_passed = {i: set() for i in range(6)} win = False while not win: for i in range(6): if racers[i].xcor() < 300: near_hurdle, hurdle_x = check_hurdle_collision(i) if near_hurdle and hurdle_x not in hurdles_passed[i]: if i == player_turtle_index: questions_answered += 1 question, correct_answer = generate_math_question() user_answer = textinput(f"Hurdle Challenge for {racers[i].nickname}!", f"Question {questions_answered}: {question}") if user_answer and user_answer.strip() == correct_answer: jumpTurtle(racers[i], correct=True) racers[i].forward(60) else: jumpTurtle(racers[i], correct=False) racers[i].forward(15) else: time.sleep(0.2) #AI. Asked AI how i can show the turtle getting larger and smaller for the jump animation. The loop of time.sleep() allows me to freeze them game for a specific amount of time each loop so that the jump animation doesn't just flash by. success = auto_jump_turtle(racers[i]) if success: racers[i].forward(30) else: racers[i].forward(20) hurdles_passed[i].add(hurdle_x) else: racers[i].forward(random.randint(10, 30)) win = checkWin() if win: break time.sleep(0.1) return win def check_hurdle_collision(turtle_index): #checks if a turtle is approaching the hurdle. checks for all hurdles in its lane turtle = racers[turtle_index] turtle_x = turtle.xcor() turtle_y = turtle.ycor() lane = int((turtle_y + 20) // 40) for hurdle, hurdle_x, hurdle_lane in hurdles: if hurdle_lane == lane and abs(turtle_x - hurdle_x) < 30: return True, hurdle_x return False, None def generate_math_question(): # create simple math questions using random function. 1-10 for addition and subtraction. 1-5 for multiplication num1 = random.randint(1, 10) num2 = random.randint(1, 10) operation = random.choice(['+', '-', '*']) if operation == '+': answer = num1 + num2 question = f"{num1} + {num2} = ?" elif operation == '-': if num1 < num2: num1, num2 = num2, num1 answer = num1 - num2 question = f"{num1} - {num2} = ?" else: num1 = random.randint(1, 5) num2 = random.randint(1, 5) answer = num1 * num2 question = f"{num1} × {num2} = ?" return question, str(answer) def jumpTurtle(turtle, correct): #animation for turtle jump. correct answer -> smooth/big jump animation. wrong answer -> clumsy/small jump animation, red hit original_size = turtle.turtlesize() original_y = turtle.ycor() if correct: for size in [1.5, 2.0, 1.5, 1.0]: #AI. Asked AI for suggestions on how to show the turtle jumps. I suggested that the turtle change size so it looks closer/bigger from a birds eyeview. The turtle size changes and uses time.sleep() to show the gradual change in size, as well as the chnage in vertical position to make it look like the turtle is jumping over something. turtle.turtlesize(size) turtle.sety(original_y + 15 * (size - 1)) update() time.sleep(0.1) else: turtle.color("red") time.sleep(0.3) turtle.color("black") for size in [1.3, 1.0]: turtle.turtlesize(size) turtle.sety(original_y + 10 * (size - 1)) update() time.sleep(0.15) turtle.turtlesize(original_size[0]) turtle.sety(original_y) update() def auto_jump_turtle(turtle): #automated jumps for not predicted turtles. same way jumpTurtle() works, but a hit is indicated in orange. have 80% chance to be correct original_size = turtle.turtlesize() original_y = turtle.ycor() success = random.random() < 0.8 if success: for size in [1.4, 1.8, 1.4, 1.0]: turtle.turtlesize(size) turtle.sety(original_y + 8 * (size - 1)) update() time.sleep(0.08) else: turtle.color("orange") time.sleep(0.2) turtle.color("black") for size in [1.2, 1.0]: turtle.turtlesize(size) turtle.sety(original_y + 3 * (size - 1)) update() time.sleep(0.1) turtle.turtlesize(original_size[0]) turtle.sety(original_y) update() return success def checkWin(): # checks for any turtle that has crossed the finish line. if yes, it returns turtle name, if not it returns false for i in range(6): if racers[i].xcor() >= 300: return racers[i].nickname return False def displayResult(prediction): # displays final results including: winner, player's prediction, questions answered, and win/lose message winner = race() screen = Screen() screen.clear() screen.bgcolor("#7CBD64") pen = Turtle() pen.hideturtle() pen.color("white") pen.penup() pen.goto(0, 150) pen.write("RACE FINISHED!", align="center", font=("Arial", 30, "bold")) pen.goto(0, 100) pen.write(f"Winner: {winner}", align="center", font=("Arial", 25)) pen.goto(0, 50) pen.write(f"Your prediction: {prediction}", align="center", font=("Arial", 20)) pen.goto(0, 0) pen.write(f"Questions answered: {questions_answered}", align="center", font=("Arial", 15)) if winner.lower() == prediction.lower(): pen.goto(0, -50) pen.color("green") pen.write("Correct! You win!", align="center", font=("Arial", 30, "bold")) else: pen.goto(0, -50) pen.color("red") pen.write("Incorrect! You lose!", align="center", font=("Arial", 30, "bold")) pen.penup() button123("Play Again") button.onclick(restartGame) pen.penup() def restartGame(x, y): #function to restart the game and reset variables global questions_answered, player_prediction, racers, hurdles screen = Screen() screen.clear() questions_answered = 0 player_prediction = "" racers = [] hurdles = [] Instructions() def button123(x): #creates green rectangle as buttons global button, start start = Turtle() start.hideturtle() start.penup() start.goto(-60, -100) start.color("#4D9651") start.begin_fill() for _ in range(2): start.forward(120) start.left(90) start.forward(40) start.left(90) start.end_fill() start_text = Turtle() start_text.hideturtle() start_text.penup() start_text.goto(0, -90) start_text.color("white") start_text.write(f"{x}", align="center", font=("Arial", 20, "bold")) button = Turtle() button.penup() button.goto(0, -90) button.shape("square") button.shapesize(stretch_wid=2, stretch_len=6) button.color("") def main(): Instructions() done() main()