Nation Namer

  • September 26, 2021
  • admin
  • No Comments

(Originally published February 22, 2009)

Here’s a little algorithm I’ve been using to generate names for fake nations. I wouldn’t normally have put the randomness method directly into the factory class, and in my implementation I am using log4j, but for the purpose of this post, I decided it would be easy to put everything into one class with no external dependencies.

Some example names: Asmira, Pnohay, Litshala, Thudeacistan, Dismuy, Lienistan.

And the code:

/*
 * Created on 2009-01-25
 *
 * Earl Woodman
 * St. John's, NL
 */

package org.earljw.cultures;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
public class NationNameFactory
{
    private NationNameFactory()
    {
       
    }

    protected static final String consonants = "bcdfghjklmnpqrstvwxyz";
   
    protected static final CharSequence[] eligibleConsonantPairs = {
        "bh", "bl", "br", "bw", "by", "ch", "cl", "cr", "cw", "cy", "dh", "dr", "dw", "dy",
        "fh", "fl", "fr", "fw", "fy", "gh", "gl", "gn", "gr", "gw", "gy", "hw", "hy", "jw",
        "jy", "kh", "kl", "kr", "kw", "ky", "lh", "ly", "mc", "mn", "mw", "my", "ny", "ph",
        "pl", "pn", "pr", "pw", "py", "qh", "ql", "qr", "qw", "qy", "rh", "ry", "sc", "sh",
        "sk", "sl", "sm", "sn", "sp", "sq", "sr", "st", "sw", "sy", "th", "tl", "ts", "tw",
        "ty", "vh", "vl", "vr", "vw", "vy", "wh", "wr", "wy", "xy", "yb", "yc", "yd", "yf",
        "yg", "yl", "ym", "yn", "yp", "yq", "yr", "ys", "yt", "yv", "yx", "yz", "zh", "zl",
        "zy",
        "dl", "hl", "ml", "nl", "rl", "wl"
    };

    protected static final String   weightedAlphabet =
        "aaaaaaaaabbbcccddddeeeeeeeeeeffffgggghhhhhiiijklllllmmmmmnnnnnnnnooooooopppqrrrrrrrssssssstttttttuuuvwwxyyz";
    protected static final String[] extensions = {
            "ica",  "ia",  "land", "istan", "y",   "a",  "al",  "ala",  "ina",   "i",   "ey"
    };       
    protected static final String[] adjectiveExtensions = {
            "ican", "ian", "ish",  "i",     "ish", "an", "ese", "alan", "inian", "ese", ""
    };

    protected static final String vowels = "aeiou";
   
    protected static NationNameFactory nationNameFactory;
      
    public static synchronized NationNameFactory getNationNameFactory()
    {
        if( nationNameFactory == null )
        {
            nationNameFactory = new NationNameFactory();
        }
       
        return nationNameFactory;
    }

    /**
     * Returns a single nation name
     *
     * @return NationName
     */
    public NationName createNationName()
    {
        Collection<NationName> nationNames = createNationNames(1);
        if( nationNames.size() > 0 )
        {
            Iterator<NationName> iter = nationNames.iterator();
            return iter.next();
        }
        return null;
    }
   
    /**
     * Returns a collection of NationName objects
     *
     * @param nameCount
     * @return Collection<NationName>
     */
    public Collection<NationName> createNationNames(final int nameCount)
    {
        Collection<NationName> nationNames = new ArrayList<NationName>();
       
        int            chars;       
        StringBuffer   thisName;
        String         rootName;
        int            extensionPicked;

        NationName nationName;
       
        for( int i = 0; i < nameCount; i++ )
        {
            do
            {
                thisName = new StringBuffer();
                chars = getRandomInt(4, 8);
                for( int x = 0; x < chars; x++ )
                {
                    thisName.append(weightedAlphabet.charAt(getRandomInt(0, weightedAlphabet.length() - 1)));
                }               
                rootName = thisName.toString();
            }
            while( !isAcceptable(thisName.toString()) );
           
            extensionPicked = getRandomInt(0, extensions.length - 1);
            thisName.append(extensions[extensionPicked]);               
           
            // Capitalize the first letter
            String firstChar = thisName.substring(0, 1);
            firstChar = firstChar.toUpperCase();           
            thisName.setCharAt(0, firstChar.charAt(0));
            rootName = firstChar.charAt(0) + rootName.substring(1);

            // Create the nation name object
            nationName = new NationName();
            nationName.setAdjectiveName(rootName + adjectiveExtensions[extensionPicked]);
            nationName.setBaseName(thisName.toString());
            nationNames.add(nationName);           
        }
       
       
        for (Iterator<NationName> iterator = nationNames.iterator(); iterator.hasNext();)
        {
            NationName nationName2 = (NationName) iterator.next();
            System.out.println(nationName2.getBaseName() + ", " + nationName2.getAdjectiveName());
        }
       
        return nationNames;
       
    }

    protected static boolean isAcceptable(final String thisName)
    {
 
        // Check for 3 consecutive vowels
        int vowelCount = 0;
        for( int i = 0; i < thisName.length(); i++ )
        {
            if( vowels.indexOf(thisName.charAt(i)) != -1 )
            {
                vowelCount++;              
            }
            else
            {
                vowelCount = 0;
            }
            if( vowelCount == 3 )
            {
                System.out.println("Rejected (three vowels): " + thisName);
                return false;
            }
        }
    
        // Look for illegal consonant pairs
        boolean illegal = false;
        for( int j = 0; j < thisName.length() - 1; j++ )
        {
            // If the first two characters are consonants, make sure it's a valid
            // consonant pair. Otherwise move on to the next one
            if( consonants.indexOf(thisName.charAt(j)) != -1 && consonants.indexOf(thisName.charAt(j + 1)) != -1 )
            {
                illegal = true;
                for( int k = 0; k < eligibleConsonantPairs.length; k++ )
                {
                    if( thisName.subSequence(j, j + 2).equals(eligibleConsonantPairs[k]))
                    {
                        illegal = false;                       
                    }
                }
                if( illegal )
                {
                    break;
                }
            }
        }
       
        if( illegal )
        {
            System.out.println("Rejected (consonant pair): " + thisName);
            return false;           
        }
 
        // There may be more cases added here
        return true;       
    }
   
    protected int getRandomInt( final int lowerBound, final int upperBound )
    {
        if( lowerBound == upperBound )
            return lowerBound;
       
        double d = Math.random();
       
        return (int) ( (d * (upperBound-lowerBound) + lowerBound) + 0.5 );
    }

    public class NationName
    {
        protected String baseName;
       
        protected String adjectiveName;

        public String getBaseName()
        {
            return baseName;
        }

        public void setBaseName(final String baseName)
        {
            this.baseName = baseName;
        }

        public String getAdjectiveName()
        {
            return adjectiveName;
        }

        public void setAdjectiveName(final String adjectiveName)
        {
            this.adjectiveName = adjectiveName;
        }
    }
   
}

Drop Your Comment