private List <RefillFlowRecord.Path> CollectFromPos(Container2D_Rectangular container, FillInfo[,] fillInfos, int fromX, int fromY)
    {
        if (!container.IsLegalPosition(fromX, fromY) || null != container.GetSlot(fromX, fromY))
        {
            return(null);
        }
        var fi  = fillInfos[fromY, fromX];
        var ret = new List <RefillFlowRecord.Path>();

        var selfPos = new Pos2D(fromX, fromY);
        var self    = new RefillFlowRecord.Path();

        self.dst = selfPos;
        ret.Add(self);

        foreach (var child in fi.childrenPos)
        {
            var sub = CollectFromPos(container, fillInfos, child.x, child.y);
            if (null == sub)
            {
                continue;
            }
            ret.AddRange(sub);
        }
        foreach (var r in ret)
        {
            r.src = selfPos;
            r.movements.Add(selfPos);
        }
        return(ret);
    }
    private List <RefillFlowRecord.Path> CollectFillPathList(Container2D_Rectangular container, FillInfo[,] fillInfos)
    {
        var ret = new List <RefillFlowRecord.Path>();

        for (int x = 0; x < container.Width; x++)
        {
            var sub = CollectFromPos(container, fillInfos, x, container.Height - 1);
            if (null == sub)
            {
                continue;
            }
            sub.Sort((lhr, rhr) => {
                return((lhr.movements.Count > rhr.movements.Count) ? -1 : 1);
            });
            var len = sub.Count;
            for (int i = 0; i < len; i++)
            {
                var index = i + 1;
                var s     = sub[i];
                s.src = new Pos2D(x, container.Height - 1 + index);
                for (int j = 1; j <= index; j++)
                {
                    s.movements.Add(new Pos2D(x, container.Height - 1 + j));
                }
                s.movements.Reverse();
            }
            ret.AddRange(sub);
        }
        return(ret);
    }
	public override RefillFlowRecord Apply (Container2D_Rectangular container)
	{
		var key = GetType().Name + "-" + "RefillInfoMap";

		FillInfo[,] fillInfoMap = null;
		if (!container.UserData.ContainsKey(key))
		{
			List<Pos2D> pores = new List<Pos2D>();
			container.ForeachSlot((x, y, slot)=>{
				if (null == slot){
					pores.Add(new Pos2D(x, y));
					container.WrapperRect[y, x] = new SlotWrapper2D();
					container.WrapperRect[y, x].pos = new Pos2D(x, y);
				}
			});

			fillInfoMap = GenerateRefillTrendMap(container);
			container.UserData.Add(key, fillInfoMap);
			GlobalDebug.refillInfoMap = fillInfoMap;

			foreach (var p in pores)
			{
				container.ClearSlot(p.x, p.y);
			}
		}
		else
		{
			fillInfoMap = container.UserData[key] as FillInfo[,];
		}

		return DoApply(container, fillInfoMap);
	}
    private List <Pos2D> CollectRefillEnds(Container2D_Rectangular container, FillInfo[,] fillInfoMap)
    {
        var slots = container.WrapperRect;
        var ret   = new List <Pos2D>();

        container.ForeachSlot((x, y, slot) => {
            if (null != slot)
            {
                return;
            }
            if (fillInfoMap[y, x].IsOnSpot)
            {
                return;
            }
            foreach (var t in surroundingOffsets)
            {
                if (!container.IsLegalPosition(x + t.item1, y + t.item2))
                {
                    continue;
                }
                if (null != slots[y + t.item2, x + t.item1])
                {
                    continue;
                }
                var fi = fillInfoMap[y + t.item2, x + t.item1];
                if (!fi.IsOnSpot && fi.ancestorPos.x == x && fi.ancestorPos.y == y)
                {
                    return;
                }
            }
            ret.Add(new Pos2D(x, y));
        });
        return(ret);
    }
    public override RefillFlowRecord Apply(Container2D_Rectangular container)
    {
        var key = GetType().Name + "-" + "RefillInfoMap";

        FillInfo[,] fillInfoMap = null;
        if (!container.UserData.ContainsKey(key))
        {
            List <Pos2D> pores = new List <Pos2D>();
            container.ForeachSlot((x, y, slot) => {
                if (null == slot)
                {
                    pores.Add(new Pos2D(x, y));
                    container.WrapperRect[y, x]     = new SlotWrapper2D();
                    container.WrapperRect[y, x].pos = new Pos2D(x, y);
                }
            });

            fillInfoMap = GenerateRefillTrendMap(container);
            container.UserData.Add(key, fillInfoMap);
            GlobalDebug.refillInfoMap = fillInfoMap;

            foreach (var p in pores)
            {
                container.ClearSlot(p.x, p.y);
            }
        }
        else
        {
            fillInfoMap = container.UserData[key] as FillInfo[, ];
        }

        return(DoApply(container, fillInfoMap));
    }
