| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576 |
- import operator
- import logging
- import random
- # Logging to console
- logger = logging.getLogger(__name__)
- logging.basicConfig(level=logging.INFO) # Set loglevel to INFO or higher to prevent console spam.
- """
- Helper functions
- """
- # Return some hex colour
- """
- Waiting for Stanford to fix a bug in the environment
- CIP bug: https://codeinplace.stanford.edu/cip6/report?post=ac7c4ffd-1f90-475a-b4ee-2e5b73c5348f
- from matplotlib import colors
- import decimal
- def get_random_colour():
- return decimal.Decimal(random.randrange(0,10)/10), decimal.Decimal(random.randrange(0,10)/10), decimal.Decimal(random.randrange(0,10)/10)
- colour = colors.rgb2hex((get_random_colour()))
- background = colors.rgb2hex((get_random_colour()))
- """
- def generate_random_colour():
- rgb_values = [random.randrange(256), random.randrange(256), random.randrange(256)]
- hex_colours = [hex(value)[2:] for value in rgb_values] # Strip the '0x' prefix
- for i in range(len(hex_colours)):
- if len(str(hex_colours[i])) == 1:
- hex_colours[i] = f"0{hex_colours[i]}"
- return '#' + ''.join(hex_colours)
- """
- Karel functions
- """
- # Erase a passed Karel from the canvas
- def erase_karel(karel):
- canvas = karel[1][0]
- for shape in karel[0].values():
- canvas.delete(shape)
- logger.debug(f"Erased karel: {karel}")
- # Move a passed Karel on the canvas
- """
- Waiting for Stanford to fix a bug in the environment
- CIP bug: https://codeinplace.stanford.edu/cip6/report?post=7043ece9-7653-4e39-8a63-5f4544b1d74b
- def move_karel(canvas, karel, dx, dy):
- for shape in karel[0].values():
- canvas.move(shape, dx, dy)
- logger.debug(f"Moved: {karel} by {dx} horizontally, {dy} vertically")
- """
- def move_karel(karel, dx:int=0, dy:int=0):
- # Update coordinates in list
- karel[1][1] += dx
- karel[1][2] += dy
- erase_karel(karel)
- new_karel = draw_karel(karel[1][0], karel[1][1], karel[1][2], karel[1][3], karel[1][4], karel[1][5], karel[1][6], karel[1][7])
- logger.debug(f"Moved: {karel} by {dx} horizontally, {dy} vertically, to {new_karel}")
- return new_karel
- # Change the orientation of a passed Karel
- def rotate_karel(karel, direction):
- # Relative turn
- if direction == "right" or direction == "left":
- # East
- if karel[1][4] == "east":
- if direction == "right":
- karel[1][4] = "south"
- print(1)
- if direction == "left":
- karel[1][4] = "north"
- elif karel[1][4] == "east-flipped":
- if direction == "right":
- karel[1][4] = "south-flipped"
- if direction == "left":
- karel[1][4] = "north-flipped"
- # North
- elif karel[1][4] == "north":
- if direction == "right":
- karel[1][4] = "east"
- if direction == "left":
- karel[1][4] = "west"
- elif karel[1][4] == "north-flipped":
- if direction == "right":
- karel[1][4] = "east-flipped"
- if direction == "left":
- karel[1][4] = "west-flipped"
- # West
- elif karel[1][4] == "west":
- if direction == "right":
- karel[1][4] = "north"
- if direction == "left":
- karel[1][4] = "soutch"
- elif karel[1][4] == "west-flipped":
- if direction == "right":
- karel[1][4] = "north-flipped"
- if direction == "left":
- karel[1][4] = "south-flipped"
- # South
- elif karel[1][4] == "south":
- if direction == "right":
- karel[1][4] = "west"
- if direction == "left":
- karel[1][4] = "east"
- elif karel[1][4] == "north-flipped":
- if direction == "right":
- karel[1][4] = "west-flipped"
- if direction == "left":
- karel[1][4] = "east-flipped"
-
- # Absolute rotation
- elif direction == "east" or direction == "east-flipped" or direction == "north" or direction == "north-flipped" or direction == "west" or direction == "west-flipped" or direction == "south" or direction == "south-flipped":
- karel[1][4] = direction
-
- # Syntax error
- else:
- logger.error(f"Invalid rotation direction: {direction}")
- erase_karel(karel)
- new_karel = draw_karel(karel[1][0], karel[1][1], karel[1][2], karel[1][3], karel[1][4], karel[1][5], karel[1][6], karel[1][7])
- logger.debug(f"Rotated Karel: {karel} by {direction} as {karel[1][4]} to {new_karel}")
- return new_karel
- # Recolour a passed Karel on the canvas
- """
- Waiting for Stanford to fix a bug in the environment
- CIP bug: https://codeinplace.stanford.edu/cip6/report?post=c36ed931-3019-4830-8ba6-655c1a513471
- def recolour_karel(canvas, karel, colour:str="black", background:str="white"):
- for name, shape in karel[0].items(): # Loop over Karel dict
- if name.endswith("_fill"): # Background shape
- canvas.set_color(shape, background)
- canvas.set_outline_color(shape, background)
- else: # Foreground shape
- if name.endswith("_line") or name.endswith("_corner") or name == "mouth":
- canvas.set_color(shape, colour)
- elif name == "eye":
- #canvas.set_color(shape, "transparent")
- canvas.set_outline_color(shape, colour)
- else: # Legs and feet
- canvas.set_color(shape, colour)
- canvas.set_outline_color(shape, colour)
- """
- def recolour_karel(karel, colour:str="black", background:str="white"):
- # Random colours
- if colour == "random":
- colour = generate_random_colour()
- if background == "random":
- background = generate_random_colour()
- # Update colours in list
- karel[1][5] = colour
- karel[1][6] = background
- erase_karel(karel)
- new_karel = draw_karel(karel[1][0], karel[1][1], karel[1][2], karel[1][3], karel[1][4], karel[1][5], karel[1][6], karel[1][7])
- logger.debug(f"Re-coloured Karel: {karel} with {colour} and {background} to {new_karel}")
- return new_karel
- # Draw a Karel
- def draw_karel(
- canvas,
- centre_x:int=25,
- centre_y:int=25,
- size:int=50,
- orientation:str="east",
- colour:str="black",
- background:str="white",
- transparent:bool=False
- ):
-
- # Body constants
- MARGIN = size / 8
- APPENDAGE_MULTIPLIER = MARGIN * 0.6
-
- # Random colours
- if colour == "random":
- colour = generate_random_colour()
- if background == "random":
- background = generate_random_colour()
- ''' Flipper case
- In order to be able to flip Karel, the operands in the forumlas must be able to switch around.
- Orientations are in relation to the centre of Karel.
- '''
- match orientation.lower():
- case "east" | "south-flipped":
- left_operand = operator.sub # Left / Top
- top_operand = operator.sub # Top / Left
- right_operand = operator.add # Right / Bottom
- bottom_operand = operator.add # Bottom / Right
- case "east-flipped" | "south":
- left_operand = operator.sub # Left / Top
- top_operand = operator.add # Bottom / Right
- right_operand = operator.add # Right / Bottom
- bottom_operand = operator.sub # Top / Left
- case "west" | "north-flipped":
- left_operand = operator.add # Right
- top_operand = operator.add # Bottom
- right_operand = operator.sub # Left
- bottom_operand = operator.sub # Top
- case "west-flipped" | "north":
- left_operand = operator.add # Right / Bottom
- top_operand = operator.sub # Top / Left
- right_operand = operator.sub # Left / Top
- bottom_operand = operator.add # Bottom / Right
-
- # Coords case
- if orientation.lower() == "north" or orientation.lower() == "north-flipped" or orientation.lower() == "south" or orientation.lower() == "south-flipped":
- temp_x = centre_x
- centre_x = centre_y
- centre_y = temp_x
- #pass
- # Borders
- left = left_operand(centre_x, size / 2)
- top = top_operand(centre_y, size / 2)
- right = right_operand(centre_x, size / 2)
- bottom = bottom_operand(centre_y, size / 2)
- margin = size / 8
- appendage_multiplier = margin * 0.6
- # Body borders
- body_left = left_operand(centre_x, size / 3)
- body_top = top
- body_right = right_operand(centre_x, size / 3)
- body_bottom = bottom_operand(centre_y, size / 2.6)
- eye_left = right_operand(body_left, margin)
- eye_top = bottom_operand(body_top, margin)
- eye_right = left_operand(body_right, margin)
- eye_bottom = top_operand(body_bottom, margin * 2)
- # Body coordinates
- top_left_corner = body_left, body_top
- left_diagonal_top = body_left, top_operand(body_bottom, margin)
- left_diagonal_bottom = eye_left, body_bottom
- right_diagonal_top = eye_right, body_top
- right_diagonal_bottom = body_right, bottom_operand(body_top, margin)
- bottom_right_corner = body_right, body_bottom
- mouth_left_corner = centre_x, bottom_operand(eye_bottom, margin)
- mouth_right_corner = eye_right, mouth_left_corner[1]
- # Left appendage borders
- leftLeg_left = left_operand(body_left, margin)
- leftLeg_top = eye_bottom
- leftLeg_right = body_left
- leftLeg_bottom = bottom_operand(leftLeg_top, appendage_multiplier)
- leftFoot_left = leftLeg_left
- leftFoot_top = leftLeg_bottom
- leftFoot_right = right_operand(leftLeg_left, appendage_multiplier)
- leftFoot_bottom = bottom_operand(leftFoot_top, appendage_multiplier)
- # Right appendage borders
- rightLeg_left = centre_x
- rightLeg_top = body_bottom
- rightLeg_right = right_operand(rightLeg_left, appendage_multiplier)
- rightLeg_bottom = bottom
- rightFoot_left = rightLeg_right
- rightFoot_top = top_operand(bottom, appendage_multiplier)
- rigthFoot_right = right_operand(rightLeg_right, appendage_multiplier)
- rightFoot_bottom = bottom
- # Draw Karel
- match orientation.lower():
- case "east" | "east-flipped" | "west" | "west-flipped":
- if not transparent:
- # Meat
- top_fill = canvas.create_rectangle(
- top_left_corner[0],
- top_left_corner[1],
- right_diagonal_top[0],
- eye_top,
- background,
- background
- )
- top_corner_fill = canvas.create_polygon(
- right_diagonal_top[0] , right_diagonal_top[1],
- right_diagonal_bottom[0], right_diagonal_bottom[1],
- eye_right, eye_top,
- color = background,
- outline = background
- )
- left_fill = canvas.create_rectangle(
- top_left_corner[0],
- eye_top,
- eye_left,
- left_diagonal_top[1],
- background,
- background
- )
- right_fill = canvas.create_rectangle(
- eye_right,
- eye_top,
- right_diagonal_bottom[0],
- eye_bottom,
- background,
- background
- )
- bottom_fill = canvas.create_rectangle(
- eye_left,
- eye_bottom,
- bottom_right_corner[0],
- bottom_right_corner[1],
- background,
- background
- )
- bottom_corner_fill = canvas.create_polygon(
- left_diagonal_top[0], left_diagonal_top[1],
- eye_left, left_diagonal_top[1],
- left_diagonal_bottom[0], left_diagonal_bottom[1],
- color = background,
- outline = background
- )
-
- # Outlines
- top_line = canvas.create_line(
- top_left_corner[0], top_left_corner[1],
- right_diagonal_top[0], right_diagonal_top[1],
- colour
- )
- top_corner = canvas.create_line(
- right_diagonal_top[0], right_diagonal_top[1],
- right_diagonal_bottom[0], right_diagonal_bottom[1],
- colour
- )
- left_line = canvas.create_line(
- top_left_corner[0], top_left_corner[1],
- left_diagonal_top[0], left_diagonal_top[1],
- colour
- )
- bottom_corner = canvas.create_line(
- left_diagonal_top[0], left_diagonal_top[1],
- left_diagonal_bottom[0], left_diagonal_bottom[1],
- colour
- )
- bottom_line = canvas.create_line(
- left_diagonal_bottom[0], left_diagonal_bottom[1],
- bottom_right_corner[0], bottom_right_corner[1],
- colour
- )
- right_line = canvas.create_line(
- right_diagonal_bottom[0], right_diagonal_bottom[1],
- bottom_right_corner[0], bottom_right_corner[1],
- colour
- )
- left_leg = canvas.create_rectangle(
- leftLeg_left,
- leftLeg_top,
- leftLeg_right,
- leftLeg_bottom,
- colour,
- colour
- )
- left_foot = canvas.create_rectangle(
- leftFoot_left,
- leftFoot_top,
- leftFoot_right,
- leftFoot_bottom,
- colour,
- colour
- )
- right_leg = canvas.create_rectangle(
- rightLeg_left,
- rightLeg_top,
- rightLeg_right,
- rightLeg_bottom,
- colour,
- colour
- )
- right_foot = canvas.create_rectangle(
- rightFoot_left,
- rightFoot_top,
- rigthFoot_right,
- rightFoot_bottom,
- colour,
- colour
- )
- eye = canvas.create_rectangle(
- eye_left,
- eye_top,
- eye_right,
- eye_bottom,
- "transparent",
- colour,
- )
- mouth = canvas.create_line(
- mouth_left_corner[0], mouth_left_corner[1],
- mouth_right_corner[0], mouth_right_corner[1],
- colour
- )
-
- case "north" | "north-flipped" | "south" | "south-flipped":
- if not transparent:
- # Meat
- top_fill = canvas.create_rectangle(
- top_left_corner[1], # Top Y
- right_diagonal_top[0], # Right X
- eye_top, # Bottom Y
- top_left_corner[0], # Left X
- background,
- background
- )
- top_corner_fill = canvas.create_polygon(
- right_diagonal_top[1] , right_diagonal_top[0],
- right_diagonal_bottom[1], right_diagonal_bottom[0],
- eye_top, eye_right,
- color = background,
- outline = background
- )
- left_fill = canvas.create_rectangle(
-
- eye_top,
- eye_left,
- left_diagonal_top[1],
- top_left_corner[0],
- background,
- background
- )
- right_fill = canvas.create_rectangle(
-
- eye_top,
- right_diagonal_bottom[0],
- eye_bottom,
- eye_right,
- background,
- background
- )
- bottom_fill = canvas.create_rectangle(
-
- eye_bottom,
- bottom_right_corner[0],
- bottom_right_corner[1],
- eye_left,
- background,
- background
- )
- bottom_corner_fill = canvas.create_polygon(
- left_diagonal_top[1], left_diagonal_top[0],
- left_diagonal_top[1], eye_left,
- left_diagonal_bottom[1], left_diagonal_bottom[0],
- color = background,
- outline = background
- )
-
- # Outlines
- top_line = canvas.create_line(
- top_left_corner[1], top_left_corner[0],
- right_diagonal_top[1], right_diagonal_top[0],
- colour
- )
- top_corner = canvas.create_line(
- right_diagonal_top[1], right_diagonal_top[0],
- right_diagonal_bottom[1], right_diagonal_bottom[0],
- colour
- )
- left_line = canvas.create_line(
- top_left_corner[1], top_left_corner[0],
- left_diagonal_top[1], left_diagonal_top[0],
- colour
- )
- bottom_corner = canvas.create_line(
- left_diagonal_top[1], left_diagonal_top[0],
- left_diagonal_bottom[1], left_diagonal_bottom[0],
- colour
- )
- bottom_line = canvas.create_line(
- left_diagonal_bottom[1], left_diagonal_bottom[0],
- bottom_right_corner[1], bottom_right_corner[0],
- colour
- )
- right_line = canvas.create_line(
- right_diagonal_bottom[1], right_diagonal_bottom[0],
- bottom_right_corner[1], bottom_right_corner[0],
- colour
- )
- left_leg = canvas.create_rectangle(
- leftLeg_top,
- leftLeg_right,
- leftLeg_bottom,
- leftLeg_left,
- colour,
- colour
- )
- left_foot = canvas.create_rectangle(
- leftFoot_top,
- leftFoot_right,
- leftFoot_bottom,
- leftFoot_left,
- colour,
- colour
- )
- right_leg = canvas.create_rectangle(
- rightLeg_top,
- rightLeg_right,
- rightLeg_bottom,
- rightLeg_left,
- colour,
- colour
- )
- right_foot = canvas.create_rectangle(
- rightFoot_top,
- rigthFoot_right,
- rightFoot_bottom,
- rightFoot_left,
- colour,
- colour
- )
- eye = canvas.create_rectangle(
- eye_top,
- eye_right,
- eye_bottom,
- eye_left,
- "transparent",
- colour,
- )
- mouth = canvas.create_line(
- mouth_left_corner[1], mouth_left_corner[0],
- mouth_right_corner[1], mouth_right_corner[0],
- colour
- )
- # Return each object so it can later be altered/destroyed
- if transparent:
- shapes = {
- "top_line": top_line,
- "top_corner": top_corner,
- "left_line": left_line,
- "bottom_corner": bottom_corner,
- "bottom_line": bottom_line,
- "right_line": right_line,
- "left_leg": left_leg,
- "left_foot": left_foot,
- "right_foot": right_foot,
- "right_leg": right_leg,
- "right_foot": right_foot,
- "eye": eye,
- "mouth": mouth
- }
- else: # Not transparent
- shapes = {
- "top_fill": top_fill,
- "top_corner_fill": top_corner_fill,
- "left_fill": left_fill,
- "right_fill": right_fill,
- "bottom_fill": bottom_fill,
- "bottom_corner_fill": bottom_corner_fill,
- "top_line": top_line,
- "top_corner": top_corner,
- "left_line": left_line,
- "bottom_corner": bottom_corner,
- "bottom_line": bottom_line,
- "right_line": right_line,
- "left_leg": left_leg,
- "left_foot": left_foot,
- "right_foot": right_foot,
- "right_leg": right_leg,
- "right_foot": right_foot,
- "eye": eye,
- "mouth": mouth
- }
- arguments = [canvas, centre_x, centre_y, size, orientation, colour, background, transparent]
- logger.debug(f"Created Karel: {shapes, arguments}")
- return [shapes, arguments]
|