]> git.tdb.fi Git - r2c2.git/commitdiff
Add TrackLoopIter
authorMikko Rasa <tdb@tdb.fi>
Fri, 29 Oct 2010 21:30:03 +0000 (21:30 +0000)
committerMikko Rasa <tdb@tdb.fi>
Fri, 29 Oct 2010 21:30:03 +0000 (21:30 +0000)
Use it instead of manually maintaining visited sets

source/libmarklin/trackiter.cpp
source/libmarklin/trackiter.h
source/libmarklin/train.cpp
source/libmarklin/train.h

index 6ee7231f924ebc9ab953f22c6ca922db81d010e4..5f633997cc26e3dcdf0ac0354946325928f77377 100644 (file)
@@ -5,6 +5,7 @@ Copyright © 2010  Mikkosoft Productions, Mikko Rasa
 Distributed under the GPL
 */
 
+#include <algorithm>
 #include <msp/core/except.h>
 #include "track.h"
 #include "trackiter.h"
@@ -109,4 +110,54 @@ bool TrackIter::operator==(const TrackIter &other) const
        return _track==other._track && _entry==other._entry;
 }
 
+
+TrackLoopIter::TrackLoopIter():
+       _looped(false)
+{ }
+
+TrackLoopIter::TrackLoopIter(Track *t, unsigned e):
+       TrackIter(t, e),
+       _visited(new TrackList()),
+       _last(_visited->insert(_visited->end(), track())),
+       _looped(false)
+{ }
+
+TrackLoopIter::TrackLoopIter(const TrackIter &i):
+       TrackIter(i),
+       _visited(new TrackList()),
+       _last(_visited->insert(_visited->end(), track())),
+       _looped(false)
+{ }
+
+TrackLoopIter::TrackLoopIter(const TrackIter &i, RefPtr<TrackList> v, const TrackList::iterator &l):
+       TrackIter(i),
+       _looped(false)
+{
+       if(track())
+       {
+               _visited = v;
+               _last = l;
+               _looped = (_visited && find(_visited->begin(), _last, track())!=_last);
+
+               ++_last;
+               if(_last!=_visited->end())
+               {
+                       _visited = new TrackList(_visited->begin(), _last);
+                       _last = _visited->end();
+               }
+               _visited->push_back(track());
+               --_last;
+       }
+}
+
+TrackLoopIter TrackLoopIter::next() const
+{
+       return TrackLoopIter(TrackIter::next(), _visited, _last);
+}
+
+TrackLoopIter TrackLoopIter::next(unsigned path) const
+{
+       return TrackLoopIter(TrackIter::next(path), _visited, _last);
+}
+
 } // namespace Marklin
index c91a7689b1ffa4bdd29303da46036d2c4659eee2..e7560611277ea06c7a45a9fa68fa93327ea3e8f6 100644 (file)
@@ -43,9 +43,41 @@ public:
        Track &operator*() const;
        Track *operator->() const { return _track; }
        bool operator==(const TrackIter &) const;
+       bool operator!=(const TrackIter &other) const { return !(*this==other); }
        operator bool() const { return _track!=0; }
 };
 
+
+/**
+A track iterator that detects looping.
+
+A list of visited tracks is maintained internally to the iterator.  This list
+is shared between iterators as long as next() is only called once per iterator.
+Subsequent calls to next() cause the head of the list to be copied.
+*/
+class TrackLoopIter: public TrackIter
+{
+private:
+       typedef std::list<Track *> TrackList;
+
+       Msp::RefPtr<TrackList> _visited;
+       TrackList::iterator _last;
+       bool _looped;
+
+public:
+       TrackLoopIter();
+       TrackLoopIter(Track *, unsigned);
+       TrackLoopIter(const TrackIter &);
+private:
+       TrackLoopIter(const TrackIter &, Msp::RefPtr<TrackList>, const TrackList::iterator &);
+
+public:
+       bool looped() const { return _looped; }
+
+       TrackLoopIter next() const;
+       TrackLoopIter next(unsigned) const;
+};
+
 } // namespace Marklin
 
 #endif
index b87447f625fa31a897769987140d8da44b6e0c74..69620857a5064110d4d2a409d3e554cf365166f8 100644 (file)
@@ -266,51 +266,49 @@ bool Train::divert(Track &from)
        if(routes.empty())
                return false;
 
-       int path = -1;
-       unsigned from_ep = 0;
+       unsigned path = 0;
+       unsigned entry = 0;
        list<RouteRef>::iterator route = routes.begin();
-       BlockIter block = cur_blocks.back();
-       set<const Track *> visited;
 
        // Follow our routes to find out where we're entering the turnout
