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>
254 lines
9.4 KiB
HTML
254 lines
9.4 KiB
HTML
<!doctype html>
|
|
<html lang="ko">
|
|
<head>
|
|
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
|
|
<title>가로세로 낱말풀이</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Black+Han+Sans&family=Gothic+A1:wght@300;500;700;900&display=swap" rel="stylesheet">
|
|
<style>
|
|
:root{
|
|
--ink:#12100E;
|
|
--panel:#171410;
|
|
--paper:#E7E1D3;
|
|
--amber:#E8B23C;
|
|
--vermilion:#C6362A;
|
|
--slate:#8A8272;
|
|
--bone:#D8D2C4;
|
|
--line:#2A251F;
|
|
--ui:"Gothic A1","Apple SD Gothic Neo","Malgun Gothic","Noto Sans KR",sans-serif;
|
|
--display:"Black Han Sans","Gothic A1","Apple SD Gothic Neo",sans-serif;
|
|
--rail:238px;
|
|
}
|
|
*{box-sizing:border-box}
|
|
html,body{height:100%}
|
|
body{
|
|
margin:0;background:var(--ink);color:var(--bone);
|
|
font-family:var(--ui);font-weight:300;
|
|
display:grid;grid-template-columns:var(--rail) minmax(0,1fr);
|
|
-webkit-text-size-adjust:100%;
|
|
}
|
|
|
|
/* ---------- 왼쪽 탭 ---------- */
|
|
.rail{
|
|
background:var(--panel);border-right:1px solid var(--line);
|
|
display:flex;flex-direction:column;min-height:0;overflow:auto;
|
|
}
|
|
.brand{padding:24px 20px 20px;border-bottom:2px solid var(--vermilion)}
|
|
.brand .mark{
|
|
font-size:10px;letter-spacing:.3em;color:var(--slate);
|
|
font-weight:700;text-transform:uppercase;margin-bottom:9px;
|
|
}
|
|
.brand h1{
|
|
font-family:var(--display);font-weight:400;margin:0;
|
|
font-size:26px;line-height:1.06;color:var(--paper);letter-spacing:-.01em;
|
|
}
|
|
.brand h1 em{font-style:normal;color:var(--vermilion);display:block}
|
|
|
|
.nav{padding:16px 0;flex:1 0 auto}
|
|
.nav .label{
|
|
font-size:10px;font-weight:900;letter-spacing:.28em;color:var(--slate);
|
|
padding:0 20px 10px;
|
|
}
|
|
.nav .label.rest{
|
|
margin-top:14px;padding-top:15px;border-top:1px solid var(--line);
|
|
}
|
|
.nav .stagejump{
|
|
display:flex;flex-wrap:wrap;gap:6px;padding:9px 17px 11px;
|
|
border-bottom:1px solid #221E19;
|
|
}
|
|
.nav .stagejump a{
|
|
flex:0 0 calc(33.333% - 4px);display:block;padding:6px 0;border:1px solid var(--line);
|
|
border-radius:4px;text-align:center;text-decoration:none;
|
|
font-size:11px;font-weight:700;letter-spacing:.02em;
|
|
color:var(--slate);background:#1A1712;
|
|
}
|
|
.nav .stagejump a:hover{color:var(--amber);background:#221E19}
|
|
.nav .stagejump a[aria-current="page"]{
|
|
background:var(--vermilion);border-color:var(--amber);color:#fff;
|
|
}
|
|
.nav a{
|
|
display:block;text-decoration:none;color:inherit;
|
|
padding:12px 18px 12px 17px;border-left:3px solid transparent;
|
|
border-bottom:1px solid #221E19;transition:background .12s linear;
|
|
}
|
|
.nav a .kr{display:block;font-size:15px;font-weight:700;color:var(--bone)}
|
|
.nav a .og{display:block;font-size:10.5px;letter-spacing:.14em;color:var(--slate);margin-top:3px}
|
|
.nav a:hover{background:#1F1B16}
|
|
.nav a:hover .kr{color:var(--amber)}
|
|
.nav a[aria-current="page"]{background:var(--vermilion);border-left-color:var(--amber)}
|
|
.nav a[aria-current="page"] .kr,
|
|
.nav a[aria-current="page"] .og{color:#fff}
|
|
.nav a:focus-visible{outline:2px solid var(--amber);outline-offset:-2px}
|
|
|
|
.rail-foot{padding:16px 20px 22px;border-top:1px solid var(--line)}
|
|
.rail-foot a{
|
|
font-size:11px;font-weight:700;letter-spacing:.08em;color:var(--slate);
|
|
text-decoration:none;border-bottom:1px solid transparent;
|
|
}
|
|
.rail-foot a:hover{color:var(--amber);border-bottom-color:var(--amber)}
|
|
|
|
/* ---------- 본 프레임 ---------- */
|
|
.stage{border:0;width:100%;height:100%;display:block;background:var(--ink)}
|
|
|
|
/* ---------- 좁은 화면: 위쪽 가로 탭 ---------- */
|
|
@media (max-width:820px){
|
|
body{grid-template-columns:1fr;grid-template-rows:auto minmax(0,1fr)}
|
|
.rail{
|
|
border-right:0;border-bottom:2px solid var(--vermilion);
|
|
flex-direction:row;align-items:stretch;overflow:visible;
|
|
}
|
|
.brand{
|
|
padding:12px 14px;border-bottom:0;border-right:1px solid var(--line);
|
|
display:flex;flex-direction:column;justify-content:center;flex:none;
|
|
}
|
|
.brand .mark{display:none}
|
|
.brand h1{font-size:17px}
|
|
.brand h1 em{display:inline;margin-left:6px}
|
|
.nav{display:flex;padding:0;flex:1 1 auto;min-width:0}
|
|
.nav .label{display:none}
|
|
.nav a{
|
|
flex:1 1 0;min-width:0;display:flex;flex-direction:column;justify-content:center;
|
|
border-bottom:0;border-left:0;border-top:3px solid transparent;
|
|
padding:10px 12px;text-align:center;
|
|
}
|
|
.nav a[aria-current="page"]{border-left-color:transparent;border-top-color:var(--amber)}
|
|
.nav a .kr{font-size:13.5px}
|
|
.nav a .og{display:none}
|
|
.rail-foot{display:none}
|
|
}
|
|
/* 아주 좁은 화면: 탭이 셋 이상이면 상표를 접고 탭에 폭을 몰아 준다 */
|
|
@media (max-width:560px){
|
|
.brand{display:none}
|
|
.nav a{padding:10px 4px}
|
|
.nav a .kr{font-size:13px;white-space:nowrap}
|
|
.nav a .suffix{display:none} /* 탭이 다섯이면 나라 이름만 남긴다 */
|
|
}
|
|
@media (prefers-reduced-motion:reduce){
|
|
*{animation:none !important;transition:none !important}
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
|
|
<nav class="rail">
|
|
<div class="brand">
|
|
<div class="mark">Crossword</div>
|
|
<h1>가로세로<em>낱말풀이</em></h1>
|
|
</div>
|
|
|
|
<div class="nav">
|
|
<div class="label">문학 편</div>
|
|
<a href="korean.html" target="stage" data-key="korean">
|
|
<span class="kr">한국<span class="suffix"> 문학</span></span>
|
|
<span class="og">Korean literature</span>
|
|
</a>
|
|
<a href="russian.html" target="stage" data-key="russian">
|
|
<span class="kr">러시아<span class="suffix"> 문학</span></span>
|
|
<span class="og">Русская литература</span>
|
|
</a>
|
|
<a href="british.html" target="stage" data-key="british">
|
|
<span class="kr">영국<span class="suffix"> 문학</span></span>
|
|
<span class="og">British literature</span>
|
|
</a>
|
|
<a href="french.html" target="stage" data-key="french">
|
|
<span class="kr">프랑스<span class="suffix"> 문학</span></span>
|
|
<span class="og">Littérature française</span>
|
|
</a>
|
|
<a href="german.html" target="stage" data-key="german">
|
|
<span class="kr">독일<span class="suffix"> 문학</span></span>
|
|
<span class="og">Deutsche Literatur</span>
|
|
</a>
|
|
<a href="japanese.html" target="stage" data-key="japanese">
|
|
<span class="kr">일본<span class="suffix"> 문학</span></span>
|
|
<span class="og">日本文学</span>
|
|
</a>
|
|
|
|
<div class="label rest">휴게실</div>
|
|
<a href="tank.html" target="stage" data-key="tank">
|
|
<span class="kr">탱크게임</span>
|
|
<span class="og">Tank Battle</span>
|
|
</a>
|
|
<div class="stagejump">
|
|
<a href="tank.html#1" target="stage" data-key="tank1" title="1단계 요새">1단계</a>
|
|
<a href="tank.html#2" target="stage" data-key="tank2" title="2단계 미로">2단계</a>
|
|
<a href="tank.html#3" target="stage" data-key="tank3" title="3단계 강철 회랑">3단계</a>
|
|
<a href="tank.html#4" target="stage" data-key="tank4" title="4단계 참호 — 물길·보급 상자">4단계</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="rail-foot">
|
|
<a id="popout" href="korean.html" target="_blank" rel="noopener">새 창에서 열기 ↗</a>
|
|
</div>
|
|
</nav>
|
|
|
|
<!-- src 은 아래 스크립트가 해시를 보고 넣는다 (미리 넣으면 두 번 읽어들이게 된다) -->
|
|
<iframe class="stage" id="stage" name="stage" title="낱말풀이 게임판"></iframe>
|
|
|
|
<script>
|
|
(function(){
|
|
"use strict";
|
|
|
|
var PAGES = {
|
|
korean: {file:'korean.html', name:'한국 문학'},
|
|
russian: {file:'russian.html', name:'러시아 문학'},
|
|
british: {file:'british.html', name:'영국 문학'},
|
|
french: {file:'french.html', name:'프랑스 문학'},
|
|
german: {file:'german.html', name:'독일 문학'},
|
|
japanese:{file:'japanese.html', name:'일본 문학'},
|
|
tank: {file:'tank.html', name:'휴게실 · 탱크게임'},
|
|
tank1: {file:'tank.html#1', name:'휴게실 · 탱크게임 1단계'},
|
|
tank2: {file:'tank.html#2', name:'휴게실 · 탱크게임 2단계'},
|
|
tank3: {file:'tank.html#3', name:'휴게실 · 탱크게임 3단계'},
|
|
tank4: {file:'tank.html#4', name:'휴게실 · 탱크게임 4단계'}
|
|
};
|
|
var DEFAULT = 'korean';
|
|
|
|
var stage = document.getElementById('stage');
|
|
var popout = document.getElementById('popout');
|
|
var links = Array.prototype.slice.call(document.querySelectorAll('.nav a[data-key]'));
|
|
|
|
function keyFromHash(){
|
|
var k = decodeURIComponent(String(location.hash || '').replace(/^#\/?/, ''));
|
|
return PAGES[k] ? k : DEFAULT;
|
|
}
|
|
|
|
function apply(){
|
|
var key = keyFromHash();
|
|
var page = PAGES[key];
|
|
|
|
// 같은 페이지면 다시 읽어들이지 않는다 (풀던 내용 유지)
|
|
if (stage.getAttribute('src') !== page.file) stage.setAttribute('src', page.file);
|
|
|
|
links.forEach(function(a){
|
|
if (a.dataset.key === key) a.setAttribute('aria-current', 'page');
|
|
else a.removeAttribute('aria-current');
|
|
});
|
|
|
|
popout.setAttribute('href', page.file);
|
|
document.title = '가로세로 낱말풀이 · ' + page.name;
|
|
}
|
|
|
|
links.forEach(function(a){
|
|
a.addEventListener('click', function(ev){
|
|
if (ev.metaKey || ev.ctrlKey || ev.shiftKey || ev.button !== 0) return;
|
|
ev.preventDefault(); // 스크립트가 살아 있으면 해시로 처리
|
|
var k = a.dataset.key;
|
|
if (keyFromHash() === k && location.hash) return;
|
|
location.hash = '#' + k;
|
|
if (!location.hash) apply(); // 해시 변화가 없을 때를 위한 보험
|
|
});
|
|
});
|
|
|
|
window.addEventListener('hashchange', apply);
|
|
|
|
if (!location.hash) {
|
|
try { history.replaceState(null, '', '#' + DEFAULT); } catch(_) {}
|
|
}
|
|
apply();
|
|
})();
|
|
</script>
|
|
</body>
|
|
</html>
|