Skip to content

Commit 82fae42

Browse files
committed
feat: full language support and improve tooltip content
- Add English translations for game interface and level descriptions - Update tooltip to display language-specific content - Add detailed level descriptions with difficulty ratings and best solutions - Structure translations into organized format with language and level-specific content - Implement language-aware level names in selector - Update UI text handling to support both languages - Fix tooltip width and styling for better readability The game now fully supports English and Chinese languages with proper translations for all UI elements, tooltips, and level descriptions. Level information includes difficulty ratings and target move counts for each puzzle.
1 parent 8544f25 commit 82fae42

File tree

6 files changed

+237
-50
lines changed

6 files changed

+237
-50
lines changed

index.html

+18-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,14 @@
33
<head>
44
<meta charset="UTF-8" />
55
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6-
<title>华容道 Huarong Dao</title>
6+
<meta name="description" content="华容道 (Huarong Dao), also known as Klotski, is a classic Chinese puzzle game inspired by historical tales. Play now and test your strategy skills!" />
7+
<meta name="keywords" content="Games,Huarong Dao, Klotski, sliding puzzle game, 华容道, 曹操, ancient China" />
8+
<meta name="author" content="Stan Ke" />
9+
<meta property="og:title" content="华容道 - Huarong Dao" />
10+
<meta property="og:description" content="Explore the ancient Chinese sliding block puzzle game, 华容道 (Huarong Dao), based on the historical tale of Cao Cao and Guan Yu." />
11+
<meta property="og:image" content="public/CaocaoFailed.webp" />
12+
<meta property="og:type" content="website" />
13+
<title>华容道 - Huarong Dao</title>
714
<link rel="stylesheet" href="src/styles.css" />
815
</head>
916
<body>
@@ -18,6 +25,16 @@ <h1 style="color: var(--text-color)">华容道 Huarong Dao</h1>
1825
<option value="3">Level 4</option>
1926
<option value="4">Level 5</option>
2027
</select>
28+
29+
</div>
30+
<div class="tooltip-container">
31+
<span class="tooltip-trigger">?</span>
32+
<div class="tooltip-content">
33+
<div class="tooltip-header">华容道 - Huarong Dao</div>
34+
<p> Huarong Dao(Aka. Klotski) is an ancient Chinese puzzle game based on the story of Cao Cao being surrounded after Guandu war. The goal is to solve the puzzle with a minimum number of moves or in a minimum amount of time.
35+
</p>
36+
<div id="levelDescription"></div>
37+
</div>
2138
</div>
2239
<div class="language-selector">
2340
<select id="languageSelector">

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "klotski",
33
"private": true,
4-
"version": "0.0.0",
4+
"version": "0.1.0",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",

src/game.ts

+36
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,12 @@ export class HuarongGame {
7575
this.initializeGame();
7676
this.initializeLevelSelector();
7777
this.initializeLanguageSelector();
78+
this.initializeHintSystem();
79+
}
80+
81+
private initializeHintSystem() {
82+
const levelDesc = document.getElementById("levelDescription")!;
83+
levelDesc.innerHTML = `<p>${LEVELS[this.currentLevel].description}</p>`;
7884
}
7985

8086
async promptForUserId(): Promise<string | null> {
@@ -135,6 +141,29 @@ export class HuarongGame {
135141
document.querySelector('h1')!.textContent = t.title;
136142
document.getElementById('resetBtn')!.textContent = t.reset;
137143
document.getElementById('undoBtn')!.textContent = t.undo;
144+
145+
// Update tooltip content
146+
const tooltipHeader = document.querySelector('.tooltip-header')!;
147+
const tooltipIntro = document.querySelector('.tooltip-content > p')!;
148+
const levelDesc = document.getElementById('levelDescription')!;
149+
150+
tooltipHeader.textContent = t.title;
151+
tooltipIntro.textContent = t.gameIntro;
152+
153+
// Find current level info
154+
const currentLevelInfo = t.levels.find(level => level.id === LEVELS[this.currentLevel].name);
155+
levelDesc.innerHTML = `
156+
<h3>${currentLevelInfo?.name}</h3>
157+
<p>${currentLevelInfo?.description}</p>
158+
`;
159+
160+
// Update level selector options
161+
const levelSelector = document.getElementById('levelSelector') as HTMLSelectElement;
162+
t.levels.forEach((level, index) => {
163+
levelSelector.options[index].text = `${t.level} ${index + 1}: ${level.name}`;
164+
});
165+
166+
// Update stats display
138167
document.querySelector('.stats')!.innerHTML =
139168
`${t.moves}: <span id="moveCount">${this.moves}</span> | ${t.best}: <span id="bestScore">${this.bestScore}</span>`;
140169
}
@@ -167,6 +196,13 @@ export class HuarongGame {
167196
}
168197
this.updateStats();
169198
this.renderPieces();
199+
const t = translations[this.language];
200+
const currentLevelInfo = t.levels.find(level => level.id === LEVELS[this.currentLevel].name);
201+
const levelDesc = document.getElementById("levelDescription")!;
202+
levelDesc.innerHTML = `
203+
<h3>${currentLevelInfo?.name}</h3>
204+
<p>${currentLevelInfo?.description}</p>
205+
`;
170206
}
171207

