From c4590d86e2dbaa5502cad16573d3fdc4031199ff Mon Sep 17 00:00:00 2001 From: "Nicholas H.Tollervey" Date: Fri, 29 May 2026 15:47:13 +0100 Subject: [PATCH 1/3] Add PyScript examples for pygame-ce Generated by apply_llm_response.py from prompts/pygame-ce/response.toml. Examples included: - surfaces_and_shapes: Surfaces and shapes - rects_and_blitting: Rects and blitting Generated-By: apply_llm_response.py --- examples/pygame-ce/README.md | 18 +++++ examples/pygame-ce/order.json | 4 + examples/pygame-ce/rects_and_blitting/code.py | 74 +++++++++++++++++++ .../pygame-ce/rects_and_blitting/config.toml | 1 + .../pygame-ce/rects_and_blitting/setup.py | 40 ++++++++++ .../pygame-ce/surfaces_and_shapes/code.py | 47 ++++++++++++ .../pygame-ce/surfaces_and_shapes/config.toml | 1 + .../pygame-ce/surfaces_and_shapes/setup.py | 68 +++++++++++++++++ 8 files changed, 253 insertions(+) create mode 100644 examples/pygame-ce/README.md create mode 100644 examples/pygame-ce/order.json create mode 100644 examples/pygame-ce/rects_and_blitting/code.py create mode 100644 examples/pygame-ce/rects_and_blitting/config.toml create mode 100644 examples/pygame-ce/rects_and_blitting/setup.py create mode 100644 examples/pygame-ce/surfaces_and_shapes/code.py create mode 100644 examples/pygame-ce/surfaces_and_shapes/config.toml create mode 100644 examples/pygame-ce/surfaces_and_shapes/setup.py diff --git a/examples/pygame-ce/README.md b/examples/pygame-ce/README.md new file mode 100644 index 0000000..1a112cc --- /dev/null +++ b/examples/pygame-ce/README.md @@ -0,0 +1,18 @@ +# pygame-ce Examples + +Each sub-directory contains a self-contained example. The order in +which the examples are to appear is specified in `order.json` (an +array of directory names in the expected order). + +In each example directory you'll find: + +* `config.toml` - must conform to the specification outlined here: + https://docs.pyscript.net/latest/user-guide/configuration/ This is + parsed and ultimately turned into a JSON representation as part of + the package's API object. +* `setup.py` - Python code for contextual and environmental setup, + NOT SEEN BY THE END USER, but is run before the `code.py` code is + evaluated. Allows us to create useful (IPython) shims, avoid + repeating boilerplate and whatnot. +* `code.py` - the actual code added to the editor which forms the + practical example of using the package. diff --git a/examples/pygame-ce/order.json b/examples/pygame-ce/order.json new file mode 100644 index 0000000..70c49c9 --- /dev/null +++ b/examples/pygame-ce/order.json @@ -0,0 +1,4 @@ +[ + "surfaces_and_shapes", + "rects_and_blitting" +] diff --git a/examples/pygame-ce/rects_and_blitting/code.py b/examples/pygame-ce/rects_and_blitting/code.py new file mode 100644 index 0000000..6a449f2 --- /dev/null +++ b/examples/pygame-ce/rects_and_blitting/code.py @@ -0,0 +1,74 @@ +# --------------------------------------------------------------------- +# Rects, sprites, and blitting one Surface onto another. +# --------------------------------------------------------------------- + +heading("Rects: the workhorse of pygame") +note( + "A pygame.Rect describes a rectangular area: position and size. " + "Almost every game uses Rects for positioning sprites, " + "detecting collisions, and clipping draw operations." +) + +# Build a small "sprite" Surface with per-pixel alpha so its +# transparent areas don't show when we blit it later. +def make_token(color, label): + """Make a 64x64 round token with a letter on it.""" + token = pygame.Surface((64, 64), pygame.SRCALPHA) + pygame.draw.circle(token, color, (32, 32), 30) + pygame.draw.circle(token, "white", (32, 32), 30, width=3) + font = pygame.font.SysFont(None, 40) + text = font.render(label, True, "white") + token.blit(text, text.get_rect(center=(32, 32))) + return token + +red_token = make_token("crimson", "R") +blue_token = make_token("steelblue", "B") + +# A board to blit them onto. +board = pygame.Surface((480, 320)) +board.fill((20, 80, 40)) # felt-table green + +# Draw a grid of guide lines using Rect for the playfield. +play_area = pygame.Rect(20, 20, 440, 280) +pygame.draw.rect(board, (10, 50, 25), play_area) +pygame.draw.rect(board, "white", play_area, width=2) + +# Place the tokens. Surface.get_rect() gives us a Rect we can move +# around with helpers like .center, .move(), and .colliderect(). +red_rect = red_token.get_rect(center=(140, 160)) +blue_rect = blue_token.get_rect(center=(180, 170)) + +# blit() copies one Surface onto another at the rect's top-left. +board.blit(red_token, red_rect) +board.blit(blue_token, blue_rect) + +# Rects can detect overlap, useful for collision checks. +overlapping = red_rect.colliderect(blue_rect) +note(f"Do the two tokens overlap? {overlapping}") + +show_surface(board, caption="Two tokens blitted onto a board") + +heading("Animating a Rect across frames") +note( + "A game loop usually updates positions then redraws. Here we " + "render a few frames of a token sliding across the board and " + "show them as a strip so you can see the motion." +) + +frames = [] +slider = make_token("gold", "G") +slider_rect = slider.get_rect(center=(60, 160)) + +for step in range(5): + frame = board.copy() + # Move the rect 90 pixels to the right each frame. + slider_rect = slider_rect.move(90, 0) + frame.blit(slider, slider_rect) + frames.append(frame) + +# Compose all frames into one tall image. +strip = pygame.Surface((480, 320 * len(frames))) +for index, frame in enumerate(frames): + strip.blit(frame, (0, index * 320)) + +show_surface(strip, caption="Five frames of a sliding token") diff --git a/examples/pygame-ce/rects_and_blitting/config.toml b/examples/pygame-ce/rects_and_blitting/config.toml new file mode 100644 index 0000000..98e0600 --- /dev/null +++ b/examples/pygame-ce/rects_and_blitting/config.toml @@ -0,0 +1 @@ +packages = ["pygame-ce"] diff --git a/examples/pygame-ce/rects_and_blitting/setup.py b/examples/pygame-ce/rects_and_blitting/setup.py new file mode 100644 index 0000000..e3cbcb4 --- /dev/null +++ b/examples/pygame-ce/rects_and_blitting/setup.py @@ -0,0 +1,40 @@ +"""Setup for the rects-and-blitting example. No IPython shim here.""" +import os +os.environ.setdefault("SDL_VIDEODRIVER", "dummy") + +import io +import base64 +import js +from pyscript import window, HTML, display as _display + +js.alert = window.alert + + +def display(*args, **kwargs): + return _display(*args, **kwargs, target=__pyscript_display_target__) + + +def heading(text, level=2): + display(HTML(f"{text}"), append=True) + + +def note(text): + display(HTML(f"

