Help us shape the future of Go! Take the 2026 Go Developer Survey

react

package
v0.10.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 25, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Overview

Package react makes characters react to what the player says or does, without a language model. See docs/decisions/0015-reaction-and-chat.md.

A Stimulus (typed text, a game event, numbers such as a tip) runs through the built-in analyzers. They attach Tags with a confidence in 0..1: lexicon words, acrostics, repetition and references to what the character remembers. Tags the game already knows come in Stimulus.Tags. The rules of the person in the Book, whose conditions are expressions, then pick a Reaction. When no rule holds, the person dodges in character (teases, asks back, changes the subject) with a line chosen by how sure the analysis was. Judging is deliberately simple; the person's manner is what makes it feel clever.

Load and Parse read a Book (a shared lexicon and the people) and validate every reference. New makes a Mind, the running state of one person: mood, memory, cooldowns and a pending question. A Mind is not safe for concurrent use. Save and Restore keep that state as bytes.

Index

Constants

View Source
const (
	// TagRepeat is attached when the sender says the same thing again.
	TagRepeat = "repeat"
	// TagRecall is attached when the text mentions something the person remembers.
	TagRecall = "recall"
	// TagEmpty is attached to a "say" stimulus with no text.
	TagEmpty = "empty"
	// AcrosticPrefix prefixes a lexicon tag found in the first characters of the lines
	// (acrostic_cat for the tag cat).
	AcrosticPrefix = "acrostic_"
)

Built-in tags that the analyzers attach besides the lexicon tags.

View Source
const KindSay = "say"

KindSay is the stimulus kind of spoken or typed words.

Variables

This section is empty.

Functions

func Normalize

func Normalize(s string) string

Normalize folds text so that casual spellings match the lexicon: full-width and half-width forms are unified, katakana becomes hiragana, Latin letters become lower case, and spaces, punctuation and long-vowel marks ("ー", "〜") are dropped. A run of three or more of the same character becomes two, so "かわいいいい" matches "かわいい". The lexicon and the input go through the same folding.

Types

type Book

type Book struct {
	// contains filtered or unexported fields
}

Book is a lexicon shared by the people and the people themselves, as Load and Parse read them. A Book is read-only after loading and may be shared by many Minds. Its parts are read with YAML.

func Load

func Load(fsys fs.FS, path string) (*Book, error)

Load reads and validates a book from fsys.

func Parse

func Parse(b []byte) (*Book, error)

Parse reads and validates a book from YAML.

func (*Book) YAML

func (b *Book) YAML(path string) ([]byte, error)

YAML is the part of the book at a logical path, as written: "people", "people.host.rules" (an entry of a list by its id, or [0]), "lexicon", "" for the whole book. Read your own small structs from it. An unknown path is an error that is fs.ErrNotExist.

type Config

type Config struct {
	// Rand chooses lines. Nil uses a fixed seed, so reactions are reproducible.
	Rand *rand.Rand
	// Clock is the game time, for memories with a Time limit. Nil is allowed only when
	// no rule of the person uses Time.
	Clock func() time.Duration
	// MaxRunes cuts longer text before analysis (0 means 400).
	MaxRunes int
	// History is how many recent texts per sender are kept to find repeats (0 means 8).
	History int
}

Config holds a Mind's collaborators. The zero value works: a fixed random seed, no game clock, 400 runes and 8 remembered texts per sender.

type Mind

type Mind struct {
	// contains filtered or unexported fields
}

Mind is the running state of one person. It is not safe for concurrent use.

func New

func New(b *Book, id string, cfg Config) (*Mind, error)

New makes the running state of the person id in the book. The book must have been validated (Load and Parse do it) and must not change while the Mind is used.

func (*Mind) Ask

func (m *Mind) Ask(id string) (Reaction, error)

Ask has the person put the question id, as a reaction of its own (for example when the game wants the person to start a topic).

func (*Mind) Mood

func (m *Mind) Mood() float64

Mood is the current mood.

func (*Mind) React

func (m *Mind) React(s Stimulus) (Reaction, error)

React analyzes the stimulus, chooses a rule (or a dodge) and applies it to the mood and memory. An error means a condition failed to evaluate (for example comparing a remembered string with a number). The turn then still advances and memories whose limit has passed are dropped, as on every call; the mood, the remaining memory, the recent texts, the pending question and the cooldowns are unchanged, so retrying the same stimulus is not counted as a repeat.

func (*Mind) Recall

func (m *Mind) Recall(key string) (any, bool)

Recall is a remembered value (a string or a float64); ok is false when it is absent or expired.

func (*Mind) Reset

func (m *Mind) Reset()

Reset starts a new conversation with the same person, such as the next stream or a later visit: the recent texts used to find repeats and the pending question are cleared, while the mood, the memory and the cooldowns are kept.

func (*Mind) Restore

func (m *Mind) Restore(b []byte) error

Restore replaces the state with one that Save returned for the same person. On an error the state is unchanged.

func (*Mind) Save

func (m *Mind) Save() ([]byte, error)

Save returns the mood, memory, cooldowns, pending question and recent texts as JSON. The random generator's position is not saved.

type Reaction

type Reaction struct {
	// ruleDef is the rule ID, "dodge.repeat", "dodge.unsure", "dodge.unknown",
	// "ask.ID" for Mind.Ask, or empty when the person had nothing to say.
	Rule string
	// Lines are the line said and, when the rule asks a question, the question.
	Lines []string
	Face  string
	// Delta is the mood change applied; Mood is the mood after it.
	Delta, Mood float64
	// Confidence is the highest tag score (0 when nothing was found).
	Confidence float64
	// Tags are the findings, the most confident first.
	Tags []Tag
	// question is the ID of the question now pending, if any.
	Ask string
}

Reaction is how the person reacted.

type Stimulus

type Stimulus struct {
	// Kind is KindSay for words, or a game-defined kind such as "event.steal".
	Kind string
	// From names the sender (a player, a viewer); repeats are counted per sender.
	From string
	// Text is the words; it may be empty for events.
	Text string
	// Values are numbers the rules read as value.NAME (a tip, an item's price).
	Values map[string]float64
	// Tags are tags the game already knows (score 1), declared in Book.Tags.
	Tags []string
}

Stimulus is what reaches a person: words, an event, numbers.

type Tag

type Tag struct {
	Name  string
	Score float64
	Slot  string
}

Tag is a finding with a confidence Score in 0..1. Slot is the word that suggested it, for templates and memory.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL