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 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,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
4
+
5
+ require 'pokemon_name_generator'
6
+
7
+ Dry::CLI.new(PokemonNameGenerator::CLI::Commands).call
@@ -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,5 @@
1
+ module PokemonNameGenerator
2
+ module CLI
3
+ require "pokemon_name_generator/cli/commands"
4
+ end
5
+ 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,7 @@
1
+ module Phoneme
2
+ DATA = "./data/phonemes.txt"
3
+
4
+ def self.load_data
5
+ File.readlines(DATA, chomp: true)
6
+ end
7
+ 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,3 @@
1
+ module PokemonNameGenerator
2
+ VERSION = "0.1.0"
3
+ 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: []