pokemon_name_generator 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/README.md +44 -0
- data/bin/pokeng +7 -0
- data/lib/pokemon_name_generator/cli/commands.rb +88 -0
- data/lib/pokemon_name_generator/cli.rb +5 -0
- data/lib/pokemon_name_generator/corpus.rb +46 -0
- data/lib/pokemon_name_generator/markov.rb +64 -0
- data/lib/pokemon_name_generator/naive.rb +28 -0
- data/lib/pokemon_name_generator/phoneme.rb +7 -0
- data/lib/pokemon_name_generator/pokemon.rb +10 -0
- data/lib/pokemon_name_generator/version.rb +3 -0
- data/lib/pokemon_name_generator.rb +9 -0
- metadata +54 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: a69c8787f7aae6ede03ceacb61be806b2ca42b10949657df5d1ddb4aba026d5b
|
4
|
+
data.tar.gz: b3dfe791b03f31d986f9abe4d6ab4fdb66c4bb102afaa14ebb55ddf5f121c314
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 0ae71b4218fd57a08cefe4717ebbff446007cf80bb15e1d877595035b32776f55a33553d1bc31609f94d9465d6770a12d122f71dffd3fdcf69e46779e63c7153
|
7
|
+
data.tar.gz: 2fd38d0192ff55b2c5a2cf13bec54491ece8a9eb3c5cd1b9019a8628a5358833deb150731d2eca55fa2934198de36eebe9443e55b4212d1fa9ad3d4eb6b1d05b
|
data/README.md
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
# Pokémon Name Generator
|
2
|
+
|
3
|
+
A command line ruby utility to generate fake (and sometimes accidentally real)
|
4
|
+
Pokémon names.
|
5
|
+
|
6
|
+
## Usage
|
7
|
+
|
8
|
+
Usage can be very simple.
|
9
|
+
|
10
|
+
```bash
|
11
|
+
$ bin/pokeng generate
|
12
|
+
```
|
13
|
+
|
14
|
+
You can choose between algorithms.
|
15
|
+
|
16
|
+
```bash
|
17
|
+
$ bin/pokeng generate --algorithm=naive
|
18
|
+
$ bin/pokeng generate --algorithm=markov
|
19
|
+
```
|
20
|
+
|
21
|
+
Some algorithms have additional configuration options.
|
22
|
+
|
23
|
+
```bash
|
24
|
+
$ bin/pokeng generate --algorithm=markov --context=2
|
25
|
+
```
|
26
|
+
|
27
|
+
You can also produce a number of names at once.
|
28
|
+
Useful for exporting to other apps.
|
29
|
+
|
30
|
+
```bash
|
31
|
+
$ bin/pokeng generate --algorithm=markov --context=3 --number=100000 > generated_names.txt
|
32
|
+
```
|
33
|
+
|
34
|
+
The default options are chosen to produce the most realistic names.
|
35
|
+
|
36
|
+
## Testing the Algorithm
|
37
|
+
|
38
|
+
You can put an algorithm through a testbed to find out how well it performs.
|
39
|
+
It counts how many times an actual Pokémon name is produced (from the test and
|
40
|
+
training data seperately) and how often a overly long or short name is produced.
|
41
|
+
|
42
|
+
```bash
|
43
|
+
$ bin/pokeng test --algorithm=markov --context=2 --number=500000
|
44
|
+
```
|
data/bin/pokeng
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
require "bundler/setup"
|
2
|
+
require "dry/cli"
|
3
|
+
|
4
|
+
module PokemonNameGenerator
|
5
|
+
module CLI
|
6
|
+
module Commands
|
7
|
+
extend Dry::CLI::Registry
|
8
|
+
|
9
|
+
class Version < Dry::CLI::Command
|
10
|
+
desc "Print version"
|
11
|
+
|
12
|
+
def call(*)
|
13
|
+
puts PokemonNameGenerator::VERSION
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class Generate < Dry::CLI::Command
|
18
|
+
desc "Generate one or more Pokémon Names"
|
19
|
+
|
20
|
+
option :algorithm, default: "markov", values: %w[naive markov], desc: "Algorithm to use"
|
21
|
+
|
22
|
+
option :context, type: :integer, default: 3, desc: "Number of links in the Markov Chain"
|
23
|
+
|
24
|
+
option :number, type: :integer, default: 1, desc: "Number of names to generate"
|
25
|
+
|
26
|
+
def call(**options)
|
27
|
+
corpus = Corpus.new
|
28
|
+
|
29
|
+
algorithm = case options.fetch(:algorithm)
|
30
|
+
when "naive" then Naive.new(corpus.pokemon_phonemes)
|
31
|
+
when "markov" then Markov.new(corpus.pokemon_phonemes, context_length: options.fetch(:context).to_i)
|
32
|
+
end
|
33
|
+
|
34
|
+
options.fetch(:number).to_i.times { puts algorithm.generate_name }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class Test < Dry::CLI::Command
|
39
|
+
desc "Test an algorithm"
|
40
|
+
|
41
|
+
option :algorithm, required: true, values: %w[naive markov], desc: "Algorithm to test"
|
42
|
+
|
43
|
+
option :context, required: true, type: :integer, desc: "Number of links in the Markov Chain"
|
44
|
+
|
45
|
+
option :number, type: :integer, default: 10_000, desc: "Number of names to generate"
|
46
|
+
|
47
|
+
def call(**options)
|
48
|
+
corpus = Corpus.new
|
49
|
+
actual_pokemon = corpus.pokemon
|
50
|
+
all_data = corpus.pokemon_phonemes.shuffle
|
51
|
+
|
52
|
+
longest_name = actual_pokemon.max_by(&:length)
|
53
|
+
shortest_name = actual_pokemon.min_by(&:length)
|
54
|
+
|
55
|
+
mid_point = (all_data.size + 1) / 2
|
56
|
+
training_data = all_data[..mid_point]
|
57
|
+
test_data = all_data[mid_point..]
|
58
|
+
|
59
|
+
algorithm = case options.fetch(:algorithm)
|
60
|
+
when "naive" then Naive.new(training_data)
|
61
|
+
when "markov" then Markov.new(training_data, context_length: options.fetch(:context).to_i)
|
62
|
+
end
|
63
|
+
|
64
|
+
puts "============================="
|
65
|
+
puts "🧪 Generator: #{algorithm.name}"
|
66
|
+
puts "============================="
|
67
|
+
|
68
|
+
generated_names = options.fetch(:number).to_i.times.map { algorithm.generate_name }
|
69
|
+
|
70
|
+
puts "Training Data: #{training_data.size}"
|
71
|
+
puts "Test Data: #{test_data.size}"
|
72
|
+
puts "Generated Names: #{generated_names.size}"
|
73
|
+
puts "Unique Generated Names: #{generated_names.uniq.size}"
|
74
|
+
puts "Training Data Names Generated: #{training_data.map { |datum| datum.join("") }.intersection(generated_names.uniq).size}"
|
75
|
+
puts "Test Data Names Generated: #{test_data.map { |datum| datum.join("") }.intersection(generated_names.uniq).size}"
|
76
|
+
puts "Test Data Names Generated: #{test_data.map { |datum| datum.join("") }.intersection(generated_names.uniq)}"
|
77
|
+
puts "Overly Short Names: #{generated_names.count { |name| name.size < shortest_name.size }}"
|
78
|
+
puts "Overly Long Names: #{generated_names.count { |name| name.size > longest_name.size }}"
|
79
|
+
puts ""
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
register "version", Version, aliases: ["-v", "--version"]
|
84
|
+
register "generate", Generate, aliases: ["g", "generate"]
|
85
|
+
register "test", Test, aliases: ["t", "test"]
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require_relative "./phoneme"
|
2
|
+
require_relative "./pokemon"
|
3
|
+
|
4
|
+
class Corpus
|
5
|
+
def pokemon_letters
|
6
|
+
@pokemon_letters ||= load_pokemon_letters.values
|
7
|
+
end
|
8
|
+
|
9
|
+
def pokemon_phonemes
|
10
|
+
@pokemon_phonemes ||= load_pokemon_phonemes.values
|
11
|
+
end
|
12
|
+
|
13
|
+
def phonemes
|
14
|
+
@phonemes ||= Phoneme.load_data
|
15
|
+
end
|
16
|
+
|
17
|
+
def pokemon
|
18
|
+
@pokemon ||= Pokemon.load_data
|
19
|
+
end
|
20
|
+
|
21
|
+
private
|
22
|
+
|
23
|
+
# This generates a hash so we can debug it easily
|
24
|
+
def load_pokemon_letters
|
25
|
+
pokemon.map { |name| [name, name.chars] }.to_h
|
26
|
+
end
|
27
|
+
|
28
|
+
# This generates a hash so we can debug it easily
|
29
|
+
def load_pokemon_phonemes
|
30
|
+
pokemon.map do |this_pokemon_name|
|
31
|
+
remaining_pokemon_name = this_pokemon_name
|
32
|
+
this_pokemon_phonemes = []
|
33
|
+
|
34
|
+
loop do
|
35
|
+
break if remaining_pokemon_name.empty?
|
36
|
+
|
37
|
+
phoneme = phonemes.find { |phoneme| remaining_pokemon_name.start_with?(phoneme) }
|
38
|
+
|
39
|
+
this_pokemon_phonemes << phoneme
|
40
|
+
remaining_pokemon_name = remaining_pokemon_name.sub(phoneme, "")
|
41
|
+
end
|
42
|
+
|
43
|
+
[this_pokemon_name, this_pokemon_phonemes]
|
44
|
+
end.to_h
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
class Markov
|
2
|
+
def initialize(training_data, context_length:)
|
3
|
+
@training_data = training_data
|
4
|
+
@context_length = context_length
|
5
|
+
end
|
6
|
+
|
7
|
+
def name
|
8
|
+
"Markov[#{@context_length}]"
|
9
|
+
end
|
10
|
+
|
11
|
+
def generate_name
|
12
|
+
pokemon_name = ""
|
13
|
+
context = []
|
14
|
+
current_phoneme = statistics[context].sample
|
15
|
+
|
16
|
+
loop do
|
17
|
+
break if current_phoneme.nil?
|
18
|
+
|
19
|
+
pokemon_name << current_phoneme
|
20
|
+
context << current_phoneme
|
21
|
+
context = context.drop(1) if context.size > context_length
|
22
|
+
current_phoneme = statistics[context].sample
|
23
|
+
end
|
24
|
+
|
25
|
+
pokemon_name
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
attr_reader :training_data, :context_length
|
31
|
+
|
32
|
+
def statistics
|
33
|
+
@statistics ||= load_statistics
|
34
|
+
end
|
35
|
+
|
36
|
+
def load_statistics
|
37
|
+
markov_statistics = {}
|
38
|
+
|
39
|
+
training_data.each do |this_pokemon_phonemes|
|
40
|
+
context = []
|
41
|
+
|
42
|
+
this_pokemon_phonemes.each do |this_pokemon_phoneme|
|
43
|
+
if markov_statistics[context]
|
44
|
+
markov_statistics[context] << this_pokemon_phoneme
|
45
|
+
else
|
46
|
+
markov_statistics[context] = [this_pokemon_phoneme]
|
47
|
+
end
|
48
|
+
|
49
|
+
context = [context, this_pokemon_phoneme].flatten
|
50
|
+
context = context.drop(1) if context.size > context_length
|
51
|
+
end
|
52
|
+
|
53
|
+
if markov_statistics[context]
|
54
|
+
markov_statistics[context] << nil
|
55
|
+
else
|
56
|
+
markov_statistics[context] = [nil]
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
markov_statistics[[]] = markov_statistics[[]].uniq
|
61
|
+
|
62
|
+
markov_statistics
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
class Naive
|
2
|
+
def initialize(training_data)
|
3
|
+
@training_data = training_data
|
4
|
+
end
|
5
|
+
|
6
|
+
def name
|
7
|
+
"Naïve"
|
8
|
+
end
|
9
|
+
|
10
|
+
def generate_name
|
11
|
+
statistics[:phoneme_count_distribution].sample.times.map { statistics[:phoneme_distribution].sample }.join
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
attr_reader :training_data, :context_length
|
17
|
+
|
18
|
+
def statistics
|
19
|
+
@statistics ||= load_statistics
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_statistics
|
23
|
+
{
|
24
|
+
phoneme_count_distribution: training_data.map(&:count),
|
25
|
+
phoneme_distribution: training_data.flatten
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
module Pokemon
|
2
|
+
DATA = "./data/pokemon.txt"
|
3
|
+
|
4
|
+
def self.load_data
|
5
|
+
File.readlines(DATA, chomp: true)
|
6
|
+
.map { |line| line[5..] } # drop the number
|
7
|
+
.map(&:downcase)
|
8
|
+
.reject { |name| name =~ /[♀♂.'2\-é:\ ]/ } # don't deal with special characters yet
|
9
|
+
end
|
10
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
module PokemonNameGenerator
|
2
|
+
require "pokemon_name_generator/cli"
|
3
|
+
require "pokemon_name_generator/corpus"
|
4
|
+
require "pokemon_name_generator/markov"
|
5
|
+
require "pokemon_name_generator/naive"
|
6
|
+
require "pokemon_name_generator/phoneme"
|
7
|
+
require "pokemon_name_generator/pokemon"
|
8
|
+
require "pokemon_name_generator/version"
|
9
|
+
end
|
metadata
ADDED
@@ -0,0 +1,54 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: pokemon_name_generator
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Tony Rowan
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2022-07-14 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description:
|
14
|
+
email: trowan812@gmail.com
|
15
|
+
executables:
|
16
|
+
- pokeng
|
17
|
+
extensions: []
|
18
|
+
extra_rdoc_files: []
|
19
|
+
files:
|
20
|
+
- README.md
|
21
|
+
- bin/pokeng
|
22
|
+
- lib/pokemon_name_generator.rb
|
23
|
+
- lib/pokemon_name_generator/cli.rb
|
24
|
+
- lib/pokemon_name_generator/cli/commands.rb
|
25
|
+
- lib/pokemon_name_generator/corpus.rb
|
26
|
+
- lib/pokemon_name_generator/markov.rb
|
27
|
+
- lib/pokemon_name_generator/naive.rb
|
28
|
+
- lib/pokemon_name_generator/phoneme.rb
|
29
|
+
- lib/pokemon_name_generator/pokemon.rb
|
30
|
+
- lib/pokemon_name_generator/version.rb
|
31
|
+
homepage: https://github.com/tony-rowan/pokemon-name-generator
|
32
|
+
licenses:
|
33
|
+
- MIT
|
34
|
+
metadata: {}
|
35
|
+
post_install_message:
|
36
|
+
rdoc_options: []
|
37
|
+
require_paths:
|
38
|
+
- lib
|
39
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '0'
|
44
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
45
|
+
requirements:
|
46
|
+
- - ">="
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
requirements: []
|
50
|
+
rubygems_version: 3.3.7
|
51
|
+
signing_key:
|
52
|
+
specification_version: 4
|
53
|
+
summary: A tool to create new Pokémon names
|
54
|
+
test_files: []
|