private void MakeTunnels(Dictionary<string, XElement> provinceXml)
        {
            var todo = (from tunnel in provinceXml.Values
                           where tunnel.Element("type").Value == "tunnel" || tunnel.Element("type").Value == "chamber"
                           let routes = tunnel.Descendants("route")
                           select new TempProvince
                           {
                               Id = tunnel.Element("id").Value,
                               North = GetDirection(routes, "North"),
                               South = GetDirection(routes, "South"),
                               East = GetDirection(routes, "East"),
                               West = GetDirection(routes, "West"),
                               Up = GetDirection(routes, "Up"),
                               Down = GetDirection(routes, "Down"),
                               IsSeed = GetDirection(routes, "Up") != null
                           })
                          .ToDictionary(k => k.Id);

            if (!todo.Any())
                return;

            //starting points for each level
            var seeds = todo.Values.Where(t => t.IsSeed).Select(t => t.Id).ToArray();
            var tunnelDict = new Dictionary<string, TempProvince>(todo);

            //first find all the connected sets, i.e. levels, per seed, i.e. tunnel where you first enter that level
            var connectedSetsPerSeed = new Dictionary<string, List<TempProvince>>(); // the set at index i contains the id of the sewer of the tunnels originating there.
            foreach (var seed in seeds)
            {
                var connected = new List<TempProvince>();
                MakeConnectedSet(todo[seed], todo, connected);
                connectedSetsPerSeed.Add(seed, connected);
            }

            //now organize the connected sets according to the sewer they originate from (the string key), and level (index in the first list)
            var tunnelSets = new Dictionary<string, List<List<TempProvince>>>();
            foreach (var seed in seeds.Where(s => new Regex(@"\w\d\d\d").IsMatch(tunnelDict[s].Up))) //get all the sewers - basically entry to level 0
            {
                var levelsPerSewer = new List<List<TempProvince>>();
                levelsPerSewer.Add(connectedSetsPerSeed[seed]);
                string nextDownId = seed;
                List<TempProvince> nextDown = null;
                while (FindLevelDown(connectedSetsPerSeed[nextDownId], out nextDownId) && connectedSetsPerSeed.TryGetValue(nextDownId, out nextDown))
                {
                    //ok, we've got the next set
                    levelsPerSewer.Add(nextDown);
                }
                //levelsPerSewer is now filled up with levels from 0 -> n
                tunnelSets.Add(seed, levelsPerSewer);
            }

            //finally, lay them out per set left to right, level 0 -> n
            int startX = 0, startY = 0, bottomMost = 0;
            foreach (var sewer in tunnelSets.Keys)
            {
                foreach (var level in tunnelSets[sewer])
                {
                    var leftMost = level.Min(tp => tp.X);
                    var topMost = level.Min(tp => tp.Y);

                    foreach (var t in level)
                    {
                        t.X += (-leftMost + startX);
                        t.Y += (-topMost + startY);
                    }

                    //put all disconnected parts next to one another for now
                    var rightMost = level.Max(tp => tp.X);
                    bottomMost = Math.Max(bottomMost, level.Max(tp => tp.Y));
                    startX = (rightMost + 2);
                }
                //wrap
                startX = 0;
                startY = bottomMost + 2;
            }

            //}
            var tunnels = tunnelDict.Values;
            var right = tunnels.Max(tp => tp.X);
            var bottom = tunnels.Max(tp => tp.Y);
            width = right + 1;
            height = bottom + 1;
            provinces = new Province[width * height];
            for (int c = 0; c < width; c++)
                for (int r = 0; r < height; r++)
                {
                    var found = tunnels.FirstOrDefault(tun => tun.X == c && tun.Y == r);
                    if ((found != null))
                    {
                        provinces[Index(r, c)] = MakeProvince(provinceXml, found.Id, r, c);
                    }
                    else
                    {
                        provinces[Index(r, c)] = new UndergroundProvince
                        {
                            Id = "",
                            Name = "",
                            Height = length,
                            Width = length,
                            X = c * length,
                            Y = r * length
                        };
                    }
                }
        }
        private void MakeRegionKnownSize(Dictionary<string, XElement> provinceXml, string region, int xSize, int ySize)
        {
            // these are just the visited ones - for the neightboring ones we don't have a region.
            var todo = (from province in provinceXml.Values
                        where province.Element("region").Value == region || province.Element("name").Value == region //second is for Hades
                        let routes = province.Descendants("route")
                        select new TempProvince
                        {
                            Id = province.Element("id").Value,
                            North = GetDirection(routes, "North"),
                            South = GetDirection(routes, "South"),
                            East = GetDirection(routes, "East"),
                            West = GetDirection(routes, "West"),
                            IsSeed = province.Descendants("visit").Any() //we'll start to look from these preferentially, so that if a route is not bi
                            //directional as in Hades, we start from the best explored provinces. Otherwise pieces mights show as disconnected that reallyl aren't
                        })
                        .ToDictionary(k => k.Id);

            // now, for each of those provinces add the n,s,e,w routes that we know of
            var copy = new List<TempProvince>(todo.Values);
            foreach (var vis in copy)
            {
                if (vis.North != null && !todo.ContainsKey(vis.North))
                {
                    var province = provinceXml[vis.North];
                    var routes = province.Descendants("route");
                    todo.Add(vis.North, new TempProvince
                           {
                               Id = province.Element("id").Value,
                               North = GetDirection(routes, "North"),
                               South = GetDirection(routes, "South"),
                               East = GetDirection(routes, "East"),
                               West = GetDirection(routes, "West"),
                           });
                }
                if (vis.South != null && !todo.ContainsKey(vis.South))
                {
                    var province = provinceXml[vis.South];
                    var routes = province.Descendants("route");
                    todo.Add(vis.South,new TempProvince
                    {
                        Id = province.Element("id").Value,
                        North = GetDirection(routes, "North"),
                        South = GetDirection(routes, "South"),
                        East = GetDirection(routes, "East"),
                        West = GetDirection(routes, "West"),
                    });
                }
                if (vis.West != null && !todo.ContainsKey(vis.West))
                {
                    var province = provinceXml[vis.West];
                    var routes = province.Descendants("route");
                    todo.Add(vis.West,new TempProvince
                    {
                        Id = province.Element("id").Value,
                        North = GetDirection(routes, "North"),
                        South = GetDirection(routes, "South"),
                        East = GetDirection(routes, "East"),
                        West = GetDirection(routes, "West"),
                    });
                }
                if (vis.East != null && !todo.ContainsKey(vis.East))
                {
                    var province = provinceXml[vis.East];
                    var routes = province.Descendants("route");
                    todo.Add(vis.East,new TempProvince
                    {
                        Id = province.Element("id").Value,
                        North = GetDirection(routes, "North"),
                        South = GetDirection(routes, "South"),
                        East = GetDirection(routes, "East"),
                        West = GetDirection(routes, "West"),
                    });
                }
            }

            if (!todo.Any())
                return;

            //starting points
            var seeds = todo.OrderBy(v => v.Value.IsSeed ? 0 : 1).Select( v => v.Key).ToArray(); //we want the seeds first
            var allFaery = new Dictionary<string, TempProvince>(todo);

            //first find all the connected provinces to the first seed, removing seeds
            var connectedSetsPerSeed = new Dictionary<string, List<TempProvince>>(); // the set at index i contains the id of the sewer of the tunnels originating there.
            //while (todo.Any())
            foreach (var seed in seeds)
            {
                //var seed = todo.First().Key;
                if (todo.ContainsKey(seed))
                {
                    var connected = new List<TempProvince>();
                    MakeConnectedSet(todo[seed], todo, connected);
                    connectedSetsPerSeed.Add(seed, connected);
                }
            }

            //finally, lay them out per set left to right, level 0 -> n
            int startX = 0, startY = 0, bottomMost = 0;
            foreach (var connectedSet in connectedSetsPerSeed.Values)
            {
                var leftMost = connectedSet.Min(tp => tp.X);
                var topMost = connectedSet.Min(tp => tp.Y);

                foreach (var t in connectedSet)
                {
                    t.X += (-leftMost + startX);
                    t.Y += (-topMost + startY);
                    t.Y %= ySize; //faery is max 7 provinces high
                    t.X %= xSize;
                }

                //put all disconnected parts next to one another for now
                var rightMost = connectedSet.Max(tp => tp.X);
                bottomMost = Math.Max(bottomMost, connectedSet.Max(tp => tp.Y));
                startX = (rightMost + 2);

                //wrap
                //if (startX > 20)
                //{
                //    startX = 0;
                //    startY = bottomMost + 2;
                //}
            }

            //}
            var faeryProvinces = allFaery.Values;
            var right = faeryProvinces.Max(tp => tp.X);
            var bottom = faeryProvinces.Max(tp => tp.Y);
            width = right + 1;
            height = bottom + 1;
            provinces = new Province[width * height];
            for (int c = 0; c < width; c++)
                for (int r = 0; r < height; r++)
                {
                    var found = faeryProvinces.FirstOrDefault(tun => tun.X == c && tun.Y == r);
                    if ((found != null))
                    {
                        provinces[Index(r, c)] = MakeProvince(provinceXml, found.Id, r, c);
                    }
                    else
                    {
                        provinces[Index(r, c)] = new UndergroundProvince
                        {
                            Id = "",
                            Name = "",
                            Height = length,
                            Width = length,
                            X = c * length,
                            Y = r * length
                        };
                    }
                }
        }