Skip to content

Commit 8544f25

Browse files
committed
fix: consistent general names for game pieces and align blocks
 - Add char property to Block interface to store assigned names - Fix character assignment for vertical and horizontal pieces - fix i18n bugs - Reset character indices when changing level or resetting game - Maintain consistent character names during moves - Use stored character names instead of generating new ones on each render  This fixes the issue where piece names would change randomly during gameplay by assigning characters once at initialization and persisting them through the game session.
1 parent aa51616 commit 8544f25

10 files changed

+272
-146
lines changed

flowChart.md

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
graph TD
2+
A[Constructor] -->|1| B[Initialize Game State]
3+
B --> B1[Set Language]
4+
B --> B2[Initialize BLOCKS]
5+
B --> B3[Load Level Layout]
6+
7+
A -->|2| C[initializeGame]
8+
C --> C1[Create Board]
9+
C --> C2[Render Pieces]
10+
C --> C3[Update Stats]
11+
C --> C4[Update UI Text]
12+
C --> C5[Setup Event Listeners]
13+
14+
D[User Interactions] --> D1[Level Selection]
15+
D --> D2[Language Selection]
16+
D --> D3[Piece Click]
17+
D --> D4[Reset Game]
18+
D --> D5[Undo Move]
19+
20+
D3 --> E[handlePieceClick]
21+
E --> F[Get Possible Moves]
22+
F --> G{Multiple Moves?}
23+
G -->|Yes| H[Show Move Options]
24+
G -->|No| I[Move Piece]
25+
H --> I
26+
27+
I --> J[moveToPosition]
28+
J --> J1[Save History]
29+
J --> J2[Update Position]
30+
J --> J3[Update Stats]
31+
J --> J4[Render Pieces]
32+
J --> J5[Check Win]
33+
34+
J5 -->|Win| K[Handle Win]
35+
K --> K1[Prompt UserID]
36+
K1 --> K2[Save Best Score]
37+
K2 --> K3[Reset Game]
38+
39+
D4 --> L[resetGame]
40+
L --> L1[Clear Arrows]
41+
L --> L2[Reset State]
42+
L --> L3[Reset Stats]
43+
L --> L4[Render Pieces]
44+
45+
D5 --> M[undoMove]
46+
M --> M1[Pop History]
47+
M1 --> M2[Update State]
48+
M2 --> M3[Update Stats]
49+
M3 --> M4[Render Pieces]
50+
51+
subgraph Game Loop
52+
E
53+
F
54+
G
55+
H
56+
I
57+
J
58+
end
59+
60+
subgraph Rendering
61+
C2
62+
J4
63+
L4
64+
M4
65+
end
66+
67+
subgraph State Management
68+
J1
69+
J2
70+
L2
71+
M2
72+
end

index.html

+16-8
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,22 @@
99
<body>
1010
<div class="game-container">
1111
<h1 style="color: var(--text-color)">华容道 Huarong Dao</h1>
12-
<div class="level-selector">
13-
<select id="levelSelector">
14-
<option value="0">Level 1</option>
15-
<option value="1">Level 2</option>
16-
<option value="2">Level 3</option>
17-
<option value="3">Level 4</option>
18-
<option value="4">Level 5</option>
19-
</select>
12+
<div class="controls-row">
13+
<div class="level-selector">
14+
<select id="levelSelector">
15+
<option value="0">Level 1</option>
16+
<option value="1">Level 2</option>
17+
<option value="2">Level 3</option>
18+
<option value="3">Level 4</option>
19+
<option value="4">Level 5</option>
20+
</select>
21+
</div>
22+
<div class="language-selector">
23+
<select id="languageSelector">
24+
<option value="en">English</option>
25+
<option value="zh">中文</option>
26+
</select>
27+
</div>
2028
</div>
2129
<div class="board-wrapper">
2230
<div class="board" id="board"></div>

src/counter.ts

-9
This file was deleted.

src/game.ts