172208
createBoard() {

src/gameLayout.ts

+35-45
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// <!-- 前六个关卡(横刀立马、指挥若定、将撞曹营、齐头并进、兵分三路、雨声淅沥) -->
1+
// <!-- 经典五关(横刀立马、指挥若定、水泄不通、四面楚歌、兵分三路) -->
22

33
interface Block {
44
type: "VERTICAL" | "CAO_CAO" | "HORIZONTAL" | "SINGLE";
@@ -9,11 +9,13 @@ interface Block {
99
interface Level {
1010
name: string;
1111
layout: Block[];
12+
description?: string; // Add description field
1213
}
1314

1415
const LEVELS: Level[] = [
1516
{
1617
name: "横刀立马",
18+
description: "相传曹操在赤壁之战后被围困于华容道,关羽放他离去。此局难度:★★★",
1719
layout: [
1820
{ type: "CAO_CAO", x: 1, y: 0 },
1921
{ type: "VERTICAL", x: 0, y: 0 },
@@ -29,6 +31,7 @@ const LEVELS: Level[] = [
2931
},
3032
{
3133
name: "指挥若定",
34+
description: "需要仔细分析每个将领的移动路线。此局难度:★★",
3235
layout: [
3336
{ type: "VERTICAL", x: 0, y: 0 },
3437
{ type: "CAO_CAO", x: 1, y: 0 },
@@ -43,65 +46,52 @@ const LEVELS: Level[] = [
4346
],
4447
},
4548
{
46-
"name": "水泄不通",
47-
"layout": [
48-
{ "type": "VERTICAL", "x": 0, "y": 0 },
49-
{ "type": "CAO_CAO", "x": 1, "y": 0 },
50-
{ "type": "SINGLE", "x": 3, "y": 0 },
51-
{ "type": "SINGLE", "x": 3, "y": 1 },
52-
{ "type": "HORIZONTAL", "x": 0, "y": 2 },
53-
{ "type": "HORIZONTAL", "x": 0, "y": 3 },
54-
{ "type": "HORIZONTAL", "x": 2, "y": 3 },
55-
{ "type": "HORIZONTAL", "x": 2, "y": 2 },
56-
{ "type": "SINGLE", "x": 0, "y": 4 },
57-
{ "type": "SINGLE", "x": 3, "y": 4 },
58-
]
59-
},
60-
{
61-
"name": "四面楚歌",
62-
"layout": [
63-
{ "type": "VERTICAL", "x": 0, "y": 0 },
64-
{ "type": "CAO_CAO", "x": 1, "y": 1 },
65-
{ "type": "VERTICAL", "x": 3, "y": 0 },
66-
{ "type": "VERTICAL", "x": 0, "y": 3 },
67-
{ "type": "VERTICAL", "x": 3, "y": 2 },
68-
{ "type": "HORIZONTAL", "x": 1, "y": 3 },
69-
{ "type": "SINGLE", "x": 0, "y": 2 },
70-
{ "type": "SINGLE", "x": 3, "y": 4 },
71-
{ "type": "SINGLE", "x": 1, "y": 0 },
72-
{ "type": "SINGLE", "x": 2, "y": 0 }
73-
]
74-
},
75-
{
76-
name: "兵分三路",
49+
name: "水泄不通",
50+
description: "四周被重重包围,需要巧妙调度。此局难度:★★★★",
7751
layout: [
7852
{ type: "VERTICAL", x: 0, y: 0 },
7953
{ type: "CAO_CAO", x: 1, y: 0 },
80-
{ type: "VERTICAL", x: 3, y: 0 },
54+
{ type: "SINGLE", x: 3, y: 0 },
55+
{ type: "SINGLE", x: 3, y: 1 },
8156
{ type: "HORIZONTAL", x: 0, y: 2 },
57+
{ type: "HORIZONTAL", x: 0, y: 3 },
58+
{ type: "HORIZONTAL", x: 2, y: 3 },
8259
{ type: "HORIZONTAL", x: 2, y: 2 },
83-
{ type: "SINGLE", x: 0, y: 3 },
84-
{ type: "SINGLE", x: 1, y: 3 },
85-
{ type: "SINGLE", x: 2, y: 3 },
86-
{ type: "SINGLE", x: 3, y: 3 },
8760
{ type: "SINGLE", x: 0, y: 4 },
8861
{ type: "SINGLE", x: 3, y: 4 },
8962
],
9063
},
9164
{
92-
name: "雨声淅沥",
65+
name: "四面楚歌",
66+
description: "四面受敌,需要找到突破口。此局难度:★★★★★",
9367
layout: [
9468
{ type: "VERTICAL", x: 0, y: 0 },
95-
{ type: "CAO_CAO", x: 1, y: 0 },
69+
{ type: "CAO_CAO", x: 1, y: 1 },
9670
{ type: "VERTICAL", x: 3, y: 0 },
97-
{ type: "HORIZONTAL", x: 0, y: 2 },
98-
{ type: "HORIZONTAL", x: 2, y: 2 },
99-
{ type: "SINGLE", x: 0, y: 3 },
71+
{ type: "VERTICAL", x: 0, y: 3 },
72+
{ type: "VERTICAL", x: 3, y: 2 },
73+
{ type: "HORIZONTAL", x: 1, y: 3 },
74+
{ type: "SINGLE", x: 0, y: 2 },
75+
{ type: "SINGLE", x: 3, y: 4 },
76+
{ type: "SINGLE", x: 1, y: 0 },
77+
{ type: "SINGLE", x: 2, y: 0 },
78+
],
79+
},
80+
{
81+
name: "兵分三路",
82+
description: "敌军分散,寻找突破时机。此局难度:★★★",
83+
layout: [
84+
{ type: "VERTICAL", x: 0, y: 1 },
85+
{ type: "CAO_CAO", x: 1, y: 0 },
86+
{ type: "VERTICAL", x: 3, y: 1 },
87+
{ type: "VERTICAL", x: 0, y: 3 },
88+
{ type: "VERTICAL", x: 3, y: 3 },
89+
{ type: "HORIZONTAL", x: 1, y: 2 },
90+
{ type: "SINGLE", x: 0, y: 0 },
10091
{ type: "SINGLE", x: 1, y: 3 },
10192
{ type: "SINGLE", x: 2, y: 3 },
102-
{ type: "SINGLE", x: 3, y: 3 },
103-
{ type: "SINGLE", x: 0, y: 4 },
104-
{ type: "SINGLE", x: 3, y: 4 },
93+
{ type: "SINGLE", x: 3, y: 0 },
94+
10595
],
10696
},
10797
];

src/styles.css

+89-1
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,8 @@ button:hover {
118118
.level-selector {
119119
margin-bottom: 20px;
120120
color: var(--text-color);
121+
display: flex;
122+
align-items: center;
121123
}
122124

123125
.level-selector select {
@@ -153,6 +155,92 @@ button:hover {
153155
.controls-row {
154156
display: flex;
155157
gap: 1rem;
158+
justify-content: space-between;
159+
}
160+
161+
.hint-btn {
162+
margin-left: 8px;
163+
width: 24px;
164+
height: 24px;
165+
border-radius: 12px;
166+
border: none;
167+
background: #666;
168+
color: white;
169+
font-weight: bold;
170+
cursor: pointer;
171+
}
172+
173+
.hint-btn:hover {
174+
background: #888;
175+
}
176+
177+
.tooltip-container {
178+
display: inline-block;
179+
position: relative;
180+
margin-left: 8px;
181+
align-items: center;
182+
}
183+
184+
.tooltip-trigger {
185+
display: inline-flex;
186+
align-items: center;
156187
justify-content: center;
157-
margin-bottom: 1rem;
188+
width: 24px;
189+
height: 24px;
190+
border-radius: 12px;
191+
background: #666;
192+
color: white;
193+
font-weight: bold;
194+
cursor: help;
195+
}
196+
197+
.tooltip-content {
198+
display: none;
199+
position: absolute;
200+
left: 50%;
201+
transform: translateX(-50%);
202+
margin-top: 10px;
203+
width: 200px;
204+
background: var(--board-color);
205+
color: var(--piece-color);
206+
padding: 12px;
207+
border-radius: 6px;
208+
font-size: 14px;
209+
box-shadow: 0 2px 8px rgba(0,0,0,0.15);
210+
z-index: 100;
211+
}
212+
213+
.tooltip-content::before {
214+
content: '';
215+
position: absolute;
216+
top: -6px;
217+
left: 50%;
218+
transform: translateX(-50%);
219+
border-width: 0 6px 6px 6px;
220+
border-style: solid;
221+
border-color: transparent transparent var(--board-color) transparent;
222+
}
223+
224+
.tooltip-header {
225+
font-weight: bold;
226+
margin-bottom: 8px;
227+
}
228+
229+
.tooltip-container:hover .tooltip-content {
230+
display: block;
231+
}
232+
233+
.tooltip-content h3 {
234+
margin: 12px 0 8px;
235+
color: var(--cao-color);
236+
}
237+
238+
.tooltip-content p {
239+
margin: 0 0 8px 0;
240+
line-height: 1.4;
241+
}
242+
243+
.game-intro {
244+
margin-bottom: 20px;
245+
line-height: 1.5;
158246
}

0 commit comments

Comments
 (0)