Skip to content

feat(ism330dl): Add maze game example with OLED and accelerometer.#390

Merged
nedseb merged 2 commits intomainfrom
feat/ism330dl-maze-game
Apr 16, 2026
Merged

feat(ism330dl): Add maze game example with OLED and accelerometer.#390
nedseb merged 2 commits intomainfrom
feat/ism330dl-maze-game

Conversation

@MatteoCnda1
Copy link
Copy Markdown
Contributor

Summary

Add a maze game example using the ISM330DL accelerometer and SSD1327 OLED display. Closes #335

Changes

  • Added lib/ism330dl/examples/maze_game.py: a single-file maze game where the player navigates a randomly generated maze by tilting the STeaMi board
  • Maze generation uses the Recursive Backtracker (DFS) algorithm
  • Player movement is driven by the ISM330DL accelerometer tilt detection
  • Score is computed at the end using Dijkstra's algorithm to compare the player's path to the optimal path
  • Display uses the steami_screen widget library on the round 128x128 OLED

Checklist

  • ruff check passes
  • python -m pytest tests/ -k mock -v passes (no mock test broken)
  • Tested on hardware (STM32WB55 / STeaMi board)
  • README updated (if adding/changing public API)
  • Examples added/updated (lib/ism330dl/examples/maze_game.py)
  • Commit messages follow <scope>: <Description.> format

@nedseb nedseb requested a review from DumontALINE April 14, 2026 07:46
@nedseb nedseb requested review from Copilot and removed request for Charly-sketch and nedseb April 16, 2026 19:00
@nedseb nedseb added the enhancement New feature or request label Apr 16, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new MicroPython example game to the ISM330DL library, demonstrating interactive use of the accelerometer for tilt input and the SSD1327-based round OLED via steami_screen.

Changes:

  • Added maze_game.py example implementing maze generation (DFS) and scoring via shortest-path comparison (Dijkstra)
  • Integrated SSD1327 round OLED rendering using steami_screen widgets and drawing primitives
  • Implemented tilt-based movement using ISM330DL.acceleration_g()

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +254 to +282
maze = generate_maze(MAZE_W, MAZE_H)
optimal = dijkstra(maze, START_ROW, START_COL, GOAL_ROW, GOAL_COL)

player_row = START_ROW
player_col = START_COL
steps = 0

show_start_screen()

while True:
render(maze, player_row, player_col, steps, optimal)

direction = read_direction()
if direction != NONE:
dr, dc = direction
nr = player_row + dr
nc = player_col + dc
if 0 <= nr < MAZE_H and 0 <= nc < MAZE_W and maze[nr][nc] == PATH:
player_row = nr
player_col = nc
steps += 1

# Check win condition
if player_row == GOAL_ROW and player_col == GOAL_COL:
show_win_screen(steps, optimal)
run_game()
return

sleep_ms(MOVE_DELAY_MS)
Copy link

Copilot AI Apr 16, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

run_game() restarts the game by calling itself recursively when the player wins. After multiple wins this will grow the call stack and can eventually raise a recursion/stack overflow or exhaust memory on-device. Refactor to use an outer loop (e.g., while True: ... generate maze ... play until win ... show_win_screen ... continue) instead of recursive self-calls.

