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:
117
internal/lang/knowledge.go
Normal file
117
internal/lang/knowledge.go
Normal file
@@ -0,0 +1,117 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user