Re: [New release] Simutrans-Experimental 2.7
Reply #1 –
Hi James,
Regarding this part of code :
if (previous_connexion != NULL
&& (previous_best_line.is_bound() && previous_best_line == current_connexion->best_line)
|| (previous_best_convoy.is_bound() && previous_best_convoy == current_connexion->best_convoy))
{
continue;
}
I think the condition can be simplified to
( previous_best_line == current_connexion->best_line && previous_best_convoy == current_connexion->best_convoy )
The key points are, all null quickstone pointers have an internal entry value of 0, and that operator == is overloaded to compare only the equality of the entry values of 2 quickstone pointers.
(previous_connexion != NULL) can be dropped because, in case where there is no previous connexion, both previous_best_line and previous_best_convoy must be null quickstone pointers, and as long as the current_connexion has a defined (i.e. non-null) best_line or best_convoy, the whole expression will fail, and the remaining portion of the iteration will not be skipped.
previous_best_line.is_bound() and previous_best_convoy.is_bound() can be dropped because :
1) in case where one connexion has a defined best_line and the other connexion has a defined best_convoy, the defined best_line and the defined best_convoy are compared to a null pointer respectively and must evaluate to false, thus the remaining portion of the iteration will not be skipped.
2) in case where both connexions have a defined best_line, comparison between 2 null best_convoy pointers is always true, thus the whole expression depends on whether the 2 best_line pointers are the same
3) similar logic as in (2) can be applied to cases where both connexions have a defined best_convoy
Edit :
I have spot another problem :
const uint32 total_halts = alle_haltestellen.get_count();
const sint32 max_transfers = welt->get_einstellungen()->get_max_transfers();
const uint32 max_depth = total_halts * max_transfers;
const uint32 max_iterations = max_depth <= 65535 ? max_depth : 65535;
So, max_iterations is recalculated every time calculate_paths() is called. It is fine if the previous search has completed. But if you are to resume a previously uncompleted search, and if the total number of halts has increased since the last search, max_iterations will contain a number larger than the size of the previously created path_nodes, and there will be the risk of accessing memory beyond the boundary of path_nodes.
Thus, you need to check if previous search is complete [ open_list.empty() ]; if not, set max_iterations to current size of path_nodes.
While there should be room for improving and optimizing the route searching routines, it is an inherent limitation of the Dijkstra's Algorithm that, when the paths are marked stale, and a route from a certain halt to an unreachable destination is searched, the search will take a long time because it searches and creates all available paths and it will continue until either the open_list becomes empty or max_iterations is reached. That is probably the main reason why sometimes the game freezes.
I really like the idea that your implementation allows resumable search as well as search on demand. But search on demand may not always be able to spread the load of searching evenly across the time horizon, as in the case of searching for unreachable destinations.
The only partial solution I can think of is to make more use of resumable search -- you may regularly initiate route search in each step [ possibly called from halt.step() if rerouting of goods will not perform in that step ] provided that the paths are currently stale, but controlling the number of iterations performed in each of such calls. These calls do not have a defined target, but will perform as many iterations as specified before returning halfway through the search. In this way, when an unreachable destination is searched, the extra search time needed will likely be reduced. As I have said, it is just a partial solution, if an unreachable destination is searched immediately after the paths are marked stale, game freezing is still inevitable.
I am not sure if that will work, but there is a need to eliminate, or at least reduce, the game freezing problem.
BTW, is it possible to have individual options to switch on/off (not customize) each non-core ST EXP feature? Revenue and routing system is core, but weight limit and private cars is not. Some players may only want to play with a subset of ST EXP features, so it would be nice if there is an easy way for them to manipulate. Besides, it is easier to test and debug if we can disable certain features which interfere with testing results (e.g. processing speed) or make bugs harder to detect (because you don't know which feature is responsible for the bug/problem). Especially, city roads now have a low weight limit, and many vehicles are "crawling", causing serious overcrowding at the halts.