Water in stage 4 stops tanks; forest is its opposite - tanks and shells both pass, but the canopy draws over the tanks so anything sitting in it is hidden. It needs no collision change at all, only a draw pass after the tanks. The enemy roster ends on a commander: 3 hits to kill, faster, fires more often, worth 300. Kinds carry a worth so the score follows the roster rather than a flat 100. Crates here can also grant 10s of 3-shot, faster fire, and five enemies hold the field at once. Enemy pips shrank to 9px so twelve of them still leave the label room. Checked headlessly: stage 5 keeps 125 reachable cells and leaves the base standing in 6 of 10 idle runs; the power badge fires from a crate; and with the roster reordered in memory the commander pays 300 against 100 for the rest. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
8.2 KiB
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) | 물길 + 보급 상자(시간 정지 / 기지 강화) |
| ↳ | /tank.html#5 |
5단계 최후 방어선 | 12대 (동시 5) | 숲(은폐) + 화력 강화 상자 + 지휘 탱크 |
방향키 이동, 스페이스 발사. 한 단계를 돌파하면 다음 단계로 이어지고, 점수와 남은 탱크를 그대로 가져간다. 단계를 거치지 않고 바로 들어가는 방법은 셋이다.
- 왼쪽 탭의
휴게실 > 탱크게임아래 단계 칩 (셸 주소로는/#tank2…/#tank5) - 게임 화면 오른쪽 아래의 단계 버튼, 또는
1~5키 - 주소 직접 입력 —
/tank.html#5,/tank.html?stage=5(범위를 벗어난 값은 1단계)
특수 요소
3단계까지는 지형만 달라지지만 4단계부터는 규칙이 하나씩 붙는다. 단계에 들어갈 때
시작 화면 세 번째 줄과 오른쪽 특수 요소 칸에 그 단계에 걸린 규칙이 나오고, 앞
단계를 돌파한 화면에서는 다음 단계의 규칙을 미리 알려 준다.
| 요소 | 동작 | 쓰인 단계 |
|---|---|---|
물길 W |
탱크는 건널 수 없고 포탄은 넘어간다. 건너려면 도선(ford)으로 돌아야 한다 | 4 |
숲 F |
탱크도 포탄도 지나가지만 잎이 탱크를 덮어 가린다. 매복이 된다 | 5 |
| 보급 상자 | 일정 주기로 빈 칸에 하나 놓인다. 밟으면 효과 + 50점 | 4, 5 |
| ↳ 시간 정지 | 적 전원이 3초간 멈춘다 (포탄은 계속 난다) | 4, 5 |
| ↳ 기지 강화 | 기지 외곽 5칸이 10초간 강철이 된다. 끝나면 벽돌로 돌아오며 부서졌던 칸도 메워진다 | 4, 5 |
| ↳ 화력 강화 | 10초간 동시 3발 + 탄속 1.35배 | 5 |
| 지휘 탱크 | 편성 마지막에 나오는 대장. 체력 3, 빠르고 연사가 잦다. 격파 300점 | 5 |
효과가 걸린 동안에는 화면 왼쪽 위에 남은 시간이 배지로 표시된다.
낱말풀이와 공유하는 자산이 없는 단독 페이지다. 캔버스와 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 로 파일만 내려주면 끝.
편을 추가할 때
site/<편>.html을 기존 편 하나를 복사해 만든다. 고칠 곳은<title>, 머리말(.eyebrow/h1), 그리고const PUZZLE = {...}뿐이다.site/index.html의PAGES객체와.nav링크에 한 줄씩 추가한다.
PUZZLE 규약:
size— 격자 한 변 (9)grid[r][c]— 정답 음절, 빈 칸은nullnums[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)을 돌리면 아래가 순서대로 진행된다.
- gitea
plpark/crosswordmain 브랜치 체크아웃 gitea.plpark.ectech.co.kr/plpark/crossword:<BUILD_NUMBER>빌드 & 푸시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 빌드. 그 외 변경 필요 없음.