diff --git a/CircuitPython_Karel_The_Robot/code.py b/CircuitPython_Karel_The_Robot/code.py index d42bf2b5f..a28f11d6a 100644 --- a/CircuitPython_Karel_The_Robot/code.py +++ b/CircuitPython_Karel_The_Robot/code.py @@ -1,5 +1,36 @@ +# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" +Karel the Robot code.py for devices with built-in display available +at board.DISPLAY. +""" +# pylint: disable=wildcard-import, unused-wildcard-import from karel.circuitpythonkarel import * + +# load a chapter. Edit the chapter filename to change chapters +# see available chapter files in chapters/ directory. +chapter_data = load_state_file("chapters/karel_ch01.json") + + def main(): + """ + Karel main() function declaration. + Put your code for Karel into this function. + """ + ## START OF MAIN FUNCTION, YOUR CODE GOES BELOW HERE ## + + + + ## END OF MAIN FUNCTION, YOUR CODE GOES ABOVE HERE ## + print(f"Goal state reached? {world.check_goal_state(chapter_data)}") + + +# call the main() function +main() - ch_obj = load_state_file("chapters/karel_ch04.json") +# Run forever so that the ending state of Karel and the world +# remains visible on the display. +while True: + pass diff --git a/CircuitPython_Karel_The_Robot/karel/circuitpythonkarel.py b/CircuitPython_Karel_The_Robot/karel/circuitpythonkarel.py index a9830208d..a91d8c3f4 100644 --- a/CircuitPython_Karel_The_Robot/karel/circuitpythonkarel.py +++ b/CircuitPython_Karel_The_Robot/karel/circuitpythonkarel.py @@ -1,3 +1,10 @@ +# SPDX-FileCopyrightText: 2024 Tim Cocks for Adafruit Industries +# +# SPDX-License-Identifier: MIT +""" +Karel the Robot helper class +""" + import json import time @@ -34,7 +41,7 @@ "dark_green", "turquoise", "dark_blue", - "dark_red" + "dark_red", ] COLOR_VALUES = [ 0xFFFFFF, @@ -52,7 +59,7 @@ 0x008700, 0x00C0C0, 0x0000AA, - 0x800000 + 0x800000, ] @@ -69,10 +76,15 @@ class NoBeepersInBag(Exception): class Karel: - def __init__(self, spritesheet_bmp, spritesheet_palette, world_width=20, world_height=15): - - self.tilegrid = TileGrid(spritesheet_bmp, pixel_shader=spritesheet_palette, - default_tile=0, tile_width=TILE_SIZE, tile_height=TILE_SIZE) + def __init__(self, spritesheet_bmp, spritesheet_palette): + + self.tilegrid = TileGrid( + spritesheet_bmp, + pixel_shader=spritesheet_palette, + default_tile=0, + tile_width=TILE_SIZE, + tile_height=TILE_SIZE, + ) self._direction = EAST self.beeper_count = 0 @@ -111,7 +123,6 @@ def y(self, new_value): class World: def __init__(self, display, world_width=10, world_height=10, beeper_limit=False): - print("world init()") self.world_width = world_width self.world_height = world_height color_count = len(COLOR_NAMES) @@ -119,7 +130,9 @@ def __init__(self, display, world_width=10, world_height=10, beeper_limit=False) self.background_palette = Palette(color_count) for i, color_val in enumerate(COLOR_VALUES): self.background_palette[i] = color_val - self.background_tilegrid = TileGrid(bitmap=self.background_bmp, pixel_shader=self.background_palette) + self.background_tilegrid = TileGrid( + bitmap=self.background_bmp, pixel_shader=self.background_palette + ) self.background_group = Group(scale=TILE_SIZE) self.background_group.append(self.background_tilegrid) self.display = display @@ -128,12 +141,20 @@ def __init__(self, display, world_width=10, world_height=10, beeper_limit=False) self.world_group.append(self.background_group) lib_dir = "/".join(__file__.split("/")[0:3]) - # print(lib_dir) - self.spritesheet_bmp, self.spritesheet_palette = adafruit_imageload.load(f"{lib_dir}/spritesheet_24px.png") + self.spritesheet_bmp, self.spritesheet_palette = adafruit_imageload.load( + f"{lib_dir}/spritesheet.png" + ) self.spritesheet_palette.make_transparent(0) - self.world_tilegrid = TileGrid(self.spritesheet_bmp, pixel_shader=self.spritesheet_palette, - tile_width=TILE_SIZE, tile_height=TILE_SIZE, width=20, height=15, default_tile=7) + self.world_tilegrid = TileGrid( + self.spritesheet_bmp, + pixel_shader=self.spritesheet_palette, + tile_width=TILE_SIZE, + tile_height=TILE_SIZE, + width=20, + height=15, + default_tile=7, + ) self.beeper_limit = beeper_limit @@ -154,24 +175,24 @@ def _init_beeper_counts(self): self.beeper_count_labels = {} self.beeper_counts = [] - for y in range(self.background_bmp.width): - self.beeper_counts.append([0 for x in range(self.background_bmp.width)]) + for _ in range(self.world_height): + self.beeper_counts.append([0 for x in range(self.world_width)]) def load_state(self, state_obj): self._init_beeper_counts() if "beeper_counts" in state_obj["input"]: for beeper_count_loc_str in state_obj["input"]["beeper_counts"].keys(): beeper_count_loc = [int(_) for _ in beeper_count_loc_str.split(",")] - print(beeper_count_loc) - self.beeper_counts[world.world_height - 1 - beeper_count_loc[1]][beeper_count_loc[0]] \ - = state_obj["input"]["beeper_counts"][beeper_count_loc_str] - for row in self.beeper_counts: - print(row) + self.beeper_counts[world.world_height - 1 - beeper_count_loc[1]][ + beeper_count_loc[0] + ] = state_obj["input"]["beeper_counts"][beeper_count_loc_str] update_beeper_count_labels() self.karel.x = state_obj["input"]["karel"]["x"] self.karel.y = self.world_height - 1 - state_obj["input"]["karel"]["y"] - self.karel.direction = DIRECTION_WORDS.index(state_obj["input"]["karel"]["direction"]) + self.karel.direction = DIRECTION_WORDS.index( + state_obj["input"]["karel"]["direction"] + ) for y, row in enumerate(state_obj["input"]["world"]): for x, cell in enumerate(row): @@ -185,15 +206,21 @@ def check_goal_state(self, state_obj): if self.karel.x != state_obj["goal"]["karel"]["x"]: print("karel x incorrect") return False - if self.karel.direction != DIRECTION_WORDS.index(state_obj["goal"]["karel"]["direction"]): + if self.karel.direction != DIRECTION_WORDS.index( + state_obj["goal"]["karel"]["direction"] + ): print("karel dir incorrect") return False if "beeper_counts" in state_obj["goal"]: for beeper_count_loc_str in state_obj["goal"]["beeper_counts"].keys(): beeper_count_loc = [int(_) for _ in beeper_count_loc_str.split(",")] - if self.beeper_counts[world.world_height - 1 - beeper_count_loc[1]][beeper_count_loc[0]] != \ - state_obj["goal"]["beeper_counts"][beeper_count_loc_str]: + if ( + self.beeper_counts[world.world_height - 1 - beeper_count_loc[1]][ + beeper_count_loc[0] + ] + != state_obj["goal"]["beeper_counts"][beeper_count_loc_str] + ): print(f"beeper count incorrect {beeper_count_loc}") return False @@ -202,25 +229,11 @@ def check_goal_state(self, state_obj): goal_cell_index = state_obj["goal"]["world"][y][x] if self.world_tilegrid[x, y] != goal_cell_index: - print(f"world mismatch: {(x, y)}: {self.world_tilegrid[x, y]} != {goal_cell_index}") + print( + f"world mismatch: {(x, world.world_height - 1 - y)}: " + + f"{self.world_tilegrid[x, y]} != {goal_cell_index}" + ) return False - - # print(f"({x}, {y}) goal: {goal_cell_index} actual: {self.world_tilegrid[x, y]}") - # if goal_cell_index == 0: - # if self.karel.x != x or self.karel.y != y or self.karel.direction != EAST: - # return False - # elif goal_cell_index == 1: - # if self.karel.x != x or self.karel.y != y or self.karel.direction != NORTH: - # return False - # elif goal_cell_index == 2: - # if self.karel.x != x or self.karel.y != y or self.karel.direction != WEST: - # return False - # elif goal_cell_index == 3: - # if self.karel.x != x or self.karel.y != y or self.karel.direction != SOUTH: - # return False - # else: - # if self.world_tilegrid[x, y] != goal_cell_index: - # return False return True @@ -228,8 +241,6 @@ def check_goal_state(self, state_obj): def move(): - print(f"Moving: {world.karel.direction}") - if front_is_blocked(): raise FrontIsBlocked("Karel can't move there") @@ -313,8 +324,9 @@ def left_is_blocked(): def paint_corner(color): if color not in COLOR_NAMES: - raise ValueError(f"Color {color} is not valid. Supported colors are {COLOR_NAMES}") - print(f"name: {color} index: {COLOR_NAMES.index(color)}") + raise ValueError( + f"Color {color} is not valid. Supported colors are {COLOR_NAMES}" + ) world.background_bmp[world.karel.x, world.karel.y] = COLOR_NAMES.index(color) @@ -330,12 +342,16 @@ def update_beeper_count_labels(): if (x, y) in world.beeper_count_labels: world.beeper_count_labels[(x, y)].text = str(count) else: - world.beeper_count_labels[(x, y)] = Label(terminalio.FONT, - text=str(count), - color=0x000000, - anchor_point=(0.5, 0.5), - anchored_position=(x * TILE_SIZE + TILE_SIZE // 2, - y * TILE_SIZE + TILE_SIZE // 2)) + world.beeper_count_labels[(x, y)] = Label( + terminalio.FONT, + text=str(count), + color=0x000000, + anchor_point=(0.5, 0.5), + anchored_position=( + x * TILE_SIZE + TILE_SIZE // 2, + y * TILE_SIZE + TILE_SIZE // 2, + ), + ) world.world_group.append(world.beeper_count_labels[(x, y)]) @@ -345,8 +361,9 @@ def pick_beeper(): world.karel.beeper_count += 1 - world.beeper_counts[world.karel.y][world.karel.x] = max(0, - world.beeper_counts[world.karel.y][world.karel.x] - 1) + world.beeper_counts[world.karel.y][world.karel.x] = max( + 0, world.beeper_counts[world.karel.y][world.karel.x] - 1 + ) update_beeper_count_labels() if world.beeper_counts[world.karel.y][world.karel.x] == 0: world.world_tilegrid[world.karel.x, world.karel.y] = 5 @@ -426,10 +443,11 @@ def right_is_clear(): def front_is_clear(): return not front_is_blocked() + def load_state_file(state_filepath): with open(state_filepath, "r") as f: ch_obj = json.load(f) world.load_state(ch_obj) time.sleep(DELAY) - return ch_obj \ No newline at end of file + return ch_obj diff --git a/CircuitPython_Karel_The_Robot/karel/spritesheet.png b/CircuitPython_Karel_The_Robot/karel/spritesheet.png index 9a40f6144..f8bac2068 100644 Binary files a/CircuitPython_Karel_The_Robot/karel/spritesheet.png and b/CircuitPython_Karel_The_Robot/karel/spritesheet.png differ