|
|
@@ -0,0 +1,511 @@
|
|
|
+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
|
|
|
+"""
|
|
|
+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, dy):
|
|
|
+ canvas = karel[1][0]
|
|
|
+
|
|
|
+ erase_karel(karel)
|
|
|
+
|
|
|
+ # Update coordinates in list
|
|
|
+ karel[1][1] += dx
|
|
|
+ karel[1][2] += dy
|
|
|
+
|
|
|
+ new_karel = draw_karel(karel[1][0], )
|
|
|
+ logger.debug(f"Moved: {karel} by {dx} horizontally, {dy} vertically, 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
|
|
|
+ ):
|
|
|
+
|
|
|
+ # Random colours
|
|
|
+ if colour == "random":
|
|
|
+ colour = generate_random_colour()
|
|
|
+ if background == "random":
|
|
|
+ background = generate_random_colour()
|
|
|
+
|
|
|
+ # Body constants
|
|
|
+ MARGIN = size / 8
|
|
|
+ APPENDAGE_MULTIPLIER = MARGIN * 0.6
|
|
|
+
|
|
|
+ ''' 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]
|