-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathscript.js
More file actions
4001 lines (3863 loc) · 214 KB
/
Copy pathscript.js
File metadata and controls
4001 lines (3863 loc) · 214 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
(function () {
// engine.js가 module.exports로만 내보내므로, 브라우저용으로 함수들을 다시 참조.
// → engine.js를 브라우저에서도 쓰도록 아래에서 직접 로드한 전역 함수 사용.
const Eng = window.JanggiEngine;
const ROWS = 10, COLS = 9;
const frame = document.getElementById('frame');
const mistOverlay = document.getElementById('mistOverlay');
const elSub = document.querySelector('.title-bar .sub');
const elTurnSuffix = document.getElementById('turnSuffix');
const elMovelogTitle = document.getElementById('movelogTitle');
const elUndo = document.getElementById('undoBtn');
const elReset = document.getElementById('resetBtn');
const elResign = document.getElementById('resignBtn');
const resignConfirmBox = document.getElementById('resignConfirm');
const elResignConfirmText = document.getElementById('resignConfirmText');
const elResignYes = document.getElementById('resignYes');
const elResignNo = document.getElementById('resignNo');
const elWinHint = document.getElementById('winHint');
const elMistText = document.getElementById('mistText');
const langKo = document.getElementById('langKo');
const langEn = document.getElementById('langEn');
const langZhHans = document.getElementById('langZhHans');
const langZhHant = document.getElementById('langZhHant');
const langJa = document.getElementById('langJa');
const langDe = document.getElementById('langDe');
const langFr = document.getElementById('langFr');
// langLabel은 타이틀바 ⚙ 드롭다운으로 이동 — HTML에 없음, null-safe 처리
const langLabel = document.getElementById('langLabel');
const settingsBtn = document.getElementById('settingsBtn');
const settingsDropdown = document.getElementById('settingsDropdown');
const settingsBackdrop = document.getElementById('settingsBackdrop');
const grid = document.getElementById('grid');
const piecesLayer = document.getElementById('pieces');
const statusEl = document.getElementById('status');
const turnLabel = document.getElementById('turnLabel');
const turnPersp = document.getElementById('turnPersp');
const msgEl = document.getElementById('msg');
const capR = document.getElementById('capR');
const capB = document.getElementById('capB');
const winOverlay = document.getElementById('winOverlay');
const winTitle = document.getElementById('winTitle');
const winFactionLine = document.getElementById('winFactionLine');
const winScore = document.getElementById('winScore'); // [6-2 UI] 무승부 점수 보조 줄
const setupOverlay = document.getElementById('setupOverlay');
const setupStep = document.getElementById('setupStep');
const setupGrid = document.getElementById('setupGrid');
const setupSideLabel = document.getElementById('setupSideLabel');
const setupTitleWrap = setupStep.querySelector('.setup-title');
const setupSkip = document.getElementById('setupSkip');
const movelog = document.getElementById('movelog');
const HANJA = {
r: { K:'楚', A:'士', E:'象', H:'馬', R:'車', C:'包', P:'卒' },
b: { K:'漢', A:'士', E:'象', H:'馬', R:'車', C:'包', P:'兵' },
};
// 보드 위 기물 이미지 (백자 팔각알). r=초(CHU), b=한(HAN)
// 에셋 구조: pieces/{테마}/{진영}/{기물키}.png — 테마 시스템 대비
const PIECE_IMG = {
r: { K:'assets/pieces/baekja/chu/chu_k.webp', R:'assets/pieces/baekja/chu/chu_r.webp', H:'assets/pieces/baekja/chu/chu_h.webp',
E:'assets/pieces/baekja/chu/chu_e.webp', C:'assets/pieces/baekja/chu/chu_c.webp', A:'assets/pieces/baekja/chu/chu_a.webp', P:'assets/pieces/baekja/chu/chu_p.webp' },
b: { K:'assets/pieces/baekja/han/han_k.webp', R:'assets/pieces/baekja/han/han_r.webp', H:'assets/pieces/baekja/han/han_h.webp',
E:'assets/pieces/baekja/han/han_e.webp', C:'assets/pieces/baekja/han/han_c.webp', A:'assets/pieces/baekja/han/han_a.webp', P:'assets/pieces/baekja/han/han_p.webp' },
};
// 튜토리얼 카드 전용 이미지 — baekja_tut 고정 (테마 시스템 확장과 무관)
const TUT_PIECE_IMG = {
r: { K:'assets/pieces/baekja_tut/chu/chu_k_tut.webp', R:'assets/pieces/baekja_tut/chu/chu_r_tut.webp', H:'assets/pieces/baekja_tut/chu/chu_h_tut.webp',
E:'assets/pieces/baekja_tut/chu/chu_e_tut.webp', C:'assets/pieces/baekja_tut/chu/chu_c_tut.webp', A:'assets/pieces/baekja_tut/chu/chu_a_tut.webp', P:'assets/pieces/baekja_tut/chu/chu_p_tut.webp' },
b: { K:'assets/pieces/baekja_tut/han/han_k_tut.webp', R:'assets/pieces/baekja_tut/han/han_r_tut.webp', H:'assets/pieces/baekja_tut/han/han_h_tut.webp',
E:'assets/pieces/baekja_tut/han/han_e_tut.webp', C:'assets/pieces/baekja_tut/han/han_c_tut.webp', A:'assets/pieces/baekja_tut/han/han_a_tut.webp', P:'assets/pieces/baekja_tut/han/han_p_tut.webp' },
};
// ── i18n: UI 문구만 번역. 기물 한자·상차림 배치는 불변 ──────────
// 개발 중에만 누락 키 경고를 띄우는 플래그. 배포 시 false 유지(콘솔 깨끗).
const DEBUG_I18N = false;
// 언어 선택 저장 키. 새로고침/재방문 시 이 값을 먼저 본다.
const LANG_STORE_KEY = 'janggi.lang';
// 지원 언어 목록 (저장값 검증용 — 모르는 값이 저장돼 있으면 무시).
const SUPPORTED_LANGS = ['ko', 'en', 'zh-Hans', 'zh-Hant', 'ja', 'de', 'fr'];
// 브라우저 언어 추측: 한국어→ko, 중국어→지역별 간체/번체, 일·독·불→각 언어, 그 외→en.
function detectLang() {
const raw = (navigator.language || 'en').toLowerCase();
if (raw.startsWith('ko')) return 'ko';
if (raw.startsWith('zh')) {
// 번체권: 대만(tw)·홍콩(hk)·마카오(mo). 그 외 중국어는 간체 기본.
if (/-(tw|hk|mo)\b/.test(raw) || raw.includes('hant')) return 'zh-Hant';
return 'zh-Hans';
}
if (raw.startsWith('ja')) return 'ja';
if (raw.startsWith('de')) return 'de';
if (raw.startsWith('fr')) return 'fr';
return 'en';
}
// 시작 언어: 저장값이 있으면 그걸 쓰고, 없거나 모르는 값이면 브라우저 추측.
let lang = detectLang();
try {
const saved = localStorage.getItem(LANG_STORE_KEY);
if (saved && SUPPORTED_LANGS.includes(saved)) lang = saved;
} catch (e) { /* localStorage 차단(사생활 모드 등) 시 추측값 그대로 */ }
const I18N = {
ko: {
sub: 'JANGGI · 한국 장기',
langLabel: '언어 선택 :',
chooseFaction: '진영을 고르세요',
chuName: '초(楚)', hanName: '한(漢)',
chuSub: '선수 · 청록', hanSub: '후수 · 인주',
factionNoteDefault: '고른 진영이 화면 아래에 놓입니다 · 선수는 항상 초(楚)',
autoWon: '직전 판 승리 — 이번엔 한(漢)으로 자동 배정되었습니다. 바꾸려면 다른 진영을 고르세요.',
autoLost: '직전 판 패배 — 이번엔 초(楚)로 자동 배정되었습니다. 바꾸려면 다른 진영을 고르세요.',
setupTitlePre: '의 상차림을 고르세요', // <b>초(楚)</b>의 상차림을 고르세요
autoPick: '자동 선택(무작위)',
turnLabel: '차례', mistStart: '대국 시작',
undo: '무르기', reset: '처음부터', flip: '판 뒤집기',
resign: '돌 던지기', resignConfirm: '정말 돌을 던지시겠습니까?',
resignYes: '예', resignNo: '아니오',
resigned: (s) => `${s} 항복 — 돌을 던졌습니다`,
capByChu: '초가 잡음', capByHan: '한이 잡음',
capChuEmpty: '초가 잡은 기물', capHanEmpty: '한이 잡은 기물',
movelogTitle: '기 보', movelogEmpty: '아직 둔 수가 없습니다',
pickPiece: '기물을 골라 두세요',
pickDest: '둘 곳을 고르세요', cantMove: '둘 수 없는 기물입니다', cantMovePinned: '장을 지키고 있어 움직일 수 없습니다', cantMoveInCheck: '지금은 장군을 막아야 합니다',
notYourTurn: (s) => `지금은 ${s} 차례입니다`,
check: (s) => `장군! ${s} 왕을 구하는 수를 두세요`,
checkWord: '장군',
defendWord: '멍군',
myFaction: (you, sR, sB) => `내 진영: ${you} · 초 ${sR} · 한 ${sB}`,
chuFirst: '초(楚) 선수', hanSecond: '한(漢) 후수',
win: (s) => `${s} 승리`,
outcomeWin: '승리', outcomeLose: '패배',
factionWon: (s) => `${s} 승리`,
youWon: '승리했습니다 · 다음 판은 한(漢)으로',
youLost: '패배했습니다 · 다음 판은 초(楚)로',
outcomeDraw: '무승부', drawLine: '비김',
drawStalemate: '둘 곳이 없어 무승부입니다',
// ★ [6] 전통 규칙 종료 표시 — 빅장(draw+score) / 반복수(승패)
outcomeDrawBikjang: '무승부 (빅장)',
drawBikjang: '빅장 — 점수로 가립니다',
scoreLead: (s, n) => `${s} ${n}점 우세`,
chuShort: '초', hanShort: '한', // 격차 줄용 — 한자 병기 없는 짧은 진영명(반복 회피)
byCheckmate: (s) => `외통 — ${s} 승리`,
byTimeout: (s) => `시간패 — ${s} 승리`,
// 반복수는 무승부가 아니라 실격패(연맹 규정) → 승/패 화면. "반복수 — {승자} 승리".
byRepetition: (s) => `반복수 — ${s} 승리`,
// ★ [6-3c] 반복수 종료 부제 — 빅장 점수줄처럼 화면이 스스로 사유를 설명(왜 끝났는지).
// winScore(결과 보조 설명 줄)를 점수가 아닌 사유 한 줄로 재사용.
// ★ 톤: 사람은 컴퓨터 대국에서 반복수 시 거의 항상 패배 쪽을 봄. 순수 중립("반복되었습니다")은
// 패배와 연결이 약하고, 책임 추궁("당신이 반복")은 다실 톤이 깨짐 → 가운데("…종료되었습니다").
repetitionReason: '같은 국면이 네 번 반복되어 대국이 종료되었습니다',
undone: '한 수 물렀습니다',
winHint: '다시 두려면 ‘처음부터’를 누르세요',
// ★ AI 대국 문구 (루미 톤: 보이지 않는 기객, 조용한 안내)
aiThinking: '건너편이 잠시 생각합니다',
aiWaking: 'AI 상대를 깨우는 중입니다',
aiFailLong: 'AI 상대를 깨우는 중입니다. 브라우저 환경에 따라 잠시 멈출 수 있어요. 지금은 혼자 판을 살펴볼 수 있습니다.',
aiRetry: '다시 깨우기',
aiWatch: '판만 보기',
// ★ 관점 줄 (내 차례인지 즉시 보이게)
perspMine: '당신이 둘 차례입니다',
perspAi: '상대가 수를 고르고 있습니다',
perspHuman: (s) => `${s}이(가) 둘 차례입니다`, // AI 없는 모드용
// ★ 모드 메뉴 / 강도 선택
levelTitle: '오늘은 어떻게 두실까요?',
modeCpu: '컴퓨터와 두기', modeCpuSub: 'AI 상대와 대국',
modeTutorial: '장기 배우기', modeTutorialSub: '기물 행마법과 이기는 방법',
modeRules: '장기 규칙', modeRulesSub: '장군·외통·승리 조건',
modeSettings: '환경 설정', modeSettingsSub: '언어 · 장기판 배경',
modeHuman: '사람과 두기', modeHumanSub: '준비 중입니다',
modeReview: '복기하기', modeReviewSub: '준비 중입니다',
modeComing: '아직 준비 중입니다',
levelPlayCpu: '컴퓨터와 두기',
lvBeginnerName: '초심자', lvBeginnerSub: '처음 장기를 배우는 분을 위한 상대',
lvFriendName: '익숙한 벗', lvFriendSub: '가볍게 한 판 즐길 수 있는 상대',
lvMasterName: '노련한 기객', lvMasterSub: '쉽게 빈틈을 보이지 않는 상대',
lvExpertName: '명인', lvExpertSub: '한 치의 빈틈도 허락하지 않는 상대',
levelNote: '마음에 드는 상대를 고르세요 · 한 판이 끝나면 다시 고를 수 있습니다',
settingsLangLabel: '언어',
settingsBtnLabel: '설정',
settingsBgLabel: '장기판 배경',
bgSansuHwa: '산수화', bgSimple: '심플', bgWood: '원목', bgSipjangsaeng: '십장생', bgPaper: '한지',
rulesTitle: '장기 규칙',
rulesSubtitle: '게임을 시작하기 전에 알아두면 좋은 기본 규칙',
rulesExLabel: '예시',
rulesClose: '닫기',
aboutTitle: '소개',
aboutClose: '닫기',
aboutSourceLabel: '출처 ↗',
aboutTagline: '정성을 담아 만들었습니다',
aboutSections: [
{
title: '소개',
body: '장기는 한국의 전통 전략 보드게임으로, 흔히 Korean chess로 소개됩니다.',
},
{
title: '제작',
body: 'Hanrim',
link: { url: 'https://cozyshelter.tistory.com', label: 'Cozy Shelter' },
},
{
title: '그래픽',
body: '장기판 · 기물 디자인 — 이 프로젝트를 위해 제작',
},
{
title: '사운드',
items: [
{ label: '보드게임 기물음 (이동·잡기·선택·외통)', by: 'taure', url: 'https://pixabay.com/sound-effects/board-game-pieces-59039/' },
{ label: '승리', by: 'Sarah H', url: 'https://pixabay.com/users/astralsynthesizer-50776509/' },
{ label: '패배', by: 'Universfield', url: 'https://pixabay.com/users/universfield-28281460/' },
],
},
{
title: 'AI 엔진',
body: 'Fairy-Stockfish · GPLv3',
link: { url: 'https://github.com/fairy-stockfish/Fairy-Stockfish', label: 'GitHub' },
},
],
rulesSections: [
{
title: '장기의 목표',
body: '장기는 두 진영이 겨루는 한국의 전통 보드게임입니다. 상대의 왕(楚 또는 漢)을 외통으로 몰아넣으면 승리합니다. 내 왕을 지키면서 상대의 왕을 공격하는 것이 핵심입니다.',
example: null,
},
{
title: '장군 (Check)',
body: '상대의 왕을 다음 수에 잡을 수 있는 상태를 장군이라고 합니다. 장군을 받으면 반드시 왕을 안전한 곳으로 옮기거나, 공격을 막거나, 공격하는 기물을 잡아 위협에서 벗어나야 합니다.',
example: '차가 상대의 왕을 일직선으로 겨누고 있는 경우.',
},
{
title: '양수겸장 (Double Check)',
body: '한 수로 두 기물이 동시에 왕을 노리는 경우를 양수겸장이라고 합니다. 두 위협을 한 번에 막을 수는 없으므로, 두 공격을 모두 피하려면 왕을 안전한 칸으로 옮기는 길밖에 없습니다. 옮길 곳마저 없으면 외통입니다.',
example: '왕을 막고 있던 기물이 비켜서며, 그 자리 뒤의 기물과 비켜선 기물이 함께 왕을 겨누는 경우.',
},
{
title: '외통 (Checkmate)',
body: '장군을 받았지만 어떤 방법으로도 벗어날 수 없는 상태를 외통이라고 합니다. 왕을 옮길 수도, 공격을 막을 수도, 공격하는 기물을 잡을 수도 없으면 외통이며, 이 순간 대국이 끝납니다.',
example: '왕이 도망갈 칸이 모두 막혀 있고, 공격을 막을 기물도 없는 경우.',
},
{
title: '승리 조건',
body: '상대를 외통으로 몰면 승리합니다. 또한 상대가 스스로 돌을 던지면 그 즉시 이깁니다.',
example: null,
},
{
title: '빅장 (점수제 무승부)',
body: '양쪽 왕이 같은 세로줄에서 마주 보고, 그 사이를 가로막는 기물이 하나도 없는 상태를 빅장이라고 합니다. 차례인 쪽이 이 마주 봄을 풀 수 있는 합법적인 수가 하나도 없으면 대국은 무승부로 끝납니다. 이때 승부는 남은 기물의 점수로 가립니다. 차는 13점, 포는 7점, 마는 5점, 상과 사는 각 3점, 병(졸)은 2점이며 왕은 0점입니다. 후수인 한(漢)은 먼저 두는 불리함을 보정받아 1.5점을 더 받습니다. 점수가 높은 쪽이 빅장 무승부에서 우세를 가져갑니다.',
example: '두 왕이 같은 줄에서 마주 본 채, 차례인 쪽이 그 줄을 벗어나거나 사이를 막을 방법이 전혀 없는 경우.',
},
{
title: '반복수',
body: '똑같은 배치가 같은 차례에 거듭 나타나는 것을 반복수라고 합니다. 같은 국면을 세 번까지는 둘 수 있지만, 같은 국면을 네 번째로 반복한 쪽이 실격패합니다. 빅장과 달리 반복수는 무승부가 아니라 승패가 갈리는 종료입니다. 한쪽이 계속 장군을 걸어 무한정 대국을 끄는 만년장을 막기 위한 규칙입니다.',
example: '한 기물로 같은 자리를 오가며 장군을 거듭 거는 경우, 네 번째 같은 국면에서 그 쪽이 패합니다.',
},
{
title: '알아두기',
body: '이 게임은 장군과 외통, 돌던지기에 더해 빅장(점수제 무승부)과 반복수까지 적용합니다. 행마법과 상차림 등 더 자세한 내용은 장기를 직접 두며 익혀 보세요.',
example: null,
},
],
},
en: {
sub: 'JANGGI · Korean Chess',
langLabel: 'Language :',
chooseFaction: 'Choose your side',
chuName: 'Cho (楚)', hanName: 'Han (漢)',
chuSub: 'First · Teal', hanSub: 'Second · Red',
factionNoteDefault: 'Your side sits at the bottom · Cho (楚) always moves first',
autoWon: 'You won the last game — assigned Han (漢) this time. Pick the other side to change.',
autoLost: 'You lost the last game — assigned Cho (楚) this time. Pick the other side to change.',
setupTitlePre: ' — choose the formation', // <b>Cho (楚)</b> — choose the formation
autoPick: 'Auto (random)',
turnLabel: 'to move', mistStart: 'Game Start',
undo: 'Undo', reset: 'New Game', flip: 'Flip Board',
resign: 'Surrender', resignConfirm: 'Surrender this game?',
resignYes: 'Yes', resignNo: 'No',
resigned: (s) => `${s} surrendered`,
capByChu: 'Cho captured', capByHan: 'Han captured',
capChuEmpty: 'Captured by Cho', capHanEmpty: 'Captured by Han',
movelogTitle: 'Moves', movelogEmpty: 'No moves yet',
pickPiece: 'Select a piece',
pickDest: 'Choose a destination', cantMove: 'This piece has no legal moves', cantMovePinned: 'It is guarding the general and cannot move', cantMoveInCheck: 'You must respond to the check first',
notYourTurn: (s) => `It's ${s}'s turn`,
check: (s) => `Check! Save ${s}'s general`,
checkWord: 'Check',
defendWord: 'Defend',
myFaction: (you, sR, sB) => `You: ${you} · Cho ${sR} · Han ${sB}`,
chuFirst: 'Cho (楚) · First', hanSecond: 'Han (漢) · Second',
win: (s) => `${s} wins`,
outcomeWin: 'Victory', outcomeLose: 'Defeat',
factionWon: (s) => `${s} wins`,
youWon: 'You won · next game you play Han (漢)',
youLost: 'You lost · next game you play Cho (楚)',
outcomeDraw: 'Draw', drawLine: 'Stalemate',
drawStalemate: 'No legal moves — the game is a draw',
outcomeDrawBikjang: 'Draw (Bikjang)',
drawBikjang: 'Bikjang — decided by points',
scoreLead: (s, n) => `${s} leads by ${n}`,
chuShort: 'Cho', hanShort: 'Han',
byCheckmate: (s) => `Checkmate — ${s} wins`,
byTimeout: (s) => `Timeout — ${s} wins`,
// ★ [6-3b] Repetition is a forfeit loss (federation rule), not a draw → win/lose screen.
byRepetition: (s) => `Repetition — ${s} wins`,
repetitionReason: 'The same position occurred four times, ending the game',
undone: 'Move undone',
winHint: 'Press “New Game” to play again',
// AI opponent strings (quiet, unseen-player tone)
aiThinking: 'Your opponent is considering a move',
aiWaking: 'Waking the AI opponent',
aiFailLong: 'Waking the AI opponent. Depending on your browser it may pause for a moment. For now you can study the board on your own.',
aiRetry: 'Wake again',
aiWatch: 'Just view the board',
// perspective line (so it's instantly clear whose turn it is)
perspMine: 'Your move',
perspAi: 'Your opponent is choosing a move',
perspHuman: (s) => `${s} to move`,
// strength selection (small foyer — "How would you like to play today?")
levelTitle: 'How would you like to play today?',
modeCpu: 'Play with Computer', modeCpuSub: 'Play against the AI',
modeTutorial: 'Learn Janggi', modeTutorialSub: 'How pieces move and how to win',
modeRules: 'Rules', modeRulesSub: 'Check, checkmate, how to win',
modeSettings: 'Settings', modeSettingsSub: 'Language · board background',
modeHuman: 'Play with a Friend', modeHumanSub: 'Coming soon',
modeReview: 'Review a Game', modeReviewSub: 'Coming soon',
modeComing: 'This mode is not yet available',
levelPlayCpu: 'Play with Computer',
lvBeginnerName: 'Beginner', lvBeginnerSub: 'For someone learning Janggi for the first time',
lvFriendName: 'Familiar Friend', lvFriendSub: 'A relaxed opponent for a casual game',
lvMasterName: 'Seasoned Player', lvMasterSub: 'Rarely leaves an opening',
lvExpertName: 'Master', lvExpertSub: 'Allows not a single opening',
levelNote: 'Choose your opponent · you can pick again after each game',
settingsLangLabel: 'Language',
settingsBtnLabel: 'Settings',
settingsBgLabel: 'Board Background',
bgSansuHwa: 'Ink Wash', bgSimple: 'Simple', bgWood: 'Wood', bgSipjangsaeng: 'Sipjangsaeng', bgPaper: 'Hanji',
rulesTitle: 'How to Play Janggi',
rulesSubtitle: "The basics you'll want to know before your first game",
rulesExLabel: 'Example',
rulesClose: 'Close',
aboutTitle: 'About',
aboutClose: 'Close',
aboutSourceLabel: 'Source ↗',
aboutTagline: 'Made with care',
aboutSections: [
{
title: 'About',
body: 'Korean Janggi is a traditional Korean strategy board game, often described as Korean chess.',
},
{
title: 'Made by',
body: 'Hanrim',
link: { url: 'https://cozyshelter.tistory.com', label: 'Cozy Shelter' },
},
{
title: 'Graphics',
body: 'Board & piece design — created for this project',
},
{
title: 'Sound',
items: [
{ label: 'Board Game Pieces (move · capture · pick · checkmate)', by: 'taure', url: 'https://pixabay.com/sound-effects/board-game-pieces-59039/' },
{ label: 'Victory', by: 'Sarah H', url: 'https://pixabay.com/users/astralsynthesizer-50776509/' },
{ label: 'Defeat', by: 'Universfield', url: 'https://pixabay.com/users/universfield-28281460/' },
],
},
{
title: 'AI Engine',
body: 'Fairy-Stockfish · GPLv3',
link: { url: 'https://github.com/fairy-stockfish/Fairy-Stockfish', label: 'GitHub' },
},
],
rulesSections: [
{
title: 'The Goal',
body: "Janggi is a traditional Korean board game played between two sides. You win by trapping your opponent's General (楚 or 漢) in checkmate. The key is to defend your own General while attacking your opponent's.",
example: null,
},
{
title: 'Check',
body: "When your opponent's General could be captured on the next move, it is in check. A General in check must escape the threat: move to a safe spot, block the attack, or capture the attacking piece.",
example: 'A Chariot lined up directly against the opposing General.',
},
{
title: 'Double Check',
body: 'When a single move puts two pieces in attack on the General at once, it is a double check. No single block or capture can answer both threats, so the only escape is to move the General to a safe square. If it has nowhere to go, it is checkmate.',
example: 'A piece shielding the General steps aside, so that it and the piece behind it both attack the General at the same time.',
},
{
title: 'Checkmate',
body: 'When a General is in check and cannot escape by any means, it is checkmate. If the General cannot move, the attack cannot be blocked, and the attacking piece cannot be captured, it is checkmate and the game ends.',
example: 'The General has no safe square to flee to, and no piece can block the attack.',
},
{
title: 'How to Win',
body: 'Trap your opponent in checkmate to win. You also win immediately if your opponent resigns.',
example: null,
},
{
title: 'Bikjang (Scored Draw)',
body: "Bikjang occurs when both Generals face each other on the same file with no piece standing between them. If the side to move has no legal way to break this face-off, the game ends in a draw. The outcome is then decided by the points of the remaining pieces: the Chariot is worth 13, the Cannon 7, the Horse 5, the Elephant and Guard 3 each, the Soldier 2, and the General 0. Han (漢), moving second, receives an extra 1.5 points to offset the disadvantage of going later. The side with more points takes the edge in a Bikjang draw.",
example: 'The two Generals face each other on the same file, and the side to move has no way to leave that file or block the space between them.',
},
{
title: 'Repetition',
body: 'Repetition is when the same arrangement recurs with the same side to move. A position may be repeated up to three times, but the side that creates the same position for the fourth time loses by forfeit. Unlike Bikjang, repetition ends the game with a winner and a loser rather than a draw. The rule exists to stop a player from dragging the game on forever with perpetual check.',
example: 'A piece shuttles back and forth giving check again and again; on the fourth identical position, that side loses.',
},
{
title: 'Good to Know',
body: 'Alongside check, checkmate, and resignation, this game applies Bikjang (a scored draw) and repetition. For more on how the pieces move and the opening formations, the best way to learn is to play.',
example: null,
},
],
},
'zh-Hans': {
sub: 'JANGGI · 韩国象棋',
langLabel: '语言 :',
chooseFaction: '请选择阵营',
chuName: '楚', hanName: '汉',
chuSub: '先手 · 青绿', hanSub: '后手 · 朱红',
factionNoteDefault: '所选阵营位于棋盘下方 · 楚方始终先行',
autoWon: '上一局获胜 — 本局自动分配为汉方。如需更改,请选择另一阵营。',
autoLost: '上一局落败 — 本局自动分配为楚方。如需更改,请选择另一阵营。',
setupTitlePre: ' 的布阵', // <b>楚</b> 的布阵
autoPick: '自动选择(随机)',
turnLabel: '行棋', mistStart: '对局开始',
undo: '悔棋', reset: '重新开始', flip: '翻转棋盘',
resign: '认输', resignConfirm: '确定要认输吗?',
resignYes: '是', resignNo: '否',
resigned: (s) => `${s} 认输 — 已投子`,
capByChu: '楚方吃子', capByHan: '汉方吃子',
capChuEmpty: '楚方吃掉的棋子', capHanEmpty: '汉方吃掉的棋子',
movelogTitle: '棋 谱', movelogEmpty: '尚无棋步',
pickPiece: '请选择棋子',
pickDest: '请选择落点', cantMove: '该棋子无合法走法', cantMovePinned: '该棋子正护卫将帅,无法移动', cantMoveInCheck: '现在必须先应将',
notYourTurn: (s) => `现在轮到 ${s} 行棋`,
check: (s) => `将军!请走子保护 ${s} 方的将`,
checkWord: '将军',
defendWord: '应将',
myFaction: (you, sR, sB) => `我的阵营:${you} · 楚 ${sR} · 汉 ${sB}`,
chuFirst: '楚 · 先手', hanSecond: '汉 · 后手',
win: (s) => `${s} 方获胜`,
outcomeWin: '胜', outcomeLose: '负',
factionWon: (s) => `${s} 方获胜`,
youWon: '你获胜了 · 下一局执汉',
youLost: '你落败了 · 下一局执楚',
outcomeDraw: '和棋', drawLine: '和局',
drawStalemate: '无子可动 — 本局为和棋',
outcomeDrawBikjang: '和棋(逼将)',
drawBikjang: '逼将 — 由点数决定胜负',
scoreLead: (s, n) => `${s} 领先 ${n} 分`,
chuShort: '楚', hanShort: '汉',
byCheckmate: (s) => `将死 — ${s} 方获胜`,
byTimeout: (s) => `超时 — ${s} 方获胜`,
byRepetition: (s) => `重复局面 — ${s} 方获胜`,
repetitionReason: '同一局面出现四次,本局结束',
undone: '已悔一步',
winHint: '若要再下一局,请点击“重新开始”',
aiThinking: '对面正在思考',
aiWaking: '正在唤醒 AI 对手',
aiFailLong: '正在唤醒 AI 对手。视浏览器环境可能会短暂停顿。此刻你可以先自行研究棋局。',
aiRetry: '重新唤醒',
aiWatch: '只看棋盘',
perspMine: '轮到你行棋',
perspAi: '对手正在选择走法',
perspHuman: (s) => `轮到 ${s} 行棋`,
levelTitle: '今天想怎么下?',
modeCpu: '与电脑对弈', modeCpuSub: '与 AI 对手对局',
modeTutorial: '学习象棋', modeTutorialSub: '棋子走法与取胜之道',
modeRules: '象棋规则', modeRulesSub: '将军 · 将死 · 取胜条件',
modeSettings: '设置', modeSettingsSub: '语言 · 棋盘背景',
modeHuman: '与人对弈', modeHumanSub: '敬请期待',
modeReview: '复盘', modeReviewSub: '敬请期待',
modeComing: '该模式尚未开放',
levelPlayCpu: '与电脑对弈',
lvBeginnerName: '初学者', lvBeginnerSub: '为初次学习象棋的人准备的对手',
lvFriendName: '熟悉的棋友', lvFriendSub: '轻松对弈一局的对手',
lvMasterName: '老练的棋客', lvMasterSub: '不轻易露出破绽的对手',
lvExpertName: '国手', lvExpertSub: '不容许一丝破绽的对手',
levelNote: '请选择心仪的对手 · 每局结束后可重新选择',
settingsLangLabel: '语言',
settingsBtnLabel: '设置',
settingsBgLabel: '棋盘背景',
bgSansuHwa: '山水画', bgSimple: '简约', bgWood: '原木', bgSipjangsaeng: '十长生', bgPaper: '韩纸',
rulesTitle: '象棋规则',
rulesSubtitle: '开局前值得了解的基本规则',
rulesExLabel: '示例',
rulesClose: '关闭',
aboutTitle: '关于',
aboutClose: '关闭',
aboutSourceLabel: '来源 ↗',
aboutTagline: '用心制作',
aboutSections: [
{
title: '关于',
body: '韩国象棋(장기)是韩国传统的策略棋类游戏,常被介绍为韩国象棋(Korean chess)。',
},
{
title: '制作',
body: 'Hanrim',
link: { url: 'https://cozyshelter.tistory.com', label: 'Cozy Shelter' },
},
{
title: '图像',
body: '棋盘与棋子设计 — 为本项目制作',
},
{
title: '音效',
items: [
{ label: '棋子音效(移动 · 吃子 · 选择 · 将死)', by: 'taure', url: 'https://pixabay.com/sound-effects/board-game-pieces-59039/' },
{ label: '胜利', by: 'Sarah H', url: 'https://pixabay.com/users/astralsynthesizer-50776509/' },
{ label: '失败', by: 'Universfield', url: 'https://pixabay.com/users/universfield-28281460/' },
],
},
{
title: 'AI 引擎',
body: 'Fairy-Stockfish · GPLv3',
link: { url: 'https://github.com/fairy-stockfish/Fairy-Stockfish', label: 'GitHub' },
},
],
rulesSections: [
{
title: '象棋的目标',
body: '韩国象棋是两方对弈的韩国传统棋类游戏。将对方的将(楚或汉)逼入将死即可获胜。核心在于守护己方的将,同时攻击对方的将。',
example: null,
},
{
title: '将军 (Check)',
body: '当对方的将在下一步可能被吃掉时,称为将军。被将军时,必须解除威胁:将移到安全之处、挡住攻击,或吃掉发动攻击的棋子。',
example: '车在一条直线上正对对方的将。',
},
{
title: '双将 (Double Check)',
body: '一步之内有两枚棋子同时将军,称为双将。由于无法一次挡住或吃掉两个威胁,唯一的解法是把将移到安全格。若无处可移,即为将死。',
example: '原本挡在将前的棋子让开,使它与其身后的棋子同时正对将。',
},
{
title: '将死 (Checkmate)',
body: '被将军且无论如何都无法解除时,称为将死。若将无法移动、攻击无法阻挡、攻击的棋子也无法吃掉,即为将死,此刻对局结束。',
example: '将无安全格可逃,也没有棋子能挡住攻击。',
},
{
title: '取胜条件',
body: '将对方逼入将死即可获胜。此外,对方主动认输时立即取胜。',
example: null,
},
{
title: '逼将(计分和棋)',
body: '双方的将在同一纵线上相对,且两者之间没有任何棋子相隔,这种状态称为逼将。若轮到行棋的一方没有任何合法走法可以解除这种相对,本局即以和棋结束。此时胜负由剩余棋子的点数决定:车为13分,炮为7分,马为5分,象与士各3分,兵(卒)为2分,将为0分。后手的汉方为弥补后行的不利,额外获得1.5分。点数较高的一方在逼将和棋中占优。',
example: '双方的将在同一纵线上相对,轮到行棋的一方既无法离开该纵线,也无法在两者之间放子相隔。',
},
{
title: '重复局面',
body: '同一布局在同一方行棋时反复出现,称为重复局面。同一局面最多可重复三次,但第四次造成相同局面的一方判负。与逼将不同,重复局面并非和棋,而是分出胜负的结束方式。此规则用于防止一方以连续将军无限拖延对局。',
example: '一枚棋子来回移动反复将军,在第四次出现相同局面时,该方落败。',
},
{
title: '须知',
body: '本游戏在将军、将死与认输之外,还采用逼将(计分和棋)与重复局面规则。关于各棋子的走法与布阵等更多内容,最好的学习方式就是亲自对弈。',
example: null,
},
],
},
'zh-Hant': {
sub: 'JANGGI · 韓國象棋',
langLabel: '語言 :',
chooseFaction: '請選擇陣營',
chuName: '楚', hanName: '漢',
chuSub: '先手 · 青綠', hanSub: '後手 · 朱紅',
factionNoteDefault: '所選陣營位於棋盤下方 · 楚方始終先行',
autoWon: '上一局獲勝 — 本局自動分配為漢方。如需更改,請選擇另一陣營。',
autoLost: '上一局落敗 — 本局自動分配為楚方。如需更改,請選擇另一陣營。',
setupTitlePre: ' 的佈陣', // <b>楚</b> 的佈陣
autoPick: '自動選擇(隨機)',
turnLabel: '行棋', mistStart: '對局開始',
undo: '悔棋', reset: '重新開始', flip: '翻轉棋盤',
resign: '認輸', resignConfirm: '確定要認輸嗎?',
resignYes: '是', resignNo: '否',
resigned: (s) => `${s} 認輸 — 已投子`,
capByChu: '楚方吃子', capByHan: '漢方吃子',
capChuEmpty: '楚方吃掉的棋子', capHanEmpty: '漢方吃掉的棋子',
movelogTitle: '棋 譜', movelogEmpty: '尚無棋步',
pickPiece: '請選擇棋子',
pickDest: '請選擇落點', cantMove: '該棋子無合法走法', cantMovePinned: '該棋子正護衛將帥,無法移動', cantMoveInCheck: '現在必須先應將',
notYourTurn: (s) => `現在輪到 ${s} 行棋`,
check: (s) => `將軍!請走子保護 ${s} 方的將`,
checkWord: '將軍',
defendWord: '應將',
myFaction: (you, sR, sB) => `我的陣營:${you} · 楚 ${sR} · 漢 ${sB}`,
chuFirst: '楚 · 先手', hanSecond: '漢 · 後手',
win: (s) => `${s} 方獲勝`,
outcomeWin: '勝', outcomeLose: '負',
factionWon: (s) => `${s} 方獲勝`,
youWon: '你獲勝了 · 下一局執漢',
youLost: '你落敗了 · 下一局執楚',
outcomeDraw: '和棋', drawLine: '和局',
drawStalemate: '無子可動 — 本局為和棋',
outcomeDrawBikjang: '和棋(逼將)',
drawBikjang: '逼將 — 由點數決定勝負',
scoreLead: (s, n) => `${s} 領先 ${n} 分`,
chuShort: '楚', hanShort: '漢',
byCheckmate: (s) => `將死 — ${s} 方獲勝`,
byTimeout: (s) => `超時 — ${s} 方獲勝`,
byRepetition: (s) => `重複局面 — ${s} 方獲勝`,
repetitionReason: '同一局面出現四次,本局結束',
undone: '已悔一步',
winHint: '若要再下一局,請點擊「重新開始」',
aiThinking: '對面正在思考',
aiWaking: '正在喚醒 AI 對手',
aiFailLong: '正在喚醒 AI 對手。視瀏覽器環境可能會短暫停頓。此刻你可以先自行研究棋局。',
aiRetry: '重新喚醒',
aiWatch: '只看棋盤',
perspMine: '輪到你行棋',
perspAi: '對手正在選擇走法',
perspHuman: (s) => `輪到 ${s} 行棋`,
levelTitle: '今天想怎麼下?',
modeCpu: '與電腦對弈', modeCpuSub: '與 AI 對手對局',
modeTutorial: '學習象棋', modeTutorialSub: '棋子走法與取勝之道',
modeRules: '象棋規則', modeRulesSub: '將軍 · 將死 · 取勝條件',
modeSettings: '設定', modeSettingsSub: '語言 · 棋盤背景',
modeHuman: '與人對弈', modeHumanSub: '敬請期待',
modeReview: '覆盤', modeReviewSub: '敬請期待',
modeComing: '該模式尚未開放',
levelPlayCpu: '與電腦對弈',
lvBeginnerName: '初學者', lvBeginnerSub: '為初次學習象棋的人準備的對手',
lvFriendName: '熟悉的棋友', lvFriendSub: '輕鬆對弈一局的對手',
lvMasterName: '老練的棋客', lvMasterSub: '不輕易露出破綻的對手',
lvExpertName: '國手', lvExpertSub: '不容許一絲破綻的對手',
levelNote: '請選擇心儀的對手 · 每局結束後可重新選擇',
settingsLangLabel: '語言',
settingsBtnLabel: '設定',
settingsBgLabel: '棋盤背景',
bgSansuHwa: '山水畫', bgSimple: '簡約', bgWood: '原木', bgSipjangsaeng: '十長生', bgPaper: '韓紙',
rulesTitle: '象棋規則',
rulesSubtitle: '開局前值得了解的基本規則',
rulesExLabel: '範例',
rulesClose: '關閉',
aboutTitle: '關於',
aboutClose: '關閉',
aboutSourceLabel: '來源 ↗',
aboutTagline: '用心製作',
aboutSections: [
{
title: '關於',
body: '韓國象棋(장기)是韓國傳統的策略棋類遊戲,常被介紹為韓國象棋(Korean chess)。',
},
{
title: '製作',
body: 'Hanrim',
link: { url: 'https://cozyshelter.tistory.com', label: 'Cozy Shelter' },
},
{
title: '圖像',
body: '棋盤與棋子設計 — 為本專案製作',
},
{
title: '音效',
items: [
{ label: '棋子音效(移動 · 吃子 · 選擇 · 將死)', by: 'taure', url: 'https://pixabay.com/sound-effects/board-game-pieces-59039/' },
{ label: '勝利', by: 'Sarah H', url: 'https://pixabay.com/users/astralsynthesizer-50776509/' },
{ label: '失敗', by: 'Universfield', url: 'https://pixabay.com/users/universfield-28281460/' },
],
},
{
title: 'AI 引擎',
body: 'Fairy-Stockfish · GPLv3',
link: { url: 'https://github.com/fairy-stockfish/Fairy-Stockfish', label: 'GitHub' },
},
],
rulesSections: [
{
title: '象棋的目標',
body: '韓國象棋是兩方對弈的韓國傳統棋類遊戲。將對方的將(楚或漢)逼入將死即可獲勝。核心在於守護己方的將,同時攻擊對方的將。',
example: null,
},
{
title: '將軍 (Check)',
body: '當對方的將在下一步可能被吃掉時,稱為將軍。被將軍時,必須解除威脅:將移到安全之處、擋住攻擊,或吃掉發動攻擊的棋子。',
example: '車在一條直線上正對對方的將。',
},
{
title: '雙將 (Double Check)',
body: '一步之內有兩枚棋子同時將軍,稱為雙將。由於無法一次擋住或吃掉兩個威脅,唯一的解法是把將移到安全格。若無處可移,即為將死。',
example: '原本擋在將前的棋子讓開,使它與其身後的棋子同時正對將。',
},
{
title: '將死 (Checkmate)',
body: '被將軍且無論如何都無法解除時,稱為將死。若將無法移動、攻擊無法阻擋、攻擊的棋子也無法吃掉,即為將死,此刻對局結束。',
example: '將無安全格可逃,也沒有棋子能擋住攻擊。',
},
{
title: '取勝條件',
body: '將對方逼入將死即可獲勝。此外,對方主動認輸時立即取勝。',
example: null,
},
{
title: '逼將(計分和棋)',
body: '雙方的將在同一縱線上相對,且兩者之間沒有任何棋子相隔,這種狀態稱為逼將。若輪到行棋的一方沒有任何合法走法可以解除這種相對,本局即以和棋結束。此時勝負由剩餘棋子的點數決定:車為13分,炮為7分,馬為5分,象與士各3分,兵(卒)為2分,將為0分。後手的漢方為彌補後行的不利,額外獲得1.5分。點數較高的一方在逼將和棋中占優。',
example: '雙方的將在同一縱線上相對,輪到行棋的一方既無法離開該縱線,也無法在兩者之間放子相隔。',
},
{
title: '重複局面',
body: '同一佈局在同一方行棋時反覆出現,稱為重複局面。同一局面最多可重複三次,但第四次造成相同局面的一方判負。與逼將不同,重複局面並非和棋,而是分出勝負的結束方式。此規則用於防止一方以連續將軍無限拖延對局。',
example: '一枚棋子來回移動反覆將軍,在第四次出現相同局面時,該方落敗。',
},
{
title: '須知',
body: '本遊戲在將軍、將死與認輸之外,還採用逼將(計分和棋)與重複局面規則。關於各棋子的走法與佈陣等更多內容,最好的學習方式就是親自對弈。',
example: null,
},
],
},
ja: {
sub: 'JANGGI · 韓国将棋(チャンギ)',
langLabel: '言語 :',
chooseFaction: '陣営を選んでください',
chuName: '楚(チョ)', hanName: '漢(ハン)',
chuSub: '先手 · 青緑', hanSub: '後手 · 朱',
factionNoteDefault: '選んだ陣営が画面の下側に配置されます · 先手は常に楚',
autoWon: '前局は勝利 — 今回は自動的に漢に割り当てられました。変更するには別の陣営を選んでください。',
autoLost: '前局は敗北 — 今回は自動的に楚に割り当てられました。変更するには別の陣営を選んでください。',
setupTitlePre: ' の駒組みを選んでください', // <b>楚</b> の駒組みを選んでください
autoPick: '自動選択(ランダム)',
turnLabel: 'の手番', mistStart: '対局開始',
undo: '待った', reset: '最初から', flip: '盤を反転',
resign: '投了', resignConfirm: '本当に投了しますか?',
resignYes: 'はい', resignNo: 'いいえ',
resigned: (s) => `${s} 投了 — 負けを認めました`,
capByChu: '楚が取った', capByHan: '漢が取った',
capChuEmpty: '楚が取った駒', capHanEmpty: '漢が取った駒',
movelogTitle: '棋 譜', movelogEmpty: 'まだ手がありません',
pickPiece: '駒を選んでください',
pickDest: '移動先を選んでください', cantMove: 'この駒は動かせません', cantMovePinned: '王を守っているため動かせません', cantMoveInCheck: '今は王手を防がなければなりません',
notYourTurn: (s) => `今は ${s} の手番です`,
check: (s) => `王手!${s} の王を守る手を指してください`,
checkWord: '王手',
defendWord: '受け',
myFaction: (you, sR, sB) => `自分の陣営:${you} · 楚 ${sR} · 漢 ${sB}`,
chuFirst: '楚 · 先手', hanSecond: '漢 · 後手',
win: (s) => `${s} の勝ち`,
outcomeWin: '勝ち', outcomeLose: '負け',
factionWon: (s) => `${s} の勝ち`,
youWon: '勝ちました · 次局は漢を持ちます',
youLost: '負けました · 次局は楚を持ちます',
outcomeDraw: '引き分け', drawLine: '引き分け',
drawStalemate: '指す手がなく引き分けです',
outcomeDrawBikjang: '引き分け(ビッカン)',
drawBikjang: 'ビッカン — 点数で決します',
scoreLead: (s, n) => `${s} が ${n} 点リード`,
chuShort: '楚', hanShort: '漢',
byCheckmate: (s) => `詰み — ${s} の勝ち`,
byTimeout: (s) => `時間切れ — ${s} の勝ち`,
byRepetition: (s) => `局面反復 — ${s} の勝ち`,
repetitionReason: '同じ局面が四回繰り返され、対局が終了しました',
undone: '一手戻しました',
winHint: 'もう一度指すには「最初から」を押してください',
aiThinking: '相手が少し考えています',
aiWaking: 'AI 対戦相手を起動しています',
aiFailLong: 'AI 対戦相手を起動しています。ブラウザ環境によっては少し止まることがあります。今は一人で盤を眺めることができます。',
aiRetry: 'もう一度起動',
aiWatch: '盤だけ見る',
perspMine: 'あなたの手番です',
perspAi: '相手が手を選んでいます',
perspHuman: (s) => `${s} の手番です`,
levelTitle: '今日はどう指しますか?',
modeCpu: 'コンピュータと対局', modeCpuSub: 'AI 相手と対局',
modeTutorial: '将棋を学ぶ', modeTutorialSub: '駒の動かし方と勝ち方',
modeRules: 'ルール', modeRulesSub: '王手 · 詰み · 勝利条件',
modeSettings: '設定', modeSettingsSub: '言語 · 盤の背景',
modeHuman: '人と対局', modeHumanSub: '準備中です',
modeReview: '振り返り', modeReviewSub: '準備中です',
modeComing: 'このモードはまだ利用できません',
levelPlayCpu: 'コンピュータと対局',
lvBeginnerName: '初心者', lvBeginnerSub: '初めて学ぶ方のための相手',
lvFriendName: '気軽な相手', lvFriendSub: '気軽に一局楽しめる相手',
lvMasterName: '熟練の打ち手', lvMasterSub: 'なかなか隙を見せない相手',
lvExpertName: '名人', lvExpertSub: '一切の隙を許さない相手',
levelNote: 'お好みの相手を選んでください · 一局終わるごとに選び直せます',
settingsLangLabel: '言語',
settingsBtnLabel: '設定',
settingsBgLabel: '盤の背景',
bgSansuHwa: '山水画', bgSimple: 'シンプル', bgWood: '木目', bgSipjangsaeng: '十長生', bgPaper: '韓紙',
rulesTitle: 'チャンギのルール',
rulesSubtitle: '対局を始める前に知っておきたい基本ルール',
rulesExLabel: '例',
rulesClose: '閉じる',
aboutTitle: 'このゲームについて',
aboutClose: '閉じる',
aboutSourceLabel: '出典 ↗',
aboutTagline: '心を込めて作りました',
aboutSections: [
{
title: 'このゲームについて',
body: 'チャンギ(장기)は韓国の伝統的な戦略ボードゲームで、しばしば「韓国将棋(Korean chess)」として紹介されます。',
},
{
title: '制作',
body: 'Hanrim',
link: { url: 'https://cozyshelter.tistory.com', label: 'Cozy Shelter' },
},
{
title: 'グラフィック',
body: '盤と駒のデザイン — 本プロジェクトのために制作',
},
{
title: 'サウンド',
items: [
{ label: '駒音(移動 · 捕獲 · 選択 · 詰み)', by: 'taure', url: 'https://pixabay.com/sound-effects/board-game-pieces-59039/' },
{ label: '勝利', by: 'Sarah H', url: 'https://pixabay.com/users/astralsynthesizer-50776509/' },
{ label: '敗北', by: 'Universfield', url: 'https://pixabay.com/users/universfield-28281460/' },
],
},
{
title: 'AIエンジン',
body: 'Fairy-Stockfish · GPLv3',
link: { url: 'https://github.com/fairy-stockfish/Fairy-Stockfish', label: 'GitHub' },
},
],
rulesSections: [
{
title: 'チャンギの目的',
body: 'チャンギは二つの陣営が対戦する韓国の伝統的なボードゲームです。相手の王(楚または漢)を詰みに追い込めば勝ちです。自分の王を守りながら相手の王を攻めるのが核心です。',
example: null,
},
{
title: '王手(Check)',
body: '相手の王を次の手で取れる状態を王手といいます。王手をかけられたら、必ず脅威から逃れなければなりません:王を安全な場所へ動かす、攻撃を遮る、または攻めている駒を取る、のいずれかです。',
example: '車が相手の王を一直線に狙っている場合。',
},
{
title: '両王手(Double Check)',
body: '一手で二つの駒が同時に王を狙う状態を両王手といいます。二つの脅威を一度に遮ったり取ったりはできないため、唯一の逃れ方は王を安全な升へ動かすことです。動かす先もなければ詰みです。',
example: '王の前を遮っていた駒がどき、その駒と背後の駒が同時に王を狙う場合。',
},
{
title: '詰み(Checkmate)',
body: '王手をかけられ、どうやっても逃れられない状態を詰みといいます。王を動かすことも、攻撃を遮ることも、攻めている駒を取ることもできなければ詰みであり、この瞬間に対局が終わります。',
example: '王の逃げ場がすべて塞がれ、攻撃を遮る駒もない場合。',
},
{
title: '勝利条件',
body: '相手を詰みに追い込めば勝ちです。また、相手が自ら投了した場合もその時点で勝ちとなります。',
example: null,
},
{
title: 'ビッカン(点数で決める引き分け)',
body: '両陣営の王が同じ縦の列で向かい合い、その間を遮る駒が一つもない状態をビッカン(Bikjang)といいます。手番の側がこの向かい合いを解く合法手を一つも持たないとき、対局は引き分けとなります。このとき勝敗は残った駒の点数で決めます。車は13点、包は7点、馬は5点、象と士は各3点、兵(卒)は2点、王は0点です。後手の漢は後に指す不利を補うため、1.5点が加算されます。点数の高い側がビッカンの引き分けで優位となります。',
example: '両王が同じ列で向かい合い、手番の側がその列から離れることも、間を遮ることもできない場合。',
},
{
title: '局面反復',
body: '同じ配置が同じ手番で繰り返し現れることを局面反復といいます。同じ局面は三回まで許されますが、四回目に同じ局面を作った側が反則負けとなります。ビッカンと違い、局面反復は引き分けではなく勝敗の決まる終局です。一方が王手を繰り返して対局を無限に引き延ばすのを防ぐためのルールです。',
example: '一つの駒が同じ場所を行き来して王手を繰り返す場合、四回目の同一局面でその側が負けとなります。',
},
{
title: '覚えておくこと',
body: 'このゲームは王手・詰み・投了に加えて、ビッカン(点数で決める引き分け)と局面反復にも対応しています。駒の動かし方や駒組みなど、さらに詳しい内容は、実際に指しながら覚えるのが一番です。',
example: null,
},
],
},
de: {
sub: 'JANGGI · Koreanisches Schach',
langLabel: 'Sprache :',
chooseFaction: 'Wähle deine Seite',
chuName: 'Cho (楚)', hanName: 'Han (漢)',
chuSub: 'Anziehend · Türkis', hanSub: 'Nachziehend · Rot',
factionNoteDefault: 'Deine Seite steht unten · Cho (楚) zieht immer zuerst',
autoWon: 'Letzte Partie gewonnen — diesmal Han (漢) zugewiesen. Wähle die andere Seite, um zu wechseln.',
autoLost: 'Letzte Partie verloren — diesmal Cho (楚) zugewiesen. Wähle die andere Seite, um zu wechseln.',
setupTitlePre: ' — wähle die Aufstellung', // <b>Cho (楚)</b> — wähle die Aufstellung
autoPick: 'Automatisch (zufällig)',
turnLabel: 'am Zug', mistStart: 'Spielbeginn',
undo: 'Zurücknehmen', reset: 'Neue Partie', flip: 'Brett drehen',
resign: 'Aufgeben', resignConfirm: 'Diese Partie aufgeben?',
resignYes: 'Ja', resignNo: 'Nein',
resigned: (s) => `${s} hat aufgegeben`,
capByChu: 'Cho hat geschlagen', capByHan: 'Han hat geschlagen',
capChuEmpty: 'Von Cho geschlagen', capHanEmpty: 'Von Han geschlagen',
movelogTitle: 'Züge', movelogEmpty: 'Noch keine Züge',
pickPiece: 'Wähle eine Figur',
pickDest: 'Wähle ein Zielfeld', cantMove: 'Diese Figur hat keine gültigen Züge', cantMovePinned: 'Sie deckt den General und kann nicht ziehen', cantMoveInCheck: 'Du musst zuerst das Schach abwehren',
notYourTurn: (s) => `${s} ist am Zug`,
check: (s) => `Schach! Rette den General von ${s}`,
checkWord: 'Schach',
defendWord: 'Abwehr',
myFaction: (you, sR, sB) => `Du: ${you} · Cho ${sR} · Han ${sB}`,
chuFirst: 'Cho (楚) · Anziehend', hanSecond: 'Han (漢) · Nachziehend',
win: (s) => `${s} gewinnt`,
outcomeWin: 'Sieg', outcomeLose: 'Niederlage',
factionWon: (s) => `${s} gewinnt`,
youWon: 'Du hast gewonnen · nächste Partie spielst du Han (漢)',
youLost: 'Du hast verloren · nächste Partie spielst du Cho (楚)',
outcomeDraw: 'Remis', drawLine: 'Patt',
drawStalemate: 'Keine gültigen Züge — die Partie endet remis',
outcomeDrawBikjang: 'Remis (Bikjang)',
drawBikjang: 'Bikjang — Entscheidung nach Punkten',
scoreLead: (s, n) => `${s} führt mit ${n} Punkten`,
chuShort: 'Cho', hanShort: 'Han',
byCheckmate: (s) => `Schachmatt — ${s} gewinnt`,
byTimeout: (s) => `Zeitüberschreitung — ${s} gewinnt`,
byRepetition: (s) => `Stellungswiederholung — ${s} gewinnt`,
repetitionReason: 'Dieselbe Stellung trat viermal auf, die Partie endet',
undone: 'Zug zurückgenommen',
winHint: 'Drücke „Neue Partie“, um erneut zu spielen',
aiThinking: 'Dein Gegner überlegt',
aiWaking: 'KI-Gegner wird gestartet',
aiFailLong: 'KI-Gegner wird gestartet. Je nach Browser kann es kurz stocken. Bis dahin kannst du das Brett allein studieren.',
aiRetry: 'Erneut starten',
aiWatch: 'Nur das Brett ansehen',
perspMine: 'Du bist am Zug',
perspAi: 'Dein Gegner wählt einen Zug',
perspHuman: (s) => `${s} ist am Zug`,
levelTitle: 'Wie möchtest du heute spielen?',
modeCpu: 'Gegen den Computer', modeCpuSub: 'Gegen die KI spielen',
modeTutorial: 'Janggi lernen', modeTutorialSub: 'Wie Figuren ziehen und wie man gewinnt',
modeRules: 'Regeln', modeRulesSub: 'Schach, Schachmatt, Siegbedingungen',
modeSettings: 'Einstellungen', modeSettingsSub: 'Sprache · Bretthintergrund',
modeHuman: 'Gegen einen Freund', modeHumanSub: 'Demnächst verfügbar',
modeReview: 'Partie analysieren', modeReviewSub: 'Demnächst verfügbar',
modeComing: 'Dieser Modus ist noch nicht verfügbar',
levelPlayCpu: 'Gegen den Computer',
lvBeginnerName: 'Anfänger', lvBeginnerSub: 'Für alle, die Janggi zum ersten Mal lernen',
lvFriendName: 'Vertrauter Gegner', lvFriendSub: 'Ein entspannter Gegner für eine lockere Partie',
lvMasterName: 'Erfahrener Spieler', lvMasterSub: 'Lässt selten eine Lücke',
lvExpertName: 'Meister', lvExpertSub: 'Lässt nicht die kleinste Lücke zu',
levelNote: 'Wähle deinen Gegner · nach jeder Partie kannst du neu wählen',
settingsLangLabel: 'Sprache',
settingsBtnLabel: 'Einstellungen',
settingsBgLabel: 'Bretthintergrund',
bgSansuHwa: 'Tuschmalerei', bgSimple: 'Schlicht', bgWood: 'Holz', bgSipjangsaeng: 'Sipjangsaeng', bgPaper: 'Hanji',
rulesTitle: 'Janggi-Regeln',
rulesSubtitle: 'Die Grundlagen, die du vor deiner ersten Partie kennen solltest',
rulesExLabel: 'Beispiel',
rulesClose: 'Schließen',
aboutTitle: 'Über',
aboutClose: 'Schließen',
aboutSourceLabel: 'Quelle ↗',
aboutTagline: 'Mit Sorgfalt erstellt',
aboutSections: [
{
title: 'Über',
body: 'Janggi ist ein traditionelles koreanisches Strategie-Brettspiel, oft als koreanisches Schach (Korean chess) bezeichnet.',
},
{
title: 'Erstellt von',
body: 'Hanrim',
link: { url: 'https://cozyshelter.tistory.com', label: 'Cozy Shelter' },
},
{
title: 'Grafik',
body: 'Brett- & Figurendesign — für dieses Projekt erstellt',
},
{
title: 'Sound',
items: [
{ label: 'Brettspiel-Figurensounds (Zug · Schlag · Auswahl · Matt)', by: 'taure', url: 'https://pixabay.com/sound-effects/board-game-pieces-59039/' },
{ label: 'Sieg', by: 'Sarah H', url: 'https://pixabay.com/users/astralsynthesizer-50776509/' },
{ label: 'Niederlage', by: 'Universfield', url: 'https://pixabay.com/users/universfield-28281460/' },
],
},
{
title: 'KI-Engine',
body: 'Fairy-Stockfish · GPLv3',
link: { url: 'https://github.com/fairy-stockfish/Fairy-Stockfish', label: 'GitHub' },
},
],
rulesSections: [
{
title: 'Das Ziel',
body: 'Janggi ist ein traditionelles koreanisches Brettspiel für zwei Seiten. Du gewinnst, indem du den General des Gegners (楚 oder 漢) schachmatt setzt. Der Kern besteht darin, den eigenen General zu schützen und zugleich den gegnerischen anzugreifen.',
example: null,
},
{
title: 'Schach (Check)',
body: 'Wenn der General des Gegners im nächsten Zug geschlagen werden könnte, steht er im Schach. Ein General im Schach muss der Bedrohung entkommen: auf ein sicheres Feld ziehen, den Angriff blockieren oder die angreifende Figur schlagen.',
example: 'Ein Wagen steht in direkter Linie dem gegnerischen General gegenüber.',
},
{
title: 'Doppelschach (Double Check)',
body: 'Wenn ein einziger Zug zwei Figuren zugleich den General angreifen lässt, ist es ein Doppelschach. Da man nicht beide Bedrohungen auf einmal blockieren oder schlagen kann, bleibt nur, den General auf ein sicheres Feld zu ziehen. Hat er kein Feld mehr, ist es Schachmatt.',
example: 'Eine Figur, die den General deckte, zieht beiseite, sodass sie und die Figur dahinter den General gleichzeitig angreifen.',
},
{
title: 'Schachmatt (Checkmate)',
body: 'Wenn ein General im Schach steht und auf keine Weise entkommen kann, ist es Schachmatt. Kann der General nicht ziehen, der Angriff nicht blockiert und die angreifende Figur nicht geschlagen werden, ist es Schachmatt und die Partie endet.',
example: 'Der General hat kein sicheres Feld zur Flucht, und keine Figur kann den Angriff blockieren.',
},
{
title: 'Wie man gewinnt',
body: 'Setze den Gegner schachmatt, um zu gewinnen. Du gewinnst auch sofort, wenn der Gegner aufgibt.',
example: null,
},
{
title: 'Bikjang (Remis nach Punkten)',
body: 'Bikjang liegt vor, wenn beide Generäle einander auf derselben Linie gegenüberstehen und kein Stein zwischen ihnen steht. Hat die Seite am Zug keinen legalen Weg, dieses Gegenüber aufzulösen, endet die Partie remis. Über den Ausgang entscheiden dann die Punkte der verbliebenen Steine: der Wagen zählt 13, die Kanone 7, das Pferd 5, Elefant und Wächter je 3, der Soldat 2 und der General 0. Han (漢), das als Zweiter zieht, erhält 1.5 Punkte zusätzlich als Ausgleich für den späteren Zug. Die Seite mit mehr Punkten geht aus dem Bikjang-Remis als überlegen hervor.',
example: 'Beide Generäle stehen einander auf derselben Linie gegenüber, und die Seite am Zug kann weder die Linie verlassen noch den Raum dazwischen versperren.',
},
{
title: 'Stellungswiederholung',