Skip to content

Commit

Permalink
Merge pull request #46 from tmck-code/japanese-names
Browse files Browse the repository at this point in the history
Japanese names
  • Loading branch information
tmck-code authored Jan 16, 2023
2 parents 6bdc098 + e63d1ed commit e74a552
Show file tree
Hide file tree
Showing 15 changed files with 377 additions and 174 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@ build/assets
build/bin

# Built binaries
pokesay
pokedex
# pokesay
# pokedex

# Test binary, built with `go test -c`
*.test
Expand Down
3 changes: 2 additions & 1 deletion build/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ WORKDIR /usr/local/src

RUN BUILD_DEPS="make gcc"
RUN apt-get update \
&& apt-get install -y --no-install-recommends "${BUILD_DEPS}" libmagickwand-dev ncurses-dev
&& apt-get install -y --no-install-recommends "${BUILD_DEPS}" libmagickwand-dev ncurses-dev jq

RUN git clone --depth 1 https://github.com/denilsonsa/img2xterm \
&& (cd img2xterm && make && make install) \
Expand All @@ -29,6 +29,7 @@ RUN go run /usr/local/src/src/bin/convert/png_convert.go \
-padding 4 \
-skip '["resources/", "misc/", "icons/", "items/", "items-outline/", "pokemon-gen7x/"]' \
&& mv /tmp/cows/pokemon-gen8/* /tmp/cows && rmdir /tmp/cows/pokemon-gen8/ \
&& cat /tmp/original/pokesprite/data/pokemon.json | jq -c .[] > /tmp/cows/pokemon.json \
&& rm -rf /tmp/original/pokesprite

ADD . .
13 changes: 10 additions & 3 deletions build/Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
DOCKER_OUTPUT_DIR ?= /tmp/cows
DOCKER_REPO ?= "tmckcode"
DOCKER_TAG ?= "latest"
DOCKER_REPO ?= tmckcode
DOCKER_TAG ?= latest

DOCKER_IMAGE=$(DOCKER_REPO)/pokesay:$(DOCKER_TAG)

Expand Down Expand Up @@ -38,4 +38,11 @@ build/release: build/assets
build/scripts/build.sh
tree $(PWD)/bin/

.PHONY: all build/docker build/cows build/assets build/release
test:
docker run \
-v $(PWD)/../:/usr/local/src \
--rm --name pokesay-test \
$(DOCKER_IMAGE) \
go test -v ./test/

.PHONY: all build/docker build/cows build/assets build/release test
1 change: 1 addition & 0 deletions build/scripts/build_assets.sh
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ FROM="${1:-/tmp/cows/}"

go run ./src/bin/pokedex/pokedex.go \
-from "${FROM}" \
-fromMetadata "${FROM}/pokemon.json" \
-to ./build/assets/ \
-toCategoryFpath pokedex.gob \
-toDataSubDir cows/ \
Expand Down
151 changes: 37 additions & 114 deletions pokesay.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,14 @@ package main
import (
"bufio"
"embed"
"errors"
"flag"
"fmt"
"log"
"math/rand"
"os"
"sort"
"strconv"
"strings"
"time"

"github.com/fatih/color"
"github.com/tmck-code/pokesay/src/pokedex"

"github.com/mitchellh/go-wordwrap"
"github.com/tmck-code/pokesay/src/pokesay"
)

var (
Expand All @@ -29,82 +22,8 @@ var (
GOBCowData embed.FS
//go:embed build/assets/metadata/*metadata
GOBCowNames embed.FS

Rand rand.Source = rand.NewSource(time.Now().UnixNano())

textStyleItalic *color.Color = color.New(color.Italic)
textStyleBold *color.Color = color.New(color.Bold)
)

func check(e error) {
if e != nil {
log.Fatal(e)
}
}

func randomInt(n int) int {
if n <= 0 {
log.Fatal(errors.New("randomInt arg must be >0"))
}
return rand.New(Rand).Intn(n)
}

func printSpeechBubbleLine(line string, width int) {
if len(line) > width {
fmt.Printf("| %s\n", line)
} else if len(line) == width {
fmt.Printf("| %s |\n", line)
} else {
fmt.Printf("| %s%s |\n", line, strings.Repeat(" ", width-len(line)))
}
}

func printWrappedText(line string, width int, tabSpaces string) {
for _, wline := range strings.Split(wordwrap.WrapString(strings.Replace(line, "\t", tabSpaces, -1), uint(width)), "\n") {
printSpeechBubbleLine(wline, width)
}
}

func printSpeechBubble(scanner *bufio.Scanner, args Args) {
border := strings.Repeat("-", args.Width+2)
fmt.Println("/" + border + "\\")

for scanner.Scan() {
line := scanner.Text()

if !args.NoTabSpaces {
line = strings.Replace(line, "\t", args.TabSpaces, -1)
}
if args.NoWrap {
printSpeechBubbleLine(line, args.Width)
} else {
printWrappedText(line, args.Width, args.TabSpaces)
}
}
fmt.Println("\\" + border + "/")
for i := 0; i < 4; i++ {
fmt.Println(strings.Repeat(" ", i+8), "\\")
}
}

func printPokemon(index int, name string, categoryKeys []string) {
d, _ := GOBCowData.ReadFile(pokedex.EntryFpath("build/assets/cows", index))

fmt.Printf(
"%s> %s | %s\n",
pokedex.Decompress(d),
textStyleBold.Sprint(name),
textStyleItalic.Sprint(strings.Join(categoryKeys, "/")),
)
}

func chooseRandomCategory(keys [][]string, categories pokedex.PokemonTrie) ([]string, []*pokedex.PokemonEntry) {
choice := keys[randomInt(len(keys)-1)]
category, err := categories.GetCategory(choice)
check(err)
return choice, category
}

type Args struct {
Width int
NoWrap bool
Expand All @@ -114,6 +33,7 @@ type Args struct {
ListNames bool
Category string
NameToken string
JapaneseName bool
}

func parseFlags() Args {
Expand All @@ -126,6 +46,7 @@ func parseFlags() Args {
name := flag.String("name", "", "choose a pokemon from a specific name")
listCategories := flag.Bool("list-categories", false, "list all available categories")
listNames := flag.Bool("list-names", false, "list all available names")
japaneseName := flag.Bool("japanese-name", false, "print the japanese name")

flag.Parse()
var args Args
Expand All @@ -147,30 +68,14 @@ func parseFlags() Args {
ListNames: *listNames,
Category: *category,
NameToken: *name,
JapaneseName: *japaneseName,
}
}
return args
}

func ListCategories(categories pokedex.PokemonTrie) []string {
ukm := map[string]bool{}
for _, v := range categories.Keys {
for _, k := range v {
ukm[k] = true
}
}
keys := make([]string, len(ukm))
i := 0
for k := range ukm {
keys[i] = k
i++
}
sort.Strings(keys)
return keys
}

func runListCategories(categories pokedex.PokemonTrie) {
keys := ListCategories(categories)
keys := pokesay.ListCategories(categories)
fmt.Printf("%s\n%d %s\n", strings.Join(keys, " "), len(keys), "total categories")
}

Expand All @@ -181,42 +86,60 @@ func runListNames() {
for i := 0; i < total; i++ {
data := pokedex.MetadataFpath("build/assets/metadata", i)
m, err := GOBCowNames.ReadFile(data)
check(err)
pokesay.Check(err)
metadata := pokedex.ReadMetadataFromBytes(m)
names[i] = metadata.Name
}
fmt.Println(strings.Join(names, " "))
fmt.Printf("\n%d %s\n", len(names), "total names")
}

func GenerateName(metadata pokedex.PokemonMetadata, args Args) string {
if args.JapaneseName {
return fmt.Sprintf("%s / %s (%s)", metadata.Name, metadata.JapaneseName, metadata.JapanesePhonetic)
} else {
return metadata.Name
}
}

func runPrintByName(args Args, categories pokedex.PokemonTrie) {
matches, err := categories.MatchNameToken(args.NameToken)
check(err)
match := matches[randomInt(len(matches))]
pokesay.Check(err)
match := matches[pokesay.RandomInt(len(matches))]

printSpeechBubble(bufio.NewScanner(os.Stdin), args)
printPokemon(match.Entry.Index, match.Entry.Name, match.Categories)
m, err := GOBCowNames.ReadFile(pokedex.MetadataFpath("build/assets/metadata", match.Entry.Index))
pokesay.Check(err)
metadata := pokedex.ReadMetadataFromBytes(m)

pokesay.PrintSpeechBubble(bufio.NewScanner(os.Stdin), args.Width, args.NoTabSpaces, args.TabSpaces, args.NoWrap)
pokesay.PrintPokemon(match.Entry.Index, []string{GenerateName(metadata, args)}, match.Categories, GOBCowData)
}

func runPrintByCategory(args Args, categories pokedex.PokemonTrie) {
matches, err := categories.GetCategoryPaths(args.Category)
check(err)
keys, category := chooseRandomCategory(matches, categories)
choice := category[randomInt(len(category))]
pokesay.Check(err)
keys, category := pokesay.ChooseRandomCategory(matches, categories)
choice := category[pokesay.RandomInt(len(category))]

fmt.Printf("+%v\n", choice)

m, err := GOBCowNames.ReadFile(pokedex.MetadataFpath("build/assets/metadata", choice.Index))
pokesay.Check(err)
metadata := pokedex.ReadMetadataFromBytes(m)

printSpeechBubble(bufio.NewScanner(os.Stdin), args)
printPokemon(choice.Index, choice.Name, keys)
pokesay.PrintSpeechBubble(bufio.NewScanner(os.Stdin), args.Width, args.NoTabSpaces, args.TabSpaces, args.NoWrap)
pokesay.PrintPokemon(choice.Index, []string{GenerateName(metadata, args)}, keys, GOBCowData)
}

func runPrintRandom(args Args) {
total, _ := strconv.Atoi(string(GOBTotal))
choice := randomInt(total)
choice := pokesay.RandomInt(total)
m, err := GOBCowNames.ReadFile(pokedex.MetadataFpath("build/assets/metadata", choice))
check(err)
pokesay.Check(err)
metadata := pokedex.ReadMetadataFromBytes(m)

printSpeechBubble(bufio.NewScanner(os.Stdin), args)
printPokemon(choice, metadata.Name, strings.Split(metadata.Categories, "/"))
pokesay.PrintSpeechBubble(bufio.NewScanner(os.Stdin), args.Width, args.NoTabSpaces, args.TabSpaces, args.NoWrap)
pokesay.PrintPokemon(choice, []string{GenerateName(metadata, args)}, strings.Split(metadata.Categories, "/"), GOBCowData)
}

func main() {
Expand Down
56 changes: 38 additions & 18 deletions src/bin/pokedex/pokedex.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,19 @@ func check(e error) {
}

type PokedexArgs struct {
FromDir string
ToDir string
Debug bool
ToCategoryFname string
ToDataSubDir string
ToMetadataSubDir string
ToTotalFname string
FromDir string
FromMetadataFname string
ToDir string
Debug bool
ToCategoryFname string
ToDataSubDir string
ToMetadataSubDir string
ToTotalFname string
}

func parseArgs() PokedexArgs {
fromDir := flag.String("from", "/tmp/cows", "from dir")
fromMetadataFname := flag.String("fromMetadata", "/tmp/cows/pokemon.json", "metadata file")
toDir := flag.String("to", "build/assets/", "to dir")

toDataSubDir := flag.String("toDataSubDir", "cows/", "dir to write all binary (image) data to")
Expand All @@ -41,50 +43,68 @@ func parseArgs() PokedexArgs {
flag.Parse()

args := PokedexArgs{
FromDir: pokedex.NormaliseRelativeDir(*fromDir),
ToDir: pokedex.NormaliseRelativeDir(*toDir),
ToCategoryFname: *toCategoryFname,
ToDataSubDir: pokedex.NormaliseRelativeDir(*toDataSubDir),
ToMetadataSubDir: pokedex.NormaliseRelativeDir(*toMetadataSubDir),
ToTotalFname: *toTotalFname,
Debug: *debug,
FromDir: pokedex.NormaliseRelativeDir(*fromDir),
FromMetadataFname: *fromMetadataFname,
ToDir: pokedex.NormaliseRelativeDir(*toDir),
ToCategoryFname: *toCategoryFname,
ToDataSubDir: pokedex.NormaliseRelativeDir(*toDataSubDir),
ToMetadataSubDir: pokedex.NormaliseRelativeDir(*toMetadataSubDir),
ToTotalFname: *toTotalFname,
Debug: *debug,
}
if args.Debug {
fmt.Printf("%+v\n", args)
}
return args
}

// This function reads in the files given by the PokedexArgs, and generates the data that pokesay will use when running
// - The "category" struct
// - contains category information, and the index of the corresponding metadata file
// - The "metadata" files
// - named like 1.metadata, contains pokemon info like name, categories, japanese name
// - The "data" files
// - contain the pokemon as gzipped text
// - The "total" file
// - contains the total number of pokemon files, used for random selection
func main() {
args := parseArgs()

totalFpath := path.Join(args.ToDir, args.ToTotalFname)
categoryFpath := path.Join(args.ToDir, args.ToCategoryFname)

fpaths := pokedex.FindFiles(args.FromDir, ".cow", make([]string, 0))
pokemonNames := pokedex.ReadNames(args.FromMetadataFname)
fmt.Printf("%+v\n", pokemonNames)
cowfileFpaths := pokedex.FindFiles(args.FromDir, ".cow", make([]string, 0))

err := os.MkdirAll(path.Join(args.ToDir, args.ToDataSubDir), 0755)
check(err)
err = os.MkdirAll(path.Join(args.ToDir, args.ToMetadataSubDir), 0755)
check(err)

// 1. Create the category struct using the cowfile paths, pokemon names and indexes
categories := pokedex.CreateCategoryStruct(args.FromDir, cowfileFpaths, args.Debug)

// 2. Create the metadata files, containing name/category/japanese name info for each pokemon
metadata := pokedex.CreateMetadata(args.FromDir, cowfileFpaths, pokemonNames, args.Debug)

// categories is a PokemonTrie struct that will be written to a file using encoding/gob
// metadata is a list of pokemon data and an index to use when writing them to a file
// - this index matches a corresponding one in the categories struct
// - these files are embedded into the build binary using go:embed and then loaded at runtime
categories, metadata := pokedex.CreateMetadata(args.FromDir, fpaths, args.Debug)
// categories, metadata := pokedex.CreateMetadata(args.FromDir, cowfileFpaths, pokemonNames, args.Debug)

pokedex.WriteStructToFile(categories, categoryFpath)

fmt.Println("\nConverting cowfiles -> category & metadata GOB")
pbar := bin.NewProgressBar(len(fpaths))
pbar := bin.NewProgressBar(len(cowfileFpaths))
for _, m := range metadata {
pokedex.WriteBytesToFile(m.Data, pokedex.EntryFpath(path.Join(args.ToDir, args.ToDataSubDir), m.Index), true)
pokedex.WriteStructToFile(m.Metadata, pokedex.MetadataFpath(path.Join(args.ToDir, args.ToMetadataSubDir), m.Index))
pbar.Add(1)
}
pokedex.WriteBytesToFile([]byte(strconv.Itoa(len(metadata))), totalFpath, false)

fmt.Println("Finished converting", len(fpaths), "pokesprite -> cowfiles")
fmt.Println("Finished converting", len(cowfileFpaths), "pokesprite -> cowfiles")
fmt.Println("Wrote categories to", path.Join(args.ToDir, args.ToCategoryFname))
}
Loading

0 comments on commit e74a552

Please sign in to comment.