Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support filtering by both name and category #57

Merged
merged 7 commits into from
Mar 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ DEBUG=test go test -v ./test/
## TODO

- Short-term
- [ ] add short versions of command line args
- [ ] support long and short cli args (e.g. --name/-n)
- [ ] optionally print ID assigned to each pokemon, support deterministic selection via the same ID
- Longer-term
- Completed
- [x] Make the category struct faster to load - currently takes up to 80% of the execution time
Expand All @@ -168,3 +169,4 @@ DEBUG=test go test -v ./test/
- [x] Make data structure to hold categories, names and pokemon
- [x] Increase speed
- [x] Improve categories to be more specific than shiny/regular
- [x] Filter by both name and category
18 changes: 18 additions & 0 deletions pokesay.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,22 @@ func runPrintByCategory(args pokesay.Args) {
t.PrintJson()
}

func runPrintByNameAndCategory(args pokesay.Args) {
t := timer.NewTimer("runPrintByNameAndCategory", true)

names := pokedex.ReadStructFromBytes[map[string][]int](GOBAllNames)
t.Mark("read name struct")

metadata, final := pokesay.ChooseByNameAndCategory(names, args.NameToken, GOBCowNames, MetadataRoot, args.Category)
t.Mark("find and read metadata")

pokesay.Print(args, final.EntryIndex, GenerateNames(metadata, args), final.Categories, GOBCowData)
t.Mark("print")

t.Stop()
t.PrintJson()
}

func runPrintRandom(args pokesay.Args) {
t := timer.NewTimer("runPrintRandom", true)
choice := pokesay.RandomInt(pokedex.ReadIntFromBytes(GOBTotal))
Expand All @@ -175,6 +191,8 @@ func main() {
runListCategories()
} else if args.ListNames {
runListNames()
} else if args.NameToken != "" && args.Category != "" {
runPrintByNameAndCategory(args)
} else if args.NameToken != "" {
runPrintByName(args)
} else if args.Category != "" {
Expand Down
53 changes: 52 additions & 1 deletion src/pokesay/lookup.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,17 @@ func RandomInt(n int) int {
return rand.New(Rand).Intn(n)
}

// ChooseByCategory chooses a pokemon via a requested category
// 1. It loads the category search structure and finds the name of a random Pokemon matching the entry
// e.g. if given the category "small", this function might pick the file `1.cat` in
// - categories/
// - small/
// - 1.cat
// - 44.cat
//
// This file contains entries representing the <pokemon metadata index>/<the pokemon entry index>,
// e.g. "4/1" would represent 4.metadata, and the 2nd entry in that file
// 2. Using the indexes, load the corresponding metadata file and entry, and then return it
func ChooseByCategory(category string, categoryDir []fs.DirEntry, categoryFiles embed.FS, categoryRootDir string, metadataFiles embed.FS, metadataRootDir string) (pokedex.PokemonMetadata, pokedex.PokemonEntryMapping) {
if len(categoryDir) == 0 {
log.Fatalf("cannot find pokemon by category '%s'", category)
Expand Down Expand Up @@ -53,7 +64,7 @@ func ListNames(names map[string][]int) []string {
return pokedex.GatherMapKeys(names)
}

func ChooseByName(names map[string][]int, nameToken string, metadataFiles embed.FS, metadataRootDir string) (pokedex.PokemonMetadata, pokedex.PokemonEntryMapping) {
func fetchMetadataByName(names map[string][]int, nameToken string, metadataFiles embed.FS, metadataRootDir string) pokedex.PokemonMetadata {
match := names[nameToken]
if len(match) == 0 {
log.Fatalf("cannot find pokemon by name '%s'", nameToken)
Expand All @@ -64,10 +75,50 @@ func ChooseByName(names map[string][]int, nameToken string, metadataFiles embed.
metadataFiles,
pokedex.MetadataFpath(metadataRootDir, nameChoice),
)
return metadata

}

func ChooseByName(names map[string][]int, nameToken string, metadataFiles embed.FS, metadataRootDir string) (pokedex.PokemonMetadata, pokedex.PokemonEntryMapping) {
metadata := fetchMetadataByName(
names,
nameToken,
metadataFiles,
metadataRootDir,
)

// pick a random entry
choice := RandomInt(len(metadata.Entries))
return metadata, metadata.Entries[choice]
}

func ChooseByNameAndCategory(names map[string][]int, nameToken string, metadataFiles embed.FS, metadataRootDir string, category string) (pokedex.PokemonMetadata, pokedex.PokemonEntryMapping) {
// fetch the metadata of a pokemon matching the nameToken
metadata := fetchMetadataByName(
names,
nameToken,
metadataFiles,
metadataRootDir,
)

// now try and find a metadata entry that matches the requested category
matching := make([]pokedex.PokemonEntryMapping, 0)
for _, entry := range metadata.Entries {
for _, entryCategory := range entry.Categories {
if entryCategory == category {
matching = append(matching, entry)
}
}
}

// if the category is not found for this pokemon, return a random entry
if len(matching) == 0 {
return metadata, metadata.Entries[RandomInt(len(metadata.Entries))]
} else {
return metadata, matching[RandomInt(len(matching))]
}
}

func ChooseByRandomIndex(totalInBytes []byte) (int, int) {
total := pokedex.ReadIntFromBytes(totalInBytes)
return total, RandomInt(total)
Expand Down
15 changes: 15 additions & 0 deletions test/pokesay_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,21 @@ func TestChooseByCategory(test *testing.T) {
Assert(expectedEntry, entry, test)
}

func TestChooseByNameAndCategory(test *testing.T) {
names := make(map[string][]int)
names["hoothoot"] = []int{4}
metadata, entry := pokesay.ChooseByNameAndCategory(
names,
"hoothoot",
GOBCowNames,
"data/cows",
"small",
)

Assert("small", entry.Categories[0], test)
Assert("Hoothoot", metadata.Name, test)
}

func TestChooseByRandomIndex(test *testing.T) {
resultTotal, result := pokesay.ChooseByRandomIndex(GOBTotal)
Assert(9, resultTotal, test)
Expand Down
Loading