{text}

"), append=True) + + +import pygame +pygame.init() + + +def show_surface(surface, caption=""): + buffer = io.BytesIO() + pygame.image.save(surface, buffer, "PNG") + encoded = base64.b64encode(buffer.getvalue()).decode("ascii") + label = f"
{caption}
" if caption else "" + display( + HTML( + f'{label}' + ), + append=True, + ) diff --git a/examples/pygame-ce/surfaces_and_shapes/code.py b/examples/pygame-ce/surfaces_and_shapes/code.py new file mode 100644 index 0000000..ac9c65c --- /dev/null +++ b/examples/pygame-ce/surfaces_and_shapes/code.py @@ -0,0 +1,47 @@ +""" +A first look at pygame-ce: drawing on a Surface. + +Pygame is a game library, and at its heart is the Surface, a 2D +pixel canvas you draw onto. In a normal pygame program you would +call pygame.display.set_mode(...) to get a window-backed Surface +and run a game loop. Here we work with offscreen Surfaces directly, +which is the same API minus the window, and show the result inline. + +See https://pyga.me/docs/ for the full reference. +""" +from IPython.core.display import display, HTML + +# pygame must be initialized before most subsystems will work. +pygame.init() + +heading("Drawing shapes on a Surface") +note( + "We create a 480x320 Surface, fill the background, and draw some " + "shapes with pygame.draw. Colors can be given as RGB tuples or " + "named CSS-style strings." +) + +canvas = pygame.Surface((480, 320)) +canvas.fill((30, 30, 60)) # deep navy background + +# A row of filled circles, like balloons rising. +for index, color in enumerate(["tomato", "gold", "mediumseagreen", "skyblue"]): + center = (80 + index * 110, 200) + pygame.draw.circle(canvas, color, center, 36) + pygame.draw.circle(canvas, "white", center, 36, width=3) + +# A polygon (a simple house outline). +house = [(360, 260), (360, 180), (410, 140), (460, 180), (460, 260)] +pygame.draw.polygon(canvas, "khaki", house) +pygame.draw.polygon(canvas, "saddlebrown", house, width=4) + +# Anti-aliased line across the sky. +pygame.draw.aaline(canvas, "white", (10, 40), (470, 80)) + +show_surface(canvas, caption="Hand-drawn scene on a 480x320 Surface") + +note( + "The Surface is just an in-memory image. The same drawing calls " + "would render to the screen Surface returned by " + "pygame.display.set_mode in a regular game." +) diff --git a/examples/pygame-ce/surfaces_and_shapes/config.toml b/examples/pygame-ce/surfaces_and_shapes/config.toml new file mode 100644 index 0000000..98e0600 --- /dev/null +++ b/examples/pygame-ce/surfaces_and_shapes/config.toml @@ -0,0 +1 @@ +packages = ["pygame-ce"] diff --git a/examples/pygame-ce/surfaces_and_shapes/setup.py b/examples/pygame-ce/surfaces_and_shapes/setup.py new file mode 100644 index 0000000..0ce564c --- /dev/null +++ b/examples/pygame-ce/surfaces_and_shapes/setup.py @@ -0,0 +1,68 @@ +""" +Shim IPython's display API onto PyScript so example code written in a +Jupyter/IPython idiom runs unmodified in the browser. +""" + +import sys +import types +import js +from pyscript import window, HTML, display as _display + +js.alert = window.alert + + +def display(*args, **kwargs): + """Wrap pyscript.display so output lands in the example target.""" + return _display( + *args, **kwargs, target=__pyscript_display_target__, + ) + + +ipython = types.ModuleType("IPython") +core = types.ModuleType("IPython.core") +core_display = types.ModuleType("IPython.core.display") +core_display.display = display +core_display.HTML = HTML +ipython.core = core +core.display = core_display +ipython.get_ipython = lambda: None +ipython.display = core_display +sys.modules["IPython"] = ipython +sys.modules["IPython.core"] = core +sys.modules["IPython.core.display"] = core_display +sys.modules["IPython.display"] = core_display + + +def heading(text, level=2): + display(HTML(f"{text}"), append=True) + + +def note(text): + display(HTML(f"

