Skip to content

Commit 4e5a86e

Browse files
tytremblayclaude
andcommitted
Add maze module: generator playground, wall-follower, maze-solver integration
New lesson 35 "Navigating a maze" and its interactive tooling, plus the plumbing to run FRC2713/maze-solver-java in the browser: - MazePlayground: renders a generated maze (correct recursive-backtracking, since the maze-generator npm package's shuffle is broken), with a drivable robot, start/goal markers, an animated wall-follower, and "Copy for Java" serialization to an int[][] bitmask literal. - Lesson pages build from "what a maze is" -> robot navigation -> algorithms (sense/decide/move) -> the wall follower -> the same algorithm in raw Java -> the same algorithm against the maze-solver library's Robot/Cell API. - Site wiring: `maze` fence (lessons.ts/LessonView), maze-solver.jar on the CheerpJ classpath, a CI step + vendor script that build the jar (gitignored, never committed). Also includes the in-flight lessons ordering refactor and module docs (CONTEXT.md glossary, ADR 0001, ordering-refactor handoff) that were already in the working tree. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 740b271 commit 4e5a86e

50 files changed

Lines changed: 1146 additions & 175 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/settings.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
{
2+
"enabledPlugins": {
3+
"frc2713-skills@frc2713": true
4+
}
5+
}

.github/workflows/deploy-pages.yml

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,26 @@ jobs:
1919
runs-on: ubuntu-latest
2020
steps:
2121
- uses: actions/checkout@v4
22+
23+
# Build the maze-solver library and vendor its jar into site/public/ so
24+
# the Java playground can load the com.frc2713.mazesolver API. Built fresh
25+
# each deploy from the library's main; not committed (see site/.gitignore).
26+
- uses: actions/checkout@v4
27+
with:
28+
repository: FRC2713/maze-solver-java
29+
ref: main
30+
path: maze-solver-java
31+
- uses: actions/setup-java@v4
32+
with:
33+
distribution: temurin
34+
java-version: '21'
35+
cache: maven
36+
- name: Build maze-solver jar
37+
run: |
38+
mvn -B -q -DskipTests package
39+
cp target/maze-solver.jar "$GITHUB_WORKSPACE/site/public/maze-solver.jar"
40+
working-directory: maze-solver-java
41+
2242
- uses: actions/setup-node@v4
2343
with:
2444
node-version: 20

CONTEXT.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Maze Solver Module
2+
3+
Glossary for the maze-solving module of the FRC 2713 training curriculum — a
4+
series of lessons in which students write Java that solves a maze. This file is
5+
the shared language for that module only; it is not a spec.
6+
7+
## Language
8+
9+
**Maze**:
10+
A fixed puzzle the robot must traverse from Start to Goal. The student's program
11+
is handed the whole maze up front (see _Grid_).
12+
13+
**Grid**:
14+
The god's-eye representation of a Maze the program receives — the entire layout,
15+
known in full before the robot moves, as a rectangular 2D array of Cells. Each
16+
Cell carries its own Wall flags, so the Grid is a `Cell[][]`, not a grid of
17+
blocked/open squares.
18+
_Avoid_: map (reserve "map" for a robot-built model, which this module does not use)
19+
20+
**Cell**:
21+
One square of the Grid — always open and standable. A Cell records a **Wall** on
22+
each of its four sides (up / down / left / right); those flags, not the Cells
23+
themselves, are what block movement. Every Cell is reachable to stand on; what
24+
varies is which of its edges are walled.
25+
26+
**Wall**:
27+
A **thin** barrier on the edge between two adjacent Cells (or on a Cell's outer
28+
edge), blocking movement across that edge. Walls live on edges, not whole Cells:
29+
a Cell is never "a wall". Wall flags are consistent between neighbours — the down
30+
edge of a Cell is the up edge of the Cell below it.
31+
32+
**Robot**:
33+
The thing that traverses the Maze by executing a Solution. It has a position on
34+
the Grid (a Cell) and **no orientation** — it does not face a direction, so every
35+
Move is an absolute step, not a turn.
36+
37+
**Start**:
38+
The cell where the Robot begins.
39+
40+
**Goal**:
41+
The target cell. A Solution **succeeds** if and only if executing it from Start
42+
leaves the Robot standing on the Goal.
43+
44+
**Solution**:
45+
The ordered sequence of Moves the student's program emits. Success is judged
46+
only by whether it finishes the Maze (reaches the Goal). A Move blocked by a Wall
47+
is not a modeled failure — it simply does nothing; nothing is penalized. The
48+
module is about *finishing*, not collision avoidance.
49+
50+
**Maze Battle**:
51+
The module's finale. A student submits one program; it is scored by how many of a
52+
set of *unseen* Mazes it finishes (a gauntlet), tie-broken by total Moves. The
53+
format rewards a general algorithm (which clears the whole set) over a hard-coded
54+
Solution (which finishes only the one Maze it was written for).
55+
56+
**Move** (also **Command**):
57+
One instruction to the Robot. Exactly four exist: **UP**, **DOWN**, **LEFT**,
58+
**RIGHT** — absolute, screen-relative directions. Each Move steps the Robot
59+
**one Cell** in that direction *if that edge is open*; a Move blocked by a Wall
60+
(or the grid edge) does nothing. There are no turns and no orientation to track.
61+
_Avoid_: turn, heading, facing, North/South/East/West, "drive to the next junction"
62+
63+
**Helper** (injected):
64+
Convenience methods the harness puts in scope so students query the Maze without
65+
hand-indexing Wall flags: `robot.moveUp/moveDown/moveLeft/moveRight()`,
66+
`robot.canGoUp/canGoDown/canGoLeft/canGoRight()`, `robot.atGoal()`,
67+
`robot.row()/col()`, plus god's-eye access to the whole `Cell[][]` grid for route
68+
planning.
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# 1. Derive lesson order from position
2+
3+
Status: Accepted
4+
5+
Date: 2026-07-20
6+
7+
## Context
8+
9+
A lesson's **absolute position** used to be stored as *content* in three places,
10+
which made inserting or reordering a lesson a ~250-edit chore:
11+
12+
- `order:` frontmatter was a dense consecutive integer sequence (1, 2, 3, …), so
13+
inserting a lesson shifted the `order` of every later lesson.
14+
- `title:` embedded the ordinal (`"Lesson 20: State machines as diagrams"`).
15+
- Cross-references named the ordinal in visible link text and prose
16+
(`[lesson 12](#/lesson/12-arrays)`, and bare "in lesson 20" mentions) — 103
17+
hash-links plus dozens of prose mentions across 34 files.
18+
19+
Sorting is driven **only** by `order` (`site/src/lib/lessons.ts`); the numeric
20+
prefix on a folder/slug name is cosmetic and never read. So the displayed number
21+
can be computed from a lesson's position in the sorted list instead of stored.
22+
23+
## Decision
24+
25+
Derive the displayed lesson number from position; get ordinals out of stored
26+
content. Specifically (Option B — we did **not** rename folders or change any
27+
`#/lesson/<slug>` href, which was the rejected Option C):
28+
29+
1. **Gapped `order`.** Each lesson's `order` is `position * 10` (1st → 10, 2nd →
30+
20, … 34th → 340). Same sequence as before, but with room to insert a lesson
31+
between neighbors (e.g. a future maze module at 191–195) without touching any
32+
other lesson's `order`.
33+
2. **Concept-only titles.** The `Lesson N: ` prefix is stripped from all `title:`
34+
values (`title: "State machines as diagrams"`).
35+
3. **Number rendered from position.** The `lessons` array is sorted by `order`;
36+
the displayed number is its 1-based index. `lessonNumber(slug)` in
37+
`site/src/lib/lessons.ts` is the single source of truth; the index cards,
38+
sidebar, and lesson-view header all read it.
39+
4. **Slug-keyed cross-references.** Cross-reference links carry no stored number.
40+
Their visible text uses `{n}` / `{title}` tokens (e.g.
41+
`[lesson {n}](#/lesson/09-if-statements)`) that a custom react-markdown link
42+
renderer in `LessonView.tsx` fills in from the target's **current**
43+
`lessonNumber` / title at render time. Bare-prose "lesson NN" mentions were
44+
converted to the same token-link form (or rephrased to a concept reference,
45+
e.g. "the Objects lessons").
46+
47+
## Consequences
48+
49+
- Inserting or reordering a lesson touches **only** `order:` values. No title,
50+
prose, or cross-reference edits are needed; every displayed number and
51+
reference re-labels itself automatically from the new positions.
52+
- Slugs keep their numeric prefix (`12-arrays`). It is now an opaque, harmless id
53+
that users never see — it is not the lesson's displayed number and need not
54+
match it. A lesson can sit at any position regardless of its slug prefix.
55+
- Lesson READMEs remain valid Markdown, but the `{n}` / `{title}` tokens and
56+
`#/lesson/<slug>` hrefs only resolve inside the site renderer; on GitHub the
57+
tokens render literally and the hash links do not navigate. This was already
58+
true of the hash links and is an accepted cost of Option B.
59+
- Anything that reads a lesson's number must derive it (`lessonNumber`), never
60+
parse it from a title or slug.