Example #6
0
    private List <LMRecord2D_Retangular> SeekContainerLM(Container2D_Rectangular container)
    {
        var ret     = new List <LMRecord2D_Retangular>();
        var seekCtx = new LMSeeker2D_Retangular.SeekContext();

        seekCtx.container  = container;
        seekCtx.matchRules = matchRules;
        seekCtx.result     = ret;
        LMSeeker2D_Retangular.Seek(seekCtx);
        return(ret);
    }
Example #7
0
    private bool[,] CollectContainerEliminate(Container2D_Rectangular container,
                                              List <LMRecord2D_Retangular> lmRecords)
    {
        var eliminationCtx = new EliminationCollector2D_Rectangular.EliminateContext();

        eliminationCtx.container      = container;
        eliminationCtx.extensionRules = extensionRules;
        eliminationCtx.sandbox        = new bool[container.Height, container.Width];
        eliminationCtx.records        = lmRecords;
        EliminationCollector2D_Rectangular.CollectElimination(eliminationCtx);
        return(eliminationCtx.sandbox);
    }
Example #8
0
 public void Init(Container2D_Rectangular container, ViewLayout2DR layout)
 {
     this.layout = layout;
     cells       = new ViewCell[container.Height, container.Width];
     container.ForeachSlot((x, y, s) => {
         InsertCell(s.slotAttribute, new Pos2D(x, y));
     });
     if (drawCoordinate)
     {
         DrawCoordinates(container);
     }
 }
Example #9
0
	public void Init(Container2D_Rectangular container, ViewLayout2DR layout)
	{
		this.layout = layout;
		cells = new ViewCell[container.Height,container.Width];
		container.ForeachSlot((x, y, s)=>{
			InsertCell(s.slotAttribute, new Pos2D(x, y));
		});
		if (drawCoordinate)
		{
			DrawCoordinates(container);
		}
	}
Example #10
0
    private List <PLMRecord2D_Retangular> SeekContainerPLM(Container2D_Rectangular container)
    {
        var seekCtx = new PLMSeeker2D_Retangular.SeekContext();

        seekCtx.container      = container;
        seekCtx.matchRules     = matchRules;
        seekCtx.operationRules = operationRules;
        seekCtx.result         = new List <PLMRecord2D_Retangular>();
        PLMSeeker2D_Retangular.Seek(seekCtx);
        var ret = seekCtx.result;

        return(ret);
    }
Example #11
0
	void DrawCoordinates(Container2D_Rectangular container)
	{
		for (int y = 0; y < container.Height; y++)
		{
			for (int x = 0; x < container.Width; x++)
			{
				var tm = GameObject.Instantiate<TextMesh>(pfbText);
				tm.gameObject.SetActive(true);
				tm.text = "[" + x + "," + y + "]";
				var pos = layout.Logic2View(new Pos2D(x, y));
				tm.transform.SetParent(textRoot.transform, new Vector3(pos.x, pos.y, -1f), Vector3.one);
			}
		}
	}
