From 0b85528a6cb03bcfc62d3bb938dfb88bca082914 Mon Sep 17 00:00:00 2001 From: Pavel Machek Date: Fri, 6 Mar 2026 23:30:36 +0100 Subject: [PATCH] floodit: simple game Objective is to turn the board into uniform color in minimum number of steps. --- .../META-INF/MANIFEST.JSON | 24 ++ .../apps/cz.ucw.pavel.floodit/assets/main.py | 210 ++++++++++++++++++ .../res/mipmap-mdpi/icon_64x64.png | Bin 0 -> 10235 bytes 3 files changed, 234 insertions(+) create mode 100644 internal_filesystem/apps/cz.ucw.pavel.floodit/META-INF/MANIFEST.JSON create mode 100644 internal_filesystem/apps/cz.ucw.pavel.floodit/assets/main.py create mode 100644 internal_filesystem/apps/cz.ucw.pavel.floodit/res/mipmap-mdpi/icon_64x64.png diff --git a/internal_filesystem/apps/cz.ucw.pavel.floodit/META-INF/MANIFEST.JSON b/internal_filesystem/apps/cz.ucw.pavel.floodit/META-INF/MANIFEST.JSON new file mode 100644 index 00000000..55f1cdee --- /dev/null +++ b/internal_filesystem/apps/cz.ucw.pavel.floodit/META-INF/MANIFEST.JSON @@ -0,0 +1,24 @@ +{ +"name": "Floodit", +"publisher": "Pavel Machek", +"short_description": "Simple game with colors.", +"long_description": "Game with colors, where objective is to turn whole board into single color in minimum number of steps.", +"icon_url": "https://apps.micropythonos.com/apps/cz.ucw.pavel.floodit/icons/cz.ucw.pavel.floodit_0.0.1_64x64.png", +"download_url": "https://apps.micropythonos.com/apps/cz.ucw.pavel.floodit/mpks/cz.ucw.pavel.floodit_0.0.1.mpk", +"fullname": "cz.ucw.pavel.floodit", +"version": "0.0.1", +"category": "utilities", +"activities": [ + { + "entrypoint": "assets/main.py", + "classname": "Main", + "intent_filters": [ + { + "action": "main", + "category": "launcher" + } + ] + } + ] +} + diff --git a/internal_filesystem/apps/cz.ucw.pavel.floodit/assets/main.py b/internal_filesystem/apps/cz.ucw.pavel.floodit/assets/main.py new file mode 100644 index 00000000..cf0c77ca --- /dev/null +++ b/internal_filesystem/apps/cz.ucw.pavel.floodit/assets/main.py @@ -0,0 +1,210 @@ +import time +import random + +""" +Flood-It game + +Fill the entire board with a single color +using the smallest number of moves. + +Touch a color button to flood the region +starting from the top-left corner. +""" + +from mpos import Activity + +try: + import lvgl as lv +except ImportError: + pass + + +class Main(Activity): + + COLS = 10 + ROWS = 10 + + COLORS = [ + 0xE74C3C, # red + 0xF1C40F, # yellow + 0x2ECC71, # green + 0x3498DB, # blue + 0x9B59B6, # purple + 0xE67E22, # orange + ] + + def __init__(self): + super().__init__() + + self.board = [] + self.cells = [] + + self.moves = 0 + + # --------------------------------------------------------------------- + + def onCreate(self): + + self.screen = lv.obj() + self.screen.remove_flag(lv.obj.FLAG.SCROLLABLE) + + font = lv.font_montserrat_20 + + score = lv.label(self.screen) + score.align(lv.ALIGN.TOP_LEFT, 5, 25) + score.set_text("Moves") + score.set_style_text_font(font, 0) + self.lb_score = score + + d = lv.display_get_default() + self.SCREEN_WIDTH = d.get_horizontal_resolution() + self.SCREEN_HEIGHT = d.get_vertical_resolution() + + # color buttons + btn_size = 45 + spacing = 5 + + self.CELL = min( + self.SCREEN_WIDTH // (self.COLS + 2), + (self.SCREEN_HEIGHT - btn_size) // (self.ROWS + 3) + ) + + board_x = (self.SCREEN_WIDTH - self.CELL * self.COLS) // 2 + board_y = (self.SCREEN_HEIGHT - self.CELL * self.ROWS) // 2 + + for r in range(self.ROWS): + row = [] + for c in range(self.COLS): + + o = lv.obj(self.screen) + o.set_size(self.CELL - 2, self.CELL - 2) + + o.set_pos( + board_x + c * self.CELL + 1, + board_y + r * self.CELL + 1 - btn_size // 2 + ) + + o.set_style_radius(4, 0) + o.set_style_border_width(1, 0) + + row.append(o) + + self.cells.append(row) + + + for i, col in enumerate(self.COLORS): + + btn = lv.button(self.screen) + btn.set_size(btn_size, btn_size) + + btn.align( + lv.ALIGN.BOTTOM_LEFT, + 5 + i * (btn_size + spacing), + -5 + ) + + btn.set_style_bg_color(lv.color_hex(col), 0) + + btn.add_event_cb( + lambda e, c=i: self.pick_color(c), + lv.EVENT.CLICKED, + None + ) + + focusgroup = lv.group_get_default() + if focusgroup: + focusgroup.add_obj(self.screen) + + self.setContentView(self.screen) + + self.new_game() + + # --------------------------------------------------------------------- + + def new_game(self): + + self.moves = 0 + self.lb_score.set_text("Moves\n0") + + self.board = [ + [random.randrange(len(self.COLORS)) for _ in range(self.COLS)] + for _ in range(self.ROWS) + ] + + self.redraw() + + # --------------------------------------------------------------------- + + def pick_color(self, color): + + start_color = self.board[0][0] + + if start_color == color: + return + + self.flood_fill(start_color, color) + + self.moves += 1 + self.lb_score.set_text("Moves\n%d" % self.moves) + + self.redraw() + + if self.check_win(): + self.win() + + # --------------------------------------------------------------------- + + def flood_fill(self, old, new): + + stack = [(0, 0)] + + while stack: + + r, c = stack.pop() + + if not (0 <= r < self.ROWS and 0 <= c < self.COLS): + continue + + if self.board[r][c] != old: + continue + + self.board[r][c] = new + + stack.append((r + 1, c)) + stack.append((r - 1, c)) + stack.append((r, c + 1)) + stack.append((r, c - 1)) + + # --------------------------------------------------------------------- + + def check_win(self): + + color = self.board[0][0] + + for r in range(self.ROWS): + for c in range(self.COLS): + if self.board[r][c] != color: + return False + + return True + + # --------------------------------------------------------------------- + + def win(self): + + label = lv.label(self.screen) + label.set_text("Finished in %d moves!" % self.moves) + label.center() + + # --------------------------------------------------------------------- + + def redraw(self): + + for r in range(self.ROWS): + for c in range(self.COLS): + + v = self.board[r][c] + + self.cells[r][c].set_style_bg_color( + lv.color_hex(self.COLORS[v]), 0 + ) diff --git a/internal_filesystem/apps/cz.ucw.pavel.floodit/res/mipmap-mdpi/icon_64x64.png b/internal_filesystem/apps/cz.ucw.pavel.floodit/res/mipmap-mdpi/icon_64x64.png new file mode 100644 index 0000000000000000000000000000000000000000..f0ca6f7574a9e9bcd7974ac8572cdb417550199c GIT binary patch literal 10235 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 eh8fT_0n{(0xngG