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>
118 lines
3.1 KiB
Go
118 lines
3.1 KiB
Go
package lang
|
|
|
|
import "math"
|
|
|
|
// Knowledge är vad spelaren kan: ord ur ordboken och om hen kan läsa kana.
|
|
type Knowledge struct {
|
|
Words map[string]bool `json:"words"`
|
|
Kana bool `json:"kana"`
|
|
Helps int `json:"helps"` // antal gånger spelaren frågat om hjälp
|
|
}
|
|
|
|
// KanaUnlockWords: när så här många kända ord innehåller kana "lossnar"
|
|
// kana-läsningen och alla kana-ord går att läsa (som romaji) utan att förstås.
|
|
const KanaUnlockWords = 3
|
|
|
|
// WordsPerLevel styr japanska-nivån: nivå = kända ord / WordsPerLevel, max MaxLevel.
|
|
const (
|
|
WordsPerLevel = 4
|
|
MaxLevel = 5
|
|
)
|
|
|
|
// NewKnowledge börjar från noll.
|
|
func NewKnowledge() *Knowledge { return &Knowledge{Words: map[string]bool{}} }
|
|
|
|
// Knows rapporterar om ordet är känt.
|
|
func (k *Knowledge) Knows(word string) bool { return k.Words[word] }
|
|
|
|
// Learn lär in ett ord. Returnerar true om det var nytt. Låser upp kana när
|
|
// tillräckligt många kana-ord är kända.
|
|
func (k *Knowledge) Learn(word string) bool {
|
|
if k.Words == nil {
|
|
k.Words = map[string]bool{}
|
|
}
|
|
if k.Words[word] {
|
|
return false
|
|
}
|
|
k.Words[word] = true
|
|
if !k.Kana {
|
|
n := 0
|
|
for w := range k.Words {
|
|
if HasKana(w) {
|
|
n++
|
|
}
|
|
}
|
|
if n >= KanaUnlockWords {
|
|
k.Kana = true
|
|
}
|
|
}
|
|
return true
|
|
}
|
|
|
|
// Count är antalet kända ord.
|
|
func (k *Knowledge) Count() int { return len(k.Words) }
|
|
|
|
// Level är japanska-nivån 0..MaxLevel.
|
|
func (k *Knowledge) Level() int {
|
|
return int(math.Min(MaxLevel, float64(k.Count()/WordsPerLevel)))
|
|
}
|
|
|
|
// Hintar som växer med nivån.
|
|
|
|
// ShowsDestinationJP: från nivå 1 visar HUD:en resmålet även på japanska så
|
|
// att man kan matcha tecknen mot skyltarna.
|
|
func (k *Knowledge) ShowsDestinationJP() bool { return k.Level() >= 1 }
|
|
|
|
// ShowsReadings: från nivå 2 visas läsningen (romaji) för kända ord på skyltar.
|
|
func (k *Knowledge) ShowsReadings() bool { return k.Level() >= 2 }
|
|
|
|
// HelpSeconds är hur länge det tar att få hjälp av en förbipasserande.
|
|
// Bättre japanska = snabbare, men aldrig under HelpMinSeconds.
|
|
const (
|
|
HelpBaseSeconds = 6.0
|
|
HelpPerLevel = 0.8
|
|
HelpMinSeconds = 2.0
|
|
)
|
|
|
|
// HelpSeconds beräknar väntetiden för en given nivå.
|
|
func HelpSeconds(level int) float64 {
|
|
return math.Max(HelpMinSeconds, HelpBaseSeconds-HelpPerLevel*float64(level))
|
|
}
|
|
|
|
// EnglishShare är andelen skyltar som också visar engelska, per svårighet.
|
|
func EnglishShare(difficulty int) float64 {
|
|
switch {
|
|
case difficulty <= 1:
|
|
return 1.0
|
|
case difficulty == 2:
|
|
return 0.6
|
|
case difficulty == 3:
|
|
return 0.3
|
|
case difficulty == 4:
|
|
return 0.1
|
|
default:
|
|
return 0
|
|
}
|
|
}
|
|
|
|
// ShowEnglish avgör deterministiskt om skylten på (x, y) i en bana visar
|
|
// engelska: banor är statiska så valet hashas från bana + position, inte
|
|
// från spelomgångens seed.
|
|
func ShowEnglish(levelID string, x, y, difficulty int) bool {
|
|
share := EnglishShare(difficulty)
|
|
if share >= 1 {
|
|
return true
|
|
}
|
|
if share <= 0 {
|
|
return false
|
|
}
|
|
h := uint32(2166136261)
|
|
for _, b := range []byte(levelID) {
|
|
h = (h ^ uint32(b)) * 16777619
|
|
}
|
|
for _, v := range []int{x, y} {
|
|
h = (h ^ uint32(v+7)) * 16777619
|
|
}
|
|
return float64(h%1000)/1000 < share
|
|
}
|