#============================================================================== # # GaryCXJk - TriNameGenVXA v1.00 # * Last Updated: 2013.06.30 # * Level: Medium # * Requires: N/A # #============================================================================== $imported = {} if $imported.nil? $imported["CXJ-TriNameGenVXA"] = true #============================================================================== # # Changelog: # #------------------------------------------------------------------------------ # 2013.06.30 - v1.00 # # * Initial release # #============================================================================== # # Back when I was still a Minecraft modder I wanted to create something that # would be very useful to content creators, both Minecraft modders and regular # Java programmers. I originally created a name generator which was pretty # complicated. # # A year or two later I decided to simplify the script a little, and decided # to just start from scratch. When creating the first name generator, I had # already heard something about Markov Chains, so I decided to just do whatever # the hell I wanted (I don't need no stinkin' Markov Chains). Well, turns out # that this name generator might be more Markov Chain-y than I thought. # # So yeah, here's my very simple name generator. Mind you, it's simple in its # design. It doesn't mean the process of creating your own name set is. # #============================================================================== # # Installation: # # Make sure to put this below Materials, but above Main Process. # #============================================================================== # # Usage: # # You basically need one thing, and that is a list of names. This list can be # anything from human names to even cities and animals. You can even use # five letter words if you wanted to. You can create this list in two ways, # either as an array or as a text file, where each name is separated by a new # line. # # Now, you'll need to first create a dictionary object. # # dictionary = TNG_Dictionary.new # # Alternatively you could use optional parameters. # # dictionary = TNG_Dictionary.new(min_length, max_length, start_min, dict...) # # min_length and max_length define the minimum and maximum search length. # What it basically does is stitch two name parts that originally followed # eah other together. What the name generator does is splice each name in # groups of any amount of characters between min_length and max_length, and # for each name part stores what other name parts have followed. # # start_min determines the minimum length of the first name part. When set to # -1, this value would be min_length - 1. # # If you have previously created dictionaries and want to easily merge the two # name lists, you can also supply other dictionaries at the end. Do note that # name parts are not being merged with the dictionary. # # Next, add the names, using either method or both. # # dictionary.add_names(array) # dictionary.add_file(filename) # # After the names are added, the names need to be processed. # # dictionary.process_names # # Even after you've processed the names, though, you can still add names to be # processed. You do still need to process the names again though, although it # will skip over those that already have been processed. # # Finally, to generate names, you could call the next method. All parameters # are optional. # # dictionary.generate_name(max_namelength, min_namelength, rng, names_list) # # max_namelength and min_namelength define the minimum and maximum length of # the name. They default to 10 and 3 respectively. rng is a Random object. # If set to nil, it will create a new instance of Random. names_list is an # array where the generated name will be stored. When left empty, it will # use the generated names list inside the dictionary. This list can be # retrieved and cleared. # # dictionary.generated_names # dictionary.clear_generated # #============================================================================== # # License: # # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE # Version 2, December 2004 # # Copyright (C) 2013 Sam Hocevar # # Everyone is permitted to copy and distribute verbatim or modified # copies of this license document, and changing it is allowed as long # as the name is changed. # # DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE # TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION # # 0. You just DO WHAT THE FUCK YOU WANT TO. # # See http://www.wtfpl.net/ for more details. # #------------------------------------------------------------------------------ # Extra notes: # # This license was picked due to the fact that in my opinion this script # shouldn't be restricted by the creative commons license, which mostly still # requires you to attribute the content to the original creator. I also feel # like this script could help others understand certain mechanics and can # learn from it without the fear of violating a license due to similar coding. # # You can still give credits if you want though, and are free to pick the # following names when you give credit: # # * GaryCXJk # * Gary A.M. Kertopermono # * G.A.M. Kertopermono # * GARYCXJK # # To make it clear, this license allows you to DO WHAT THE FUCK YOU WANT TO, # which means you can use it in commercial as well as non-commercial products, # you can modify it, redistribute it, make toilet paper out of it, sell it on # eBay and win a presidential election with it. But it's mostly for making # games. There are no restrictions, no need to attribute regardless of the # aount of modifications made, you are allowed to remove all references to me, # you are allowed to change the license, although that's pretty much a dick # move (actually, I'm not sure if you can change the license or not, but let's # just not be dicks here, mmm'kay?), you are allowed to directly sell this # script or monetize on it on your own site. # # This script was originally hosted on: # http://area91.multiverseworks.com # #============================================================================== # # The code below should not be altered unless you know what you're doing. # #============================================================================== #============================================================================== # ** TNG_Part #------------------------------------------------------------------------------ # This defines a name part for TriNameGen. #============================================================================== class TNG_Part #-------------------------------------------------------------------------- # * Object Initialization #-------------------------------------------------------------------------- def initialize(part_string) @part_string = part_string end #-------------------------------------------------------------------------- # * Define if this name part can end the name #-------------------------------------------------------------------------- def set_can_end(can_end) @can_end = can_end end #-------------------------------------------------------------------------- # * Determine if this name part can end the name #-------------------------------------------------------------------------- def can_end? @can_end = false if @can_end.nil? return @can_end end #-------------------------------------------------------------------------- # * Add a name part as a possible follow-up #-------------------------------------------------------------------------- def add_part(part) @next_parts||= [] @next_parts.push(part) unless @next_parts.include?(part) end #-------------------------------------------------------------------------- # * Gets the amount of name parts that can follow #-------------------------------------------------------------------------- def length return @next_parts.size end #-------------------------------------------------------------------------- # * Returns a copy of the list of follow-up parts #-------------------------------------------------------------------------- def get_parts @next_parts||= [] return Array.new(@next_parts) end #-------------------------------------------------------------------------- # * Gets a copy of the part string #-------------------------------------------------------------------------- def part_string return String.new(@part_string) end end #============================================================================== # ** TNG_Dictionary #------------------------------------------------------------------------------ # This defines a dictionary for TriNameGen. This is basically the main class # you'll be working with. #============================================================================== class TNG_Dictionary #-------------------------------------------------------------------------- # * Object Initialization #-------------------------------------------------------------------------- def initialize(min_length = 2, max_length = 3, start_min = 1, *dictionary) @unprocessed_names = [] @processed_names = [] @generated_names = [] @start_parts = [] @part_dictionary = {} @min_length = (min_length <= 0 ? 2 : min_length) @max_length = (max_length < @min_length ? (@min_length == 2 ? 3 : @min_length) : max_length) @start_min = (start_min > 0 ? [start_min, @min_length].min : @min_length - 1) add_dictionary(dictionary) unless dictionary.nil? || dictionary.empty? end #-------------------------------------------------------------------------- # * Add the names of other dictionaries into this dictionary #-------------------------------------------------------------------------- def add_dictionary(*dictionaries) dictionaries.each do |dictionary| add_names(dictionary.base_names) end end #-------------------------------------------------------------------------- # * Add names to the unprocessed names list in this dictionary #-------------------------------------------------------------------------- def add_names(names) names.each do |name| next if names.empty? @unprocessed_names.push(name) unless base_names.include?(name) end end #-------------------------------------------------------------------------- # * Add names to the unprocessed names list in this dictionary from a # plaintext file #-------------------------------------------------------------------------- def add_file(filename) File.open(filename, "rb") do |f| data = f.read.split(/[\r\n]+/) end add_names(data) end #-------------------------------------------------------------------------- # * Get all names used to generate this library (both used and unused) #-------------------------------------------------------------------------- def base_names return @unprocessed_names + @processed_names end #-------------------------------------------------------------------------- # * Process the names list #-------------------------------------------------------------------------- def process_names while @unprocessed_names.size > 0 name = @unprocessed_names.shift process_name(name) @processed_names.push(name) end end #-------------------------------------------------------------------------- # * Process the current name #-------------------------------------------------------------------------- def process_name(name) proc = [[[@start_min, name.size].min, name, nil]] while proc.size > 0 while proc.size > 0 && proc[-1][0] > [proc[-1][1].size, @max_length].min proc.pop end next unless proc.size > 0 part_string = proc[-1][1][0, proc[-1][0]] if @part_dictionary.include?(part_string) part = @part_dictionary[part_string] else part = TNG_Part.new(part_string) @part_dictionary[part_string] = part end if !proc[-1][2].nil? proc[-1][2].add_part(part) else @start_parts.push(part) unless @start_parts.include?(part) end if proc[-1][1].size == proc[-1][0] part.set_can_end(true) proc[-1][0]+= 1 else name = proc[-1][1][part_string.size, proc[-1][1].size - part_string.size] proc.push([[@min_length, name.size].min, name, part]) proc[-2][0]+= 1 end end end #-------------------------------------------------------------------------- # * Get all parts #-------------------------------------------------------------------------- def get_parts return Array.new(@part_dictionary.values) end #-------------------------------------------------------------------------- # * Get all start parts #-------------------------------------------------------------------------- def start_parts return Array.new(@start_parts) end #-------------------------------------------------------------------------- # * Generate all possible names # Only advisable during development or to pre-generate a complete # names list #-------------------------------------------------------------------------- def generate_all_names(max_namelength = 10, min_namelength = 3, names_list = @generated_names) min_namelength = 3 if min_namelength < 2 max_namelength = min_namelength if max_namelength < min_namelength proc = [[start_parts, ""]] while !proc.empty? if proc[-1][0].empty? proc.pop next end part = proc[-1][0].shift name = proc[-1][1] + part.part_string if ((min_namelength)..max_namelength).include?(name.size) && part.can_end? && !names_list.include?(name) names_list.push(name) end next if name.size >= max_namelength index = [@min_length, name.size].min next_parts = [] while index <= [@max_length, name.size].min unless @part_dictionary.include?(name[-index, index]) index+= 1 next end parts = @part_dictionary[name[-index, index]].get_parts index+= 1 next if parts.empty? parts.each do |cpart| next if (name + cpart.part_string).size > max_namelength || next_parts.include?(cpart) next_parts.push(cpart) end end proc.push([next_parts, name]) end end #-------------------------------------------------------------------------- # * Generate name #-------------------------------------------------------------------------- def generate_name(max_namelength = 10, min_namelength = 3, rng = nil, names_list = @generated_names) min_namelength = 3 if min_namelength < 2 max_namelength = min_namelength if max_namelength < min_namelength rng = Random.new if rng.nil? proc = [[start_parts, ""]] while !proc.empty? if proc[-1][0].empty? proc.pop next end index = rng.rand(proc[-1][0].size) part = proc[-1][0].delete_at(index) name = proc[-1][1] + part.part_string rndn = rng.rand(2) if ((min_namelength)..max_namelength).include?(name.size) && part.can_end? && !names_list.include?(name) if rndn == 0 names_list.push(name) return name end proc[-1][0].push(part) end next if name.size >= max_namelength index = [@min_length, name.size].min next_parts = [] while index <= [@max_length, name.size].min next unless @part_dictionary.include?(name[-index, index]) parts = @part_dictionary[name[-index, index]].get_parts index+= 1 next if parts.empty? parts.each do |cpart| next if (name + cpart.part_string).size > max_namelength || next_parts.include?(cpart) next_parts.push(cpart) end end proc.push([next_parts, name]) end return "" end #-------------------------------------------------------------------------- # * Clear the default list of generated names #-------------------------------------------------------------------------- def clear_generated @generated_names.clear end def generated_names return Array.new(@generated_names) end end