1 /// Acornsoft Elite space trading video game planet name generator
2 module namegen.elite;
3 
4 pure:
5 
6 const
7 {
8     ubyte galaxiesNum = 8;
9     ubyte galaxiesMask = 0x07;
10     ushort systemsPerGalaxy = 256;
11 
12     ushort[3] seed = [0x5A4A, 0x0248, 0xB753];
13 
14     string otherDigrams =
15         "ABOUSEITILETSTONLONUTHNO";
16 
17     // Dots should be nullprint characters
18     string planetDigrams =
19         "..LEXEGEZACEBISO"~
20         "USESARMAINDIREA."~
21         "ERATENBERALAVETI"~
22         "EDORQUANTEISRION";
23 
24     string[] governments =
25         [
26             "Anarchy",
27             "Feudal",
28             "Multi-gov",
29             "Dictatorship",
30             "Communist",
31             "Confederacy",
32             "Democracy",
33             "Corporate State",
34         ];
35 
36     string[] economies =
37         [
38             "Rich Industrial",
39             "Average Industrial",
40             "Poor Industrial",
41             "Mainly Industrial",
42             "Mainly Agricultural",
43             "Rich Agricultural",
44             "Average Agricultural",
45             "Poor Agricultural",
46         ];
47 }
48 
49 struct GalaxySeed
50 {
51     ushort[3] w;
52     alias w this;
53 
54     void tweakSeed() pure
55     {
56         ushort t = w[0];
57         t += w[1];
58         t += w[2];
59 
60         w[0] = w[1];
61         w[1] = w[2];
62         w[2] = t;
63     }
64 }
65 
66 GalaxySeed createGalaxySeed(ubyte galaxyNum)
67 {
68     assert(galaxyNum < 8);
69 
70     auto g = GalaxySeed(seed);
71 
72     while(galaxyNum > 0)
73     {
74         g[0].twist;
75         g[1].twist;
76         g[2].twist;
77 
78         galaxyNum--;
79     }
80 
81     return g;
82 }
83 
84 string makeName(ref GalaxySeed currSeed)
85 {
86     string name;
87 
88     const longName = currSeed.isLongName;
89 
90     foreach(_; 0 .. 3)
91     {
92         name ~= getPair(currSeed);
93         currSeed.tweakSeed;
94     }
95 
96     if(longName)
97         name ~= getPair(currSeed);
98 
99     currSeed.tweakSeed;
100 
101     return name;
102 }
103 
104 unittest
105 {
106     for(ubyte gal = 0; gal < galaxiesNum; gal++)
107     {
108         auto currSeed = createGalaxySeed(gal);
109 
110         foreach(i; 0 .. systemsPerGalaxy)
111         {
112             string name = makeName(currSeed);
113 
114             if(gal == 0)
115             {
116                 assert(i != 0 || name == "TIBEDIED", name);
117                 assert(i != 7 || name == "LAVE", name);
118             }
119 
120             if(gal == 1)
121             {
122                 assert(i != 2 || name == "BEEDBEON", name);
123             }
124         }
125     }
126 }
127 
128 private:
129 
130 void twist(ref ushort x)
131 {
132     x = rotatel((x >> 8) & 0xFF) * 256 + rotatel(x & 255);
133 }
134 
135 ubyte rotatel(ubyte b) /// rotate 8 bit number leftwards
136 {
137     b &= 0xFF;
138 
139     return (b & 127) << 1 | b >> 7;
140 }
141 
142 bool isLongName(in GalaxySeed seed)
143 {
144     return (seed.w[0] & 64) != 0;
145 }
146 
147 auto wrapSeedValueToDigramsIndex(ushort w)
148 {
149     return ((w >> 8) & 31) << 1;
150 }
151 
152 string getPair(in GalaxySeed seed)
153 {
154     size_t idx = seed.w[2].wrapSeedValueToDigramsIndex;
155     auto dg = planetDigrams[idx .. idx + 2];
156 
157     import std.string: replace;
158 
159     return dg.replace(".", "");
160 }