mirror of
https://codeberg.org/scip/gfn.git
synced 2025-12-16 18:30:57 +01:00
program works now, added a little documentation
This commit is contained in:
108
README.md
108
README.md
@@ -1,2 +1,108 @@
|
|||||||
# gfn
|
# gfn
|
||||||
Generate fantasy names for games and stories
|
|
||||||
|
Generate fantasy names for games and stories. It uses the fine
|
||||||
|
[fantasyname module](https://github.com/s0rg/fantasyname) by
|
||||||
|
[s0rg](https://github.com/s0rg/), which implements the code created by
|
||||||
|
the [rinkworks fantasy name
|
||||||
|
generator](http://rinkworks.com/namegen/). The code itself is
|
||||||
|
[outlined here](http://rinkworks.com/namegen/instr.shtml), or take a
|
||||||
|
quick look at the [reference
|
||||||
|
guide](http://rinkworks.com/namegen/reference.shtml).
|
||||||
|
|
||||||
|
In case the site vanishes some day, a copy of those documents is
|
||||||
|
contained here in the repository.
|
||||||
|
|
||||||
|
# Install
|
||||||
|
|
||||||
|
Execute
|
||||||
|
|
||||||
|
```shell
|
||||||
|
% go build
|
||||||
|
% cp gfn $HOME/bin
|
||||||
|
```
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
|
||||||
|
There are a bunch of pre compiled fantasy name codes builtin, you can
|
||||||
|
get a list with:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
% gfn -l
|
||||||
|
```
|
||||||
|
|
||||||
|
To use one of them and limit the number of
|
||||||
|
words generated:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
% gfn JapaneseNamesDiverse -c 24
|
||||||
|
yumufuchi afuchin keyu amorekin ekimuwo ashihewani rosa chireki
|
||||||
|
oterun ruwahi uwamine emiyumu temimon yuwa awayason fuki
|
||||||
|
emiwa nushiron achihora yomichi saniyutan kewaritsu saroru uhashi
|
||||||
|
```
|
||||||
|
|
||||||
|
You can also write a code yourself:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
gfn '!sVm' -c 24
|
||||||
|
Quaoobunker Emeemoopsie Angeepookie Osousmoosh Umuisweetie Ustoesnookum Sulealover Imopookie
|
||||||
|
Skelaesnoogle Echiapookie Cereepoochie Gariwuddle Echaewookie Tiaieschmoopie Queaubooble Athesmoosh
|
||||||
|
Undousnuggy Urnuigooble Mosoesnuggy Eldoegooble Denoipoochie Mosoosmooch Shyucuddly Tiaeylovey
|
||||||
|
```
|
||||||
|
|
||||||
|
A short outline of the code will be printed if you add the `-h`
|
||||||
|
parameter:
|
||||||
|
```shell
|
||||||
|
This is gfn, a fantasy name generator cli.
|
||||||
|
|
||||||
|
Usage: gfn [-vlc] <name|code>
|
||||||
|
|
||||||
|
Options:
|
||||||
|
-c --count How many names to generate
|
||||||
|
-l --list List pre-compiled shortcuts
|
||||||
|
-v --version Show program version
|
||||||
|
|
||||||
|
pattern syntax The letters s, v, V, c, B, C, i, m, M, D, and d
|
||||||
|
represent different types of random replacements:
|
||||||
|
|
||||||
|
s - generic syllable
|
||||||
|
v - vowel
|
||||||
|
V - vowel or vowel combination
|
||||||
|
c - consonant
|
||||||
|
B - consonant or consonant combination suitable for beginning a word
|
||||||
|
C - consonant or consonant combination suitable anywhere in a word
|
||||||
|
i - insult
|
||||||
|
m - mushy name
|
||||||
|
M - mushy name ending
|
||||||
|
D - consonant suited for a stupid person's name
|
||||||
|
d - syllable suited for a stupid person's name (begins with a vowel)
|
||||||
|
Everything else is emitted literally.
|
||||||
|
|
||||||
|
All characters between parenthesis () are emitted literally. For
|
||||||
|
example, the pattern s(dim), emits a random generic syllable followed
|
||||||
|
by dim.
|
||||||
|
|
||||||
|
Characters between angle brackets <> emit patterns from the table
|
||||||
|
above. Imagine the entire pattern is wrapped in one of these.
|
||||||
|
|
||||||
|
In both types of groupings, a vertical bar | denotes a random
|
||||||
|
choice. Empty groups are allowed. For example, (foo|bar) emits either
|
||||||
|
foo or bar. The pattern <c|v|> emits a constant, vowel, or nothing at
|
||||||
|
all.
|
||||||
|
|
||||||
|
An exclamation point ! means to capitalize the component that follows
|
||||||
|
it. For example, !(foo) will emit Foo and v!s will emit a lowercase
|
||||||
|
vowel followed by a capitalized syllable, like eRod.
|
||||||
|
```
|
||||||
|
|
||||||
|
# Report bugs
|
||||||
|
|
||||||
|
[Please open an issue](https://github.com/TLINDEN/gfn/issues). Thanks!
|
||||||
|
|
||||||
|
# License
|
||||||
|
|
||||||
|
This work is licensed under the terms of the General Public Licens
|
||||||
|
version 3.
|
||||||
|
|
||||||
|
# Author
|
||||||
|
|
||||||
|
Copyleft (c) 2024 Thomas von Dein
|
||||||
|
|||||||
54
config.go
54
config.go
@@ -79,33 +79,35 @@ type Template struct {
|
|||||||
Tmpl string
|
Tmpl string
|
||||||
}
|
}
|
||||||
|
|
||||||
var Templates = []Template{
|
var Templates = map[string]string{
|
||||||
{Name: "MiddleEarth", Tmpl: MIDDLE_EARTH},
|
"MiddleEarth": MIDDLE_EARTH,
|
||||||
{Name: "JapaneseNamesConstrained", Tmpl: JAPANESE_NAMES_CONSTRAINED},
|
"JapaneseNamesConstrained": JAPANESE_NAMES_CONSTRAINED,
|
||||||
{Name: "JapaneseNamesDiverse", Tmpl: JAPANESE_NAMES_DIVERSE},
|
"JapaneseNamesDiverse": JAPANESE_NAMES_DIVERSE,
|
||||||
{Name: "ChineseNames", Tmpl: CHINESE_NAMES},
|
"ChineseNames": CHINESE_NAMES,
|
||||||
{Name: "GreekNames", Tmpl: GREEK_NAMES},
|
"GreekNames": GREEK_NAMES,
|
||||||
{Name: "HawaiianNames1", Tmpl: HAWAIIAN_NAMES_1},
|
"HawaiianNames1": HAWAIIAN_NAMES_1,
|
||||||
{Name: "HawaiianNames2", Tmpl: HAWAIIAN_NAMES_2},
|
"HawaiianNames2": HAWAIIAN_NAMES_2,
|
||||||
{Name: "OldLatinPlaceNames", Tmpl: OLD_LATIN_PLACE_NAMES},
|
"OldLatinPlaceNames": OLD_LATIN_PLACE_NAMES,
|
||||||
{Name: "DragonsPern", Tmpl: DRAGONS_PERN},
|
"DragonsPern": DRAGONS_PERN,
|
||||||
{Name: "DragonRiders", Tmpl: DRAGON_RIDERS},
|
"DragonRiders": DRAGON_RIDERS,
|
||||||
{Name: "Pokemon", Tmpl: POKEMON},
|
"Pokemon": POKEMON,
|
||||||
{Name: "FantasyR", Tmpl: FANTASY_VOWELS_R},
|
"FantasyR": FANTASY_VOWELS_R,
|
||||||
{Name: "FantasySA", Tmpl: FANTASY_S_A},
|
"FantasySA": FANTASY_S_A,
|
||||||
{Name: "FantasyHL", Tmpl: FANTASY_H_L},
|
"FantasyHL": FANTASY_H_L,
|
||||||
{Name: "FantasyNL", Tmpl: FANTASY_N_L},
|
"FantasyNL": FANTASY_N_L,
|
||||||
{Name: "FantasyKN", Tmpl: FANTASY_K_N},
|
"FantasyKN": FANTASY_K_N,
|
||||||
{Name: "FantasyJGZ", Tmpl: FANTASY_J_G_Z},
|
"FantasyJGZ": FANTASY_J_G_Z,
|
||||||
{Name: "FantasyKJY", Tmpl: FANTASY_K_J_Y},
|
"FantasyKJY": FANTASY_K_J_Y,
|
||||||
{Name: "FantasySE", Tmpl: FANTASY_S_E},
|
"FantasySE": FANTASY_S_E,
|
||||||
{Name: "Funny", Tmpl: "sdD"},
|
"Funny": "sdD",
|
||||||
{Name: "Idiots", Tmpl: "ii"},
|
"Idiots": "ii",
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
VERSION string = "0.0.1"
|
VERSION string = "0.0.1"
|
||||||
Usage string = `This is gfn, a fantasy name generator cli.
|
DefaultCount int = 160 // number of words to generate if -c is omitted
|
||||||
|
Columns int = 8 // number of columns to print
|
||||||
|
Usage string = `This is gfn, a fantasy name generator cli.
|
||||||
|
|
||||||
Usage: gfn [-vlc] <name|code>
|
Usage: gfn [-vlc] <name|code>
|
||||||
|
|
||||||
@@ -151,6 +153,7 @@ type Config struct {
|
|||||||
Showversion bool `koanf:"version"` // -v
|
Showversion bool `koanf:"version"` // -v
|
||||||
Listshortcuts bool `koanf:"list"` // -l
|
Listshortcuts bool `koanf:"list"` // -l
|
||||||
Code string // arg
|
Code string // arg
|
||||||
|
Count int `koanf:"count"` // -c
|
||||||
}
|
}
|
||||||
|
|
||||||
func InitConfig(output io.Writer) (*Config, error) {
|
func InitConfig(output io.Writer) (*Config, error) {
|
||||||
@@ -158,7 +161,7 @@ func InitConfig(output io.Writer) (*Config, error) {
|
|||||||
|
|
||||||
// Load default values using the confmap provider.
|
// Load default values using the confmap provider.
|
||||||
if err := kloader.Load(confmap.Provider(map[string]interface{}{
|
if err := kloader.Load(confmap.Provider(map[string]interface{}{
|
||||||
"count": 10,
|
"count": DefaultCount,
|
||||||
}, "."), nil); err != nil {
|
}, "."), nil); err != nil {
|
||||||
return nil, fmt.Errorf("failed to load default values into koanf: %w", err)
|
return nil, fmt.Errorf("failed to load default values into koanf: %w", err)
|
||||||
}
|
}
|
||||||
@@ -173,6 +176,7 @@ func InitConfig(output io.Writer) (*Config, error) {
|
|||||||
// parse commandline flags
|
// parse commandline flags
|
||||||
flagset.BoolP("list", "l", false, "show list of precompiled codes")
|
flagset.BoolP("list", "l", false, "show list of precompiled codes")
|
||||||
flagset.BoolP("version", "V", false, "show program version")
|
flagset.BoolP("version", "V", false, "show program version")
|
||||||
|
flagset.IntP("count", "c", 1, "how many names to generate")
|
||||||
|
|
||||||
if err := flagset.Parse(os.Args[1:]); err != nil {
|
if err := flagset.Parse(os.Args[1:]); err != nil {
|
||||||
return nil, fmt.Errorf("failed to parse program arguments: %w", err)
|
return nil, fmt.Errorf("failed to parse program arguments: %w", err)
|
||||||
|
|||||||
@@ -14,12 +14,12 @@ func Generate(count int, code string) ([]string, error) {
|
|||||||
|
|
||||||
gen, err := fn.Compile(code, fn.Collapse(true))
|
gen, err := fn.Compile(code, fn.Collapse(true))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Could not compile FN code:", err)
|
return nil, fmt.Errorf("could not compile FN code: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := 0; i < count; i++ {
|
for i := 0; i < count; i++ {
|
||||||
name := gen.String()
|
name := gen.String()
|
||||||
if !exists(reg, name) {
|
if !Exists(reg, name) {
|
||||||
reg[name] = 1
|
reg[name] = 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
go.mod
1
go.mod
@@ -5,6 +5,7 @@ go 1.22
|
|||||||
require github.com/s0rg/fantasyname v1.3.4
|
require github.com/s0rg/fantasyname v1.3.4
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/alecthomas/repr v0.4.0 // indirect
|
||||||
github.com/fatih/color v1.16.0 // indirect
|
github.com/fatih/color v1.16.0 // indirect
|
||||||
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect
|
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 // indirect
|
||||||
github.com/knadh/koanf/maps v0.1.1 // indirect
|
github.com/knadh/koanf/maps v0.1.1 // indirect
|
||||||
|
|||||||
2
go.sum
2
go.sum
@@ -1,3 +1,5 @@
|
|||||||
|
github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc=
|
||||||
|
github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||||
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM=
|
||||||
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE=
|
||||||
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c=
|
github.com/go-viper/mapstructure/v2 v2.0.0-alpha.1 h1:TQcrn6Wq+sKGkpyPvppOz99zsMBaUOKXq6HSv655U1c=
|
||||||
|
|||||||
43
main.go
43
main.go
@@ -3,7 +3,6 @@ package main
|
|||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"log"
|
|
||||||
"os"
|
"os"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -24,42 +23,26 @@ func Main(output io.Writer) int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if conf.Listshortcuts {
|
if conf.Listshortcuts {
|
||||||
for _, name := range Templates {
|
ListTemplates(output)
|
||||||
fmt.Println(name)
|
|
||||||
}
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: this is a slice of Template{}, turn it into a map
|
if len(conf.Code) == 0 {
|
||||||
if Contains(Templates, conf.Code) {
|
fmt.Fprintln(output, Usage)
|
||||||
|
}
|
||||||
|
|
||||||
|
if Exists(Templates, conf.Code) {
|
||||||
conf.Code = Templates[conf.Code]
|
conf.Code = Templates[conf.Code]
|
||||||
}
|
}
|
||||||
|
|
||||||
names, err := Generate(conf.Count, conf.Code)
|
names, err := Generate(conf.Count, conf.Code)
|
||||||
|
if err != nil {
|
||||||
|
return Die(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = PrintColumns(names, output); err != nil {
|
||||||
|
return Die(err)
|
||||||
|
}
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
}
|
}
|
||||||
|
|
||||||
func exists[K comparable, V any](m map[K]V, v K) bool {
|
|
||||||
if _, ok := m[v]; ok {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
func Die(err error) int {
|
|
||||||
log.Fatal("Error", err.Error())
|
|
||||||
|
|
||||||
return 1
|
|
||||||
}
|
|
||||||
|
|
||||||
// find an item in a list, generic variant
|
|
||||||
func Contains[E comparable](s []E, v E) bool {
|
|
||||||
for _, vs := range s {
|
|
||||||
if v == vs {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|||||||
81
printer.go
Normal file
81
printer.go
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"io"
|
||||||
|
"sort"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ListTemplates(output io.Writer) {
|
||||||
|
names := []string{}
|
||||||
|
|
||||||
|
for name := range Templates {
|
||||||
|
names = append(names, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Strings(names)
|
||||||
|
|
||||||
|
for _, name := range names {
|
||||||
|
fmt.Fprintln(output, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PrintColumns(names []string, output io.Writer) error {
|
||||||
|
count := len(names)
|
||||||
|
|
||||||
|
// no need for the hassle to calculate columns
|
||||||
|
if count <= Columns {
|
||||||
|
for _, name := range names {
|
||||||
|
fmt.Fprintln(output, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// get a transposed list of columns
|
||||||
|
padlist, max := Getcolumns(names, Columns)
|
||||||
|
|
||||||
|
// make sure there's enough spacing between the columns
|
||||||
|
format := fmt.Sprintf("%%-%ds", max+1)
|
||||||
|
|
||||||
|
for _, row := range padlist {
|
||||||
|
for _, word := range row {
|
||||||
|
fmt.Fprintf(output, format, word)
|
||||||
|
}
|
||||||
|
fmt.Fprintln(output)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func Getcolumns(names []string, columns int) ([][]string, int) {
|
||||||
|
words := len(names)
|
||||||
|
max := 0
|
||||||
|
|
||||||
|
// we'll have a list of $columns columns
|
||||||
|
padlist := make([][]string, columns)
|
||||||
|
|
||||||
|
// initialize'em
|
||||||
|
for col := 0; col < columns; col++ {
|
||||||
|
padlist[col] = []string{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fill from input
|
||||||
|
for idx := 0; idx < words; idx += columns {
|
||||||
|
for col := 0; col < columns; col++ {
|
||||||
|
if idx+col >= words {
|
||||||
|
padlist[col] = append(padlist[col], "")
|
||||||
|
} else {
|
||||||
|
padlist[col] = append(padlist[col], names[idx+col])
|
||||||
|
length := len(names[idx+col])
|
||||||
|
if length > max {
|
||||||
|
max = length
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// turn columns to rows
|
||||||
|
return Transpose(padlist), max
|
||||||
|
}
|
||||||
48
util.go
Normal file
48
util.go
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import "log"
|
||||||
|
|
||||||
|
func Exists[K comparable, V any](m map[K]V, v K) bool {
|
||||||
|
if _, ok := m[v]; ok {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func Die(err error) int {
|
||||||
|
log.Fatal("Error: ", err.Error())
|
||||||
|
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
// find an item in a list, generic variant
|
||||||
|
func Contains[E comparable](s []E, v E) bool {
|
||||||
|
for _, vs := range s {
|
||||||
|
if v == vs {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
// Transpose a matrix, x=>y, y=>x
|
||||||
|
// via https://gist.github.com/tanaikech/5cb41424ff8be0fdf19e78d375b6adb8
|
||||||
|
func Transpose(slice [][]string) [][]string {
|
||||||
|
xl := len(slice[0])
|
||||||
|
yl := len(slice)
|
||||||
|
|
||||||
|
result := make([][]string, xl)
|
||||||
|
|
||||||
|
for i := range result {
|
||||||
|
result[i] = make([]string, yl)
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < xl; i++ {
|
||||||
|
for j := 0; j < yl; j++ {
|
||||||
|
result[i][j] = slice[j][i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user