docs/handoffs/ordering-refactor.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
# Handoff: make lesson ordering cheap to change (Option B)
2+
3+
## Why
4+
Today, a lesson's **absolute position** is stored as *content* in three places, so
5+
inserting or moving a lesson is a ~250-edit chore:
6+
7+
- `order:` frontmatter is dense consecutive integers → inserting shifts every later lesson.
8+
- `title:` embeds the ordinal (`"Lesson 20: State machines as diagrams"`).
9+
- Cross-references name the ordinal in visible prose and link text
10+
(`[lesson 12](#/lesson/12-arrays)`, and bare "in lesson 20" mentions).
11+
12+
Sorting is driven **only** by `order` (`site/src/lib/lessons.ts:92`); the numeric
13+
prefix on folder names is cosmetic. The fix: **derive the displayed number from a
14+
lesson's position in the sorted list**, and get ordinals out of stored content.
15+
16+
Measured footprint (lessons 1–34): 34 titles, 103 hash-links, 128 prose "lesson NN"
17+
mentions, 34 numbered folders.
18+
19+
## Scope: Option B (do), Option C (do NOT)
20+
**In scope (B):**
21+
1. **Gapped `order`.** Rewrite each lesson's `order` to `position * 10` (current
22+
1st → 10, 2nd → 20, … 34th → 340), preserving today's exact sequence but
23+
leaving room to insert. (A future maze module will slot in at, e.g., 191–195
24+
between Objects and State Machines — nothing else will need to move.)
25+
2. **De-numbered titles.** Strip the `Lesson N: ` prefix from all 34 `title:`
26+
values → concept only (`title: "State machines as diagrams"`).
27+
3. **Site renders the number from position.** The `lessons` array is already
28+
sorted by `order`; display a 1-based index as the lesson number everywhere a
29+
number should appear — index cards (`LessonCard.tsx`), the lesson header
30+
(`LessonView`), and the sidebar (`AppSidebar.tsx`, whose `shortTitle` regex on
31+
line 18 becomes obsolete — titles no longer carry the prefix; prepend the
32+
derived number instead if a number is wanted there). Numbers must stay 1..34
33+
in the same order after the refactor — this is a no-visible-reorder change.
34+
4. **Reorder-proof cross-references.** This is the point of the whole exercise:
35+
after this, moving/inserting a lesson must NOT require editing any other
36+
lesson's prose. Recommended mechanism: a custom link renderer (the site
37+
renders lesson markdown — find the react-markdown/MD renderer in
38+
`site/src/`) that, for any `#/lesson/<slug>` link, resolves the target
39+
lesson's **current** derived number (and/or title) at render time via
40+
`getLesson(slug)` + its index. Convert the 103 existing
41+
`[lesson N](#/lesson/<slug>)` links to a canonical slug-keyed form the
42+
renderer fills in, so the visible "lesson N" is always computed, never stored.
43+
For the bare-prose "lesson NN" mentions that are not links, prefer turning them
44+
into such links, or rephrase to a concept reference ("the arrays lesson"). No
45+
stored ordinal may remain in prose.
46+
47+
**Out of scope (Option C — do NOT do):**
48+
- Do **not** rename lesson folders or strip numeric prefixes from slugs.
49+
- Do **not** change any `#/lesson/<slug>` href target. Slugs stay exactly as they
50+
are (their numeric prefix is now a harmless opaque id users never see).
51+
52+
## Deliverables
53+
- All 34 lessons updated (order + title) and cross-references converted.
54+
- Site code renders derived numbers and resolves cross-ref numbers at render time.
55+
- `docs/adr/0001-derive-lesson-order-from-position.md` recording the decision
56+
(context: ordinal-as-content churn; decision: derive from position, concept-only
57+
titles, slug-keyed cross-refs; consequence: inserts/reorders touch only `order`).
58+
- `cd site && npm run build && npm run lint` both pass.
59+
60+
## Acceptance check
61+
- Index and sidebar show lessons 1..34 in the identical order and with the same
62+
visible numbers as before this change.
63+
- Every cross-reference link still resolves, and its visible number matches the
64+
target's current position (test by temporarily bumping one lesson's `order`
65+
past a neighbor — the reference text should follow automatically, with no
66+
content edit — then revert).
67+
- No `title:` contains `Lesson \d+:`; no lesson prose contains a hard-coded
68+
"lesson NN" ordinal.

