Stages so far only varied the walls. Give stage 4 rules of its own. Water blocks tanks but not shells, so the centre of the map is crossed only at the fords while both sides can still shoot across it. It is one predicate - rectBlocked is used for tank movement alone, so adding WATER there leaves bullets untouched. Supply crates appear on a timer and grant a 3s enemy freeze or 10s of steel around the base; the shovel effect puts brick back when it lapses, so broken walls are repaired. Active effects show a countdown badge. Stages carry a specials line: shown on the start overlay and in the HUD on entry, and previewed on the preceding stage's clear screen. Checked headlessly: over 247k sampled tank positions none sat on water while 7591 bullet frames did, crates were taken 8 times in 8 runs, and stage 4 leaves the base standing in 7 of 10 idle runs with 98 cells the player can reach without firing. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
144 lines
7.8 KiB
Markdown
144 lines
7.8 KiB
Markdown
# crossword
|
||
|
||
문학 가로세로 낱말풀이 — 정적 사이트. 왼쪽 탭에서 편을 골라 본 프레임에서 바로 푼다.
|
||
낱말풀이 목록 아래 **휴게실** 구획에는 곁들이로 탱크게임을 두었다.
|
||
|
||
- 공개 주소: https://crossword.plpark.ectech.co.kr
|
||
- 내부 주소: http://192.168.0.216:31380 (NodePort)
|
||
|
||
## 수록 편
|
||
|
||
| 탭 | 경로 | 격자 | 항목 |
|
||
|---|---|---|---|
|
||
| 한국 문학 | `/korean.html` | 9 × 9 · 41칸 | 15 |
|
||
| 러시아 문학 | `/russian.html` | 9 × 9 · 40칸 | 13 |
|
||
| 영국 문학 | `/british.html` | 9 × 9 · 39칸 | 12 |
|
||
| 프랑스 문학 | `/french.html` | 9 × 9 · 40칸 | 15 |
|
||
| 독일 문학 | `/german.html` | 9 × 9 · 41칸 | 14 |
|
||
| 일본 문학 | `/japanese.html` | 9 × 9 · 44칸 | 14 |
|
||
|
||
기본으로 열리는 탭은 한국 문학이다. 각 페이지는 단독으로 열어도 동작한다. 셸(`/`)의 탭은 해시로 상태를 남기므로
|
||
`/#korean`, `/#russian`, `/#british`, `/#french`, `/#german`, `/#japanese`, `/#tank` 로 바로 들어갈 수 있고, 탭을 다시 눌러도 풀던 내용은 유지된다.
|
||
휴게실 탱크게임의 단계별 진입은 `/#tank1`, `/#tank2`, `/#tank3` 이다.
|
||
|
||
## 휴게실
|
||
|
||
| 탭 | 경로 | 단계 | 적 | 특징 |
|
||
|---|---|---|---|---|
|
||
| 탱크게임 | `/tank.html` | 1단계 요새 | 4대 (동시 3) | 벽돌 밀도 25%, 넓은 기동 공간 |
|
||
| ↳ | `/tank.html#2` | 2단계 미로 | 6대 (동시 4) | 밀도 43%, 1칸 폭 통로가 얽힌 미로 |
|
||
| ↳ | `/tank.html#3` | 3단계 강철 회랑 | 8대 (동시 4) | 강철 31칸, 기지 외곽 2중 + 좁은 진입로 |
|
||
| ↳ | `/tank.html#4` | 4단계 참호 | 10대 (동시 4) | **물길** + **보급 상자**(시간 정지 / 기지 강화) |
|
||
|
||
방향키 이동, 스페이스 발사. 한 단계를 돌파하면 다음 단계로 이어지고, 점수와 남은 탱크를
|
||
그대로 가져간다. **단계를 거치지 않고 바로 들어가는 방법**은 셋이다.
|
||
|
||
- 왼쪽 탭의 `휴게실 > 탱크게임` 아래 단계 칩 (셸 주소로는 `/#tank2` … `/#tank4`)
|
||
- 게임 화면 오른쪽 아래의 단계 버튼, 또는 `1` ~ `4` 키
|
||
- 주소 직접 입력 — `/tank.html#4`, `/tank.html?stage=4` (범위를 벗어난 값은 1단계)
|
||
|
||
## 특수 요소
|
||
|
||
3단계까지는 지형만 달라지지만 4단계부터는 규칙이 하나씩 붙는다. 단계에 들어갈 때
|
||
시작 화면 세 번째 줄과 오른쪽 `특수 요소` 칸에 그 단계에 걸린 규칙이 나오고, 앞
|
||
단계를 돌파한 화면에서는 다음 단계의 규칙을 미리 알려 준다.
|
||
|
||
| 요소 | 동작 | 쓰인 단계 |
|
||
|---|---|---|
|
||
| 물길 `W` | 탱크는 건널 수 없고 포탄은 넘어간다. 건너려면 도선(ford)으로 돌아야 한다 | 4 |
|
||
| 보급 상자 | 일정 주기로 빈 칸에 하나 놓인다. 밟으면 효과 + 50점 | 4 |
|
||
| ↳ 시간 정지 | 적 전원이 3초간 멈춘다 (포탄은 계속 난다) | 4 |
|
||
| ↳ 기지 강화 | 기지 외곽 5칸이 10초간 강철이 된다. 끝나면 벽돌로 돌아오며 부서졌던 칸도 메워진다 | 4 |
|
||
|
||
효과가 걸린 동안에는 화면 왼쪽 위에 남은 시간이 배지로 표시된다.
|
||
|
||
낱말풀이와 공유하는 자산이 없는 단독 페이지다. 캔버스와 WebAudio 만 쓰므로 자산 해시
|
||
스탬프(아래 캐시 항목)의 영향도 받지 않는다. iframe 안에서는 **화면을 한 번 클릭해
|
||
포커스를 준 뒤** 키를 눌러야 한다. 셸이 iframe 의 조각(`#2`)만 바꿀 때는 문서가 다시
|
||
읽히지 않으므로, 게임 쪽에서 `hashchange` 를 받아 단계를 바꾼다.
|
||
|
||
## 구성
|
||
|
||
| 경로 | 설명 |
|
||
|---|---|
|
||
| `site/index.html` | 셸 — 왼쪽 탭(좁은 화면에서는 위쪽 탭) + 게임을 띄우는 iframe |
|
||
| `site/korean.html` | 한국 문학 편 (머리말 + 퍼즐 데이터) — 기본 탭 |
|
||
| `site/russian.html` | 러시아 문학 편 (머리말 + 퍼즐 데이터) |
|
||
| `site/british.html` | 영국 문학 편 (머리말 + 퍼즐 데이터) |
|
||
| `site/french.html` | 프랑스 문학 편 (머리말 + 퍼즐 데이터) |
|
||
| `site/german.html` | 독일 문학 편 (머리말 + 퍼즐 데이터) |
|
||
| `site/japanese.html` | 일본 문학 편 (머리말 + 퍼즐 데이터) |
|
||
| `site/tank.html` | 휴게실 · 탱크게임 (단독 페이지 — 캔버스 + WebAudio) |
|
||
| `site/assets/crossword.css` | 각 편이 공유하는 화면 양식 |
|
||
| `site/assets/crossword.js` | 각 편이 공유하는 게임 엔진 (한글 IME 입력, 검사/힌트/타이머) |
|
||
| `nginx/default.conf` | nginx 설정 (8080 리슨, `/healthz` 헬스체크) |
|
||
| `Dockerfile` | 자산 URL 에 내용 해시를 박는 단계 + nginx:1.27-alpine 정적 서빙 |
|
||
| `k8s/deployment.yaml` | Deployment + NodePort Service (31380) |
|
||
| `Jenkinsfile` | Checkout → 이미지 빌드/푸시 → kubectl apply |
|
||
|
||
Spring Boot 등 런타임은 없음. HTML/CSS/JS 만으로 동작하는 완전 정적 사이트라
|
||
nginx 로 파일만 내려주면 끝.
|
||
|
||
## 편을 추가할 때
|
||
|
||
1. `site/<편>.html` 을 기존 편 하나를 복사해 만든다. 고칠 곳은 `<title>`,
|
||
머리말(`.eyebrow` / `h1`), 그리고 `const PUZZLE = {...}` 뿐이다.
|
||
2. `site/index.html` 의 `PAGES` 객체와 `.nav` 링크에 한 줄씩 추가한다.
|
||
|
||
`PUZZLE` 규약:
|
||
|
||
- `size` — 격자 한 변 (9)
|
||
- `grid[r][c]` — 정답 음절, 빈 칸은 `null`
|
||
- `nums[r][c]` — 칸에 찍히는 번호, 없으면 `0`. 길이 2 이상 구간의 시작 칸에
|
||
좌→우 · 위→아래 순서로 붙인다
|
||
- `entries[]` — `{num, dir:"A"|"D", r, c, answer, clue, kind}`
|
||
|
||
**가로·세로로 길이 2 이상 이어지는 모든 구간은 반드시 `entries` 에 단서와 함께
|
||
등록돼 있어야 한다.** 우연히 생긴 두 글자 구간이 남아 있으면 그 칸을 채워도
|
||
완료 판정이 나지 않는 것처럼 보이므로, 격자를 새로 짜면 이 조건을 먼저 확인한다.
|
||
|
||
단서를 쓸 때는 **다른 항목의 정답을 단서 안에 그대로 적지 않는다.** 서로가 서로를
|
||
알려 주면 두 칸이 한꺼번에 풀려 버린다. 한 정답이 다른 정답에 통째로 들어가는 짝
|
||
(`소세키` / `나쓰메소세키`) 도 한 격자에 같이 두지 않는다.
|
||
|
||
낱말 풀은 작품과 작가만으로는 성글다. 특히 원어 표기의 음절이 서로 겹치지 않는
|
||
편(독일 편)에서는 **등장인물과 작품 속 핵심어까지 넣어야** 교차 지점이 생긴다.
|
||
|
||
## 캐시
|
||
|
||
프록시(NPM)가 `js` / `css` 응답에 제 캐시 헤더를 덧붙인다. 원본 nginx 가
|
||
`no-cache` 를 줘도 덮어써지므로, 브라우저가 옛 자산을 계속 쓰는 일을 막기 위해
|
||
**이미지 빌드 때 자산 URL 에 내용 해시를 박는다.**
|
||
|
||
- `Dockerfile` 의 `stamp` 단계가 `crossword.css` + `crossword.js` 의 sha256 앞
|
||
열 자리를 뽑아 각 페이지의 링크를 `assets/crossword.js?v=<해시>` 로 바꾼다
|
||
- 자산 내용이 바뀌면 해시가 바뀌고 URL 도 바뀌므로 캐시가 저절로 무효화된다
|
||
- 저장소의 HTML 은 해시 없는 상태로 두면 된다. 손으로 붙일 필요 없다
|
||
- HTML 자체는 프록시가 캐시하지 않으므로 새 해시가 바로 전달된다
|
||
|
||
## 배포
|
||
|
||
Jenkins 잡(`crossword`)을 돌리면 아래가 순서대로 진행된다.
|
||
|
||
1. gitea `plpark/crossword` main 브랜치 체크아웃
|
||
2. `gitea.plpark.ectech.co.kr/plpark/crossword:<BUILD_NUMBER>` 빌드 & 푸시
|
||
3. `plpark-int-ns` 네임스페이스에 apply, rollout 대기
|
||
|
||
Jenkins credentials 는 기존 loggen/pilot1 과 동일하게
|
||
`gitea-registry`(usernamePassword), `rancher-kubeconfig`(file) 를 사용한다.
|
||
|
||
## 프록시 (NPM)
|
||
|
||
`https://npm.ectech.co.kr` 에서 Proxy Host 추가:
|
||
|
||
- Domain Names: `crossword.plpark.ectech.co.kr`
|
||
- Scheme: `http`
|
||
- Forward Hostname / IP: `192.168.0.216`
|
||
- Forward Port: `31380`
|
||
- Block Common Exploits / Websockets Support: ON
|
||
- SSL: Let's Encrypt 발급 + Force SSL + HTTP/2
|
||
|
||
## 콘텐츠 수정
|
||
|
||
`site/` 아래만 고치고 커밋 → Jenkins 빌드. 그 외 변경 필요 없음.
|