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 }