lessons/01-flowcharts/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
2-
title: "Lesson 1: Programs are step-by-step"
2+
title: "Programs are step-by-step"
33
goal: "See a program as a flowchart of blocks and watch the computer evaluate it one step at a time."
4-
order: 1
4+
order: 10
55
section: "Programming with Blocks"
66
---
77

lessons/02-conditionals/README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
2-
title: "Lesson 2: Making decisions"
2+
title: "Making decisions"
33
goal: "Use a condition to make a program choose between two paths."
4-
order: 2
4+
order: 20
55
section: "Programming with Blocks"
66
---
77

@@ -30,7 +30,7 @@ asks `a > b?` — that's `true` here (`7 > 4`) — so the program follows the
3030
Press **▶ Run step by step**. Watch the diamond answer `true` and the program
3131
head down the `true` arrow — while the other branch **grays out and gets
3232
skipped**. Those blocks never run. This fork is exactly what Java writes as
33-
`if / else` — you'll type it yourself in lesson 9.
33+
`if / else` — you'll type it yourself in [lesson {n}](#/lesson/09-if-statements).
3434

3535
```blocks
3636
preset: cond-demo

lessons/03-loops/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
2-
title: "Lesson 3: Doing things over and over"
2+
title: "Doing things over and over"
33
goal: "Use a loop to repeat a step many times without copying it out by hand."
4-
order: 3
4+
order: 30
55
section: "Programming with Blocks"
66
---
77

lessons/04-functions/README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
2-
title: "Lesson 4: Reusable blocks"
2+
title: "Reusable blocks"
33
goal: "Package a flowchart into a named block you can reuse instead of rebuilding it."
4-
order: 4
4+
order: 40
55
section: "Programming with Blocks"
66
---
77

lessons/05-what-is-java/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
---
2-
title: "Lesson 5: What is Java?"
2+
title: "What is Java?"
33
goal: "Recognize Java's basic punctuation and program shape before writing or running any code."
4-
order: 5
4+
order: 50
55
section: "Java Fundamentals"
66
---
77

88
# A language, not a magic spell
99

1010
You've been building programs already — as flowcharts of blocks
11-
([lesson 1](#/lesson/01-flowcharts) onward). From here on we write those same
11+
([lesson {n}](#/lesson/01-flowcharts) onward). From here on we write those same
1212
ideas as **text**, in a language called **Java**. It's the language FRC robot
1313
code is written in, and one of the most widely used languages in the world.
1414

@@ -72,7 +72,7 @@ public class Main {
7272

7373
Don't worry about memorizing this — the playground writes it for you for now,
7474
and you'll learn to write it yourself in
75-
[lesson 11](#/lesson/11-writing-methods). For today, just recognize the shapes
75+
[lesson {n}](#/lesson/11-writing-methods). For today, just recognize the shapes
7676
in it with the vocabulary from above:
7777

7878
- `public class Main { ... }` — a **class** named `Main`, with everything it

0 commit comments

Comments
 (0)