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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 literal 0 HcmV?d00001