generate characters with sane compile time/memory

This commit is contained in:
2026-01-07 16:36:50 -05:00
parent 4bd9100954
commit b134bea670
7 changed files with 210 additions and 80039 deletions

File diff suppressed because one or more lines are too long

View File

@@ -29,6 +29,16 @@ WITH uma_names AS (
LEFT JOIN relation_pairs 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
-- Reflexive cases.
SELECT
uma_names.id AS id_a,
uma_names.name AS name_a,
uma_names.id AS id_b,
uma_names.name AS name_b,
0 AS base_affinity
FROM uma_names
)
SELECT * FROM affinity
ORDER BY id_a, id_b
ORDER BY id_a, id_b

View File

@@ -13,29 +13,75 @@ WITH uma_names AS (
b.id AS id_b,
b.name AS name_b,
c.id AS id_c,
c.name AS name_c,
ra.relation_type
c.name AS name_c
FROM uma_names a
JOIN uma_names b ON a.id != b.id
JOIN uma_names b ON a.id != b.id -- exclude pairwise reflexive cases
JOIN uma_names c ON a.id != c.id AND b.id != c.id
JOIN succession_relation_member ra ON a.id = ra.chara_id
JOIN succession_relation_member rb ON b.id = rb.chara_id
JOIN succession_relation_member rc ON c.id = rc.chara_id
WHERE
ra.relation_type = rb.relation_type
AND ra.relation_type = rc.relation_type
), relation_trios 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
id_a,
name_a,
id_b,
name_b,
id_c,
name_c,
SUM(relation_point) AS base_affinity
trios.*,
SUM(IFNULL(relation_point, 0)) AS base_affinity
FROM trios
JOIN succession_relation sr ON trios.relation_type = sr.relation_type
GROUP BY id_a, id_b, id_c
LEFT JOIN relation_trios 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
UNION ALL
-- A = B = C
SELECT
n.id AS id_a,
n.name AS name_a,
n.id AS id_b,
n.name AS name_b,
n.id AS id_c,
n.name AS name_c,
0 AS base_affinity
FROM uma_names n
UNION ALL
-- A = B
SELECT
n.id AS id_a,
n.name AS name_a,
n.id AS id_a,
n.name AS id_b,
m.id AS id_c,
m.name AS name_c,
0 AS base_affinity
FROM uma_names n JOIN uma_names m ON n.id != m.id
UNION ALL
-- A = C
SELECT
n.id AS id_a,
n.name AS name_a,
m.id AS id_a,
m.name AS id_b,
n.id AS id_c,
n.name AS name_c,
0 AS base_affinity
FROM uma_names n JOIN uma_names m ON n.id != m.id
UNION ALL
-- B = C
SELECT
m.id AS id_a,
m.name AS name_a,
n.id AS id_a,
n.name AS id_b,
n.id AS id_c,
n.name AS name_c,
0 AS base_affinity
FROM uma_names n JOIN uma_names m ON n.id != m.id
)
SELECT * FROM affinity
ORDER BY id_a, id_b, id_c
ORDER BY id_a, id_b, id_c

View File

