|
@@ -0,0 +1,473 @@
|
|
|
|
|
+from graphics import Canvas
|
|
|
|
|
+import random
|
|
|
|
|
+import time
|
|
|
|
|
+
|
|
|
|
|
+# ======================================================
|
|
|
|
|
+# MADE BY : YESSENIA CHOQUEHUANCA MASIAS - COUNTRY: PERÚ
|
|
|
|
|
+# PROJECT : Code Catcher - Catch the Codes
|
|
|
|
|
+# UNIVERSITY: STANFORD - Peru
|
|
|
|
|
+# COLORS : Peru Gold/Red
|
|
|
|
|
+# ======================================================
|
|
|
|
|
+
|
|
|
|
|
+CANVAS_WIDTH = 600
|
|
|
|
|
+CANVAS_HEIGHT = 400
|
|
|
|
|
+
|
|
|
|
|
+BALL_SIZE = 25
|
|
|
|
|
+GRAVITY = 0.9
|
|
|
|
|
+JUMP_STRENGTH = -22
|
|
|
|
|
+BOUNCE_FACTOR = 0.6
|
|
|
|
|
+MIN_BOUNCE_VY = 3.0
|
|
|
|
|
+
|
|
|
|
|
+CODE_W = 110
|
|
|
|
|
+CODE_H = 26
|
|
|
|
|
+CODE_COUNT = 4
|
|
|
|
|
+CODES_TO_WIN = 10
|
|
|
|
|
+
|
|
|
|
|
+COLOR_STANFORD_RED = "#800b0bff"
|
|
|
|
|
+COLOR_PERU_RED = "#BF0000"
|
|
|
|
|
+COLOR_GOLD = "#c49620ff"
|
|
|
|
|
+COLOR_BRIGHT_GOLD = "#938224ff"
|
|
|
|
|
+COLOR_DARK_NAVY = "#1a1a2e"
|
|
|
|
|
+COLOR_WHITE = "WHITE"
|
|
|
|
|
+COLOR_GRAY = "#AAAAAA"
|
|
|
|
|
+COLOR_BLACK = "black"
|
|
|
|
|
+
|
|
|
|
|
+CODE_SNIPPETS = [
|
|
|
|
|
+ "move()",
|
|
|
|
|
+ "pick_beeper()",
|
|
|
|
|
+ "turn_left()",
|
|
|
|
|
+ "turn_right()",
|
|
|
|
|
+ "put_beeper()",
|
|
|
|
|
+]
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class KarelBall:
|
|
|
|
|
+ """
|
|
|
|
|
+ Pelota que se convierte en Karel parte a parte.
|
|
|
|
|
+ Nivel 0 → pelota blanca
|
|
|
|
|
+ Nivel 1-2 → cuerpo
|
|
|
|
|
+ Nivel 3-4 → piernas
|
|
|
|
|
+ Nivel 5-6 → brazos
|
|
|
|
|
+ Nivel 7-8 → cabeza/pantalla
|
|
|
|
|
+ Nivel 9-10 → Karel completo con antena
|
|
|
|
|
+ """
|
|
|
|
|
+
|
|
|
|
|
+ def __init__(self, canvas, x, y):
|
|
|
|
|
+ self.canvas = canvas
|
|
|
|
|
+ self.x = x
|
|
|
|
|
+ self.y = y
|
|
|
|
|
+ self.vx = 0
|
|
|
|
|
+ self.vy = 0
|
|
|
|
|
+ self.is_grounded = False
|
|
|
|
|
+ self.on_platform = None
|
|
|
|
|
+ self.level = 0 # how many codes has he caught
|
|
|
|
|
+
|
|
|
|
|
+ # IDs of each drawn part
|
|
|
|
|
+ self.parts = {}
|
|
|
|
|
+ self._draw_level()
|
|
|
|
|
+
|
|
|
|
|
+ # --------------------------------------------------
|
|
|
|
|
+ # DRAWN
|
|
|
|
|
+ # --------------------------------------------------
|
|
|
|
|
+ def _clear(self):
|
|
|
|
|
+ for pid in self.parts.values():
|
|
|
|
|
+ self.canvas.delete(pid)
|
|
|
|
|
+ self.parts = {}
|
|
|
|
|
+
|
|
|
|
|
+ def _draw_level(self):
|
|
|
|
|
+ self._clear()
|
|
|
|
|
+ x, y = self.x, self.y # top-left corner of the sprite (25x25)
|
|
|
|
|
+
|
|
|
|
|
+ # --- LEVEL 0: single ball ---
|
|
|
|
|
+ self.parts['body_oval'] = self.canvas.create_oval(
|
|
|
|
|
+ x, y, x + 25, y + 25,
|
|
|
|
|
+ COLOR_WHITE, COLOR_STANFORD_RED
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ if self.level >= 2:
|
|
|
|
|
+ # Gray square (replaces the oval with a rectangle on top)
|
|
|
|
|
+ self.parts['body'] = self.canvas.create_rectangle(
|
|
|
|
|
+ x + 4, y + 8, x + 21, y + 22,
|
|
|
|
|
+ COLOR_GRAY, COLOR_BLACK
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ if self.level >= 4:
|
|
|
|
|
+ # Left leg
|
|
|
|
|
+ self.parts['leg_l'] = self.canvas.create_rectangle(
|
|
|
|
|
+ x + 5, y + 22, x + 10, y + 30,
|
|
|
|
|
+ COLOR_GRAY, COLOR_BLACK
|
|
|
|
|
+ )
|
|
|
|
|
+ # Right leg
|
|
|
|
|
+ self.parts['leg_r'] = self.canvas.create_rectangle(
|
|
|
|
|
+ x + 15, y + 22, x + 20, y + 30,
|
|
|
|
|
+ COLOR_GRAY, COLOR_BLACK
|
|
|
|
|
+ )
|
|
|
|
|
+ # Left foot
|
|
|
|
|
+ self.parts['foot_l'] = self.canvas.create_rectangle(
|
|
|
|
|
+ x + 3, y + 28, x + 11, y + 32,
|
|
|
|
|
+ COLOR_BLACK, COLOR_BLACK
|
|
|
|
|
+ )
|
|
|
|
|
+ # Ceiling height
|
|
|
|
|
+ self.parts['foot_r'] = self.canvas.create_rectangle(
|
|
|
|
|
+ x + 14, y + 28, x + 22, y + 32,
|
|
|
|
|
+ COLOR_BLACK, COLOR_BLACK
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ if self.level >= 6:
|
|
|
|
|
+ # Left arm
|
|
|
|
|
+ self.parts['arm_l'] = self.canvas.create_rectangle(
|
|
|
|
|
+ x - 5, y + 10, x + 4, y + 15,
|
|
|
|
|
+ COLOR_GRAY, COLOR_BLACK
|
|
|
|
|
+ )
|
|
|
|
|
+ # Right-hand man
|
|
|
|
|
+ self.parts['arm_r'] = self.canvas.create_rectangle(
|
|
|
|
|
+ x + 21, y + 10, x + 30, y + 15,
|
|
|
|
|
+ COLOR_GRAY, COLOR_BLACK
|
|
|
|
|
+ )
|
|
|
|
|
+ # Left hand
|
|
|
|
|
+ self.parts['hand_l'] = self.canvas.create_oval(
|
|
|
|
|
+ x - 8, y + 8, x - 2, y + 17,
|
|
|
|
|
+ COLOR_BLACK, COLOR_BLACK
|
|
|
|
|
+ )
|
|
|
|
|
+ # Right hand
|
|
|
|
|
+ self.parts['hand_r'] = self.canvas.create_oval(
|
|
|
|
|
+ x + 27, y + 8, x + 33, y + 17,
|
|
|
|
|
+ COLOR_BLACK, COLOR_BLACK
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ if self.level >= 8:
|
|
|
|
|
+ # Head
|
|
|
|
|
+ self.parts['head'] = self.canvas.create_rectangle(
|
|
|
|
|
+ x + 5, y - 10, x + 20, y + 8,
|
|
|
|
|
+ COLOR_GRAY, COLOR_BLACK
|
|
|
|
|
+ )
|
|
|
|
|
+ # Screen (eye)
|
|
|
|
|
+ self.parts['screen'] = self.canvas.create_rectangle(
|
|
|
|
|
+ x + 8, y - 8, x + 17, y + 2,
|
|
|
|
|
+ COLOR_DARK_NAVY, COLOR_BLACK
|
|
|
|
|
+ )
|
|
|
|
|
+ # Screen resolution
|
|
|
|
|
+ self.parts['pixel'] = self.canvas.create_rectangle(
|
|
|
|
|
+ x + 10, y - 5, x + 15, y - 1,
|
|
|
|
|
+ COLOR_BRIGHT_GOLD, COLOR_BRIGHT_GOLD
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ if self.level >= 10:
|
|
|
|
|
+ # AntenNa
|
|
|
|
|
+ self.parts['antenna'] = self.canvas.create_rectangle(
|
|
|
|
|
+ x + 11, y - 18, x + 14, y - 10,
|
|
|
|
|
+ COLOR_BLACK, COLOR_BLACK
|
|
|
|
|
+ )
|
|
|
|
|
+ # Antenna tip
|
|
|
|
|
+ self.parts['antenna_tip'] = self.canvas.create_oval(
|
|
|
|
|
+ x + 9, y - 22, x + 16, y - 16,
|
|
|
|
|
+ COLOR_BRIGHT_GOLD, COLOR_BLACK
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ def _move_parts(self, dx, dy):
|
|
|
|
|
+ for pid in self.parts.values():
|
|
|
|
|
+ self.canvas.move(pid, dx, dy)
|
|
|
|
|
+
|
|
|
|
|
+ # --------------------------------------------------
|
|
|
|
|
+ # DEVELOPMENT
|
|
|
|
|
+ # --------------------------------------------------
|
|
|
|
|
+ def evolve(self, codes_caught):
|
|
|
|
|
+ self.level = codes_caught
|
|
|
|
|
+ self._draw_level()
|
|
|
|
|
+
|
|
|
|
|
+ # --------------------------------------------------
|
|
|
|
|
+ # PHYSICS
|
|
|
|
|
+ # --------------------------------------------------
|
|
|
|
|
+ def jump(self):
|
|
|
|
|
+ if self.is_grounded:
|
|
|
|
|
+ self.vy = JUMP_STRENGTH
|
|
|
|
|
+ self.is_grounded = False
|
|
|
|
|
+ self.on_platform = None
|
|
|
|
|
+
|
|
|
|
|
+ def update_physics(self, platforms):
|
|
|
|
|
+ if self.is_grounded and self.on_platform is not None:
|
|
|
|
|
+ dx = self.on_platform.drift
|
|
|
|
|
+ self.x += dx
|
|
|
|
|
+ plat_top = CANVAS_HEIGHT - self.on_platform.height
|
|
|
|
|
+ self.y = plat_top - BALL_SIZE
|
|
|
|
|
+ self._move_parts(dx, 0)
|
|
|
|
|
+
|
|
|
|
|
+ self.vy += GRAVITY
|
|
|
|
|
+ old_x, old_y = self.x, self.y
|
|
|
|
|
+ self.y += self.vy
|
|
|
|
|
+ self.x += self.vx
|
|
|
|
|
+
|
|
|
|
|
+ self.is_grounded = False
|
|
|
|
|
+ self.on_platform = None
|
|
|
|
|
+
|
|
|
|
|
+ ball_bottom = self.y + BALL_SIZE
|
|
|
|
|
+ ball_center_x = self.x + BALL_SIZE / 2
|
|
|
|
|
+
|
|
|
|
|
+ for platform in platforms:
|
|
|
|
|
+ plat_top = CANVAS_HEIGHT - platform.height
|
|
|
|
|
+ if (self.vy >= 0 and plat_top - 8 <= ball_bottom <= plat_top + self.vy + 2):
|
|
|
|
|
+ if platform.x <= ball_center_x <= platform.x + platform.width:
|
|
|
|
|
+ self.y = plat_top - BALL_SIZE
|
|
|
|
|
+ if abs(self.vy) > MIN_BOUNCE_VY:
|
|
|
|
|
+ self.vy = -self.vy * BOUNCE_FACTOR
|
|
|
|
|
+ else:
|
|
|
|
|
+ self.vy = 0
|
|
|
|
|
+ self.is_grounded = True
|
|
|
|
|
+ self.on_platform = platform
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ if self.x < 0:
|
|
|
|
|
+ self.x = 0
|
|
|
|
|
+ self.vx = abs(self.vx) * BOUNCE_FACTOR
|
|
|
|
|
+ elif self.x + BALL_SIZE > CANVAS_WIDTH:
|
|
|
|
|
+ self.x = CANVAS_WIDTH - BALL_SIZE
|
|
|
|
|
+ self.vx = -abs(self.vx) * BOUNCE_FACTOR
|
|
|
|
|
+
|
|
|
|
|
+ if self.y < 0:
|
|
|
|
|
+ self.y = 0
|
|
|
|
|
+ self.vy = abs(self.vy) * BOUNCE_FACTOR
|
|
|
|
|
+
|
|
|
|
|
+ if self.y > CANVAS_HEIGHT:
|
|
|
|
|
+ p = platforms[0]
|
|
|
|
|
+ self.x = p.x + p.width / 2 - BALL_SIZE / 2
|
|
|
|
|
+ self.y = CANVAS_HEIGHT - p.height - BALL_SIZE
|
|
|
|
|
+ self.vy = 0
|
|
|
|
|
+ self.vx = 0
|
|
|
|
|
+ self.is_grounded = True
|
|
|
|
|
+ self.on_platform = p
|
|
|
|
|
+
|
|
|
|
|
+ dx = self.x - old_x
|
|
|
|
|
+ dy = self.y - old_y
|
|
|
|
|
+ self._move_parts(dx, dy)
|
|
|
|
|
+
|
|
|
|
|
+ def get_center(self):
|
|
|
|
|
+ return self.x + BALL_SIZE / 2, self.y + BALL_SIZE / 2
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class GamePlatform:
|
|
|
|
|
+ COLORS = [
|
|
|
|
|
+ COLOR_STANFORD_RED,
|
|
|
|
|
+ COLOR_PERU_RED,
|
|
|
|
|
+ COLOR_GOLD,
|
|
|
|
|
+ COLOR_DARK_NAVY,
|
|
|
|
|
+ "#C0392B"
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ def __init__(self, canvas, x, width, height):
|
|
|
|
|
+ self.canvas = canvas
|
|
|
|
|
+ self.x = x
|
|
|
|
|
+ self.width = width
|
|
|
|
|
+ self.height = height
|
|
|
|
|
+ self.color = random.choice(self.COLORS)
|
|
|
|
|
+ self.drift = random.choice([-1, 1])
|
|
|
|
|
+ self.rect_id = None
|
|
|
|
|
+ self.draw()
|
|
|
|
|
+
|
|
|
|
|
+ def draw(self):
|
|
|
|
|
+ top_y = CANVAS_HEIGHT - self.height
|
|
|
|
|
+ self.rect_id = self.canvas.create_rectangle(
|
|
|
|
|
+ self.x, top_y,
|
|
|
|
|
+ self.x + self.width, CANVAS_HEIGHT,
|
|
|
|
|
+ self.color, COLOR_BRIGHT_GOLD
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ def update(self):
|
|
|
|
|
+ self.x += self.drift
|
|
|
|
|
+ self.canvas.move(self.rect_id, self.drift, 0)
|
|
|
|
|
+ if self.x <= 0:
|
|
|
|
|
+ self.drift = abs(self.drift)
|
|
|
|
|
+ elif self.x + self.width >= CANVAS_WIDTH:
|
|
|
|
|
+ self.drift = -abs(self.drift)
|
|
|
|
|
+
|
|
|
|
|
+ def move(self, dx):
|
|
|
|
|
+ self.x += dx
|
|
|
|
|
+ self.canvas.move(self.rect_id, dx, 0)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+class CodeToken:
|
|
|
|
|
+ SLOTS = [
|
|
|
|
|
+ (100, 80), (280, 80), (440, 80),
|
|
|
|
|
+ (100, 160), (280, 160), (440, 160),
|
|
|
|
|
+ (180, 220), (340, 220),
|
|
|
|
|
+ ]
|
|
|
|
|
+ used_slots = []
|
|
|
|
|
+ @classmethod
|
|
|
|
|
+ def reset_slots(cls):
|
|
|
|
|
+ cls.used_slots = []
|
|
|
|
|
+
|
|
|
|
|
+ @classmethod
|
|
|
|
|
+ def get_free_slot(cls):
|
|
|
|
|
+ free = [s for s in cls.SLOTS if s not in cls.used_slots]
|
|
|
|
|
+ if not free:
|
|
|
|
|
+ cls.used_slots = []
|
|
|
|
|
+ free = cls.SLOTS[:]
|
|
|
|
|
+ slot = random.choice(free)
|
|
|
|
|
+ cls.used_slots.append(slot)
|
|
|
|
|
+ return slot
|
|
|
|
|
+
|
|
|
|
|
+ def __init__(self, canvas):
|
|
|
|
|
+ self.canvas = canvas
|
|
|
|
|
+ self.active = True
|
|
|
|
|
+ self.snippet = random.choice(CODE_SNIPPETS)
|
|
|
|
|
+ self.rect_id = None
|
|
|
|
|
+ self.text_id = None
|
|
|
|
|
+ self.slot = None
|
|
|
|
|
+ self._place()
|
|
|
|
|
+
|
|
|
|
|
+ def _place(self):
|
|
|
|
|
+ self.slot = CodeToken.get_free_slot()
|
|
|
|
|
+ self.x, self.y = self.slot
|
|
|
|
|
+ self.rect_id = self.canvas.create_rectangle(
|
|
|
|
|
+ self.x, self.y,
|
|
|
|
|
+ self.x + CODE_W, self.y + CODE_H,
|
|
|
|
|
+ COLOR_GOLD, COLOR_BRIGHT_GOLD
|
|
|
|
|
+ )
|
|
|
|
|
+ self.text_id = self.canvas.create_text(
|
|
|
|
|
+ self.x + CODE_W / 2,
|
|
|
|
|
+ self.y + CODE_H / 2,
|
|
|
|
|
+ text=self.snippet,
|
|
|
|
|
+ font="Courier", font_size=24, color=COLOR_STANFORD_RED
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ def check_catch(self, ball_cx, ball_cy):
|
|
|
|
|
+ if not self.active:
|
|
|
|
|
+ return False
|
|
|
|
|
+ cx = self.x + CODE_W / 2
|
|
|
|
|
+ cy = self.y + CODE_H / 2
|
|
|
|
|
+ if (abs(ball_cx - cx) < CODE_W / 2 + BALL_SIZE / 2 and
|
|
|
|
|
+ abs(ball_cy - cy) < CODE_H / 2 + BALL_SIZE / 2):
|
|
|
|
|
+ self.canvas.delete(self.rect_id)
|
|
|
|
|
+ self.canvas.delete(self.text_id)
|
|
|
|
|
+ if self.slot in CodeToken.used_slots:
|
|
|
|
|
+ CodeToken.used_slots.remove(self.slot)
|
|
|
|
|
+ self.active = False
|
|
|
|
|
+ return True
|
|
|
|
|
+ return False
|
|
|
|
|
+
|
|
|
|
|
+ def respawn(self):
|
|
|
|
|
+ self.snippet = random.choice(CODE_SNIPPETS)
|
|
|
|
|
+ self._place()
|
|
|
|
|
+ self.active = True
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def show_win_screen(canvas):
|
|
|
|
|
+ canvas.create_rectangle(
|
|
|
|
|
+ 0, 0, CANVAS_WIDTH, CANVAS_HEIGHT,
|
|
|
|
|
+ COLOR_DARK_NAVY, COLOR_DARK_NAVY
|
|
|
|
|
+ )
|
|
|
|
|
+ canvas.create_rectangle(
|
|
|
|
|
+ 50, 100, 550, 310,
|
|
|
|
|
+ COLOR_STANFORD_RED, COLOR_BRIGHT_GOLD
|
|
|
|
|
+ )
|
|
|
|
|
+ canvas.create_text(150, 135,
|
|
|
|
|
+ text="YOU WIN!",
|
|
|
|
|
+ font="Arial", font_size=38, color=COLOR_BRIGHT_GOLD)
|
|
|
|
|
+ canvas.create_text(150, 175,
|
|
|
|
|
+ text="Karel is fully built!",
|
|
|
|
|
+ font="Arial", font_size=16, color=COLOR_WHITE)
|
|
|
|
|
+ canvas.create_text(150, 205,
|
|
|
|
|
+ text="You collected all 10 Karel codes!",
|
|
|
|
|
+ font="Arial", font_size=13, color=COLOR_WHITE)
|
|
|
|
|
+ canvas.create_text(150, 240,
|
|
|
|
|
+ text="Final Score: 100 points",
|
|
|
|
|
+ font="Arial", font_size=15, color=COLOR_BRIGHT_GOLD)
|
|
|
|
|
+ canvas.create_text(150, 268,
|
|
|
|
|
+ text="Keep coding! PERU @ STANFORD - CODE IN PLACE",
|
|
|
|
|
+ font="Arial", font_size=11, color=COLOR_BRIGHT_GOLD)
|
|
|
|
|
+ canvas.create_text(150, 290,
|
|
|
|
|
+ text="YESSENIA CH. M.",
|
|
|
|
|
+ font="Arial", font_size=10, color=COLOR_WHITE)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+def main():
|
|
|
|
|
+ canvas = Canvas(CANVAS_WIDTH, CANVAS_HEIGHT)
|
|
|
|
|
+
|
|
|
|
|
+ CodeToken.reset_slots()
|
|
|
|
|
+
|
|
|
|
|
+ # Franja superior
|
|
|
|
|
+ canvas.create_rectangle(
|
|
|
|
|
+ 0, 0, CANVAS_WIDTH, 50,
|
|
|
|
|
+ COLOR_STANFORD_RED, COLOR_STANFORD_RED
|
|
|
|
|
+ )
|
|
|
|
|
+
|
|
|
|
|
+ canvas.create_text(10, 15,
|
|
|
|
|
+ text="CONTROLS: [ N ] Forward [ M ] Back [ B ] JUMP",
|
|
|
|
|
+ font="Arial", font_size=11, color=COLOR_BRIGHT_GOLD)
|
|
|
|
|
+ canvas.create_text(10, 35,
|
|
|
|
|
+ text="CODE CATCHER — Collect 10 Karel codes to build Karel!",
|
|
|
|
|
+ font="Arial", font_size=11, color=COLOR_WHITE)
|
|
|
|
|
+
|
|
|
|
|
+ score_label = canvas.create_text(480, 25,
|
|
|
|
|
+ text="Codes: 0 / 10",
|
|
|
|
|
+ font="Arial", font_size=13, color=COLOR_BRIGHT_GOLD)
|
|
|
|
|
+
|
|
|
|
|
+ canvas.create_text(300, CANVAS_HEIGHT - 8,
|
|
|
|
|
+ text="YESSENIA CH.M. | PERU @ STANFORD UNIVERSITY",
|
|
|
|
|
+ font="Arial", font_size=11, color=COLOR_STANFORD_RED)
|
|
|
|
|
+
|
|
|
|
|
+ platforms = [
|
|
|
|
|
+ GamePlatform(canvas, 30, 140, 80),
|
|
|
|
|
+ GamePlatform(canvas, 230, 130, 130),
|
|
|
|
|
+ GamePlatform(canvas, 420, 120, 100),
|
|
|
|
|
+ ]
|
|
|
|
|
+
|
|
|
|
|
+ codes = [CodeToken(canvas) for _ in range(CODE_COUNT)]
|
|
|
|
|
+
|
|
|
|
|
+ p0 = platforms[0]
|
|
|
|
|
+ ball = KarelBall(canvas, p0.x + 50, CANVAS_HEIGHT - p0.height - BALL_SIZE)
|
|
|
|
|
+ ball.is_grounded = True
|
|
|
|
|
+ ball.on_platform = p0
|
|
|
|
|
+
|
|
|
|
|
+ speed = 12
|
|
|
|
|
+ codes_caught = 0
|
|
|
|
|
+ game_over = False
|
|
|
|
|
+
|
|
|
|
|
+ while True:
|
|
|
|
|
+ if game_over:
|
|
|
|
|
+ time.sleep(0.1)
|
|
|
|
|
+ continue
|
|
|
|
|
+
|
|
|
|
|
+ key = canvas.get_last_key_press()
|
|
|
|
|
+
|
|
|
|
|
+ if key:
|
|
|
|
|
+ key = key.lower()
|
|
|
|
|
+ move_speed = speed * 2 if not ball.is_grounded else speed
|
|
|
|
|
+ if key == 'n':
|
|
|
|
|
+ for platform in platforms:
|
|
|
|
|
+ platform.move(-move_speed)
|
|
|
|
|
+ elif key == 'm':
|
|
|
|
|
+ for platform in platforms:
|
|
|
|
|
+ platform.move(move_speed)
|
|
|
|
|
+ elif key == 'b':
|
|
|
|
|
+ ball.jump()
|
|
|
|
|
+
|
|
|
|
|
+ for platform in platforms:
|
|
|
|
|
+ platform.update()
|
|
|
|
|
+
|
|
|
|
|
+ ball.update_physics(platforms)
|
|
|
|
|
+
|
|
|
|
|
+ bx, by = ball.get_center()
|
|
|
|
|
+ for code in codes:
|
|
|
|
|
+ if code.check_catch(bx, by):
|
|
|
|
|
+ codes_caught += 1
|
|
|
|
|
+
|
|
|
|
|
+ # Karel evolves with every code he captures
|
|
|
|
|
+ ball.evolve(codes_caught)
|
|
|
|
|
+
|
|
|
|
|
+ canvas.delete(score_label)
|
|
|
|
|
+ score_label = canvas.create_text(480, 25,
|
|
|
|
|
+ text=f"Codes: {codes_caught} / {CODES_TO_WIN}",
|
|
|
|
|
+ font="Arial", font_size=13, color=COLOR_BRIGHT_GOLD)
|
|
|
|
|
+
|
|
|
|
|
+ if codes_caught >= CODES_TO_WIN:
|
|
|
|
|
+ show_win_screen(canvas)
|
|
|
|
|
+ game_over = True
|
|
|
|
|
+ break
|
|
|
|
|
+
|
|
|
|
|
+ code.respawn()
|
|
|
|
|
+
|
|
|
|
|
+ time.sleep(0.04)
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+if __name__ == '__main__':
|
|
|
|
|
+ main()
|