+113-30
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import LEVELS from './gameLayout';
1+
import LEVELS from "./gameLayout";
2+
import { translations, Language } from './translations';
23

34
interface Block {
45
type: string;
56
x: number;
67
y: number;
8+
char?: string; // Add this property
79
}
810

911
interface BlockType {
@@ -28,24 +30,57 @@ export class HuarongGame {
2830
private blocks: Block[];
2931
private userId: string | null = null;
3032
private currentLevel: number;
33+
private language: Language = navigator.language.toLowerCase().startsWith('en') ? 'en' : 'zh';
3134

32-
private readonly BLOCKS: { [key: string]: BlockType } = {
33-
CAO_CAO: { width: 2, height: 2, char: "曹操", class: "cao" },
34-
VERTICAL: { width: 1, height: 2, char: "|" },
35-
HORIZONTAL: { width: 2, height: 1, char: "一" },
36-
SINGLE: { width: 1, height: 1, char: "口" },
37-
};
35+
private generals = ["张飞", "马超", "赵云", "黄忠"];
36+
private generalIndex = 0;
37+
private horizontalGenerals = ["关羽", "周仓", "魏延", "庞德"];
38+
private horizontalIndex = 0;
39+
40+
private BLOCKS: { [key: string]: BlockType };
41+
42+
private getNextGeneral(): string {
43+
const char = this.generals[this.generalIndex];
44+
this.generalIndex = (this.generalIndex + 1) % this.generals.length;
45+
return char;
46+
}
47+
48+
private getNextHorizontalGeneral(): string {
49+
const char = this.horizontalGenerals[this.horizontalIndex];
50+
this.horizontalIndex = (this.horizontalIndex + 1) % this.horizontalGenerals.length;
51+
return char;
52+
}
3853

3954
constructor(level: number = 0) {
55+
this.BLOCKS = {
56+
CAO_CAO: { width: 2, height: 2, char: "曹操", class: "cao" },
57+
VERTICAL: { width: 1, height: 2, char: "将军" },
58+
HORIZONTAL: { width: 2, height: 1, char: "将军" },
59+
SINGLE: { width: 1, height: 1, char: "兵" },
60+
};
61+
4062
this.currentLevel = level;
4163
this.bestScore = "-";
4264
this.blocks = JSON.parse(JSON.stringify(LEVELS[level].layout));
65+
66+
// Assign character names to blocks
67+
this.blocks.forEach(block => {
68+
if (block.type === 'VERTICAL') {
69+
block.char = this.getNextGeneral();
70+
} else if (block.type === 'HORIZONTAL') {
71+
block.char = this.getNextHorizontalGeneral();
72+
}
73+
});
74+
4375
this.initializeGame();
4476
this.initializeLevelSelector();
77+
this.initializeLanguageSelector();
4578
}
4679

4780
async promptForUserId(): Promise<string | null> {
48-
const id = prompt("Congratulations, you won! Enter your user ID to save your score:");
81+
const id = prompt(
82+
"Congratulations, you won! Enter your user ID to save your score:"
83+
);
4984
if (id) {
5085
this.userId = id;
5186
const existingBestScore = localStorage.getItem(`bestScore_${id}`);
@@ -64,6 +99,7 @@ export class HuarongGame {
6499
this.createBoard();
65100
this.renderPieces();
66101
this.updateStats();
102+
this.updateUIText();
67103

68104
const resetBtn = document.getElementById("resetBtn");
69105
resetBtn?.addEventListener("click", () => this.resetGame());
@@ -73,26 +109,60 @@ export class HuarongGame {
73109
}
74110

75111
private initializeLevelSelector() {
76-
const levelSelector = document.getElementById('levelSelector') as HTMLSelectElement;
112+
const levelSelector = document.getElementById(
113+
"levelSelector"
114+
) as HTMLSelectElement;
77115
if (levelSelector) {
78116
levelSelector.value = this.currentLevel.toString();
79-
levelSelector.addEventListener('change', (e) => {
117+
levelSelector.addEventListener("change", (e) => {
80118
const newLevel = parseInt((e.target as HTMLSelectElement).value);
81119
this.changeLevel(newLevel);
82120
});
83121
}
84122
}
85123

124+
private initializeLanguageSelector() {
125+
const languageSelector = document.getElementById('languageSelector') as HTMLSelectElement;
126+
languageSelector.value = this.language;
127+
languageSelector.addEventListener('change', (e) => {
128+
this.language = (e.target as HTMLSelectElement).value as Language;
129+
this.updateUIText();
130+
});
131+
}
132+
133+
private updateUIText() {
134+
const t = translations[this.language];
135+
document.querySelector('h1')!.textContent = t.title;
136+
document.getElementById('resetBtn')!.textContent = t.reset;
137+
document.getElementById('undoBtn')!.textContent = t.undo;
138+
document.querySelector('.stats')!.innerHTML =
139+
`${t.moves}: <span id="moveCount">${this.moves}</span> | ${t.best}: <span id="bestScore">${this.bestScore}</span>`;
140+
}
141+
86142
private changeLevel(level: number) {
87143
this.currentLevel = level;
88144
this.blocks = JSON.parse(JSON.stringify(LEVELS[level].layout));
145+
146+
// Reset indices and assign new characters for new level
147+
this.generalIndex = 0;
148+
this.horizontalIndex = 0;
149+
this.blocks.forEach(block => {
150+
if (block.type === 'VERTICAL') {
151+
block.char = this.getNextGeneral();
152+
} else if (block.type === 'HORIZONTAL') {
153+
block.char = this.getNextHorizontalGeneral();
154+
}
155+
});
156+
89157
this.moves = 0;
90158
this.moveHistory = [];
91159
this.selectedPiece = null;
92160
if (!this.userId) {
93161
this.bestScore = "-";
94162
} else {
95-
const existingBestScore = localStorage.getItem(`bestScore_${this.userId}_level${level}`);
163+
const existingBestScore = localStorage.getItem(
164+
`bestScore_${this.userId}_level${level}`
165+
);
96166
this.bestScore = existingBestScore ? parseInt(existingBestScore) : "-";
97167
}
98168
this.updateStats();
@@ -112,10 +182,10 @@ export class HuarongGame {
112182
board.innerHTML = "";
113183

114184
this.blocks.forEach((block, index) => {
115-
const blockType = this.BLOCKS[block.type];
185+
const blockType = { ...this.BLOCKS[block.type] };
116186
const piece = document.createElement("div");
117187
piece.className = `piece ${blockType.class || ""}`;
118-
piece.textContent = blockType.char;
188+
piece.textContent = block.char || blockType.char; // Use stored character
119189
piece.style.gridColumn = `${block.x + 1} / span ${blockType.width}`;
120190
piece.style.gridRow = `${block.y + 1} / span ${blockType.height}`;
121191
piece.dataset.index = index.toString();
@@ -167,24 +237,25 @@ export class HuarongGame {
167237
// Position the arrow based on direction
168238
const pieceWidth = rect.width;
169239
const pieceHeight = rect.height;
240+
const arrowSize = 34;
170241
let left, top;
171242

172243
switch (direction) {
173244
case "Left":
174-
left = rect.left - boardRect.left - 35;
175-
top = rect.top - boardRect.top + pieceHeight / 2 - 15;
245+
left = rect.left - boardRect.left - arrowSize;
246+
top = rect.top - boardRect.top + pieceHeight / 2 - arrowSize / 2 - 3;
176247
break;
177248
case "Right":
178-
left = rect.right - boardRect.left + 5;
179-
top = rect.top - boardRect.top + pieceHeight / 2 - 15;
249+
left = rect.right - boardRect.left - 7;
250+
top = rect.top - boardRect.top + pieceHeight / 2 - arrowSize / 2 - 3;
180251
break;
181252
case "Up":
182-
left = rect.left - boardRect.left + pieceWidth / 2 - 15;
183-
top = rect.top - boardRect.top - 35;
253+
left = rect.left - boardRect.left + pieceWidth / 2 - arrowSize / 2 - 3;
254+
top = rect.top - boardRect.top - arrowSize;
184255
break;
185256
case "Down":
186-
left = rect.left - boardRect.left + pieceWidth / 2 - 15;
187-
top = rect.bottom - boardRect.top + 5;
257+
left = rect.left - boardRect.left + pieceWidth / 2 - 20;
258+
top = rect.bottom - boardRect.top - 7;
188259
break;
189260
}
190261

@@ -279,15 +350,12 @@ export class HuarongGame {
279350
const b1Type = this.BLOCKS[block1.type];
280351
const b2Type = this.BLOCKS[block2.type];
281352
return !(
282-
block1.x >= block2.x + b2Type.width ||
283-
block2.x >= block1.x + b1Type.width
353+
block1.x >= block2.x + b2Type.width || block2.x >= block1.x + b1Type.width
284354
);
285355
}
286356

287357
getPossibleMoves(block: Block) {
288358
const moves: Position[] = [];
289-
const blockType = this.BLOCKS[block.type];
290-
291359
[
292360
[-1, 0],
293361
[1, 0],
@@ -333,7 +401,10 @@ export class HuarongGame {
333401
});
334402
}
335403

336-
rectsIntersect(r1: { x: number; y: number; w: number; h: number }, r2: { x: number; y: number; w: number; h: number }) {
404+
rectsIntersect(
405+
r1: { x: number; y: number; w: number; h: number },
406+
r2: { x: number; y: number; w: number; h: number }
407+
) {
337408
return !(
338409
r2.x >= r1.x + r1.w ||
339410
r2.x + r2.w <= r1.x ||
@@ -348,12 +419,14 @@ export class HuarongGame {
348419
if (!this.userId) {
349420
const id = await this.promptForUserId();
350421
if (!id) {
351-
alert(`Congratulations! You won in ${this.moves} moves! (Score not saved)`);
422+
alert(
423+
`Congratulations! You won in ${this.moves} moves! (Score not saved)`
424+
);
352425
this.resetGame();
353426
return;
354427
}
355428
}
356-
429+
357430
if (this.userId) {
358431
const scoreKey = `bestScore_${this.userId}_level${this.currentLevel}`;
359432
const currentBest = localStorage.getItem(scoreKey);
@@ -362,7 +435,7 @@ export class HuarongGame {
362435
localStorage.setItem(scoreKey, this.moves.toString());
363436
}
364437
}
365-
438+
366439
alert(`Congratulations! You won in ${this.moves} moves!`);
367440
this.resetGame();
368441
};
@@ -387,7 +460,16 @@ export class HuarongGame {
387460
document
388461
.querySelectorAll(".direction-arrow")
389462
.forEach((arrow) => arrow.remove());
463+
this.generalIndex = 0;
464+
this.horizontalIndex = 0;
390465
this.blocks = JSON.parse(JSON.stringify(LEVELS[this.currentLevel].layout));
466+
this.blocks.forEach(block => {
467+
if (block.type === 'VERTICAL') {
468+
block.char = this.getNextGeneral();
469+
} else if (block.type === 'HORIZONTAL') {
470+
block.char = this.getNextHorizontalGeneral();
471+
}
472+
});
391473
this.moves = 0;
392474
this.moveHistory = [];
393475
this.selectedPiece = null;
@@ -396,6 +478,7 @@ export class HuarongGame {
396478
}
397479
this.updateStats();
398480
this.renderPieces();
481+
this.updateUIText();
399482
}
400483

401484
updateStats() {
@@ -408,4 +491,4 @@ export class HuarongGame {
408491
bestScore.textContent = this.bestScore.toString();
409492
}
410493
}
411-
}
494+
}

0 commit comments

Comments
 (0)