M3: japanska skyltar – pixelfont, ordbok, kunskap, delvis översättning, hjälp
All checks were successful
release / build-release (push) Successful in 1m42s
All checks were successful
release / build-release (push) Successful in 1m42s
- resources/text/fonts: PixelMplus 10/12 px (M+ FONT LICENSE) + licens; all text ritas via text/v2 på heltalspositioner, debugfonten är borta - internal/lang: ordbok (60 ord, validerad), tokenisering med längsta matchning, Knowledge (ord + kana, nivå 0–5, hintar), Render (kända ord markerade/översatta, okända kvar, kana som romaji), EnglishShare/ ShowEnglish per svårighet (deterministiskt per bana+position), HelpSeconds; datatest kräver att all japanska i banorna finns i ordboken - sim.Run: AskHelp (H) med väntetid efter nivå, avbryts om man går, avslöjar skylten och lär första okända ordet; Knowledge delas mellan banor - game: skylt- och avgångstavlor renderade genom kunskapen med färgkodning, engelsk rad på lätta banor, hjälp-förlopp, "Nytt ord"-notis, HUD visar resmålet på japanska från nivå 1 och japanska-nivå; meny med svårighet; pixelbaserad radbrytning av färgade textsegment; -know-flagga - tools/shots: tre skyltbilder (skylt, hjälp, nytt ord) med förkunskap - docs/README/wiki uppdaterade Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
@@ -1,13 +1,28 @@
|
||||
package sim
|
||||
|
||||
import (
|
||||
"math"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/lang"
|
||||
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/level"
|
||||
"gitea.brasse-pc.eu/brasse/metro-puzzle-jp/internal/res"
|
||||
)
|
||||
|
||||
func loadDict(t *testing.T) *lang.Dictionary {
|
||||
t.Helper()
|
||||
b, err := res.ReadFile("text", "jp-dictionary.json")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
d, err := lang.ParseDictionary(b)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return d
|
||||
}
|
||||
|
||||
const frame = 1.0 / 60
|
||||
|
||||
func loadLevel(t *testing.T, id string) *level.Level {
|
||||
@@ -75,7 +90,7 @@ func waitForTrain(t *testing.T, r *Run) *level.Train {
|
||||
// och kliver på – helt utan fönster.
|
||||
func TestBotWinsLevel001(t *testing.T) {
|
||||
l := loadLevel(t, "001-flygplatsen")
|
||||
r := NewRun(l, 6)
|
||||
r := NewRun(l, 6, nil, nil)
|
||||
goal := l.Find(level.Platform)[0]
|
||||
path := l.Path(r.Player.Tile, goal, r.CanEnter)
|
||||
if path == nil {
|
||||
@@ -111,7 +126,7 @@ func TestBotWinsLevel001(t *testing.T) {
|
||||
|
||||
func TestBotLosesOnWrongTrain(t *testing.T) {
|
||||
l := loadLevel(t, "001-flygplatsen")
|
||||
r := NewRun(l, 6)
|
||||
r := NewRun(l, 6, nil, nil)
|
||||
goal := l.Find(level.WrongPlatform)[0]
|
||||
walk(t, r, l.Path(r.Player.Tile, goal, r.CanEnter))
|
||||
tr := waitForTrain(t, r)
|
||||
@@ -123,7 +138,7 @@ func TestBotLosesOnWrongTrain(t *testing.T) {
|
||||
|
||||
func TestGateNeedsCardBalance(t *testing.T) {
|
||||
l := loadLevel(t, "001-flygplatsen")
|
||||
r := NewRun(l, 6)
|
||||
r := NewRun(l, 6, nil, nil)
|
||||
gate := l.Find(level.Gate)[0]
|
||||
r.Card.Balance = 0
|
||||
if r.CanEnter(gate) {
|
||||
@@ -150,7 +165,7 @@ func TestGateNeedsCardBalance(t *testing.T) {
|
||||
|
||||
func TestBoardOnlyWhenTrainPresent(t *testing.T) {
|
||||
l := loadLevel(t, "001-flygplatsen")
|
||||
r := NewRun(l, 6)
|
||||
r := NewRun(l, 6, nil, nil)
|
||||
goal := l.Find(level.Platform)[0]
|
||||
walk(t, r, l.Path(r.Player.Tile, goal, r.CanEnter))
|
||||
// Spola fram till en tidpunkt då tåget är borta.
|
||||
@@ -169,7 +184,7 @@ func TestBoardOnlyWhenTrainPresent(t *testing.T) {
|
||||
|
||||
func TestSignHereOnlyWhenStill(t *testing.T) {
|
||||
l := loadLevel(t, "001-flygplatsen")
|
||||
r := NewRun(l, 6)
|
||||
r := NewRun(l, 6, nil, nil)
|
||||
sign := l.Find(level.Sign)[0]
|
||||
walk(t, r, l.Path(r.Player.Tile, sign, r.CanEnter))
|
||||
if sg := r.SignHere(); sg == nil || sg.Text.Get("sv") == "" {
|
||||
@@ -180,3 +195,69 @@ func TestSignHereOnlyWhenStill(t *testing.T) {
|
||||
t.Fatal("i rörelse visas ingen skylt")
|
||||
}
|
||||
}
|
||||
|
||||
// TestAskHelpRevealsSignAndTeachesWord: att fråga om hjälp tar
|
||||
// lang.HelpSeconds, avslöjar skylten och lär spelaren ett ord från den.
|
||||
func TestAskHelpRevealsSignAndTeachesWord(t *testing.T) {
|
||||
l := loadLevel(t, "001-flygplatsen")
|
||||
know := lang.NewKnowledge()
|
||||
r := NewRun(l, 6, loadDict(t), know)
|
||||
sign := l.Find(level.Sign)[1]
|
||||
walk(t, r, l.Path(r.Player.Tile, sign, r.CanEnter))
|
||||
|
||||
if !r.AskHelp() {
|
||||
t.Fatal("ska kunna fråga om hjälp på en skylt")
|
||||
}
|
||||
if r.AskHelp() {
|
||||
t.Fatal("inte två hjälp samtidigt")
|
||||
}
|
||||
want := lang.HelpSeconds(0)
|
||||
if math.Abs(r.Help.Total-want) > 1e-9 {
|
||||
t.Fatalf("hjälptid %.1f, vill ha %.1f", r.Help.Total, want)
|
||||
}
|
||||
frames := int(want/frame) - 2
|
||||
for i := 0; i < frames; i++ {
|
||||
r.Step(frame, None, false)
|
||||
}
|
||||
if !r.Help.Active || r.SignRevealed(sign) || r.HelpProgress() < 0.9 {
|
||||
t.Fatalf("strax före klart: active=%v revealed=%v progress=%.2f", r.Help.Active, r.SignRevealed(sign), r.HelpProgress())
|
||||
}
|
||||
for i := 0; i < 4; i++ {
|
||||
r.Step(frame, None, false)
|
||||
}
|
||||
if r.Help.Active || !r.SignRevealed(sign) {
|
||||
t.Fatal("hjälpen ska vara klar och skylten avslöjad")
|
||||
}
|
||||
if know.Count() != 1 || r.Learned == "" || !know.Knows(r.Learned) || know.Helps != 1 {
|
||||
t.Fatalf("lärde sig fel: count=%d learned=%q helps=%d", know.Count(), r.Learned, know.Helps)
|
||||
}
|
||||
if first := lang.FirstUnknownWord(r.Dict, lang.NewKnowledge(), l.SignAt(sign).Text["ja"]); first != r.Learned {
|
||||
t.Fatalf("ska lära sig första okända ordet %q, lärde sig %q", first, r.Learned)
|
||||
}
|
||||
if r.AskHelp() {
|
||||
t.Fatal("en avslöjad skylt behöver ingen hjälp")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHelpIsCancelledByWalkingAway(t *testing.T) {
|
||||
l := loadLevel(t, "001-flygplatsen")
|
||||
r := NewRun(l, 6, loadDict(t), nil)
|
||||
sign := l.Find(level.Sign)[1]
|
||||
walk(t, r, l.Path(r.Player.Tile, sign, r.CanEnter))
|
||||
r.AskHelp()
|
||||
r.Step(frame, Left, false)
|
||||
if r.Help.Active {
|
||||
t.Fatal("att gå iväg ska avbryta hjälpen")
|
||||
}
|
||||
if r.Know.Count() != 0 || r.SignRevealed(sign) {
|
||||
t.Fatal("inget lärt, inget avslöjat")
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoHelpOffSign(t *testing.T) {
|
||||
l := loadLevel(t, "001-flygplatsen")
|
||||
r := NewRun(l, 6, loadDict(t), nil)
|
||||
if r.AskHelp() {
|
||||
t.Fatal("ingen skylt på startrutan")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user