Example #12
0
 void DrawCoordinates(Container2D_Rectangular container)
 {
     for (int y = 0; y < container.Height; y++)
     {
         for (int x = 0; x < container.Width; x++)
         {
             var tm = GameObject.Instantiate <TextMesh>(pfbText);
             tm.gameObject.SetActive(true);
             tm.text = "[" + x + "," + y + "]";
             var pos = layout.Logic2View(new Pos2D(x, y));
             tm.transform.SetParent(textRoot.transform, new Vector3(pos.x, pos.y, -1f), Vector3.one);
         }
     }
 }
Example #13
0
    private List <PLMRecord2D_Retangular> MakeContainerPlayable(Container2D_Rectangular container)
    {
        var records = SeekContainerPLM(container);

        if (records.Count >= minimalPlayablePLM)
        {
            return(records);
        }
        else
        {
            container.RecreateSubjects(true);
            MakeContainerStable(container);
            return(MakeContainerPlayable(container));
        }
    }
Example #14
0
    private void MakeContainerStable(Container2D_Rectangular container)
    {
        var lmRecords = SeekContainerLM(container);

        if (lmRecords.Count <= 0)
        {
            return;
        }
        var sandbox = CollectContainerEliminate(container, lmRecords);

        container.ForeachSlot((x, y, slot) => {
            if (sandbox[y, x])
            {
                container.ClearSlot(x, y);
                container.FillSlot(x, y);
            }
        });
        MakeContainerStable(container);
    }
Example #15
0
    private static bool JudgeOperable(SlotTrait matchingTrait,
                                      Container2D_Rectangular container,
                                      int xMask, int yMask,
                                      int xInMask, int yInMask,
                                      RuleMatchBasic2D_Rectangular match,
                                      RuleOperation2D_Rectangular operation,
                                      bool inverse)
    {
        int xRelative = inverse ? -operation.xRelative : operation.xRelative;
        int yRelative = inverse ? -operation.yRelative : operation.yRelative;

        int xTouch = xInMask + xRelative;
        int yTouch = yInMask + yRelative;

        if (yTouch >= 0 && yTouch < match.maskHeight &&
            xTouch >= 0 && xTouch < match.maskWidth &&
            match.PeekMask(xTouch, yTouch))
        {
            return(false);
        }

        var sniffX = xMask + xInMask + xRelative;
        var sniffY = yMask + yInMask + yRelative;

        if (!container.IsLegalPosition(sniffX, sniffY))
        {
            return(false);
        }

        var sniff = container.GetSlot(sniffX, sniffY);

        if (!sniff.IsTarget)
        {
            return(false);
        }

        return(matchingTrait.AbsoluteEqual(sniff.slotAttribute.trait));
    }
Example #16
0
 public abstract RefillFlowRecord Apply(Container2D_Rectangular container);
	private List<Pos2D> CollectRefillEnds(Container2D_Rectangular container, FillInfo[,] fillInfoMap)
	{
		var slots = container.WrapperRect;
		var ret = new List<Pos2D>();

		container.ForeachSlot((x, y, slot)=>{
			if (null != slot) return;
			if (fillInfoMap[y, x].IsOnSpot) return;
			foreach (var t in surroundingOffsets){
				if (!container.IsLegalPosition(x + t.item1, y + t.item2)) continue;
				if (null != slots[y + t.item2, x + t.item1]) continue;
				var fi = fillInfoMap[y + t.item2, x + t.item1];
				if (!fi.IsOnSpot && fi.ancestorPos.x == x && fi.ancestorPos.y == y)
				{
					return;
				}
			}
			ret.Add(new Pos2D(x, y));
		});
		return ret;
	}
	public abstract RefillFlowRecord Apply(Container2D_Rectangular container);
Example #19
0
 // Do nothing
 public override RefillFlowRecord Apply(Container2D_Rectangular container)
 {
     throw new NotImplementedException();
 }
	public abstract bool Apply(Container2D_Rectangular container,
	                           LMRecord2D_Retangular matchRecord,
	                           List<Pos2D> results);
	// Do nothing
	public override RefillFlowRecord Apply (Container2D_Rectangular container) 
	{
		throw new NotImplementedException();
	}
