plan: caro simplification - remove landlords code, implement gomoku flow

5-phase plan to strip card game remnants, clean enums/entities,
rewrite server/client event handlers for Gomoku, and verify build.
This commit is contained in:
2026-04-09 17:10:17 +07:00
parent 1ea2b3d118
commit 0d71b39f60
6 changed files with 642 additions and 0 deletions
@@ -0,0 +1,80 @@
## Phase 1: Delete Dead Files
### Context Links
- [Plan overview](./plan.md)
### Overview
- **Priority:** P1 (do first -- unblocks everything)
- **Status:** Pending
- **Effort:** 30m
Pure deletion phase. No logic changes. Every file listed is either landlords-specific code or obsolete documentation.
### Files to Delete
**Common module -- Poker domain:**
- `landlords-common/src/main/java/org/nico/ratel/landlords/entity/Poker.java`
- `landlords-common/src/main/java/org/nico/ratel/landlords/entity/PokerSell.java`
- `landlords-common/src/main/java/org/nico/ratel/landlords/enums/PokerLevel.java`
- `landlords-common/src/main/java/org/nico/ratel/landlords/enums/PokerType.java`
- `landlords-common/src/main/java/org/nico/ratel/landlords/helper/PokerHelper.java`
- `landlords-common/src/main/java/org/nico/ratel/landlords/utils/LastCardsUtils.java`
**Common module -- Old robot system:**
- `landlords-common/src/main/java/org/nico/ratel/landlords/robot/AbstractRobotDecisionMakers.java`
- `landlords-common/src/main/java/org/nico/ratel/landlords/robot/EasyRobotDecisionMakers.java`
- `landlords-common/src/main/java/org/nico/ratel/landlords/robot/MediumRobotDecisionMakers.java`
- `landlords-common/src/main/java/org/nico/ratel/landlords/robot/RobotDecisionMakers.java`
**Common module -- Tests:**
- `landlords-common/src/test/java/org/nico/ratel/landlords/helper/tests/PokerHelperTest.java`
- `landlords-common/src/test/java/org/nico/ratel/landlords/robot/tests/MediumRobotDecisionMakersTests.java`
**Common module -- Chinese i18n:**
- `landlords-common/src/main/resources/messages_zh_CN.properties`
**Server module -- Landlord event handlers (6 files):**
- `landlords-server/.../event/ServerEventListener_CODE_GAME_LANDLORD_ELECT.java`
- `landlords-server/.../event/ServerEventListener_CODE_GAME_POKER_PLAY.java`
- `landlords-server/.../event/ServerEventListener_CODE_GAME_POKER_PLAY_PASS.java`
- `landlords-server/.../event/ServerEventListener_CODE_GAME_POKER_PLAY_REDIRECT.java`
- `landlords-server/.../robot/RobotEventListener_CODE_GAME_LANDLORD_ELECT.java`
- `landlords-server/.../robot/RobotEventListener_CODE_GAME_POKER_PLAY.java`
**Client module -- Landlord event handlers (12 files):**
- `landlords-client/.../event/ClientEventListener_CODE_GAME_LANDLORD_CONFIRM.java`
- `landlords-client/.../event/ClientEventListener_CODE_GAME_LANDLORD_CYCLE.java`
- `landlords-client/.../event/ClientEventListener_CODE_GAME_LANDLORD_ELECT.java`
- `landlords-client/.../event/ClientEventListener_CODE_GAME_POKER_PLAY.java`
- `landlords-client/.../event/ClientEventListener_CODE_GAME_POKER_PLAY_CANT_PASS.java`
- `landlords-client/.../event/ClientEventListener_CODE_GAME_POKER_PLAY_INVALID.java`
- `landlords-client/.../event/ClientEventListener_CODE_GAME_POKER_PLAY_LESS.java`
- `landlords-client/.../event/ClientEventListener_CODE_GAME_POKER_PLAY_MISMATCH.java`
- `landlords-client/.../event/ClientEventListener_CODE_GAME_POKER_PLAY_ORDER_ERROR.java`
- `landlords-client/.../event/ClientEventListener_CODE_GAME_POKER_PLAY_PASS.java`
- `landlords-client/.../event/ClientEventListener_CODE_GAME_POKER_PLAY_REDIRECT.java`
- `landlords-client/.../event/ClientEventListener_CODE_SHOW_POKERS.java`
**Root-level files:**
- `GOMOKU_CONVERSION_SUMMARY.md`
- `PROTOCO_CN.md`
- `UPDATE.md`
- `serverlist.json`
- `docker/` (entire directory)
### Total: ~35 files/dirs deleted
### Implementation Steps
1. Delete all files listed above via `git rm`
2. Run `mvn compile` -- expect failures (imports of deleted classes). Those are fixed in Phase 2.
3. Commit: `refactor: delete all landlords card game code and obsolete docs`
### Risk Assessment
- **Risk:** Accidentally delete a file still referenced by kept code
- **Mitigation:** Phase 2 explicitly fixes all broken imports. Compile verification in Phase 5.
- **Likelihood:** Low (all files audited against grep results)
### Success Criteria
- [ ] All listed files removed from repo
- [ ] No landlord/poker .java files remain
- [ ] Commit is clean and focused
@@ -0,0 +1,92 @@
## Phase 2: Clean Shared Code (Common Module)
### Context Links
- [Plan overview](./plan.md)
- [Phase 1](./phase-01-delete-dead-files.md) (must complete first)
### Overview
- **Priority:** P1
- **Status:** Pending
- **Effort:** 1h
- **Blocked by:** Phase 1
Remove all landlords references from shared entities, enums, helpers, and printers. After this phase, the common module compiles cleanly with only Gomoku domain code.
### Key Insights
- `Room.java` is already mostly clean (gomoku fields present). But it still has `setCurrentSellClient()` called from server code, and leftover `scoreRate`/`baseScore`/`score` fields.
- `ClientSide.java` has no `pokers` field (already removed) but still has `score`, `scoreInc`, `type` (ClientType = LANDLORD/PEASANT), and linked-list `next`/`pre` fields used for 3-player turn order.
- `ClientEventCode.java` and `ServerEventCode.java` are already cleaned up -- only Gomoku codes remain. No changes needed.
- `ClientRole.java` has `PLAYER, ROBOT, BLACK_PLAYER, WHITE_PLAYER` -- needs simplification.
- `ClientStatus.java` has `CALL_LANDLORD` -- remove it.
- `ClientType.java` (LANDLORD/PEASANT) -- delete entire enum, not applicable to Gomoku.
- `SimplePrinter.java` has `printPokers()` method referencing deleted PokerHelper -- must remove.
- `ClientEventListener.java` (client base class) has `lastPokers`, `lastSellClientNickname`, `lastSellClientType` static fields -- must remove.
### Files to Modify
| File | Changes |
|------|---------|
| `entity/Room.java` | Remove `scoreRate`, `baseScore`, `currentSellClient`, `firstSellClient`, `landlordPokers`, `landlordId`, `lastSellClient` and all their getters/setters. Keep gomoku fields. |
| `entity/ClientSide.java` | Remove `score`, `scoreInc`, `type` (ClientType), `next`, `pre`, `round` fields + getters/setters. Gomoku doesn't need linked-list player chaining (use Room's clientSideMap). |
| `enums/ClientRole.java` | Remove `PLAYER` and `ROBOT`. Keep `BLACK_PLAYER`, `WHITE_PLAYER`. Add `SPECTATOR`. |
| `enums/ClientStatus.java` | Remove `CALL_LANDLORD`. Keep `TO_CHOOSE`, `NO_READY`, `READY`, `WAIT`, `PLAYING`. |
| `enums/ClientType.java` | **Delete entire file** -- no concept of landlord/peasant in Gomoku. |
| `print/SimplePrinter.java` | Remove `printPokers()` method and `import Poker/PokerHelper`. Remove `pokerDisplayFormat` field. |
| `helper/I18nHelper.java` | Verify no reference to `messages_zh_CN.properties` (already handles fallback to en_US). |
### Files to Delete
- `landlords-common/src/main/java/org/nico/ratel/landlords/enums/ClientType.java`
### Implementation Steps
1. **Delete `ClientType.java`**
2. **Clean `Room.java`:**
- Remove fields: `scoreRate`, `baseScore` (and `getScore()`, `initScoreRate()`, `increaseRate()` methods)
- Verify no `landlordPokers`, `currentSellClient`, `firstSellClient`, `lastSellClient`, `landlordId` fields exist (grep confirms Room.java already lacks these -- they exist only in deleted server files that call non-existent setters)
- Actually: grep shows `room.setCurrentSellClient()` called from server files, but Room.java has no such method. These are in files being deleted in Phase 1. **No Room.java changes needed for these.**
- Remove: `scoreRate`, `baseScore`, `getScore()`, `getBaseScore()`, `setBaseScore()`, `getScoreRate()`, `setScoreRate()`, `initScoreRate()`, `increaseRate()` -- these are unused in Gomoku
3. **Clean `ClientSide.java`:**
- Remove `type` field (ClientType) + getter/setter
- Remove `score`, `scoreInc` fields + getter/setter/`addScore()`
- Remove `next`, `pre` fields + getter/setter (3-player circular list not needed for 2-player Gomoku; use Room.clientSideMap)
- Remove `round`, `resetRound()`, `addRound()`, `getRound()`
- Update `init()` to remove references to removed fields
4. **Clean `ClientRole.java`:**
- Remove `PLAYER` and `ROBOT`
- Add `SPECTATOR`
- Result: `BLACK_PLAYER, WHITE_PLAYER, SPECTATOR`
5. **Clean `ClientStatus.java`:**
- Remove `CALL_LANDLORD`
6. **Clean `SimplePrinter.java`:**
- Remove `import org.nico.ratel.landlords.entity.Poker`
- Remove `import org.nico.ratel.landlords.helper.PokerHelper`
- Remove `pokerDisplayFormat` static field
- Remove `printPokers()` method
7. **Clean `ClientEventListener.java` (client module base class):**
- Remove `import org.nico.ratel.landlords.entity.Poker`
- Remove static fields: `lastPokers`, `lastSellClientNickname`, `lastSellClientType`
- Remove `initLastSellInfo()` method
8. Run `mvn compile -pl landlords-common` -- must pass
### Risk Assessment
- **Risk:** Removing `next`/`pre` from ClientSide breaks server event handlers that use circular linked list for turn order
- **Mitigation:** Server handlers are rewritten in Phase 3 to use Room's `isPlayerTurn()` / `currentTurn` instead. Phase 3 must not use `client.getNext()`.
- **Likelihood:** Medium
- **Impact:** Compile error (caught immediately)
### Security Considerations
None -- no auth/data changes.
### Success Criteria
- [ ] `ClientType.java` deleted
- [ ] No `import.*Poker` in any kept file
- [ ] No `score`/`scoreRate`/`baseScore` in Room or ClientSide
- [ ] ClientRole has exactly: `BLACK_PLAYER, WHITE_PLAYER, SPECTATOR`
- [ ] `mvn compile -pl landlords-common` passes
@@ -0,0 +1,164 @@
## Phase 3: Rewrite Server Event Handlers
### Context Links
- [Plan overview](./plan.md)
- [Phase 2](./phase-02-clean-shared-code.md) (must complete first)
- Key domain files: `Board.java`, `GomokuHelper.java`, `GomokuAI.java`, `Room.java`
### Overview
- **Priority:** P1
- **Status:** Pending
- **Effort:** 2h
- **Blocked by:** Phase 2
Rewrite the server-side event handlers to implement Gomoku game flow. Create the missing `ServerEventListener_CODE_GAME_MOVE.java`. Fix existing handlers that still contain landlords logic.
### Data Flow: Gomoku Game Lifecycle
```
Client Server
|-- CODE_ROOM_CREATE ---------->| Create room, assign client as BLACK_PLAYER
|<-- CODE_ROOM_CREATE_SUCCESS --|
| |
|-- CODE_ROOM_JOIN ------------>| Join room, assign as WHITE_PLAYER
|<-- CODE_ROOM_JOIN_SUCCESS ----| (to both players)
| | Auto-start: call CODE_GAME_STARTING
|<-- CODE_GAME_STARTING --------| (to both players: board state, who is black/white)
| |
|-- CODE_GAME_MOVE ------------>| Validate move via GomokuHelper
|<-- CODE_GAME_MOVE_SUCCESS ----| (broadcast to both + spectators)
| or CODE_GAME_MOVE_INVALID |
| or CODE_GAME_MOVE_OCCUPIED |
| or CODE_GAME_MOVE_NOT_YOUR_TURN |
| |
|<-- CODE_GAME_OVER -----------| (when GomokuHelper detects win/draw)
```
### Files to Create
**`ServerEventListener_CODE_GAME_MOVE.java`** -- the core missing handler
```
Package: org.nico.ratel.landlords.server.event
```
Logic:
1. Parse `data` as JSON: `{ "row": int, "col": int }`
2. Get room from `ServerContains.getRoom(clientSide.getRoomId())`
3. Null-check room -> push `CODE_ROOM_PLAY_FAIL_BY_INEXIST`
4. Check `room.isPlayerTurn(clientSide.getId())` -> if false, push `CODE_GAME_MOVE_NOT_YOUR_TURN`
5. Check `room.getGameBoard().isValidMove(row, col)`:
- Out of bounds -> `CODE_GAME_MOVE_OUT_OF_BOUNDS`
- Position occupied -> `CODE_GAME_MOVE_OCCUPIED`
6. Call `GomokuHelper.makeMove(room, row, col, clientSide.getId())`
7. Build result JSON: `{ row, col, piece, playerId, playerNickname, nextPlayerId }`
8. Broadcast `CODE_GAME_MOVE_SUCCESS` to all players + spectators
9. Check `GomokuHelper.isGameOver(room)`:
- If yes, determine winner, broadcast `CODE_GAME_OVER` with `{ result, winnerNickname, board }`
- For PVE: if next turn is AI, trigger AI move via `GomokuAI.getNextMove()` and recurse
**`RobotEventListener_CODE_GAME_MOVE.java`** -- AI move handler for PVE
```
Package: org.nico.ratel.landlords.server.robot
```
Logic:
1. Get room, get AI piece color from room
2. Call `GomokuAI.getNextMove(board, difficulty)`
3. Delegate to `ServerEventListener_CODE_GAME_MOVE` with the AI's move data
### Files to Rewrite
**`ServerEventListener_CODE_GAME_STARTING.java`** -- Currently distributes poker cards. Rewrite for Gomoku:
1. Get room
2. Assign players: first player = BLACK, second = WHITE
3. Set `room.setBlackPlayerId(first.getId())`, `room.setWhitePlayerId(second.getId())`
4. Set player roles: `first.setRole(ClientRole.BLACK_PLAYER)`, `second.setRole(ClientRole.WHITE_PLAYER)`
5. Set `room.setStatus(RoomStatus.STARTING)`
6. Set `room.setCurrentTurn(PieceType.BLACK)`
7. Reset board: `room.getGameBoard().reset()`
8. Build result JSON: `{ roomId, blackPlayer: {id, nickname}, whitePlayer: {id, nickname}, boardSize: 15 }`
9. Push `CODE_GAME_STARTING` to both players + spectators
10. For PVE: if AI is BLACK, trigger AI first move
**`ServerEventListener_CODE_GAME_READY.java`** -- Currently checks for 3 players and uses `ClientRole.PLAYER`. Rewrite:
1. Change player count check from 3 to 2
2. Replace `ClientRole.PLAYER` references with check for non-null channel (human player)
3. Remove Chinese log messages (`"房间状态"`, `"玩家状态"`)
4. When all ready, call `CODE_GAME_STARTING`
**`ServerEventListener_CODE_ROOM_CREATE.java`** -- Currently calls `room.setCurrentSellClient()`. Rewrite:
1. Remove `room.setCurrentSellClient()` call
2. Set first player as `ClientRole.BLACK_PLAYER`
3. Rest is fine
**`ServerEventListener_CODE_ROOM_CREATE_PVE.java`** -- Currently creates 2 robots for 3-player game. Rewrite:
1. Create room with human player
2. Create 1 AI robot (not 2) -- Gomoku is 2-player
3. Assign human as BLACK, AI as WHITE (or configurable)
4. Replace `RobotDecisionMakers.contains()` with simple difficulty range check (1-3)
5. Remove `client.setNext()`/`client.setPre()` linked-list wiring
6. Auto-start game immediately
**`ServerEventListener_CODE_ROOM_JOIN.java`** -- Currently allows up to 3 players. Rewrite:
1. Change full-room check from `size == 3` to `size == 2`
2. Remove `next`/`pre` linked-list wiring
3. When 2nd player joins, auto-start game (call `CODE_GAME_STARTING`)
4. Remove Chinese comments
**`ServerEventListener_CODE_CLIENT_EXIT.java`** -- Currently uses `ClientRole.PLAYER`. Update:
1. Replace `ClientRole.PLAYER` with check against `BLACK_PLAYER`/`WHITE_PLAYER`
2. Remove Chinese comments
**`RoomClearTask.java`** -- Heavy landlords logic (robot substitution, poker custody). Rewrite:
1. Keep timeout-based room cleanup (waitingStatusInterval, liveTime)
2. Remove all robot-substitution logic (lines 76-130)
3. Remove references to `currentSellClient`, `lastSellClient`, `landlordId`, `setPokers`, `setType`
4. On timeout: just close the room and notify players
5. Remove `RobotEventListener` import and call
**`RobotEventListener.java`** (interface) -- Keep but will only resolve `CODE_GAME_MOVE`:
1. No structural change needed, reflection-based lookup still works
### Files to Verify (minor touch-ups)
- `ServerEventListener_CODE_GAME_WATCH.java` -- likely uses Chinese comments, remove them
- `ServerEventListener_CODE_GAME_WATCH_EXIT.java` -- same
- `ServerEventListener_CODE_CLIENT_OFFLINE.java` -- verify no poker references
- `ServerEventListener_CODE_CLIENT_INFO_SET.java` -- verify clean
- `ServerEventListener_CODE_CLIENT_NICKNAME_SET.java` -- verify clean
- `ServerEventListener_CODE_GET_ROOMS.java` -- verify clean
### Architecture: PVE Move Flow
```
Human makes move -> ServerEventListener_CODE_GAME_MOVE
-> validate + apply move
-> check game over?
-> if not over && next turn is AI:
-> GomokuAI.getNextMove(board, difficulty)
-> apply AI move to board via GomokuHelper
-> broadcast AI move as CODE_GAME_MOVE_SUCCESS
-> check game over again
```
No separate robot event listener needed for moves -- handle AI inline in CODE_GAME_MOVE handler to avoid complexity. Delete RobotEventListener_CODE_GAME_MOVE if created, or simply don't create it.
**Revised approach:** Handle AI response inline in `ServerEventListener_CODE_GAME_MOVE.java` rather than via separate RobotEventListener. Simpler, fewer files, same behavior.
### Risk Assessment
| Risk | Likelihood | Impact | Mitigation |
|------|-----------|--------|------------|
| Breaking room join flow (2 vs 3 players) | Medium | High | Unit test: create room, join, verify auto-start |
| AI move infinite loop (AI triggers AI) | Low | High | Guard: only trigger AI if current turn belongs to AI player, and game not over |
| Race condition on concurrent moves | Low | Medium | Room operations already single-threaded per room via Netty event loop |
### Success Criteria
- [ ] `ServerEventListener_CODE_GAME_MOVE.java` exists and handles move validation + win detection
- [ ] PVE mode creates 1 AI robot, not 2
- [ ] Room join auto-starts at 2 players
- [ ] No references to Poker, PokerSell, PokerHelper, LastCardsUtils in any server file
- [ ] No Chinese text in any server file (except LICENSE)
- [ ] `mvn compile -pl landlords-server` passes
@@ -0,0 +1,144 @@
## Phase 4: Rewrite Client Event Handlers
### Context Links
- [Plan overview](./plan.md)
- [Phase 2](./phase-02-clean-shared-code.md) (must complete first)
- Can run **in parallel** with Phase 3 (no file overlap)
### Overview
- **Priority:** P1
- **Status:** Pending
- **Effort:** 1.5h
- **Blocked by:** Phase 2
Rewrite client-side event handlers to display Gomoku game state. The client is a CLI app -- it reads server events and prints board/prompts to the console, then sends user input back.
### Files to Rewrite
**`ClientEventListener_CODE_GAME_STARTING.java`** -- Currently prints poker cards. Rewrite:
1. Parse server data: `{ roomId, blackPlayer: {id, nickname}, whitePlayer: {id, nickname}, boardSize }`
2. Print: "Game starting! You are [BLACK/WHITE]"
3. Print initial empty board via `GomokuHelper.formatBoardForDisplay()`
4. If player is BLACK, prompt for first move
5. Remove all Poker imports and references
**`ClientEventListener_CODE_GAME_OVER.java`** -- Currently shows poker scores. Rewrite:
1. Parse: `{ result, winnerNickname, board }`
2. Print final board state
3. Print result: "Black wins!" / "White wins!" / "Draw!" via `GomokuHelper.getWinnerMessage()`
4. Remove score display logic
5. Call `ClientEventListener_CODE_GAME_READY.gameReady(channel)` to offer rematch
**`ClientEventListener_CODE_SHOW_OPTIONS_PVE.java`** -- Currently calls `initLastSellInfo()`. Update:
1. Remove `initLastSellInfo()` call (method deleted in Phase 2)
2. Keep difficulty selection (Easy/Medium/Hard maps to 1/2/3)
3. Rest is clean
**`ClientEventListener_CODE_SHOW_OPTIONS_SETTING.java`** -- Check for poker display format references:
1. Remove any `pokerDisplayFormat` references
2. Keep language selection if present
**New: `ClientEventListener_CODE_GAME_MOVE_SUCCESS.java`** -- Handle successful move broadcast:
1. Parse: `{ row, col, piece, playerNickname, nextPlayerId }`
2. Print: "[playerNickname] placed [BLACK/WHITE] at (row, col)"
3. Print updated board via `GomokuHelper.formatBoardForDisplay()`
4. If it's this player's turn next, prompt for move input
5. Read input as "row,col", send `CODE_GAME_MOVE` to server with `{ row, col }`
**New: `ClientEventListener_CODE_GAME_MOVE_INVALID.java`** -- Handle invalid move:
1. Print "Invalid move. Please try again."
2. Re-prompt for move input
**New: `ClientEventListener_CODE_GAME_MOVE_OCCUPIED.java`** -- Handle occupied position:
1. Print "Position already occupied. Please choose another."
2. Re-prompt for move input
**New: `ClientEventListener_CODE_GAME_MOVE_OUT_OF_BOUNDS.java`** -- Handle out of bounds:
1. Print "Move out of bounds. Board is 15x15 (0-14)."
2. Re-prompt for move input
**New: `ClientEventListener_CODE_GAME_MOVE_NOT_YOUR_TURN.java`** -- Handle wrong turn:
1. Print "It's not your turn. Please wait."
**New: `ClientEventListener_CODE_SHOW_BOARD.java`** -- Handle board display request:
1. This is a client-only code; may need special handling
2. Or simply handle "board" command locally in the move input loop
### Files Unchanged (already clean)
- `ClientEventListener_CODE_CLIENT_CONNECT.java`
- `ClientEventListener_CODE_CLIENT_EXIT.java`
- `ClientEventListener_CODE_CLIENT_KICK.java`
- `ClientEventListener_CODE_CLIENT_NICKNAME_SET.java`
- `ClientEventListener_CODE_ROOM_CREATE_SUCCESS.java`
- `ClientEventListener_CODE_ROOM_JOIN_SUCCESS.java`
- `ClientEventListener_CODE_ROOM_JOIN_FAIL_BY_FULL.java`
- `ClientEventListener_CODE_ROOM_JOIN_FAIL_BY_INEXIST.java`
- `ClientEventListener_CODE_ROOM_PLAY_FAIL_BY_INEXIST.java`
- `ClientEventListener_CODE_SHOW_OPTIONS.java`
- `ClientEventListener_CODE_SHOW_OPTIONS_PVP.java`
- `ClientEventListener_CODE_SHOW_ROOMS.java`
- `ClientEventListener_CODE_PVE_DIFFICULTY_NOT_SUPPORT.java`
- `ClientEventListener_CODE_GAME_READY.java`
- `ClientEventListener_CODE_GAME_WATCH.java`
- `ClientEventListener_CODE_GAME_WATCH_SUCCESSFUL.java`
### Move Input Pattern
The client prompts for a move and sends it to server. Pattern used in new handlers:
```java
String input = SimpleWriter.write(nickname, "move");
// Parse "row,col" format
// Handle special commands: "board"/"b", "history"/"h", "exit"/"e"
if (input matches "\\d+,\\d+") {
String moveData = MapHelper.newInstance()
.put("row", row).put("col", col).json();
pushToServer(channel, ServerEventCode.CODE_GAME_MOVE, moveData);
} else if (input is "board" or "b") {
// print board locally (need to store board state client-side or request from server)
} else if (input is "exit" or "e") {
pushToServer(channel, ServerEventCode.CODE_CLIENT_EXIT, null);
}
```
**Key decision:** Store board state client-side (in a static field on the listener or a shared client state object) so "board" and "history" commands work without server round-trip.
### Client-Side State
Add a simple static state holder (or use existing `User.java`):
```java
// In ClientEventListener or a new small class
static Board localBoard = null;
static PieceType myPiece = null;
static String myNickname = null;
```
Set these in `CODE_GAME_STARTING` handler. Update board in `CODE_GAME_MOVE_SUCCESS` handler.
### Files to Verify (Chinese text removal)
- `ClientEventListener_CODE_GAME_WATCH.java` -- grep found Chinese; remove
- `ClientEventListener_CODE_GAME_WATCH_SUCCESSFUL.java` -- check
### SimpleClient.java Changes
- Remove `serverAddressSource` array (fetches from upstream ratel repo)
- When no `-h` flag provided, print error asking user to specify host instead of fetching server list
- Remove `getServerAddressList()` method
- Keep language selection logic (only en_US matters now)
### Risk Assessment
| Risk | Likelihood | Impact | Mitigation |
|------|-----------|--------|------------|
| Move input parsing errors (non "row,col" format) | Medium | Low | Validate format, re-prompt on bad input |
| Client board state out of sync with server | Low | Medium | Server is authoritative; client board is display-only. Re-sync on each CODE_GAME_MOVE_SUCCESS |
| Blocking on SimpleWriter.write() during opponent's turn | Low | Low | Existing pattern -- client blocks on stdin. Move prompt only shown when it's player's turn |
### Success Criteria
- [ ] 5 new client event handler files created for Gomoku move codes
- [ ] `CODE_GAME_STARTING` prints board, not poker cards
- [ ] `CODE_GAME_OVER` shows winner without scores
- [ ] Client can display board and prompt for "row,col" input
- [ ] No Poker imports in any client file
- [ ] No Chinese text in any client file
- [ ] `mvn compile -pl landlords-client` passes
@@ -0,0 +1,116 @@
## Phase 5: Integration Test & Compile Verify
### Context Links
- [Plan overview](./plan.md)
- [Phase 3](./phase-03-rewrite-server-events.md), [Phase 4](./phase-04-rewrite-client-events.md) (must both complete first)
### Overview
- **Priority:** P1
- **Status:** Pending
- **Effort:** 1h
- **Blocked by:** Phase 3, Phase 4
Full build verification, fix remaining compile errors, clean up README, and validate the game flow works end-to-end.
### Implementation Steps
#### 1. Full Maven Build
```bash
mvn clean compile
```
Fix any compile errors iteratively. Common expected issues:
- Missing imports of deleted classes in files not yet touched
- `ClientRole.PLAYER` references in files outside the main event handlers
- `ClientType` references anywhere
- `getNext()`/`getPre()` calls if any kept file uses them
#### 2. Run Existing Tests
```bash
mvn test
```
- `GomokuHelperTest.java` should pass (tests Gomoku logic, no poker deps)
- Deleted tests (PokerHelperTest, MediumRobotDecisionMakersTests) are gone -- no failures from those
#### 3. Add Basic Gomoku Integration Test
Create `landlords-common/src/test/java/org/nico/ratel/landlords/helper/tests/GomokuIntegrationTest.java`:
- Test full game flow in-memory: create Room, assign players, make moves, detect win
- Test draw detection (fill board)
- Test invalid move rejection (occupied, out of bounds, wrong turn)
- Test board reset
#### 4. Clean Up README.md
- Remove references to original ratel/landlords
- Remove badge URLs pointing to ainilili/ratel
- Remove serverlist.json references
- Remove ecosystem links (go-ratel-client etc.) or mark as incompatible
- Remove bilibili video link
- Keep installation instructions, update to reflect current project
- Update game commands section (already correct for Gomoku)
#### 5. Grep Sweep -- Verify No Leftovers
Run these greps to ensure nothing was missed:
```bash
# No poker/landlord references in kept Java files
grep -r "Poker\|PokerSell\|PokerHelper\|PokerLevel\|PokerType" --include="*.java" .
grep -r "landlord\|LANDLORD\|Landlord" --include="*.java" .
grep -r "LastCardsUtils\|lastCards\|lastPokers" --include="*.java" .
# No Chinese characters in Java files
grep -rP "[\x{4e00}-\x{9fff}]" --include="*.java" .
# No references to deleted event codes
grep -r "CODE_GAME_POKER\|CODE_GAME_LANDLORD\|CODE_SHOW_POKERS" --include="*.java" .
```
Allowed exceptions:
- `LandlordException.java` class name -- rename to `GameException.java` or leave (low priority)
- Package names contain `landlords` -- intentionally kept (see plan.md decision #1)
- `LICENSE` file -- keep as-is
#### 6. Verify Game Flow Manually (if time permits)
Start server:
```bash
java -jar landlords-server/target/landlords-server-1.4.0.jar -p 1024
```
Start 2 clients:
```bash
java -jar landlords-client/target/landlords-client-1.4.0.jar -h 127.0.0.1 -p 1024
```
Test flow:
1. Client 1: set nickname, create room
2. Client 2: set nickname, join room
3. Verify game auto-starts, board displays
4. Make alternating moves, verify board updates
5. Play to win condition, verify game over message
### Cleanup Items (Low Priority, Optional)
| Item | Rationale |
|------|-----------|
| Rename `LandlordException` to `GameException` | Cosmetic, low value |
| Rename modules `landlords-*` to `caro-*` | High churn, defer to separate PR |
| Remove `FormatPrinter.java` | Check if used; if not, delete |
| Remove `features/Features.java` | Check if only VERSION constant; if so, keep |
| Simplify `SimpleClient.java` server list fetching | Already addressed in Phase 4 |
### Risk Assessment
| Risk | Likelihood | Impact | Mitigation |
|------|-----------|--------|------------|
| Hidden compile error in untouched file | Medium | Low | Full `mvn compile` catches all |
| GomokuHelperTest relies on deleted code | Low | Low | Test file already reviewed -- uses only Gomoku classes |
| Manual test reveals game flow bug | Medium | Medium | Fix in this phase before merging |
### Success Criteria
- [ ] `mvn clean compile` passes with zero errors
- [ ] `mvn test` passes -- all tests green
- [ ] Grep sweep shows no poker/landlord references in Java code (except package names and LandlordException)
- [ ] No Chinese characters in Java files
- [ ] README reflects current Gomoku project
- [ ] GomokuIntegrationTest covers: valid move, invalid move, win detection, draw detection
@@ -0,0 +1,46 @@
---
title: "Caro/Gomoku Codebase Simplification"
description: "Strip landlords card game code, fix Gomoku game flow end-to-end, simplify to working client-server Gomoku"
status: pending
priority: P1
effort: 6h
branch: master
tags: [cleanup, gomoku, simplification]
created: 2026-04-09
---
# Caro/Gomoku Codebase Simplification
## Overview
Strip all leftover Chinese Landlords card game code from this Netty-based project, leaving a clean Gomoku (Five-in-a-Row) client-server application. The Gomoku domain classes (Board, GameMove, GomokuHelper, GomokuAI, PieceType, GameResult) already exist and are well-implemented. The main gap is the server/client event handlers still run landlords logic.
## Phase Summary
| # | Phase | Status | Effort | Blocked By |
|---|-------|--------|--------|------------|
| 1 | Delete dead files | Pending | 30m | - |
| 2 | Clean shared code (common module) | Pending | 1h | Phase 1 |
| 3 | Rewrite server event handlers | Pending | 2h | Phase 2 |
| 4 | Rewrite client event handlers | Pending | 1.5h | Phase 2 |
| 5 | Integration test & compile verify | Pending | 1h | Phase 3, 4 |
## Phases
- [Phase 1: Delete Dead Files](./phase-01-delete-dead-files.md)
- [Phase 2: Clean Shared Code](./phase-02-clean-shared-code.md)
- [Phase 3: Rewrite Server Event Handlers](./phase-03-rewrite-server-events.md)
- [Phase 4: Rewrite Client Event Handlers](./phase-04-rewrite-client-events.md)
- [Phase 5: Integration Test & Compile Verify](./phase-05-integration-verify.md)
## Key Architectural Decisions
1. **Keep module names as `landlords-*`** -- renaming Maven modules cascades into groupId, package names, imports across every file. High churn, zero functional value. Defer to a separate PR if desired.
2. **Keep WebSocket support** -- already wired, removing adds risk, keeping costs nothing.
3. **2-player rooms** -- Gomoku is 2-player. Change room full check from `size == 3` to `size == 2`. Auto-start when second player joins.
4. **Remove scoring system** -- Gomoku has no points/scoring. Strip `score`, `scoreRate`, `baseScore`, `scoreInc` from Room and ClientSide.
5. **PVE uses GomokuAI** -- Replace old robot system with single GomokuAI class. Delete AbstractRobotDecisionMakers, Easy/MediumRobotDecisionMakers, RobotDecisionMakers.
## Rollback Plan
Each phase is a separate commit. `git revert` any phase independently. Phase 1 (file deletion) is fully recoverable from git history.