Suggested change
maze = generate_maze(MAZE_W, MAZE_H)
optimal = dijkstra(maze, START_ROW, START_COL, GOAL_ROW, GOAL_COL)
player_row = START_ROW
player_col = START_COL
steps = 0
show_start_screen()
while True:
render(maze, player_row, player_col, steps, optimal)
direction = read_direction()
if direction != NONE:
dr, dc = direction
nr = player_row + dr
nc = player_col + dc
if 0 <= nr < MAZE_H and 0 <= nc < MAZE_W and maze[nr][nc] == PATH:
player_row = nr
player_col = nc
steps += 1
# Check win condition
if player_row == GOAL_ROW and player_col == GOAL_COL:
show_win_screen(steps, optimal)
run_game()
return
sleep_ms(MOVE_DELAY_MS)
while True:
maze = generate_maze(MAZE_W, MAZE_H)
optimal = dijkstra(maze, START_ROW, START_COL, GOAL_ROW, GOAL_COL)
player_row = START_ROW
player_col = START_COL
steps = 0
show_start_screen()
while True:
render(maze, player_row, player_col, steps, optimal)
direction = read_direction()
if direction != NONE:
dr, dc = direction
nr = player_row + dr
nc = player_col + dc
if 0 <= nr < MAZE_H and 0 <= nc < MAZE_W and maze[nr][nc] == PATH:
player_row = nr
player_col = nc
steps += 1
# Check win condition
if player_row == GOAL_ROW and player_col == GOAL_COL:
show_win_screen(steps, optimal)
break
sleep_ms(MOVE_DELAY_MS)

Copilot uses AI. Check for mistakes.
nedseb added a commit that referenced this pull request Apr 16, 2026
Address review comments on #390:

1. Replace recursive run_game() self-call with an outer `while True` loop.
   On MicroPython's limited stack (8-16 KB), each completed game added a
   stack frame that was never freed — a few wins would crash with a
   RecursionError or silent stack overflow.

2. Add try/except/finally around the game loop: Ctrl+C now cleanly
   clears the display and powers off the IMU (same pattern as
   spirit_level.py) to avoid battery drain.

3. Add maze_game.py to the README examples table.
MatteoCnda1 and others added 2 commits April 16, 2026 21:53
Address review comments on #390:

1. Replace recursive run_game() self-call with an outer `while True` loop.
   On MicroPython's limited stack (8-16 KB), each completed game added a
   stack frame that was never freed — a few wins would crash with a
   RecursionError or silent stack overflow.

2. Add try/except/finally around the game loop: Ctrl+C now cleanly
   clears the display and powers off the IMU (same pattern as
   spirit_level.py) to avoid battery drain.

3. Add maze_game.py to the README examples table.
@nedseb nedseb force-pushed the feat/ism330dl-maze-game branch from 5093119 to 83b480a Compare April 16, 2026 19:54
Copy link
Copy Markdown
Contributor

@nedseb nedseb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bel exemple Matteo — le labyrinthe généré par DFS + le scoring Dijkstra sur un OLED 128x128 piloté par accéléromètre, c'est ambitieux et ça fonctionne bien.

J'ai poussé un commit de review (83b480a) qui corrige trois points :

1. Récursion → boucle (critique sur MicroPython)run_game() s'appelait elle-même après chaque victoire. Sur le stack limité de MicroPython (8-16 KB), quelques parties gagnées auraient provoqué un crash (RecursionError ou overflow silencieux). Remplacé par une boucle while True dans le point d'entrée, et run_game() retourne simplement après chaque partie.

2. Cleanup try/finally — un Ctrl+C laissait l'écran allumé et l'IMU tournant à 104 Hz. Ajouté screen.clear() + screen.show() + imu.power_off() dans le finally, comme dans spirit_level.py.

3. README — ajouté maze_game.py dans la table des exemples (manquant dans la PR originale). Conflit de rebase résolu avec la ligne spirit_level.py arrivée via main.

Branche rebasée sur main. Prêt à merger.

@nedseb nedseb merged commit 5552b5c into main Apr 16, 2026
9 checks passed
@nedseb nedseb deleted the feat/ism330dl-maze-game branch April 16, 2026 19:56
semantic-release-updater Bot pushed a commit that referenced this pull request Apr 16, 2026
# [0.21.0](v0.20.3...v0.21.0) (2026-04-16)

### Features

* **ism330dl:** Add maze game example with OLED and accelerometer. ([#390](#390)) ([5552b5c](5552b5c))
@semantic-release-updater
Copy link
Copy Markdown

🎉 This PR is included in version 0.21.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request released

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(ism330dl): Add maze game example with OLED and accelerometer.

3 participants