@@ -3,14 +3,17 @@ module horse/character
// Automatically generated with the horsegen tool; DO NOT EDIT
import std/core/vector
import std/core-extras
// Character identity.
pub type character
{{- range $uma := $.Characters }}
{{ kkenum $uma.Name }}
{{- end }}
// The list of all characters.
val character/all = [
// The list of all characters in order by ID, for easy iterating.
pub val character/all = [
{{- range $uma := $.Characters }}
{{ kkenum $uma.Name }},
{{- end }}
@@ -58,24 +61,32 @@ pub fun character/(==)(a: character, b: character): bool
{{- end }}
_ -> False
inline fip fun character/index(^c: character): int
match c
{{- range $uma := $.Characters }}
{{ kkenum $uma.Name }} -> {{ $uma.Index }}
{{- end }}
// Create the table of all pair affinities.
// The affinity is the value at a.index*count + b.index.
extern global/create-pair-table(): vector<int>
c inline "kk_intx_t arr[] = { {{- range $a := $.Characters }}{{ range $b := $.Characters }}{{ index $.PairMaps $a.ID $b.ID }},{{ end }}{{ end -}} };\nkk_vector_from_cintarray(arr, (kk_ssize_t){{ $.Count }}*(kk_ssize_t){{ $.Count }}, kk_context())"
js inline "[ {{- range $a := $.Characters }}{{ range $b := $.Characters }}{{ index $.PairMaps $a.ID $b.ID }},{{ end }}{{ end -}} ]"
val global/pair-table = global/create-pair-table()
// Base affinity between a pair using the global ruleset.
pub fun global/pair-affinity(a: character, b: character): int
match (a, b)
{{- range $pair := $.Pairs }}
( {{- kkenum $pair.NameA }}, {{ kkenum $pair.NameB -}} ) -> {{ $pair.Affinity }}
{{- end }}
// Reflexive cases are always 0.
{{- range $uma := $.Characters }}{{ $e := kkenum $uma.Name }}
( {{- $e }}, {{ $e -}} ) -> 0
{{- end }}
global/pair-table.at(a.index * {{ $.Count }} + b.index).default(0)
// Create the table of all trio affinities.
// The affinity is the value at a.index*count*count + b.index*count + c.index.
extern global/create-trio-table(): vector<int>
c inline "kk_intx_t arr[] = { {{- range $a := $.Characters }}{{ range $b := $.Characters }}{{ range $c := $.Characters }}{{ index $.TrioMaps $a.ID $b.ID $c.ID }},{{ end }}{{ end }}{{ end -}} };\nkk_vector_from_cintarray(arr, (kk_ssize_t){{ $.Count }}*(kk_ssize_t){{ $.Count }}*(kk_ssize_t){{ $.Count }}, kk_context())"
js inline "[ {{- range $a := $.Characters }}{{ range $b := $.Characters }}{{ range $c := $.Characters }}{{ index $.TrioMaps $a.ID $b.ID $c.ID }},{{ end }}{{ end }}{{ end -}} ]"
val global/trio-table = global/create-trio-table()
{{/* TODO: probably use a data structure instead of hoping the compilers fix this */ -}}
// Base affinity for a trio using the global ruleset.
pub fun global/trio-affinity(a: character, b: character, c: character): int
match (a, b, c)
{{- range $trio := $.Trios }}
( {{- kkenum $trio.NameA }}, {{ kkenum $trio.NameB }}, {{ kkenum $trio.NameC -}} ) -> {{ $trio.Affinity }}
{{- end }}
(_, _, _) -> 0
global/trio-table.at(a.index * {{ $.Count }} * {{ $.Count }} + b.index * {{ $.Count }} + c.index).default(0)
{{- end }}

View File

@@ -1,7 +1,9 @@
SELECT
"index" AS "id",
"text" AS "name"
"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)
AND "index" IN (SELECT chara_id FROM succession_relation_member)
ORDER BY "id"

View File

@@ -26,16 +26,40 @@ func LoadTemplates() (*template.Template, error) {
// ExecCharacterKK renders the Koka character module to w.
func ExecCharacterKK(t *template.Template, w io.Writer, c []Character, pairs, trios []AffinityRelation) error {
if len(pairs) != len(c)*len(c) {
return fmt.Errorf("there are %d pairs but there must be %d for %d characters", len(pairs), len(c)*len(c), len(c))
}
if len(trios) != len(c)*len(c)*len(c) {
return fmt.Errorf("there are %d trios but there must be %d for %d characters", len(trios), len(c)*len(c)*len(c), len(c))
}
maxid := 0
pm := make(map[int]map[int]int, len(c))
tm := make(map[int]map[int]map[int]int, len(c))
for _, u := range c {
maxid = max(maxid, u.ID)
pm[u.ID] = make(map[int]int, len(c))
tm[u.ID] = make(map[int]map[int]int, len(c))
for _, v := range c {
tm[u.ID][v.ID] = make(map[int]int, len(c))
}
}
for _, p := range pairs {
pm[p.IDA][p.IDB] = p.Affinity
}
for _, t := range trios {
tm[t.IDA][t.IDB][t.IDC] = t.Affinity
}
data := struct {
Characters []Character
Pairs []AffinityRelation
Trios []AffinityRelation
PairMaps map[int]map[int]int
TrioMaps map[int]map[int]map[int]int
Count int
MaxID int
}{c, pairs, trios, maxid}
}{c, pairs, trios, pm, tm, len(c), maxid}
return t.ExecuteTemplate(w, "koka-character", &data)
}

View File

@@ -20,6 +20,9 @@ var characterAffinity3SQL string
type Character struct {
ID int
Name string
// For internal use, the index of the character.
// We don't show this in public API, but it lets us use vectors for lookups.
Index int
}
func Characters(ctx context.Context, db *sqlitex.Pool) ([]Character, error) {
@@ -44,8 +47,9 @@ func Characters(ctx context.Context, db *sqlitex.Pool) ([]Character, error) {
break
}
c := Character{
ID: stmt.ColumnInt(0),
Name: stmt.ColumnText(1),
ID: stmt.ColumnInt(0),
Name: stmt.ColumnText(1),
Index: stmt.ColumnInt(2),
}
r = append(r, c)
}