Files
crossword/README.md
T
plparkandClaude Opus 5 a9bd956c99 Add tank stage 6: mirrors and a raid on the enemy base
Five stages of holding the line; this one lets you attack. An enemy
eagle sits at the top behind steel, and a player shell that reaches it
ends the stage at once for 1000 - enemy shells cannot touch it, so the
stage cannot clear itself.

Reaching it needs the mirrors. A shell entering one turns 90 degrees,
capped at five turns and stepped one cell past the mirror so it cannot
re-enter the same one. The eagle's only opening is a bullet-only shaft
at column 8, walled in steel and fed by a "/" mirror below it, so only
a shot travelling right along row 6 banks up into it; shooting left or
from underneath deflects away. Mirrors block tanks and cannot be broken,
and they block the enemies' line of sight so they do not waste shots.

The clear/win paths were one block inside afterKill; they are now
clearStage(bonus, note) so the snipe and the last kill share them.

Checked headlessly: the snipe lands and pays 1000, the shaft saw 0 tank
samples against 639 bullet samples, 6 idle runs left the enemy eagle
standing, and closing r11's centre brought the base through 6 of 10
idle runs.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-11 17:35:09 +09:00

9.2 KiB
Raw Blame History

crossword

문학 가로세로 낱말풀이 — 정적 사이트. 왼쪽 탭에서 편을 골라 본 프레임에서 바로 푼다. 낱말풀이 목록 아래 휴게실 구획에는 곁들이로 탱크게임을 두었다.

수록 편

탭 경로 격자 항목
한국 문학 /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) 숲(은폐) + 화력 강화 상자 + 지휘 탱크
↳ /tank.html#6 6단계 거울 요새 12대 (동시 4) 반사판(포탄 90° 꺾임) + 적 본진 습격

방향키 이동, 스페이스 발사. 한 단계를 돌파하면 다음 단계로 이어지고, 점수와 남은 탱크를 그대로 가져간다. 단계를 거치지 않고 바로 들어가는 방법은 셋이다.

  • 왼쪽 탭의 휴게실 > 탱크게임 아래 단계 칩 (셸 주소로는 /#tank2 … /#tank6)
  • 게임 화면 오른쪽 아래의 단계 버튼, 또는 1 ~ 6 키
  • 주소 직접 입력 — /tank.html#6, /tank.html?stage=6 (범위를 벗어난 값은 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, 6
반사판 / \ 포탄이 맞으면 90° 꺾인다. 탱크는 못 지나가고 부술 수도 없다. 한 포탄이 5번까지 꺾인다 6
적 본진 X 위쪽 강철 안에 갇힌 적 독수리. 아군 포탄으로 부수면 적을 다 잡지 않아도 즉시 클리어(+1000점). 적 포탄은 제 본진을 부수지 못한다 6

효과가 걸린 동안에는 화면 왼쪽 위에 남은 시간이 배지로 표시된다.

6단계 저격 경로

적 본진 (8,2) 위·좌·우는 강철이고, 아래 (8,3)~(8,5) 는 좌우가 강철로 막힌 탄도 전용 갱도다. 그 아래 (8,6) 에 반사판 / 이 있어, 6행을 따라 오른쪽으로 쏜 포탄만 위로 꺾여 갱도를 타고 본진에 닿는다. 왼쪽으로 쏘면 아래로, 아래에서 위로 쏘면 오른쪽으로 꺾이므로 각을 맞춰야 한다. 아군 출발점 (5,13) 에서 6열을 따라 위로 올라가면 사격 위치가 나온다.

낱말풀이와 공유하는 자산이 없는 단독 페이지다. 캔버스와 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 빌드. 그 외 변경 필요 없음.