Example #22
0
	private static bool DryRun(PlayableScheme scheme, out string reason)
	{
		reason = "";
		if (scheme.canvasConfig is CanvasConfig2DR)
		{
			var container = new Container2D_Rectangular(scheme.canvasConfig as CanvasConfig2DR,
			                                            scheme.slotConfig);
			container.InitBlocks();

			var count1 = 0;
			var count2 = 0;
			for (int i = 0; i < 20; i++)
			{
				container.RecreateSubjects(true);
				var ctx1 = new LMSeeker2D_Retangular.SeekContext();
				ctx1.container = container;
				ctx1.matchRules = scheme.matchRules.SchemeStyleMap<RuleMatchBasic, RuleMatchBasic2D_Rectangular>((r)=>{
					var raw = r as RuleMatchBasic2D_Rectangular;
					var ret = new RuleMatchBasic2D_Rectangular();
					ret.mask = raw.mask;
					ret.maskWidth = raw.maskWidth;
					ret.maskHeight = raw.maskHeight;
					ret.Compile();
					return ret;
				}).ToArray();
				ctx1.result = new List<LMRecord2D_Retangular>();
				LMSeeker2D_Retangular.Seek(ctx1);
				if (ctx1.result.Count > 0)
				{
					count1++;
				}

				var ctx2 = new PLMSeeker2D_Retangular.SeekContext();
				ctx2.container = container;
				ctx2.matchRules = scheme.matchRules.SchemeStyleMap<RuleMatchBasic, RuleMatchBasic2D_Rectangular>((r)=>{
					var raw = r as RuleMatchBasic2D_Rectangular;
					var ret = new RuleMatchBasic2D_Rectangular();
					ret.mask = raw.mask;
					ret.maskWidth = raw.maskWidth;
					ret.maskHeight = raw.maskHeight;
					ret.Compile();
					return ret;
				}).ToArray();
				ctx2.operationRules = scheme.operationRules.SchemeStyleMap<RuleOperation, RuleOperation2D_Rectangular>((r)=>{
					return r as RuleOperation2D_Rectangular;
				}).ToArray();
				ctx2.result = new List<PLMRecord2D_Retangular>();
				PLMSeeker2D_Retangular.Seek(ctx2);
				if (ctx2.result.Count >= scheme.minimalPlayablePLM)
				{
					count2++;
				}
			}

			if (count1 < 2 || count2 < 2)
			{
				reason = "Scheme is NOT playable!";
				return false;
			}

			return true;
		}
		throw new NotImplementedException();
	}