-       while(1)
+       for(TrackLoopIter track = cur_blocks.back().track_iter();;)
        {
-               block = block.next(route->route);
-
-               const Block::Endpoint &entry_ep = block->get_endpoints()[block.entry()];
-
-               if(visited.count(entry_ep.track))
-                       return false;
-               visited.insert(entry_ep.track);
-
-               if(!advance_route(route, *entry_ep.track))
+               if(!advance_route(route, *track))
                        return false;
 
-               if(entry_ep.track==&from)
+               if(&*track==&from)
                {
-                       if(block->get_train()==this && !free_block(*block))
+                       Block &block = track->get_block();
+                       if(block.get_train()==this && !free_block(block))
                                return false;
 
-                       from_ep = entry_ep.track_ep;
-                       path = route->route->get_turnout(from.get_turnout_id());
-                       break;
-               }
-       }
+                       int route_path = route->route->get_turnout(from.get_turnout_id());
 
-       // Check that more than one path is available
-       unsigned ep_paths = from.get_type().get_endpoints()[from_ep].paths;
-       if(!(ep_paths&(ep_paths-1)))
-               return false;
+                       // Check that more than one path is available
+                       unsigned ep_paths = track->get_type().get_endpoints()[track.entry()].paths;
+                       if(!(ep_paths&(ep_paths-1)))
+                               return false;
 
-       // Choose some other path
-       for(int i=0; ep_paths>>i; ++i)
-               if((ep_paths&(1<<i)) && i!=path)
-               {
-                       path = i;
+                       // Choose some other path
+                       for(int i=0; ep_paths>>i; ++i)
+                               if((ep_paths&(1<<i)) && i!=route_path)
+                               {
+                                       path = i;
+                                       break;
+                               }
+
+                       entry = track.entry();
                        break;
                }
 
-       TrackIter track = TrackIter(&from, from_ep).next(path);
+               unsigned tid = track->get_turnout_id();
+               track = track.next(tid ? route->route->get_turnout(tid) : 0);
+
+               if(!track || track.looped())
+                       return false;
+       }
+
+       TrackIter track = TrackIter(&from, entry).next(path);
        if(!track)
                return false;
 
@@ -325,7 +323,7 @@ bool Train::divert(Track &from)
        diversion->add_track(from);
        diversion->set_turnout(from.get_turnout_id(), path);
 
-       if(!is_valid_diversion(*diversion, from, from_ep))
+       if(!is_valid_diversion(*diversion, TrackIter(&from, entry)))
                return false;
 
        // Follow the diversion route until we get back to the original route
@@ -1270,48 +1268,52 @@ Route *Train::create_lead_route(Route *lead, const Route *target)
        return lead;
 }
 
-bool Train::is_valid_diversion(const Route &diversion, Track &from, unsigned from_ep)
+bool Train::is_valid_diversion(const Route &diversion, const TrackIter &from)
 {
        float diversion_len = 0;
-       TrackIter track(&from, from_ep);
-       while(diversion.has_track(*track))
+       TrackLoopIter track1 = from;
+       while(diversion.has_track(*track1))
        {
-               unsigned tid = track->get_turnout_id();
+               unsigned tid = track1->get_turnout_id();
                unsigned path = (tid ? diversion.get_turnout(tid) : 0);
-               diversion_len += track->get_type().get_path_length(path);
+               diversion_len += track1->get_type().get_path_length(path);
 
-               track = track.next(path);
+               track1 = track1.next(path);
 
-               if(&*track==&from)
+               if(track1.looped())
                        return false;
        }
 
        list<RouteRef>::iterator route = routes.begin();
-       if(!advance_route(route, from))
+       if(!advance_route(route, *from))
                return false;
 
-       set<Track *> visited;
        float route_len = 0;
-       track = TrackIter(&from, from_ep);
+       TrackLoopIter track2 = from;
        while(1)
        {
-               unsigned tid = track->get_turnout_id();
+               unsigned tid = track2->get_turnout_id();
                unsigned path = (tid ? route->route->get_turnout(tid) : 0);
-               route_len += track->get_type().get_path_length(path);
+               route_len += track2->get_type().get_path_length(path);
+
+               bool ok = (track2!=from && diversion.has_track(*track2));
 
-               if(&*track!=&from && diversion.has_track(*track))
+               track2 = track2.next(path);
+
+               if(ok)
                        break;
 
-               if(visited.count(&*track))
+               if(track2.looped())
                        return false;
-               visited.insert(&*track);
 
-               track = track.next(path);
-
-               if(!advance_route(route, *track))
+               if(!advance_route(route, *track2))
                        return false;
        }
 
+       // Must end up at the same place through both routes
+       if(track2!=track1)
+               return false;
+
        return diversion_len<route_len*1.2;
 }
 
index 8418d899c5d37980188d12bcc87c38d8dd4ff908..e55e36e2c40c973bab93fb20bfca0cf1d33622fa 100644 (file)
@@ -171,7 +171,7 @@ private:
        void reverse_blocks(BlockList &) const;
        bool advance_route(std::list<RouteRef>::iterator &, Track &);
        Route *create_lead_route(Route *, const Route *);
-       bool is_valid_diversion(const Route &, Track &, unsigned);
+       bool is_valid_diversion(const Route &, const TrackIter &);
 };
 
 } // namespace Marklin