{text}

"), append=True) + + +# pygame-ce needs a dummy video driver when running headless inside a +# web worker. We must set this BEFORE importing pygame. +import os +os.environ.setdefault("SDL_VIDEODRIVER", "dummy") + +import io +import base64 +import pygame + + +def show_surface(surface, caption=""): + """Render a pygame Surface as an inline PNG in the page.""" + # pygame.image.save can write to any file-like object. We grab the + # PNG bytes, base64-encode them, and embed them in an tag. + buffer = io.BytesIO() + pygame.image.save(surface, buffer, "PNG") + encoded = base64.b64encode(buffer.getvalue()).decode("ascii") + label = f"
{caption}
" if caption else "" + display( + HTML( + f'{label}' + ), + append=True, + ) From f51e5ebd02e418afbf10870d02ac915e53aed382 Mon Sep 17 00:00:00 2001 From: "Nicholas H.Tollervey" Date: Thu, 11 Jun 2026 13:13:06 +0100 Subject: [PATCH 2/3] Fix imports and setup. --- examples/pygame-ce/rects_and_blitting/code.py | 18 +++++++++++++ .../pygame-ce/rects_and_blitting/setup.py | 18 ------------- .../pygame-ce/surfaces_and_shapes/code.py | 27 +++++++++++++++++++ .../pygame-ce/surfaces_and_shapes/setup.py | 27 ------------------- 4 files changed, 45 insertions(+), 45 deletions(-) diff --git a/examples/pygame-ce/rects_and_blitting/code.py b/examples/pygame-ce/rects_and_blitting/code.py index 6a449f2..63b9e7c 100644 --- a/examples/pygame-ce/rects_and_blitting/code.py +++ b/examples/pygame-ce/rects_and_blitting/code.py @@ -2,6 +2,24 @@ # Rects, sprites, and blitting one Surface onto another. # --------------------------------------------------------------------- +import pygame +pygame.init() + + +def show_surface(surface, caption=""): + buffer = io.BytesIO() + pygame.image.save(surface, buffer, "PNG") + encoded = base64.b64encode(buffer.getvalue()).decode("ascii") + label = f"
{caption}
" if caption else "" + display( + HTML( + f'{label}' + ), + append=True, + ) + + heading("Rects: the workhorse of pygame") note( "A pygame.Rect describes a rectangular area: position and size. " diff --git a/examples/pygame-ce/rects_and_blitting/setup.py b/examples/pygame-ce/rects_and_blitting/setup.py index e3cbcb4..a0819f3 100644 --- a/examples/pygame-ce/rects_and_blitting/setup.py +++ b/examples/pygame-ce/rects_and_blitting/setup.py @@ -20,21 +20,3 @@ def heading(text, level=2): def note(text): display(HTML(f"

{text}

"), append=True) - - -import pygame -pygame.init() - - -def show_surface(surface, caption=""): - buffer = io.BytesIO() - pygame.image.save(surface, buffer, "PNG") - encoded = base64.b64encode(buffer.getvalue()).decode("ascii") - label = f"
{caption}
" if caption else "" - display( - HTML( - f'{label}' - ), - append=True, - ) diff --git a/examples/pygame-ce/surfaces_and_shapes/code.py b/examples/pygame-ce/surfaces_and_shapes/code.py index ac9c65c..7e5ff48 100644 --- a/examples/pygame-ce/surfaces_and_shapes/code.py +++ b/examples/pygame-ce/surfaces_and_shapes/code.py @@ -11,6 +11,33 @@ """ from IPython.core.display import display, HTML +# pygame-ce needs a dummy video driver when running headless inside a +# web worker. We must set this BEFORE importing pygame. +import os +os.environ.setdefault("SDL_VIDEODRIVER", "dummy") + +import io +import base64 +import pygame + + +def show_surface(surface, caption=""): + """Render a pygame Surface as an inline PNG in the page.""" + # pygame.image.save can write to any file-like object. We grab the + # PNG bytes, base64-encode them, and embed them in an tag. + buffer = io.BytesIO() + pygame.image.save(surface, buffer, "PNG") + encoded = base64.b64encode(buffer.getvalue()).decode("ascii") + label = f"
{caption}
" if caption else "" + display( + HTML( + f'{label}' + ), + append=True, + ) + + # pygame must be initialized before most subsystems will work. pygame.init() diff --git a/examples/pygame-ce/surfaces_and_shapes/setup.py b/examples/pygame-ce/surfaces_and_shapes/setup.py index 0ce564c..b4f3ee1 100644 --- a/examples/pygame-ce/surfaces_and_shapes/setup.py +++ b/examples/pygame-ce/surfaces_and_shapes/setup.py @@ -39,30 +39,3 @@ def heading(text, level=2): def note(text): display(HTML(f"

