▶
Friday Facts #331 - 0.18.0 release Train pathfinder changes
Early this week we pushed the deploy button on 0.18.0. This was quite a surprise to many of our players, as more typically the time between major releases and the scope of the release is greater. However this isn't like the old days, we are trying to keep the size of releases as small as possible (FFF-314). What this means, is that what is currently in 0.18 is only really a small part of the work needed to be done on 0.18, and releases in the coming months will continue finishing off our 0.18 task list. Once everything on the 0.18 list is completed and the time is right, we will turn 0.18 into 1.0. What we have accomplished with 0.18.0:
Initially we planned for the Character GUI to be in the first 0.18 release. However the task was proving quite difficult the way it was written, and when the release date came it was not ready, so we delayed the entire 0.18.0 release (twice). After a tough and thorough review, we decided to discard most of the work, and start fresh with a different team member. With programming, you've got to know when to hold 'em, know when to fold 'em. With this change, we decided to release 0.18 without the Character GUI, the alternative was an additional 4-6 week delay, which with only 8 months left, is a big chunk of time. We also don't want to get back into the old habit of always delaying the release for another and another reason, and the things we present in Friday Facts not being seen in-game for months and months. After this decision, Dominik, who was working on the Character GUI, decided to leave the Factorio team.
In the recent FFF-329 we have mentioned two different approaches to the campaign. In this FFF, we are announcing that the campaign has been cancelled, and Ill try to explain why. After the Tutorial/NPE was cancelled, I realized several things and it made me re-evaluate what the campaign is trying to be, asking questions like:
Today I would like to talk about changes in the Train Pathfinder I made for 0.18. They are subtle changes to fix weird corner cases, but first a short glossary:
This was the issue that made me look into the pathfinder code. Train stops are always at the end of a segment. Segments can have at most 2 train stops, one for each direction of travel. Any more is not possible, since the train stop inside would split the segment into 2. This means that a train entering the last segment on its path, must travel the whole distance of it to reach its destination. As it was implemented, the total cost on nodes was computed as cost of all previous segments and transitions. That means the node with the requested train stop would be picked from a priority queue before the cost of the last segment distance was added. Since the node has the requested train stop on its end, the path would be returned even if there are other nodes with a higher current cost but would be better since the cost of the last segment would be lower. The cost of the current segment would be added to the new nodes during expansion but it was already too late - the path was returned.
0.17 - Pathfinder chooses the bottom path which is longer The fix was quite simple: include the node's segment length into the node's total cost from the start. This changed the node expansion code as it was not adding the cost of the current node's segment, but the next node's segment. This would lead to cases where the first segment distance would not be counted, so I added code that when creating starting nodes, creates them with the penalty of the whole starting segment.
0.18 - Fixed Pathfinder chooses upper path that is shorter
The second issue is also related to the last segment and has a similar explanation as the issue above. The cost of the train stop in the current segment was added only when expanding node to the next segments.
0.17 - The pathfinder chooses the bottom path because it does not contain the penalty of the opposite station The simple fix of shifting the train stop penalty by 1 segment (when expanding, add the cost of the train stops to the next segment, not the current segment) and including both train stops inside the start segment fixed the issue (this is not the final solution as it will be explained in issue 5).
0.18 - The fixed pathfinder chooses the upper path that is shorter. Both paths have a penalty of 1 station.
Now after the fix for issue 1 I noticed that adding the penalty of the whole starting segment has a flaw - if a train can go in both directions and both train ends are in the same segment, the cost of the first segment would be same in both directions and would cancel out.
0.17 - The pathfinder chooses to go left. Path to the right has a cost higher by 2. The fix here was simple: since the pathfinder is only given the start rail in the first segment, it has to go by all the rails in a given direction to find the end rail in a given segment. I have added the distance measurement during that search and used it as the initial cost. That way the position of train will affect the initial cost when looking at paths for both directions.
0.18 - the fixed pathfinder chooses to go to the right. The train position is considered.
Now that issue 3 was fixed, there happened to be extra code to handle single segment paths. It covers weird cases such as cyclic segment, or station in the same segment. This code is computing the path cost based on rails (not segments) and if it finds any path, it will choose the path of lowest cost and skip the main pathfinder logic for multi-segment paths.
0.17 - The pathfinder chooses the single segment path. The solution for this - if there exists a single segment path, create marker node with cost of shortest single segment path and throw it into pathfinder priority queue. If this single segment path is truly the shortest, then this marker node will be picked up during expansion meaning all the multi-segment paths have a higher cost. If there would be a shorter multi-segment path, this marker node will not be picked up in time and so will be ignored.
0.18 - The fixed pathfinder chooses the multi-segment path because it has a lower cost.
After the fix for issue 2, every segment on the path with train stops would add the penalty of the train stops, not counting if it was on an entrance or exit of the segment. This means that a double-sided train for which one end is inside the segment with a train stop that is under the train, would still consider that train stop in the penalty.
0.17 - The pathfinder chooses the left path since the right path has the penalty of the train stop Based on this, I decided to discard the penalty of opposite train stops in the first segment. Doing performance measurements here showed another issue: the fix for issue 2 was flawed. The penalty of the goal train stop in the last segment should not be added, because this forced the pathfinder to over-expand nodes up to the train stop cost looking for other paths that maybe would not have that last station penalty (when in fact, it was unavoidable). So now I saw: the opposite train stop in the first segment and the goal train stop in the last segment must not add a penalty. This gave the final solution for the train stop penalty: when expanding a node, the exit train stop in the current segment adds a penalty and opposite train stop in the next segment entrance adds a penalty.
0.18 - The fixed pathfinder chooses the right path. Train stop under the train is not counted.
This is quite subtle issue. It is related to this penalty rule: "When the rail block is occupied by a train -> Add a penalty of 2 * length of the block divided by block distance from the start, so the far away occupied paths don't matter much." This means that looking for path, not only the cost from the start is counted, but also the number of blocks from the start - every time a node expands to another segment that is from a different block than the previous node's segment, increase the total block count from start by 1. That was working fine as long node expansion would create a new node. In the case of updating a node, the total block count was not updated the leaving old value in the node. That means the updated node would have the cost of new, lower cost path, but would have the block distance from the start of the previous path.
0.17 - The pathfinder chooses the bottom path because blockDistanceFromStart is not properly updated in the upper contraption. In this contraption, the issue happens in the upper part. The segment under the upper middle locomotive is expanded first when going from the straight segment on its left - it was of lower cost. Now this segment has the cost of the straight path and the block distance from start of 1, since there was only 1 transition to a different block. There was also the penalty of the block being occupied by another train. When expansion goes through the upper siderail, it hits 4 rail signals and so the block distance from start is increased up to 4. This path has a higher cost up until the left rail signal near the upper middle locomotive. Here, however, the cost of the block occupied by another train is lower since we are entering the 5th block from start when going through the upper path. The side-rail path is chosen to update the node cost related to the segment with the upper middle locomotive, but the block distance from start was not updated. Because of this, the penalty on the next rail signal that goes to another occupied block, was equal to the last segment distance divided by 2 instead of 6. This difference would make the path through the bottom contraption of lower cost since the straight path here is cut and the node for the segment under the middle locomotive is created with the proper block distance from the start. The fix was simple: when changing the cost from the start on an existing node, also change the block distance from start.
0.18 - Fixed pathfinder chooses upper path
This is not exactly a pathfinder issue but it lead to trains being stuck. If a train is waiting on a signal, it will repath periodicaly to find another possible path that may now be unblocked. The repath however clears the counter of how long the train is waiting on the signal. Periodic repath logic was aware of this so it would restore the previous waiting on signal tick counter when the train entered arriving-to-signal state. However some fix ago changed train behavior, and the train no longer goes through the arriving-to-signal state and so the tick counter was cleared. This broke the following rule: "When the path includes a train currently waiting at a rail signal -> Add a penalty of 100 + 0.1 for every tick the train has already waited."
0.17 - The right train is stuck because the time-dependent penalty of the left train is reset every time the left train repaths. Universal fix here: clear this counter only if the train changes state to anything other than waiting-on-signal.
0.18 - The fixed ticks waiting on signal logic, after 3 repaths the penalty of left train increases to a point where right train chooses the long path.
In the heart of the pathfinder there is a priority queue that collects open nodes. As it was implemented up until now, the priority queue was based on a double-linked list. Finding the node with the lowest cost (highest priority) was fast (constant), but inserting new nodes or updating existing nodes would be, in worst case, linear (O(n)). After a quick prototyping phase, I decided to implement a binary heap with min-property over array. This change alone gave around a 20% speedup to the pathfinder.
The second optimisation applied was the addition of a heuristic function that gives the minimum cost to any end. This means that node expansion is now guided into the goal by the heuristic function, and so less nodes should be required to visit before finding the goal. This gave another performance increase. The rough guess as to impact of the train pathfinder changes, is that its about 2x faster, and should have fewer weird edge-cases. As always, lets us know what you think on our forum.
[ 2020-01-24 14:24:16 CET ] [ Original post ]
0.18.0 release (Klonan)
Early this week we pushed the deploy button on 0.18.0. This was quite a surprise to many of our players, as more typically the time between major releases and the scope of the release is greater. However this isn't like the old days, we are trying to keep the size of releases as small as possible (FFF-314). What this means, is that what is currently in 0.18 is only really a small part of the work needed to be done on 0.18, and releases in the coming months will continue finishing off our 0.18 task list. Once everything on the 0.18 list is completed and the time is right, we will turn 0.18 into 1.0. What we have accomplished with 0.18.0:
- GUI
- Main menu redesign
- GFX
- Water animation
- Tree animation
- Color correction (LUTs)
- New explosions and damage effects
- Other
- Optimizations
- New Particle system
- First work on new sound design
- Steam login
- GUI
- Character GUI
- Blueprint library
- Statistics GUIs (production, electric network stats, etc.)
- Entity GUIs (Inserter, Assembling machine, chests, etc.)
- Main screen GUIs (Chat, minimap, etc.)
- Many more...
- GFX
- Offshore pump redesign
- Assembling machine redesign
- Beacon redesign
- High-res icons
- Final tweaks and polish
- Other
- Further sound design improvements
- Mini-tutorials
- Replace NPE with old tutorial
- Final game balancing and tweaks
- Finalised locale and proofreading
Character GUI?
Initially we planned for the Character GUI to be in the first 0.18 release. However the task was proving quite difficult the way it was written, and when the release date came it was not ready, so we delayed the entire 0.18.0 release (twice). After a tough and thorough review, we decided to discard most of the work, and start fresh with a different team member. With programming, you've got to know when to hold 'em, know when to fold 'em. With this change, we decided to release 0.18 without the Character GUI, the alternative was an additional 4-6 week delay, which with only 8 months left, is a big chunk of time. We also don't want to get back into the old habit of always delaying the release for another and another reason, and the things we present in Friday Facts not being seen in-game for months and months. After this decision, Dominik, who was working on the Character GUI, decided to leave the Factorio team.
Campaign cancelled (V453000)
In the recent FFF-329 we have mentioned two different approaches to the campaign. In this FFF, we are announcing that the campaign has been cancelled, and Ill try to explain why. After the Tutorial/NPE was cancelled, I realized several things and it made me re-evaluate what the campaign is trying to be, asking questions like:
- Is the campaign meant to be 'The Way' how to play the game or just 'extra content'?
- Should the campaign try to mimic Freeplay?
- Is losing progress and starting from scratch that much of a problem?
- ...
Trains Pathfinder changes (boskid)
Today I would like to talk about changes in the Train Pathfinder I made for 0.18. They are subtle changes to fix weird corner cases, but first a short glossary:
- Rail - a single entity over which trains can ride. The basic unit of building.
- Segment - a series of rails that are not split in between by junctions, signals or stations. The basic unit for pathfinder.
- Block - a group of segments that are colliding or connected without signals in between. The basic unit for train reservations.
Issue 1: the last segment distance is not counted into penalty
This was the issue that made me look into the pathfinder code. Train stops are always at the end of a segment. Segments can have at most 2 train stops, one for each direction of travel. Any more is not possible, since the train stop inside would split the segment into 2. This means that a train entering the last segment on its path, must travel the whole distance of it to reach its destination. As it was implemented, the total cost on nodes was computed as cost of all previous segments and transitions. That means the node with the requested train stop would be picked from a priority queue before the cost of the last segment distance was added. Since the node has the requested train stop on its end, the path would be returned even if there are other nodes with a higher current cost but would be better since the cost of the last segment would be lower. The cost of the current segment would be added to the new nodes during expansion but it was already too late - the path was returned.
0.17 - Pathfinder chooses the bottom path which is longer The fix was quite simple: include the node's segment length into the node's total cost from the start. This changed the node expansion code as it was not adding the cost of the current node's segment, but the next node's segment. This would lead to cases where the first segment distance would not be counted, so I added code that when creating starting nodes, creates them with the penalty of the whole starting segment.
0.18 - Fixed Pathfinder chooses upper path that is shorter
Issue 2: the opposite station in the last segment is not counted into the penalty
The second issue is also related to the last segment and has a similar explanation as the issue above. The cost of the train stop in the current segment was added only when expanding node to the next segments.
0.17 - The pathfinder chooses the bottom path because it does not contain the penalty of the opposite station The simple fix of shifting the train stop penalty by 1 segment (when expanding, add the cost of the train stops to the next segment, not the current segment) and including both train stops inside the start segment fixed the issue (this is not the final solution as it will be explained in issue 5).
0.18 - The fixed pathfinder chooses the upper path that is shorter. Both paths have a penalty of 1 station.
Issue 3: the first segment distance would not consider train position
Now after the fix for issue 1 I noticed that adding the penalty of the whole starting segment has a flaw - if a train can go in both directions and both train ends are in the same segment, the cost of the first segment would be same in both directions and would cancel out.
0.17 - The pathfinder chooses to go left. Path to the right has a cost higher by 2. The fix here was simple: since the pathfinder is only given the start rail in the first segment, it has to go by all the rails in a given direction to find the end rail in a given segment. I have added the distance measurement during that search and used it as the initial cost. That way the position of train will affect the initial cost when looking at paths for both directions.
0.18 - the fixed pathfinder chooses to go to the right. The train position is considered.
Issue 4: a single segment path had priority over multi-segment paths
Now that issue 3 was fixed, there happened to be extra code to handle single segment paths. It covers weird cases such as cyclic segment, or station in the same segment. This code is computing the path cost based on rails (not segments) and if it finds any path, it will choose the path of lowest cost and skip the main pathfinder logic for multi-segment paths.
0.17 - The pathfinder chooses the single segment path. The solution for this - if there exists a single segment path, create marker node with cost of shortest single segment path and throw it into pathfinder priority queue. If this single segment path is truly the shortest, then this marker node will be picked up during expansion meaning all the multi-segment paths have a higher cost. If there would be a shorter multi-segment path, this marker node will not be picked up in time and so will be ignored.
0.18 - The fixed pathfinder chooses the multi-segment path because it has a lower cost.
Issue 5: the opposite station in the first segment was added into the penalty
After the fix for issue 2, every segment on the path with train stops would add the penalty of the train stops, not counting if it was on an entrance or exit of the segment. This means that a double-sided train for which one end is inside the segment with a train stop that is under the train, would still consider that train stop in the penalty.
0.17 - The pathfinder chooses the left path since the right path has the penalty of the train stop Based on this, I decided to discard the penalty of opposite train stops in the first segment. Doing performance measurements here showed another issue: the fix for issue 2 was flawed. The penalty of the goal train stop in the last segment should not be added, because this forced the pathfinder to over-expand nodes up to the train stop cost looking for other paths that maybe would not have that last station penalty (when in fact, it was unavoidable). So now I saw: the opposite train stop in the first segment and the goal train stop in the last segment must not add a penalty. This gave the final solution for the train stop penalty: when expanding a node, the exit train stop in the current segment adds a penalty and opposite train stop in the next segment entrance adds a penalty.
0.18 - The fixed pathfinder chooses the right path. Train stop under the train is not counted.
Issue 6: the block distance from the start was not updated properly
This is quite subtle issue. It is related to this penalty rule: "When the rail block is occupied by a train -> Add a penalty of 2 * length of the block divided by block distance from the start, so the far away occupied paths don't matter much." This means that looking for path, not only the cost from the start is counted, but also the number of blocks from the start - every time a node expands to another segment that is from a different block than the previous node's segment, increase the total block count from start by 1. That was working fine as long node expansion would create a new node. In the case of updating a node, the total block count was not updated the leaving old value in the node. That means the updated node would have the cost of new, lower cost path, but would have the block distance from the start of the previous path.
0.17 - The pathfinder chooses the bottom path because blockDistanceFromStart is not properly updated in the upper contraption. In this contraption, the issue happens in the upper part. The segment under the upper middle locomotive is expanded first when going from the straight segment on its left - it was of lower cost. Now this segment has the cost of the straight path and the block distance from start of 1, since there was only 1 transition to a different block. There was also the penalty of the block being occupied by another train. When expansion goes through the upper siderail, it hits 4 rail signals and so the block distance from start is increased up to 4. This path has a higher cost up until the left rail signal near the upper middle locomotive. Here, however, the cost of the block occupied by another train is lower since we are entering the 5th block from start when going through the upper path. The side-rail path is chosen to update the node cost related to the segment with the upper middle locomotive, but the block distance from start was not updated. Because of this, the penalty on the next rail signal that goes to another occupied block, was equal to the last segment distance divided by 2 instead of 6. This difference would make the path through the bottom contraption of lower cost since the straight path here is cut and the node for the segment under the middle locomotive is created with the proper block distance from the start. The fix was simple: when changing the cost from the start on an existing node, also change the block distance from start.
0.18 - Fixed pathfinder chooses upper path
Issue 7: repathing would clear a train's counter of ticks waiting on signal.
This is not exactly a pathfinder issue but it lead to trains being stuck. If a train is waiting on a signal, it will repath periodicaly to find another possible path that may now be unblocked. The repath however clears the counter of how long the train is waiting on the signal. Periodic repath logic was aware of this so it would restore the previous waiting on signal tick counter when the train entered arriving-to-signal state. However some fix ago changed train behavior, and the train no longer goes through the arriving-to-signal state and so the tick counter was cleared. This broke the following rule: "When the path includes a train currently waiting at a rail signal -> Add a penalty of 100 + 0.1 for every tick the train has already waited."
0.17 - The right train is stuck because the time-dependent penalty of the left train is reset every time the left train repaths. Universal fix here: clear this counter only if the train changes state to anything other than waiting-on-signal.
0.18 - The fixed ticks waiting on signal logic, after 3 repaths the penalty of left train increases to a point where right train chooses the long path.
Train Pathfinder optimisation - priority queue
In the heart of the pathfinder there is a priority queue that collects open nodes. As it was implemented up until now, the priority queue was based on a double-linked list. Finding the node with the lowest cost (highest priority) was fast (constant), but inserting new nodes or updating existing nodes would be, in worst case, linear (O(n)). After a quick prototyping phase, I decided to implement a binary heap with min-property over array. This change alone gave around a 20% speedup to the pathfinder.
Train Pathfinder optimisation - heuristic (conversion from Dijkstra to A*)
The second optimisation applied was the addition of a heuristic function that gives the minimum cost to any end. This means that node expansion is now guided into the goal by the heuristic function, and so less nodes should be required to visit before finding the goal. This gave another performance increase. The rough guess as to impact of the train pathfinder changes, is that its about 2x faster, and should have fewer weird edge-cases. As always, lets us know what you think on our forum.
[ 2020-01-24 14:24:16 CET ] [ Original post ]
Factorio
Wube Software LTD.
Developer
Wube Software LTD.
Publisher
2020-08-14
Release
Game News Posts:
506
🎹🖱️Keyboard + Mouse
Overwhelmingly Positive
(164072 reviews)
The Game includes VR Support
Public Linux Depots:
- Factorio Linux64 [306.86 M]
- Factorio Linux32 [300.1 M]
Available DLCs:
- Factorio: Space Age
Factorio is a game in which you build and maintain factories. You will be mining resources, researching technologies, building infrastructure, automating production and fighting enemies. In the beginning you will find yourself chopping trees, mining ores and crafting mechanical arms and transport belts by hand, but in short time you can become an industrial powerhouse, with huge solar fields, oil refining and cracking, manufacture and deployment of construction and logistic robots, all for your resource needs. However this heavy exploitation of the planet's resources does not sit nicely with the locals, so you will have to be prepared to defend yourself and your machine empire.
Join forces with other players in cooperative Multiplayer, create huge factories, collaborate and delegate tasks between you and your friends. Add mods to increase your enjoyment, from small tweak and helper mods to complete game overhauls, Factorio's ground-up Modding support has allowed content creators from around the world to design interesting and innovative features. While the core gameplay is in the form of the freeplay scenario, there are a range of interesting challenges in the form of the Scenario pack, available as free DLC. If you don't find any maps or scenarios you enjoy, you can create your own with the in-game Map Editor, place down entities, enemies, and terrain in any way you like, and even add your own custom script to make for interesting gameplay.
Discount Disclaimer: We don't have any plans to take part in a sale or to reduce the price for the foreseeable future.
Join forces with other players in cooperative Multiplayer, create huge factories, collaborate and delegate tasks between you and your friends. Add mods to increase your enjoyment, from small tweak and helper mods to complete game overhauls, Factorio's ground-up Modding support has allowed content creators from around the world to design interesting and innovative features. While the core gameplay is in the form of the freeplay scenario, there are a range of interesting challenges in the form of the Scenario pack, available as free DLC. If you don't find any maps or scenarios you enjoy, you can create your own with the in-game Map Editor, place down entities, enemies, and terrain in any way you like, and even add your own custom script to make for interesting gameplay.
Discount Disclaimer: We don't have any plans to take part in a sale or to reduce the price for the foreseeable future.
What people say about Factorio
- No other game in the history of gaming handles the logistics side of management simulator so perfectly. - Reddit
- I see conveyor belts when I close my eyes. I may have been binging Factorio lately. - Notch, Mojang
- Factorio is a super duper awesome game where we use conveyor belts to shoot aliens. - Zisteau, Youtube
MINIMAL SETUP
- OS: Linux (tarball installation)
- Processor: Dual core 3Ghz+Memory: 4 GB RAM
- Memory: 4 GB RAM
- Graphics: OpenGL 3.3 core. DirectX 10.1 capable GPU with 512 MB VRAM - GeForce GTX 260. Radeon HD 4850 or Intel HD Graphics 5500
- Storage: 3 GB available space
- OS: Linux (tarball installation)
- Processor: Quad core 3GHz+Memory: 8 GB RAM
- Memory: 8 GB RAM
- Graphics: OpenGL 4.3 core. DirectX 11 capable GPU with 2 GB VRAM - GeForce GTX 750 Ti. Radeon R7 360
- Storage: 3 GB available space
GAMEBILLET
[ 6102 ]
GAMERSGATE
[ 764 ]
FANATICAL BUNDLES
HUMBLE BUNDLES
by buying games/dlcs from affiliate links you are supporting tuxDB