HEAD: sfllaw/jim's patch for xplc detection broke xplc detection in
[wvapps.git] / twc / src / twcgenerator.cc
blob24bddfbb2d50fd94838674dad06ff66f5e0f2fe1
1 #include <wvlog.h>
2 #include <wvstring.h>
3 #include <uniconfroot.h>
5 #include <fcntl.h>
6 #include <unistd.h>
8 #include "twccontainer.h"
9 #include "twcgenerator.h"
10 #include "twcutils.h"
12 #include <strutils.h>
14 #define RANDOMIZE_SECTORS 1
16 TWCGenerator::TWCGenerator() : all_sectors(), planet_sectors(), port_sectors(), log("Generator", WvLog::Info)
18 log(WvLog::Info, "And there was light...\n");
22 TWCGenerator::~TWCGenerator()
24 Purge();
26 log(WvLog::Info, "Universe Generator collapsing in upon itself...\n");
30 unsigned int TWCGenerator::Connect(unsigned int from, unsigned int to, unsigned int dist)
32 if(from >= all_sectors.Count() || to >= all_sectors.Count())
34 log(WvLog::Error, "Out of range.\n");
35 exit(-1);
38 if(IsConnected(from, to))
40 log(WvLog::Info, "Sector %s is already conected to Sector %s\n", from, to);
41 return GENERATOR_SUCCESS;
43 else
45 log(WvLog::Info, "Connecting Sector %s to Sector %s\n", from, to);
46 all_sectors[from]->AddExit(all_sectors[to], dist);
47 return GENERATOR_SUCCESS;
52 unsigned int TWCGenerator::ConnectAllSectors(float stdev, float prob_bi_dir, unsigned int max_distance)
54 log(WvLog::Info, "Connecting Sectors.\n");
56 // connect all of the sectors in 'all_sectors' in order, thereby
57 // establishing a uni-directional, circular path through them.
58 for (unsigned int n = 0; n < all_sectors.Count() - 1; ++n)
60 log(WvLog::Debug1, "Connecting sector %s to sector %s\n", n, n+1);
62 if(Connect(n, n+1, 1) != GENERATOR_SUCCESS)
64 assert(0);
66 // all_sectors[i]->AddExit(all_sectors[i+1], 1);
68 // complete the circle...
70 if(all_sectors.Count() > 1)
72 log(WvLog::Debug1, "Connecting sector %s to sector %s\n", all_sectors.Count()-1, 0);
74 if(Connect(all_sectors.Count() - 1, 0, 1) != GENERATOR_SUCCESS)
76 assert(0);
80 // for each sector, establish a uni-directional link with other sectors
81 // (the number of connections is dictated by a pdf)
82 // (for now, just have each sector connected to 'degree'
83 // other sectors)
84 for (unsigned int n = 0; n < all_sectors.Count(); ++n)
86 log(WvLog::Debug, "Generating random connections for sector %s.\n", n);
88 unsigned int j = abs((int)(box_muller(0, stdev/2)));
90 // eliminate the chance that the generator will try to establish more
91 // exits than exist sectors
92 if(j >= all_sectors.Count() - 1)
94 // -1 because it can't connect to itself
95 j = all_sectors.Count() - 1;
98 // now, subtract the number of pre-existing connections
99 if(all_sectors[n]->NumExits() > j)
101 j = 0;
103 else
105 j -= all_sectors[n]->NumExits();
108 log(WvLog::Debug1, "This sector will have %s additional exit(s).\n", j);
110 for (; j > 0 ; --j)
112 unsigned int rnd = (rand() % (all_sectors.Count() - 1));
114 // don't let the sector connect to itself
115 if(rnd >= n)
117 ++rnd;
120 // don't let the sector have multiple connections to the same
121 // destination
122 while(IsConnected(n, rnd) == true)
124 log(WvLog::Debug1, "<!> Sector %s and sector %s are already connected. Try again.\n", n, rnd);
125 log(WvLog::Debug1, "<!> -->Need to establish %s more connections.\n", j);
127 rnd = (rand() % (all_sectors.Count() - 1));
128 if(rnd >= n)
130 ++rnd;
134 unsigned int dist;
135 dist = max_distance==0?0:(rand() % max_distance) + 1;
137 log(WvLog::Debug1, "Connecting sector %s to sector %s\n", n, rnd);
138 if(Connect(n, rnd, dist) != GENERATOR_SUCCESS)
140 assert(0);
143 #ifdef DEBUG
144 if(IsConnected(n, rnd) == false)
146 log(WvLog::Error, "Internal error. Aborting.\n");
147 abort();
149 #endif
150 // check for a bi-directional link
151 if(ranf() <= prob_bi_dir && IsConnected(rnd, n) == false)
153 log(WvLog::Debug1, "A bidirectional link will be made...");
154 log(WvLog::Debug1, "Connecting sector %s to sector %s\n", rnd, n);
155 if(Connect(rnd, n, dist) != GENERATOR_SUCCESS)
157 assert(0);
159 #ifdef DEBUG
160 if(IsConnected(rnd, n) == false)
162 log(WvLog::Error, "Internal error. Aborting.\n");
163 abort();
165 #endif
171 #if RANDOMIZE_SECTORS
172 // now, re-arrange the sectors to randomize their effective sector
173 // numbers (which are determined by their offset into the array).
175 log(WvLog::Debug1, "Reordering sectors...\n");
177 for (unsigned int n = 0; n < all_sectors.Count() * 2; ++n)
179 unsigned int rnd = rand() % (all_sectors.Count());
181 log(WvLog::Debug1, "%s ", rnd);
183 all_sectors.Add(all_sectors.Remove(all_sectors[rnd]));
186 log(WvLog::Debug1, "\n");
188 #endif
190 // renumber the sectors, starting at ID #1
191 for (unsigned int n = 0; n < all_sectors.Count(); ++n)
193 all_sectors[n]->id = n+1;
197 return GENERATOR_SUCCESS;
201 unsigned int TWCGenerator::GenerateSectors(unsigned int num)
203 log(WvLog::Info, "Generating %s Sectors.\n", num);
205 if(num == 0)
207 log(WvLog::Error, "How can I create 0 sectors?\n");
208 return GENERATOR_FAILURE;
210 else if(all_sectors.Count() != 0)
212 log(WvLog::Error, "Sectors have already been generated.\n");
213 return GENERATOR_FAILURE;
216 while(num > 0)
218 log(WvLog::Debug1, "Generating sector %s\n", --num);
220 all_sectors.Add(new Sector(num));
223 return GENERATOR_SUCCESS;
227 void TWCGenerator::Purge(void)
229 log(WvLog::Info, "Purging existing universe...\n");
231 port_sectors.Purge();
232 planet_sectors.Purge();
234 // destroy all of the sectors contained in 'all_sectors'
235 while(all_sectors.Count() != 0)
237 delete(all_sectors.Remove(all_sectors[0]));
241 static int numcmp(const UniConf &a, const UniConf &b)
243 return (a.key().printable().num() > b.key().printable().num());
246 WvString strip_slash(WvStringParm keyname)
248 if (*keyname.cstr() == '/')
250 WvString newname;
251 newname.setsize(keyname.len());
252 strcpy(newname.edit(), keyname.cstr() + 1);
253 return newname;
255 else
256 return keyname;
259 void TWCGenerator::DumpToFile(char* galaxy_file)
261 UniConfRoot cfg(WvString("ini:%s", galaxy_file));
263 int num_old_ports = cfg["global"]["ports"].getmeint();
265 cfg["global"]["sectors"].setmeint(all_sectors.Count());
266 cfg["global"]["planets"].setmeint(planet_sectors.Count());
267 cfg["global"]["ports"].setmeint(port_sectors.Count());
269 // erase all of the existing, relevant entries
270 // i.e.sectors, planets, ports
271 cfg["sectors"].remove();
272 cfg["planets"].remove();
274 // erase extra ports
275 for (int n = num_old_ports - port_sectors.Count(); n >= 0; --n)
277 cfg["ports"][num_old_ports--].remove();
280 // keep all other port info (buying, selling info)
281 // (Note: port location will be overwritten)
283 // iterate and place all ships in sector 1
284 // [sectors/1].ships ={...}
285 // [ships/n].location = 1
286 WvString ships("");
287 UniConf::SortedIter s(cfg["ships"], numcmp);
288 for (s.rewind(); s.next();)
290 int id = strip_slash(s->key()).num();
291 ships.append(WvString("%s ", id));
292 s()["location"].setme(1);
294 log(WvLog::Info, "Setting location of ship %s to %s\n",
295 id, cfg["ships"][WvString("%s",id)]["location"].getmeint());
298 ships.edit()[ships.len() - 1] = '\0';
299 cfg["sectors"][1]["ships"].setme(ships);
301 for (unsigned int from = 0; from < all_sectors.Count(); ++from)
303 WvString exits("");
304 WvString distances("");
306 // all sectors should have at least one exit.
307 if (all_sectors[from]->exits.Count() == 0)
309 log (WvLog::Error, "Sector %s has no exits!\n", WvString(all_sectors[from]->id));
310 exit(-1);
313 for (unsigned int exit_num = 0; exit_num < all_sectors[from]->exits.Count(); ++exit_num)
315 unsigned int to = (all_sectors[from]->exits)[exit_num]->destination->id;
316 unsigned int distance = (all_sectors[from]->exits)[exit_num]->distance;
318 log (WvLog::Info, "Sector %s is connected to Sector %s with distance %s\n",
319 WvString(all_sectors[from]->id),
320 WvString(to),
321 distance);
323 exits.append(WvString("%s ", to));
324 distances.append(WvString("%s ", distance));
326 exits.edit()[exits.len() - 1] = '\0';
327 distances.edit()[distances.len() - 1] = '\0';
329 //wvcon->print("Exits are: %s\n", exits);
330 //wvcon->print("Distances are: %s\n", distances);
331 cfg["sectors"][WvString("%s", all_sectors[from]->id)]["exits"].setme(exits);
332 cfg["sectors"][WvString("%s", all_sectors[from]->id)]["distances"].setme(distances);
335 for (unsigned int from = 0; from < planet_sectors.Count(); ++from)
337 cfg["sectors"][WvString("%s", planet_sectors[from]->id)]["planet"].setmeint(from + 1);
338 cfg["planets"][WvString("%s", from + 1)]["name"].setme(WvString("Planet %s", from + 1));
339 cfg["planets"][WvString("%s", from + 1)]["class"].setme("M");
342 for (unsigned int from = 0; from < port_sectors.Count(); ++from)
344 cfg["sectors"][WvString("%s", port_sectors[from]->id)]["port"].setmeint(from + 1);
345 cfg["ports"][WvString("%s", from + 1)]["type"].setmeint(1);
346 cfg["ports"][WvString("%s", from + 1)]["name"].setme(WvString("Port %s", from + 1));
347 cfg["ports"][WvString("%s", from + 1)]["location"].setme(WvString("%s", port_sectors[from]->id));
350 cfg.commit();
353 bool TWCGenerator::IsConnected(unsigned int from, unsigned int to)
355 return IsConnected(from, to, NULL);
358 bool TWCGenerator::IsConnected(unsigned int from, unsigned int to, unsigned int *distance)
360 if(from >= all_sectors.Count() || to >= all_sectors.Count())
362 return false;
365 return (IsConnected(all_sectors[from], all_sectors[to], distance));
368 bool TWCGenerator::IsConnected(Sector *from, Sector *to)
370 return (IsConnected(from, to, NULL));
373 bool TWCGenerator::IsConnected(Sector *from, Sector *to, unsigned int *distance)
375 assert(from);
376 assert(to);
378 if(from == to)
380 return false;
383 return (from->DoesExitExist(to, distance));
386 void TWCGenerator::PlaceRandomPlanets(unsigned int num)
388 log(WvLog::Info, "Randomly placing %s planets.\n", num);
390 if(num > all_sectors.Count())
392 log(WvLog::Error, "Cannot place %s planets in %s sectors.\n", num, all_sectors.Count());
393 exit(-1);
396 // place planets randomly -- later on I'll fix this up to make sure that they're
397 // fairly well distributed over the sector-space
398 while(num > 0)
400 unsigned int tmp = rand() % (all_sectors.Count());
401 unsigned int index;
403 while(planet_sectors.Contains(&index, all_sectors[tmp]))
405 tmp = rand() % (all_sectors.Count());
408 log(WvLog::Info, "Placing planet at sector %s\n", tmp);
410 PlacePlanet(tmp);
412 #ifdef DEBUG
413 printf("all_sectors[%d] = %p, planet_sectors[%d] = %p\n",
414 tmp, all_sectors[tmp],
415 planet_sectors.Count() - 1, planet_sectors[planet_sectors.Count()-1]);
416 #endif
418 --num;
422 void TWCGenerator::PlacePlanet(unsigned int sector)
424 planet_sectors.Add(all_sectors[sector]);
427 void TWCGenerator::PlacePortsAtPlanets()
429 // for each of the planets, place a port of type 'type'
430 for (int n = planet_sectors.Count() - 1; n >= 0; --n)
432 port_sectors.Add(planet_sectors[n]);