The following code sets up a game in which a number of pieces of cake are spread randomly throughout a playing field. Your turtle starts at the middle of this playing field and needs to eat cake.
Every time you get within a few pixels of a piece of cake, your turtle eats the cake, gets a point, and grows in size.
Your objective is to write the body of the function seekCake(), such that your turtle tries to eat as much cake as possible. This function will be called once every animation frame, so your function should call at least call the forward() and right()/left() functions of the passed turtle object.
The arguments to the seekCake() function consist of the turtle object, the current frame index (i.e., how many moves have been taken), a maximum distance the turtle can move in one turn, and a grid height and width. The height and width defines the playing field in which the pieces of cake will be placed. Ideally, your turtle should not go outside this playing field (there is no cake there, and you won't be able to see it animated).
You will be assessed based on how well your turtle stays within bounds, how easily understandable your code is, and how many points it scores over the course of a game round.
The following code is provided, so you can test your own function. It will set up a playing field, randomly place pieces of cake, and determine whether you consume one.
# Import the turtle package, so we can actually play the game
import turtle
# Need random for cake placement
import random
# Need math for sqrt(), so we can calculate collisions
import math
# Create a screen on which we can play
screen = turtle.Screen()
# Configure the playing field
dimX = 400
dimY = 400
screen.setup(dimX, dimY)
# Calculate euclidean distance between (x1, y1) and (x2, y2)
def distance(x1, y1, x2, y2):
"""Given two points, (x1, y1) and (x2, y2), calculate the
euclidean distance between them"""
return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
# Collision detection and scoring for turtles and cake pieces
def nearPie(ttl, pieceMap, scoreMap, edibleDistance=30):
"""Given a turtle object, map of piece locations, map of scores,
and optionally a maximum distance at which a piece can be eaten,
determine whether this turtle actuall eats anything this frame."""
x, y = ttl.position()
for (pieceLoc, piece) in pieceMap.items():
otherX, otherY = pieceLoc
if ( piece.isvisible() and
distance(x, y, otherX, otherY) < edibleDistance ):
piece.hideturtle()
scoreMap[ttl] += 1
(w, h, stretch) = ttl.shapesize()
ttl.shapesize(w, h, stretch+5)
Fill out the body of your movement function here. I have also provided a few sample functions here for your reference.
def seekCake_Random(ttl, frameIndex, gridW, gridH, dist=10):
"""Randomly run some random direction and move"""
if ( random.random() < 0.5 ):
ttl.right(90 * random.random())
ttl.fd(dist)
return None
def seekCake_RandomV2(ttl, frameIndex, gridW, gridH, dist=10):
"""Every third frame, turn a random direction"""
if ( frameIndex % 3 == 0 ):
ttl.right(90 * random.random())
ttl.fd(dist)
return None
def seekCake_Checker(ttl, frameIndex, gridW, gridH, dist=10):
"""Spiral outward"""
# These functions ensure we continuously expand our
# spiral
factor = int(math.sqrt(frameIndex))
checkVal = (frameIndex) - factor ** 2
# If we have a perfect square or one before that, turn
if ( checkVal == 0 or checkVal == factor ):
ttl.right(90)
ttl.fd(dist)
return None
def seekCake_Spiro(ttl, frameIndex, gridW, gridH, dist=10):
"""Spiral outward"""
# Calculate the current radius of our arc
r = 20 + 2*frameIndex
# What angle do we need to achieve this distance?
angle = (360 * dist) / (2 * math.pi * r)
# How many whole steps whould we take?
n = int(dist / 3) + 1
step_length = dist / n
step_angle = angle / n
# Move the turtle
for i in range(n):
ttl.fd(step_length)
ttl.right(step_angle)
return None
def seekCake(ttl, frameIndex, gridW, gridH, dist=10):
# Implement your own code here or call one of the examples
seekCake_Spiro(ttl, frameIndex, gridW, gridH, dist)
return None
def randomWalk(ttl, frameIndex, gridW, gridH, maxD):
# Determine the boundaries
xBoundary = gridW // 2 - 1
yBoundary = gridH // 2 - 1
d = random.random()
angle = random.randint(0,90)
if ( d < 0.5 ):
ttl.left(angle)
else:
ttl.right(angle)
ttl.fd(maxD * random.random())
x, y = ttl.position()
# Set boundary condition
if ( abs(x) > xBoundary ):
ttl.right(180)
if ( x < 0 ):
ttl.setx(-xBoundary)
else:
ttl.setx(xBoundary)
if ( abs(y) > yBoundary ):
ttl.right(180)
if ( y < 0 ):
ttl.sety(-yBoundary)
else:
ttl.sety(yBoundary)
# Reset the screen
turtle.clearscreen()
screen.reset()
screen.screensize(dimX, dimY)
# Create a turtle
ttl = turtle.Turtle()
# Determine the boundaries
gridW = screen.canvwidth
gridH = screen.canvheight
xBoundary = screen.canvwidth // 2 - 1
yBoundary = screen.canvheight // 2 - 1
# Collision space. If we're within this many pixels,
# you've eaten a piece. Also, pieces can't overlap
# or be closer to each other than this.
minPieRadius = 30
# Store scores and where pieces are placed
scoreMap = {ttl : 0}
piePlacement = {}
# Configure turtle size
ttl.resizemode("user")
ttl.shapesize(1.0, 1.0, 1)
# Place pieces of cake
pieceCount = 10 # How many pieces?
turtle.addshape("cake.gif") # What does the piece look like?
# For each piece, place it
for i in range(pieceCount):
piePiece = turtle.Turtle()
piePiece.speed(0) # Don't animate the piece's movement
piePiece.hideturtle() # Hide it while we're placing
piePiece.penup() # Don't track its path
# Sentinel for whether this piece is in a valid location
safeDistance = False
# Loop until we are in a valid place
while ( not safeDistance ):
# Find a random x, y point
newX = random.randint(-xBoundary, xBoundary)
newY = random.randint(-yBoundary, yBoundary)
# We assume we are in a good place
safeDistance = True
# For all other pieces that have been placed, check
# if we collide with them
for otherPiece in piePlacement.keys():
otherX, otherY = otherPiece # Other location
# Are we closer than min distance?
if ( distance(newX, newY, otherX, otherY) < minPieRadius ):
safeDistance = False # If so, fail and break out
break
# If we **ARE** in a safe space, set the new position,
# shape, show the piece, and add this piece to the
# dictionary of other pieces
if ( safeDistance ):
piePiece.setposition((newX, newY))
piePiece.shape("cake.gif")
piePiece.showturtle()
piePlacement[(newX, newY)] = piePiece
# Set animation speed (1-10, 1 = slow, 10 = fast, 0 = no animation)
ttl.speed(10)
# Maximum distance we can move in one frame
maxMovement = 30
for i in range(1000):
# Here's where your code gets called.
randomWalk(ttl, i, gridW, gridH, maxMovement)
# Perform collision testing, sizing, and scoring
nearPie(ttl, piePlacement, scoreMap, minPieRadius)
for item in scoreMap.items():
print("Score:", item[1])
random.randint(-200, 200)