Skip to content

Commit d7f9ad5

Browse files
committed
feat(#1): add spec-driven solutions for matrix traversal problems
0 parents  commit d7f9ad5

22 files changed

Lines changed: 1400 additions & 0 deletions

File tree

.github/workflows/ci.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches:
6+
- '**'
7+
8+
jobs:
9+
test:
10+
name: Maven Test
11+
runs-on: ubuntu-latest
12+
13+
steps:
14+
- name: Checkout code
15+
uses: actions/checkout@v5
16+
17+
- name: Set up JDK 17
18+
uses: actions/setup-java@v4
19+
with:
20+
distribution: 'temurin'
21+
java-version: '17'
22+
23+
- name: Run tests
24+
run: mvn test

.gitignore

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
target/
2+
!.mvn/wrapper/maven-wrapper.jar
3+
!**/src/main/**/target/
4+
!**/src/test/**/target/
5+
.kotlin
6+
7+
### IntelliJ IDEA ###
8+
.idea/modules.xml
9+
.idea/jarRepositories.xml
10+
.idea/compiler.xml
11+
.idea/libraries/
12+
*.iws
13+
*.iml
14+
*.ipr
15+
16+
### Eclipse ###
17+
.apt_generated
18+
.classpath
19+
.factorypath
20+
.project
21+
.settings
22+
.springBeans
23+
.sts4-cache
24+
25+
### NetBeans ###
26+
/nbproject/private/
27+
/nbbuild/
28+
/dist/
29+
/nbdist/
30+
/.nb-gradle/
31+
build/
32+
!**/src/main/**/build/
33+
!**/src/test/**/build/
34+
35+
### VS Code ###
36+
.vscode/
37+
38+
### Mac OS ###
39+
.DS_Store

.idea/.gitignore

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/inspectionProfiles/Project_Default.xml

Lines changed: 12 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/misc.xml

Lines changed: 14 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

.idea/vcs.xml

Lines changed: 6 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Archived OpenSpec Changes
2+
3+
This project implements classic and advanced matrix traversal problems using OpenSpec-driven workflows. Below are summaries of the two completed and archived changes:
4+
5+
---
6+
7+
## Biggest Island
8+
- **Problem:** Given a 2D binary matrix, find the area of the largest island (group of 1s connected 4-directionally).
9+
- **Approach:** Iterative BFS, in-place marking of visited cells (O(m·n) time, O(1) extra space).
10+
- **Artifacts:** Full OpenSpec (proposal, spec, design, tasks) and implementation with comprehensive tests.
11+
- **Archive Location:** `openspec/changes/archive/biggest-island/`
12+
13+
---
14+
15+
## Making a Large Island
16+
- **Problem:** Given an n×n binary matrix, flip at most one 0 to 1 to maximize the largest island area. Return the largest possible area after the flip.
17+
- **Approach:** Two-pass O(n²) algorithm: first label all islands with unique IDs and record their areas, then for each water cell, sum the areas of all distinct neighboring islands plus one (the flip).
18+
- **Artifacts:** Full OpenSpec (proposal, spec, design, tasks) and implementation with comprehensive tests.
19+
- **Archive Location:** `openspec/changes/archive/2026-04-18-making-a-large-island/`
20+
21+
---
22+
23+
Both features are fully specified, implemented, and tested. See the respective archive folders for detailed specs, design decisions, and test coverage.
24+
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
schema: spec-driven
2+
version: "1.2.0"
3+
Lines changed: 125 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,125 @@
1+
# Design: making-a-large-island
2+
3+
## Context
4+
5+
Extends the matrix-traversal-java library with a harder island variant. The existing
6+
`IslandSolver` solves the read-only "biggest island" problem. `LargeIslandSolver`
7+
builds on that domain but requires a two-pass strategy because a brute-force re-BFS
8+
per water cell would be O(n⁴) — too slow for n = 500. The input grid may be mutated
9+
in-place (same convention as `IslandSolver`).
10+
11+
## Goals / Non-Goals
12+
13+
**Goals:**
14+
- O(n²) time and O(n²) space solution satisfying R-1 through R-6.
15+
- Correct handling of all-land, all-water, and 1×1 edge cases.
16+
- Full JUnit 5 unit-test coverage for all spec scenarios.
17+
18+
**Non-Goals:**
19+
- Flipping more than one `0`.
20+
- Diagonal connectivity.
21+
- Returning the coordinates of the optimal flip cell.
22+
- Concurrent or streaming APIs.
23+
24+
## Decisions
25+
26+
### D1: Two-Pass Algorithm over Brute-Force Re-BFS
27+
28+
**Choice:** Two-pass approach:
29+
1. **Pass 1 — Label:** BFS every land cell, assigning each island a unique integer ID
30+
(starting at `2`, since `0`=water and `1`=land are already used). Store each
31+
island's area in a `Map<Integer, Integer> areaById`.
32+
2. **Pass 2 — Probe:** For each water cell (`0`), look at its 4 neighbors, collect
33+
**distinct** island IDs, sum their areas + 1, and track the global maximum.
34+
35+
**Rationale:**
36+
- Brute-force (flip each `0`, re-run BFS, flip back) is O(n²) × O(n²) = O(n⁴) — too slow for n = 500.
37+
- Two-pass is O(n²) time and O(n²) space — fast enough for the constraint.
38+
- Satisfies R-1, R-6 (distinct IDs prevent double-counting).
39+
40+
**Alternatives considered:**
41+
- *Brute-force re-BFS per water cell* — O(n⁴), too slow for n = 500; rejected.
42+
- *Union-Find* — correct and O(n² α(n²)) ≈ O(n²), but more complex to implement; labels-and-map approach is simpler and equally fast; rejected for readability.
43+
44+
---
45+
46+
### D2: In-Place Island Labeling (IDs start at 2)
47+
48+
**Choice:** Overwrite land cells with their island ID (≥ 2) directly in the grid during Pass 1.
49+
50+
**Rationale:**
51+
- Eliminates the need for a separate `int[][] label` array — saves O(n²) auxiliary space.
52+
- IDs start at `2` so `0` (water) and `1` (unlabeled land encountered during outer loop scan) remain unambiguous sentinels during Pass 1.
53+
- Consistent with the project convention of in-place mutation established in `IslandSolver` (D2 there).
54+
55+
**Alternatives considered:**
56+
- *Separate label array* — preserves the input but costs O(n²) extra space; rejected.
57+
58+
---
59+
60+
### D3: Distinct-ID Set per Water Cell Probe
61+
62+
**Choice:** For each water cell in Pass 2, collect neighbor IDs into a `Set<Integer>` before summing areas, ensuring each neighboring island is counted at most once (R-6).
63+
64+
**Rationale:**
65+
- A water cell can be adjacent to the same large island on two or more sides; summing without deduplication would double-count that island's area.
66+
- A small fixed-size set (at most 4 entries) — allocation cost is negligible.
67+
68+
**Alternatives considered:**
69+
- *Bitmask deduplication* — works only if IDs are small enough; fragile and less readable; rejected.
70+
71+
---
72+
73+
### D4: All-Land Early Exit
74+
75+
**Choice:** Track `maxArea` during Pass 1. After Pass 1, if no `0` cells exist, return `maxArea` immediately (which equals n² for a fully connected grid, R-2).
76+
77+
**Rationale:**
78+
- Avoids an unnecessary Pass 2 scan when no flip is possible.
79+
- Naturally falls out of the Pass 1 loop with no special code path.
80+
81+
---
82+
83+
## Risks / Trade-offs
84+
85+
| Risk | Impact | Mitigation |
86+
|------|--------|------------|
87+
| ID collision with `0` or `1` sentinels | Wrong label / wrong area | IDs start at `2`; guarded by `nextId` counter initialised to `2` |
88+
| Integer overflow on area sum for n=500 | Wrong max (all-land = 250 000, fits in int) | n²=250 000 ≪ Integer.MAX_VALUE — no overflow risk |
89+
| Water cell with no land neighbors | Returns 1 (the flip itself) | Correct — an isolated flipped cell is an island of area 1 |
90+
91+
## Architecture
92+
93+
```
94+
largestIsland(int[][] grid)
95+
96+
├─ guard: null / empty → return 0 (R-3)
97+
98+
├─ PASS 1 — Label islands
99+
│ nextId = 2, areaById = new HashMap
100+
│ for each cell (r, c):
101+
│ if grid[r][c] == 1:
102+
│ area = bfs(r, c, nextId, grid) (D1, D2)
103+
│ areaById.put(nextId, area)
104+
│ maxArea = max(maxArea, area)
105+
│ nextId++
106+
107+
├─ if no zeros found during Pass 1 → return maxArea (D4, R-2)
108+
109+
└─ PASS 2 — Probe water cells
110+
for each cell (r, c) where grid[r][c] == 0:
111+
neighbors = distinct island IDs in 4 directions (D3, R-6)
112+
candidate = 1 + sum(areaById.get(id) for id in neighbors)
113+
maxArea = max(maxArea, candidate) (R-1)
114+
115+
return maxArea
116+
```
117+
118+
## Migration Plan
119+
120+
New feature — no existing APIs are changed.
121+
122+
## Open Questions
123+
124+
- [ ] Package placement: `org.example` (consistent with `IslandSolver`) — confirm once reviewed.
125+
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
# Proposal: making-a-large-island
2+
3+
## Why
4+
5+
A natural follow-up to the "biggest island" problem: given that you can flip exactly one water cell to land, what is the maximum island size achievable? This extends the matrix-traversal library with a more challenging variant that requires island labeling and neighbor-merging logic.
6+
7+
## What Changes
8+
9+
Given an n×n binary matrix, flip **at most one** `0` to `1`, then return the area of the largest island after that flip. If no `0` exists the grid is already all land — return n².
10+
11+
- New public method `int largestIsland(int[][] grid)` on a new class `LargeIslandSolver`.
12+
- Two-pass approach: label islands by ID, then probe each water cell for the best flip.
13+
- Input grid **may be mutated** during traversal (cells are labelled in-place with island IDs).
14+
15+
**Constraints (per problem definition):**
16+
- `n == grid.length == grid[i].length`
17+
- `1 <= n <= 500`
18+
- `grid[i][j]` is `0` or `1`
19+
20+
## Capabilities
21+
22+
### New Capabilities
23+
- `large-island-expansion`: Two-pass island-labeling + water-cell probing that computes the maximum island area achievable by flipping at most one `0`.
24+
25+
### Modified Capabilities
26+
<!-- none -->
27+
28+
## Impact
29+
30+
- New source file: `src/main/java/org/example/LargeIslandSolver.java`
31+
- New test file: `src/test/java/org/example/LargeIslandSolverTest.java`
32+
- No changes to existing classes or APIs.
33+
- No new external dependencies.
34+
35+
## Acceptance Criteria
36+
37+
- [ ] `largestIsland` returns the correct answer when flipping one `0` merges two islands.
38+
- [ ] `largestIsland` returns the correct answer when flipping one `0` extends one island.
39+
- [ ] `largestIsland` returns n² when the grid is all `1`s (no flip possible).
40+
- [ ] `largestIsland` returns `1` for a 1×1 grid of `0` (flip the only cell).
41+
- [ ] `largestIsland` handles `null` or empty input by returning `0`.
42+
- [ ] The same water cell is never counted twice when its neighbors belong to the same island.
43+
- [ ] All unit tests pass (`mvn test`).
44+

0 commit comments

Comments
 (0)