Overview
SWIsland and Coordinates are the two JavaScript classes introduced in Stage 7 of the SWIsland Recursion Saga. They encapsulate the island-building algorithm that was previously spread across six standalone sketch functions, moving it into reusable, self-contained classes that directly mirror the Java source taught in APCS A.
Before Stage 7, the sketch handled seed selection, adjacency testing, recursive growth, and step recording all inline. Stage 7 extracts all of that logic into the two classes below, so the sketch only handles orchestration and drawing. Functionally, the output is identical to Stage 6b — the refactoring is entirely architectural.
Coordinates
A tiny immutable value object storing a single (row, col)
position in the ocean grid.
- Source:
shapeClasses/coordinates.js - Mirrors:
Coordinates.java - Cells in
SWIsland._cells[]areCoordinatesinstances.
SWIsland
Represents a single island grown by recursive backtracking. Holds all cells, the step queue, and the three-rule validity check.
- Source:
shapeClasses/swIsland.js - Mirrors:
Island.java - Entry point: static
SWIsland.buildIsland()
Java Connection
The SWIsland Recursion Saga runs in parallel with a Java project studied in APCS A. The JavaScript classes mirror the Java source one-to-one so you can compare the two languages side-by-side using the same algorithm.
| Java class / method | JavaScript equivalent | Notes |
|---|---|---|
Coordinates.java |
coordinates.js → Coordinates |
Identical constructor, accessors, and toString() |
Island.java |
swIsland.js → SWIsland |
Same rules, same direction arrays, same method names |
Island.contains() |
SWIsland.contains(row, col) |
Uses Coordinates instances instead of int arrays |
Island.isValidExpansion() |
SWIsland.isValidExpansion(row, col, ocean, numRows, numCols) |
Same 3 rules; instance method so Rule 3 can call contains() |
Island.grow() |
SWIsland.grow(targetSize, ocean, numRows, numCols, useRandom, maxSteps) |
JS adds step recording and a browser-safety abort limit |
Island.buildIsland() |
SWIsland.buildIsland(ocean, targetSize, numRows, numCols, useRandom, maxSteps) |
JS adds a BFS reachability pre-check (Step 3) |
Island.toString() |
SWIsland.toString() |
Identical output format: SWIsland [size=5]: (3, 4) -> ... |
Island.DR4 / DC4 / DR8 / DC8 |
SWIsland.DR4 / DC4 / DR8 / DC8 |
JavaScript static class fields; same values |
Coordinates Class
Coordinates is a small immutable value object that stores a single
(row, col) position. Every cell in an SWIsland is a
Coordinates instance.
| Kind | Signature | Description |
|---|---|---|
| instance | new Coordinates(row, col) |
Creates a coordinate. Throws Error if either value is negative. |
| instance | getRow() → number |
Returns the zero-based row index. |
| instance | getCol() → number |
Returns the zero-based column index. |
| instance | toString() → string |
Returns "(row, col)" — exactly matches Coordinates.toString() in Java. |
Usage
SWIsland Class
SWIsland represents a single island grown by recursive backtracking.
It stores its cells as Coordinates instances, enforces the three-rule
expansion validity check, and records every ADD / BACKTRACK decision into
an internal step queue for animation replay.
Static Constants
These four direction arrays are static fields and mirror the
static final int[] arrays in Island.java exactly.
Constructor
| Kind | Signature | Description |
|---|---|---|
| instance | new SWIsland(startCell) |
Creates a new island seeded with one land cell.
startCell must be a Coordinates instance and
must already be stamped as land (ocean[r][c] = 1)
by the caller before construction.
Mirrors Island(Coordinates startCell) in Java.
|
Instance Methods
| Kind | Signature | Description |
|---|---|---|
| instance | getCells() → Coordinates[] |
Returns the array of land cells. Mirrors Island.getCells(). |
| instance | getSize() → number |
Returns the number of land cells. Mirrors Island.getSize(). |
| JS only | getStepQueue() → {type, row, col}[] |
Returns the ADD / BACKTRACK step array recorded by grow().
Each element is { type: 'ADD' | 'BACKTRACK', row: number, col: number }.
No Java equivalent.
|
| JS only | wasAborted() → boolean |
Returns true if grow() exceeded the maxSteps safety limit. No Java equivalent. |
| instance | contains(row, col) → boolean |
Returns true if this island already owns (row, col). Used by isValidExpansion() Rule 3. Mirrors Island.contains(). |
| instance | isValidExpansion(row, col, ocean, numRows, numCols) → boolean |
Returns true only if all three rules pass:
Island.isValidExpansion().
|
| instance | grow(targetSize, ocean, numRows, numCols, useRandom, maxSteps) → boolean |
Recursively grows this island to targetSize cells.
Mutates ocean during growth; restores it on backtrack.
Records every ADD and BACKTRACK step in this._stepQueue.
Returns true on success, false on failure.
Mirrors Island.grow(); see The Algorithm for the full breakdown.
|
| instance | toString() → string |
Returns a human-readable description of the island. Example: "SWIsland [size=5]: (3, 4) -> (3, 5) -> (4, 5) -> (5, 5) -> (5, 4)"Mirrors Island.toString() — matches console output from RecursiveIslandDriver.java.
|
Static Methods
| Kind | Signature | Description |
|---|---|---|
| static | SWIsland.buildIsland(ocean, targetSize, numRows, numCols, useRandom, maxSteps) → SWIsland|null |
Primary entry point. Tries random seed positions until it finds one where grow() succeeds.
Returns the finished island, an aborted partial island, or null if all positions are exhausted.
Mirrors Island.buildIsland() with a BFS reachability pre-check added.
See Usage Example.
|
| static JS only | SWIsland.countReachableCells(seedRow, seedCol, ocean, numRows, numCols) → number |
BFS flood-fill from the (already-stamped) seed. Returns the count of open-water cells reachable orthogonally.
Used internally by buildIsland() to fast-fail seeds with insufficient reachable space before recursion begins.
|
Separation Rule
The fundamental constraint that isValidExpansion() enforces:
Every island must be separated from every other island by at least one open-water cell in all 8 surrounding directions.
This rule is enforced at two points:
-
During growth —
isValidExpansion()Rule 3: Before adding any candidate cell, all 8 of its neighbors are checked. If any neighbor is land (ocean === 1) belonging to a different island, the candidate is rejected. -
During seed placement —
buildIsland()Step 2: A seed position is rejected if any of its 8 neighbors is already occupied by existing island land, preventing a new island from starting too close to an existing one.
The stricter 8-direction rule (introduced in Stage 6b over Stage 6a's 4-direction rule) measurably increases backtrack cost — the algorithm has fewer valid expansion paths available at each step.
Direction Arrays
Two pairs of parallel offset arrays drive all neighbor scanning. Indexing
DR4[i] and DC4[i] together gives one orthogonal step;
DR8[i] and DC8[i] give one of the 8 surrounding cells.
DR4 / DC4 — Orthogonal (used in grow())
4 orthogonal neighbors only
DR8 / DC8 — All 8 (used in isValidExpansion() & seed check)
All 8 surrounding cells
The Algorithm — grow()
grow() is a classic recursive backtracking function.
Each call either commits to adding a new cell or undoes its choice and tries another.
Base Case
When cells.length >= targetSize, the island has enough cells.
Return true to propagate success all the way up the call stack.
ADD Step
Tentatively commit to a candidate cell: push it onto _cells,
stamp ocean[r][c] = 1, and record { type:'ADD', row, col }
in the step queue.
BACKTRACK Step
If recursion from the added cell returned false, undo:
pop _cells, restore ocean[r][c] = 0, and
record { type:'BACKTRACK', row, col }.
Browser Safety: maxSteps
grow() checks this._stepQueue.length after each ADD.
If the queue reaches maxSteps (default 150 000),
it sets this._buildAborted = true and returns false immediately,
preventing the browser thread from hanging on adversarial ocean layouts.
The sketch displays a TOO SLOW warning in this case.
This guard has no equivalent in the Java version.
Step Queue
The step queue is a JavaScript-only addition to the algorithm — Island.java has no equivalent.
Its purpose is to record every decision grow() makes so the sketch can
replay the algorithm as a frame-by-frame animation after the synchronous
build phase completes.
Each element in the queue is a plain object:
The sketch calls island.getStepQueue() after buildIsland() returns
and splices those steps between ISLAND_START and ISLAND_SUCCESS
markers in the global animation queue. The animation then replays them at the chosen
speed (1–10 frames per step) so you can watch ADD flashes and BACKTRACK flashes in real time.
Global Step Types (sketch-level)
In addition to the 'ADD' and 'BACKTRACK' steps from
SWIsland, the sketch inserts marker steps into the merged queue:
| Step type | Source | Meaning |
|---|---|---|
'ADD' | SWIsland.grow() | A candidate cell was tentatively added to the island. |
'BACKTRACK' | SWIsland.grow() | The previous ADD failed; the cell was removed and the ocean restored. |
'ISLAND_START' | sketch | A new island's animation is beginning; sets up color and seed display. |
'ISLAND_SUCCESS' | sketch | Island reached target size; logs island.toString() to the breadcrumb panel. |
'ISLAND_FAIL' | sketch | No valid seed position was found; island skipped. |
'TOOSLOW' | sketch | grow() hit maxSteps; browser-safety abort. |
'ALL_DONE' | sketch | All islands have been processed; animation enters the DONE state. |
Usage Example
The typical call pattern from swIsland7Sketch.js:
Checking validity manually
Java vs. JavaScript Comparison
The table below highlights every deliberate difference between the Java and JavaScript implementations. Where a cell says "identical", the logic is a direct port.
| Feature | Java (Island.java) |
JavaScript (SWIsland) |
|---|---|---|
| Cell representation | Coordinates objects (int fields) |
Coordinates instances (JS class) — identical API |
| Direction arrays | static final int[] |
static class fields — identical values |
isValidExpansion() rules |
Rules 1, 2, 3 — identical | Rules 1, 2, 3 — identical |
grow() backtracking |
Identical recursive structure | Identical + step recording + maxSteps abort |
| Step queue | Not present | JS only this._stepQueue records every ADD and BACKTRACK for animation |
| Browser safety abort | Not needed (JVM handles it) | JS only maxSteps = 150,000 guard prevents browser hang |
| BFS reachability pre-check | Not present | JS only countReachableCells() inside buildIsland() fast-fails impossible seeds |
toString() output |
SWIsland [size=N]: (r, c) -> ... |
Identical — matches RecursiveIslandDriver.java console output |
| Ocean grid type | int[][] |
number[][] — semantically identical (0 = water, 1 = land, 2 = pit) |