{text}

"), append=True) - - -# pygame-ce needs a dummy video driver when running headless inside a -# web worker. We must set this BEFORE importing pygame. -import os -os.environ.setdefault("SDL_VIDEODRIVER", "dummy") - -import io -import base64 -import pygame - - -def show_surface(surface, caption=""): - """Render a pygame Surface as an inline PNG in the page.""" - # pygame.image.save can write to any file-like object. We grab the - # PNG bytes, base64-encode them, and embed them in an tag. - buffer = io.BytesIO() - pygame.image.save(surface, buffer, "PNG") - encoded = base64.b64encode(buffer.getvalue()).decode("ascii") - label = f"
{caption}
" if caption else "" - display( - HTML( - f'{label}' - ), - append=True, - ) From f6cadaae90a50279188c0575604a457ffe0b8e7b Mon Sep 17 00:00:00 2001 From: "Nicholas H.Tollervey" Date: Fri, 26 Jun 2026 10:49:49 +0100 Subject: [PATCH 3/3] Add version_info to IPython shim for PyScript update. --- examples/pygame-ce/surfaces_and_shapes/setup.py | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/pygame-ce/surfaces_and_shapes/setup.py b/examples/pygame-ce/surfaces_and_shapes/setup.py index b4f3ee1..bb018e1 100644 --- a/examples/pygame-ce/surfaces_and_shapes/setup.py +++ b/examples/pygame-ce/surfaces_and_shapes/setup.py @@ -25,6 +25,7 @@ def display(*args, **kwargs): core_display.HTML = HTML ipython.core = core core.display = core_display +ipython.version_info = (9, 0, 2, '') ipython.get_ipython = lambda: None ipython.display = core_display sys.modules["IPython"] = ipython