1 /*
2 * Seven Kingdoms: Ancient Adversaries
3 *
4 * Copyright 2013 Richard Dijk <thor_madman@hotmail.com>
5 *
6 * This program is free software: you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 *
19 */
20
21 //Filename : OTownNetwork.cpp
22 //Description : Source file for TownNetwork, an extension to linked towns
23 // which is binary compatible with existing data by dynamically
24 // generating from existing data
25
26
27 #include <OTownNetwork.h>
28 #include <OTOWN.h>
29 #include <stack>
30
31 #ifdef _DEBUG
32 #define DEBUG_CHECK true
33 #define DEBUG_CHECK_WORK true
34 #else
35 #define DEBUG_CHECK false
36 #define DEBUG_CHECK_WORK false
37 #endif
38
39
TownNetwork(int nationRecno)40 TownNetwork::TownNetwork(int nationRecno) : _Nation(nationRecno), _MyRecno(0)
41 {
42 if (DEBUG_CHECK && nationRecno == 0) throw "nationRecno can not be 0 for a Town Network";
43 }
44
45
set_recno(int recno)46 void TownNetwork::set_recno(int recno)
47 {
48 if (DEBUG_CHECK && recno == 0) throw "My recno can not be 0";
49 else if (DEBUG_CHECK && _MyRecno != 0) throw "My recno has already been set";
50
51 _MyRecno = recno;
52 }
53
54
add_town(int townRecno)55 void TownNetwork::add_town(int townRecno)
56 {
57 if (DEBUG_CHECK)
58 {
59 if (townRecno == 0) throw "townRecno is 0";
60 else if (town_array[townRecno]->nation_recno != _Nation) throw "The town given by townRecno does not have the same nation as the network";
61 }
62
63 // If in heavy debugging phase, then ensure that townRecno is not already in the list
64 if (DEBUG_CHECK_WORK)
65 {
66 for (std::vector<int>::iterator it = _Towns.begin(); it != _Towns.end(); ++it)
67 if (*it == townRecno)
68 throw "The specified town is already in the network";
69 }
70
71 _Towns.push_back(townRecno);
72 town_array[townRecno]->town_network_recno = _MyRecno;
73 }
74
75
remove_town(int townRecno)76 void TownNetwork::remove_town(int townRecno)
77 {
78 if ( ! townRecno)
79 {
80 if (DEBUG_CHECK) throw "townRecno is 0";
81 else return;
82 }
83
84 // Need to find the town with the given recno and remove it
85 for (std::vector<int>::iterator it = _Towns.begin(); it != _Towns.end(); ++it)
86 {
87 if (*it == townRecno)
88 {
89 _Towns.erase(it);
90 return;
91 }
92 }
93
94 if (DEBUG_CHECK)
95 throw "TownNetwork::remove_town did not find an item with that record number";
96 }
97
98
99 // ---- Function merge_in ----
100 // Merges the Towns of the other Town network into this one.
101 // Used by TownNetworkArray.
102 //
merge_in(TownNetwork const * pNetwork)103 void TownNetwork::merge_in(TownNetwork const * pNetwork)
104 {
105 std::vector<int> const *pTowns = &pNetwork->_Towns;
106
107 if (DEBUG_CHECK && ((void*)pNetwork == (void*)this)) throw "Attempted to merge_in self";
108
109 for (std::vector<int>::const_iterator it = pTowns->begin(); it != pTowns->end(); ++it)
110 {
111 if (DEBUG_CHECK_WORK)
112 add_town(*it); // add_town does checks, such as uniqueness
113 else
114 {
115 _Towns.push_back(*it);
116 town_array[*it]->town_network_recno = _MyRecno;
117 }
118 }
119 }
120
121
122 // ---- Function split_by_pulsed ----
123 // Splits the current network into two parts, based on pulsed status of towns
124 //
125 // - splitDestination: the destination network to hold the towns that were split off
126 // - movePulsedTowns: if this is true, then towns that have their pulsed true are moved into splitDestination.
127 // If this is false, then towns that have their pulsed false are moved into splitDestination.
128 // - <returns>: The town network containing the non-pulsed Towns.
129 //
split_by_pulsed(TownNetwork * splitDestination,bool movePulsedTowns)130 TownNetwork* TownNetwork::split_by_pulsed(TownNetwork *splitDestination, bool movePulsedTowns)
131 {
132 // TODO: Change for greater efficiency: set _Towns[i] to 0 at first whilst splitting, then compact vector
133 // by performing a one-pass copy operation to move non-zero elements to the top, then call resize()
134
135 for (std::vector<int>::iterator it = _Towns.begin(); it != _Towns.end(); /* 'it' updated in loop */)
136 {
137 Town *pTown = town_array[*it];
138
139 if (pTown->town_network_pulsed == movePulsedTowns)
140 {
141 splitDestination->_Towns.push_back(*it);
142 pTown->town_network_recno = splitDestination->recno();
143 it = _Towns.erase(it);
144 }
145 else
146 ++it;
147 }
148
149 return (movePulsedTowns ? this : splitDestination);
150 }
151
152
153 // ---- Function reset_pulsed ----
154 // Resets pulsed value of each town in the town network. Called after pulsing event.
155 //
reset_pulsed()156 void TownNetwork::reset_pulsed()
157 {
158 for (std::vector<int>::iterator it = _Towns.begin(); it != _Towns.end(); ++it)
159 {
160 if (DEBUG_CHECK && !town_array[*it]->town_network_pulsed)
161 throw "Found unpulsed Town in the network ... Should have reached all in pulsing event";
162 town_array[*it]->town_network_pulsed = false;
163 }
164 }
165
166
167
168
169
170
171
172
173
174
175
176
177
178
TownNetworkArray()179 TownNetworkArray::TownNetworkArray() : DynArrayB(sizeof(TownNetwork*), 10, DEFAULT_REUSE_INTERVAL_DAYS)
180 {
181 }
182
~TownNetworkArray()183 TownNetworkArray::~TownNetworkArray()
184 {
185 deinit();
186 }
187
init()188 void TownNetworkArray::init()
189 {
190 }
191
192
deinit()193 void TownNetworkArray::deinit()
194 {
195 //----- delete TownNetwork objects ------//
196
197 if( size() > 0 )
198 {
199 TownNetwork* townNetworkPtr;
200
201 for( int i=1 ; i<=size() ; i++ )
202 {
203 townNetworkPtr = (TownNetwork*) get_ptr(i);
204
205 if( townNetworkPtr )
206 delete townNetworkPtr;
207 }
208
209 zap();
210 }
211 }
212
213
add_network(int nationRecno)214 TownNetwork* TownNetworkArray::add_network(int nationRecno)
215 {
216 TownNetwork *network = new TownNetwork(nationRecno);
217
218 linkin(&network);
219 network->set_recno(recno());
220
221 return network;
222 }
223
224
remove_network(int recno)225 void TownNetworkArray::remove_network(int recno)
226 {
227 if (recno < 1 || recno > size())
228 {
229 if (DEBUG_CHECK) throw "Invalid recno passed to remove_network";
230 else return;
231 }
232
233 TownNetwork *pNetwork = network(recno);
234 if (DEBUG_CHECK && pNetwork == NULL) throw "Town Network has already been removed";
235 delete pNetwork; // deleting null is safe
236 linkout(recno);
237 }
238
239
240 // ---- Function town_created ----
241 // Updates the town networks when a town is created
242 //
243 // - townRecno: the record number of the newly created town
244 // - nationRecno: the nation of the newly created town
245 // - linkedTowns: the list of linked towns
246 // - linkedCount: number of towns in the linked towns list
247 // - <returns>: the town network recno for the newly created town
248 //
town_created(int townRecno,int nationRecno,short const * linkedTowns,int linkedCount)249 int TownNetworkArray::town_created(int townRecno, int nationRecno, short const *linkedTowns, int linkedCount)
250 {
251 // Logic: iterate over all linked towns. First, find a town with the same
252 // nation, and add it to that network. Then, for the remaining links, if any same-nation town
253 // is in a different network then join these networks.
254
255 if (DEBUG_CHECK)
256 {
257 if (townRecno == 0) throw "townRecno is 0";
258 else if (linkedTowns == NULL) throw "linkedTowns is NULL";
259 else if (linkedCount < 0) throw "linkedCount is not a nonnegative integer";
260 }
261
262 // Independent towns do not form town networks
263 if (nationRecno == 0)
264 return 0;
265
266 int tnRecno = 0;
267 for (int i = 0; i < linkedCount; ++i)
268 {
269 Town *pTown = town_array[linkedTowns[i]];
270
271 if (pTown->nation_recno == nationRecno)
272 {
273 // Add some robustness for errors (in Release) by verifying and attempting to repair faults
274 verify_town_network_before_merge(nationRecno, pTown->town_recno);
275
276 // If it is the first own-nation town then add it to that network
277 if (tnRecno == 0)
278 {
279 tnRecno = pTown->town_network_recno;
280 network(tnRecno)->add_town(townRecno);
281 }
282 else if (pTown->town_network_recno != tnRecno)
283 {
284 // Found a different network that has now been connected to. Perform a merge.
285 tnRecno = merge(tnRecno, pTown->town_network_recno);
286 }
287 }
288 }
289
290 // If there were no network to connect to, create a new network
291 if (tnRecno == 0)
292 {
293 TownNetwork *pNetwork = add_network(nationRecno);
294 pNetwork->add_town(townRecno);
295 tnRecno = pNetwork->recno();
296 }
297
298 return tnRecno;
299 }
300
301
302 // ---- Function verify_town_before_merge ----
303 // Adds some robustness for errors by checking a town and fixing errors
304 // in the town network structure. Used only by town_create.
305 //
306 // - nationRecno: the nation recno of the town that is created
307 // - townRecno: the recno of the town to check. This should be a town linked to the newly created town.
308 //
verify_town_network_before_merge(int nationRecno,int townRecno)309 void TownNetworkArray::verify_town_network_before_merge(int nationRecno, int townRecno)
310 {
311 Town *pTown = town_array[townRecno];
312 int tnRecno = pTown->town_network_recno;
313
314 bool shouldFix = false;
315
316 if (tnRecno < 1 || tnRecno > size())
317 {
318 if (DEBUG_CHECK)
319 throw "town_created: one of the same-nation linked towns has an invalid town network recno";
320 else
321 shouldFix = true;
322 }
323 else if (network(tnRecno) == NULL)
324 {
325 if (DEBUG_CHECK)
326 throw "Town Network has been deleted, but towns still point to its recno";
327 else
328 shouldFix = true;
329 }
330
331 // Fixing errors in the town-network structure, by creating a new town network for the town
332 if (shouldFix)
333 {
334 TownNetwork *pTN = add_network(nationRecno);
335 pTN->add_town(townRecno);
336 }
337 }
338
339
340 // ---- Function merge ----
341 // Merges the two town-network, and returns the new recno for the network
342 //
343 // - tn1, tn2: the record numbers of the the towns networks
344 // - <returns>: the new town network recno for the combined network
345 //
merge(int tn1,int tn2)346 int TownNetworkArray::merge(int tn1, int tn2)
347 {
348 TownNetwork *pTN1 = network(tn1), *pTN2 = network(tn2), *pSwap;
349 if (pTN2->size() > pTN1->size())
350 pSwap = pTN1, pTN1 = pTN2, pTN2 = pSwap;
351
352 // Merge 2 into 1
353 pTN1->merge_in(pTN2);
354 remove_network(pTN2->recno()); pTN2 = NULL;
355
356 return pTN1->recno();
357 }
358
359
360 // ---- Function town_destroyed ----
361 // Updates the town networks when a town is destroyed
362 //
363 // - townRecno: the record number of the town.
364 //
town_destroyed(int townRecno)365 void TownNetworkArray::town_destroyed(int townRecno)
366 {
367 // Logic: Set Pulsing = false, and pulsed for the source Town to true. Remove source Town from network.
368 // Iterate over all linked towns of same nation:
369 // If not Pulsing, then Pulse link, which will recursively pulse all towns that are linked via any number of hops. Set Pulsing = true.
370 // Else if Pulsing, if linked town has been pulsed, continue. If it has not been pulsed, then split off all pulsed towns so far into a
371 // new town network; set Pulsing = false, and redo iteration for current linked town.
372 // Finally, reset pulsed for all towns in the original network to false.
373 // NOTE: Do not cache town-network numbers in this function, use pNetwork->town_network_recno instead.
374
375 if (townRecno == 0) {if (DEBUG_CHECK) throw "townRecno is 0"; else return;}
376
377 Town *pTown = town_array[townRecno];
378 if (pTown == NULL) {if (DEBUG_CHECK) throw "Town no longer exists in TownArray"; else return;}
379
380 // Independent towns do not form town networks
381 if (pTown->nation_recno == 0)
382 {
383 if (DEBUG_CHECK && pTown->town_network_recno != 0) throw "Found independent town with a non-trivial town network";
384 return;
385 }
386
387 if (DEBUG_CHECK && pTown->town_network_pulsed) throw "Town has town_network_pulsed to true outside pulsing session";
388
389
390 TownNetwork *pNetwork = network(pTown->town_network_recno); // The currently 'active' network (the one being pulsed), may change during the loop
391
392 // Remove the town from the town-network, before starting the rebuild. If this was the only town in it, then remove the network and return
393 pNetwork->remove_town(townRecno);
394 if (pNetwork->size() == 0)
395 {
396 remove_network(pNetwork->recno());
397 return;
398 }
399
400 // Keep track of all town networks involved, so the pulsed can be reset
401 TownNetwork *allNetworks[1 + MAX_LINKED_TOWN_TOWN], // Can only have as much disconnected parts as there can be links to the town
402 **pNextAllNetwork = allNetworks; // Fill pointer
403 *(pNextAllNetwork++) = NULL; // Set first element to 0, so it can be used as a end-of-list delimiter
404 *(pNextAllNetwork++) = pNetwork;
405
406
407 // Initialise pulsing-variables
408 int nationRecno = pTown->nation_recno;
409 bool Pulsing = false;
410 pTown->town_network_pulsed = true;
411 int pulsedCount = 0;
412
413 // Iterate over the linked towns
414 for (int i = 0; i < pTown->linked_town_count; ++i)
415 {
416 Town *pLinked = town_array[pTown->linked_town_array[i]];
417 if (pLinked->nation_recno != nationRecno) continue;
418
419 if (!Pulsing)
420 {
421 Pulsing = true;
422 pulsedCount = pulse(pLinked->town_recno, pLinked->nation_recno);
423 }
424 else if ( ! pLinked->town_network_pulsed)
425 {
426 // Found disconnected parts, so need to split off all pulsed. Note: this may cause the town-network-recno's of any of the towns
427 // in the original network to change, so the town network recno should not be cached.
428 TownNetwork *pNewNetwork = add_network(nationRecno);
429 *(pNextAllNetwork++) = pNewNetwork;
430 // Decide which part gets to stay in the original network by checking which is larger
431 bool movePulsed = (pulsedCount <= (pNetwork->size() / 2 + 1));
432 pNetwork = pNetwork->split_by_pulsed(pNewNetwork, movePulsed); // pNetwork is set to the network of unpulsed towns
433 // Redo iteration for current, now with pulsing false
434 Pulsing = false;
435 --i;
436 }
437 }
438
439 // Reset pulsed for all towns
440 while( *(--pNextAllNetwork) != NULL )
441 (*pNextAllNetwork)->reset_pulsed();
442 pTown->town_network_pulsed = false;
443 }
444
445
446 // ---- Function pulse ----
447 // Pulses the towns that are linked, and returns the amount of pulses set
448 //
449 // - <returns>: number of pulses set (towns that were already pulsed are not counted)
450 //
pulse(int townRecno,int nationRecno)451 int TownNetworkArray::pulse(int townRecno, int nationRecno)
452 {
453 std::stack<int> towns;
454 int pulsed = 0;
455
456 // Use a stack to implement the algorithm of successively pulsing each linked town
457 // without employing recursion
458
459 towns.push(townRecno);
460 while( towns.size() > 0 )
461 {
462 // Pop from the top
463 int activeRecno = towns.top();
464 towns.pop();
465 Town *pTown = town_array[activeRecno];
466 // If it has not yet been pulsed, do so now, and add linked towns to the stack
467 if (!pTown->town_network_pulsed)
468 {
469 pTown->town_network_pulsed = true;
470 ++pulsed;
471 for (int i = 0; i < pTown->linked_town_count; ++i)
472 {
473 // Only same-nation linked
474 if (town_array[ pTown->linked_town_array[i] ]->nation_recno == nationRecno)
475 towns.push(pTown->linked_town_array[i]);
476 }
477 }
478 }
479
480 return pulsed;
481 }
482
483 // ---- Function add_all_linked ----
484 // Adds all the linked towns (directly or indirectly) to the given town network.
485 //
486 // - townRecno: the town to start the link traversal for
487 // - pTownNetwork: pointer to the town-network to add all towns to; should be an empty network
488 //
add_all_linked(int townRecno,TownNetwork * pTownNetwork)489 void TownNetworkArray::add_all_linked(int townRecno, TownNetwork *pTownNetwork)
490 {
491 std::stack<int> towns;
492
493 if (DEBUG_CHECK && pTownNetwork->size() > 0)
494 throw "pTownNetwork does not point to a freshly created town network. Was this the intention?";
495 else if (DEBUG_CHECK && town_array[townRecno]->nation_recno != pTownNetwork->nation_recno())
496 throw "Start town nationality and Town Network nationality do not match";
497
498 // Use a stack to implement the algorithm of successively pulsing each linked town
499 // without employing recursion
500
501 towns.push(townRecno);
502 while( towns.size() > 0 )
503 {
504 // Pop from the top
505 int activeRecno = towns.top();
506 towns.pop();
507 Town *pTown = town_array[activeRecno];
508 // If it has not yet been pulsed, do so now, and add linked towns to the stack
509 if (!pTown->town_network_pulsed && pTown->nation_recno == pTownNetwork->nation_recno())
510 {
511 // Add town, and set its pulsed value
512 pTownNetwork->add_town(pTown->town_recno);
513 pTown->town_network_pulsed = true;
514 // Add linked towns
515 for (int i = 0; i < pTown->linked_town_count; ++i)
516 {
517 // Only same-nation linked
518 if (town_array[ pTown->linked_town_array[i] ]->nation_recno == pTownNetwork->nation_recno())
519 towns.push(pTown->linked_town_array[i]);
520 }
521 }
522 }
523
524 // Pulse value was used for each town in the network - reset it now
525 pTownNetwork->reset_pulsed();
526 }
527
528
529 // ---- Function town_pre_changing_nation ----
530 // Does the pre-update for the town networks when a town is about to change nation
531 //
532 // - townRecno: the record number of the town
533 //
town_pre_changing_nation(int townRecno)534 void TownNetworkArray::town_pre_changing_nation(int townRecno)
535 {
536 if (townRecno == 0) {if (DEBUG_CHECK) throw "townRecno is 0"; else return;}
537
538 Town *pTown = town_array[townRecno];
539 if (pTown == NULL) {if (DEBUG_CHECK) throw "Town no longer exists in TownArray"; else return;}
540
541 // Changing nation is equivalent to being destroyed (as old nation) and recreated as new nation.
542 // Pre-step is destroying
543 town_destroyed(townRecno);
544 pTown->town_network_recno = 0;
545 }
546
547
548 // ---- Function town_post_changing_nation ----
549 // Does the post-update for the town networks when a town has just changed nation
550 //
551 // - townRecno: the record number of the town
552 //
town_post_changing_nation(int townRecno,int newNationRecno)553 void TownNetworkArray::town_post_changing_nation(int townRecno, int newNationRecno)
554 {
555 if (townRecno == 0) {if (DEBUG_CHECK) throw "townRecno is 0"; else return;}
556
557 Town *pTown = town_array[townRecno];
558 if (pTown == NULL) {if (DEBUG_CHECK) throw "Town is no longer exists in TownArray"; else return;}
559
560 if (DEBUG_CHECK && pTown->nation_recno != newNationRecno) throw "Town has not yet changed nations (newNationsRecno != nation_recno), but town_post_changing_nation was called already";
561 else if (DEBUG_CHECK && pTown->town_network_recno != 0) throw "Town has changed to neutral but still has a town_network_recno";
562
563 // Changing nation is equivalent to being destroyed (as old nation) and recreated as new nation.
564 // Post-step is creating
565 town_created(townRecno, newNationRecno, pTown->linked_town_array, pTown->linked_town_count);
566 }
567
568
569 // ---- Function recreate_after_load ----
570 // Recreates the town network after TownArray has finished loading from file.
571 // Note: this function can only access the Towns and TownArray, as these are guaranteed to be loaded.
572 //
recreate_after_load()573 void TownNetworkArray::recreate_after_load()
574 {
575 for (int i = 1; i <= town_array.size(); ++i)
576 {
577 if (town_array.is_deleted(i)) continue; // Skip over empty elements
578 Town *pTown = town_array[i];
579
580 // Create town networks for each town without a town network.
581 // Independent towns do not form town networks
582 if (pTown->town_network_recno == 0 && pTown->nation_recno != 0)
583 {
584 TownNetwork *pNetwork = add_network(pTown->nation_recno);
585 // Add all (indirectly) linked towns to the network
586 add_all_linked(pTown->town_recno, pNetwork);
587 }
588 }
589 }