Compare commits
98 Commits
ecc602e040
...
ephemeral-
| Author | SHA1 | Date | |
|---|---|---|---|
| 02c543922d | |||
| 63659a4934 | |||
| af4e06411d | |||
| 4426925ebb | |||
| 8632bb8c3c | |||
| 7ff271ff2d | |||
| 5540bb2c4e | |||
| 9a73f2147b | |||
| 01b88994f3 | |||
| 424f65dc8a | |||
| cf814c6c72 | |||
| 7972bab46c | |||
| 3fa30903cd | |||
| 9ef568202c | |||
| b0e422ac01 | |||
| 2184515938 | |||
| 489457c63c | |||
| 9b3c9b22aa | |||
| 1f2824246f | |||
| 63e8327125 | |||
| e608363a24 | |||
| e3903e5312 | |||
| 5e7103befd | |||
| 0723fe0c6a | |||
| cbe08cd8a7 | |||
| db3e18e586 | |||
| 8fb29a953c | |||
| c00d3d0186 | |||
| a534975601 | |||
| b55e1bc200 | |||
| c58dbd19b0 | |||
| 2fcd608102 | |||
| 546f2db327 | |||
| 856c94723f | |||
| 2393bf2fa5 | |||
| bf06de0f5e | |||
| f3f070ca2b | |||
| 34edcf97a7 | |||
| 9dd18ed972 | |||
| 332cf3f13a | |||
| c5a1cdea5f | |||
| 542d4198e7 | |||
| 98afe7384a | |||
| e890108591 | |||
| 0126101b1b | |||
| 5bf2588d41 | |||
| a5f84754ea | |||
| 4bfb06b682 | |||
| 72b8bc9c6c | |||
| 1ae654c266 | |||
| 74ee76c5da | |||
| 36d27f1642 | |||
| 9469c2c7a2 | |||
| a8921e9cf6 | |||
| f9ad769d9f | |||
| ec2efee5d5 | |||
| d147d71519 | |||
| b22b77c535 | |||
| b98513864a | |||
| 5a1194358b | |||
| 43d02b4b00 | |||
| e6032f995f | |||
| a86aa0daeb | |||
| cb1c51db05 | |||
| 5576dd8d3f | |||
| 19fb713aaa | |||
| dc2094bd50 | |||
| b0c555f547 | |||
| 7e06c23175 | |||
| 49d809d695 | |||
| f32cc1e651 | |||
| 8059c07ebf | |||
| a04ec970f2 | |||
| b844c4c24c | |||
| c5b5585f1e | |||
| 16067a1acc | |||
| d6fb4b6caf | |||
| 5b5e008b5e | |||
| 079b996f5a | |||
| c9a7e15f89 | |||
| be41389006 | |||
| 399f160718 | |||
| fae0fa7699 | |||
| 107a85b384 | |||
| 05688a08e2 | |||
| 5bcdd45b10 | |||
| 96c6273419 | |||
| 7d8c6ae82e | |||
| 024742c053 | |||
| cdcd4b9b7f | |||
| d13bc45d67 | |||
| b134bea670 | |||
| 4bd9100954 | |||
| 0cbc0f93b5 | |||
| bfc497b6fc | |||
| e181e4c833 | |||
| 3282a36397 | |||
| a02495e317 |
6
README.md
Normal file
6
README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# horse
|
||||||
|
|
||||||
|
Models, data, algorithms, and tools for Umamusume: Pretty Derby.
|
||||||
|
|
||||||
|
Data is generated from the game's local database.
|
||||||
|
Algorithms come from Erzzy and Kireina's reference, KuromiAK's race mechanics reference, and Crazyfellow's parenting and gene guide.
|
||||||
1
cmd/horsebot/.gitignore
vendored
Normal file
1
cmd/horsebot/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
token
|
||||||
10
cmd/horsebot/README.md
Normal file
10
cmd/horsebot/README.md
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
# horsebot
|
||||||
|
|
||||||
|
Discord bot serving horse game data.
|
||||||
|
|
||||||
|
Production instance is named Zenno Rob Roy, because she has read all about Umamusume and is always happy to share her knowledge and give recommendations.
|
||||||
|
|
||||||
|
## Running
|
||||||
|
|
||||||
|
The bot always uses the Gateway API.
|
||||||
|
If the `-http` argument is provided, it will also use the HTTP API, and `-key` must also be provided.
|
||||||
56
cmd/horsebot/autocomplete/autocomplete.go
Normal file
56
cmd/horsebot/autocomplete/autocomplete.go
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
package autocomplete
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"cmp"
|
||||||
|
"slices"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/junegunn/fzf/src/algo"
|
||||||
|
"github.com/junegunn/fzf/src/util"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Set is an autocomplete set.
|
||||||
|
type Set[V any] struct {
|
||||||
|
keys []util.Chars
|
||||||
|
vals []V
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add associates a value with a key in the autocomplete set.
|
||||||
|
// The behavior is undefined if the key already has a value.
|
||||||
|
func (s *Set[V]) Add(key string, val V) {
|
||||||
|
k := util.ToChars([]byte(key))
|
||||||
|
i, _ := slices.BinarySearchFunc(s.keys, k, func(a, b util.Chars) int {
|
||||||
|
return bytes.Compare(a.Bytes(), b.Bytes())
|
||||||
|
})
|
||||||
|
s.keys = slices.Insert(s.keys, i, k)
|
||||||
|
s.vals = slices.Insert(s.vals, i, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find appends to r all values in the set with keys that key matches.
|
||||||
|
func (s *Set[V]) Find(r []V, key string) []V {
|
||||||
|
initFzf()
|
||||||
|
var (
|
||||||
|
p = []rune(key)
|
||||||
|
|
||||||
|
got []V
|
||||||
|
t []algo.Result
|
||||||
|
slab util.Slab
|
||||||
|
)
|
||||||
|
for i := range s.keys {
|
||||||
|
res, _ := algo.FuzzyMatchV2(false, true, true, &s.keys[i], p, false, &slab)
|
||||||
|
if res.Score <= 0 {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
j, _ := slices.BinarySearchFunc(t, res, func(a, b algo.Result) int { return -cmp.Compare(a.Score, b.Score) })
|
||||||
|
// Insert after all other matches with the same score for stability.
|
||||||
|
for j < len(t) && t[j].Score == res.Score {
|
||||||
|
j++
|
||||||
|
}
|
||||||
|
t = slices.Insert(t, j, res)
|
||||||
|
got = slices.Insert(got, j, s.vals[i])
|
||||||
|
}
|
||||||
|
return append(r, got...)
|
||||||
|
}
|
||||||
|
|
||||||
|
var initFzf = sync.OnceFunc(func() { algo.Init("default") })
|
||||||
70
cmd/horsebot/autocomplete/autocomplete_test.go
Normal file
70
cmd/horsebot/autocomplete/autocomplete_test.go
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
package autocomplete_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"slices"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.sunturtle.xyz/zephyr/horse/cmd/horsebot/autocomplete"
|
||||||
|
)
|
||||||
|
|
||||||
|
func these(s ...string) []string { return s }
|
||||||
|
|
||||||
|
func TestAutocomplete(t *testing.T) {
|
||||||
|
cases := []struct {
|
||||||
|
name string
|
||||||
|
add []string
|
||||||
|
search string
|
||||||
|
want []string
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "empty",
|
||||||
|
add: nil,
|
||||||
|
search: "",
|
||||||
|
want: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "exact",
|
||||||
|
add: these("bocchi"),
|
||||||
|
search: "bocchi",
|
||||||
|
want: these("bocchi"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "extra",
|
||||||
|
add: these("bocchi", "ryo", "nijika", "kita"),
|
||||||
|
search: "bocchi",
|
||||||
|
want: these("bocchi"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "short",
|
||||||
|
add: these("bocchi", "ryo", "nijika", "kita"),
|
||||||
|
search: "o",
|
||||||
|
want: these("bocchi", "ryo"),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "unrelated",
|
||||||
|
add: these("bocchi", "ryo", "nijika", "kita"),
|
||||||
|
search: "x",
|
||||||
|
want: nil,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "map",
|
||||||
|
add: these("Corazón ☆ Ardiente"),
|
||||||
|
search: "corazo",
|
||||||
|
want: these("Corazón ☆ Ardiente"),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
t.Run(c.name, func(t *testing.T) {
|
||||||
|
var set autocomplete.Set[string]
|
||||||
|
for _, s := range c.add {
|
||||||
|
set.Add(s, s)
|
||||||
|
}
|
||||||
|
got := set.Find(nil, c.search)
|
||||||
|
slices.Sort(c.want)
|
||||||
|
slices.Sort(got)
|
||||||
|
if !slices.Equal(c.want, got) {
|
||||||
|
t.Errorf("wrong results: want %q, got %q", c.want, got)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
55
cmd/horsebot/log.go
Normal file
55
cmd/horsebot/log.go
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
"github.com/disgoorg/disgo/discord"
|
||||||
|
"github.com/disgoorg/disgo/handler"
|
||||||
|
)
|
||||||
|
|
||||||
|
func logMiddleware(next handler.Handler) handler.Handler {
|
||||||
|
return func(e *handler.InteractionEvent) error {
|
||||||
|
var msg string
|
||||||
|
attrs := make([]slog.Attr, 0, 8)
|
||||||
|
attrs = append(attrs,
|
||||||
|
slog.Uint64("interaction", uint64(e.Interaction.ID())),
|
||||||
|
slog.Uint64("user", uint64(e.Interaction.User().ID)),
|
||||||
|
)
|
||||||
|
if guild := e.Interaction.GuildID(); guild != nil {
|
||||||
|
attrs = append(attrs, slog.String("guild", guild.String()))
|
||||||
|
}
|
||||||
|
switch i := e.Interaction.(type) {
|
||||||
|
case discord.ApplicationCommandInteraction:
|
||||||
|
msg = "command"
|
||||||
|
attrs = append(attrs,
|
||||||
|
slog.String("name", i.Data.CommandName()),
|
||||||
|
slog.Int("type", int(i.Data.Type())),
|
||||||
|
)
|
||||||
|
switch data := i.Data.(type) {
|
||||||
|
case discord.SlashCommandInteractionData:
|
||||||
|
attrs = append(attrs, slog.String("path", data.CommandPath()))
|
||||||
|
}
|
||||||
|
|
||||||
|
case discord.AutocompleteInteraction:
|
||||||
|
msg = "autocomplete"
|
||||||
|
attrs = append(attrs,
|
||||||
|
slog.String("name", i.Data.CommandName),
|
||||||
|
slog.String("path", i.Data.CommandPath()),
|
||||||
|
slog.String("focus", i.Data.Focused().Name),
|
||||||
|
)
|
||||||
|
|
||||||
|
case discord.ComponentInteraction:
|
||||||
|
msg = "component"
|
||||||
|
attrs = append(attrs,
|
||||||
|
slog.Int("type", int(i.Data.Type())),
|
||||||
|
slog.String("custom", i.Data.CustomID()),
|
||||||
|
)
|
||||||
|
|
||||||
|
default:
|
||||||
|
slog.WarnContext(e.Ctx, "unknown interaction", slog.Any("event", e))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
slog.LogAttrs(e.Ctx, slog.LevelInfo, msg, attrs...)
|
||||||
|
return next(e)
|
||||||
|
}
|
||||||
|
}
|
||||||
173
cmd/horsebot/main.go
Normal file
173
cmd/horsebot/main.go
Normal file
@@ -0,0 +1,173 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/disgoorg/disgo"
|
||||||
|
"github.com/disgoorg/disgo/bot"
|
||||||
|
"github.com/disgoorg/disgo/discord"
|
||||||
|
"github.com/disgoorg/disgo/handler"
|
||||||
|
"github.com/disgoorg/disgo/handler/middleware"
|
||||||
|
"github.com/disgoorg/disgo/httpserver"
|
||||||
|
"github.com/disgoorg/disgo/rest"
|
||||||
|
|
||||||
|
"git.sunturtle.xyz/zephyr/horse/horse"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
dataDir string
|
||||||
|
tokenFile string
|
||||||
|
// http api options
|
||||||
|
addr string
|
||||||
|
route string
|
||||||
|
pubkey string
|
||||||
|
// logging options
|
||||||
|
level slog.Level
|
||||||
|
textfmt string
|
||||||
|
)
|
||||||
|
flag.StringVar(&dataDir, "data", "", "`dir`ectory containing exported json data")
|
||||||
|
flag.StringVar(&tokenFile, "token", "", "`file` containing the Discord bot token")
|
||||||
|
flag.StringVar(&addr, "http", "", "`address` to bind HTTP API server")
|
||||||
|
flag.StringVar(&route, "route", "/interactions/callback", "`path` to serve HTTP API calls")
|
||||||
|
flag.StringVar(&pubkey, "key", "", "Discord public key")
|
||||||
|
flag.TextVar(&level, "log", slog.LevelInfo, "slog logging `level`")
|
||||||
|
flag.StringVar(&textfmt, "log-format", "text", "slog logging `format`, text or json")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
var lh slog.Handler
|
||||||
|
switch textfmt {
|
||||||
|
case "text":
|
||||||
|
lh = slog.NewTextHandler(os.Stderr, &slog.HandlerOptions{Level: level})
|
||||||
|
case "json":
|
||||||
|
lh = slog.NewJSONHandler(os.Stderr, &slog.HandlerOptions{Level: level})
|
||||||
|
default:
|
||||||
|
fmt.Fprintf(os.Stderr, "invalid log format %q, must be text or json", textfmt)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
slog.SetDefault(slog.New(lh))
|
||||||
|
|
||||||
|
skills, err := loadSkills(filepath.Join(dataDir, "skill.json"))
|
||||||
|
slog.Info("loaded skills", slog.Int("count", len(skills)))
|
||||||
|
groups, err2 := loadSkillGroups(filepath.Join(dataDir, "skill-group.json"))
|
||||||
|
slog.Info("loaded skill groups", slog.Int("count", len(groups)))
|
||||||
|
if err = errors.Join(err, err2); err != nil {
|
||||||
|
slog.Error("loading data", slog.Any("err", err))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
skillSrv := newSkillServer(skills, groups)
|
||||||
|
slog.Info("skill server ready")
|
||||||
|
|
||||||
|
token, err := os.ReadFile(tokenFile)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("reading token", slog.Any("err", err))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
token = bytes.TrimSuffix(token, []byte{'\n'})
|
||||||
|
|
||||||
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||||
|
|
||||||
|
r := handler.New()
|
||||||
|
r.DefaultContext(func() context.Context { return ctx })
|
||||||
|
r.Use(middleware.Go)
|
||||||
|
r.Use(logMiddleware)
|
||||||
|
r.Route("/skill", func(r handler.Router) {
|
||||||
|
r.SlashCommand("/", skillSrv.slash)
|
||||||
|
r.Autocomplete("/", skillSrv.autocomplete)
|
||||||
|
r.ButtonComponent("/swap/{id}", skillSrv.button)
|
||||||
|
r.ButtonComponent("/share/{id}", skillSrv.share)
|
||||||
|
})
|
||||||
|
|
||||||
|
opts := []bot.ConfigOpt{bot.WithDefaultGateway(), bot.WithEventListeners(r)}
|
||||||
|
if addr != "" {
|
||||||
|
if pubkey == "" {
|
||||||
|
slog.Error("Discord public key must be provided when using HTTP API")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
opts = append(opts, bot.WithHTTPServerConfigOpts(pubkey,
|
||||||
|
httpserver.WithAddress(addr),
|
||||||
|
httpserver.WithURL(route),
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.Info("connect", slog.String("disgo", disgo.Version))
|
||||||
|
client, err := disgo.New(string(token), opts...)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("building bot", slog.Any("err", err))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := handler.SyncCommands(client, commands, nil, rest.WithCtx(ctx)); err != nil {
|
||||||
|
slog.Error("syncing commands", slog.Any("err", err))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
if addr != "" {
|
||||||
|
slog.Info("start HTTP server", slog.String("address", addr), slog.String("route", route))
|
||||||
|
if err := client.OpenHTTPServer(); err != nil {
|
||||||
|
slog.Error("starting HTTP server", slog.Any("err", err))
|
||||||
|
stop()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
slog.Info("start gateway")
|
||||||
|
if err := client.OpenGateway(ctx); err != nil {
|
||||||
|
slog.Error("starting gateway", slog.Any("err", err))
|
||||||
|
stop()
|
||||||
|
}
|
||||||
|
slog.Info("ready")
|
||||||
|
<-ctx.Done()
|
||||||
|
stop()
|
||||||
|
|
||||||
|
ctx, stop = context.WithTimeout(context.Background(), 5*time.Second)
|
||||||
|
defer stop()
|
||||||
|
client.Close(ctx)
|
||||||
|
}
|
||||||
|
|
||||||
|
var commands = []discord.ApplicationCommandCreate{
|
||||||
|
discord.SlashCommandCreate{
|
||||||
|
Name: "skill",
|
||||||
|
Description: "Umamusume skill data",
|
||||||
|
Options: []discord.ApplicationCommandOption{
|
||||||
|
discord.ApplicationCommandOptionString{
|
||||||
|
Name: "query",
|
||||||
|
Description: "Skill name or ID",
|
||||||
|
Required: true,
|
||||||
|
Autocomplete: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadSkills(file string) ([]horse.Skill, error) {
|
||||||
|
b, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var skills []horse.Skill
|
||||||
|
if err := json.Unmarshal(b, &skills); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return skills, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func loadSkillGroups(file string) ([]horse.SkillGroup, error) {
|
||||||
|
b, err := os.ReadFile(file)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
var groups []horse.SkillGroup
|
||||||
|
if err := json.Unmarshal(b, &groups); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
return groups, nil
|
||||||
|
}
|
||||||
234
cmd/horsebot/skill.go
Normal file
234
cmd/horsebot/skill.go
Normal file
@@ -0,0 +1,234 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/disgoorg/disgo/discord"
|
||||||
|
"github.com/disgoorg/disgo/handler"
|
||||||
|
|
||||||
|
"git.sunturtle.xyz/zephyr/horse/cmd/horsebot/autocomplete"
|
||||||
|
"git.sunturtle.xyz/zephyr/horse/horse"
|
||||||
|
)
|
||||||
|
|
||||||
|
type skillServer struct {
|
||||||
|
skills map[horse.SkillID]horse.Skill
|
||||||
|
byName map[string]horse.SkillID
|
||||||
|
groups map[horse.SkillGroupID]horse.SkillGroup
|
||||||
|
autocom autocomplete.Set[discord.AutocompleteChoice]
|
||||||
|
}
|
||||||
|
|
||||||
|
func newSkillServer(skills []horse.Skill, groups []horse.SkillGroup) *skillServer {
|
||||||
|
s := skillServer{
|
||||||
|
skills: make(map[horse.SkillID]horse.Skill, len(skills)),
|
||||||
|
byName: make(map[string]horse.SkillID, len(skills)),
|
||||||
|
groups: make(map[horse.SkillGroupID]horse.SkillGroup, len(groups)),
|
||||||
|
}
|
||||||
|
for _, skill := range skills {
|
||||||
|
s.skills[skill.ID] = skill
|
||||||
|
s.byName[skill.Name] = skill.ID
|
||||||
|
s.autocom.Add(skill.Name, discord.AutocompleteChoiceString{Name: skill.Name, Value: skill.Name})
|
||||||
|
if skill.UniqueOwner != "" {
|
||||||
|
if skill.Rarity >= 3 {
|
||||||
|
s.autocom.Add(skill.UniqueOwner, discord.AutocompleteChoiceString{Name: "Unique: " + skill.UniqueOwner, Value: skill.Name})
|
||||||
|
} else {
|
||||||
|
s.autocom.Add(skill.UniqueOwner, discord.AutocompleteChoiceString{Name: "Inherited unique: " + skill.UniqueOwner, Value: skill.Name})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, g := range groups {
|
||||||
|
s.groups[g.ID] = g
|
||||||
|
}
|
||||||
|
return &s
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *skillServer) slash(data discord.SlashCommandInteractionData, e *handler.CommandEvent) error {
|
||||||
|
q := data.String("query")
|
||||||
|
id, err := strconv.ParseInt(q, 10, 32)
|
||||||
|
if err == nil {
|
||||||
|
// note inverted condition; this is when we have an id
|
||||||
|
id = int64(s.skills[horse.SkillID(id)].ID)
|
||||||
|
}
|
||||||
|
if id == 0 {
|
||||||
|
// Either we weren't given a number or the number doesn't match any skill ID.
|
||||||
|
v := s.byName[q]
|
||||||
|
if v == 0 {
|
||||||
|
// No such skill.
|
||||||
|
m := discord.MessageCreate{
|
||||||
|
Content: "No such skill.",
|
||||||
|
Flags: discord.MessageFlagEphemeral,
|
||||||
|
}
|
||||||
|
return e.CreateMessage(m)
|
||||||
|
}
|
||||||
|
id = int64(v)
|
||||||
|
}
|
||||||
|
m := discord.MessageCreate{
|
||||||
|
Components: []discord.LayoutComponent{s.render(horse.SkillID(id), false)},
|
||||||
|
Flags: discord.MessageFlagIsComponentsV2 | discord.MessageFlagEphemeral,
|
||||||
|
}
|
||||||
|
return e.CreateMessage(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *skillServer) autocomplete(e *handler.AutocompleteEvent) error {
|
||||||
|
q := e.Data.String("query")
|
||||||
|
opts := s.autocom.Find(nil, q)
|
||||||
|
return e.AutocompleteResult(opts[:min(len(opts), 25)])
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *skillServer) button(data discord.ButtonInteractionData, e *handler.ComponentEvent) error {
|
||||||
|
id, err := strconv.ParseInt(e.Vars["id"], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
m := discord.MessageCreate{
|
||||||
|
Content: "That button produced an invalid skill ID. That's not supposed to happen.",
|
||||||
|
Flags: discord.MessageFlagEphemeral,
|
||||||
|
}
|
||||||
|
return e.CreateMessage(m)
|
||||||
|
}
|
||||||
|
m := discord.MessageUpdate{
|
||||||
|
Components: &[]discord.LayoutComponent{s.render(horse.SkillID(id), false)},
|
||||||
|
}
|
||||||
|
return e.UpdateMessage(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *skillServer) share(data discord.ButtonInteractionData, e *handler.ComponentEvent) error {
|
||||||
|
id, err := strconv.ParseInt(e.Vars["id"], 10, 32)
|
||||||
|
if err != nil {
|
||||||
|
m := discord.MessageCreate{
|
||||||
|
Content: "That button produced an invalid skill ID. That's not supposed to happen.",
|
||||||
|
Flags: discord.MessageFlagEphemeral,
|
||||||
|
}
|
||||||
|
return e.CreateMessage(m)
|
||||||
|
}
|
||||||
|
m := discord.MessageCreate{
|
||||||
|
Components: []discord.LayoutComponent{s.render(horse.SkillID(id), true)},
|
||||||
|
}
|
||||||
|
return e.CreateMessage(m)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *skillServer) render(id horse.SkillID, share bool) discord.ContainerComponent {
|
||||||
|
skill, ok := s.skills[id]
|
||||||
|
if !ok {
|
||||||
|
slog.Error("invalid skill id", slog.Int("id", int(id)), slog.Bool("share", share))
|
||||||
|
return discord.NewContainer(discord.NewTextDisplayf("invalid skill ID %v made it to render", id))
|
||||||
|
}
|
||||||
|
|
||||||
|
thumburl := fmt.Sprintf("https://gametora.com/images/umamusume/skill_icons/utx_ico_skill_%d.png", skill.IconID)
|
||||||
|
top := "## " + skill.Name
|
||||||
|
if skill.UniqueOwner != "" {
|
||||||
|
top += "\n-# " + skill.UniqueOwner
|
||||||
|
}
|
||||||
|
r := discord.NewContainer(
|
||||||
|
discord.NewSection(
|
||||||
|
discord.NewTextDisplay(top),
|
||||||
|
discord.NewTextDisplay(skill.Description),
|
||||||
|
).WithAccessory(discord.NewThumbnail(thumburl)),
|
||||||
|
)
|
||||||
|
var skilltype string
|
||||||
|
switch {
|
||||||
|
case skill.Rarity == 3, skill.Rarity == 4, skill.Rarity == 5:
|
||||||
|
// unique of various star levels
|
||||||
|
r.AccentColor = 0xaca4d4
|
||||||
|
skilltype = "Unique Skill"
|
||||||
|
case skill.UniqueOwner != "":
|
||||||
|
r.AccentColor = 0xcccccc
|
||||||
|
skilltype = "Inherited Unique"
|
||||||
|
case skill.Rarity == 2:
|
||||||
|
// rare (gold)
|
||||||
|
r.AccentColor = 0xd7c25b
|
||||||
|
skilltype = "Rare Skill"
|
||||||
|
case skill.GroupRate == -1:
|
||||||
|
// negative (purple) skill
|
||||||
|
r.AccentColor = 0x9151d4
|
||||||
|
skilltype = "Negative Skill"
|
||||||
|
case !skill.WitCheck:
|
||||||
|
// should be passive (green)
|
||||||
|
r.AccentColor = 0x66ae1c
|
||||||
|
skilltype = "Passive Skill"
|
||||||
|
case isDebuff(skill):
|
||||||
|
// debuff (red)
|
||||||
|
r.AccentColor = 0xe34747
|
||||||
|
skilltype = "Debuff Skill"
|
||||||
|
case skill.Rarity == 1:
|
||||||
|
// common (white)
|
||||||
|
r.AccentColor = 0xcccccc
|
||||||
|
skilltype = "Common Skill"
|
||||||
|
}
|
||||||
|
r.Components = append(r.Components, discord.NewSmallSeparator())
|
||||||
|
text := make([]string, 0, 3)
|
||||||
|
abils := make([]string, 0, 3)
|
||||||
|
for _, act := range skill.Activations {
|
||||||
|
text, abils = text[:0], abils[:0]
|
||||||
|
if act.Precondition != "" {
|
||||||
|
text = append(text, "Precondition: "+formatCondition(act.Precondition))
|
||||||
|
}
|
||||||
|
text = append(text, "Condition: "+formatCondition(act.Condition))
|
||||||
|
var t string
|
||||||
|
switch {
|
||||||
|
case act.Duration < 0:
|
||||||
|
// passive; do nothing
|
||||||
|
case act.Duration == 0:
|
||||||
|
t = "Instantaneous "
|
||||||
|
case act.Duration >= 500e4:
|
||||||
|
t = "Permanent "
|
||||||
|
case act.DurScale == horse.DurationDirect:
|
||||||
|
t = "For " + act.Duration.String() + "s, "
|
||||||
|
default:
|
||||||
|
t = "For " + act.Duration.String() + "s " + act.DurScale.String() + ", "
|
||||||
|
}
|
||||||
|
for _, a := range act.Abilities {
|
||||||
|
abils = append(abils, a.String())
|
||||||
|
}
|
||||||
|
t += strings.Join(abils, ", ")
|
||||||
|
if act.Cooldown > 0 && act.Cooldown < 500e4 {
|
||||||
|
t += " on " + act.Cooldown.String() + "s cooldown"
|
||||||
|
}
|
||||||
|
text = append(text, t)
|
||||||
|
r.Components = append(r.Components, discord.NewTextDisplay(strings.Join(text, "\n")))
|
||||||
|
}
|
||||||
|
|
||||||
|
l := discord.NewTextDisplayf("%s ・ SP cost %d ・ Grade value %d ・ [Conditions on GameTora](https://gametora.com/umamusume/skill-condition-viewer?skill=%d)", skilltype, skill.SPCost, skill.GradeValue, skill.ID)
|
||||||
|
r.Components = append(r.Components, discord.NewSmallSeparator(), l)
|
||||||
|
rel := make([]horse.Skill, 0, 4)
|
||||||
|
group := s.groups[skill.Group]
|
||||||
|
for _, id := range [...]horse.SkillID{group.Skill1, group.Skill2, group.Skill3, group.SkillBad} {
|
||||||
|
if id != 0 {
|
||||||
|
rel = append(rel, s.skills[id])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(rel) > 1 || !share {
|
||||||
|
buttons := make([]discord.InteractiveComponent, 0, 4)
|
||||||
|
for _, rs := range rel {
|
||||||
|
b := discord.NewSecondaryButton(rs.Name, fmt.Sprintf("/skill/swap/%d", rs.ID))
|
||||||
|
if rs.ID == id {
|
||||||
|
b = b.AsDisabled()
|
||||||
|
}
|
||||||
|
buttons = append(buttons, b)
|
||||||
|
}
|
||||||
|
if !share {
|
||||||
|
buttons = append(buttons, discord.NewPrimaryButton("Share", fmt.Sprintf("/skill/share/%d", skill.ID)))
|
||||||
|
}
|
||||||
|
r.Components = append(r.Components, discord.NewActionRow(buttons...))
|
||||||
|
}
|
||||||
|
return r
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatCondition(s string) string {
|
||||||
|
s = strings.ReplaceAll(s, "&", " & ")
|
||||||
|
if strings.ContainsRune(s, '@') {
|
||||||
|
return "```\n" + strings.ReplaceAll(s, "@", "\n@\n") + "```"
|
||||||
|
}
|
||||||
|
return "`" + s + "`"
|
||||||
|
}
|
||||||
|
|
||||||
|
func isDebuff(s horse.Skill) bool {
|
||||||
|
for _, act := range s.Activations {
|
||||||
|
for _, a := range act.Abilities {
|
||||||
|
if a.Value < 0 {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
403
cmd/horsegen/generate.go
Normal file
403
cmd/horsegen/generate.go
Normal file
@@ -0,0 +1,403 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"cmp"
|
||||||
|
"context"
|
||||||
|
_ "embed"
|
||||||
|
"encoding/json"
|
||||||
|
"errors"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"log/slog"
|
||||||
|
"maps"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"path/filepath"
|
||||||
|
"slices"
|
||||||
|
|
||||||
|
"golang.org/x/sync/errgroup"
|
||||||
|
"zombiezen.com/go/sqlite"
|
||||||
|
"zombiezen.com/go/sqlite/sqlitex"
|
||||||
|
|
||||||
|
"git.sunturtle.xyz/zephyr/horse/horse"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
var (
|
||||||
|
mdb string
|
||||||
|
out string
|
||||||
|
region string
|
||||||
|
)
|
||||||
|
flag.StringVar(&mdb, "mdb", os.ExpandEnv(`$USERPROFILE\AppData\LocalLow\Cygames\Umamusume\master\master.mdb`), "`path` to Umamusume master.mdb")
|
||||||
|
flag.StringVar(&out, "o", `.`, "`dir`ectory for output files")
|
||||||
|
flag.StringVar(®ion, "region", "global", "region the database is for (global, jp)")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
slog.Info("open", slog.String("mdb", mdb))
|
||||||
|
db, err := sqlitex.NewPool(mdb, sqlitex.PoolOptions{Flags: sqlite.OpenReadOnly})
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("opening mdb", slog.String("mdb", mdb), slog.Any("err", err))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||||
|
go func() {
|
||||||
|
<-ctx.Done()
|
||||||
|
stop()
|
||||||
|
}()
|
||||||
|
|
||||||
|
loadgroup, ctx1 := errgroup.WithContext(ctx)
|
||||||
|
charas := load(ctx1, loadgroup, db, "characters", characterSQL, func(s *sqlite.Stmt) horse.Character {
|
||||||
|
return horse.Character{
|
||||||
|
ID: horse.CharacterID(s.ColumnInt(0)),
|
||||||
|
Name: s.ColumnText(1),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
aff := load(ctx1, loadgroup, db, "pair affinity", affinitySQL, func(s *sqlite.Stmt) horse.AffinityRelation {
|
||||||
|
return horse.AffinityRelation{
|
||||||
|
IDA: s.ColumnInt(0),
|
||||||
|
IDB: s.ColumnInt(1),
|
||||||
|
IDC: s.ColumnInt(2),
|
||||||
|
Affinity: s.ColumnInt(3),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
umas := load(ctx1, loadgroup, db, "umas", umaSQL, func(s *sqlite.Stmt) horse.Uma {
|
||||||
|
return horse.Uma{
|
||||||
|
ID: horse.UmaID(s.ColumnInt(0)),
|
||||||
|
CharacterID: horse.CharacterID(s.ColumnInt(1)),
|
||||||
|
Name: s.ColumnText(2),
|
||||||
|
Variant: s.ColumnText(3),
|
||||||
|
Sprint: horse.AptitudeLevel(s.ColumnInt(4)),
|
||||||
|
Mile: horse.AptitudeLevel(s.ColumnInt(6)),
|
||||||
|
Medium: horse.AptitudeLevel(s.ColumnInt(7)),
|
||||||
|
Long: horse.AptitudeLevel(s.ColumnInt(8)),
|
||||||
|
Front: horse.AptitudeLevel(s.ColumnInt(9)),
|
||||||
|
Pace: horse.AptitudeLevel(s.ColumnInt(10)),
|
||||||
|
Late: horse.AptitudeLevel(s.ColumnInt(11)),
|
||||||
|
End: horse.AptitudeLevel(s.ColumnInt(12)),
|
||||||
|
Turf: horse.AptitudeLevel(s.ColumnInt(13)),
|
||||||
|
Dirt: horse.AptitudeLevel(s.ColumnInt(14)),
|
||||||
|
Unique: horse.SkillID(s.ColumnInt(15)),
|
||||||
|
Skill1: horse.SkillID(s.ColumnInt(16)),
|
||||||
|
Skill2: horse.SkillID(s.ColumnInt(17)),
|
||||||
|
Skill3: horse.SkillID(s.ColumnInt(18)),
|
||||||
|
SkillPL2: horse.SkillID(s.ColumnInt(19)),
|
||||||
|
SkillPL3: horse.SkillID(s.ColumnInt(20)),
|
||||||
|
SkillPL4: horse.SkillID(s.ColumnInt(21)),
|
||||||
|
SkillPL5: horse.SkillID(s.ColumnInt(22)),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
sg := load(ctx1, loadgroup, db, "skill groups", skillGroupSQL, func(s *sqlite.Stmt) horse.SkillGroup {
|
||||||
|
return horse.SkillGroup{
|
||||||
|
ID: horse.SkillGroupID(s.ColumnInt(0)),
|
||||||
|
Skill1: horse.SkillID(s.ColumnInt(1)),
|
||||||
|
Skill2: horse.SkillID(s.ColumnInt(2)),
|
||||||
|
Skill3: horse.SkillID(s.ColumnInt(3)),
|
||||||
|
SkillBad: horse.SkillID(s.ColumnInt(4)),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
skills := load(ctx1, loadgroup, db, "skills", skillSQL, func(s *sqlite.Stmt) horse.Skill {
|
||||||
|
return horse.Skill{
|
||||||
|
ID: horse.SkillID(s.ColumnInt(0)),
|
||||||
|
Name: s.ColumnText(1),
|
||||||
|
Description: s.ColumnText(2),
|
||||||
|
Group: horse.SkillGroupID(s.ColumnInt32(3)),
|
||||||
|
Rarity: int8(s.ColumnInt(5)),
|
||||||
|
GroupRate: int8(s.ColumnInt(6)),
|
||||||
|
GradeValue: s.ColumnInt32(7),
|
||||||
|
WitCheck: s.ColumnBool(8),
|
||||||
|
Activations: trimActivations([]horse.Activation{
|
||||||
|
{
|
||||||
|
Precondition: s.ColumnText(9),
|
||||||
|
Condition: s.ColumnText(10),
|
||||||
|
Duration: horse.TenThousandths(s.ColumnInt(11)),
|
||||||
|
DurScale: horse.DurScale(s.ColumnInt(12)),
|
||||||
|
Cooldown: horse.TenThousandths(s.ColumnInt(13)),
|
||||||
|
Abilities: trimAbilities([]horse.Ability{
|
||||||
|
{
|
||||||
|
Type: horse.AbilityType(s.ColumnInt(14)),
|
||||||
|
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(15)),
|
||||||
|
Value: horse.TenThousandths(s.ColumnInt(16)),
|
||||||
|
Target: horse.AbilityTarget(s.ColumnInt(17)),
|
||||||
|
TargetValue: s.ColumnInt32(18),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: horse.AbilityType(s.ColumnInt(19)),
|
||||||
|
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(20)),
|
||||||
|
Value: horse.TenThousandths(s.ColumnInt(21)),
|
||||||
|
Target: horse.AbilityTarget(s.ColumnInt(22)),
|
||||||
|
TargetValue: s.ColumnInt32(23),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: horse.AbilityType(s.ColumnInt(24)),
|
||||||
|
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(25)),
|
||||||
|
Value: horse.TenThousandths(s.ColumnInt(26)),
|
||||||
|
Target: horse.AbilityTarget(s.ColumnInt(27)),
|
||||||
|
TargetValue: s.ColumnInt32(28),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Precondition: s.ColumnText(29),
|
||||||
|
Condition: s.ColumnText(30),
|
||||||
|
Duration: horse.TenThousandths(s.ColumnInt(31)),
|
||||||
|
DurScale: horse.DurScale(s.ColumnInt(32)),
|
||||||
|
Cooldown: horse.TenThousandths(s.ColumnInt(33)),
|
||||||
|
Abilities: trimAbilities([]horse.Ability{
|
||||||
|
{
|
||||||
|
Type: horse.AbilityType(s.ColumnInt(34)),
|
||||||
|
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(35)),
|
||||||
|
Value: horse.TenThousandths(s.ColumnInt(36)),
|
||||||
|
Target: horse.AbilityTarget(s.ColumnInt(37)),
|
||||||
|
TargetValue: s.ColumnInt32(38),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: horse.AbilityType(s.ColumnInt(39)),
|
||||||
|
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(40)),
|
||||||
|
Value: horse.TenThousandths(s.ColumnInt(41)),
|
||||||
|
Target: horse.AbilityTarget(s.ColumnInt(42)),
|
||||||
|
TargetValue: s.ColumnInt32(43),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Type: horse.AbilityType(s.ColumnInt(44)),
|
||||||
|
ValueUsage: horse.AbilityValueUsage(s.ColumnInt(45)),
|
||||||
|
Value: horse.TenThousandths(s.ColumnInt(46)),
|
||||||
|
Target: horse.AbilityTarget(s.ColumnInt(47)),
|
||||||
|
TargetValue: s.ColumnInt32(48),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
UniqueOwner: s.ColumnText(52), // TODO(zeph): should be id, not name
|
||||||
|
SPCost: s.ColumnInt(49),
|
||||||
|
IconID: s.ColumnInt(53),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
races := load(ctx1, loadgroup, db, "races", raceSQL, func(s *sqlite.Stmt) horse.Race {
|
||||||
|
return horse.Race{
|
||||||
|
ID: horse.RaceID(s.ColumnInt(0)),
|
||||||
|
Name: s.ColumnText(1),
|
||||||
|
// TODO(zeph): grade
|
||||||
|
Thumbnail: s.ColumnInt(3),
|
||||||
|
Primary: horse.RaceID(s.ColumnInt(4)),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
saddles := load(ctx1, loadgroup, db, "saddles", saddleSQL, func(s *sqlite.Stmt) horse.Saddle {
|
||||||
|
return horse.Saddle{
|
||||||
|
ID: horse.SaddleID(s.ColumnInt(0)),
|
||||||
|
Name: s.ColumnText(1),
|
||||||
|
Races: trimZeros(
|
||||||
|
horse.RaceID(s.ColumnInt(2)),
|
||||||
|
horse.RaceID(s.ColumnInt(3)),
|
||||||
|
horse.RaceID(s.ColumnInt(4)),
|
||||||
|
),
|
||||||
|
Type: horse.SaddleType(s.ColumnInt(5)),
|
||||||
|
Primary: horse.SaddleID(s.ColumnInt(6)),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
scenarios := load(ctx1, loadgroup, db, "scenarios", scenarioSQL, func(s *sqlite.Stmt) horse.Scenario {
|
||||||
|
return horse.Scenario{
|
||||||
|
ID: horse.ScenarioID(s.ColumnInt(0)),
|
||||||
|
Name: s.ColumnText(1),
|
||||||
|
Title: s.ColumnText(2),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
sparks := load(ctx1, loadgroup, db, "sparks", sparkSQL, func(s *sqlite.Stmt) horse.Spark {
|
||||||
|
return horse.Spark{
|
||||||
|
ID: horse.SparkID(s.ColumnInt(0)),
|
||||||
|
Name: s.ColumnText(1),
|
||||||
|
Description: s.ColumnText(2),
|
||||||
|
Group: horse.SparkGroupID(s.ColumnInt(3)),
|
||||||
|
Rarity: horse.SparkRarity(s.ColumnInt(4)),
|
||||||
|
Type: horse.SparkType(s.ColumnInt(5)),
|
||||||
|
// Effects filled in later.
|
||||||
|
}
|
||||||
|
})
|
||||||
|
sparkeffs := load(ctx1, loadgroup, db, "spark effects", sparkEffectSQL, func(s *sqlite.Stmt) SparkEffImm {
|
||||||
|
return SparkEffImm{
|
||||||
|
Group: horse.SparkGroupID(s.ColumnInt(0)),
|
||||||
|
Effect: s.ColumnInt(1),
|
||||||
|
Target: horse.SparkTarget(s.ColumnInt(2)),
|
||||||
|
Value1: s.ColumnInt32(3),
|
||||||
|
Value2: s.ColumnInt32(4),
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if err := os.MkdirAll(filepath.Join(out, region), 0775); err != nil {
|
||||||
|
slog.Error("create output dir", slog.Any("err", err))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
writegroup, ctx2 := errgroup.WithContext(ctx)
|
||||||
|
writegroup.Go(func() error { return write(ctx2, out, region, "character.json", charas) })
|
||||||
|
writegroup.Go(func() error { return write(ctx2, out, region, "affinity.json", aff) })
|
||||||
|
writegroup.Go(func() error { return write(ctx2, out, region, "uma.json", umas) })
|
||||||
|
writegroup.Go(func() error { return write(ctx2, out, region, "skill-group.json", sg) })
|
||||||
|
writegroup.Go(func() error { return write(ctx2, out, region, "skill.json", skills) })
|
||||||
|
writegroup.Go(func() error { return write(ctx2, out, region, "race.json", races) })
|
||||||
|
writegroup.Go(func() error { return write(ctx2, out, region, "saddle.json", saddles) })
|
||||||
|
writegroup.Go(func() error { return write(ctx2, out, region, "scenario.json", scenarios) })
|
||||||
|
writegroup.Go(func() error { return write(ctx2, out, region, "spark.json", mergesparks(sparks, sparkeffs)) })
|
||||||
|
if err := writegroup.Wait(); err != nil {
|
||||||
|
slog.ErrorContext(ctx, "write", slog.Any("err", err))
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
slog.InfoContext(ctx, "done")
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
//go:embed sql/character.sql
|
||||||
|
characterSQL string
|
||||||
|
//go:embed sql/affinity.sql
|
||||||
|
affinitySQL string
|
||||||
|
//go:embed sql/uma.sql
|
||||||
|
umaSQL string
|
||||||
|
//go:embed sql/skill-group.sql
|
||||||
|
skillGroupSQL string
|
||||||
|
//go:embed sql/skill.sql
|
||||||
|
skillSQL string
|
||||||
|
//go:embed sql/race.sql
|
||||||
|
raceSQL string
|
||||||
|
//go:embed sql/saddle.sql
|
||||||
|
saddleSQL string
|
||||||
|
//go:embed sql/scenario.sql
|
||||||
|
scenarioSQL string
|
||||||
|
//go:embed sql/spark.sql
|
||||||
|
sparkSQL string
|
||||||
|
//go:embed sql/spark-effect.sql
|
||||||
|
sparkEffectSQL string
|
||||||
|
)
|
||||||
|
|
||||||
|
func load[T any](ctx context.Context, group *errgroup.Group, db *sqlitex.Pool, kind, sql string, row func(*sqlite.Stmt) T) func() ([]T, error) {
|
||||||
|
slog.InfoContext(ctx, "load", slog.String("kind", kind))
|
||||||
|
var r []T
|
||||||
|
group.Go(func() error {
|
||||||
|
conn, err := db.Take(ctx)
|
||||||
|
defer db.Put(conn)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("couldn't get connection for %s: %w", kind, err)
|
||||||
|
}
|
||||||
|
stmt, _, err := conn.PrepareTransient(sql)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("couldn't prepare statement for %s: %w", kind, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
ok, err := stmt.Step()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error stepping %s: %w", kind, err)
|
||||||
|
}
|
||||||
|
if !ok {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
r = append(r, row(stmt))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return func() ([]T, error) {
|
||||||
|
err := group.Wait()
|
||||||
|
if err == context.Canceled {
|
||||||
|
// After the first wait, all future ones return context.Canceled.
|
||||||
|
// We want to be able to wait any number of times, so hide it.
|
||||||
|
err = nil
|
||||||
|
}
|
||||||
|
return r, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func write[T any](ctx context.Context, out, region, name string, v func() (T, error)) error {
|
||||||
|
p := filepath.Join(out, region, name)
|
||||||
|
r, err := v()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
slog.InfoContext(ctx, "write", slog.String("path", p))
|
||||||
|
f, err := os.Create(p)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
w := bufio.NewWriter(f)
|
||||||
|
enc := json.NewEncoder(w)
|
||||||
|
enc.SetEscapeHTML(false)
|
||||||
|
enc.SetIndent("", "\t")
|
||||||
|
err = enc.Encode(r)
|
||||||
|
err = errors.Join(err, w.Flush())
|
||||||
|
slog.InfoContext(ctx, "marshaled", slog.String("path", p))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
func mergesparks(sparks func() ([]horse.Spark, error), effs func() ([]SparkEffImm, error)) func() ([]horse.Spark, error) {
|
||||||
|
return func() ([]horse.Spark, error) {
|
||||||
|
sp, err := sparks()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
ef, err := effs()
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
// Spark effects are sorted by group ID, but groups apply to multiple
|
||||||
|
// sparks, and we don't rely on sparks and groups being in the same order.
|
||||||
|
// It is possible to merge in linear time, but not worth the effort:
|
||||||
|
// n log n is fine since this is an AOT step.
|
||||||
|
for i := range sp {
|
||||||
|
k, ok := slices.BinarySearchFunc(ef, sp[i].Group, func(e SparkEffImm, v horse.SparkGroupID) int { return cmp.Compare(e.Group, v) })
|
||||||
|
if !ok {
|
||||||
|
panic(fmt.Errorf("mergesparks: no spark group for %+v", &sp[i]))
|
||||||
|
}
|
||||||
|
// Back up to the first effect in the group.
|
||||||
|
for k > 0 && ef[k-1].Group == sp[i].Group {
|
||||||
|
k--
|
||||||
|
}
|
||||||
|
// Map effect IDs to the lists of their effects.
|
||||||
|
m := make(map[int][]horse.SparkEffect)
|
||||||
|
for _, e := range ef[k:] {
|
||||||
|
if e.Group != sp[i].Group {
|
||||||
|
// Done with this group.
|
||||||
|
break
|
||||||
|
}
|
||||||
|
m[e.Effect] = append(m[e.Effect], horse.SparkEffect{Target: e.Target, Value1: e.Value1, Value2: e.Value2})
|
||||||
|
}
|
||||||
|
// Now get effects in order.
|
||||||
|
keys := slices.Sorted(maps.Keys(m))
|
||||||
|
sp[i].Effects = make([][]horse.SparkEffect, 0, len(keys))
|
||||||
|
for _, key := range keys {
|
||||||
|
sp[i].Effects = append(sp[i].Effects, m[key])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return sp, nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type SparkEffImm struct {
|
||||||
|
Group horse.SparkGroupID
|
||||||
|
Effect int
|
||||||
|
Target horse.SparkTarget
|
||||||
|
Value1 int32
|
||||||
|
Value2 int32
|
||||||
|
}
|
||||||
|
|
||||||
|
func trimAbilities(s []horse.Ability) []horse.Ability {
|
||||||
|
for len(s) > 0 && s[len(s)-1].Type == 0 {
|
||||||
|
s = s[:len(s)-1]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func trimActivations(s []horse.Activation) []horse.Activation {
|
||||||
|
for len(s) > 0 && s[len(s)-1].Condition == "" {
|
||||||
|
s = s[:len(s)-1]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
|
|
||||||
|
func trimZeros[T comparable](s ...T) []T {
|
||||||
|
var zero T
|
||||||
|
for len(s) > 0 && s[len(s)-1] == zero {
|
||||||
|
s = s[:len(s)-1]
|
||||||
|
}
|
||||||
|
return s
|
||||||
|
}
|
||||||
60
cmd/horsegen/sql/affinity.sql
Normal file
60
cmd/horsegen/sql/affinity.sql
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
WITH pairs AS (
|
||||||
|
SELECT
|
||||||
|
a.id AS id_a,
|
||||||
|
b.id AS id_b
|
||||||
|
FROM chara_data a
|
||||||
|
JOIN chara_data b ON a.id < b.id
|
||||||
|
-- Exclude characters who have no succession relations defined.
|
||||||
|
WHERE a.id IN (SELECT chara_id FROM succession_relation_member)
|
||||||
|
AND b.id IN (SELECT chara_id FROM succession_relation_member)
|
||||||
|
), trios AS (
|
||||||
|
SELECT
|
||||||
|
a.id AS id_a,
|
||||||
|
b.id AS id_b,
|
||||||
|
c.id AS id_c
|
||||||
|
FROM chara_data a
|
||||||
|
JOIN chara_data b ON a.id < b.id
|
||||||
|
JOIN chara_data c ON a.id < c.id AND b.id < c.id
|
||||||
|
-- Exclude characters who have no succession relations defined.
|
||||||
|
WHERE a.id IN (SELECT chara_id FROM succession_relation_member)
|
||||||
|
AND b.id IN (SELECT chara_id FROM succession_relation_member)
|
||||||
|
AND c.id IN (SELECT chara_id FROM succession_relation_member)
|
||||||
|
), pair_relations AS (
|
||||||
|
SELECT
|
||||||
|
ra.relation_type,
|
||||||
|
ra.chara_id AS id_a,
|
||||||
|
rb.chara_id AS id_b
|
||||||
|
FROM succession_relation_member ra
|
||||||
|
JOIN succession_relation_member rb ON ra.relation_type = rb.relation_type
|
||||||
|
), trio_relations AS (
|
||||||
|
SELECT
|
||||||
|
ra.relation_type,
|
||||||
|
ra.chara_id AS id_a,
|
||||||
|
rb.chara_id AS id_b,
|
||||||
|
rc.chara_id AS id_c
|
||||||
|
FROM succession_relation_member ra
|
||||||
|
JOIN succession_relation_member rb ON ra.relation_type = rb.relation_type
|
||||||
|
JOIN succession_relation_member rc ON ra.relation_type = rc.relation_type
|
||||||
|
), affinity AS (
|
||||||
|
SELECT
|
||||||
|
pairs.*,
|
||||||
|
0 AS id_c,
|
||||||
|
SUM(IFNULL(relation_point, 0)) AS base_affinity
|
||||||
|
FROM pairs
|
||||||
|
LEFT JOIN pair_relations rp ON pairs.id_a = rp.id_a AND pairs.id_b = rp.id_b
|
||||||
|
LEFT JOIN succession_relation sr ON rp.relation_type = sr.relation_type
|
||||||
|
GROUP BY pairs.id_a, pairs.id_b
|
||||||
|
|
||||||
|
UNION ALL
|
||||||
|
|
||||||
|
SELECT
|
||||||
|
trios.*,
|
||||||
|
SUM(IFNULL(relation_point, 0)) AS base_affinity
|
||||||
|
FROM trios
|
||||||
|
LEFT JOIN trio_relations rt ON trios.id_a = rt.id_a AND trios.id_b = rt.id_b AND trios.id_c = rt.id_c
|
||||||
|
LEFT JOIN succession_relation sr ON rt.relation_type = sr.relation_type
|
||||||
|
GROUP BY trios.id_a, trios.id_b, trios.id_c
|
||||||
|
)
|
||||||
|
SELECT * FROM affinity
|
||||||
|
WHERE base_affinity != 0
|
||||||
|
ORDER BY id_a, id_b, id_c
|
||||||
9
cmd/horsegen/sql/character.sql
Normal file
9
cmd/horsegen/sql/character.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
SELECT
|
||||||
|
"index" AS "id",
|
||||||
|
"text" AS "name",
|
||||||
|
ROW_NUMBER() OVER (ORDER BY "index") - 1 AS "index"
|
||||||
|
FROM text_data
|
||||||
|
WHERE category = 6 AND "index" BETWEEN 1000 AND 1999
|
||||||
|
-- Exclude characters who have no succession relations defined.
|
||||||
|
AND "index" IN (SELECT chara_id FROM succession_relation_member)
|
||||||
|
ORDER BY "id"
|
||||||
14
cmd/horsegen/sql/race.sql
Normal file
14
cmd/horsegen/sql/race.sql
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
WITH race_names AS (
|
||||||
|
SELECT "index" AS id, "text" AS name FROM text_data WHERE category = 33
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
race.id,
|
||||||
|
race_names.name,
|
||||||
|
race.grade,
|
||||||
|
race.thumbnail_id,
|
||||||
|
MIN(race.id) OVER (PARTITION BY race_names.name) AS "primary",
|
||||||
|
ROW_NUMBER() OVER (PARTITION BY race_names.name ORDER BY race.id) - 1 AS "alternate"
|
||||||
|
FROM race
|
||||||
|
JOIN race_names ON race.id = race_names.id
|
||||||
|
WHERE race."group" = 1
|
||||||
|
ORDER BY race.id
|
||||||
20
cmd/horsegen/sql/saddle.sql
Normal file
20
cmd/horsegen/sql/saddle.sql
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
WITH saddle_names AS (
|
||||||
|
SELECT "index" AS id, "text" AS name
|
||||||
|
FROM text_data
|
||||||
|
WHERE category = 111
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
s.id,
|
||||||
|
n.name,
|
||||||
|
ri1.id AS race1,
|
||||||
|
IFNULL(ri2.id, 0) AS race2,
|
||||||
|
IFNULL(ri3.id, 0) AS race3,
|
||||||
|
s.win_saddle_type,
|
||||||
|
MIN(s.id) OVER (PARTITION BY n.name) AS "primary",
|
||||||
|
ROW_NUMBER() OVER (PARTITION BY n.name ORDER BY s.id) - 1 AS "alternate"
|
||||||
|
FROM single_mode_wins_saddle s
|
||||||
|
JOIN race_instance ri1 ON s.race_instance_id_1 = ri1.id
|
||||||
|
LEFT JOIN race_instance ri2 ON s.race_instance_id_2 = ri2.id
|
||||||
|
LEFT JOIN race_instance ri3 ON s.race_instance_id_3 = ri3.id
|
||||||
|
LEFT JOIN saddle_names n ON s.id = n.id
|
||||||
|
ORDER BY s.id
|
||||||
17
cmd/horsegen/sql/scenario.sql
Normal file
17
cmd/horsegen/sql/scenario.sql
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
WITH scenario_name AS (
|
||||||
|
SELECT "index" AS id, "text" AS name
|
||||||
|
FROM text_data
|
||||||
|
WHERE category = 237
|
||||||
|
), scenario_title AS (
|
||||||
|
SELECT "index" AS id, "text" AS title
|
||||||
|
FROM text_data
|
||||||
|
WHERE category = 119
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
sc.id,
|
||||||
|
n.name,
|
||||||
|
t.title
|
||||||
|
FROM single_mode_scenario sc
|
||||||
|
JOIN scenario_name n ON sc.id = n.id
|
||||||
|
JOIN scenario_title t ON sc.id = t.id
|
||||||
|
ORDER BY sc.id
|
||||||
15
cmd/horsegen/sql/skill-group.sql
Normal file
15
cmd/horsegen/sql/skill-group.sql
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
WITH skill_groups AS (
|
||||||
|
SELECT DISTINCT group_id FROM skill_data
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
g.group_id,
|
||||||
|
IFNULL(s1.id, 0) AS skill1,
|
||||||
|
IFNULL(s2.id, 0) AS skill2,
|
||||||
|
IFNULL(s3.id, 0) AS skill3,
|
||||||
|
IFNULL(m1.id, 0) AS skill_bad
|
||||||
|
FROM skill_groups g
|
||||||
|
LEFT JOIN skill_data s1 ON g.group_id = s1.group_id AND s1.group_rate = 1
|
||||||
|
LEFT JOIN skill_data s2 ON g.group_id = s2.group_id AND s2.group_rate = 2
|
||||||
|
LEFT JOIN skill_data s3 ON g.group_id = s3.group_id AND s3.group_rate = 3
|
||||||
|
LEFT JOIN skill_data m1 ON g.group_id = m1.group_id AND m1.group_rate = -1
|
||||||
|
ORDER BY g.group_id
|
||||||
98
cmd/horsegen/sql/skill.sql
Normal file
98
cmd/horsegen/sql/skill.sql
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
WITH skill_names AS (
|
||||||
|
SELECT
|
||||||
|
n."index" AS "id",
|
||||||
|
n."text" AS "name",
|
||||||
|
d."text" AS "description"
|
||||||
|
FROM text_data n
|
||||||
|
JOIN text_data d ON n."index" = d."index" AND n."category" = 47 AND d."category" = 48
|
||||||
|
), skill_groups AS (
|
||||||
|
SELECT
|
||||||
|
group_id,
|
||||||
|
name
|
||||||
|
FROM skill_data d
|
||||||
|
JOIN skill_names n ON d.id = n.id
|
||||||
|
WHERE group_rate = 1
|
||||||
|
), card_name AS (
|
||||||
|
SELECT
|
||||||
|
"index" AS "id",
|
||||||
|
"text" AS "name"
|
||||||
|
FROM text_data n
|
||||||
|
WHERE category = 4
|
||||||
|
), card_unique AS (
|
||||||
|
SELECT DISTINCT
|
||||||
|
ss.skill_id1 AS unique_id,
|
||||||
|
card_name.id AS owner_id,
|
||||||
|
card_name.name
|
||||||
|
FROM card_data card
|
||||||
|
JOIN card_name ON card.id = card_name.id
|
||||||
|
JOIN card_rarity_data rd ON card.id = rd.card_id
|
||||||
|
JOIN skill_set ss ON rd.skill_set = ss.id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
d.id,
|
||||||
|
n.name,
|
||||||
|
n.description,
|
||||||
|
IIF(d.unique_skill_id_1 = 0, d.group_id, ud.group_id) AS group_id,
|
||||||
|
CASE
|
||||||
|
WHEN g.name IS NOT NULL THEN g.name
|
||||||
|
WHEN d.unique_skill_id_1 != 0 THEN n.name
|
||||||
|
ELSE ''
|
||||||
|
END AS group_name,
|
||||||
|
d.rarity,
|
||||||
|
d.group_rate,
|
||||||
|
d.grade_value,
|
||||||
|
d.activate_lot,
|
||||||
|
d.precondition_1,
|
||||||
|
d.condition_1,
|
||||||
|
d.float_ability_time_1,
|
||||||
|
d.ability_time_usage_1,
|
||||||
|
d.float_cooldown_time_1,
|
||||||
|
d.ability_type_1_1,
|
||||||
|
d.ability_value_usage_1_1,
|
||||||
|
d.float_ability_value_1_1,
|
||||||
|
d.target_type_1_1,
|
||||||
|
d.target_value_1_1,
|
||||||
|
d.ability_type_1_2,
|
||||||
|
d.ability_value_usage_1_2,
|
||||||
|
d.float_ability_value_1_2,
|
||||||
|
d.target_type_1_2,
|
||||||
|
d.target_value_1_2,
|
||||||
|
d.ability_type_1_3,
|
||||||
|
d.ability_value_usage_1_3,
|
||||||
|
d.float_ability_value_1_3,
|
||||||
|
d.target_type_1_3,
|
||||||
|
d.target_value_1_3,
|
||||||
|
d.precondition_2,
|
||||||
|
d.condition_2,
|
||||||
|
d.float_ability_time_2,
|
||||||
|
d.ability_time_usage_2,
|
||||||
|
d.float_cooldown_time_2,
|
||||||
|
d.ability_type_2_1,
|
||||||
|
d.ability_value_usage_2_1,
|
||||||
|
d.float_ability_value_2_1,
|
||||||
|
d.target_type_2_1,
|
||||||
|
d.target_value_2_1,
|
||||||
|
d.ability_type_2_2,
|
||||||
|
d.ability_value_usage_2_2,
|
||||||
|
d.float_ability_value_2_2,
|
||||||
|
d.target_type_2_2,
|
||||||
|
d.target_value_2_2,
|
||||||
|
d.ability_type_2_3,
|
||||||
|
d.ability_value_usage_2_3,
|
||||||
|
d.float_ability_value_2_3,
|
||||||
|
d.target_type_2_3,
|
||||||
|
d.target_value_2_3,
|
||||||
|
IFNULL(p.need_skill_point, 0) AS sp_cost,
|
||||||
|
d.unique_skill_id_1,
|
||||||
|
COALESCE(u.owner_id, iu.owner_id, 0) AS unique_owner_id,
|
||||||
|
COALESCE(u.name, iu.name, '') AS unique_owner,
|
||||||
|
d.icon_id,
|
||||||
|
ROW_NUMBER() OVER (ORDER BY d.id) - 1 AS "index"
|
||||||
|
FROM skill_data d
|
||||||
|
JOIN skill_names n ON d.id = n.id
|
||||||
|
LEFT JOIN skill_data ud ON d.unique_skill_id_1 = ud.id
|
||||||
|
LEFT JOIN skill_groups g ON d.group_id = g.group_id
|
||||||
|
LEFT JOIN single_mode_skill_need_point p ON d.id = p.id
|
||||||
|
LEFT JOIN card_unique u ON d.id = u.unique_id
|
||||||
|
LEFT JOIN card_unique iu ON d.unique_skill_id_1 = iu.unique_id
|
||||||
|
ORDER BY d.id
|
||||||
9
cmd/horsegen/sql/spark-effect.sql
Normal file
9
cmd/horsegen/sql/spark-effect.sql
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
SELECT
|
||||||
|
factor_group_id,
|
||||||
|
effect_id,
|
||||||
|
target_type,
|
||||||
|
value_1,
|
||||||
|
value_2
|
||||||
|
FROM succession_factor_effect
|
||||||
|
WHERE factor_group_id NOT IN (40001) -- exclude Carnival Bonus
|
||||||
|
ORDER BY factor_group_id, effect_id, id
|
||||||
20
cmd/horsegen/sql/spark.sql
Normal file
20
cmd/horsegen/sql/spark.sql
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
WITH spark AS (
|
||||||
|
SELECT
|
||||||
|
n."index" AS "id",
|
||||||
|
n."text" AS "name",
|
||||||
|
d."text" AS "description"
|
||||||
|
FROM text_data n
|
||||||
|
LEFT JOIN text_data d ON n."index" = d."index" AND d."category" = 172
|
||||||
|
WHERE n.category = 147
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
sf.factor_id,
|
||||||
|
spark.name,
|
||||||
|
spark.description,
|
||||||
|
sf.factor_group_id,
|
||||||
|
sf.rarity,
|
||||||
|
sf.factor_type
|
||||||
|
FROM spark
|
||||||
|
JOIN succession_factor sf ON spark.id = sf.factor_id
|
||||||
|
WHERE sf.factor_type != 7 -- exclude Carnival Bonus
|
||||||
|
ORDER BY sf.factor_id
|
||||||
59
cmd/horsegen/sql/uma.sql
Normal file
59
cmd/horsegen/sql/uma.sql
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
WITH uma_name AS (
|
||||||
|
SELECT "index" AS id, "text" AS name
|
||||||
|
FROM text_data
|
||||||
|
WHERE category = 4
|
||||||
|
), uma_variant AS (
|
||||||
|
SELECT "index" AS id, "text" AS variant
|
||||||
|
FROM text_data
|
||||||
|
WHERE category = 5
|
||||||
|
), chara_name AS (
|
||||||
|
SELECT "index" AS id, "text" AS name
|
||||||
|
FROM text_data
|
||||||
|
WHERE category = 6
|
||||||
|
), skills AS (
|
||||||
|
SELECT
|
||||||
|
uma.id,
|
||||||
|
s.skill_id,
|
||||||
|
s.need_rank,
|
||||||
|
ROW_NUMBER() OVER (PARTITION BY s.available_skill_set_id, s.need_rank) AS idx
|
||||||
|
FROM card_data uma
|
||||||
|
LEFT JOIN available_skill_set s ON uma.available_skill_set_id = s.available_skill_set_id
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
uma.card_id,
|
||||||
|
card_data.chara_id,
|
||||||
|
n.name,
|
||||||
|
v.variant,
|
||||||
|
c.name AS chara_name,
|
||||||
|
uma.proper_distance_short,
|
||||||
|
uma.proper_distance_mile,
|
||||||
|
uma.proper_distance_middle,
|
||||||
|
uma.proper_distance_long,
|
||||||
|
uma.proper_running_style_nige,
|
||||||
|
uma.proper_running_style_senko,
|
||||||
|
uma.proper_running_style_sashi,
|
||||||
|
uma.proper_running_style_oikomi,
|
||||||
|
uma.proper_ground_turf,
|
||||||
|
uma.proper_ground_dirt,
|
||||||
|
su.skill_id1 AS unique_skill,
|
||||||
|
s1.skill_id AS skill1,
|
||||||
|
s2.skill_id AS skill2,
|
||||||
|
s3.skill_id AS skill3,
|
||||||
|
sp2.skill_id AS skill_pl2,
|
||||||
|
sp3.skill_id AS skill_pl3,
|
||||||
|
sp4.skill_id AS skill_pl4,
|
||||||
|
sp5.skill_id AS skill_pl5
|
||||||
|
FROM card_data
|
||||||
|
JOIN card_rarity_data uma ON card_data.id = uma.card_id
|
||||||
|
JOIN chara_name c ON card_data.chara_id = c.id
|
||||||
|
JOIN skill_set su ON uma.skill_set = su.id
|
||||||
|
JOIN skills s1 ON uma.card_id = s1.id AND s1.need_rank = 0 AND s1.idx = 1
|
||||||
|
JOIN skills s2 ON uma.card_id = s2.id AND s2.need_rank = 0 AND s2.idx = 2
|
||||||
|
JOIN skills s3 ON uma.card_id = s3.id AND s3.need_rank = 0 AND s3.idx = 3
|
||||||
|
JOIN skills sp2 ON uma.card_id = sp2.id AND sp2.need_rank = 2
|
||||||
|
JOIN skills sp3 ON uma.card_id = sp3.id AND sp3.need_rank = 3
|
||||||
|
JOIN skills sp4 ON uma.card_id = sp4.id AND sp4.need_rank = 4
|
||||||
|
JOIN skills sp5 ON uma.card_id = sp5.id AND sp5.need_rank = 5
|
||||||
|
LEFT JOIN uma_name n ON uma.card_id = n.id
|
||||||
|
LEFT JOIN uma_variant v ON uma.card_id = v.id
|
||||||
|
WHERE uma.rarity = 5
|
||||||
122197
doc/2026-01-05-global.sql
Normal file
122197
doc/2026-01-05-global.sql
Normal file
File diff suppressed because it is too large
Load Diff
1812
doc/2026-01-15-global.diff
Normal file
1812
doc/2026-01-15-global.diff
Normal file
File diff suppressed because it is too large
Load Diff
1903
doc/2026-01-22-global.diff
Normal file
1903
doc/2026-01-22-global.diff
Normal file
File diff suppressed because it is too large
Load Diff
123780
doc/2026-01-22-global.sql
Normal file
123780
doc/2026-01-22-global.sql
Normal file
File diff suppressed because it is too large
Load Diff
1574
doc/2026-01-29-global.diff
Normal file
1574
doc/2026-01-29-global.diff
Normal file
File diff suppressed because it is too large
Load Diff
363
doc/README.md
Normal file
363
doc/README.md
Normal file
@@ -0,0 +1,363 @@
|
|||||||
|
# doc
|
||||||
|
|
||||||
|
This is documentation relevant to the project.
|
||||||
|
SQL files are master.mdb dumps labeled by date and region.
|
||||||
|
This file is my notes from exploring the database.
|
||||||
|
|
||||||
|
# text_data categories
|
||||||
|
|
||||||
|
- 6 is character names, 4 is [variant] character name, 5 is [variant], 14 is clothing names
|
||||||
|
- 47 is skill names, 48 is skill descriptions
|
||||||
|
- 75 is support card names incl. variant, 76 is support card variant, 77 is support card character
|
||||||
|
- 147 is spark names, 172 is spark descriptions
|
||||||
|
- 33 is race names by race id, 28 is race names by race instance id, 31 is race courses, 111 is saddle names
|
||||||
|
- 65 is player titles, 66 is title descriptions - ties with honor_data?
|
||||||
|
- 119 is scenario full titles (e.g. The Beginning: URA Finale), 120 is scenario descriptions, 237 is scenario names (e.g. URA Finale)
|
||||||
|
|
||||||
|
# succession factor (sparks)
|
||||||
|
|
||||||
|
tables are succession_factor and succession_factor_effect
|
||||||
|
|
||||||
|
factor_type:
|
||||||
|
- 1 stat
|
||||||
|
- 2 aptitude
|
||||||
|
- 5 race
|
||||||
|
- 4 skill
|
||||||
|
- 6 scenario
|
||||||
|
- 7 carnival bonus
|
||||||
|
- 10 surface gene (white, on jp)
|
||||||
|
- 8 distance gene (white)
|
||||||
|
- 11 style gene (white)
|
||||||
|
- 9 hidden (white, for things like many wins in west japan, summer sprint series, &c.)
|
||||||
|
- 3 unique
|
||||||
|
|
||||||
|
target_type:
|
||||||
|
- 1 speed; value 1 is amount, value 2 is 0
|
||||||
|
- 2 stam
|
||||||
|
- 3 power
|
||||||
|
- 4 guts
|
||||||
|
- 5 wit
|
||||||
|
- 6 skill points
|
||||||
|
- 7 random stat; value 1 is amount, value 2 is always 1?
|
||||||
|
- 11 turf; value 1 is number of levels (1 or 2), value 2 is 0
|
||||||
|
- 12 dirt
|
||||||
|
- 21 front
|
||||||
|
- 22 pace
|
||||||
|
- 23 late
|
||||||
|
- 24 end
|
||||||
|
- 31 sprint
|
||||||
|
- 32 mile
|
||||||
|
- 33 medium
|
||||||
|
- 34 long
|
||||||
|
- 41 skill; value 1 is skill id, value 2 is hint level (1-5)
|
||||||
|
- 51 carnival bonus; value 1 is skill id, value 2 is 1
|
||||||
|
- 61 speed cap; value 1 is presumably amount but the numbers are very small, 1-4; value 2 is 0
|
||||||
|
- 62 stam cap
|
||||||
|
- 63 power cap
|
||||||
|
- 64 guts cap
|
||||||
|
- 65 wit cap
|
||||||
|
|
||||||
|
grade is 2 for unique sparks and 1 otherwise.
|
||||||
|
|
||||||
|
every possible result has a row in succession_factor_effect.
|
||||||
|
effect_id distinguishes possibilities; factors with multiple effects (race and scenario sparks) have multiple rows with equal effect_id.
|
||||||
|
effect_group_id determines the pmf, but no tables have non-empty joins with the same column name, so the distribution values are mystery.
|
||||||
|
even searching for 51 and 52 (effect group ids for 1\* and 2\* race and scenario sparks) on consecutive lines gives nothing.
|
||||||
|
|
||||||
|
getting all interesting spark data, fully expanded with all effects:
|
||||||
|
```sql
|
||||||
|
WITH spark AS (
|
||||||
|
SELECT
|
||||||
|
n."index" AS "id",
|
||||||
|
n."text" AS "name",
|
||||||
|
d."text" AS "description"
|
||||||
|
FROM text_data n
|
||||||
|
LEFT JOIN text_data d ON n."index" = d."index" AND d."category" = 172
|
||||||
|
WHERE n.category = 147
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
spark.name,
|
||||||
|
spark.description,
|
||||||
|
sf.factor_id,
|
||||||
|
sf.factor_group_id,
|
||||||
|
sf.factor_type,
|
||||||
|
sf.effect_group_id,
|
||||||
|
sfe.effect_id,
|
||||||
|
sfe.target_type,
|
||||||
|
sfe.value_1,
|
||||||
|
sfe.value_2
|
||||||
|
FROM spark
|
||||||
|
JOIN succession_factor sf ON spark.id = sf.factor_id
|
||||||
|
JOIN succession_factor_effect sfe ON sf.factor_group_id = sfe.factor_group_id
|
||||||
|
ORDER BY sf.factor_id, sfe.effect_id, sfe.target_type
|
||||||
|
```
|
||||||
|
|
||||||
|
(change joins to left to include sparks that aren't implemented yet, mostly uniques)
|
||||||
|
|
||||||
|
getting all skills' levels that each spark can give:
|
||||||
|
```sql
|
||||||
|
WITH spark AS (
|
||||||
|
SELECT
|
||||||
|
n."index" AS "id",
|
||||||
|
n."text" AS "name",
|
||||||
|
d."text" AS "description"
|
||||||
|
FROM text_data n
|
||||||
|
LEFT JOIN text_data d ON n."index" = d."index" AND d."category" = 172
|
||||||
|
WHERE n.category = 147
|
||||||
|
), spark_skills AS (
|
||||||
|
SELECT DISTINCT
|
||||||
|
factor_group_id,
|
||||||
|
value_1,
|
||||||
|
value_2
|
||||||
|
FROM succession_factor_effect
|
||||||
|
WHERE target_type = 41
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
spark.name,
|
||||||
|
spark.description,
|
||||||
|
sf.factor_id,
|
||||||
|
sf.factor_group_id,
|
||||||
|
sf.factor_type,
|
||||||
|
sf.effect_group_id,
|
||||||
|
ss.value_1 AS skill,
|
||||||
|
JSON_GROUP_ARRAY(ss.value_2) AS levels
|
||||||
|
FROM spark
|
||||||
|
JOIN succession_factor sf ON spark.id = sf.factor_id
|
||||||
|
JOIN spark_skills ss ON sf.factor_group_id = ss.factor_group_id
|
||||||
|
GROUP BY sf.factor_id
|
||||||
|
ORDER BY sf.factor_id
|
||||||
|
```
|
||||||
|
although realistically you can just use effect_group_id to determine both probabilities and support.
|
||||||
|
race sparks with skills always give +1, skill sparks always give +1-5, unique sparks always give +1-3.
|
||||||
|
|
||||||
|
# support cards
|
||||||
|
|
||||||
|
- hints are defined in single_mode_hint_gain, grouped by hint_group
|
||||||
|
- hint_gain_type is 0 for skill, 1 for stat or skill points
|
||||||
|
- hint_value_1 is skill id or stat target 1 = speed 2 = stam ... 5 = guts; 30 = skill points
|
||||||
|
- hint_value_2 appears to be base skill hint level (1 always) or stat amount (6, 2, 1 for guts speed/pow, 5 for wit skill points)
|
||||||
|
|
||||||
|
# skills
|
||||||
|
|
||||||
|
- single_mode_skill_need_point is base number of skill points to buy each skill
|
||||||
|
- support card skill hints are defined in single_mode_hint_gain
|
||||||
|
- skill_set includes trainee unique starting skills, among many other things
|
||||||
|
- available_skill_set has trainee starting skills other than their uniques
|
||||||
|
|
||||||
|
skill categories:
|
||||||
|
- 0 passive
|
||||||
|
- 1 early race
|
||||||
|
- 2 mid-race
|
||||||
|
- 3 late race or last spurt
|
||||||
|
- 4 anytime
|
||||||
|
- 5 unique
|
||||||
|
|
||||||
|
unique_skill_id_1 is for inherited uniques, points to non-inherited version.
|
||||||
|
unique_skill_id_2 is same but points to 1\*/2\* version.
|
||||||
|
|
||||||
|
exp_type appears to be whether the skill gains levels, i.e. own unique.
|
||||||
|
|
||||||
|
ability time and cooldown time are given in tenths of milliseconds, i.e. divide by 10000 to get seconds.
|
||||||
|
|
||||||
|
ability types:
|
||||||
|
- 1 speed bonus => ability_value / 10000 is flat gain
|
||||||
|
- 2 stamina bonus
|
||||||
|
- 3 power bonus
|
||||||
|
- 4 guts bonus
|
||||||
|
- 5 wit bonus
|
||||||
|
- 6 runaway => ability_value = 0
|
||||||
|
- 8 vision => ability_value / 10000 is amount (what are the units? what does vision do??)
|
||||||
|
- 9 heal or stam debuff => ability_value / 10000 is hp modify
|
||||||
|
- 10 starting gate delay (focus, concentration, gatekept) => ability_value / 10000 is multiplier
|
||||||
|
- 13 frenzy => ability_value / 10000 is rush time modifier (add?)
|
||||||
|
- 21 current speed => ability_value / 10000 is multiplier modifier
|
||||||
|
- 27 target speed => ''
|
||||||
|
- 28 lane change speed => ''
|
||||||
|
- 31 accel => ''
|
||||||
|
- 35 force lane change? it's only on dodging danger/sixth sense with a value of 5000 on both, gametora says "change lane (50% of the track)"
|
||||||
|
|
||||||
|
target types:
|
||||||
|
- 0 none (second and third abilities on skills that only have one)
|
||||||
|
- 1 self
|
||||||
|
- 2 others who have sympathy? only on autumn neo universe's rare green skill which triggers everyone's sympathy, and there's no target value
|
||||||
|
- 4 others in view
|
||||||
|
- 7 frontmost? only on daiichi ruby's unique, has a target value of 5 but the description says 先頭から4人まで
|
||||||
|
- 9 others ahead, target_value is number of targets (18 for all)
|
||||||
|
- 10 others behind, target_value is number of targets
|
||||||
|
- 11 all teammates
|
||||||
|
- 18 others in style, target_value is style (1=front, 2=pace, 3=late, 4=end)
|
||||||
|
- 19 rushing others ahead
|
||||||
|
- 20 rushing others behind
|
||||||
|
- 21 rushing others in style, target_value is style
|
||||||
|
- 22 specific character, target value is character id
|
||||||
|
- 23 other who triggered the skill
|
||||||
|
|
||||||
|
TODO target types only in jp: 2, 7, 11, 22, 23
|
||||||
|
|
||||||
|
ability_value_usage can be 1 for plain or 2-6 for aoharu stat skill stat scaling
|
||||||
|
|
||||||
|
seems to be activate_lot = 1 means wit check, 0 means guaranteed
|
||||||
|
|
||||||
|
# races
|
||||||
|
|
||||||
|
- group 1, grade: g1 100, g2 200, g3 300, op 400, pre-op 700
|
||||||
|
- group 61 is ....?? don't match anything
|
||||||
|
- several races are defined twice for "weird" races that appear in certain careers (maruzensky's 5 opponent spring stakes, mcqueen/ryan/rice's kyoto takarazuka kinen, &c.); these **do not** count as the same race as the normal versions for shared saddle bonus
|
||||||
|
|
||||||
|
single_mode_wins_saddle defines titles (classic triple crown, tenno sweep, &c.) using win_saddle_type = 0
|
||||||
|
|
||||||
|
race_instance is a combination of race, npc group, race date (month*100 + day), and time of day.
|
||||||
|
it isn't actually anything i care about.
|
||||||
|
|
||||||
|
which is to say, what i do care about is mapping races to each turn they're offered, and having a "race instance" enum like Hopeful-Stakes-Junior, Yasuda-Kinen-Classic, &c.
|
||||||
|
|
||||||
|
single_mode_program defines the race instances available for each turn, but the year is expressed a bit weirdly in the race_permission column:
|
||||||
|
- 1 = junior year
|
||||||
|
- 2 = classic year
|
||||||
|
- 3 = classic and senior year
|
||||||
|
- 4 = senior year
|
||||||
|
- 5 = ura finale
|
||||||
|
|
||||||
|
grade_rate_id appears to be consistently 800 iff maiden race and 900 iff debut race, but the values particularly for g1s are all over the place.
|
||||||
|
recommend_class_id appears to be consistently 1 iff maiden race or debut race, 2 iff pre-op, 3 iff op; but other values are confusing.
|
||||||
|
so, it doesn't seem like there's a particular flag that identifies maiden races, despite the restrictions on when they appear in the ui.
|
||||||
|
|
||||||
|
# trainee definitions
|
||||||
|
|
||||||
|
- card_data has universal trainee stats: base skill set, stat growth bonuses ("talent"), default running style
|
||||||
|
- card_rarity_data has per-star-level stats: initial numbers, stat caps (!!), aptitudes (!!)
|
||||||
|
- card_talent_upgrade has costs to increase potential level, but it doesn't seem to have skill sets
|
||||||
|
- card_talent_hint_upgrade has costs to raise hint levels, but it's actually universal, only six rows
|
||||||
|
- single_mode_route_race is career goals (not only races)
|
||||||
|
- available_skill_set has starting skills including those unlocked by potential level given under need_rank (0 for pl1, 2 for pl2)
|
||||||
|
|
||||||
|
# lobby conversations!!!
|
||||||
|
|
||||||
|
table is home_story_trigger.
|
||||||
|
|
||||||
|
pos_id values:
|
||||||
|
- 110 right side, toward the front
|
||||||
|
- 120 same, but two characters
|
||||||
|
- 130 same, but three characters
|
||||||
|
- 210 left side table
|
||||||
|
- 220
|
||||||
|
- 310 center back seat
|
||||||
|
- 410 center posters
|
||||||
|
- 420
|
||||||
|
- 430
|
||||||
|
- 510 left school map
|
||||||
|
- 520
|
||||||
|
- 530
|
||||||
|
|
||||||
|
num is how many characters are involved, but also can just check chara_id_{1,2,3} for nonzero.
|
||||||
|
|
||||||
|
unsure what condition_type is.
|
||||||
|
values of 2 and 3 always have two or three characters, and values of 4 (jp only) always have three, but 0 and 1 can have any number.
|
||||||
|
there's no requirement for stories like having a horse at all, much less an affinity level.
|
||||||
|
|
||||||
|
gallery_chara_id is the character whose conversation it is; chara_id_{1,2,3} are the characters involved.
|
||||||
|
gallery_chara_id is always one of the three, but it can be any one of the three.
|
||||||
|
disp_order then is the conversation number within their gallery.
|
||||||
|
|
||||||
|
getting all conversation data:
|
||||||
|
```sql
|
||||||
|
WITH chara_name AS (
|
||||||
|
SELECT "index" AS id, "text" AS name
|
||||||
|
FROM text_data
|
||||||
|
WHERE category = 6
|
||||||
|
), convo_loc_names AS (
|
||||||
|
SELECT 110 AS pos_id, 'right side front' AS name UNION ALL
|
||||||
|
SELECT 120 AS pos_id, 'right side front' AS name UNION ALL
|
||||||
|
SELECT 130 AS pos_id, 'right side front' AS name UNION ALL
|
||||||
|
SELECT 210 AS pos_id, 'left side table' AS name UNION ALL
|
||||||
|
SELECT 220 AS pos_id, 'left side table' AS name UNION ALL
|
||||||
|
SELECT 310 AS pos_id, 'center back seat' AS name UNION ALL
|
||||||
|
SELECT 410 AS pos_id, 'center posters' AS name UNION ALL
|
||||||
|
SELECT 420 AS pos_id, 'center posters' AS name UNION ALL
|
||||||
|
SELECT 430 AS pos_id, 'center posters' AS name UNION ALL
|
||||||
|
SELECT 510 AS pos_id, 'left side school map' AS name UNION ALL
|
||||||
|
SELECT 520 AS pos_id, 'left side school map' AS name UNION ALL
|
||||||
|
SELECT 530 AS pos_id, 'left side school map' AS name
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
n.name,
|
||||||
|
s.disp_order,
|
||||||
|
l.name,
|
||||||
|
c1.name,
|
||||||
|
c2.name,
|
||||||
|
c3.name,
|
||||||
|
s.condition_type
|
||||||
|
FROM home_story_trigger s
|
||||||
|
LEFT JOIN chara_name n ON s.gallery_chara_id = n.id
|
||||||
|
LEFT JOIN chara_name c1 ON s.chara_id_1 = c1.id
|
||||||
|
LEFT JOIN chara_name c2 ON s.chara_id_2 = c2.id
|
||||||
|
LEFT JOIN chara_name c3 ON s.chara_id_3 = c3.id
|
||||||
|
LEFT JOIN convo_loc_names l ON s.pos_id = l.pos_id
|
||||||
|
ORDER BY s.gallery_chara_id, s.disp_order
|
||||||
|
```
|
||||||
|
|
||||||
|
# update diffs
|
||||||
|
|
||||||
|
complete list of tables with inserts in both the 2026-01-15 update adding fine motion, manhattan cafe ssr, inari one sr and the 2026-01-22 update adding tamamo cross and main story 5:
|
||||||
|
|
||||||
|
- announce_character
|
||||||
|
- announce_data
|
||||||
|
- available_skill_set
|
||||||
|
- background_data
|
||||||
|
- banner_data
|
||||||
|
- campaign_chara_story_schedule
|
||||||
|
- campaign_data
|
||||||
|
- campaign_single_race_add_data
|
||||||
|
- campaign_single_race_add_reward
|
||||||
|
- card_data
|
||||||
|
- card_rarity_data
|
||||||
|
- card_talent_upgrade
|
||||||
|
- champions_news_chara_comment
|
||||||
|
- chara_category_motion
|
||||||
|
- character_system_lottery
|
||||||
|
- character_system_text
|
||||||
|
- chara_motion_set
|
||||||
|
- chara_story_data
|
||||||
|
- dress_data
|
||||||
|
- gacha_available
|
||||||
|
- gacha_data
|
||||||
|
- gacha_exchange
|
||||||
|
- gacha_top_bg
|
||||||
|
- home_story_trigger
|
||||||
|
- home_walk_group
|
||||||
|
- honor_data
|
||||||
|
- item_exchange
|
||||||
|
- item_place
|
||||||
|
- jukebox_chara_tag_data
|
||||||
|
- jukebox_comment
|
||||||
|
- jukebox_reaction_data
|
||||||
|
- mission_data
|
||||||
|
- nickname
|
||||||
|
- piece_data
|
||||||
|
- race
|
||||||
|
- race_bgm_cutin_extension_time
|
||||||
|
- race_instance
|
||||||
|
- race_jikkyo_base
|
||||||
|
- race_jikkyo_race
|
||||||
|
- race_jikkyo_trigger
|
||||||
|
- single_mode_chara_program
|
||||||
|
- single_mode_conclusion_set
|
||||||
|
- single_mode_event_production
|
||||||
|
- single_mode_hint_gain
|
||||||
|
- single_mode_npc
|
||||||
|
- single_mode_rival
|
||||||
|
- single_mode_route
|
||||||
|
- single_mode_route_race
|
||||||
|
- single_mode_scout_chara
|
||||||
|
- single_mode_skill_need_point
|
||||||
|
- single_mode_story_data
|
||||||
|
- single_mode_tag_card_pos
|
||||||
|
- skill_data
|
||||||
|
- skill_set
|
||||||
|
- succession_factor
|
||||||
|
- succession_factor_effect
|
||||||
|
- succession_relation_member
|
||||||
|
- support_card_data
|
||||||
|
- support_card_effect_table
|
||||||
|
- support_card_unique_effect
|
||||||
|
- text_data
|
||||||
8
generate.sh
Executable file
8
generate.sh
Executable file
@@ -0,0 +1,8 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
go run ./horsegen "$@"
|
||||||
|
go generate ./horse/...
|
||||||
|
go fmt ./...
|
||||||
|
go test ./...
|
||||||
114126
global/affinity.json
Normal file
114126
global/affinity.json
Normal file
File diff suppressed because it is too large
Load Diff
214
global/character.json
Normal file
214
global/character.json
Normal file
@@ -0,0 +1,214 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"chara_id": 1001,
|
||||||
|
"name": "Special Week"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1002,
|
||||||
|
"name": "Silence Suzuka"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1003,
|
||||||
|
"name": "Tokai Teio"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1004,
|
||||||
|
"name": "Maruzensky"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1005,
|
||||||
|
"name": "Fuji Kiseki"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1006,
|
||||||
|
"name": "Oguri Cap"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1007,
|
||||||
|
"name": "Gold Ship"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1008,
|
||||||
|
"name": "Vodka"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1009,
|
||||||
|
"name": "Daiwa Scarlet"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1010,
|
||||||
|
"name": "Taiki Shuttle"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1011,
|
||||||
|
"name": "Grass Wonder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1012,
|
||||||
|
"name": "Hishi Amazon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1013,
|
||||||
|
"name": "Mejiro McQueen"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1014,
|
||||||
|
"name": "El Condor Pasa"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1015,
|
||||||
|
"name": "T.M. Opera O"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1016,
|
||||||
|
"name": "Narita Brian"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1017,
|
||||||
|
"name": "Symboli Rudolf"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1018,
|
||||||
|
"name": "Air Groove"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1019,
|
||||||
|
"name": "Agnes Digital"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1020,
|
||||||
|
"name": "Seiun Sky"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1021,
|
||||||
|
"name": "Tamamo Cross"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1022,
|
||||||
|
"name": "Fine Motion"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1023,
|
||||||
|
"name": "Biwa Hayahide"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1024,
|
||||||
|
"name": "Mayano Top Gun"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1025,
|
||||||
|
"name": "Manhattan Cafe"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1026,
|
||||||
|
"name": "Mihono Bourbon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1027,
|
||||||
|
"name": "Mejiro Ryan"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1028,
|
||||||
|
"name": "Hishi Akebono"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1030,
|
||||||
|
"name": "Rice Shower"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1032,
|
||||||
|
"name": "Agnes Tachyon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1033,
|
||||||
|
"name": "Admire Vega"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1034,
|
||||||
|
"name": "Inari One"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1035,
|
||||||
|
"name": "Winning Ticket"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1037,
|
||||||
|
"name": "Eishin Flash"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1038,
|
||||||
|
"name": "Curren Chan"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1039,
|
||||||
|
"name": "Kawakami Princess"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1040,
|
||||||
|
"name": "Gold City"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1041,
|
||||||
|
"name": "Sakura Bakushin O"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1044,
|
||||||
|
"name": "Sweep Tosho"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1045,
|
||||||
|
"name": "Super Creek"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1046,
|
||||||
|
"name": "Smart Falcon"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1048,
|
||||||
|
"name": "Tosen Jordan"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1050,
|
||||||
|
"name": "Narita Taishin"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1051,
|
||||||
|
"name": "Nishino Flower"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1052,
|
||||||
|
"name": "Haru Urara"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1056,
|
||||||
|
"name": "Matikanefukukitaru"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1058,
|
||||||
|
"name": "Meisho Doto"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1059,
|
||||||
|
"name": "Mejiro Dober"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1060,
|
||||||
|
"name": "Nice Nature"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1061,
|
||||||
|
"name": "King Halo"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1068,
|
||||||
|
"name": "Kitasan Black"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1069,
|
||||||
|
"name": "Sakura Chiyono O"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"chara_id": 1071,
|
||||||
|
"name": "Mejiro Ardan"
|
||||||
|
}
|
||||||
|
]
|
||||||
1712
global/race.json
Normal file
1712
global/race.json
Normal file
File diff suppressed because it is too large
Load Diff
1420
global/saddle.json
Normal file
1420
global/saddle.json
Normal file
File diff suppressed because it is too large
Load Diff
12
global/scenario.json
Normal file
12
global/scenario.json
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"scenario_id": 1,
|
||||||
|
"name": "URA Finale",
|
||||||
|
"title": "The Beginning: URA Finale"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"scenario_id": 2,
|
||||||
|
"name": "Unity Cup",
|
||||||
|
"title": "Unity Cup: Shine On, Team Spirit!"
|
||||||
|
}
|
||||||
|
]
|
||||||
1563
global/skill-group.json
Normal file
1563
global/skill-group.json
Normal file
File diff suppressed because it is too large
Load Diff
15756
global/skill.json
Normal file
15756
global/skill.json
Normal file
File diff suppressed because it is too large
Load Diff
35564
global/spark.json
Normal file
35564
global/spark.json
Normal file
File diff suppressed because it is too large
Load Diff
1634
global/uma.json
Normal file
1634
global/uma.json
Normal file
File diff suppressed because it is too large
Load Diff
32
go.mod
Normal file
32
go.mod
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
module git.sunturtle.xyz/zephyr/horse
|
||||||
|
|
||||||
|
go 1.25.5
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/disgoorg/disgo v0.19.0-rc.15
|
||||||
|
github.com/junegunn/fzf v0.67.0
|
||||||
|
golang.org/x/sync v0.20.0
|
||||||
|
zombiezen.com/go/sqlite v1.4.2
|
||||||
|
)
|
||||||
|
|
||||||
|
require (
|
||||||
|
github.com/disgoorg/json/v2 v2.0.0 // indirect
|
||||||
|
github.com/disgoorg/omit v1.0.0 // indirect
|
||||||
|
github.com/disgoorg/snowflake/v2 v2.0.3 // indirect
|
||||||
|
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||||
|
github.com/google/uuid v1.6.0 // indirect
|
||||||
|
github.com/gorilla/websocket v1.5.3 // indirect
|
||||||
|
github.com/klauspost/compress v1.18.2 // indirect
|
||||||
|
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 // indirect
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||||
|
github.com/rivo/uniseg v0.4.7 // indirect
|
||||||
|
github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad // indirect
|
||||||
|
golang.org/x/crypto v0.46.0 // indirect
|
||||||
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
|
||||||
|
golang.org/x/sys v0.39.0 // indirect
|
||||||
|
modernc.org/libc v1.65.7 // indirect
|
||||||
|
modernc.org/mathutil v1.7.1 // indirect
|
||||||
|
modernc.org/memory v1.11.0 // indirect
|
||||||
|
modernc.org/sqlite v1.37.1 // indirect
|
||||||
|
)
|
||||||
79
go.sum
Normal file
79
go.sum
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||||
|
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/disgoorg/disgo v0.19.0-rc.15 h1:x0NsV2gcbdjwuztsg2wYXw76p1Cpc8f6ByDrkPcfQtU=
|
||||||
|
github.com/disgoorg/disgo v0.19.0-rc.15/go.mod h1:14mgXzenkJqifkDmsEgU0zI1di6jNXodwX6L8geW33A=
|
||||||
|
github.com/disgoorg/json/v2 v2.0.0 h1:U16yy/ARK7/aEpzjjqK1b/KaqqGHozUdeVw/DViEzQI=
|
||||||
|
github.com/disgoorg/json/v2 v2.0.0/go.mod h1:jZTBC0nIE1WeetSEI3/Dka8g+qglb4FPVmp5I5HpEfI=
|
||||||
|
github.com/disgoorg/omit v1.0.0 h1:y0LkVUOyUHT8ZlnhIAeOZEA22UYykeysK8bLJ0SfT78=
|
||||||
|
github.com/disgoorg/omit v1.0.0/go.mod h1:RTmSARkf6PWT/UckwI0bV8XgWkWQoPppaT01rYKLcFQ=
|
||||||
|
github.com/disgoorg/snowflake/v2 v2.0.3 h1:3B+PpFjr7j4ad7oeJu4RlQ+nYOTadsKapJIzgvSI2Ro=
|
||||||
|
github.com/disgoorg/snowflake/v2 v2.0.3/go.mod h1:W6r7NUA7DwfZLwr00km6G4UnZ0zcoLBRufhkFWgAc4c=
|
||||||
|
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
|
||||||
|
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
|
||||||
|
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||||
|
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||||
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
|
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
|
||||||
|
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||||
|
github.com/junegunn/fzf v0.67.0 h1:naiOdIkV5/ZCfHgKQIV/f5YDWowl95G6yyOQqW8FeSo=
|
||||||
|
github.com/junegunn/fzf v0.67.0/go.mod h1:xlXX2/rmsccKQUnr9QOXPDi5DyV9cM0UjKy/huScBeE=
|
||||||
|
github.com/klauspost/compress v1.18.2 h1:iiPHWW0YrcFgpBYhsA6D1+fqHssJscY/Tm/y2Uqnapk=
|
||||||
|
github.com/klauspost/compress v1.18.2/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=
|
||||||
|
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||||
|
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||||
|
github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
|
||||||
|
github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
|
||||||
|
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
|
||||||
|
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||||
|
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||||
|
github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad h1:qIQkSlF5vAUHxEmTbaqt1hkJ/t6skqEGYiMag343ucI=
|
||||||
|
github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad/go.mod h1:/pA7k3zsXKdjjAiUhB5CjuKib9KJGCaLvZwtxGC8U0s=
|
||||||
|
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||||
|
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||||
|
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
|
||||||
|
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
|
||||||
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 h1:R84qjqJb5nVJMxqWYb3np9L5ZsaDtB+a39EqjV0JSUM=
|
||||||
|
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0/go.mod h1:S9Xr4PYopiDyqSyp5NjCrhFrqg6A5zA2E/iPHPhqnS8=
|
||||||
|
golang.org/x/mod v0.24.0 h1:ZfthKaKaT4NrhGVZHO1/WDTwGES4De8KtWO0SIbNJMU=
|
||||||
|
golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww=
|
||||||
|
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||||
|
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||||
|
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
|
golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
|
||||||
|
golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
|
||||||
|
golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
|
||||||
|
golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
|
||||||
|
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||||
|
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||||
|
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||||
|
modernc.org/cc/v4 v4.26.1 h1:+X5NtzVBn0KgsBCBe+xkDC7twLb/jNVj9FPgiwSQO3s=
|
||||||
|
modernc.org/cc/v4 v4.26.1/go.mod h1:uVtb5OGqUKpoLWhqwNQo/8LwvoiEBLvZXIQ/SmO6mL0=
|
||||||
|
modernc.org/ccgo/v4 v4.28.0 h1:rjznn6WWehKq7dG4JtLRKxb52Ecv8OUGah8+Z/SfpNU=
|
||||||
|
modernc.org/ccgo/v4 v4.28.0/go.mod h1:JygV3+9AV6SmPhDasu4JgquwU81XAKLd3OKTUDNOiKE=
|
||||||
|
modernc.org/fileutil v1.3.1 h1:8vq5fe7jdtEvoCf3Zf9Nm0Q05sH6kGx0Op2CPx1wTC8=
|
||||||
|
modernc.org/fileutil v1.3.1/go.mod h1:HxmghZSZVAz/LXcMNwZPA/DRrQZEVP9VX0V4LQGQFOc=
|
||||||
|
modernc.org/gc/v2 v2.6.5 h1:nyqdV8q46KvTpZlsw66kWqwXRHdjIlJOhG6kxiV/9xI=
|
||||||
|
modernc.org/gc/v2 v2.6.5/go.mod h1:YgIahr1ypgfe7chRuJi2gD7DBQiKSLMPgBQe9oIiito=
|
||||||
|
modernc.org/libc v1.65.7 h1:Ia9Z4yzZtWNtUIuiPuQ7Qf7kxYrxP1/jeHZzG8bFu00=
|
||||||
|
modernc.org/libc v1.65.7/go.mod h1:011EQibzzio/VX3ygj1qGFt5kMjP0lHb0qCW5/D/pQU=
|
||||||
|
modernc.org/mathutil v1.7.1 h1:GCZVGXdaN8gTqB1Mf/usp1Y/hSqgI2vAGGP4jZMCxOU=
|
||||||
|
modernc.org/mathutil v1.7.1/go.mod h1:4p5IwJITfppl0G4sUEDtCr4DthTaT47/N3aT6MhfgJg=
|
||||||
|
modernc.org/memory v1.11.0 h1:o4QC8aMQzmcwCK3t3Ux/ZHmwFPzE6hf2Y5LbkRs+hbI=
|
||||||
|
modernc.org/memory v1.11.0/go.mod h1:/JP4VbVC+K5sU2wZi9bHoq2MAkCnrt2r98UGeSK7Mjw=
|
||||||
|
modernc.org/opt v0.1.4 h1:2kNGMRiUjrp4LcaPuLY2PzUfqM/w9N23quVwhKt5Qm8=
|
||||||
|
modernc.org/opt v0.1.4/go.mod h1:03fq9lsNfvkYSfxrfUhZCWPk1lm4cq4N+Bh//bEtgns=
|
||||||
|
modernc.org/sortutil v1.2.1 h1:+xyoGf15mM3NMlPDnFqrteY07klSFxLElE2PVuWIJ7w=
|
||||||
|
modernc.org/sortutil v1.2.1/go.mod h1:7ZI3a3REbai7gzCLcotuw9AC4VZVpYMjDzETGsSMqJE=
|
||||||
|
modernc.org/sqlite v1.37.1 h1:EgHJK/FPoqC+q2YBXg7fUmES37pCHFc97sI7zSayBEs=
|
||||||
|
modernc.org/sqlite v1.37.1/go.mod h1:XwdRtsE1MpiBcL54+MbKcaDvcuej+IYSMfLN6gSKV8g=
|
||||||
|
modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
|
||||||
|
modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
|
||||||
|
modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
|
||||||
|
modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
|
||||||
|
zombiezen.com/go/sqlite v1.4.2 h1:KZXLrBuJ7tKNEm+VJcApLMeQbhmAUOKA5VWS93DfFRo=
|
||||||
|
zombiezen.com/go/sqlite v1.4.2/go.mod h1:5Kd4taTAD4MkBzT25mQ9uaAlLjyR0rFhsR6iINO70jc=
|
||||||
6
horse/README.md
Normal file
6
horse/README.md
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# horse
|
||||||
|
|
||||||
|
This directory contains manually written code and types on which the generated code depends.
|
||||||
|
|
||||||
|
The generated code is in ./global; other regions will follow the same convention once they are supported.
|
||||||
|
It is always safe to delete the entire directories and regenerate them.
|
||||||
58
horse/abilitytarget_string.go
Normal file
58
horse/abilitytarget_string.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
// Code generated by "stringer -type AbilityTarget -trimprefix Target -linecomment"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package horse
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[TargetSelf-1]
|
||||||
|
_ = x[TargetSympathizers-2]
|
||||||
|
_ = x[TargetInView-4]
|
||||||
|
_ = x[TargetFrontmost-7]
|
||||||
|
_ = x[TargetAhead-9]
|
||||||
|
_ = x[TargetBehind-10]
|
||||||
|
_ = x[TargetAllTeammates-11]
|
||||||
|
_ = x[TargetStyle-18]
|
||||||
|
_ = x[TargetRushingAhead-19]
|
||||||
|
_ = x[TargetRushingBehind-20]
|
||||||
|
_ = x[TargetRushingStyle-21]
|
||||||
|
_ = x[TargetCharacter-22]
|
||||||
|
_ = x[TargetTriggering-23]
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_AbilityTarget_name_0 = "selfothers with Sympathy"
|
||||||
|
_AbilityTarget_name_1 = "others in view"
|
||||||
|
_AbilityTarget_name_2 = "frontmost"
|
||||||
|
_AbilityTarget_name_3 = "others aheadothers behindall teammates"
|
||||||
|
_AbilityTarget_name_4 = "using stylerushing others aheadrushing others behindrushing using stylespecific characterwhosoever triggered this skill"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_AbilityTarget_index_0 = [...]uint8{0, 4, 24}
|
||||||
|
_AbilityTarget_index_3 = [...]uint8{0, 12, 25, 38}
|
||||||
|
_AbilityTarget_index_4 = [...]uint8{0, 11, 31, 52, 71, 89, 119}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i AbilityTarget) String() string {
|
||||||
|
switch {
|
||||||
|
case 1 <= i && i <= 2:
|
||||||
|
i -= 1
|
||||||
|
return _AbilityTarget_name_0[_AbilityTarget_index_0[i]:_AbilityTarget_index_0[i+1]]
|
||||||
|
case i == 4:
|
||||||
|
return _AbilityTarget_name_1
|
||||||
|
case i == 7:
|
||||||
|
return _AbilityTarget_name_2
|
||||||
|
case 9 <= i && i <= 11:
|
||||||
|
i -= 9
|
||||||
|
return _AbilityTarget_name_3[_AbilityTarget_index_3[i]:_AbilityTarget_index_3[i+1]]
|
||||||
|
case 18 <= i && i <= 23:
|
||||||
|
i -= 18
|
||||||
|
return _AbilityTarget_name_4[_AbilityTarget_index_4[i]:_AbilityTarget_index_4[i+1]]
|
||||||
|
default:
|
||||||
|
return "AbilityTarget(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
66
horse/abilitytype_string.go
Normal file
66
horse/abilitytype_string.go
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
// Code generated by "stringer -type AbilityType -trimprefix Ability -linecomment"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package horse
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[AbilityPassiveSpeed-1]
|
||||||
|
_ = x[AbilityPassiveStamina-2]
|
||||||
|
_ = x[AbilityPassivePower-3]
|
||||||
|
_ = x[AbilityPassiveGuts-4]
|
||||||
|
_ = x[AbilityPassiveWit-5]
|
||||||
|
_ = x[AbilityGreatEscape-6]
|
||||||
|
_ = x[AbilityVision-8]
|
||||||
|
_ = x[AbilityHP-9]
|
||||||
|
_ = x[AbilityGateDelay-10]
|
||||||
|
_ = x[AbilityFrenzy-13]
|
||||||
|
_ = x[AbilityCurrentSpeed-21]
|
||||||
|
_ = x[AbilityTargetSpeed-27]
|
||||||
|
_ = x[AbilityLaneSpeed-28]
|
||||||
|
_ = x[AbilityAccel-31]
|
||||||
|
_ = x[AbilityLaneChange-35]
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_AbilityType_name_0 = "SpeedStaminaPowerGutsWitEnable Great Escape"
|
||||||
|
_AbilityType_name_1 = "VisionHPGate delay multiplier"
|
||||||
|
_AbilityType_name_2 = "Frenzy"
|
||||||
|
_AbilityType_name_3 = "Current speed"
|
||||||
|
_AbilityType_name_4 = "Target speedLane change speed"
|
||||||
|
_AbilityType_name_5 = "Acceleration"
|
||||||
|
_AbilityType_name_6 = "Forced lane change"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_AbilityType_index_0 = [...]uint8{0, 5, 12, 17, 21, 24, 43}
|
||||||
|
_AbilityType_index_1 = [...]uint8{0, 6, 8, 29}
|
||||||
|
_AbilityType_index_4 = [...]uint8{0, 12, 29}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i AbilityType) String() string {
|
||||||
|
switch {
|
||||||
|
case 1 <= i && i <= 6:
|
||||||
|
i -= 1
|
||||||
|
return _AbilityType_name_0[_AbilityType_index_0[i]:_AbilityType_index_0[i+1]]
|
||||||
|
case 8 <= i && i <= 10:
|
||||||
|
i -= 8
|
||||||
|
return _AbilityType_name_1[_AbilityType_index_1[i]:_AbilityType_index_1[i+1]]
|
||||||
|
case i == 13:
|
||||||
|
return _AbilityType_name_2
|
||||||
|
case i == 21:
|
||||||
|
return _AbilityType_name_3
|
||||||
|
case 27 <= i && i <= 28:
|
||||||
|
i -= 27
|
||||||
|
return _AbilityType_name_4[_AbilityType_index_4[i]:_AbilityType_index_4[i+1]]
|
||||||
|
case i == 31:
|
||||||
|
return _AbilityType_name_5
|
||||||
|
case i == 35:
|
||||||
|
return _AbilityType_name_6
|
||||||
|
default:
|
||||||
|
return "AbilityType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
62
horse/abilityvalueusage_string.go
Normal file
62
horse/abilityvalueusage_string.go
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
// Code generated by "stringer -type AbilityValueUsage -trimprefix ValueUsage -linecomment"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package horse
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[ValueUsageDirect-1]
|
||||||
|
_ = x[ValueUsageSkillCount-2]
|
||||||
|
_ = x[ValueUsageTeamSpeed-3]
|
||||||
|
_ = x[ValueUsageTeamStamina-4]
|
||||||
|
_ = x[ValueUsageTeamPower-5]
|
||||||
|
_ = x[ValueUsageTeamGuts-6]
|
||||||
|
_ = x[ValueUsageTeamWit-7]
|
||||||
|
_ = x[ValueUsageRandom-8]
|
||||||
|
_ = x[ValueUsageRandom2-9]
|
||||||
|
_ = x[ValueUsageClimax-10]
|
||||||
|
_ = x[ValueUsageMaxStat-13]
|
||||||
|
_ = x[ValueUsageGreenCount-14]
|
||||||
|
_ = x[ValueUsageDistAdd-19]
|
||||||
|
_ = x[ValueUsageMidSideBlock-20]
|
||||||
|
_ = x[ValueUsageSpeed-22]
|
||||||
|
_ = x[ValueUsageSpeed2-23]
|
||||||
|
_ = x[ValueUsageArcPotential-24]
|
||||||
|
_ = x[ValueUsageMaxLead-25]
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_AbilityValueUsage_name_0 = "directlyscaling with the number of skillsscaling with team Speedscaling with team Staminascaling with team Powerscaling with team Gutsscaling with team Witwith a random 0× to 0.04× multiplierwith a random 0× to 0.04× multiplierscaling with the number of races won in training"
|
||||||
|
_AbilityValueUsage_name_1 = "scaling with the highest raw statscaling with the number of Passive skills activated"
|
||||||
|
_AbilityValueUsage_name_2 = "plus extra when far from the leadscaling with mid-race phase blocked side time"
|
||||||
|
_AbilityValueUsage_name_3 = "scaling with overall speedscaling with overall speedscaling with L'Arc global potentialscaling with the longest lead obtained in the first ⅔"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_AbilityValueUsage_index_0 = [...]uint16{0, 8, 41, 64, 89, 112, 134, 155, 193, 231, 279}
|
||||||
|
_AbilityValueUsage_index_1 = [...]uint8{0, 33, 84}
|
||||||
|
_AbilityValueUsage_index_2 = [...]uint8{0, 33, 78}
|
||||||
|
_AbilityValueUsage_index_3 = [...]uint8{0, 26, 52, 87, 142}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i AbilityValueUsage) String() string {
|
||||||
|
switch {
|
||||||
|
case 1 <= i && i <= 10:
|
||||||
|
i -= 1
|
||||||
|
return _AbilityValueUsage_name_0[_AbilityValueUsage_index_0[i]:_AbilityValueUsage_index_0[i+1]]
|
||||||
|
case 13 <= i && i <= 14:
|
||||||
|
i -= 13
|
||||||
|
return _AbilityValueUsage_name_1[_AbilityValueUsage_index_1[i]:_AbilityValueUsage_index_1[i+1]]
|
||||||
|
case 19 <= i && i <= 20:
|
||||||
|
i -= 19
|
||||||
|
return _AbilityValueUsage_name_2[_AbilityValueUsage_index_2[i]:_AbilityValueUsage_index_2[i+1]]
|
||||||
|
case 22 <= i && i <= 25:
|
||||||
|
i -= 22
|
||||||
|
return _AbilityValueUsage_name_3[_AbilityValueUsage_index_3[i]:_AbilityValueUsage_index_3[i+1]]
|
||||||
|
default:
|
||||||
|
return "AbilityValueUsage(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
31
horse/aptitudelevel_string.go
Normal file
31
horse/aptitudelevel_string.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
// Code generated by "stringer -type AptitudeLevel -trimprefix AptitudeLv"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package horse
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[AptitudeLvG-1]
|
||||||
|
_ = x[AptitudeLvF-2]
|
||||||
|
_ = x[AptitudeLvE-3]
|
||||||
|
_ = x[AptitudeLvD-4]
|
||||||
|
_ = x[AptitudeLvC-5]
|
||||||
|
_ = x[AptitudeLvB-6]
|
||||||
|
_ = x[AptitudeLvA-7]
|
||||||
|
_ = x[AptitudeLvS-8]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _AptitudeLevel_name = "GFEDCBAS"
|
||||||
|
|
||||||
|
var _AptitudeLevel_index = [...]uint8{0, 1, 2, 3, 4, 5, 6, 7, 8}
|
||||||
|
|
||||||
|
func (i AptitudeLevel) String() string {
|
||||||
|
idx := int(i) - 1
|
||||||
|
if i < 1 || idx >= len(_AptitudeLevel_index)-1 {
|
||||||
|
return "AptitudeLevel(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _AptitudeLevel_name[_AptitudeLevel_index[idx]:_AptitudeLevel_index[idx+1]]
|
||||||
|
}
|
||||||
19
horse/character.go
Normal file
19
horse/character.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package horse
|
||||||
|
|
||||||
|
type CharacterID int16
|
||||||
|
|
||||||
|
type Character struct {
|
||||||
|
ID CharacterID `json:"chara_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (c Character) String() string {
|
||||||
|
return c.Name
|
||||||
|
}
|
||||||
|
|
||||||
|
type AffinityRelation struct {
|
||||||
|
IDA int `json:"chara_a"`
|
||||||
|
IDB int `json:"chara_b"`
|
||||||
|
IDC int `json:"chara_c,omitzero"`
|
||||||
|
Affinity int `json:"affinity"`
|
||||||
|
}
|
||||||
17
horse/character.kk
Normal file
17
horse/character.kk
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
module horse/character
|
||||||
|
|
||||||
|
import horse/game-id
|
||||||
|
|
||||||
|
pub struct character-detail
|
||||||
|
character-id: character-id
|
||||||
|
name: string
|
||||||
|
|
||||||
|
pub fun detail(
|
||||||
|
c: character-id,
|
||||||
|
?character/show: (character-id) -> string
|
||||||
|
): character-detail
|
||||||
|
Character-detail(c, c.show)
|
||||||
|
|
||||||
|
pub fun character-detail/show(d: character-detail): string
|
||||||
|
val Character-detail(Character-id(id), name) = d
|
||||||
|
name ++ " (ID " ++ id.show ++ ")"
|
||||||
38
horse/durscale_string.go
Normal file
38
horse/durscale_string.go
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// Code generated by "stringer -type DurScale -trimprefix Duration -linecomment"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package horse
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[DurationDirect-1]
|
||||||
|
_ = x[DurationFrontDistance-2]
|
||||||
|
_ = x[DurationRemainingHP-3]
|
||||||
|
_ = x[DurationIncrementPass-4]
|
||||||
|
_ = x[DurationMidSideBlock-5]
|
||||||
|
_ = x[DurationRemainingHP2-7]
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_DurScale_name_0 = "directlyscaling with distance from the frontscaling with remaining HPincreasing with each pass while activescaling with mid-race phase blocked side time"
|
||||||
|
_DurScale_name_1 = "scaling with remaining HP"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_DurScale_index_0 = [...]uint8{0, 8, 44, 69, 107, 152}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i DurScale) String() string {
|
||||||
|
switch {
|
||||||
|
case 1 <= i && i <= 5:
|
||||||
|
i -= 1
|
||||||
|
return _DurScale_name_0[_DurScale_index_0[i]:_DurScale_index_0[i+1]]
|
||||||
|
case i == 7:
|
||||||
|
return _DurScale_name_1
|
||||||
|
default:
|
||||||
|
return "DurScale(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
80
horse/game-id.kk
Normal file
80
horse/game-id.kk
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
module horse/game-id
|
||||||
|
|
||||||
|
// Game ID for characters, cards, skills, races, &c.
|
||||||
|
// Values for different categories may overlap.
|
||||||
|
pub alias game-id = int
|
||||||
|
|
||||||
|
// Specific game ID types.
|
||||||
|
// I've already made mistakes with ID categories and I haven't even committed this file yet.
|
||||||
|
|
||||||
|
pub struct scenario-id
|
||||||
|
game-id: game-id
|
||||||
|
|
||||||
|
// Game ID for characters.
|
||||||
|
// Generally numbers in the range 1000-9999.
|
||||||
|
pub struct character-id
|
||||||
|
game-id: game-id
|
||||||
|
|
||||||
|
// Game ID for trainees, i.e. costume instances of characters.
|
||||||
|
// Generally a character ID with two digits appended.
|
||||||
|
pub struct uma-id
|
||||||
|
game-id: game-id
|
||||||
|
|
||||||
|
// Game ID for skills.
|
||||||
|
pub struct skill-id
|
||||||
|
game-id: game-id
|
||||||
|
|
||||||
|
// Game ID for skill groups.
|
||||||
|
pub struct skill-group-id
|
||||||
|
game-id: game-id
|
||||||
|
|
||||||
|
// Game ID for skill icons.
|
||||||
|
pub struct skill-icon-id
|
||||||
|
game-id: game-id
|
||||||
|
|
||||||
|
// Game ID for races,
|
||||||
|
// i.e. "Tenno Sho (Spring)" and not "Tenno Sho (Spring) at Kyoto Racecourse."
|
||||||
|
pub struct race-id
|
||||||
|
game-id: game-id
|
||||||
|
|
||||||
|
// Game ID for race thumbnails.
|
||||||
|
pub struct race-thumbnail-id
|
||||||
|
game-id: game-id
|
||||||
|
|
||||||
|
// Game ID for saddles,
|
||||||
|
// i.e. one or more race wins that appear as a title.
|
||||||
|
pub struct saddle-id
|
||||||
|
game-id: game-id
|
||||||
|
|
||||||
|
// Game ID for sparks,
|
||||||
|
// i.e. succession factors.
|
||||||
|
pub struct spark-id
|
||||||
|
game-id: game-id
|
||||||
|
|
||||||
|
// Game ID for spark groups,
|
||||||
|
// i.e. all rarities (star counts) of a single spark.
|
||||||
|
pub struct spark-group-id
|
||||||
|
game-id: game-id
|
||||||
|
|
||||||
|
// order2 comparison between any game ID types.
|
||||||
|
pub inline fun order2(x: a, y: a, ?a/game-id: (a) -> game-id): order2<a>
|
||||||
|
match x.game-id.cmp(y.game-id)
|
||||||
|
Lt -> Lt2(x, y)
|
||||||
|
Eq -> Eq2(x)
|
||||||
|
Gt -> Gt2(x, y)
|
||||||
|
|
||||||
|
// Comparison between any game ID types.
|
||||||
|
pub inline fun cmp(x: a, y: a, ?a/game-id: (a) -> game-id): order
|
||||||
|
x.game-id.cmp(y.game-id)
|
||||||
|
|
||||||
|
// Equality between any game ID types.
|
||||||
|
pub inline fun (==)(x: a, y: a, ?a/game-id: (a) -> game-id): bool
|
||||||
|
x.game-id == y.game-id
|
||||||
|
|
||||||
|
// Check whether a game ID is valid, i.e. nonzero.
|
||||||
|
pub inline fun is-valid(x: a, ?a/game-id: (a) -> game-id): bool
|
||||||
|
x.game-id != 0
|
||||||
|
|
||||||
|
// Construct an invalid game ID.
|
||||||
|
pub inline fun default/game-id(): game-id
|
||||||
|
0
|
||||||
8
horse/global.kk
Normal file
8
horse/global.kk
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
module horse/global
|
||||||
|
|
||||||
|
import horse/game-id
|
||||||
|
|
||||||
|
// Shared saddle affinity bonus.
|
||||||
|
// `s` should be the complete list of all saddles shared between the veterans.
|
||||||
|
pub fun saddle-bonus(s: list<saddle-id>): int
|
||||||
|
s.length
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
module horse/horse
|
|
||||||
|
|
||||||
import std/data/rb-map
|
|
||||||
|
|
||||||
// Aptitudes of an umamusume being trained.
|
|
||||||
pub struct uma
|
|
||||||
turf: aptitudes
|
|
||||||
dirt: aptitudes
|
|
||||||
sprint: aptitudes
|
|
||||||
mile: aptitudes
|
|
||||||
medium: aptitudes
|
|
||||||
long: aptitudes
|
|
||||||
front-runner: aptitudes
|
|
||||||
pace-chaser: aptitudes
|
|
||||||
late-surger: aptitudes
|
|
||||||
end-closer: aptitudes
|
|
||||||
|
|
||||||
// Aptitude level distribution.
|
|
||||||
pub alias aptitudes = rbmap<level, float64>
|
|
||||||
|
|
||||||
// Starting aptitude levels.
|
|
||||||
pub type level
|
|
||||||
G
|
|
||||||
F
|
|
||||||
E
|
|
||||||
D
|
|
||||||
C
|
|
||||||
B
|
|
||||||
A
|
|
||||||
S
|
|
||||||
|
|
||||||
// Automatically generated.
|
|
||||||
// Fip comparison of the `level` type.
|
|
||||||
pub fun level/order2(this : level, other : level) : order2<level>
|
|
||||||
match (this, other)
|
|
||||||
(G, G) -> Eq2(G)
|
|
||||||
(G, other') -> Lt2(G, other')
|
|
||||||
(this', G) -> Gt2(G, this')
|
|
||||||
(F, F) -> Eq2(F)
|
|
||||||
(F, other') -> Lt2(F, other')
|
|
||||||
(this', F) -> Gt2(F, this')
|
|
||||||
(E, E) -> Eq2(E)
|
|
||||||
(E, other') -> Lt2(E, other')
|
|
||||||
(this', E) -> Gt2(E, this')
|
|
||||||
(D, D) -> Eq2(D)
|
|
||||||
(D, other') -> Lt2(D, other')
|
|
||||||
(this', D) -> Gt2(D, this')
|
|
||||||
(C, C) -> Eq2(C)
|
|
||||||
(C, other') -> Lt2(C, other')
|
|
||||||
(this', C) -> Gt2(C, this')
|
|
||||||
(B, B) -> Eq2(B)
|
|
||||||
(B, other') -> Lt2(B, other')
|
|
||||||
(this', B) -> Gt2(B, this')
|
|
||||||
(A, A) -> Eq2(A)
|
|
||||||
(A, other') -> Lt2(A, other')
|
|
||||||
(this', A) -> Gt2(A, this')
|
|
||||||
(S, S) -> Eq2(S)
|
|
||||||
|
|
||||||
// Automatically generated.
|
|
||||||
// Shows a string representation of the `level` type.
|
|
||||||
pub fun level/show(this : level) : string
|
|
||||||
match this
|
|
||||||
G -> "G"
|
|
||||||
F -> "F"
|
|
||||||
E -> "E"
|
|
||||||
D -> "D"
|
|
||||||
C -> "C"
|
|
||||||
B -> "B"
|
|
||||||
A -> "A"
|
|
||||||
S -> "S"
|
|
||||||
349
horse/legacy.kk
349
horse/legacy.kk
@@ -1,247 +1,124 @@
|
|||||||
module horse/legacy
|
module horse/legacy
|
||||||
|
|
||||||
import horse/race
|
import std/num/decimal
|
||||||
|
import std/data/linearmap
|
||||||
|
import std/data/linearset
|
||||||
|
import horse/game-id
|
||||||
import horse/spark
|
import horse/spark
|
||||||
|
import horse/prob/dist
|
||||||
|
|
||||||
|
// A legacy, or parent and grandparents.
|
||||||
pub struct legacy
|
pub struct legacy
|
||||||
uma: veteran
|
uma: veteran
|
||||||
parents: (veteran, veteran)
|
sub1: veteran
|
||||||
|
sub2: veteran
|
||||||
|
|
||||||
|
// A veteran, or the result of a completed career.
|
||||||
pub struct veteran
|
pub struct veteran
|
||||||
character: character
|
uma: uma-id
|
||||||
stat: spark<stat>
|
sparks: list<spark-id>
|
||||||
aptitude: spark<aptitude>
|
saddles: list<saddle-id>
|
||||||
unique: maybe<spark<unique>>
|
|
||||||
generic: list<spark<generic>>
|
|
||||||
results: list<race-result>
|
|
||||||
|
|
||||||
// Character identity.
|
// Get all saddles shared between two lists thereof.
|
||||||
pub type character
|
pub fun shared-saddles(a: list<saddle-id>, b: list<saddle-id>): list<saddle-id>
|
||||||
Special-Week
|
val sa: linearSet<saddle-id> = a.foldl(linear-set(Nil)) fn(s, id) if id.is-valid then s.add(id) else s
|
||||||
Silence-Suzuka
|
val c: linearSet<saddle-id> = b.foldl(linear-set(Nil)) fn(s, id) if sa.member(id) then s.add(id) else s
|
||||||
Tokai-Teio
|
c.list
|
||||||
Maruzensky
|
|
||||||
Fuji-Kiseki
|
|
||||||
Oguri-Cap
|
|
||||||
Gold-Ship
|
|
||||||
Vodka
|
|
||||||
Daiwa-Scarlet
|
|
||||||
Taiki-Shuttle
|
|
||||||
Grass-Wonder
|
|
||||||
Hishi-Amazon
|
|
||||||
Mejiro-McQueen
|
|
||||||
El-Condor-Pasa
|
|
||||||
TM-Opera-O
|
|
||||||
Narita-Brian
|
|
||||||
Symboli-Rudolf
|
|
||||||
Air-Groove
|
|
||||||
Agnes-Digital
|
|
||||||
Seiun-Sky
|
|
||||||
Biwa-Hayahide
|
|
||||||
Mayano-Top-Gun
|
|
||||||
Manhattan-Cafe
|
|
||||||
Mihono-Bourbon
|
|
||||||
Mejiro-Ryan
|
|
||||||
Hishi-Akebono
|
|
||||||
Rice-Shower
|
|
||||||
Agnes-Tachyon
|
|
||||||
Winning-Ticket
|
|
||||||
Eishin-Flash
|
|
||||||
Curren-Chan
|
|
||||||
Kawakami-Princess
|
|
||||||
Gold-City
|
|
||||||
Sakura-Bakushin-O
|
|
||||||
Super-Creek
|
|
||||||
Smart-Falcon
|
|
||||||
Tosen-Jordan
|
|
||||||
Narita-Taishin
|
|
||||||
Haru-Urara
|
|
||||||
Matikanefukukitaru
|
|
||||||
Meisho-Doto
|
|
||||||
Mejiro-Dober
|
|
||||||
Nice-Nature
|
|
||||||
King-Halo
|
|
||||||
|
|
||||||
// Automatically generated.
|
// Get the individual affinity for a legacy.
|
||||||
// Fip comparison of the `character` type.
|
// Any invalid ID is treated as giving 0.
|
||||||
pub fun character/order2(this : character, other : character) : order2<character>
|
pub fun parent-affinity(
|
||||||
match (this, other)
|
trainee: uma-id,
|
||||||
(Special-Week, Special-Week) -> Eq2(Special-Week)
|
legacy: legacy,
|
||||||
(Special-Week, other') -> Lt2(Special-Week, other')
|
other-parent: uma-id,
|
||||||
(this', Special-Week) -> Gt2(Special-Week, this')
|
?character-id: (uma-id) -> character-id,
|
||||||
(Silence-Suzuka, Silence-Suzuka) -> Eq2(Silence-Suzuka)
|
?saddle-bonus: (list<saddle-id>) -> int,
|
||||||
(Silence-Suzuka, other') -> Lt2(Silence-Suzuka, other')
|
?pair-affinity: (a: character-id, b: character-id) -> int,
|
||||||
(this', Silence-Suzuka) -> Gt2(Silence-Suzuka, this')
|
?trio-affinity: (a: character-id, b: character-id, c: character-id) -> int
|
||||||
(Tokai-Teio, Tokai-Teio) -> Eq2(Tokai-Teio)
|
): int
|
||||||
(Tokai-Teio, other') -> Lt2(Tokai-Teio, other')
|
val t = trainee.character-id
|
||||||
(this', Tokai-Teio) -> Gt2(Tokai-Teio, this')
|
val p1 = legacy.uma.uma.character-id
|
||||||
(Maruzensky, Maruzensky) -> Eq2(Maruzensky)
|
val s1 = legacy.sub1.uma.character-id
|
||||||
(Maruzensky, other') -> Lt2(Maruzensky, other')
|
val s2 = legacy.sub2.uma.character-id
|
||||||
(this', Maruzensky) -> Gt2(Maruzensky, this')
|
val p2 = other-parent.character-id
|
||||||
(Fuji-Kiseki, Fuji-Kiseki) -> Eq2(Fuji-Kiseki)
|
pair-affinity(t, p1) + pair-affinity(p1, p2)
|
||||||
(Fuji-Kiseki, other') -> Lt2(Fuji-Kiseki, other')
|
+ trio-affinity(t, p1, s1) + trio-affinity(t, p1, s2)
|
||||||
(this', Fuji-Kiseki) -> Gt2(Fuji-Kiseki, this')
|
+ saddle-bonus(shared-saddles(legacy.uma.saddles, legacy.sub1.saddles)) + saddle-bonus(shared-saddles(legacy.uma.saddles, legacy.sub2.saddles))
|
||||||
(Oguri-Cap, Oguri-Cap) -> Eq2(Oguri-Cap)
|
|
||||||
(Oguri-Cap, other') -> Lt2(Oguri-Cap, other')
|
|
||||||
(this', Oguri-Cap) -> Gt2(Oguri-Cap, this')
|
|
||||||
(Gold-Ship, Gold-Ship) -> Eq2(Gold-Ship)
|
|
||||||
(Gold-Ship, other') -> Lt2(Gold-Ship, other')
|
|
||||||
(this', Gold-Ship) -> Gt2(Gold-Ship, this')
|
|
||||||
(Vodka, Vodka) -> Eq2(Vodka)
|
|
||||||
(Vodka, other') -> Lt2(Vodka, other')
|
|
||||||
(this', Vodka) -> Gt2(Vodka, this')
|
|
||||||
(Daiwa-Scarlet, Daiwa-Scarlet) -> Eq2(Daiwa-Scarlet)
|
|
||||||
(Daiwa-Scarlet, other') -> Lt2(Daiwa-Scarlet, other')
|
|
||||||
(this', Daiwa-Scarlet) -> Gt2(Daiwa-Scarlet, this')
|
|
||||||
(Taiki-Shuttle, Taiki-Shuttle) -> Eq2(Taiki-Shuttle)
|
|
||||||
(Taiki-Shuttle, other') -> Lt2(Taiki-Shuttle, other')
|
|
||||||
(this', Taiki-Shuttle) -> Gt2(Taiki-Shuttle, this')
|
|
||||||
(Grass-Wonder, Grass-Wonder) -> Eq2(Grass-Wonder)
|
|
||||||
(Grass-Wonder, other') -> Lt2(Grass-Wonder, other')
|
|
||||||
(this', Grass-Wonder) -> Gt2(Grass-Wonder, this')
|
|
||||||
(Hishi-Amazon, Hishi-Amazon) -> Eq2(Hishi-Amazon)
|
|
||||||
(Hishi-Amazon, other') -> Lt2(Hishi-Amazon, other')
|
|
||||||
(this', Hishi-Amazon) -> Gt2(Hishi-Amazon, this')
|
|
||||||
(Mejiro-McQueen, Mejiro-McQueen) -> Eq2(Mejiro-McQueen)
|
|
||||||
(Mejiro-McQueen, other') -> Lt2(Mejiro-McQueen, other')
|
|
||||||
(this', Mejiro-McQueen) -> Gt2(Mejiro-McQueen, this')
|
|
||||||
(El-Condor-Pasa, El-Condor-Pasa) -> Eq2(El-Condor-Pasa)
|
|
||||||
(El-Condor-Pasa, other') -> Lt2(El-Condor-Pasa, other')
|
|
||||||
(this', El-Condor-Pasa) -> Gt2(El-Condor-Pasa, this')
|
|
||||||
(TM-Opera-O, TM-Opera-O) -> Eq2(TM-Opera-O)
|
|
||||||
(TM-Opera-O, other') -> Lt2(TM-Opera-O, other')
|
|
||||||
(this', TM-Opera-O) -> Gt2(TM-Opera-O, this')
|
|
||||||
(Narita-Brian, Narita-Brian) -> Eq2(Narita-Brian)
|
|
||||||
(Narita-Brian, other') -> Lt2(Narita-Brian, other')
|
|
||||||
(this', Narita-Brian) -> Gt2(Narita-Brian, this')
|
|
||||||
(Symboli-Rudolf, Symboli-Rudolf) -> Eq2(Symboli-Rudolf)
|
|
||||||
(Symboli-Rudolf, other') -> Lt2(Symboli-Rudolf, other')
|
|
||||||
(this', Symboli-Rudolf) -> Gt2(Symboli-Rudolf, this')
|
|
||||||
(Air-Groove, Air-Groove) -> Eq2(Air-Groove)
|
|
||||||
(Air-Groove, other') -> Lt2(Air-Groove, other')
|
|
||||||
(this', Air-Groove) -> Gt2(Air-Groove, this')
|
|
||||||
(Agnes-Digital, Agnes-Digital) -> Eq2(Agnes-Digital)
|
|
||||||
(Agnes-Digital, other') -> Lt2(Agnes-Digital, other')
|
|
||||||
(this', Agnes-Digital) -> Gt2(Agnes-Digital, this')
|
|
||||||
(Seiun-Sky, Seiun-Sky) -> Eq2(Seiun-Sky)
|
|
||||||
(Seiun-Sky, other') -> Lt2(Seiun-Sky, other')
|
|
||||||
(this', Seiun-Sky) -> Gt2(Seiun-Sky, this')
|
|
||||||
(Biwa-Hayahide, Biwa-Hayahide) -> Eq2(Biwa-Hayahide)
|
|
||||||
(Biwa-Hayahide, other') -> Lt2(Biwa-Hayahide, other')
|
|
||||||
(this', Biwa-Hayahide) -> Gt2(Biwa-Hayahide, this')
|
|
||||||
(Mayano-Top-Gun, Mayano-Top-Gun) -> Eq2(Mayano-Top-Gun)
|
|
||||||
(Mayano-Top-Gun, other') -> Lt2(Mayano-Top-Gun, other')
|
|
||||||
(this', Mayano-Top-Gun) -> Gt2(Mayano-Top-Gun, this')
|
|
||||||
(Manhattan-Cafe, Manhattan-Cafe) -> Eq2(Manhattan-Cafe)
|
|
||||||
(Manhattan-Cafe, other') -> Lt2(Manhattan-Cafe, other')
|
|
||||||
(this', Manhattan-Cafe) -> Gt2(Manhattan-Cafe, this')
|
|
||||||
(Mihono-Bourbon, Mihono-Bourbon) -> Eq2(Mihono-Bourbon)
|
|
||||||
(Mihono-Bourbon, other') -> Lt2(Mihono-Bourbon, other')
|
|
||||||
(this', Mihono-Bourbon) -> Gt2(Mihono-Bourbon, this')
|
|
||||||
(Mejiro-Ryan, Mejiro-Ryan) -> Eq2(Mejiro-Ryan)
|
|
||||||
(Mejiro-Ryan, other') -> Lt2(Mejiro-Ryan, other')
|
|
||||||
(this', Mejiro-Ryan) -> Gt2(Mejiro-Ryan, this')
|
|
||||||
(Hishi-Akebono, Hishi-Akebono) -> Eq2(Hishi-Akebono)
|
|
||||||
(Hishi-Akebono, other') -> Lt2(Hishi-Akebono, other')
|
|
||||||
(this', Hishi-Akebono) -> Gt2(Hishi-Akebono, this')
|
|
||||||
(Rice-Shower, Rice-Shower) -> Eq2(Rice-Shower)
|
|
||||||
(Rice-Shower, other') -> Lt2(Rice-Shower, other')
|
|
||||||
(this', Rice-Shower) -> Gt2(Rice-Shower, this')
|
|
||||||
(Agnes-Tachyon, Agnes-Tachyon) -> Eq2(Agnes-Tachyon)
|
|
||||||
(Agnes-Tachyon, other') -> Lt2(Agnes-Tachyon, other')
|
|
||||||
(this', Agnes-Tachyon) -> Gt2(Agnes-Tachyon, this')
|
|
||||||
(Winning-Ticket, Winning-Ticket) -> Eq2(Winning-Ticket)
|
|
||||||
(Winning-Ticket, other') -> Lt2(Winning-Ticket, other')
|
|
||||||
(this', Winning-Ticket) -> Gt2(Winning-Ticket, this')
|
|
||||||
(Eishin-Flash, Eishin-Flash) -> Eq2(Eishin-Flash)
|
|
||||||
(Eishin-Flash, other') -> Lt2(Eishin-Flash, other')
|
|
||||||
(this', Eishin-Flash) -> Gt2(Eishin-Flash, this')
|
|
||||||
(Curren-Chan, Curren-Chan) -> Eq2(Curren-Chan)
|
|
||||||
(Curren-Chan, other') -> Lt2(Curren-Chan, other')
|
|
||||||
(this', Curren-Chan) -> Gt2(Curren-Chan, this')
|
|
||||||
(Kawakami-Princess, Kawakami-Princess) -> Eq2(Kawakami-Princess)
|
|
||||||
(Kawakami-Princess, other') -> Lt2(Kawakami-Princess, other')
|
|
||||||
(this', Kawakami-Princess) -> Gt2(Kawakami-Princess, this')
|
|
||||||
(Gold-City, Gold-City) -> Eq2(Gold-City)
|
|
||||||
(Gold-City, other') -> Lt2(Gold-City, other')
|
|
||||||
(this', Gold-City) -> Gt2(Gold-City, this')
|
|
||||||
(Sakura-Bakushin-O, Sakura-Bakushin-O) -> Eq2(Sakura-Bakushin-O)
|
|
||||||
(Sakura-Bakushin-O, other') -> Lt2(Sakura-Bakushin-O, other')
|
|
||||||
(this', Sakura-Bakushin-O) -> Gt2(Sakura-Bakushin-O, this')
|
|
||||||
(Super-Creek, Super-Creek) -> Eq2(Super-Creek)
|
|
||||||
(Super-Creek, other') -> Lt2(Super-Creek, other')
|
|
||||||
(this', Super-Creek) -> Gt2(Super-Creek, this')
|
|
||||||
(Smart-Falcon, Smart-Falcon) -> Eq2(Smart-Falcon)
|
|
||||||
(Smart-Falcon, other') -> Lt2(Smart-Falcon, other')
|
|
||||||
(this', Smart-Falcon) -> Gt2(Smart-Falcon, this')
|
|
||||||
(Tosen-Jordan, Tosen-Jordan) -> Eq2(Tosen-Jordan)
|
|
||||||
(Tosen-Jordan, other') -> Lt2(Tosen-Jordan, other')
|
|
||||||
(this', Tosen-Jordan) -> Gt2(Tosen-Jordan, this')
|
|
||||||
(Narita-Taishin, Narita-Taishin) -> Eq2(Narita-Taishin)
|
|
||||||
(Narita-Taishin, other') -> Lt2(Narita-Taishin, other')
|
|
||||||
(this', Narita-Taishin) -> Gt2(Narita-Taishin, this')
|
|
||||||
(Haru-Urara, Haru-Urara) -> Eq2(Haru-Urara)
|
|
||||||
(Haru-Urara, other') -> Lt2(Haru-Urara, other')
|
|
||||||
(this', Haru-Urara) -> Gt2(Haru-Urara, this')
|
|
||||||
(Matikanefukukitaru, Matikanefukukitaru) -> Eq2(Matikanefukukitaru)
|
|
||||||
(Matikanefukukitaru, other') -> Lt2(Matikanefukukitaru, other')
|
|
||||||
(this', Matikanefukukitaru) -> Gt2(Matikanefukukitaru, this')
|
|
||||||
(Meisho-Doto, Meisho-Doto) -> Eq2(Meisho-Doto)
|
|
||||||
(Meisho-Doto, other') -> Lt2(Meisho-Doto, other')
|
|
||||||
(this', Meisho-Doto) -> Gt2(Meisho-Doto, this')
|
|
||||||
(Mejiro-Dober, Mejiro-Dober) -> Eq2(Mejiro-Dober)
|
|
||||||
(Mejiro-Dober, other') -> Lt2(Mejiro-Dober, other')
|
|
||||||
(this', Mejiro-Dober) -> Gt2(Mejiro-Dober, this')
|
|
||||||
(Nice-Nature, Nice-Nature) -> Eq2(Nice-Nature)
|
|
||||||
(Nice-Nature, other') -> Lt2(Nice-Nature, other')
|
|
||||||
(this', Nice-Nature) -> Gt2(Nice-Nature, this')
|
|
||||||
(King-Halo, King-Halo) -> Eq2(King-Halo)
|
|
||||||
|
|
||||||
// Automatically generated.
|
// Get the individual affinities for a legacy's sub-legacies.
|
||||||
// Shows a string representation of the `character` type.
|
// The first value is the legacy for the `legacy.sub1` and the second is for
|
||||||
pub fun character/show(this : character) : string
|
// `legacy.sub2`.
|
||||||
match this
|
// Any invalid ID is treated as giving 0.
|
||||||
Special-Week -> "Special Week"
|
pub fun sub-affinity(
|
||||||
Silence-Suzuka -> "Silence Suzuka"
|
trainee: uma-id,
|
||||||
Tokai-Teio -> "Tokai Teio"
|
legacy: legacy,
|
||||||
Maruzensky -> "Maruzensky"
|
?character-id: (uma-id) -> character-id,
|
||||||
Fuji-Kiseki -> "Fuji Kiseki"
|
?saddle-bonus: (list<saddle-id>) -> int,
|
||||||
Oguri-Cap -> "Oguri Cap"
|
?trio-affinity: (a: character-id, b: character-id, c: character-id) -> int
|
||||||
Gold-Ship -> "Gold Ship"
|
): (int, int)
|
||||||
Vodka -> "Vodka"
|
val t = trainee.character-id
|
||||||
Daiwa-Scarlet -> "Daiwa Scarlet"
|
val p = legacy.uma.uma.character-id
|
||||||
Taiki-Shuttle -> "Taiki Shuttle"
|
val s1 = legacy.sub1.uma.character-id
|
||||||
Grass-Wonder -> "Grass Wonder"
|
val s2 = legacy.sub2.uma.character-id
|
||||||
Hishi-Amazon -> "Hishi Amazon"
|
val r1 = trio-affinity(t, p, s1) + saddle-bonus(shared-saddles(legacy.uma.saddles, legacy.sub1.saddles))
|
||||||
Mejiro-McQueen -> "Mejiro McQueen"
|
val r2 = trio-affinity(t, p, s2) + saddle-bonus(shared-saddles(legacy.uma.saddles, legacy.sub2.saddles))
|
||||||
El-Condor-Pasa -> "El Condor Pasa"
|
(r1, r2)
|
||||||
TM-Opera-O -> "TM Opera O"
|
|
||||||
Narita-Brian -> "Narita Brian"
|
// Associate each spark with its actual chance to activate given an individual
|
||||||
Symboli-Rudolf -> "Symboli Rudolf"
|
// affinity value and the possible effects when it does.
|
||||||
Air-Groove -> "Air Groove"
|
pub fun uma/inspiration(l: list<spark-id>, affinity: int, ?spark-type: (spark-id) -> spark-type, ?rarity: (spark-id) -> rarity, ?effects: (spark-id) -> list<list<spark-effect>>): list<(spark-id, decimal, list<list<spark-effect>>)>
|
||||||
Agnes-Digital -> "Agnes Digital"
|
val a = decimal(1 + affinity, -2)
|
||||||
Seiun-Sky -> "Seiun Sky"
|
l.map() fn(id) (id, min(id.base-proc * a, 1.decimal), id.effects)
|
||||||
Biwa-Hayahide -> "Biwa Hayahide"
|
|
||||||
Mayano-Top-Gun -> "Mayano Top Gun"
|
// Get the complete list of effects that may occur in an inspiration event
|
||||||
Manhattan-Cafe -> "Manhattan Cafe"
|
// and the respective probability of activation.
|
||||||
Mihono-Bourbon -> "Mihono Bourbon"
|
// Duplicates, i.e. multiple veterans with the same spark, are preserved.
|
||||||
Mejiro-Ryan -> "Mejiro Ryan"
|
pub fun inspiration(
|
||||||
Hishi-Akebono -> "Hishi Akebono"
|
trainee: uma-id,
|
||||||
Rice-Shower -> "Rice Shower"
|
parent1: legacy,
|
||||||
Agnes-Tachyon -> "Agnes Tachyon"
|
parent2: legacy,
|
||||||
Winning-Ticket -> "Winning Ticket"
|
?character-id: (uma-id) -> character-id,
|
||||||
Eishin-Flash -> "Eishin Flash"
|
?saddle-bonus: (list<saddle-id>) -> int,
|
||||||
Curren-Chan -> "Curren Chan"
|
?pair-affinity: (a: character-id, b: character-id) -> int,
|
||||||
Kawakami-Princess -> "Kawakami Princess"
|
?trio-affinity: (a: character-id, b: character-id, c: character-id) -> int,
|
||||||
Gold-City -> "Gold City"
|
?spark-type: (spark-id) -> spark-type,
|
||||||
Sakura-Bakushin-O -> "Sakura Bakushin O"
|
?rarity: (spark-id) -> rarity,
|
||||||
Super-Creek -> "Super Creek"
|
?effects: (spark-id) -> list<list<spark-effect>>
|
||||||
Smart-Falcon -> "Smart Falcon"
|
): list<(spark-id, decimal, list<list<spark-effect>>)>
|
||||||
Tosen-Jordan -> "Tosen Jordan"
|
val p1a = parent-affinity(trainee, parent1, parent2.uma.uma)
|
||||||
Narita-Taishin -> "Narita Taishin"
|
val p2a = parent-affinity(trainee, parent2, parent1.uma.uma)
|
||||||
Haru-Urara -> "Haru Urara"
|
val (s11a, s12a) = sub-affinity(trainee, parent1)
|
||||||
Matikanefukukitaru -> "Matikanefukukitaru"
|
val (s21a, s22a) = sub-affinity(trainee, parent2)
|
||||||
Meisho-Doto -> "Meisho Doto"
|
[
|
||||||
Mejiro-Dober -> "Mejiro Dober"
|
inspiration(parent1.uma.sparks, p1a),
|
||||||
Nice-Nature -> "Nice Nature"
|
inspiration(parent1.sub1.sparks, s11a),
|
||||||
King-Halo -> "King Halo"
|
inspiration(parent1.sub2.sparks, s12a),
|
||||||
|
inspiration(parent2.uma.sparks, p2a),
|
||||||
|
inspiration(parent2.sub1.sparks, s21a),
|
||||||
|
inspiration(parent2.sub2.sparks, s22a),
|
||||||
|
].concat
|
||||||
|
|
||||||
|
// Reduce a spark effect list to the skill it is able to give.
|
||||||
|
pub fun skills(l: list<list<spark-effect>>): maybe<skill-id>
|
||||||
|
val r: linearSet<skill-id> = l.head(Nil).foldl(linear-set(Nil)) fn(s, eff)
|
||||||
|
match eff
|
||||||
|
Skill-Hint(id, _) -> s + id
|
||||||
|
_ -> s
|
||||||
|
r.list.head
|
||||||
|
|
||||||
|
// Reduce a spark effect list to the aptitude it is able to give.
|
||||||
|
pub fun aptitudes(l: list<list<spark-effect>>): maybe<aptitude>
|
||||||
|
val r: linearSet<aptitude> = l.head(Nil).foldl(linear-set(Nil)) fn(s, eff)
|
||||||
|
match eff
|
||||||
|
Aptitude-Up(apt) -> s + apt
|
||||||
|
_ -> s
|
||||||
|
r.list.head
|
||||||
|
|
||||||
|
// Get the overall chance of each count of sparks, including zero, providing a
|
||||||
|
// given type of effect activating in a single inspiration event.
|
||||||
|
pub fun inspiration-gives(l: list<(spark-id, decimal, list<list<spark-effect>>)>, f: (list<list<spark-effect>>) -> maybe<a>, ?a/(==): (a, a) -> bool): linearMap<a, list<decimal>>
|
||||||
|
val m: linearMap<_, list<decimal>> = l.foldl(LinearMap(Nil)) fn(m, (_, p, eff))
|
||||||
|
match f(eff)
|
||||||
|
Nothing -> m
|
||||||
|
Just(a) -> m.map/update(a, [p]) fn(cur, pp) pp.append(cur)
|
||||||
|
m.map() fn(_, v) poisson-binomial(v)
|
||||||
|
|||||||
134
horse/movement.kk
Normal file
134
horse/movement.kk
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
module horse/movement
|
||||||
|
|
||||||
|
// Surface types.
|
||||||
|
pub type surface
|
||||||
|
Turf
|
||||||
|
Dirt
|
||||||
|
|
||||||
|
// Automatically generated.
|
||||||
|
// Shows a string representation of the `surface` type.
|
||||||
|
pub fun surface/show(this : surface) : e string
|
||||||
|
match this
|
||||||
|
Turf -> "Turf"
|
||||||
|
Dirt -> "Dirt"
|
||||||
|
|
||||||
|
// Race distance types.
|
||||||
|
pub type distance
|
||||||
|
Sprint
|
||||||
|
Mile
|
||||||
|
Medium
|
||||||
|
Long
|
||||||
|
|
||||||
|
// Automatically generated.
|
||||||
|
// Shows a string representation of the `distance` type.
|
||||||
|
pub fun distance/show(this : distance) : e string
|
||||||
|
match this
|
||||||
|
Sprint -> "Sprint"
|
||||||
|
Mile -> "Mile"
|
||||||
|
Medium -> "Medium"
|
||||||
|
Long -> "Long"
|
||||||
|
|
||||||
|
// Running styles.
|
||||||
|
pub type style
|
||||||
|
Front-Runner
|
||||||
|
Pace-Chaser
|
||||||
|
Late-Surger
|
||||||
|
End-Closer
|
||||||
|
|
||||||
|
// Automatically generated.
|
||||||
|
// Equality comparison of the `style` type.
|
||||||
|
pub fun style/(==)(this : style, other : style) : e bool
|
||||||
|
match (this, other)
|
||||||
|
(Front-Runner, Front-Runner) -> True
|
||||||
|
(Pace-Chaser, Pace-Chaser) -> True
|
||||||
|
(Late-Surger, Late-Surger) -> True
|
||||||
|
(End-Closer, End-Closer) -> True
|
||||||
|
(_, _) -> False
|
||||||
|
|
||||||
|
// Shows a string representation of the `style` type.
|
||||||
|
pub fun style/show(this : style) : e string
|
||||||
|
match this
|
||||||
|
Front-Runner -> "Front Runner"
|
||||||
|
Pace-Chaser -> "Pace Chaser"
|
||||||
|
Late-Surger -> "Late Surger"
|
||||||
|
End-Closer -> "End Closer"
|
||||||
|
|
||||||
|
// Aptitude levels.
|
||||||
|
pub type aptitude-level
|
||||||
|
G
|
||||||
|
F
|
||||||
|
E
|
||||||
|
D
|
||||||
|
C
|
||||||
|
B
|
||||||
|
A
|
||||||
|
S
|
||||||
|
|
||||||
|
// Get the integer value for an aptitude level, starting at G -> 1.
|
||||||
|
pub fun aptitude-level/int(l: aptitude-level): int
|
||||||
|
match l
|
||||||
|
G -> 1
|
||||||
|
F -> 2
|
||||||
|
E -> 3
|
||||||
|
D -> 4
|
||||||
|
C -> 5
|
||||||
|
B -> 6
|
||||||
|
A -> 7
|
||||||
|
S -> 8
|
||||||
|
|
||||||
|
// Get the aptitude level corresponding to an integer, starting at 1 -> G.
|
||||||
|
pub fun int/aptitude-level(l: int): maybe<aptitude-level>
|
||||||
|
match l
|
||||||
|
1 -> Just(G)
|
||||||
|
2 -> Just(F)
|
||||||
|
3 -> Just(E)
|
||||||
|
4 -> Just(D)
|
||||||
|
5 -> Just(C)
|
||||||
|
6 -> Just(B)
|
||||||
|
7 -> Just(A)
|
||||||
|
8 -> Just(S)
|
||||||
|
_ -> Nothing
|
||||||
|
|
||||||
|
// Comparison of the `aptitude-level` type.
|
||||||
|
pub fun aptitude-level/cmp(this : aptitude-level, other : aptitude-level) : e order
|
||||||
|
cmp(this.int, other.int)
|
||||||
|
|
||||||
|
// Automatically generated.
|
||||||
|
// Fip comparison of the `aptitude-level` type.
|
||||||
|
pub fun aptitude-level/order2(this : aptitude-level, other : aptitude-level) : order2<aptitude-level>
|
||||||
|
match (this, other)
|
||||||
|
(G, G) -> Eq2(G)
|
||||||
|
(G, other') -> Lt2(G, other')
|
||||||
|
(this', G) -> Gt2(G, this')
|
||||||
|
(F, F) -> Eq2(F)
|
||||||
|
(F, other') -> Lt2(F, other')
|
||||||
|
(this', F) -> Gt2(F, this')
|
||||||
|
(E, E) -> Eq2(E)
|
||||||
|
(E, other') -> Lt2(E, other')
|
||||||
|
(this', E) -> Gt2(E, this')
|
||||||
|
(D, D) -> Eq2(D)
|
||||||
|
(D, other') -> Lt2(D, other')
|
||||||
|
(this', D) -> Gt2(D, this')
|
||||||
|
(C, C) -> Eq2(C)
|
||||||
|
(C, other') -> Lt2(C, other')
|
||||||
|
(this', C) -> Gt2(C, this')
|
||||||
|
(B, B) -> Eq2(B)
|
||||||
|
(B, other') -> Lt2(B, other')
|
||||||
|
(this', B) -> Gt2(B, this')
|
||||||
|
(A, A) -> Eq2(A)
|
||||||
|
(A, other') -> Lt2(A, other')
|
||||||
|
(this', A) -> Gt2(A, this')
|
||||||
|
(S, S) -> Eq2(S)
|
||||||
|
|
||||||
|
// Automatically generated.
|
||||||
|
// Shows a string representation of the `aptitude-level` type.
|
||||||
|
pub fun aptitude-level/show(this : aptitude-level) : string
|
||||||
|
match this
|
||||||
|
G -> "G"
|
||||||
|
F -> "F"
|
||||||
|
E -> "E"
|
||||||
|
D -> "D"
|
||||||
|
C -> "C"
|
||||||
|
B -> "B"
|
||||||
|
A -> "A"
|
||||||
|
S -> "S"
|
||||||
21
horse/prob/dist.kk
Normal file
21
horse/prob/dist.kk
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
module horse/prob/dist
|
||||||
|
|
||||||
|
import std/num/decimal
|
||||||
|
|
||||||
|
tail fun pb-step(pn: list<decimal>, pi: decimal, pmfkm1: decimal, pmf: list<decimal>, next: ctx<list<decimal>>): list<decimal>
|
||||||
|
match pn
|
||||||
|
Nil -> next ++. Nil // final step overall
|
||||||
|
Cons(_, pp) -> match pmf
|
||||||
|
Cons(pmfk, pmf') ->
|
||||||
|
val next' = next ++ ctx Cons(pi * pmfkm1 + (1.decimal - pi) * pmfk, hole)
|
||||||
|
pb-step(pp, pi, pmfk, pmf', next')
|
||||||
|
Nil -> next ++. Cons(pi * pmfkm1, Nil) // last step of this iteration
|
||||||
|
|
||||||
|
// Given `n` different Bernoulli processes with respective probabilities in `pn`,
|
||||||
|
// find the distribution of `k` successes for `k` ranging from 0 to `n` inclusive.
|
||||||
|
// The index in the result list corresponds to `k`.
|
||||||
|
pub fun pmf/poisson-binomial(pn: list<decimal>): list<decimal>
|
||||||
|
pn.foldl([1.decimal]) fn(pmf, pi)
|
||||||
|
match pmf
|
||||||
|
Cons(pmf0, pmf') -> pb-step(pn, pi, pmf0, pmf', ctx Cons((1.decimal - pi) * pmf0, hole))
|
||||||
|
Nil -> impossible("fold started with non-empty pmf but got empty pmf")
|
||||||
158
horse/prob/kfl.kk
Normal file
158
horse/prob/kfl.kk
Normal file
@@ -0,0 +1,158 @@
|
|||||||
|
module horse/prob/kfl
|
||||||
|
|
||||||
|
// kfl is a semiring of probabilities formed by vibes.
|
||||||
|
pub type kfl
|
||||||
|
// Effectively if not literally impossible events.
|
||||||
|
Impossible
|
||||||
|
// Not worth aiming for, but can technically still happen.
|
||||||
|
Probably-Not
|
||||||
|
// You expect it not to happen most of the time, but it might still be worth
|
||||||
|
// trying for it if you're being forced to play to your outs.
|
||||||
|
Doubtful
|
||||||
|
// More likely that it won't happen, but a success isn't surprising.
|
||||||
|
Unlikely
|
||||||
|
// Either it does or it doesn't.
|
||||||
|
Mayhapsibly
|
||||||
|
// Decent chance it doesn't happen, but you still expect it to.
|
||||||
|
Probably
|
||||||
|
// You expect it to happen most of the time, but accept that there will be failures.
|
||||||
|
Most-Likely
|
||||||
|
// Very close to guaranteed, but technically with a small chance to fail.
|
||||||
|
Cry-If-Not
|
||||||
|
// Absolutely guaranteed events.
|
||||||
|
Guaranteed
|
||||||
|
|
||||||
|
// Automatically generated.
|
||||||
|
// Comparison of the `kfl` type.
|
||||||
|
pub fun cmp(this : kfl, other : kfl) : e order
|
||||||
|
match (this, other)
|
||||||
|
(Impossible, Impossible) -> Eq
|
||||||
|
(Impossible, _) -> Lt
|
||||||
|
(_, Impossible) -> Gt
|
||||||
|
(Probably-Not, Probably-Not) -> Eq
|
||||||
|
(Probably-Not, _) -> Lt
|
||||||
|
(_, Probably-Not) -> Gt
|
||||||
|
(Doubtful, Doubtful) -> Eq
|
||||||
|
(Doubtful, _) -> Lt
|
||||||
|
(_, Doubtful) -> Gt
|
||||||
|
(Unlikely, Unlikely) -> Eq
|
||||||
|
(Unlikely, _) -> Lt
|
||||||
|
(_, Unlikely) -> Gt
|
||||||
|
(Mayhapsibly, Mayhapsibly) -> Eq
|
||||||
|
(Mayhapsibly, _) -> Lt
|
||||||
|
(_, Mayhapsibly) -> Gt
|
||||||
|
(Probably, Probably) -> Eq
|
||||||
|
(Probably, _) -> Lt
|
||||||
|
(_, Probably) -> Gt
|
||||||
|
(Most-Likely, Most-Likely) -> Eq
|
||||||
|
(Most-Likely, _) -> Lt
|
||||||
|
(_, Most-Likely) -> Gt
|
||||||
|
(Cry-If-Not, Cry-If-Not) -> Eq
|
||||||
|
(Cry-If-Not, _) -> Lt
|
||||||
|
(_, Cry-If-Not) -> Gt
|
||||||
|
(Guaranteed, Guaranteed) -> Eq
|
||||||
|
|
||||||
|
// Shows a string representation of the `kfl` type.
|
||||||
|
pub fun show(this : kfl) : e string
|
||||||
|
match this
|
||||||
|
Impossible -> "impossible"
|
||||||
|
Probably-Not -> "probably not"
|
||||||
|
Doubtful -> "doubtful"
|
||||||
|
Unlikely -> "unlikely"
|
||||||
|
Mayhapsibly -> "mayhapsibly"
|
||||||
|
Probably -> "probably"
|
||||||
|
Most-Likely -> "most likely"
|
||||||
|
Cry-If-Not -> "cry if not"
|
||||||
|
Guaranteed -> "guaranteed"
|
||||||
|
|
||||||
|
// KFL multiplication, or the probability of cooccurrence of two independent events.
|
||||||
|
pub fun (*)(a: kfl, b: kfl): e kfl
|
||||||
|
val (l, h) = match a.cmp(b) // this operation is commutative
|
||||||
|
Gt -> (b, a)
|
||||||
|
_ -> (a, b)
|
||||||
|
match (l, h)
|
||||||
|
(r, Guaranteed) -> r // factor out Guaranteed cases
|
||||||
|
(Impossible, _) -> Impossible
|
||||||
|
(Probably-Not, _) -> Impossible
|
||||||
|
(r, Cry-If-Not) -> r // factor out further Cry-If-Not cases
|
||||||
|
(Doubtful, Most-Likely) -> Probably-Not
|
||||||
|
(Doubtful, _) -> Impossible
|
||||||
|
(Unlikely, Most-Likely) -> Doubtful
|
||||||
|
(Unlikely, Probably) -> Doubtful
|
||||||
|
(Unlikely, Mayhapsibly) -> Probably-Not
|
||||||
|
(Unlikely, _) -> Probably-Not // (Unlikely, Unlikely) because commutative
|
||||||
|
(Mayhapsibly, Most-Likely) -> Unlikely
|
||||||
|
(Mayhapsibly, Probably) -> Unlikely
|
||||||
|
(Mayhapsibly, _) -> Unlikely
|
||||||
|
(Probably, Most-Likely) -> Mayhapsibly
|
||||||
|
(Probably, _) -> Unlikely
|
||||||
|
(Most-Likely, _) -> Probably
|
||||||
|
// These two are only needed because the type system doesn't understand commutativity.
|
||||||
|
(Cry-If-Not, _) -> Cry-If-Not
|
||||||
|
(Guaranteed, _) -> Guaranteed
|
||||||
|
|
||||||
|
// KFL addition, or the probability of occurrence of at least one of two independent events.
|
||||||
|
pub fun (+)(a: kfl, b: kfl): e kfl
|
||||||
|
val (l, h) = match a.cmp(b) // this operation is commutative
|
||||||
|
Gt -> (b, a)
|
||||||
|
_ -> (a, b)
|
||||||
|
match (l, h)
|
||||||
|
// Cases with _ on the right are (a, a) due to commutativity.
|
||||||
|
// Cases with _ on the left simplify later cases that all absorb to the right.
|
||||||
|
(Guaranteed, _) -> Guaranteed
|
||||||
|
(_, Guaranteed) -> Guaranteed
|
||||||
|
(Cry-If-Not, _) -> Guaranteed
|
||||||
|
(Most-Likely, Cry-If-Not) -> Cry-If-Not
|
||||||
|
(Most-Likely, _) -> Cry-If-Not
|
||||||
|
(_, Cry-If-Not) -> Cry-If-Not
|
||||||
|
(Probably, Most-Likely) -> Cry-If-Not
|
||||||
|
(Probably, _) -> Most-Likely
|
||||||
|
(_, Most-Likely) -> Most-Likely
|
||||||
|
(Mayhapsibly, Probably) -> Most-Likely
|
||||||
|
(Mayhapsibly, _) -> Probably
|
||||||
|
(Unlikely, Probably) -> Most-Likely
|
||||||
|
(Unlikely, Mayhapsibly) -> Probably
|
||||||
|
(Unlikely, _) -> Mayhapsibly
|
||||||
|
(_, Probably) -> Probably
|
||||||
|
(Doubtful, Mayhapsibly) -> Probably
|
||||||
|
(Doubtful, Unlikely) -> Mayhapsibly
|
||||||
|
(Doubtful, _) -> Unlikely
|
||||||
|
(_, Mayhapsibly) -> Mayhapsibly
|
||||||
|
(_, Unlikely) -> Unlikely
|
||||||
|
(Probably-Not, Doubtful) -> Unlikely
|
||||||
|
(Probably-Not, _) -> Probably-Not
|
||||||
|
(_, Doubtful) -> Doubtful
|
||||||
|
(_, Probably-Not) -> Probably-Not
|
||||||
|
(_, Impossible) -> Impossible
|
||||||
|
|
||||||
|
// KFL union, or the probability of occurrence of exactly one of two independent events.
|
||||||
|
pub fun either(a: kfl, b: kfl): e kfl
|
||||||
|
val (l, h) = match a.cmp(b) // this operation is commutative
|
||||||
|
Gt -> (b, a)
|
||||||
|
_ -> (a, b)
|
||||||
|
match (l, h)
|
||||||
|
(Impossible, r) -> r
|
||||||
|
(Probably-Not, Guaranteed) -> Cry-If-Not
|
||||||
|
(Probably-Not, r) -> r
|
||||||
|
(Doubtful, Guaranteed) -> Most-Likely
|
||||||
|
(Doubtful, Cry-If-Not) -> Most-Likely
|
||||||
|
(Doubtful, Most-Likely) -> Probably
|
||||||
|
(Doubtful, Probably) -> Mayhapsibly
|
||||||
|
(Doubtful, Mayhapsibly) -> Mayhapsibly
|
||||||
|
(Doubtful, Unlikely) -> Mayhapsibly
|
||||||
|
(Doubtful, _) -> Unlikely
|
||||||
|
(Unlikely, Guaranteed) -> Probably
|
||||||
|
(Unlikely, Cry-If-Not) -> Mayhapsibly
|
||||||
|
(Unlikely, Most-Likely) -> Mayhapsibly
|
||||||
|
(Unlikely, _) -> Probably
|
||||||
|
(Mayhapsibly, Guaranteed) -> Mayhapsibly
|
||||||
|
(Mayhapsibly, Cry-If-Not) -> Mayhapsibly
|
||||||
|
(Mayhapsibly, Most-Likely) -> Mayhapsibly
|
||||||
|
(Mayhapsibly, _) -> Probably
|
||||||
|
(Probably, Guaranteed) -> Unlikely
|
||||||
|
(Probably, Cry-If-Not) -> Unlikely
|
||||||
|
(Probably, Most-Likely) -> Unlikely
|
||||||
|
(Probably, _) -> Mayhapsibly
|
||||||
|
(Most-Likely, _) -> Doubtful
|
||||||
|
(Cry-If-Not, _) -> Probably-Not
|
||||||
|
(Guaranteed, _) -> Impossible
|
||||||
58
horse/prob/pmf.kk
Normal file
58
horse/prob/pmf.kk
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
module horse/prob/pmf
|
||||||
|
|
||||||
|
import std/core/list
|
||||||
|
|
||||||
|
// Discrete-support probability distribution implemented as a list with the invariant
|
||||||
|
// that support is always given in increasing order.
|
||||||
|
pub type pmf<s, v>
|
||||||
|
Event(s: s, v: v, next: pmf<s, v>)
|
||||||
|
End
|
||||||
|
|
||||||
|
// Add an independent event to the distribution.
|
||||||
|
pub fun add(p: pmf<s, v>, s: s, v: v, ?s/cmp: (a: s, b: s) -> order, ?v/(+): (new: v, old: v) -> e v): e pmf<s, v>
|
||||||
|
match p
|
||||||
|
End -> Event(s, v, End)
|
||||||
|
Event(s', v', next) -> match s.cmp(s')
|
||||||
|
Lt -> Event(s, v, Event(s', v', next))
|
||||||
|
Eq -> Event(s, v + v', next)
|
||||||
|
Gt -> Event(s', v', add(next, s, v))
|
||||||
|
|
||||||
|
// Replace an event in the distribution.
|
||||||
|
pub inline fun set(p: pmf<s, v>, s: s, v: v, ?s/cmp: (a: s, b: s) -> order): e pmf<s, v>
|
||||||
|
p.add(s, v, cmp, fn(new, old) new)
|
||||||
|
|
||||||
|
// Construct a pmf from a list of (support, value) entries.
|
||||||
|
pub fun list/pmf(l: list<(s, v)>, ?s/cmp: (a: s, b: s) -> order, ?v/(+): (new: v, old: v) -> e v): e pmf<s, v>
|
||||||
|
l.foldl(End) fn(p, (s, v)) p.add(s, v)
|
||||||
|
|
||||||
|
// Fold over the entries of the distribution.
|
||||||
|
pub tail fun foldl(p: pmf<s, v>, init: a, f: (a, s, v) -> e a): e a
|
||||||
|
match p
|
||||||
|
End -> init
|
||||||
|
Event(s, v, next) -> foldl(next, f(init, s, v), f)
|
||||||
|
|
||||||
|
// Convert the distribution to a list of entries.
|
||||||
|
pub fun pmf/list(p: pmf<s, v>): list<(s, v)>
|
||||||
|
p.foldl(Nil) fn(l, s, v) Cons((s, v), l)
|
||||||
|
|
||||||
|
// Distribution of cooccurrence of two events described by their distributions.
|
||||||
|
pub fun (*)(a: pmf<s, v>, b: pmf<s, v>, ?s/cmp: (a: s, b: s) -> order, ?v/(*): (a: v, b: v) -> e v): e pmf<s, v>
|
||||||
|
match a
|
||||||
|
End -> End
|
||||||
|
Event(sa, va, nexta) -> match b
|
||||||
|
End -> End
|
||||||
|
Event(sb, vb, nextb) -> match sa.cmp(sb)
|
||||||
|
Lt -> nexta * b
|
||||||
|
Eq -> Event(sa, va * vb, nexta * nextb)
|
||||||
|
Gt -> a * nextb
|
||||||
|
|
||||||
|
// Distribution of occurrence of at least one of two events described by their distributions.
|
||||||
|
pub fun (+)(a: pmf<s, v>, b: pmf<s, v>, ?s/cmp: (a: s, b: s) -> order, ?v/(+): (a: v, b: v) -> e v): e pmf<s, v>
|
||||||
|
match a
|
||||||
|
End -> b
|
||||||
|
Event(sa, va, nexta) -> match b
|
||||||
|
End -> a
|
||||||
|
Event(sb, vb, nextb) -> match sa.cmp(sb)
|
||||||
|
Lt -> Event(sa, va, nexta + b)
|
||||||
|
Eq -> Event(sa, va + vb, nexta + nextb)
|
||||||
|
Gt -> Event(sb, vb, a + nextb)
|
||||||
46
horse/race.go
Normal file
46
horse/race.go
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
package horse
|
||||||
|
|
||||||
|
type RaceID int32
|
||||||
|
|
||||||
|
// Race is the internal data about a race.
|
||||||
|
type Race struct {
|
||||||
|
ID RaceID `json:"race_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Thumbnail int `json:"thumbnail"`
|
||||||
|
// Some careers contain unusual versions of races, e.g. Tenno Sho (Spring)
|
||||||
|
// in Hanshin instead of Kyoto for Narita Taishin and Biwa Hayahide.
|
||||||
|
// For such races, this field holds the normal race ID.
|
||||||
|
Primary RaceID `json:"primary"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SaddleID int32
|
||||||
|
|
||||||
|
// Saddle is the internal data about a race win saddle.
|
||||||
|
type Saddle struct {
|
||||||
|
ID SaddleID `json:"saddle_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Races []RaceID `json:"races"`
|
||||||
|
Type SaddleType `json:"type"`
|
||||||
|
// Saddles that involve alternate races are themselves alternate.
|
||||||
|
// For such saddles, this field holds the normal saddle ID.
|
||||||
|
Primary SaddleID `json:"primary"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SaddleType int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Saddle for multiple race wins, e.g. Classic Triple Crown, Dual Grand Prix, &c.
|
||||||
|
SaddleTypeHonor SaddleType = iota
|
||||||
|
SaddleTypeG3
|
||||||
|
SaddleTypeG2
|
||||||
|
SaddleTypeG1
|
||||||
|
)
|
||||||
|
|
||||||
|
type ScenarioID int8
|
||||||
|
|
||||||
|
// Scenario is metadata about a career scenario.
|
||||||
|
type Scenario struct {
|
||||||
|
ID ScenarioID `json:"scenario_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Title string `json:"title"`
|
||||||
|
}
|
||||||
752
horse/race.kk
752
horse/race.kk
@@ -1,426 +1,30 @@
|
|||||||
module horse/race
|
module horse/race
|
||||||
|
|
||||||
import std/data/linearset
|
import std/data/linearset
|
||||||
|
import horse/game-id
|
||||||
|
|
||||||
// Exhaustive enumeration of graded races that can be run in career.
|
pub struct race-detail
|
||||||
// Races that can be run in multiple years are listed only once.
|
race-id: race-id
|
||||||
pub type career-race
|
name: string
|
||||||
February-Stakes
|
grade: grade
|
||||||
Takamatsunomiya-Kinen
|
thumbnail-id: race-thumbnail-id
|
||||||
Osaka-Hai
|
// Some careers contain unusual versions of races, e.g. Tenno Sho (Spring)
|
||||||
Oka-Sho
|
// in Hanshin instead of Kyoto for Narita Taishin and Biwa Hayahide.
|
||||||
Satsuki-Sho
|
// For such races, this field holds the normal race ID.
|
||||||
Tenno-Sho-Spring
|
primary: race-id
|
||||||
NHK-Mile-Cup
|
|
||||||
Victoria-Mile
|
|
||||||
Japanese-Oaks
|
|
||||||
Japanese-Derby
|
|
||||||
Yasuda-Kinen
|
|
||||||
Takarazuka-Kinen
|
|
||||||
Sprinters-Stakes
|
|
||||||
Shuka-Sho
|
|
||||||
Kikuka-Sho
|
|
||||||
Tenno-Sho-Autumn
|
|
||||||
Queen-Elizabeth-II-Cup
|
|
||||||
Mile-Championship
|
|
||||||
Japan-Cup
|
|
||||||
Champions-Cup
|
|
||||||
Hanshin-Juvenile-Fillies
|
|
||||||
Asahi-Hai-Futurity-Stakes
|
|
||||||
Arima-Kinen
|
|
||||||
Hopeful-Stakes
|
|
||||||
Tokyo-Daishoten
|
|
||||||
JBC-Classic
|
|
||||||
JBC-Sprint
|
|
||||||
JBC-Ladies-Classic
|
|
||||||
Japan-Dirt-Derby
|
|
||||||
Teio-Sho
|
|
||||||
Nikkei-Shinshun-Hai
|
|
||||||
Tokai-Stakes
|
|
||||||
American-Jockey-Club-Cup
|
|
||||||
Kyoto-Kinen
|
|
||||||
Nakayama-Kinen
|
|
||||||
Tulip-Sho
|
|
||||||
Yayoi-Sho
|
|
||||||
Kinko-Sho
|
|
||||||
Fillies-Revue
|
|
||||||
Hanshin-Daishoten
|
|
||||||
Spring-Stakes
|
|
||||||
Nikkei-Sho
|
|
||||||
Hanshin-Umamusume-Stakes
|
|
||||||
New-Zealand-Trophy
|
|
||||||
Yomiuri-Milers-Cup
|
|
||||||
Flora-Stakes
|
|
||||||
Aoba-Sho
|
|
||||||
Kyoto-Shimbun-Hai
|
|
||||||
Keio-Hai-Spring-Cup
|
|
||||||
Meguro-Kinen
|
|
||||||
Sapporo-Kinen
|
|
||||||
Centaur-Stakes
|
|
||||||
Rose-Stakes
|
|
||||||
St-Lite-Kinen
|
|
||||||
Kobe-Shimbun-Hai
|
|
||||||
All-Comers
|
|
||||||
Mainichi-Okan
|
|
||||||
Kyoto-Daishoten
|
|
||||||
Fuchu-Umamusume-Stakes
|
|
||||||
Fuji-Stakes
|
|
||||||
Swan-Stakes
|
|
||||||
Keio-Hai-Junior-Stakes
|
|
||||||
Copa-Republica-Argentina
|
|
||||||
Daily-Hai-Junior-Stakes
|
|
||||||
Stayers-Stakes
|
|
||||||
Hanshin-Cup
|
|
||||||
Kyoto-Kimpai
|
|
||||||
Nakayama-Kimpai
|
|
||||||
Shinzan-Kinen
|
|
||||||
Fairy-Stakes
|
|
||||||
Aichi-Hai
|
|
||||||
Keisei-Hai
|
|
||||||
Silk-Road-Stakes
|
|
||||||
Negishi-Stakes
|
|
||||||
Kisaragi-Sho
|
|
||||||
Tokyo-Shimbun-Hai
|
|
||||||
Queen-Cup
|
|
||||||
Kyodo-News-Hai
|
|
||||||
Kyoto-Umamusume-Stakes
|
|
||||||
Diamond-Stakes
|
|
||||||
Kokura-Daishoten
|
|
||||||
Arlington-Cup
|
|
||||||
Hankyu-Hai
|
|
||||||
Ocean-Stakes
|
|
||||||
Nakayama-Umamusume-Stakes
|
|
||||||
Falcon-Stakes
|
|
||||||
Flower-Cup
|
|
||||||
Mainichi-Hai
|
|
||||||
March-Stakes
|
|
||||||
Lord-Derby-Challenge-Trophy
|
|
||||||
Antares-Stakes
|
|
||||||
Fukushima-Umamusume-Stakes
|
|
||||||
Niigata-Daishoten
|
|
||||||
Heian-Stakes
|
|
||||||
Aoi-Stakes
|
|
||||||
Naruo-Kinen
|
|
||||||
Mermaid-Stakes
|
|
||||||
Epsom-Cup
|
|
||||||
Unicorn-Stakes
|
|
||||||
Hakodate-Sprint-Stakes
|
|
||||||
CBC-Sho
|
|
||||||
Radio-Nikkei-Sho
|
|
||||||
Procyon-Stakes
|
|
||||||
Tanabata-Sho
|
|
||||||
Hakodate-Kinen
|
|
||||||
Chukyo-Kinen
|
|
||||||
Hakodate-Junior-Stakes
|
|
||||||
Ibis-Summer-Dash
|
|
||||||
Queen-Stakes
|
|
||||||
Kokura-Kinen
|
|
||||||
Leopard-Stakes
|
|
||||||
Sekiya-Kinen
|
|
||||||
Elm-Stakes
|
|
||||||
Kitakyushu-Kinen
|
|
||||||
Niigata-Junior-Stakes
|
|
||||||
Keeneland-Cup
|
|
||||||
Sapporo-Junior-Stakes
|
|
||||||
Kokura-Junior-Stakes
|
|
||||||
Niigata-Kinen
|
|
||||||
Shion-Stakes
|
|
||||||
Keisei-Hai-Autumn-Handicap
|
|
||||||
Sirius-Stakes
|
|
||||||
Saudi-Arabia-Royal-Cup
|
|
||||||
Artemis-Stakes
|
|
||||||
Fantasy-Stakes
|
|
||||||
Miyako-Stakes
|
|
||||||
Musashino-Stakes
|
|
||||||
Fukushima-Kinen
|
|
||||||
Tokyo-Sports-Hai-Junior-Stakes
|
|
||||||
Kyoto-Junior-Stakes
|
|
||||||
Keihan-Hai
|
|
||||||
Challenge-Cup
|
|
||||||
Chunichi-Shimbun-Hai
|
|
||||||
Capella-Stakes
|
|
||||||
Turquoise-Stakes
|
|
||||||
|
|
||||||
// Automatically generated.
|
pub fun detail(
|
||||||
// Shows a string representation of the `career-race` type.
|
r: race-id,
|
||||||
pub fun career-race/show(this : career-race) : e string
|
?race/show: (race-id) -> string,
|
||||||
match this
|
?race/grade: (race-id) -> grade,
|
||||||
February-Stakes -> "February Stakes"
|
?race/thumbnail: (race-id) -> race-thumbnail-id,
|
||||||
Takamatsunomiya-Kinen -> "Takamatsunomiya Kinen"
|
?race/primary: (race-id) -> race-id
|
||||||
Osaka-Hai -> "Osaka Hai"
|
): race-detail
|
||||||
Oka-Sho -> "Oka Sho"
|
Race-detail(r, r.show, r.grade, r.thumbnail, r.primary)
|
||||||
Satsuki-Sho -> "Satsuki Sho"
|
|
||||||
Tenno-Sho-Spring -> "Tenno Sho Spring"
|
|
||||||
NHK-Mile-Cup -> "NHK Mile Cup"
|
|
||||||
Victoria-Mile -> "Victoria Mile"
|
|
||||||
Japanese-Oaks -> "Japanese Oaks"
|
|
||||||
Japanese-Derby -> "Japanese Derby"
|
|
||||||
Yasuda-Kinen -> "Yasuda Kinen"
|
|
||||||
Takarazuka-Kinen -> "Takarazuka Kinen"
|
|
||||||
Sprinters-Stakes -> "Sprinters Stakes"
|
|
||||||
Shuka-Sho -> "Shuka Sho"
|
|
||||||
Kikuka-Sho -> "Kikuka Sho"
|
|
||||||
Tenno-Sho-Autumn -> "Tenno Sho Autumn"
|
|
||||||
Queen-Elizabeth-II-Cup -> "Queen Elizabeth II Cup"
|
|
||||||
Mile-Championship -> "Mile Championship"
|
|
||||||
Japan-Cup -> "Japan Cup"
|
|
||||||
Champions-Cup -> "Champions Cup"
|
|
||||||
Hanshin-Juvenile-Fillies -> "Hanshin Juvenile Fillies"
|
|
||||||
Asahi-Hai-Futurity-Stakes -> "Asahi Hai Futurity Stakes"
|
|
||||||
Arima-Kinen -> "Arima Kinen"
|
|
||||||
Hopeful-Stakes -> "Hopeful Stakes"
|
|
||||||
Tokyo-Daishoten -> "Tokyo Daishoten"
|
|
||||||
JBC-Classic -> "JBC Classic"
|
|
||||||
JBC-Sprint -> "JBC Sprint"
|
|
||||||
JBC-Ladies-Classic -> "JBC Ladies Classic"
|
|
||||||
Japan-Dirt-Derby -> "Japan Dirt Derby"
|
|
||||||
Teio-Sho -> "Teio Sho"
|
|
||||||
Nikkei-Shinshun-Hai -> "Nikkei Shinshun Hai"
|
|
||||||
Tokai-Stakes -> "Tokai Stakes"
|
|
||||||
American-Jockey-Club-Cup -> "American Jockey Club Cup"
|
|
||||||
Kyoto-Kinen -> "Kyoto Kinen"
|
|
||||||
Nakayama-Kinen -> "Nakayama Kinen"
|
|
||||||
Tulip-Sho -> "Tulip Sho"
|
|
||||||
Yayoi-Sho -> "Yayoi Sho"
|
|
||||||
Kinko-Sho -> "Kinko Sho"
|
|
||||||
Fillies-Revue -> "Fillies Revue"
|
|
||||||
Hanshin-Daishoten -> "Hanshin Daishoten"
|
|
||||||
Spring-Stakes -> "Spring Stakes"
|
|
||||||
Nikkei-Sho -> "Nikkei Sho"
|
|
||||||
Hanshin-Umamusume-Stakes -> "Hanshin Umamusume Stakes"
|
|
||||||
New-Zealand-Trophy -> "New Zealand Trophy"
|
|
||||||
Yomiuri-Milers-Cup -> "Yomiuri Milers Cup"
|
|
||||||
Flora-Stakes -> "Flora Stakes"
|
|
||||||
Aoba-Sho -> "Aoba Sho"
|
|
||||||
Kyoto-Shimbun-Hai -> "Kyoto Shimbun Hai"
|
|
||||||
Keio-Hai-Spring-Cup -> "Keio Hai Spring Cup"
|
|
||||||
Meguro-Kinen -> "Meguro Kinen"
|
|
||||||
Sapporo-Kinen -> "Sapporo Kinen"
|
|
||||||
Centaur-Stakes -> "Centaur Stakes"
|
|
||||||
Rose-Stakes -> "Rose Stakes"
|
|
||||||
St-Lite-Kinen -> "St Lite Kinen"
|
|
||||||
Kobe-Shimbun-Hai -> "Kobe Shimbun Hai"
|
|
||||||
All-Comers -> "All Comers"
|
|
||||||
Mainichi-Okan -> "Mainichi Okan"
|
|
||||||
Kyoto-Daishoten -> "Kyoto Daishoten"
|
|
||||||
Fuchu-Umamusume-Stakes -> "Fuchu Umamusume Stakes"
|
|
||||||
Fuji-Stakes -> "Fuji Stakes"
|
|
||||||
Swan-Stakes -> "Swan Stakes"
|
|
||||||
Keio-Hai-Junior-Stakes -> "Keio Hai Junior Stakes"
|
|
||||||
Copa-Republica-Argentina -> "Copa Republica Argentina"
|
|
||||||
Daily-Hai-Junior-Stakes -> "Daily Hai Junior Stakes"
|
|
||||||
Stayers-Stakes -> "Stayers Stakes"
|
|
||||||
Hanshin-Cup -> "Hanshin Cup"
|
|
||||||
Kyoto-Kimpai -> "Kyoto Kimpai"
|
|
||||||
Nakayama-Kimpai -> "Nakayama Kimpai"
|
|
||||||
Shinzan-Kinen -> "Shinzan Kinen"
|
|
||||||
Fairy-Stakes -> "Fairy Stakes"
|
|
||||||
Aichi-Hai -> "Aichi Hai"
|
|
||||||
Keisei-Hai -> "Keisei Hai"
|
|
||||||
Silk-Road-Stakes -> "Silk Road Stakes"
|
|
||||||
Negishi-Stakes -> "Negishi Stakes"
|
|
||||||
Kisaragi-Sho -> "Kisaragi Sho"
|
|
||||||
Tokyo-Shimbun-Hai -> "Tokyo Shimbun Hai"
|
|
||||||
Queen-Cup -> "Queen Cup"
|
|
||||||
Kyodo-News-Hai -> "Kyodo News Hai"
|
|
||||||
Kyoto-Umamusume-Stakes -> "Kyoto Umamusume Stakes"
|
|
||||||
Diamond-Stakes -> "Diamond Stakes"
|
|
||||||
Kokura-Daishoten -> "Kokura Daishoten"
|
|
||||||
Arlington-Cup -> "Arlington Cup"
|
|
||||||
Hankyu-Hai -> "Hankyu Hai"
|
|
||||||
Ocean-Stakes -> "Ocean Stakes"
|
|
||||||
Nakayama-Umamusume-Stakes -> "Nakayama Umamusume Stakes"
|
|
||||||
Falcon-Stakes -> "Falcon Stakes"
|
|
||||||
Flower-Cup -> "Flower Cup"
|
|
||||||
Mainichi-Hai -> "Mainichi Hai"
|
|
||||||
March-Stakes -> "March Stakes"
|
|
||||||
Lord-Derby-Challenge-Trophy -> "Lord Derby Challenge Trophy"
|
|
||||||
Antares-Stakes -> "Antares Stakes"
|
|
||||||
Fukushima-Umamusume-Stakes -> "Fukushima Umamusume Stakes"
|
|
||||||
Niigata-Daishoten -> "Niigata Daishoten"
|
|
||||||
Heian-Stakes -> "Heian Stakes"
|
|
||||||
Aoi-Stakes -> "Aoi Stakes"
|
|
||||||
Naruo-Kinen -> "Naruo Kinen"
|
|
||||||
Mermaid-Stakes -> "Mermaid Stakes"
|
|
||||||
Epsom-Cup -> "Epsom Cup"
|
|
||||||
Unicorn-Stakes -> "Unicorn Stakes"
|
|
||||||
Hakodate-Sprint-Stakes -> "Hakodate Sprint Stakes"
|
|
||||||
CBC-Sho -> "CBC Sho"
|
|
||||||
Radio-Nikkei-Sho -> "Radio Nikkei Sho"
|
|
||||||
Procyon-Stakes -> "Procyon Stakes"
|
|
||||||
Tanabata-Sho -> "Tanabata Sho"
|
|
||||||
Hakodate-Kinen -> "Hakodate Kinen"
|
|
||||||
Chukyo-Kinen -> "Chukyo Kinen"
|
|
||||||
Hakodate-Junior-Stakes -> "Hakodate Junior Stakes"
|
|
||||||
Ibis-Summer-Dash -> "Ibis Summer Dash"
|
|
||||||
Queen-Stakes -> "Queen Stakes"
|
|
||||||
Kokura-Kinen -> "Kokura Kinen"
|
|
||||||
Leopard-Stakes -> "Leopard Stakes"
|
|
||||||
Sekiya-Kinen -> "Sekiya Kinen"
|
|
||||||
Elm-Stakes -> "Elm Stakes"
|
|
||||||
Kitakyushu-Kinen -> "Kitakyushu Kinen"
|
|
||||||
Niigata-Junior-Stakes -> "Niigata Junior Stakes"
|
|
||||||
Keeneland-Cup -> "Keeneland Cup"
|
|
||||||
Sapporo-Junior-Stakes -> "Sapporo Junior Stakes"
|
|
||||||
Kokura-Junior-Stakes -> "Kokura Junior Stakes"
|
|
||||||
Niigata-Kinen -> "Niigata Kinen"
|
|
||||||
Shion-Stakes -> "Shion Stakes"
|
|
||||||
Keisei-Hai-Autumn-Handicap -> "Keisei Hai Autumn Handicap"
|
|
||||||
Sirius-Stakes -> "Sirius Stakes"
|
|
||||||
Saudi-Arabia-Royal-Cup -> "Saudi Arabia Royal Cup"
|
|
||||||
Artemis-Stakes -> "Artemis Stakes"
|
|
||||||
Fantasy-Stakes -> "Fantasy Stakes"
|
|
||||||
Miyako-Stakes -> "Miyako Stakes"
|
|
||||||
Musashino-Stakes -> "Musashino Stakes"
|
|
||||||
Fukushima-Kinen -> "Fukushima Kinen"
|
|
||||||
Tokyo-Sports-Hai-Junior-Stakes -> "Tokyo Sports Hai Junior Stakes"
|
|
||||||
Kyoto-Junior-Stakes -> "Kyoto Junior Stakes"
|
|
||||||
Keihan-Hai -> "Keihan Hai"
|
|
||||||
Challenge-Cup -> "Challenge Cup"
|
|
||||||
Chunichi-Shimbun-Hai -> "Chunichi Shimbun Hai"
|
|
||||||
Capella-Stakes -> "Capella Stakes"
|
|
||||||
Turquoise-Stakes -> "Turquoise Stakes"
|
|
||||||
|
|
||||||
// Automatically generated.
|
pub fun race-detail/show(r: race-detail): string
|
||||||
// Equality comparison of the `career-race` type.
|
val Race-detail(Race-id(id), name) = r
|
||||||
pub fun career-race/(==)(this : career-race, other : career-race) : e bool
|
name ++ " (ID " ++ id.show ++ ")"
|
||||||
match (this, other)
|
|
||||||
(February-Stakes, February-Stakes) -> True
|
|
||||||
(Takamatsunomiya-Kinen, Takamatsunomiya-Kinen) -> True
|
|
||||||
(Osaka-Hai, Osaka-Hai) -> True
|
|
||||||
(Oka-Sho, Oka-Sho) -> True
|
|
||||||
(Satsuki-Sho, Satsuki-Sho) -> True
|
|
||||||
(Tenno-Sho-Spring, Tenno-Sho-Spring) -> True
|
|
||||||
(NHK-Mile-Cup, NHK-Mile-Cup) -> True
|
|
||||||
(Victoria-Mile, Victoria-Mile) -> True
|
|
||||||
(Japanese-Oaks, Japanese-Oaks) -> True
|
|
||||||
(Japanese-Derby, Japanese-Derby) -> True
|
|
||||||
(Yasuda-Kinen, Yasuda-Kinen) -> True
|
|
||||||
(Takarazuka-Kinen, Takarazuka-Kinen) -> True
|
|
||||||
(Sprinters-Stakes, Sprinters-Stakes) -> True
|
|
||||||
(Shuka-Sho, Shuka-Sho) -> True
|
|
||||||
(Kikuka-Sho, Kikuka-Sho) -> True
|
|
||||||
(Tenno-Sho-Autumn, Tenno-Sho-Autumn) -> True
|
|
||||||
(Queen-Elizabeth-II-Cup, Queen-Elizabeth-II-Cup) -> True
|
|
||||||
(Mile-Championship, Mile-Championship) -> True
|
|
||||||
(Japan-Cup, Japan-Cup) -> True
|
|
||||||
(Champions-Cup, Champions-Cup) -> True
|
|
||||||
(Hanshin-Juvenile-Fillies, Hanshin-Juvenile-Fillies) -> True
|
|
||||||
(Asahi-Hai-Futurity-Stakes, Asahi-Hai-Futurity-Stakes) -> True
|
|
||||||
(Arima-Kinen, Arima-Kinen) -> True
|
|
||||||
(Hopeful-Stakes, Hopeful-Stakes) -> True
|
|
||||||
(Tokyo-Daishoten, Tokyo-Daishoten) -> True
|
|
||||||
(JBC-Classic, JBC-Classic) -> True
|
|
||||||
(JBC-Sprint, JBC-Sprint) -> True
|
|
||||||
(JBC-Ladies-Classic, JBC-Ladies-Classic) -> True
|
|
||||||
(Japan-Dirt-Derby, Japan-Dirt-Derby) -> True
|
|
||||||
(Teio-Sho, Teio-Sho) -> True
|
|
||||||
(Nikkei-Shinshun-Hai, Nikkei-Shinshun-Hai) -> True
|
|
||||||
(Tokai-Stakes, Tokai-Stakes) -> True
|
|
||||||
(American-Jockey-Club-Cup, American-Jockey-Club-Cup) -> True
|
|
||||||
(Kyoto-Kinen, Kyoto-Kinen) -> True
|
|
||||||
(Nakayama-Kinen, Nakayama-Kinen) -> True
|
|
||||||
(Tulip-Sho, Tulip-Sho) -> True
|
|
||||||
(Yayoi-Sho, Yayoi-Sho) -> True
|
|
||||||
(Kinko-Sho, Kinko-Sho) -> True
|
|
||||||
(Fillies-Revue, Fillies-Revue) -> True
|
|
||||||
(Hanshin-Daishoten, Hanshin-Daishoten) -> True
|
|
||||||
(Spring-Stakes, Spring-Stakes) -> True
|
|
||||||
(Nikkei-Sho, Nikkei-Sho) -> True
|
|
||||||
(Hanshin-Umamusume-Stakes, Hanshin-Umamusume-Stakes) -> True
|
|
||||||
(New-Zealand-Trophy, New-Zealand-Trophy) -> True
|
|
||||||
(Yomiuri-Milers-Cup, Yomiuri-Milers-Cup) -> True
|
|
||||||
(Flora-Stakes, Flora-Stakes) -> True
|
|
||||||
(Aoba-Sho, Aoba-Sho) -> True
|
|
||||||
(Kyoto-Shimbun-Hai, Kyoto-Shimbun-Hai) -> True
|
|
||||||
(Keio-Hai-Spring-Cup, Keio-Hai-Spring-Cup) -> True
|
|
||||||
(Meguro-Kinen, Meguro-Kinen) -> True
|
|
||||||
(Sapporo-Kinen, Sapporo-Kinen) -> True
|
|
||||||
(Centaur-Stakes, Centaur-Stakes) -> True
|
|
||||||
(Rose-Stakes, Rose-Stakes) -> True
|
|
||||||
(St-Lite-Kinen, St-Lite-Kinen) -> True
|
|
||||||
(Kobe-Shimbun-Hai, Kobe-Shimbun-Hai) -> True
|
|
||||||
(All-Comers, All-Comers) -> True
|
|
||||||
(Mainichi-Okan, Mainichi-Okan) -> True
|
|
||||||
(Kyoto-Daishoten, Kyoto-Daishoten) -> True
|
|
||||||
(Fuchu-Umamusume-Stakes, Fuchu-Umamusume-Stakes) -> True
|
|
||||||
(Fuji-Stakes, Fuji-Stakes) -> True
|
|
||||||
(Swan-Stakes, Swan-Stakes) -> True
|
|
||||||
(Keio-Hai-Junior-Stakes, Keio-Hai-Junior-Stakes) -> True
|
|
||||||
(Copa-Republica-Argentina, Copa-Republica-Argentina) -> True
|
|
||||||
(Daily-Hai-Junior-Stakes, Daily-Hai-Junior-Stakes) -> True
|
|
||||||
(Stayers-Stakes, Stayers-Stakes) -> True
|
|
||||||
(Hanshin-Cup, Hanshin-Cup) -> True
|
|
||||||
(Kyoto-Kimpai, Kyoto-Kimpai) -> True
|
|
||||||
(Nakayama-Kimpai, Nakayama-Kimpai) -> True
|
|
||||||
(Shinzan-Kinen, Shinzan-Kinen) -> True
|
|
||||||
(Fairy-Stakes, Fairy-Stakes) -> True
|
|
||||||
(Aichi-Hai, Aichi-Hai) -> True
|
|
||||||
(Keisei-Hai, Keisei-Hai) -> True
|
|
||||||
(Silk-Road-Stakes, Silk-Road-Stakes) -> True
|
|
||||||
(Negishi-Stakes, Negishi-Stakes) -> True
|
|
||||||
(Kisaragi-Sho, Kisaragi-Sho) -> True
|
|
||||||
(Tokyo-Shimbun-Hai, Tokyo-Shimbun-Hai) -> True
|
|
||||||
(Queen-Cup, Queen-Cup) -> True
|
|
||||||
(Kyodo-News-Hai, Kyodo-News-Hai) -> True
|
|
||||||
(Kyoto-Umamusume-Stakes, Kyoto-Umamusume-Stakes) -> True
|
|
||||||
(Diamond-Stakes, Diamond-Stakes) -> True
|
|
||||||
(Kokura-Daishoten, Kokura-Daishoten) -> True
|
|
||||||
(Arlington-Cup, Arlington-Cup) -> True
|
|
||||||
(Hankyu-Hai, Hankyu-Hai) -> True
|
|
||||||
(Ocean-Stakes, Ocean-Stakes) -> True
|
|
||||||
(Nakayama-Umamusume-Stakes, Nakayama-Umamusume-Stakes) -> True
|
|
||||||
(Falcon-Stakes, Falcon-Stakes) -> True
|
|
||||||
(Flower-Cup, Flower-Cup) -> True
|
|
||||||
(Mainichi-Hai, Mainichi-Hai) -> True
|
|
||||||
(March-Stakes, March-Stakes) -> True
|
|
||||||
(Lord-Derby-Challenge-Trophy, Lord-Derby-Challenge-Trophy) -> True
|
|
||||||
(Antares-Stakes, Antares-Stakes) -> True
|
|
||||||
(Fukushima-Umamusume-Stakes, Fukushima-Umamusume-Stakes) -> True
|
|
||||||
(Niigata-Daishoten, Niigata-Daishoten) -> True
|
|
||||||
(Heian-Stakes, Heian-Stakes) -> True
|
|
||||||
(Aoi-Stakes, Aoi-Stakes) -> True
|
|
||||||
(Naruo-Kinen, Naruo-Kinen) -> True
|
|
||||||
(Mermaid-Stakes, Mermaid-Stakes) -> True
|
|
||||||
(Epsom-Cup, Epsom-Cup) -> True
|
|
||||||
(Unicorn-Stakes, Unicorn-Stakes) -> True
|
|
||||||
(Hakodate-Sprint-Stakes, Hakodate-Sprint-Stakes) -> True
|
|
||||||
(CBC-Sho, CBC-Sho) -> True
|
|
||||||
(Radio-Nikkei-Sho, Radio-Nikkei-Sho) -> True
|
|
||||||
(Procyon-Stakes, Procyon-Stakes) -> True
|
|
||||||
(Tanabata-Sho, Tanabata-Sho) -> True
|
|
||||||
(Hakodate-Kinen, Hakodate-Kinen) -> True
|
|
||||||
(Chukyo-Kinen, Chukyo-Kinen) -> True
|
|
||||||
(Hakodate-Junior-Stakes, Hakodate-Junior-Stakes) -> True
|
|
||||||
(Ibis-Summer-Dash, Ibis-Summer-Dash) -> True
|
|
||||||
(Queen-Stakes, Queen-Stakes) -> True
|
|
||||||
(Kokura-Kinen, Kokura-Kinen) -> True
|
|
||||||
(Leopard-Stakes, Leopard-Stakes) -> True
|
|
||||||
(Sekiya-Kinen, Sekiya-Kinen) -> True
|
|
||||||
(Elm-Stakes, Elm-Stakes) -> True
|
|
||||||
(Kitakyushu-Kinen, Kitakyushu-Kinen) -> True
|
|
||||||
(Niigata-Junior-Stakes, Niigata-Junior-Stakes) -> True
|
|
||||||
(Keeneland-Cup, Keeneland-Cup) -> True
|
|
||||||
(Sapporo-Junior-Stakes, Sapporo-Junior-Stakes) -> True
|
|
||||||
(Kokura-Junior-Stakes, Kokura-Junior-Stakes) -> True
|
|
||||||
(Niigata-Kinen, Niigata-Kinen) -> True
|
|
||||||
(Shion-Stakes, Shion-Stakes) -> True
|
|
||||||
(Keisei-Hai-Autumn-Handicap, Keisei-Hai-Autumn-Handicap) -> True
|
|
||||||
(Sirius-Stakes, Sirius-Stakes) -> True
|
|
||||||
(Saudi-Arabia-Royal-Cup, Saudi-Arabia-Royal-Cup) -> True
|
|
||||||
(Artemis-Stakes, Artemis-Stakes) -> True
|
|
||||||
(Fantasy-Stakes, Fantasy-Stakes) -> True
|
|
||||||
(Miyako-Stakes, Miyako-Stakes) -> True
|
|
||||||
(Musashino-Stakes, Musashino-Stakes) -> True
|
|
||||||
(Fukushima-Kinen, Fukushima-Kinen) -> True
|
|
||||||
(Tokyo-Sports-Hai-Junior-Stakes, Tokyo-Sports-Hai-Junior-Stakes) -> True
|
|
||||||
(Kyoto-Junior-Stakes, Kyoto-Junior-Stakes) -> True
|
|
||||||
(Keihan-Hai, Keihan-Hai) -> True
|
|
||||||
(Challenge-Cup, Challenge-Cup) -> True
|
|
||||||
(Chunichi-Shimbun-Hai, Chunichi-Shimbun-Hai) -> True
|
|
||||||
(Capella-Stakes, Capella-Stakes) -> True
|
|
||||||
(Turquoise-Stakes, Turquoise-Stakes) -> True
|
|
||||||
(_, _) -> False
|
|
||||||
|
|
||||||
// Race grades.
|
// Race grades.
|
||||||
pub type grade
|
pub type grade
|
||||||
@@ -431,250 +35,86 @@ pub type grade
|
|||||||
G1
|
G1
|
||||||
EX
|
EX
|
||||||
|
|
||||||
pub fun career-race/grade(r: career-race): grade
|
// Automatically generated.
|
||||||
match r
|
// Comparison of the `grade` type.
|
||||||
February-Stakes -> G1
|
pub fun grade/cmp(this : grade, other : grade) : e order
|
||||||
Takamatsunomiya-Kinen -> G1
|
match (this, other)
|
||||||
Osaka-Hai -> G1
|
(Pre-OP, Pre-OP) -> Eq
|
||||||
Oka-Sho -> G1
|
(Pre-OP, _) -> Lt
|
||||||
Satsuki-Sho -> G1
|
(_, Pre-OP) -> Gt
|
||||||
Tenno-Sho-Spring -> G1
|
(OP, OP) -> Eq
|
||||||
NHK-Mile-Cup -> G1
|
(OP, _) -> Lt
|
||||||
Victoria-Mile -> G1
|
(_, OP) -> Gt
|
||||||
Japanese-Oaks -> G1
|
(G3, G3) -> Eq
|
||||||
Japanese-Derby -> G1
|
(G3, _) -> Lt
|
||||||
Yasuda-Kinen -> G1
|
(_, G3) -> Gt
|
||||||
Takarazuka-Kinen -> G1
|
(G2, G2) -> Eq
|
||||||
Sprinters-Stakes -> G1
|
(G2, _) -> Lt
|
||||||
Shuka-Sho -> G1
|
(_, G2) -> Gt
|
||||||
Kikuka-Sho -> G1
|
(G1, G1) -> Eq
|
||||||
Tenno-Sho-Autumn -> G1
|
(G1, _) -> Lt
|
||||||
Queen-Elizabeth-II-Cup -> G1
|
(_, G1) -> Gt
|
||||||
Mile-Championship -> G1
|
(EX, EX) -> Eq
|
||||||
Japan-Cup -> G1
|
|
||||||
Champions-Cup -> G1
|
|
||||||
Hanshin-Juvenile-Fillies -> G1
|
|
||||||
Asahi-Hai-Futurity-Stakes -> G1
|
|
||||||
Arima-Kinen -> G1
|
|
||||||
Hopeful-Stakes -> G1
|
|
||||||
Tokyo-Daishoten -> G1
|
|
||||||
JBC-Classic -> G1
|
|
||||||
JBC-Sprint -> G1
|
|
||||||
JBC-Ladies-Classic -> G1
|
|
||||||
Japan-Dirt-Derby -> G1
|
|
||||||
Teio-Sho -> G1
|
|
||||||
Nikkei-Shinshun-Hai -> G2
|
|
||||||
Tokai-Stakes -> G2
|
|
||||||
American-Jockey-Club-Cup -> G2
|
|
||||||
Kyoto-Kinen -> G2
|
|
||||||
Nakayama-Kinen -> G2
|
|
||||||
Tulip-Sho -> G2
|
|
||||||
Yayoi-Sho -> G2
|
|
||||||
Kinko-Sho -> G2
|
|
||||||
Fillies-Revue -> G2
|
|
||||||
Hanshin-Daishoten -> G2
|
|
||||||
Spring-Stakes -> G2
|
|
||||||
Nikkei-Sho -> G2
|
|
||||||
Hanshin-Umamusume-Stakes -> G2
|
|
||||||
New-Zealand-Trophy -> G2
|
|
||||||
Yomiuri-Milers-Cup -> G2
|
|
||||||
Flora-Stakes -> G2
|
|
||||||
Aoba-Sho -> G2
|
|
||||||
Kyoto-Shimbun-Hai -> G2
|
|
||||||
Keio-Hai-Spring-Cup -> G2
|
|
||||||
Meguro-Kinen -> G2
|
|
||||||
Sapporo-Kinen -> G2
|
|
||||||
Centaur-Stakes -> G2
|
|
||||||
Rose-Stakes -> G2
|
|
||||||
St-Lite-Kinen -> G2
|
|
||||||
Kobe-Shimbun-Hai -> G2
|
|
||||||
All-Comers -> G2
|
|
||||||
Mainichi-Okan -> G2
|
|
||||||
Kyoto-Daishoten -> G2
|
|
||||||
Fuchu-Umamusume-Stakes -> G2
|
|
||||||
Fuji-Stakes -> G2
|
|
||||||
Swan-Stakes -> G2
|
|
||||||
Keio-Hai-Junior-Stakes -> G2
|
|
||||||
Copa-Republica-Argentina -> G2
|
|
||||||
Daily-Hai-Junior-Stakes -> G2
|
|
||||||
Stayers-Stakes -> G2
|
|
||||||
Hanshin-Cup -> G2
|
|
||||||
Kyoto-Kimpai -> G3
|
|
||||||
Nakayama-Kimpai -> G3
|
|
||||||
Shinzan-Kinen -> G3
|
|
||||||
Fairy-Stakes -> G3
|
|
||||||
Aichi-Hai -> G3
|
|
||||||
Keisei-Hai -> G3
|
|
||||||
Silk-Road-Stakes -> G3
|
|
||||||
Negishi-Stakes -> G3
|
|
||||||
Kisaragi-Sho -> G3
|
|
||||||
Tokyo-Shimbun-Hai -> G3
|
|
||||||
Queen-Cup -> G3
|
|
||||||
Kyodo-News-Hai -> G3
|
|
||||||
Kyoto-Umamusume-Stakes -> G3
|
|
||||||
Diamond-Stakes -> G3
|
|
||||||
Kokura-Daishoten -> G3
|
|
||||||
Arlington-Cup -> G3
|
|
||||||
Hankyu-Hai -> G3
|
|
||||||
Ocean-Stakes -> G3
|
|
||||||
Nakayama-Umamusume-Stakes -> G3
|
|
||||||
Falcon-Stakes -> G3
|
|
||||||
Flower-Cup -> G3
|
|
||||||
Mainichi-Hai -> G3
|
|
||||||
March-Stakes -> G3
|
|
||||||
Lord-Derby-Challenge-Trophy -> G3
|
|
||||||
Antares-Stakes -> G3
|
|
||||||
Fukushima-Umamusume-Stakes -> G3
|
|
||||||
Niigata-Daishoten -> G3
|
|
||||||
Heian-Stakes -> G3
|
|
||||||
Aoi-Stakes -> G3
|
|
||||||
Naruo-Kinen -> G3
|
|
||||||
Mermaid-Stakes -> G3
|
|
||||||
Epsom-Cup -> G3
|
|
||||||
Unicorn-Stakes -> G3
|
|
||||||
Hakodate-Sprint-Stakes -> G3
|
|
||||||
CBC-Sho -> G3
|
|
||||||
Radio-Nikkei-Sho -> G3
|
|
||||||
Procyon-Stakes -> G3
|
|
||||||
Tanabata-Sho -> G3
|
|
||||||
Hakodate-Kinen -> G3
|
|
||||||
Chukyo-Kinen -> G3
|
|
||||||
Hakodate-Junior-Stakes -> G3
|
|
||||||
Ibis-Summer-Dash -> G3
|
|
||||||
Queen-Stakes -> G3
|
|
||||||
Kokura-Kinen -> G3
|
|
||||||
Leopard-Stakes -> G3
|
|
||||||
Sekiya-Kinen -> G3
|
|
||||||
Elm-Stakes -> G3
|
|
||||||
Kitakyushu-Kinen -> G3
|
|
||||||
Niigata-Junior-Stakes -> G3
|
|
||||||
Keeneland-Cup -> G3
|
|
||||||
Sapporo-Junior-Stakes -> G3
|
|
||||||
Kokura-Junior-Stakes -> G3
|
|
||||||
Niigata-Kinen -> G3
|
|
||||||
Shion-Stakes -> G3
|
|
||||||
Keisei-Hai-Autumn-Handicap -> G3
|
|
||||||
Sirius-Stakes -> G3
|
|
||||||
Saudi-Arabia-Royal-Cup -> G3
|
|
||||||
Artemis-Stakes -> G3
|
|
||||||
Fantasy-Stakes -> G3
|
|
||||||
Miyako-Stakes -> G3
|
|
||||||
Musashino-Stakes -> G3
|
|
||||||
Fukushima-Kinen -> G3
|
|
||||||
Tokyo-Sports-Hai-Junior-Stakes -> G3
|
|
||||||
Kyoto-Junior-Stakes -> G3
|
|
||||||
Keihan-Hai -> G3
|
|
||||||
Challenge-Cup -> G3
|
|
||||||
Chunichi-Shimbun-Hai -> G3
|
|
||||||
Capella-Stakes -> G3
|
|
||||||
Turquoise-Stakes -> G3
|
|
||||||
|
|
||||||
pub type title
|
|
||||||
Classic-Triple-Crown
|
|
||||||
Triple-Tiara
|
|
||||||
Senior-Spring-Triple-Crown
|
|
||||||
Senior-Autumn-Triple-Crown
|
|
||||||
Tenno-Sweep
|
|
||||||
Dual-Grand-Prix
|
|
||||||
Dual-Miles
|
|
||||||
Dual-Sprints
|
|
||||||
Dual-Dirts
|
|
||||||
|
|
||||||
// Get the titles that a race contributes to.
|
|
||||||
inline fun career-race/titles(r: career-race): list<title>
|
|
||||||
match r
|
|
||||||
Satsuki-Sho -> [Classic-Triple-Crown]
|
|
||||||
Japanese-Derby -> [Classic-Triple-Crown]
|
|
||||||
Kikuka-Sho -> [Classic-Triple-Crown]
|
|
||||||
Oka-Sho -> [Triple-Tiara]
|
|
||||||
Japanese-Oaks -> [Triple-Tiara]
|
|
||||||
Shuka-Sho -> [Triple-Tiara]
|
|
||||||
Osaka-Hai -> [Senior-Spring-Triple-Crown]
|
|
||||||
Tenno-Sho-Spring -> [Senior-Spring-Triple-Crown, Tenno-Sweep]
|
|
||||||
Takarazuka-Kinen -> [Senior-Spring-Triple-Crown, Dual-Grand-Prix]
|
|
||||||
Tenno-Sho-Autumn -> [Senior-Autumn-Triple-Crown, Tenno-Sweep]
|
|
||||||
Japan-Cup -> [Senior-Autumn-Triple-Crown]
|
|
||||||
Arima-Kinen -> [Senior-Autumn-Triple-Crown, Dual-Grand-Prix]
|
|
||||||
Yasuda-Kinen -> [Dual-Miles]
|
|
||||||
Mile-Championship -> [Dual-Miles]
|
|
||||||
Takamatsunomiya-Kinen -> [Dual-Sprints]
|
|
||||||
Sprinters-Stakes -> [Dual-Sprints]
|
|
||||||
February-Stakes -> [Dual-Dirts]
|
|
||||||
Champions-Cup -> [Dual-Dirts]
|
|
||||||
_ -> []
|
|
||||||
|
|
||||||
// Get the races that a title requires.
|
|
||||||
inline fun title/races(t: title): list<career-race>
|
|
||||||
match t
|
|
||||||
Classic-Triple-Crown -> [Satsuki-Sho, Japanese-Derby, Kikuka-Sho]
|
|
||||||
Triple-Tiara -> [Oka-Sho, Japanese-Oaks, Shuka-Sho]
|
|
||||||
Senior-Spring-Triple-Crown -> [Osaka-Hai, Tenno-Sho-Spring, Takarazuka-Kinen]
|
|
||||||
Senior-Autumn-Triple-Crown -> [Tenno-Sho-Autumn, Japan-Cup, Arima-Kinen]
|
|
||||||
Tenno-Sweep -> [Tenno-Sho-Spring, Tenno-Sho-Autumn]
|
|
||||||
Dual-Grand-Prix -> [Takarazuka-Kinen, Arima-Kinen]
|
|
||||||
Dual-Miles -> [Yasuda-Kinen, Mile-Championship]
|
|
||||||
Dual-Sprints -> [Takamatsunomiya-Kinen, Sprinters-Stakes]
|
|
||||||
Dual-Dirts -> [February-Stakes, Champions-Cup]
|
|
||||||
|
|
||||||
// Get all titles earned by an uma.
|
|
||||||
pub fun career/titles(results: list<race-result>): list<title>
|
|
||||||
val wins = results.flatmap-maybe() fn(r) (if r.place == 1 then Just(r.race) else Nothing)
|
|
||||||
val title-wins = wins.filter(_.titles.is-cons).linear-set
|
|
||||||
val titles = title-wins.list.flatmap(_.titles).linear-set.list
|
|
||||||
titles.filter(_.races.linear-set.is-subset-of(title-wins))
|
|
||||||
|
|
||||||
// Automatically generated.
|
// Automatically generated.
|
||||||
// Equality comparison of the `title` type.
|
// Shows a string representation of the `grade` type.
|
||||||
pub fun title/(==)(this : title, other : title) : e bool
|
pub fun grade/show(this : grade) : e string
|
||||||
|
match this
|
||||||
|
Pre-OP -> "Pre-OP"
|
||||||
|
OP -> "OP"
|
||||||
|
G3 -> "G3"
|
||||||
|
G2 -> "G2"
|
||||||
|
G1 -> "G1"
|
||||||
|
EX -> "EX"
|
||||||
|
|
||||||
|
pub struct saddle-detail
|
||||||
|
saddle-id: saddle-id
|
||||||
|
name: string
|
||||||
|
races: list<race-id>
|
||||||
|
saddle-type: saddle-type
|
||||||
|
// For careers with unusual races, granted saddles also differ.
|
||||||
|
// This field holds the normal saddle's ID for such cases.
|
||||||
|
primary: saddle-id
|
||||||
|
|
||||||
|
pub fun saddle/detail(
|
||||||
|
id: saddle-id,
|
||||||
|
?saddle/show: (saddle-id) -> string,
|
||||||
|
?saddle/races: (saddle-id) -> list<race-id>,
|
||||||
|
?saddle/saddle-type: (saddle-id) -> saddle-type,
|
||||||
|
?saddle/primary: (saddle-id) -> saddle-id
|
||||||
|
): saddle-detail
|
||||||
|
Saddle-detail(id, id.show, id.races, id.saddle-type, id.primary)
|
||||||
|
|
||||||
|
pub fun saddle-detail/show(s: saddle-detail): string
|
||||||
|
val Saddle-detail(Saddle-id(id), name, _, _, Saddle-id(primary)) = s
|
||||||
|
if id == primary then name else name ++ " (Alternate " ++ id.show ++ ")"
|
||||||
|
|
||||||
|
// Types of saddles.
|
||||||
|
pub type saddle-type
|
||||||
|
Honor // multiple race wins: classic triple crown, dual grand prix, &c.
|
||||||
|
G3-Win
|
||||||
|
G2-Win
|
||||||
|
G1-Win
|
||||||
|
|
||||||
|
// Automatically generated.
|
||||||
|
// Shows a string representation of the `saddle-type` type.
|
||||||
|
pub fun saddle-type/show(this : saddle-type) : e string
|
||||||
|
match this
|
||||||
|
Honor -> "Honor"
|
||||||
|
G3-Win -> "G3"
|
||||||
|
G2-Win -> "G2"
|
||||||
|
G1-Win -> "G1"
|
||||||
|
|
||||||
|
// Automatically generated.
|
||||||
|
// Equality comparison of the `saddle-type` type.
|
||||||
|
pub fun saddle-type/(==)(this : saddle-type, other : saddle-type) : e bool
|
||||||
match (this, other)
|
match (this, other)
|
||||||
(Classic-Triple-Crown, Classic-Triple-Crown) -> True
|
(Honor, Honor) -> True
|
||||||
(Triple-Tiara, Triple-Tiara) -> True
|
(G3-Win, G3-Win) -> True
|
||||||
(Senior-Spring-Triple-Crown, Senior-Spring-Triple-Crown) -> True
|
(G2-Win, G2-Win) -> True
|
||||||
(Senior-Autumn-Triple-Crown, Senior-Autumn-Triple-Crown) -> True
|
(G1-Win, G1-Win) -> True
|
||||||
(Tenno-Sweep, Tenno-Sweep) -> True
|
|
||||||
(Dual-Grand-Prix, Dual-Grand-Prix) -> True
|
|
||||||
(Dual-Miles, Dual-Miles) -> True
|
|
||||||
(Dual-Sprints, Dual-Sprints) -> True
|
|
||||||
(Dual-Dirts, Dual-Dirts) -> True
|
|
||||||
(_, _) -> False
|
(_, _) -> False
|
||||||
|
|
||||||
// Automatically generated.
|
|
||||||
// Shows a string representation of the `title` type.
|
|
||||||
pub fun title/show(this : title) : e string
|
|
||||||
match this
|
|
||||||
Classic-Triple-Crown -> "Classic Triple Crown"
|
|
||||||
Triple-Tiara -> "Triple Tiara"
|
|
||||||
Senior-Spring-Triple-Crown -> "Senior Spring Triple Crown"
|
|
||||||
Senior-Autumn-Triple-Crown -> "Senior Autumn Triple Crown"
|
|
||||||
Tenno-Sweep -> "Tenno Sweep"
|
|
||||||
Dual-Grand-Prix -> "Dual Grand Prix"
|
|
||||||
Dual-Miles -> "Dual Miles"
|
|
||||||
Dual-Sprints -> "Dual Sprints"
|
|
||||||
Dual-Dirts -> "Dual Dirts"
|
|
||||||
|
|
||||||
// Graded race that a veteran ran.
|
|
||||||
pub struct race-result
|
|
||||||
race: career-race
|
|
||||||
place: int
|
|
||||||
turn: turn
|
|
||||||
|
|
||||||
// Automatically generated.
|
|
||||||
// Equality comparison of the `race-result` type.
|
|
||||||
pub fun race-result/(==)(this : race-result, other : race-result) : e bool
|
|
||||||
match (this, other)
|
|
||||||
(Race-result(race, place, turn), Race-result(race', place', turn')) -> race == race' && place == place' && turn == turn'
|
|
||||||
|
|
||||||
// Automatically generated.
|
|
||||||
// Shows a string representation of the `race-result` type.
|
|
||||||
pub fun race-result/show(this : race-result) : e string
|
|
||||||
match this
|
|
||||||
Race-result(race, place, turn) -> turn.show ++ " " ++ race.show ++ ": " ++ place.show
|
|
||||||
|
|
||||||
// Determine whether two race results are for the same race.
|
|
||||||
// This differs from (==) which also requires the race to be on the same turn.
|
|
||||||
pub fun race-result/same(a: race-result, b: race-result): bool
|
|
||||||
a.race == b.race
|
|
||||||
|
|
||||||
// Turn that a race occurred.
|
// Turn that a race occurred.
|
||||||
pub struct turn
|
pub struct turn
|
||||||
year: turn-year
|
year: turn-year
|
||||||
|
|||||||
227
horse/skill.go
Normal file
227
horse/skill.go
Normal file
@@ -0,0 +1,227 @@
|
|||||||
|
package horse
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SkillID int32
|
||||||
|
|
||||||
|
type TenThousandths int32
|
||||||
|
|
||||||
|
func (x TenThousandths) String() string {
|
||||||
|
b := make([]byte, 0, 12)
|
||||||
|
if x < 0 {
|
||||||
|
x = -x
|
||||||
|
b = append(b, '-')
|
||||||
|
}
|
||||||
|
b = strconv.AppendInt(b, int64(x/1e4), 10)
|
||||||
|
if x%1e4 != 0 {
|
||||||
|
b = append(b, '.')
|
||||||
|
b = fmt.Appendf(b, "%04d", x%1e4)
|
||||||
|
b = bytes.TrimRight(b, "0")
|
||||||
|
}
|
||||||
|
return string(b)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skill is the internal data about a skill.
|
||||||
|
type Skill struct {
|
||||||
|
ID SkillID `json:"skill_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Group SkillGroupID `json:"group"`
|
||||||
|
Rarity int8 `json:"rarity"`
|
||||||
|
GroupRate int8 `json:"group_rate"`
|
||||||
|
GradeValue int32 `json:"grade_value,omitzero"`
|
||||||
|
WitCheck bool `json:"wit_check"`
|
||||||
|
Activations []Activation `json:"activations"`
|
||||||
|
UniqueOwner string `json:"unique_owner,omitzero"`
|
||||||
|
SPCost int `json:"sp_cost,omitzero"`
|
||||||
|
IconID int `json:"icon_id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Activation is the parameters controlling when a skill activates.
|
||||||
|
type Activation struct {
|
||||||
|
Precondition string `json:"precondition,omitzero"`
|
||||||
|
Condition string `json:"condition"`
|
||||||
|
Duration TenThousandths `json:"duration,omitzero"`
|
||||||
|
DurScale DurScale `json:"dur_scale,omitzero"`
|
||||||
|
Cooldown TenThousandths `json:"cooldown,omitzero"`
|
||||||
|
Abilities []Ability `json:"abilities"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ability is an individual effect applied by a skill.
|
||||||
|
type Ability struct {
|
||||||
|
Type AbilityType `json:"type"`
|
||||||
|
ValueUsage AbilityValueUsage `json:"value_usage"`
|
||||||
|
Value TenThousandths `json:"value"`
|
||||||
|
Target AbilityTarget `json:"target"`
|
||||||
|
TargetValue int32 `json:"target_value"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a Ability) String() string {
|
||||||
|
r := make([]byte, 0, 64)
|
||||||
|
r = append(r, a.Type.String()...)
|
||||||
|
if a.Value != 0 {
|
||||||
|
r = append(r, ' ')
|
||||||
|
if a.Value > 0 {
|
||||||
|
r = append(r, '+')
|
||||||
|
}
|
||||||
|
switch a.Type {
|
||||||
|
case AbilityPassiveSpeed, AbilityPassiveStamina, AbilityPassivePower, AbilityPassiveGuts, AbilityPassiveWit:
|
||||||
|
r = append(r, a.Value.String()...)
|
||||||
|
case AbilityVision:
|
||||||
|
r = append(r, a.Value.String()...)
|
||||||
|
r = append(r, 'm')
|
||||||
|
case AbilityHP:
|
||||||
|
r = append(r, (a.Value * 100).String()...)
|
||||||
|
r = append(r, '%')
|
||||||
|
case AbilityGateDelay:
|
||||||
|
r = append(r, a.Value.String()...)
|
||||||
|
r = append(r, "×"...)
|
||||||
|
case AbilityFrenzy:
|
||||||
|
r = append(r, a.Value.String()...)
|
||||||
|
r = append(r, 's')
|
||||||
|
case AbilityCurrentSpeed, AbilityTargetSpeed, AbilityLaneSpeed:
|
||||||
|
r = append(r, a.Value.String()...)
|
||||||
|
r = append(r, " m/s"...)
|
||||||
|
case AbilityAccel:
|
||||||
|
r = append(r, a.Value.String()...)
|
||||||
|
r = append(r, " m/s²"...)
|
||||||
|
case AbilityLaneChange:
|
||||||
|
r = append(r, a.Value.String()...)
|
||||||
|
r = append(r, " track widths"...)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
switch a.Target {
|
||||||
|
case TargetSelf:
|
||||||
|
// do nothing
|
||||||
|
case TargetStyle, TargetRushingStyle:
|
||||||
|
// TargetValue is the style to target, not the number of targets.
|
||||||
|
r = append(r, " to "...)
|
||||||
|
r = append(r, a.Target.String()...)
|
||||||
|
switch a.TargetValue {
|
||||||
|
case 1:
|
||||||
|
r = append(r, " Front Runner"...)
|
||||||
|
case 2:
|
||||||
|
r = append(r, " Pace Chaser"...)
|
||||||
|
case 3:
|
||||||
|
r = append(r, " Late Surger"...)
|
||||||
|
case 4:
|
||||||
|
r = append(r, " End Closer"...)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
// For other targeting types, TargetValue is either irrelevant or limit.
|
||||||
|
r = append(r, " to "...)
|
||||||
|
if a.TargetValue > 1 && a.TargetValue < 18 {
|
||||||
|
r = strconv.AppendInt(r, int64(a.TargetValue), 10)
|
||||||
|
r = append(r, ' ')
|
||||||
|
}
|
||||||
|
r = append(r, a.Target.String()...)
|
||||||
|
}
|
||||||
|
if a.ValueUsage != ValueUsageDirect {
|
||||||
|
r = append(r, ' ')
|
||||||
|
r = append(r, a.ValueUsage.String()...)
|
||||||
|
}
|
||||||
|
return string(r)
|
||||||
|
}
|
||||||
|
|
||||||
|
type DurScale int8
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/tools/cmd/stringer@v0.41.0 -type DurScale -trimprefix Duration -linecomment
|
||||||
|
const (
|
||||||
|
DurationDirect DurScale = 1 // directly
|
||||||
|
DurationFrontDistance DurScale = 2 // scaling with distance from the front
|
||||||
|
DurationRemainingHP DurScale = 3 // scaling with remaining HP
|
||||||
|
DurationIncrementPass DurScale = 4 // increasing with each pass while active
|
||||||
|
DurationMidSideBlock DurScale = 5 // scaling with mid-race phase blocked side time
|
||||||
|
DurationRemainingHP2 DurScale = 7 // scaling with remaining HP
|
||||||
|
)
|
||||||
|
|
||||||
|
type AbilityType int8
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/tools/cmd/stringer@v0.41.0 -type AbilityType -trimprefix Ability -linecomment
|
||||||
|
const (
|
||||||
|
AbilityPassiveSpeed AbilityType = 1 // Speed
|
||||||
|
AbilityPassiveStamina AbilityType = 2 // Stamina
|
||||||
|
AbilityPassivePower AbilityType = 3 // Power
|
||||||
|
AbilityPassiveGuts AbilityType = 4 // Guts
|
||||||
|
AbilityPassiveWit AbilityType = 5 // Wit
|
||||||
|
AbilityGreatEscape AbilityType = 6 // Enable Great Escape
|
||||||
|
AbilityVision AbilityType = 8 // Vision
|
||||||
|
AbilityHP AbilityType = 9 // HP
|
||||||
|
AbilityGateDelay AbilityType = 10 // Gate delay multiplier
|
||||||
|
AbilityFrenzy AbilityType = 13 // Frenzy
|
||||||
|
AbilityCurrentSpeed AbilityType = 21 // Current speed
|
||||||
|
AbilityTargetSpeed AbilityType = 27 // Target speed
|
||||||
|
AbilityLaneSpeed AbilityType = 28 // Lane change speed
|
||||||
|
AbilityAccel AbilityType = 31 // Acceleration
|
||||||
|
AbilityLaneChange AbilityType = 35 // Forced lane change
|
||||||
|
)
|
||||||
|
|
||||||
|
type AbilityValueUsage int8
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/tools/cmd/stringer@v0.41.0 -type AbilityValueUsage -trimprefix ValueUsage -linecomment
|
||||||
|
const (
|
||||||
|
ValueUsageDirect AbilityValueUsage = 1 // directly
|
||||||
|
ValueUsageSkillCount AbilityValueUsage = 2 // scaling with the number of skills
|
||||||
|
ValueUsageTeamSpeed AbilityValueUsage = 3 // scaling with team Speed
|
||||||
|
ValueUsageTeamStamina AbilityValueUsage = 4 // scaling with team Stamina
|
||||||
|
ValueUsageTeamPower AbilityValueUsage = 5 // scaling with team Power
|
||||||
|
ValueUsageTeamGuts AbilityValueUsage = 6 // scaling with team Guts
|
||||||
|
ValueUsageTeamWit AbilityValueUsage = 7 // scaling with team Wit
|
||||||
|
ValueUsageRandom AbilityValueUsage = 8 // with a random 0× to 0.04× multiplier
|
||||||
|
ValueUsageRandom2 AbilityValueUsage = 9 // with a random 0× to 0.04× multiplier
|
||||||
|
ValueUsageClimax AbilityValueUsage = 10 // scaling with the number of races won in training
|
||||||
|
ValueUsageMaxStat AbilityValueUsage = 13 // scaling with the highest raw stat
|
||||||
|
ValueUsageGreenCount AbilityValueUsage = 14 // scaling with the number of Passive skills activated
|
||||||
|
ValueUsageDistAdd AbilityValueUsage = 19 // plus extra when far from the lead
|
||||||
|
ValueUsageMidSideBlock AbilityValueUsage = 20 // scaling with mid-race phase blocked side time
|
||||||
|
ValueUsageSpeed AbilityValueUsage = 22 // scaling with overall speed
|
||||||
|
ValueUsageSpeed2 AbilityValueUsage = 23 // scaling with overall speed
|
||||||
|
ValueUsageArcPotential AbilityValueUsage = 24 // scaling with L'Arc global potential
|
||||||
|
ValueUsageMaxLead AbilityValueUsage = 25 // scaling with the longest lead obtained in the first ⅔
|
||||||
|
)
|
||||||
|
|
||||||
|
type AbilityTarget int8
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/tools/cmd/stringer@v0.41.0 -type AbilityTarget -trimprefix Target -linecomment
|
||||||
|
const (
|
||||||
|
TargetSelf AbilityTarget = 1 // self
|
||||||
|
TargetSympathizers AbilityTarget = 2 // others with Sympathy
|
||||||
|
TargetInView AbilityTarget = 4 // others in view
|
||||||
|
TargetFrontmost AbilityTarget = 7 // frontmost
|
||||||
|
TargetAhead AbilityTarget = 9 // others ahead
|
||||||
|
TargetBehind AbilityTarget = 10 // others behind
|
||||||
|
TargetAllTeammates AbilityTarget = 11 // all teammates
|
||||||
|
TargetStyle AbilityTarget = 18 // using style
|
||||||
|
TargetRushingAhead AbilityTarget = 19 // rushing others ahead
|
||||||
|
TargetRushingBehind AbilityTarget = 20 // rushing others behind
|
||||||
|
TargetRushingStyle AbilityTarget = 21 // rushing using style
|
||||||
|
TargetCharacter AbilityTarget = 22 // specific character
|
||||||
|
TargetTriggering AbilityTarget = 23 // whosoever triggered this skill
|
||||||
|
)
|
||||||
|
|
||||||
|
type SkillGroupID int32
|
||||||
|
|
||||||
|
// SkillGroup is a group of skills which are alternate versions of each other.
|
||||||
|
//
|
||||||
|
// Any of the skill IDs in a group may be zero, indicating that there is no
|
||||||
|
// skill with the corresponding group rate.
|
||||||
|
// Some skill groups contain only Skill2 or SkillBad, while others may have all
|
||||||
|
// four skills.
|
||||||
|
type SkillGroup struct {
|
||||||
|
ID SkillGroupID `json:"skill_group"`
|
||||||
|
// Skill1 is the base version of the skill, either a common (white) skill
|
||||||
|
// or an Uma's own unique.
|
||||||
|
Skill1 SkillID `json:"skill1,omitzero"`
|
||||||
|
// Skill2 is the first upgraded version of the skill: a rare (gold)
|
||||||
|
// skill, a double circle skill, or an inherited unique skill.
|
||||||
|
Skill2 SkillID `json:"skill2,omitzero"`
|
||||||
|
// Skill3 is the highest upgraded version, a gold version of a skill with
|
||||||
|
// a double circle version.
|
||||||
|
Skill3 SkillID `json:"skill3,omitzero"`
|
||||||
|
// SkillBad is a negative (purple) skill.
|
||||||
|
SkillBad SkillID `json:"skill_bad,omitzero"`
|
||||||
|
}
|
||||||
249
horse/skill.kk
Normal file
249
horse/skill.kk
Normal file
@@ -0,0 +1,249 @@
|
|||||||
|
module horse/skill
|
||||||
|
|
||||||
|
// This module contains skill-related definitions
|
||||||
|
// common to all versions of the game.
|
||||||
|
|
||||||
|
import std/num/decimal
|
||||||
|
import horse/game-id
|
||||||
|
import horse/movement
|
||||||
|
|
||||||
|
// Full details about a skill.
|
||||||
|
pub struct skill-detail
|
||||||
|
skill-id: skill-id
|
||||||
|
name: string
|
||||||
|
description: string
|
||||||
|
group-id: skill-group-id
|
||||||
|
rarity: rarity
|
||||||
|
group-rate: int
|
||||||
|
grade-value: int
|
||||||
|
wit-check: bool
|
||||||
|
activations: list<activation>
|
||||||
|
owner: maybe<uma-id>
|
||||||
|
sp-cost: int
|
||||||
|
icon-id: skill-icon-id
|
||||||
|
|
||||||
|
pub fun detail(
|
||||||
|
s: skill-id,
|
||||||
|
?skill/show: (skill-id) -> string,
|
||||||
|
?skill/description: (skill-id) -> string,
|
||||||
|
?skill/group: (skill-id) -> skill-group-id,
|
||||||
|
?skill/rarity: (skill-id) -> rarity,
|
||||||
|
?skill/group-rate: (skill-id) -> int,
|
||||||
|
?skill/grade-value: (skill-id) -> int,
|
||||||
|
?skill/wit-check: (skill-id) -> bool,
|
||||||
|
?skill/activations: (skill-id) -> list<activation>,
|
||||||
|
?skill/unique-owner: (skill-id) -> maybe<uma-id>,
|
||||||
|
?skill/sp-cost: (skill-id) -> int,
|
||||||
|
?skill/icon-id: (skill-id) -> skill-icon-id
|
||||||
|
): skill-detail
|
||||||
|
Skill-detail(
|
||||||
|
s,
|
||||||
|
s.show,
|
||||||
|
s.description,
|
||||||
|
s.group,
|
||||||
|
s.rarity,
|
||||||
|
s.group-rate,
|
||||||
|
s.grade-value,
|
||||||
|
s.wit-check,
|
||||||
|
s.activations,
|
||||||
|
s.unique-owner,
|
||||||
|
s.sp-cost,
|
||||||
|
s.icon-id
|
||||||
|
)
|
||||||
|
|
||||||
|
pub fun skill-detail/show(d: skill-detail, ?character/show: (character-id) -> string, ?uma/show: (uma-id) -> string): string
|
||||||
|
val Skill-detail(Skill-id(id), name, desc, _, rarity, _, grade-value, wit-check, activations, owner, sp-cost, _) = d
|
||||||
|
val r = name ++ " (ID " ++ id.show ++ "): " ++ desc ++ " " ++ activations.map(activation/show).join(". ") ++ (if wit-check then ". Wit check. " else ". No wit check. ") ++ rarity.show ++ " costing " ++ sp-cost.show ++ " SP, worth " ++ grade-value.show ++ " grade value."
|
||||||
|
match owner
|
||||||
|
Nothing -> r
|
||||||
|
Just(owner-id) -> match owner-id.show
|
||||||
|
"" -> r ++ " Unique skill of Uma with ID " ++ owner-id.show ++ "."
|
||||||
|
owner-name -> r ++ " Unique skill of " ++ owner-name ++ "."
|
||||||
|
|
||||||
|
// Skill rarity levels.
|
||||||
|
pub type rarity
|
||||||
|
Common // white
|
||||||
|
Rare // gold
|
||||||
|
Unique-Low // 1*/2* unique
|
||||||
|
Unique-Upgraded // 3*+ unique on a trainee upgraded from 1*/2*
|
||||||
|
Unique // base 3* unique
|
||||||
|
|
||||||
|
pub fun rarity/show(r: rarity): string
|
||||||
|
match r
|
||||||
|
Common -> "Common"
|
||||||
|
Rare -> "Rare"
|
||||||
|
Unique-Low -> "Unique (1\u2606/2\u2606)"
|
||||||
|
Unique-Upgraded -> "Unique (3\u2606+ from 1\u2606/2\u2606 upgraded)"
|
||||||
|
Unique -> "Unique (3\u2606+)"
|
||||||
|
|
||||||
|
// Condition and precondition logic.
|
||||||
|
pub alias condition = string
|
||||||
|
|
||||||
|
// Activation conditions and effects.
|
||||||
|
// A skill has one or two activations.
|
||||||
|
pub struct activation
|
||||||
|
precondition: condition
|
||||||
|
condition: condition
|
||||||
|
duration: decimal // seconds
|
||||||
|
dur-scale: dur-scale
|
||||||
|
cooldown: decimal // seconds
|
||||||
|
abilities: list<ability> // one to three elements
|
||||||
|
|
||||||
|
pub fun activation/show(a: activation, ?character/show: (character-id) -> string): string
|
||||||
|
match a
|
||||||
|
Activation("", condition, duration, _, _, abilities) | !duration.is-pos -> condition ++ " -> " ++ abilities.show
|
||||||
|
Activation("", condition, duration, Direct-Dur, cooldown, abilities) | cooldown >= 500.decimal -> condition ++ " -> for " ++ duration.show ++ "s, " ++ abilities.show
|
||||||
|
Activation("", condition, duration, dur-scale, cooldown, abilities) | cooldown >= 500.decimal -> condition ++ " -> for " ++ duration.show ++ "s " ++ dur-scale.show ++ ", " ++ abilities.show
|
||||||
|
Activation("", condition, duration, Direct-Dur, cooldown, abilities) -> condition ++ " -> for " ++ duration.show ++ "s on " ++ cooldown.show ++ "s cooldown, " ++ abilities.show
|
||||||
|
Activation("", condition, duration, dur-scale, cooldown, abilities) -> condition ++ " -> for " ++ duration.show ++ "s " ++ dur-scale.show ++ " on " ++ cooldown.show ++ "s cooldown, " ++ abilities.show
|
||||||
|
Activation(precondition, condition, duration, _, _, abilities) | !duration.is-pos -> precondition ++ " -> " ++ condition ++ " -> " ++ abilities.show
|
||||||
|
Activation(precondition, condition, duration, Direct-Dur, cooldown, abilities) | cooldown >= 500.decimal -> precondition ++ " -> " ++ condition ++ " -> for " ++ duration.show ++ "s, " ++ abilities.show
|
||||||
|
Activation(precondition, condition, duration, dur-scale, cooldown, abilities) | cooldown >= 500.decimal -> precondition ++ " -> " ++ condition ++ " -> for " ++ duration.show ++ "s " ++ dur-scale.show ++ ", " ++ abilities.show
|
||||||
|
Activation(precondition, condition, duration, Direct-Dur, cooldown, abilities) -> precondition ++ " -> " ++ condition ++ " -> for " ++ duration.show ++ "s on " ++ cooldown.show ++ "s cooldown, " ++ abilities.show
|
||||||
|
Activation(precondition, condition, duration, dur-scale, cooldown, abilities) -> precondition ++ " -> " ++ condition ++ " -> for " ++ duration.show ++ "s " ++ dur-scale.show ++ " on " ++ cooldown.show ++ "s cooldown, " ++ abilities.show
|
||||||
|
|
||||||
|
// Special scaling types for skill activation durations.
|
||||||
|
pub type dur-scale
|
||||||
|
Direct-Dur
|
||||||
|
Front-Distance-Dur
|
||||||
|
Multiply-Remaining-HP
|
||||||
|
Increment-Pass
|
||||||
|
Midrace-Side-Block-Time-Dur
|
||||||
|
Multiply-Remaining-HP2
|
||||||
|
|
||||||
|
pub fun dur-scale/show(s: dur-scale): string
|
||||||
|
match s
|
||||||
|
Direct-Dur -> "with no scaling"
|
||||||
|
Front-Distance-Dur -> "scaling with distance from the front"
|
||||||
|
Multiply-Remaining-HP -> "scaling with remaining HP"
|
||||||
|
Increment-Pass -> "increasing with each pass while active"
|
||||||
|
Midrace-Side-Block-Time-Dur -> "scaling with mid-race phase blocked side time"
|
||||||
|
Multiply-Remaining-HP2 -> "scaling with remaining HP"
|
||||||
|
|
||||||
|
// Effects of activating a skill.
|
||||||
|
pub struct ability
|
||||||
|
ability-type: ability-type
|
||||||
|
value-usage: value-usage
|
||||||
|
target: target
|
||||||
|
|
||||||
|
pub fun ability/show(a: ability, ?character/show: (character-id) -> string): string
|
||||||
|
match a
|
||||||
|
Ability(t, Direct, Self) -> t.show
|
||||||
|
Ability(t, Direct, target) -> t.show ++ " " ++ target.show
|
||||||
|
Ability(t, v, Self) -> t.show ++ " " ++ v.show
|
||||||
|
Ability(t, v, target) -> t.show ++ " " ++ target.show ++ " " ++ v.show
|
||||||
|
|
||||||
|
// Skill ability effects.
|
||||||
|
pub type ability-type
|
||||||
|
Passive-Speed(bonus: decimal)
|
||||||
|
Passive-Stamina(bonus: decimal)
|
||||||
|
Passive-Power(bonus: decimal)
|
||||||
|
Passive-Guts(bonus: decimal)
|
||||||
|
Passive-Wit(bonus: decimal)
|
||||||
|
Great-Escape
|
||||||
|
Vision(bonus: decimal)
|
||||||
|
HP(rate: decimal)
|
||||||
|
Gate-Delay(rate: decimal)
|
||||||
|
Frenzy(add: decimal)
|
||||||
|
Current-Speed(add: decimal)
|
||||||
|
Target-Speed(add: decimal)
|
||||||
|
Lane-Speed(add: decimal)
|
||||||
|
Accel(add: decimal)
|
||||||
|
Lane-Change(add: decimal)
|
||||||
|
|
||||||
|
pub fun ability-type/show(a: ability-type): string
|
||||||
|
match a
|
||||||
|
Passive-Speed(bonus) -> bonus.show ++ " Speed"
|
||||||
|
Passive-Stamina(bonus) -> bonus.show ++ " Stamina"
|
||||||
|
Passive-Power(bonus) -> bonus.show ++ " Power"
|
||||||
|
Passive-Guts(bonus) -> bonus.show ++ " Guts"
|
||||||
|
Passive-Wit(bonus) -> bonus.show ++ " Wit"
|
||||||
|
Great-Escape -> "enable Great Escape style"
|
||||||
|
Vision(bonus) -> bonus.show ++ " vision"
|
||||||
|
HP(rate) | rate.is-pos -> show(rate * 100.decimal) ++ "% HP recovery"
|
||||||
|
HP(rate) -> show(rate * 100.decimal) ++ "% HP loss"
|
||||||
|
Gate-Delay(rate) -> rate.show ++ "× gate delay"
|
||||||
|
Frenzy(add) -> add.show ++ "s longer Rushed"
|
||||||
|
Current-Speed(rate) -> rate.show ++ "m/s current speed"
|
||||||
|
Target-Speed(rate) -> rate.show ++ "m/s target speed"
|
||||||
|
Lane-Speed(rate) -> rate.show ++ "m/s lane change speed"
|
||||||
|
Accel(rate) -> rate.show ++ "m/s² acceleration"
|
||||||
|
Lane-Change(rate) -> rate.show ++ " course width movement"
|
||||||
|
|
||||||
|
// Special scaling types for skill abilities.
|
||||||
|
pub type value-usage
|
||||||
|
Direct
|
||||||
|
Team-Speed
|
||||||
|
Team-Stamina
|
||||||
|
Team-Power
|
||||||
|
Team-Guts
|
||||||
|
Team-Wit
|
||||||
|
Multiply-Random
|
||||||
|
Multiply-Random2
|
||||||
|
Climax
|
||||||
|
Max-Stat
|
||||||
|
Passive-Count
|
||||||
|
Front-Distance-Add
|
||||||
|
Midrace-Side-Block-Time
|
||||||
|
Speed-Scaling
|
||||||
|
Speed-Scaling2
|
||||||
|
Arc-Global-Potential
|
||||||
|
Max-Lead-Distance
|
||||||
|
|
||||||
|
pub fun value-usage/show(v: value-usage): string
|
||||||
|
match v
|
||||||
|
Direct -> "with no scaling"
|
||||||
|
Team-Speed -> "scaling with team Speed"
|
||||||
|
Team-Stamina -> "scaling with team Stamina"
|
||||||
|
Team-Power -> "scaling with team Power"
|
||||||
|
Team-Guts -> "scaling with team Guts"
|
||||||
|
Team-Wit -> "scaling with team Wit"
|
||||||
|
Multiply-Random -> "scaling with a random multiplier (0×, 0.02×, or 0.04×)"
|
||||||
|
Multiply-Random2 -> "scaling with a random multiplier (0×, 0.02×, or 0.04×)"
|
||||||
|
Climax -> "scaling with the number of races won during training"
|
||||||
|
Max-Stat -> "scaling with the value of the user's highest stat"
|
||||||
|
Passive-Count -> "scaling with the number of Passive skills activated"
|
||||||
|
Front-Distance-Add -> "scaling with distance from the leader"
|
||||||
|
Midrace-Side-Block-Time -> "scaling with mid-race phase blocked side time"
|
||||||
|
Speed-Scaling -> "scaling with overall speed"
|
||||||
|
Speed-Scaling2 -> "scaling with overall speed"
|
||||||
|
Arc-Global-Potential -> "scaling with L'Arc global potential"
|
||||||
|
Max-Lead-Distance -> "scaling with the distance of the longest lead obtained in the first two thirds of the race"
|
||||||
|
|
||||||
|
// Who a skill ability targets.
|
||||||
|
pub type target
|
||||||
|
Self
|
||||||
|
Sympathizers
|
||||||
|
In-View
|
||||||
|
Frontmost(limit: int)
|
||||||
|
Ahead(limit: int)
|
||||||
|
Behind(limit: int)
|
||||||
|
All-Teammates
|
||||||
|
Style(style: style)
|
||||||
|
Rushing-Ahead(limit: int)
|
||||||
|
Rushing-Behind(limit: int)
|
||||||
|
Rushing-Style(style: style)
|
||||||
|
Specific-Character(who: character-id)
|
||||||
|
Triggering
|
||||||
|
|
||||||
|
pub fun target/show(t: target, ?character/show: (character-id) -> string): string
|
||||||
|
match t
|
||||||
|
Self -> "self"
|
||||||
|
Sympathizers -> "others with Sympathy"
|
||||||
|
In-View -> "others in field of view"
|
||||||
|
Frontmost(limit) -> "frontmost " ++ limit.show
|
||||||
|
Ahead(limit) | limit >= 18 -> "others ahead"
|
||||||
|
Ahead(limit) -> "next " ++ limit.show ++ " others ahead"
|
||||||
|
Behind(limit) | limit >= 18 -> "others behind"
|
||||||
|
Behind(limit) -> "next " ++ limit.show ++ " others behind"
|
||||||
|
All-Teammates -> "all teammates"
|
||||||
|
Style(s) -> "other " ++ s.show ++ "s"
|
||||||
|
Rushing-Ahead(limit) | limit >= 18 -> "others rushing ahead"
|
||||||
|
Rushing-Ahead(limit) -> "next " ++ limit.show ++ " others rushing ahead"
|
||||||
|
Rushing-Behind(limit) | limit >= 18 -> "others rushing behind"
|
||||||
|
Rushing-Behind(limit) -> "next " ++ limit.show ++ " others rushing behind"
|
||||||
|
Rushing-Style(s) -> "rushing " ++ s.show ++ "s"
|
||||||
|
Specific-Character(who) -> match who.show
|
||||||
|
"" -> "character with ID " ++ who.show
|
||||||
|
name -> name
|
||||||
|
Triggering -> "whosoever triggered this skill"
|
||||||
31
horse/skill_test.go
Normal file
31
horse/skill_test.go
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
package horse_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.sunturtle.xyz/zephyr/horse/horse"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestTenThousandthsString(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
cases := []struct {
|
||||||
|
val horse.TenThousandths
|
||||||
|
want string
|
||||||
|
}{
|
||||||
|
{0, "0"},
|
||||||
|
{500, "0.05"},
|
||||||
|
{-500, "-0.05"},
|
||||||
|
{10000, "1"},
|
||||||
|
{-10000, "-1"},
|
||||||
|
{15000, "1.5"},
|
||||||
|
{-15000, "-1.5"},
|
||||||
|
{10001, "1.0001"},
|
||||||
|
{5000000, "500"},
|
||||||
|
}
|
||||||
|
for _, c := range cases {
|
||||||
|
got := c.val.String()
|
||||||
|
if got != c.want {
|
||||||
|
t.Errorf("%d: want %q, got %q", c.val, c.want, got)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
87
horse/spark.go
Normal file
87
horse/spark.go
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
package horse
|
||||||
|
|
||||||
|
type (
|
||||||
|
SparkID int32
|
||||||
|
SparkGroupID int32
|
||||||
|
)
|
||||||
|
|
||||||
|
type Spark struct {
|
||||||
|
ID SparkID `json:"spark_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Description string `json:"description"`
|
||||||
|
Group SparkGroupID `json:"spark_group"`
|
||||||
|
Rarity SparkRarity `json:"rarity"`
|
||||||
|
Type SparkType `json:"type"`
|
||||||
|
Effects [][]SparkEffect `json:"effects"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SparkType int8
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/tools/cmd/stringer@v0.41.0 -type SparkType -trimprefix Spark
|
||||||
|
const (
|
||||||
|
SparkStat SparkType = iota + 1
|
||||||
|
SparkAptitude
|
||||||
|
SparkUnique
|
||||||
|
SparkSkill
|
||||||
|
SparkRace
|
||||||
|
SparkScenario
|
||||||
|
SparkCarnival
|
||||||
|
SparkDistance
|
||||||
|
SparkHidden
|
||||||
|
SparkSurface
|
||||||
|
SparkStyle
|
||||||
|
)
|
||||||
|
|
||||||
|
type SparkRarity int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
OneStar SparkRarity = iota + 1 // ★
|
||||||
|
TwoStar // ★★
|
||||||
|
ThreeStar // ★★★
|
||||||
|
)
|
||||||
|
|
||||||
|
func (r SparkRarity) String() string {
|
||||||
|
const s = "★★★"
|
||||||
|
return s[:int(r)*len("★")]
|
||||||
|
}
|
||||||
|
|
||||||
|
type SparkEffect struct {
|
||||||
|
Target SparkTarget `json:"target"`
|
||||||
|
Value1 int32 `json:"value1,omitzero"`
|
||||||
|
Value2 int32 `json:"value2,omitzero"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type SparkTarget int8
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/tools/cmd/stringer@v0.41.0 -type SparkTarget -trimprefix Spark
|
||||||
|
const (
|
||||||
|
SparkSpeed SparkTarget = iota + 1
|
||||||
|
SparkStam
|
||||||
|
SparkPower
|
||||||
|
SparkGuts
|
||||||
|
SparkWit
|
||||||
|
SparkSkillPoints
|
||||||
|
SparkRandomStat
|
||||||
|
|
||||||
|
SparkTurf SparkTarget = 11
|
||||||
|
SparkDirt SparkTarget = 12
|
||||||
|
|
||||||
|
SparkFrontRunner SparkTarget = iota + 12
|
||||||
|
SparkPaceChaser
|
||||||
|
SparkLateSurger
|
||||||
|
SparkEndCloser
|
||||||
|
|
||||||
|
SparkSprint SparkTarget = iota + 18
|
||||||
|
SparkMile
|
||||||
|
SparkMedium
|
||||||
|
SparkLong
|
||||||
|
|
||||||
|
SparkSkillHint SparkTarget = 41
|
||||||
|
SparkCarnivalBonus SparkTarget = 51
|
||||||
|
|
||||||
|
SparkSpeedCap SparkTarget = iota + 42
|
||||||
|
SparkStamCap
|
||||||
|
SparkPowerCap
|
||||||
|
SparkGutsCap
|
||||||
|
SparkWitCap
|
||||||
|
)
|
||||||
249
horse/spark.kk
249
horse/spark.kk
@@ -1,21 +1,81 @@
|
|||||||
module horse/spark
|
module horse/spark
|
||||||
|
|
||||||
// A single spark.
|
import std/num/decimal
|
||||||
// Parameterized by the spark type: stat, aptitude, unique, race, or skill.
|
import horse/game-id
|
||||||
pub struct spark<a>
|
import horse/movement
|
||||||
kind: a
|
|
||||||
level: level
|
|
||||||
|
|
||||||
pub fun spark/show(spark: spark<a>, level-fancy: string = "*", ?kind: (a) -> string): string
|
// A spark on a veteran.
|
||||||
kind(spark.kind) ++ " " ++ spark.level.show ++ level-fancy
|
pub struct spark-detail
|
||||||
|
spark-id: spark-id
|
||||||
|
typ: spark-type
|
||||||
|
rarity: rarity
|
||||||
|
|
||||||
pub type level
|
pub fun detail(id: spark-id, ?spark/spark-type: (spark-id) -> spark-type, ?spark/rarity: (spark-id) -> rarity): spark-detail
|
||||||
|
Spark-detail(id, id.spark-type, id.rarity)
|
||||||
|
|
||||||
|
pub fun spark-detail/show(s: spark-detail, ?spark/show: (spark-id) -> string): string
|
||||||
|
s.spark-id.show ++ " " ++ "\u2605".repeat(s.rarity.int)
|
||||||
|
|
||||||
|
// The category of a spark; roughly, blue, pink, green, or white, with some
|
||||||
|
// further subdivisions.
|
||||||
|
pub type spark-type
|
||||||
|
Stat // blue
|
||||||
|
Aptitude // red/pink
|
||||||
|
Unique // green
|
||||||
|
Race
|
||||||
|
Skill
|
||||||
|
// skip Carnival Bonus
|
||||||
|
Scenario
|
||||||
|
Surface
|
||||||
|
Distance
|
||||||
|
Style
|
||||||
|
Hidden
|
||||||
|
|
||||||
|
// Spark targets and effects.
|
||||||
|
pub type spark-effect
|
||||||
|
Stat-Up(s: stat, amount: int)
|
||||||
|
SP-Up(amount: int)
|
||||||
|
// skip Carnival Bonus
|
||||||
|
Random-Stat-Up(amount: int)
|
||||||
|
Aptitude-Up(a: aptitude, amount: int)
|
||||||
|
Skill-Hint(s: skill-id, levels: int)
|
||||||
|
Stat-Cap-Up(s: stat, amount: int)
|
||||||
|
|
||||||
|
// Get the base probability for a spark to trigger during a single inheritance.
|
||||||
|
pub fun decimal/base-proc(id: spark-id, ?spark-type: (spark-id) -> spark-type, ?rarity: (spark-id) -> rarity): decimal
|
||||||
|
val t = id.spark-type
|
||||||
|
val r = id.rarity
|
||||||
|
match (t, r)
|
||||||
|
(Stat, One) -> 70.decimal(-2)
|
||||||
|
(Stat, Two) -> 80.decimal(-2)
|
||||||
|
(Stat, Three) -> 90.decimal(-2)
|
||||||
|
(Aptitude, One) -> 1.decimal(-2)
|
||||||
|
(Aptitude, Two) -> 3.decimal(-2)
|
||||||
|
(Aptitude, Three) -> 5.decimal(-2)
|
||||||
|
(Unique, One) -> 5.decimal(-2)
|
||||||
|
(Unique, Two) -> 10.decimal(-2)
|
||||||
|
(Unique, Three) -> 15.decimal(-2)
|
||||||
|
(Race, One) -> 1.decimal(-2)
|
||||||
|
(Race, Two) -> 2.decimal(-2)
|
||||||
|
(Race, Three) -> 3.decimal(-2)
|
||||||
|
(_, One) -> 3.decimal(-2)
|
||||||
|
(_, Two) -> 6.decimal(-2)
|
||||||
|
(_, Three) -> 9.decimal(-2)
|
||||||
|
|
||||||
|
// The level or star count of a spark.
|
||||||
|
pub type rarity
|
||||||
One
|
One
|
||||||
Two
|
Two
|
||||||
Three
|
Three
|
||||||
|
|
||||||
pub fun level/show(this: level): string
|
pub fun rarity/int(l: rarity): int
|
||||||
match this
|
match l
|
||||||
|
One -> 1
|
||||||
|
Two -> 2
|
||||||
|
Three -> 3
|
||||||
|
|
||||||
|
pub fun rarity/show(l: rarity): string
|
||||||
|
match l
|
||||||
One -> "1"
|
One -> "1"
|
||||||
Two -> "2"
|
Two -> "2"
|
||||||
Three -> "3"
|
Three -> "3"
|
||||||
@@ -51,6 +111,55 @@ pub type aptitude
|
|||||||
Late-Surger
|
Late-Surger
|
||||||
End-Closer
|
End-Closer
|
||||||
|
|
||||||
|
// Automatically generated.
|
||||||
|
// Fip comparison of the `aptitude` type.
|
||||||
|
pub fun aptitude/order2(this : aptitude, other : aptitude) : e order2<aptitude>
|
||||||
|
match (this, other)
|
||||||
|
(Turf, Turf) -> Eq2(Turf)
|
||||||
|
(Turf, other') -> Lt2(Turf, other')
|
||||||
|
(this', Turf) -> Gt2(Turf, this')
|
||||||
|
(Dirt, Dirt) -> Eq2(Dirt)
|
||||||
|
(Dirt, other') -> Lt2(Dirt, other')
|
||||||
|
(this', Dirt) -> Gt2(Dirt, this')
|
||||||
|
(Sprint, Sprint) -> Eq2(Sprint)
|
||||||
|
(Sprint, other') -> Lt2(Sprint, other')
|
||||||
|
(this', Sprint) -> Gt2(Sprint, this')
|
||||||
|
(Mile, Mile) -> Eq2(Mile)
|
||||||
|
(Mile, other') -> Lt2(Mile, other')
|
||||||
|
(this', Mile) -> Gt2(Mile, this')
|
||||||
|
(Medium, Medium) -> Eq2(Medium)
|
||||||
|
(Medium, other') -> Lt2(Medium, other')
|
||||||
|
(this', Medium) -> Gt2(Medium, this')
|
||||||
|
(Long, Long) -> Eq2(Long)
|
||||||
|
(Long, other') -> Lt2(Long, other')
|
||||||
|
(this', Long) -> Gt2(Long, this')
|
||||||
|
(Front-Runner, Front-Runner) -> Eq2(Front-Runner)
|
||||||
|
(Front-Runner, other') -> Lt2(Front-Runner, other')
|
||||||
|
(this', Front-Runner) -> Gt2(Front-Runner, this')
|
||||||
|
(Pace-Chaser, Pace-Chaser) -> Eq2(Pace-Chaser)
|
||||||
|
(Pace-Chaser, other') -> Lt2(Pace-Chaser, other')
|
||||||
|
(this', Pace-Chaser) -> Gt2(Pace-Chaser, this')
|
||||||
|
(Late-Surger, Late-Surger) -> Eq2(Late-Surger)
|
||||||
|
(Late-Surger, other') -> Lt2(Late-Surger, other')
|
||||||
|
(this', Late-Surger) -> Gt2(Late-Surger, this')
|
||||||
|
(End-Closer, End-Closer) -> Eq2(End-Closer)
|
||||||
|
|
||||||
|
// Automatically generated.
|
||||||
|
// Equality comparison of the `aptitude` type.
|
||||||
|
pub fun aptitude/(==)(this : aptitude, other : aptitude) : e bool
|
||||||
|
match (this, other)
|
||||||
|
(Turf, Turf) -> True
|
||||||
|
(Dirt, Dirt) -> True
|
||||||
|
(Sprint, Sprint) -> True
|
||||||
|
(Mile, Mile) -> True
|
||||||
|
(Medium, Medium) -> True
|
||||||
|
(Long, Long) -> True
|
||||||
|
(Front-Runner, Front-Runner) -> True
|
||||||
|
(Pace-Chaser, Pace-Chaser) -> True
|
||||||
|
(Late-Surger, Late-Surger) -> True
|
||||||
|
(End-Closer, End-Closer) -> True
|
||||||
|
(_, _) -> False
|
||||||
|
|
||||||
// Shows a string representation of the `aptitude` type.
|
// Shows a string representation of the `aptitude` type.
|
||||||
pub fun aptitude/show(this : aptitude): string
|
pub fun aptitude/show(this : aptitude): string
|
||||||
match this
|
match this
|
||||||
@@ -64,123 +173,3 @@ pub fun aptitude/show(this : aptitude): string
|
|||||||
Pace-Chaser -> "Pace Chaser"
|
Pace-Chaser -> "Pace Chaser"
|
||||||
Late-Surger -> "Late Surger"
|
Late-Surger -> "Late Surger"
|
||||||
End-Closer -> "End Closer"
|
End-Closer -> "End Closer"
|
||||||
|
|
||||||
// Unique (green) spark.
|
|
||||||
// TODO: decide this representation; strings? umas? probably depends on skills generally
|
|
||||||
pub type unique
|
|
||||||
|
|
||||||
pub fun unique/show(this: unique): string
|
|
||||||
"TODO(zeph): unique skills"
|
|
||||||
|
|
||||||
// Race, skill, and scenario (white) sparks.
|
|
||||||
pub type generic
|
|
||||||
February-Stakes
|
|
||||||
Takamatsunomiya-Kinen
|
|
||||||
Osaka-Hai
|
|
||||||
Oka-Sho
|
|
||||||
Satsuki-Sho
|
|
||||||
Tenno-Sho-Spring
|
|
||||||
NHK-Mile-Cup
|
|
||||||
Victoria-Mile
|
|
||||||
Japanese-Oaks
|
|
||||||
Japanese-Derby
|
|
||||||
Yasuda-Kinen
|
|
||||||
Takarazuka-Kinen
|
|
||||||
Sprinters-Stakes
|
|
||||||
Shuka-Sho
|
|
||||||
Kikuka-Sho
|
|
||||||
Tenno-Sho-Autumn
|
|
||||||
Queen-Elizabeth-II-Cup
|
|
||||||
Mile-Championship
|
|
||||||
Japan-Cup
|
|
||||||
Champions-Cup
|
|
||||||
Hanshin-Juvenile-Fillies
|
|
||||||
Asahi-Hai-Futurity-Stakes
|
|
||||||
Arima-Kinen
|
|
||||||
Hopeful-Stakes
|
|
||||||
Tokyo-Daishoten
|
|
||||||
JBC-Classic
|
|
||||||
JBC-Sprint
|
|
||||||
JBC-Ladies-Classic
|
|
||||||
Japan-Dirt-Derby
|
|
||||||
Teio-Sho
|
|
||||||
Skill(skill: string)
|
|
||||||
URA-Finale
|
|
||||||
Unity-Cup
|
|
||||||
|
|
||||||
// Automatically generated.
|
|
||||||
// Equality comparison of the `generic` type.
|
|
||||||
pub fun generic/(==)(this : generic, other : generic) : e bool
|
|
||||||
match (this, other)
|
|
||||||
(February-Stakes, February-Stakes) -> True
|
|
||||||
(Takamatsunomiya-Kinen, Takamatsunomiya-Kinen) -> True
|
|
||||||
(Osaka-Hai, Osaka-Hai) -> True
|
|
||||||
(Oka-Sho, Oka-Sho) -> True
|
|
||||||
(Satsuki-Sho, Satsuki-Sho) -> True
|
|
||||||
(Tenno-Sho-Spring, Tenno-Sho-Spring) -> True
|
|
||||||
(NHK-Mile-Cup, NHK-Mile-Cup) -> True
|
|
||||||
(Victoria-Mile, Victoria-Mile) -> True
|
|
||||||
(Japanese-Oaks, Japanese-Oaks) -> True
|
|
||||||
(Japanese-Derby, Japanese-Derby) -> True
|
|
||||||
(Yasuda-Kinen, Yasuda-Kinen) -> True
|
|
||||||
(Takarazuka-Kinen, Takarazuka-Kinen) -> True
|
|
||||||
(Sprinters-Stakes, Sprinters-Stakes) -> True
|
|
||||||
(Shuka-Sho, Shuka-Sho) -> True
|
|
||||||
(Kikuka-Sho, Kikuka-Sho) -> True
|
|
||||||
(Tenno-Sho-Autumn, Tenno-Sho-Autumn) -> True
|
|
||||||
(Queen-Elizabeth-II-Cup, Queen-Elizabeth-II-Cup) -> True
|
|
||||||
(Mile-Championship, Mile-Championship) -> True
|
|
||||||
(Japan-Cup, Japan-Cup) -> True
|
|
||||||
(Champions-Cup, Champions-Cup) -> True
|
|
||||||
(Hanshin-Juvenile-Fillies, Hanshin-Juvenile-Fillies) -> True
|
|
||||||
(Asahi-Hai-Futurity-Stakes, Asahi-Hai-Futurity-Stakes) -> True
|
|
||||||
(Arima-Kinen, Arima-Kinen) -> True
|
|
||||||
(Hopeful-Stakes, Hopeful-Stakes) -> True
|
|
||||||
(Tokyo-Daishoten, Tokyo-Daishoten) -> True
|
|
||||||
(JBC-Classic, JBC-Classic) -> True
|
|
||||||
(JBC-Sprint, JBC-Sprint) -> True
|
|
||||||
(JBC-Ladies-Classic, JBC-Ladies-Classic) -> True
|
|
||||||
(Japan-Dirt-Derby, Japan-Dirt-Derby) -> True
|
|
||||||
(Teio-Sho, Teio-Sho) -> True
|
|
||||||
(Skill(skill), Skill(skill')) -> skill == skill'
|
|
||||||
(URA-Finale, URA-Finale) -> True
|
|
||||||
(Unity-Cup, Unity-Cup) -> True
|
|
||||||
(_, _) -> False
|
|
||||||
|
|
||||||
// Automatically generated.
|
|
||||||
// Shows a string representation of the `generic` type.
|
|
||||||
pub fun generic/show(this : generic) : e string
|
|
||||||
match this
|
|
||||||
February-Stakes -> "February Stakes"
|
|
||||||
Takamatsunomiya-Kinen -> "Takamatsunomiya Kinen"
|
|
||||||
Osaka-Hai -> "Osaka Hai"
|
|
||||||
Oka-Sho -> "Oka Sho"
|
|
||||||
Satsuki-Sho -> "Satsuki Sho"
|
|
||||||
Tenno-Sho-Spring -> "Tenno Sho Spring"
|
|
||||||
NHK-Mile-Cup -> "NHK Mile Cup"
|
|
||||||
Victoria-Mile -> "Victoria Mile"
|
|
||||||
Japanese-Oaks -> "Japanese Oaks"
|
|
||||||
Japanese-Derby -> "Japanese Derby"
|
|
||||||
Yasuda-Kinen -> "Yasuda Kinen"
|
|
||||||
Takarazuka-Kinen -> "Takarazuka Kinen"
|
|
||||||
Sprinters-Stakes -> "Sprinters Stakes"
|
|
||||||
Shuka-Sho -> "Shuka Sho"
|
|
||||||
Kikuka-Sho -> "Kikuka Sho"
|
|
||||||
Tenno-Sho-Autumn -> "Tenno Sho Autumn"
|
|
||||||
Queen-Elizabeth-II-Cup -> "Queen Elizabeth II Cup"
|
|
||||||
Mile-Championship -> "Mile Championship"
|
|
||||||
Japan-Cup -> "Japan Cup"
|
|
||||||
Champions-Cup -> "Champions Cup"
|
|
||||||
Hanshin-Juvenile-Fillies -> "Hanshin Juvenile Fillies"
|
|
||||||
Asahi-Hai-Futurity-Stakes -> "Asahi Hai Futurity Stakes"
|
|
||||||
Arima-Kinen -> "Arima Kinen"
|
|
||||||
Hopeful-Stakes -> "Hopeful Stakes"
|
|
||||||
Tokyo-Daishoten -> "Tokyo Daishoten"
|
|
||||||
JBC-Classic -> "JBC Classic"
|
|
||||||
JBC-Sprint -> "JBC Sprint"
|
|
||||||
JBC-Ladies-Classic -> "JBC Ladies Classic"
|
|
||||||
Japan-Dirt-Derby -> "Japan Dirt Derby"
|
|
||||||
Teio-Sho -> "Teio Sho"
|
|
||||||
Skill(skill) -> skill.show
|
|
||||||
URA-Finale -> "URA Finale"
|
|
||||||
Unity-Cup -> "Unity Cup"
|
|
||||||
|
|||||||
79
horse/sparktarget_string.go
Normal file
79
horse/sparktarget_string.go
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
// Code generated by "stringer -type SparkTarget -trimprefix Spark"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package horse
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[SparkSpeed-1]
|
||||||
|
_ = x[SparkStam-2]
|
||||||
|
_ = x[SparkPower-3]
|
||||||
|
_ = x[SparkGuts-4]
|
||||||
|
_ = x[SparkWit-5]
|
||||||
|
_ = x[SparkSkillPoints-6]
|
||||||
|
_ = x[SparkRandomStat-7]
|
||||||
|
_ = x[SparkTurf-11]
|
||||||
|
_ = x[SparkDirt-12]
|
||||||
|
_ = x[SparkFrontRunner-21]
|
||||||
|
_ = x[SparkPaceChaser-22]
|
||||||
|
_ = x[SparkLateSurger-23]
|
||||||
|
_ = x[SparkEndCloser-24]
|
||||||
|
_ = x[SparkSprint-31]
|
||||||
|
_ = x[SparkMile-32]
|
||||||
|
_ = x[SparkMedium-33]
|
||||||
|
_ = x[SparkLong-34]
|
||||||
|
_ = x[SparkSkillHint-41]
|
||||||
|
_ = x[SparkCarnivalBonus-51]
|
||||||
|
_ = x[SparkSpeedCap-61]
|
||||||
|
_ = x[SparkStamCap-62]
|
||||||
|
_ = x[SparkPowerCap-63]
|
||||||
|
_ = x[SparkGutsCap-64]
|
||||||
|
_ = x[SparkWitCap-65]
|
||||||
|
}
|
||||||
|
|
||||||
|
const (
|
||||||
|
_SparkTarget_name_0 = "SpeedStamPowerGutsWitSkillPointsRandomStat"
|
||||||
|
_SparkTarget_name_1 = "TurfDirt"
|
||||||
|
_SparkTarget_name_2 = "FrontRunnerPaceChaserLateSurgerEndCloser"
|
||||||
|
_SparkTarget_name_3 = "SprintMileMediumLong"
|
||||||
|
_SparkTarget_name_4 = "SkillHint"
|
||||||
|
_SparkTarget_name_5 = "CarnivalBonus"
|
||||||
|
_SparkTarget_name_6 = "SpeedCapStamCapPowerCapGutsCapWitCap"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
_SparkTarget_index_0 = [...]uint8{0, 5, 9, 14, 18, 21, 32, 42}
|
||||||
|
_SparkTarget_index_1 = [...]uint8{0, 4, 8}
|
||||||
|
_SparkTarget_index_2 = [...]uint8{0, 11, 21, 31, 40}
|
||||||
|
_SparkTarget_index_3 = [...]uint8{0, 6, 10, 16, 20}
|
||||||
|
_SparkTarget_index_6 = [...]uint8{0, 8, 15, 23, 30, 36}
|
||||||
|
)
|
||||||
|
|
||||||
|
func (i SparkTarget) String() string {
|
||||||
|
switch {
|
||||||
|
case 1 <= i && i <= 7:
|
||||||
|
i -= 1
|
||||||
|
return _SparkTarget_name_0[_SparkTarget_index_0[i]:_SparkTarget_index_0[i+1]]
|
||||||
|
case 11 <= i && i <= 12:
|
||||||
|
i -= 11
|
||||||
|
return _SparkTarget_name_1[_SparkTarget_index_1[i]:_SparkTarget_index_1[i+1]]
|
||||||
|
case 21 <= i && i <= 24:
|
||||||
|
i -= 21
|
||||||
|
return _SparkTarget_name_2[_SparkTarget_index_2[i]:_SparkTarget_index_2[i+1]]
|
||||||
|
case 31 <= i && i <= 34:
|
||||||
|
i -= 31
|
||||||
|
return _SparkTarget_name_3[_SparkTarget_index_3[i]:_SparkTarget_index_3[i+1]]
|
||||||
|
case i == 41:
|
||||||
|
return _SparkTarget_name_4
|
||||||
|
case i == 51:
|
||||||
|
return _SparkTarget_name_5
|
||||||
|
case 61 <= i && i <= 65:
|
||||||
|
i -= 61
|
||||||
|
return _SparkTarget_name_6[_SparkTarget_index_6[i]:_SparkTarget_index_6[i+1]]
|
||||||
|
default:
|
||||||
|
return "SparkTarget(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
}
|
||||||
34
horse/sparktype_string.go
Normal file
34
horse/sparktype_string.go
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
// Code generated by "stringer -type SparkType -trimprefix Spark"; DO NOT EDIT.
|
||||||
|
|
||||||
|
package horse
|
||||||
|
|
||||||
|
import "strconv"
|
||||||
|
|
||||||
|
func _() {
|
||||||
|
// An "invalid array index" compiler error signifies that the constant values have changed.
|
||||||
|
// Re-run the stringer command to generate them again.
|
||||||
|
var x [1]struct{}
|
||||||
|
_ = x[SparkStat-1]
|
||||||
|
_ = x[SparkAptitude-2]
|
||||||
|
_ = x[SparkUnique-3]
|
||||||
|
_ = x[SparkSkill-4]
|
||||||
|
_ = x[SparkRace-5]
|
||||||
|
_ = x[SparkScenario-6]
|
||||||
|
_ = x[SparkCarnival-7]
|
||||||
|
_ = x[SparkDistance-8]
|
||||||
|
_ = x[SparkHidden-9]
|
||||||
|
_ = x[SparkSurface-10]
|
||||||
|
_ = x[SparkStyle-11]
|
||||||
|
}
|
||||||
|
|
||||||
|
const _SparkType_name = "StatAptitudeUniqueSkillRaceScenarioCarnivalDistanceHiddenSurfaceStyle"
|
||||||
|
|
||||||
|
var _SparkType_index = [...]uint8{0, 4, 12, 18, 23, 27, 35, 43, 51, 57, 64, 69}
|
||||||
|
|
||||||
|
func (i SparkType) String() string {
|
||||||
|
idx := int(i) - 1
|
||||||
|
if i < 1 || idx >= len(_SparkType_index)-1 {
|
||||||
|
return "SparkType(" + strconv.FormatInt(int64(i), 10) + ")"
|
||||||
|
}
|
||||||
|
return _SparkType_name[_SparkType_index[idx]:_SparkType_index[idx+1]]
|
||||||
|
}
|
||||||
44
horse/uma.go
Normal file
44
horse/uma.go
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
package horse
|
||||||
|
|
||||||
|
type UmaID int32
|
||||||
|
|
||||||
|
type Uma struct {
|
||||||
|
ID UmaID `json:"chara_card_id"`
|
||||||
|
CharacterID CharacterID `json:"chara_id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Variant string `json:"variant"`
|
||||||
|
|
||||||
|
Sprint AptitudeLevel `json:"sprint"`
|
||||||
|
Mile AptitudeLevel `json:"mile"`
|
||||||
|
Medium AptitudeLevel `json:"medium"`
|
||||||
|
Long AptitudeLevel `json:"long"`
|
||||||
|
Front AptitudeLevel `json:"front"`
|
||||||
|
Pace AptitudeLevel `json:"pace"`
|
||||||
|
Late AptitudeLevel `json:"late"`
|
||||||
|
End AptitudeLevel `json:"end"`
|
||||||
|
Turf AptitudeLevel `json:"turf"`
|
||||||
|
Dirt AptitudeLevel `json:"dirt"`
|
||||||
|
|
||||||
|
Unique SkillID `json:"unique"`
|
||||||
|
Skill1 SkillID `json:"skill1"`
|
||||||
|
Skill2 SkillID `json:"skill2"`
|
||||||
|
Skill3 SkillID `json:"skill3"`
|
||||||
|
SkillPL2 SkillID `json:"skill_pl2"`
|
||||||
|
SkillPL3 SkillID `json:"skill_pl3"`
|
||||||
|
SkillPL4 SkillID `json:"skill_pl4"`
|
||||||
|
SkillPL5 SkillID `json:"skill_pl5"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type AptitudeLevel int8
|
||||||
|
|
||||||
|
//go:generate go run golang.org/x/tools/cmd/stringer@v0.41.0 -type AptitudeLevel -trimprefix AptitudeLv
|
||||||
|
const (
|
||||||
|
AptitudeLvG AptitudeLevel = iota + 1
|
||||||
|
AptitudeLvF
|
||||||
|
AptitudeLvE
|
||||||
|
AptitudeLvD
|
||||||
|
AptitudeLvC
|
||||||
|
AptitudeLvB
|
||||||
|
AptitudeLvA
|
||||||
|
AptitudeLvS
|
||||||
|
)
|
||||||
27
horse/uma.kk
Normal file
27
horse/uma.kk
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
module horse/uma
|
||||||
|
|
||||||
|
import horse/game-id
|
||||||
|
import horse/movement
|
||||||
|
|
||||||
|
// Details of an uma, or character card.
|
||||||
|
pub struct uma-detail
|
||||||
|
uma-id: uma-id
|
||||||
|
character-id: character-id
|
||||||
|
sprint: aptitude-level
|
||||||
|
mile: aptitude-level
|
||||||
|
medium: aptitude-level
|
||||||
|
long: aptitude-level
|
||||||
|
front-runner: aptitude-level
|
||||||
|
pace-chaser: aptitude-level
|
||||||
|
late-surger: aptitude-level
|
||||||
|
end-closer: aptitude-level
|
||||||
|
turf: aptitude-level
|
||||||
|
dirt: aptitude-level
|
||||||
|
unique: skill-id
|
||||||
|
skill1: skill-id
|
||||||
|
skill2: skill-id
|
||||||
|
skill3: skill-id
|
||||||
|
skill-pl2: skill-id
|
||||||
|
skill-pl3: skill-id
|
||||||
|
skill-pl4: skill-id
|
||||||
|
skill-pl5: skill-id
|
||||||
2
std
2
std
Submodule std updated: f2e9e778f0...41b8aed39e
295
test/example.kk
Normal file
295
test/example.kk
Normal file
@@ -0,0 +1,295 @@
|
|||||||
|
module test/example
|
||||||
|
|
||||||
|
import std/num/decimal
|
||||||
|
import std/data/linearmap
|
||||||
|
import horse/game-id
|
||||||
|
import horse/global
|
||||||
|
import horse/global/character
|
||||||
|
import horse/global/saddle
|
||||||
|
import horse/global/skill
|
||||||
|
import horse/global/spark
|
||||||
|
import horse/global/uma
|
||||||
|
import horse/legacy
|
||||||
|
|
||||||
|
val p1 = Legacy(
|
||||||
|
uma = Veteran(
|
||||||
|
uma = Uma-id(102001), // seiun sky
|
||||||
|
sparks = [
|
||||||
|
301, // 1* power
|
||||||
|
2102, // 2* front runner
|
||||||
|
10200103, // 3* angling and scheming
|
||||||
|
1000302, // 2* osaka hai
|
||||||
|
1001001, // 1* japanese derby
|
||||||
|
1001101, // 1* yasuda kinen
|
||||||
|
1001701, // 1* qe2
|
||||||
|
2001402, // 2* non-standard distance
|
||||||
|
2004301, // 1* focus
|
||||||
|
2005301, // 1* early lead
|
||||||
|
2012401, // 1* front runner straightaways
|
||||||
|
2012502, // 2* front runner corners
|
||||||
|
2015201, // 1* front runner savvy
|
||||||
|
2016001, // 1* groundwork
|
||||||
|
2016102, // 2* thh
|
||||||
|
2016402, // 2* lone wolf
|
||||||
|
3000201, // 1* unity cup
|
||||||
|
].map(Spark-id(_)),
|
||||||
|
saddles = [
|
||||||
|
1, // classic triple crown
|
||||||
|
2, // senior autumn triple crown
|
||||||
|
4, // senior spring triple crown
|
||||||
|
5, // tenno sweep
|
||||||
|
6, // dual grand prix
|
||||||
|
7, // dual miles
|
||||||
|
10, // arima kinen
|
||||||
|
11, // japan cup
|
||||||
|
12, // derby
|
||||||
|
13, // tss
|
||||||
|
14, // takarazuka kinen
|
||||||
|
15, // tsa
|
||||||
|
16, // kikuka sho
|
||||||
|
17, // osaka hai
|
||||||
|
18, // satsuki sho
|
||||||
|
21, // yasuda kinen
|
||||||
|
23, // mile championship
|
||||||
|
25, // victoria mile
|
||||||
|
26, // qe2
|
||||||
|
33, // asahi hai fs
|
||||||
|
34, // hopeful stakes
|
||||||
|
96, // mainichi hai
|
||||||
|
].map(Saddle-id(_))
|
||||||
|
),
|
||||||
|
sub1 = Veteran(
|
||||||
|
uma = Uma-id(102601), // mihono bourbon
|
||||||
|
sparks = [
|
||||||
|
302, // 2* power
|
||||||
|
3303, // 3* medium
|
||||||
|
10260102, // 2* g00 1st
|
||||||
|
1001201, // 1* takarazuka kinen
|
||||||
|
1001702, // 2* qe2
|
||||||
|
1001901, // 1* japan cup
|
||||||
|
2004302, // 2* focus
|
||||||
|
2004502, // 2* prudent positioning
|
||||||
|
2012502, // 2* front corners
|
||||||
|
2015202, // 2* front savvy
|
||||||
|
2016002, // 2* groundwork
|
||||||
|
2016401, // 1* lone wolf
|
||||||
|
3000201, // 1* unity cup
|
||||||
|
].map(Spark-id(_)),
|
||||||
|
saddles = [
|
||||||
|
2, // senior autumn triple crown
|
||||||
|
6, // dual grand prix
|
||||||
|
7, // dual miles
|
||||||
|
10, // arima kinen
|
||||||
|
11, // japan cup
|
||||||
|
12, // derby
|
||||||
|
14, // takarazuka kinen
|
||||||
|
15, // tsa
|
||||||
|
17, // osaka hai
|
||||||
|
18, // satsuki sho
|
||||||
|
21, // yasuda kinen
|
||||||
|
23, // mile championship
|
||||||
|
25, // victoria mile
|
||||||
|
26, // qe2
|
||||||
|
27, // nhk mile cup
|
||||||
|
33, // asahi hai fs
|
||||||
|
34, // hopeful stakes
|
||||||
|
49, // spring stakes
|
||||||
|
].map(Saddle-id(_))
|
||||||
|
),
|
||||||
|
sub2 = Veteran(
|
||||||
|
uma = Uma-id(102401), // mayano top gun
|
||||||
|
sparks = [
|
||||||
|
302, // 2* power
|
||||||
|
1103, // 3* turf
|
||||||
|
10240101, // 1* flashy landing
|
||||||
|
1000601, // 1* tss
|
||||||
|
1001202, // 2* takarazuka kinen
|
||||||
|
1001502, // 2* kikuka sho
|
||||||
|
1001601, // 1* tsa
|
||||||
|
1002102, // 2* hanshin jf
|
||||||
|
1002301, // 1* arima kinen
|
||||||
|
2003503, // 3* corner recovery
|
||||||
|
2003802, // 2* straightaway recovery
|
||||||
|
2004602, // 2* ramp up
|
||||||
|
2005502, // 2* final push
|
||||||
|
2012702, // 2* leader's pride
|
||||||
|
2016002, // 2* groundwork
|
||||||
|
3000102, // 2* ura finale
|
||||||
|
].map(Spark-id(_)),
|
||||||
|
saddles = [
|
||||||
|
1, // classic triple crown
|
||||||
|
2, // senior autumn triple crown
|
||||||
|
4, // senior spring triple crown
|
||||||
|
5, // tenno sweep
|
||||||
|
6, // dual grand prix
|
||||||
|
7, // dual miles
|
||||||
|
10, // arima kinen
|
||||||
|
11, // japan cup
|
||||||
|
12, // derby
|
||||||
|
13, // tss
|
||||||
|
14, // takarazuka kinen
|
||||||
|
15, // tsa
|
||||||
|
16, // kikuka sho
|
||||||
|
18, // satsuki sho
|
||||||
|
21, // yasuda kinen
|
||||||
|
23, // mile championship
|
||||||
|
25, // victoria mile
|
||||||
|
26, // qe2
|
||||||
|
34, // hopeful stakes
|
||||||
|
35, // hanshin jf
|
||||||
|
].map(Saddle-id(_))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val p2 = Legacy(
|
||||||
|
uma = Veteran(
|
||||||
|
uma = Uma-id(102601), // mihono bourbon
|
||||||
|
sparks = [
|
||||||
|
302,
|
||||||
|
3303,
|
||||||
|
1001201,
|
||||||
|
1001702,
|
||||||
|
1001901,
|
||||||
|
2004302,
|
||||||
|
2004502,
|
||||||
|
2012502,
|
||||||
|
2015202,
|
||||||
|
2016002,
|
||||||
|
2016401,
|
||||||
|
3000201,
|
||||||
|
10260102,
|
||||||
|
].map(Spark-id(_)),
|
||||||
|
saddles = [
|
||||||
|
2,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
14,
|
||||||
|
15,
|
||||||
|
17,
|
||||||
|
18,
|
||||||
|
21,
|
||||||
|
23,
|
||||||
|
25,
|
||||||
|
26,
|
||||||
|
27,
|
||||||
|
33,
|
||||||
|
34,
|
||||||
|
49,
|
||||||
|
].map(Saddle-id(_))
|
||||||
|
),
|
||||||
|
sub1 = Veteran(
|
||||||
|
uma = Uma-id(102402), // wedding mayano
|
||||||
|
sparks = [
|
||||||
|
203,
|
||||||
|
3202,
|
||||||
|
1000701,
|
||||||
|
1000802,
|
||||||
|
1001201,
|
||||||
|
1001803,
|
||||||
|
2003502,
|
||||||
|
2003701,
|
||||||
|
2004301,
|
||||||
|
2005502,
|
||||||
|
2012401,
|
||||||
|
2016402,
|
||||||
|
10240202,
|
||||||
|
].map(Spark-id(_)),
|
||||||
|
saddles = [
|
||||||
|
1,
|
||||||
|
2,
|
||||||
|
6,
|
||||||
|
7,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
14,
|
||||||
|
15,
|
||||||
|
16,
|
||||||
|
18,
|
||||||
|
21,
|
||||||
|
23,
|
||||||
|
25,
|
||||||
|
26,
|
||||||
|
27,
|
||||||
|
33,
|
||||||
|
34,
|
||||||
|
48,
|
||||||
|
].map(Saddle-id(_))
|
||||||
|
),
|
||||||
|
sub2 = Veteran(
|
||||||
|
uma = Uma-id(100201), // silence suzuka
|
||||||
|
sparks = [
|
||||||
|
203,
|
||||||
|
1101,
|
||||||
|
1001901,
|
||||||
|
1002203,
|
||||||
|
1002302,
|
||||||
|
2000101,
|
||||||
|
2000201,
|
||||||
|
2001902,
|
||||||
|
2003501,
|
||||||
|
2005401,
|
||||||
|
2016001,
|
||||||
|
3000102,
|
||||||
|
10020101,
|
||||||
|
].map(Spark-id(_)),
|
||||||
|
saddles = [
|
||||||
|
2,
|
||||||
|
6,
|
||||||
|
10,
|
||||||
|
11,
|
||||||
|
12,
|
||||||
|
14,
|
||||||
|
15,
|
||||||
|
17,
|
||||||
|
18,
|
||||||
|
21,
|
||||||
|
25,
|
||||||
|
26,
|
||||||
|
27,
|
||||||
|
33,
|
||||||
|
34,
|
||||||
|
40,
|
||||||
|
42,
|
||||||
|
44,
|
||||||
|
45,
|
||||||
|
46,
|
||||||
|
49,
|
||||||
|
59,
|
||||||
|
61,
|
||||||
|
63,
|
||||||
|
65,
|
||||||
|
111,
|
||||||
|
113,
|
||||||
|
117,
|
||||||
|
126,
|
||||||
|
].map(Saddle-id(_))
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
val trainee = Uma-id(104601) // smart falcon
|
||||||
|
|
||||||
|
pub fun main()
|
||||||
|
val p1a = parent-affinity(trainee, p1, p2.uma.uma)
|
||||||
|
val p2a = parent-affinity(trainee, p2, p1.uma.uma)
|
||||||
|
val (s11a, s12a) = sub-affinity(trainee, p1)
|
||||||
|
val (s21a, s22a) = sub-affinity(trainee, p2)
|
||||||
|
println("trainee: " ++ trainee.show)
|
||||||
|
println("p1: " ++ p1.uma.uma.show ++ " affinity " ++ p1a.show)
|
||||||
|
println("s1-1: " ++ p1.sub1.uma.show ++ " affinity " ++ s11a.show)
|
||||||
|
println("s1-2: " ++ p1.sub2.uma.show ++ " affinity " ++ s12a.show)
|
||||||
|
println("p2: " ++ p2.uma.uma.show ++ " affinity " ++ p1a.show)
|
||||||
|
println("s1-1: " ++ p2.sub1.uma.show ++ " affinity " ++ s21a.show)
|
||||||
|
println("s1-2: " ++ p2.sub2.uma.show ++ " affinity " ++ s22a.show)
|
||||||
|
val inspo = inspiration(trainee, p1, p2)
|
||||||
|
val s = inspiration-gives(inspo, legacy/skills)
|
||||||
|
val a = inspiration-gives(inspo, legacy/aptitudes)
|
||||||
|
println("\nskills:")
|
||||||
|
s.list.foreach() fn((skill, pmf))
|
||||||
|
println(" " ++ skill.show ++ ": " ++ pmf.show)
|
||||||
|
println("\naptitudes:")
|
||||||
|
a.list.foreach() fn((apt, pmf))
|
||||||
|
println(" " ++ apt.show ++ ": " ++ pmf.show)
|
||||||
12
test/global.kk
Normal file
12
test/global.kk
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
module test/global
|
||||||
|
|
||||||
|
import horse/global/character
|
||||||
|
import horse/global/race
|
||||||
|
import horse/global/saddle
|
||||||
|
import horse/global/scenario
|
||||||
|
import horse/global/skill
|
||||||
|
import horse/global/spark
|
||||||
|
import horse/global/uma
|
||||||
|
|
||||||
|
pub fun main()
|
||||||
|
()
|
||||||
Reference in New Issue
Block a user