Example #23
0
    private static bool DryRun(PlayableScheme scheme, out string reason)
    {
        reason = "";
        if (scheme.canvasConfig is CanvasConfig2DR)
        {
            var container = new Container2D_Rectangular(scheme.canvasConfig as CanvasConfig2DR,
                                                        scheme.slotConfig);
            container.InitBlocks();

            var count1 = 0;
            var count2 = 0;
            for (int i = 0; i < 20; i++)
            {
                container.RecreateSubjects(true);
                var ctx1 = new LMSeeker2D_Retangular.SeekContext();
                ctx1.container  = container;
                ctx1.matchRules = scheme.matchRules.SchemeStyleMap <RuleMatchBasic, RuleMatchBasic2D_Rectangular>((r) => {
                    var raw        = r as RuleMatchBasic2D_Rectangular;
                    var ret        = new RuleMatchBasic2D_Rectangular();
                    ret.mask       = raw.mask;
                    ret.maskWidth  = raw.maskWidth;
                    ret.maskHeight = raw.maskHeight;
                    ret.Compile();
                    return(ret);
                }).ToArray();
                ctx1.result = new List <LMRecord2D_Retangular>();
                LMSeeker2D_Retangular.Seek(ctx1);
                if (ctx1.result.Count > 0)
                {
                    count1++;
                }

                var ctx2 = new PLMSeeker2D_Retangular.SeekContext();
                ctx2.container  = container;
                ctx2.matchRules = scheme.matchRules.SchemeStyleMap <RuleMatchBasic, RuleMatchBasic2D_Rectangular>((r) => {
                    var raw        = r as RuleMatchBasic2D_Rectangular;
                    var ret        = new RuleMatchBasic2D_Rectangular();
                    ret.mask       = raw.mask;
                    ret.maskWidth  = raw.maskWidth;
                    ret.maskHeight = raw.maskHeight;
                    ret.Compile();
                    return(ret);
                }).ToArray();
                ctx2.operationRules = scheme.operationRules.SchemeStyleMap <RuleOperation, RuleOperation2D_Rectangular>((r) => {
                    return(r as RuleOperation2D_Rectangular);
                }).ToArray();
                ctx2.result = new List <PLMRecord2D_Retangular>();
                PLMSeeker2D_Retangular.Seek(ctx2);
                if (ctx2.result.Count >= scheme.minimalPlayablePLM)
                {
                    count2++;
                }
            }

            if (count1 < 2 || count2 < 2)
            {
                reason = "Scheme is NOT playable!";
                return(false);
            }

            return(true);
        }
        throw new NotImplementedException();
    }
	private List<RefillFlowRecord.Path> CollectFillPathList(Container2D_Rectangular container, FillInfo[,] fillInfos)
	{
		var ret = new List<RefillFlowRecord.Path>();
		for (int x = 0; x < container.Width; x++)
		{
			var sub = CollectFromPos(container, fillInfos, x, container.Height - 1);
			if (null == sub) continue;
			sub.Sort((lhr, rhr)=>{
				return (lhr.movements.Count > rhr.movements.Count) ? -1 : 1;
			});
			var len = sub.Count;
			for (int i = 0; i < len; i++)
			{
				var index = i + 1;
				var s = sub[i];
				s.src = new Pos2D(x, container.Height - 1 + index);
				for (int j = 1; j <= index; j++)
				{
					s.movements.Add(new Pos2D(x, container.Height - 1 + j));
				}
				s.movements.Reverse();
			}
			ret.AddRange(sub);
		}
		return ret;
	}
 public abstract bool Apply(Container2D_Rectangular container,
                            LMRecord2D_Retangular matchRecord,
                            List <Pos2D> results);
	private RefillFlowRecord DoApply(Container2D_Rectangular container, FillInfo[,] fillInfoMap)
	{
		var ret = new RefillFlowRecord();
		var slots = container.WrapperRect;

		container.ForeachSlot((x, y, slot)=>{
			if (null != slot) return;
			var fi = fillInfoMap[y, x];
			if (fi.IsOnSpot){
				ret.OnSpotList.Add(new Pos2D(x, y));
			}
		});
		var ends = CollectRefillEnds(container, fillInfoMap);
		var dir = new Dictionary<int, Tuple<SlotWrapper2D, int>>();
		foreach (var e in ends)
		{
			var pos = fillInfoMap[e.y, e.x].ancestorPos;
			int inverseDepth = 0;
			while (container.IsLegalPosition(pos.x, pos.y))
			{
				var s = slots[pos.y, pos.x];
				if (null != s)
				{
					inverseDepth++;
					if (!dir.ContainsKey(s.Trait))
					{
						dir.Add(s.Trait, new Tuple<SlotWrapper2D, int>(s, inverseDepth));
					}
					else if (dir[s.Trait].item2 < inverseDepth)
					{
						dir[s.Trait].item2 = inverseDepth;
					}
				}
				pos = fillInfoMap[pos.y, pos.x].ancestorPos;
			}
		}
		var list = new List<Tuple<SlotWrapper2D, int>>();
		foreach (var kvp in dir)
		{
			list.Add(kvp.Value);
		}
		list.Sort((lhr, rhr)=>{
			return (lhr.item2 < rhr.item2) ? -1 : 1;
		});

		foreach (var t in list)
		{
			var src = t.item1.pos;
			var moveTo = DepthFirstSearch(slots, fillInfoMap, src.x, src.y);
			var dst = moveTo.item1;

			var path = new RefillFlowRecord.Path();
			path.src = src.Clone();
			path.dst = dst.Clone();

			container.SwapSlot(src.x, src.y, dst.x, dst.y);
			var cur = path.dst;
			do {
				path.movements.Add(cur);
				cur = fillInfoMap[cur.y, cur.x].ancestorPos;
			} while (cur != path.src);

			path.movements.Add(path.src);
			path.movements.Reverse();

			ret.NonFillMovements.Add(path);
		}

		ret.FillMovements = CollectFillPathList(container, fillInfoMap);

		return ret;
	}
	private List<RefillFlowRecord.Path> CollectFromPos(Container2D_Rectangular container, FillInfo[,] fillInfos, int fromX, int fromY)
	{
		if (!container.IsLegalPosition(fromX, fromY) || null != container.GetSlot(fromX, fromY)) return null;
		var fi = fillInfos[fromY, fromX];
		var ret = new List<RefillFlowRecord.Path>();

		var selfPos = new Pos2D(fromX, fromY);
		var self = new RefillFlowRecord.Path();
		self.dst = selfPos;
		ret.Add(self);

		foreach (var child in fi.childrenPos)
		{
			var sub = CollectFromPos(container, fillInfos, child.x, child.y);
			if (null == sub) continue;
			ret.AddRange(sub);
		}
		foreach (var r in ret)
		{
			r.src = selfPos;
			r.movements.Add(selfPos);
		}
		return ret;
	}
	private FillInfo[,] GenerateRefillTrendMap(Container2D_Rectangular container)
	{
		var ret = new FillInfo[container.Height, container.Width];
		var slots = container.WrapperRect;
		Action<int, int, List<SlotWrapper2D>> picker = (x, y, list)=>{
			if (x < 0 || x >= container.Width) return;
			if (y < 0 || y >= container.Height) return;
			if (null != slots[y, x].slotAttribute && slots[y, x].slotAttribute.category == SlotAttribute.Category.INSULATOR)
			{
				return;
			}
			list.Add(slots[y, x]);
		};
		Action<int, int> exitMarker = (x, y)=>{
			if (null == ret[y, x])
			{
				ret[y, x] = new FillInfo();
				ret[y, x].ancestorPos = new Pos2D(x, y + 1);
			}
		};

		container.ForeachSlot((x, y, slot)=>{
			if (null != ret[y, x]) 
				return;
			if (null != slot.slotAttribute && slot.slotAttribute.category == SlotAttribute.Category.INSULATOR)
				return;
			var ctx = new AStar.Context<SlotWrapper2D>();
			ctx.start = slots[y, x];
			ctx.procTrait = (s)=>{
				return s.Trait;
			};
			ctx.procWeight = (SlotWrapper2D from, SlotWrapper2D to)=>{
				foreach (var t in surroundingOffsets){
					if (to.pos.x == from.pos.x + t.item1 && to.pos.y == from.pos.y + t.item2){
						return t.item3;
					}
				}
				throw new NotImplementedException();
			};
			ctx.procTermination = (SlotWrapper2D s)=>{
				return s.pos.y == container.Height - 1;
			};
			ctx.procDistanceEstimator = (SlotWrapper2D s)=>{
				return (container.Height - 1 - s.pos.y) * 1.0001f;
			};
			ctx.procAdjacencies = (SlotWrapper2D s)=>{
				var list = new List<SlotWrapper2D>();
				foreach (var t in surroundingOffsets){
					picker.Invoke(s.pos.x + t.item1, s.pos.y + t.item2, list);
				}
				return list;
			};

			if (AStar.Evaluate(ctx))
			{
				var result = ctx.path;
				if (result.Count <= 0)
				{
					exitMarker.Invoke(x, y);
				}
				else
				{
					result.Reverse();
					for (int i = 0, len = result.Count - 1; i < len; i++)
					{
						if (null != ret[result[i].pos.y, result[i].pos.x]) continue;
						var t = new FillInfo();
						t.ancestorPos = new Pos2D(result[i + 1].pos.x, result[i + 1].pos.y);
						ret[result[i].pos.y, result[i].pos.x] = t;
					}
					exitMarker(result[result.Count - 1].pos.x, result[result.Count - 1].pos.y);
				}
			}
			else
			{
				ret[y, x] = new FillInfo();
			}
		});

		for (int y = 0; y < container.Height; y++)
		{
			for (int x = 0; x < container.Width; x++)
			{
				var fi = ret[y, x];
				if (null == fi) continue;
				if (fi.IsOnSpot) continue;
				fi.childrenPos = new List<Pos2D>();
				foreach (var t in surroundingOffsets)
				{
					var sx = x + t.item1;
					var sy = y + t.item2;

					if (!container.IsLegalPosition(sx, sy))
					{
						continue;
					}
					var slot = container.GetSlot(sx, sy);
					if (null != slot && null != slot.slotAttribute && slot.slotAttribute.category == SlotAttribute.Category.INSULATOR)
					{
						continue;
					}

					var touch = ret[sy, sx];
					if (null != touch.ancestorPos && touch.ancestorPos.x == x && touch.ancestorPos.y == y)
					{
						fi.childrenPos.Add(new Pos2D(sx, sy));
					}
				}
			}
		}

		return ret;
	}
    private RefillFlowRecord DoApply(Container2D_Rectangular container, FillInfo[,] fillInfoMap)
    {
        var ret   = new RefillFlowRecord();
        var slots = container.WrapperRect;

        container.ForeachSlot((x, y, slot) => {
            if (null != slot)
            {
                return;
            }
            var fi = fillInfoMap[y, x];
            if (fi.IsOnSpot)
            {
                ret.OnSpotList.Add(new Pos2D(x, y));
            }
        });
        var ends = CollectRefillEnds(container, fillInfoMap);
        var dir  = new Dictionary <int, Tuple <SlotWrapper2D, int> >();

        foreach (var e in ends)
        {
            var pos          = fillInfoMap[e.y, e.x].ancestorPos;
            int inverseDepth = 0;
            while (container.IsLegalPosition(pos.x, pos.y))
            {
                var s = slots[pos.y, pos.x];
                if (null != s)
                {
                    inverseDepth++;
                    if (!dir.ContainsKey(s.Trait))
                    {
                        dir.Add(s.Trait, new Tuple <SlotWrapper2D, int>(s, inverseDepth));
                    }
                    else if (dir[s.Trait].item2 < inverseDepth)
                    {
                        dir[s.Trait].item2 = inverseDepth;
                    }
                }
                pos = fillInfoMap[pos.y, pos.x].ancestorPos;
            }
        }
        var list = new List <Tuple <SlotWrapper2D, int> >();

        foreach (var kvp in dir)
        {
            list.Add(kvp.Value);
        }
        list.Sort((lhr, rhr) => {
            return((lhr.item2 < rhr.item2) ? -1 : 1);
        });

        foreach (var t in list)
        {
            var src    = t.item1.pos;
            var moveTo = DepthFirstSearch(slots, fillInfoMap, src.x, src.y);
            var dst    = moveTo.item1;

            var path = new RefillFlowRecord.Path();
            path.src = src.Clone();
            path.dst = dst.Clone();

            container.SwapSlot(src.x, src.y, dst.x, dst.y);
            var cur = path.dst;
            do
            {
                path.movements.Add(cur);
                cur = fillInfoMap[cur.y, cur.x].ancestorPos;
            } while (cur != path.src);

            path.movements.Add(path.src);
            path.movements.Reverse();

            ret.NonFillMovements.Add(path);
        }

        ret.FillMovements = CollectFillPathList(container, fillInfoMap);

        return(ret);
    }
    private FillInfo[,] GenerateRefillTrendMap(Container2D_Rectangular container)
    {
        var ret   = new FillInfo[container.Height, container.Width];
        var slots = container.WrapperRect;
        Action <int, int, List <SlotWrapper2D> > picker = (x, y, list) => {
            if (x < 0 || x >= container.Width)
            {
                return;
            }
            if (y < 0 || y >= container.Height)
            {
                return;
            }
            if (null != slots[y, x].slotAttribute && slots[y, x].slotAttribute.category == SlotAttribute.Category.INSULATOR)
            {
                return;
            }
            list.Add(slots[y, x]);
        };
        Action <int, int> exitMarker = (x, y) => {
            if (null == ret[y, x])
            {
                ret[y, x]             = new FillInfo();
                ret[y, x].ancestorPos = new Pos2D(x, y + 1);
            }
        };

        container.ForeachSlot((x, y, slot) => {
            if (null != ret[y, x])
            {
                return;
            }
            if (null != slot.slotAttribute && slot.slotAttribute.category == SlotAttribute.Category.INSULATOR)
            {
                return;
            }
            var ctx       = new AStar.Context <SlotWrapper2D>();
            ctx.start     = slots[y, x];
            ctx.procTrait = (s) => {
                return(s.Trait);
            };
            ctx.procWeight = (SlotWrapper2D from, SlotWrapper2D to) => {
                foreach (var t in surroundingOffsets)
                {
                    if (to.pos.x == from.pos.x + t.item1 && to.pos.y == from.pos.y + t.item2)
                    {
                        return(t.item3);
                    }
                }
                throw new NotImplementedException();
            };
            ctx.procTermination = (SlotWrapper2D s) => {
                return(s.pos.y == container.Height - 1);
            };
            ctx.procDistanceEstimator = (SlotWrapper2D s) => {
                return((container.Height - 1 - s.pos.y) * 1.0001f);
            };
            ctx.procAdjacencies = (SlotWrapper2D s) => {
                var list = new List <SlotWrapper2D>();
                foreach (var t in surroundingOffsets)
                {
                    picker.Invoke(s.pos.x + t.item1, s.pos.y + t.item2, list);
                }
                return(list);
            };

            if (AStar.Evaluate(ctx))
            {
                var result = ctx.path;
                if (result.Count <= 0)
                {
                    exitMarker.Invoke(x, y);
                }
                else
                {
                    result.Reverse();
                    for (int i = 0, len = result.Count - 1; i < len; i++)
                    {
                        if (null != ret[result[i].pos.y, result[i].pos.x])
                        {
                            continue;
                        }
                        var t         = new FillInfo();
                        t.ancestorPos = new Pos2D(result[i + 1].pos.x, result[i + 1].pos.y);
                        ret[result[i].pos.y, result[i].pos.x] = t;
                    }
                    exitMarker(result[result.Count - 1].pos.x, result[result.Count - 1].pos.y);
                }
            }
            else
            {
                ret[y, x] = new FillInfo();
            }
        });

        for (int y = 0; y < container.Height; y++)
        {
            for (int x = 0; x < container.Width; x++)
            {
                var fi = ret[y, x];
                if (null == fi)
                {
                    continue;
                }
                if (fi.IsOnSpot)
                {
                    continue;
                }
                fi.childrenPos = new List <Pos2D>();
                foreach (var t in surroundingOffsets)
                {
                    var sx = x + t.item1;
                    var sy = y + t.item2;

                    if (!container.IsLegalPosition(sx, sy))
                    {
                        continue;
                    }
                    var slot = container.GetSlot(sx, sy);
                    if (null != slot && null != slot.slotAttribute && slot.slotAttribute.category == SlotAttribute.Category.INSULATOR)
                    {
                        continue;
                    }

                    var touch = ret[sy, sx];
                    if (null != touch.ancestorPos && touch.ancestorPos.x == x && touch.ancestorPos.y == y)
                    {
                        fi.childrenPos.Add(new Pos2D(sx, sy));
                    }
                }
            }
        }

        return(ret);
    }