Exemple #1
0
        private static int FindMinSteps(HashSet <char> lowers, Dictionary <char, Dictionary <char, StepsToPoint> > graph, int minSteps)
        {
            Stack <ToHere>         attempts   = new Stack <ToHere>();
            Dictionary <long, int> bestToHere = new Dictionary <long, int>();          // bitmask of keys found a = 1, b = 2, c = 4, etc., plus current key point as (key << 32)
            ToHere start = new ToHere()
            {
                current = '@'
            };

            attempts.Push(start);
            while (attempts.Count > 0)
            {
                var currentPos = attempts.Pop();
                List <NextStepCandidate> possible = new List <NextStepCandidate>();
                foreach (char destination in lowers)
                {
                    //if not already visited destination and can get from source to destination with current set of keys
                    if (((currentPos.keys & (1 << (destination - 'a'))) == 0) && (graph[currentPos.current][destination].doors & currentPos.keys) == graph[currentPos.current][destination].doors)
                    {
                        NextStepCandidate nextStep = new NextStepCandidate();
                        nextStep.val   = destination;
                        nextStep.steps = currentPos.steps + graph[currentPos.current][destination].steps;
                        long state = currentPos.keys + (1L << (destination - 'a')) + (1L << ((destination - 'a') + 32));

                        //only add this as a candidate next step if the combination of collected keys and ending position take less steps to reach than previously found for that combination
                        if (nextStep.steps < minSteps && (!bestToHere.ContainsKey(state) || bestToHere[state] > nextStep.steps))
                        {
                            possible.Add(nextStep);
                        }
                    }
                }
                possible.Sort((NextStepCandidate x, NextStepCandidate y) => { return(x.steps.CompareTo(y.steps)); });
                possible.Reverse();                 //put the longest options on the stack first so that shortest possible are tried first;
                foreach (var nextStep in possible)
                {
                    var destination = nextStep.val;
                    var next        = new ToHere();
                    next.current = destination;
                    next.keys    = currentPos.keys;
                    next.keys   += 1 << (destination - 'a');
                    next.steps   = currentPos.steps;
                    next.steps  += graph[currentPos.current][destination].steps;
                    if (next.keys == 67108863)                     //lowest 26 bits all 1
                    {
                        minSteps = Math.Min(minSteps, next.steps);
                    }
                    else if (next.steps < minSteps)
                    {
                        long state = currentPos.keys + (1L << (destination - 'a')) + (1L << ((destination - 'a') + 32));
                        bestToHere[state] = next.steps;
                        attempts.Push(next);
                    }
                }
            }

            return(minSteps);
        }
Exemple #2
0
        /// <summary>
        /// Modified version of FindMinSteps to deal with 4 separate quadrants
        /// </summary>
        /// <returns></returns>
        private static int FindMinSteps2(HashSet <char> lowers, Dictionary <char, Dictionary <char, StepsToPoint> > graph, int minSteps)
        {
            Stack <ToHere2>        attempts   = new Stack <ToHere2>();
            Dictionary <long, int> bestToHere = new Dictionary <long, int>();          // bitmask of keys found a = 1, b = 2, c = 4, etc., plus current key points as (key << 32)
            ToHere2 start = new ToHere2();

            attempts.Push(start);
            while (attempts.Count > 0)
            {
                var currentPos = attempts.Pop();


                List <NextStepCandidate> possible = new List <NextStepCandidate>();
                foreach (char destination in lowers)
                {
                    if ((currentPos.keys & (1 << (destination - 'a'))) == 0)                    //if not already visited destination
                    {
                        //find same quadrant character - if there's a non '@' character, assume that is the correct choice
                        char quadChar = ' ';
                        foreach (char possibleQ in currentPos.current)
                        {
                            if ((possibleQ != '@' || quadChar == ' ') && graph[possibleQ].ContainsKey(destination))
                            {
                                quadChar = possibleQ;
                            }
                        }


                        //if can get from source to destination with current set of keys
                        if ((graph[quadChar][destination].doors & currentPos.keys) == graph[quadChar][destination].doors)
                        {
                            NextStepCandidate nextStep = new NextStepCandidate();
                            nextStep.val   = destination;
                            nextStep.steps = currentPos.steps + graph[quadChar][destination].steps;
                            long state = currentPos.keys + (1L << (destination - 'a')) + (1L << ((destination - 'a') + 32));

                            //only add this as a candidate next step if the combination of collected keys and ending position take less steps to reach than previously found for that combination
                            if (nextStep.steps < minSteps && (!bestToHere.ContainsKey(state) || bestToHere[state] > nextStep.steps))
                            {
                                possible.Add(nextStep);
                            }
                        }
                    }
                }
                possible.Sort((NextStepCandidate x, NextStepCandidate y) => { return(x.steps.CompareTo(y.steps)); });
                possible.Reverse();                 //put the longest options on the stack first so that shortest possible are tried first;
                foreach (var nextStep in possible)
                {
                    var  destination = nextStep.val;
                    char quadChar    = ' ';
                    foreach (char possibleQ in currentPos.current)
                    {
                        if ((possibleQ != '@' || quadChar == ' ') && graph[possibleQ].ContainsKey(destination))
                        {
                            quadChar = possibleQ;
                        }
                    }
                    var next = new ToHere2();
                    next.current = new List <char>(currentPos.current);
                    if (quadChar == '@')
                    {
                        if (next.current.Count == 4)
                        {
                            next.current.Remove(quadChar);                                                  //only remove @ once all 4 quadrants have been accessed
                        }
                        next.current.Add(destination);
                    }
                    else
                    {
                        next.current.Remove(quadChar);
                        next.current.Add(destination);
                    }

                    next.keys   = currentPos.keys;
                    next.keys  += 1 << (destination - 'a');
                    next.steps  = currentPos.steps;
                    next.steps += graph[quadChar][destination].steps;
                    if (next.keys == 67108863)                     //lowest 26 bits all 1
                    {
                        minSteps = Math.Min(minSteps, next.steps);
                    }
                    else if (next.steps < minSteps)
                    {
                        long state = currentPos.keys + (1L << (destination - 'a')) + (1L << ((destination - 'a') + 32));
                        bestToHere[state] = next.steps;
                        attempts.Push(next);
                    }
                }
            }

            return(minSteps);
        }
Exemple #3
0
        void ArrangeShipGroupsOnSquarePlane()
        {
            Vector2    footprint     = Vector2.zero;
            List <int> addedGroupIDs = new List <int>();

            List <AttachmentPoint> attachmentPoints = new List <AttachmentPoint>();

            attachmentPoints.Add(new AttachmentPoint(Vector2.zero, new Vector2[] { Vector2.one *Mathf.Infinity, Vector2.one *Mathf.Infinity, Vector2.one *Mathf.Infinity, Vector2.one *Mathf.Infinity }));

            NextStepCandidate bestCandidate = new NextStepCandidate(0, Vector2.zero, false, Vector2.one, Mathf.Infinity);

            for (int i = 0; i < groups.Length; i++)
            {
                //DETERMINE BEST CANDIDATE STEP
                for (int candidateGroupID = 0; candidateGroupID < groups.Length; candidateGroupID++)
                {
                    if (!addedGroupIDs.Contains(candidateGroupID))
                    {
                        for (int verticalIndex = 0; verticalIndex < 2; verticalIndex++)
                        {
                            //CALCULATE CORNER POSITIONS
                            Vector2[] groupCorners = verticalIndex == 1 ? groups[candidateGroupID].verticalCorners : groups[candidateGroupID].horizontalCorners;
                            //CALCULATE FOOTPRINT
                            Vector2 size = groupCorners[2] - groupCorners[1];

                            //TRY ALL POSITIONING OPTIONS
                            foreach (AttachmentPoint attachmentPoint in attachmentPoints)
                            {
                                for (int examinedCorner = 0; examinedCorner < 4; examinedCorner++)
                                {
                                    Vector2 sizeLimitation = attachmentPoint.quadrantSizeLimits[examinedCorner];

                                    if (size.x <= sizeLimitation.x && size.y <= sizeLimitation.y)
                                    {
                                        NextStepCandidate newCandidate;
                                        newCandidate.groupID  = candidateGroupID;
                                        newCandidate.position = attachmentPoint.position - groupCorners[examinedCorner];
                                        newCandidate.vertical = verticalIndex == 1;

                                        Vector4 boundaries = Vector4.zero;
                                        for (int newCandidateCornerID = 0; newCandidateCornerID < groupCorners.Length; newCandidateCornerID++)
                                        {
                                            boundaries = PushBoundaries(boundaries, newCandidate.position + groupCorners[newCandidateCornerID]);
                                        }

                                        foreach (int addedGroupID in addedGroupIDs)
                                        {
                                            Vector2[] addedGroupCorners = groups[addedGroupID].Corners;
                                            for (int cornerID = 0; cornerID < 4; cornerID++)
                                            {
                                                boundaries = PushBoundaries(boundaries, groups[addedGroupID].rect.position + addedGroupCorners[cornerID]);
                                            }
                                        }

                                        newCandidate.wholeFootprint = new Vector2(Mathf.Abs(boundaries.x - boundaries.z), Mathf.Abs(boundaries.y - boundaries.w));
                                        newCandidate.balance        = newCandidate.wholeFootprint.x / newCandidate.wholeFootprint.y;

                                        if (newCandidate.wholeFootprint.x < 0.9f * plateSize && newCandidate.wholeFootprint.y < 0.9f * plateSize)
                                        {
                                            if (Mathf.Abs(1 - newCandidate.balance) < Mathf.Abs(1 - bestCandidate.balance))
                                            {
                                                bestCandidate = newCandidate;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }

                //APPLY BEST STEP
                addedGroupIDs.Add(bestCandidate.groupID);
                ShipRectangleGroup positionedGroup = groups[bestCandidate.groupID];
                positionedGroup.rect.position = bestCandidate.position;
                positionedGroup.vertical      = bestCandidate.vertical;
                groups[bestCandidate.groupID] = positionedGroup;
                footprint = bestCandidate.wholeFootprint;

                // Debug.Log("Cycle: " + i);
                // Debug.Log("Group ID: " + bestCandidate.groupID);
                // Debug.Log("Position: " + positionedGroup.rect.position);
                // Debug.Log("Vertical: " + positionedGroup.vertical);

                bestCandidate = new NextStepCandidate(0, Vector2.zero, false, Vector2.one, Mathf.Infinity);



                //RECALCULATE ATTACHMENT POINTS
                attachmentPoints = new List <AttachmentPoint>();
                foreach (int groupID in addedGroupIDs)
                {
                    ShipRectangleGroup managedGroup = groups[groupID];
                    for (int cornerIndex = 0; cornerIndex < 4; cornerIndex++)
                    {
                        AttachmentPoint potentialAttachmentPoint = new AttachmentPoint(Vector2.zero, new Vector2[4]);
                        potentialAttachmentPoint.position = managedGroup.rect.position + managedGroup.Corners[cornerIndex];

                        Vector2[] calculatedQuadrants = new Vector2[4];
                        for (int quadrantID = 0; quadrantID < 4; quadrantID++)
                        {
                            Vector2 quadrantDirectional = new Vector2((quadrantID == 0 || quadrantID == 1) ? 1 : -1, (quadrantID == 1 || quadrantID == 3) ? 1 : -1);
                            Vector2 size = Vector2.one * Mathf.Infinity;

                            foreach (int potentialIntersectorIndex in addedGroupIDs)
                            {
                                ShipRectangleGroup potentialIntersector = groups[potentialIntersectorIndex];
                                for (int intersectorCornerIndex = 0; intersectorCornerIndex < 4; intersectorCornerIndex++)
                                {
                                    Vector2 cornerGlobalPosition = potentialIntersector.rect.position + potentialIntersector.Corners[intersectorCornerIndex];
                                    Vector2 cornerPositionRelativeToAttachmentPoint = cornerGlobalPosition - potentialAttachmentPoint.position;
                                    Vector2 cornerNormalizedQuadrantPosition        = Vector2.Scale(cornerPositionRelativeToAttachmentPoint, quadrantDirectional);

                                    if (cornerNormalizedQuadrantPosition.x >= -0.000015f && cornerNormalizedQuadrantPosition.y >= -0.000015f && cornerNormalizedQuadrantPosition.x <= size.x && cornerNormalizedQuadrantPosition.y <= size.y)
                                    {
                                        Vector2 oppositeCornerNormalizedQuadrantPosition = Vector2.Scale(potentialIntersector.rect.position - potentialIntersector.Corners[intersectorCornerIndex] - potentialAttachmentPoint.position, quadrantDirectional);
                                        Vector2 sides = oppositeCornerNormalizedQuadrantPosition - cornerNormalizedQuadrantPosition;
                                        sides = sides - Vector2.Scale(new Vector2(Mathf.Clamp(-oppositeCornerNormalizedQuadrantPosition.x, 0, Mathf.Abs(sides.x)), Mathf.Clamp(-oppositeCornerNormalizedQuadrantPosition.y, 0, Mathf.Abs(sides.y))), new Vector2(Mathf.Sign(sides.x), Mathf.Sign(sides.y)));

                                        if (sides.y != 0 && sides.x != 0)
                                        {
                                            if (sides.x > 0)
                                            {
                                                size.x = cornerNormalizedQuadrantPosition.x < size.x ? cornerNormalizedQuadrantPosition.x : size.x;
                                            }
                                            else
                                            {
                                                size.y = cornerNormalizedQuadrantPosition.y < size.y ? cornerNormalizedQuadrantPosition.y : size.y;
                                            }
                                        }
                                    }
                                }
                            }

                            //TEST
                            // Debug.Log("Cycle: " + i);
                            // Debug.Log("Group: " + groupID + " Corner: " + cornerIndex + " Quadrant: " + quadrantID);
                            // Debug.Log("Size: " + size);
                            //TEST
                            // if (groupID == 3 && cornerIndex == 2 && quadrantID == 2)
                            // {
                            //     Debug.Log("Conflicting Quadrant Size: " + size);
                            // }

                            calculatedQuadrants[quadrantID] = size;
                        }

                        potentialAttachmentPoint.quadrantSizeLimits = calculatedQuadrants;
                        attachmentPoints.Add(potentialAttachmentPoint);
                    }
                }
            }


            //NORMALIZE RECTANGLE POSITIONS
            Vector2 topRightCorner   = Vector2.one * Mathf.NegativeInfinity;
            Vector2 bottomLeftCorner = Vector2.one * Mathf.Infinity;

            for (int i = 0; i < groups.Length; i++)
            {
                Vector2[] corners               = groups[i].Corners;
                Vector2   groupTopRightCorner   = corners[2] + groups[i].rect.position;
                Vector2   groupBottomLeftCorner = corners[1] + groups[i].rect.position;

                topRightCorner.x = groupTopRightCorner.x > topRightCorner.x ? groupTopRightCorner.x : topRightCorner.x;
                topRightCorner.y = groupTopRightCorner.y > topRightCorner.y ? groupTopRightCorner.y : topRightCorner.y;

                bottomLeftCorner.x = groupBottomLeftCorner.x < bottomLeftCorner.x ? groupBottomLeftCorner.x : bottomLeftCorner.x;
                bottomLeftCorner.y = groupBottomLeftCorner.y < bottomLeftCorner.y ? groupBottomLeftCorner.y : bottomLeftCorner.y;
            }

            Vector2 positionAdjustment = (bottomLeftCorner + topRightCorner) / 2.0f;

            for (int i = 0; i < groups.Length; i++)
            {
                groups[i].rect.position -= positionAdjustment;
            }


            //POSITION SHIPS ON RECTANGLE GROUPS
            for (int groupIndex = 0; groupIndex < groups.Length; groupIndex++)
            {
                ShipRectangleGroup group = groups[groupIndex];
                float   shipSpacing      = group.rect.width / group.ships.Length * 0.8f;
                float   reservedSpace    = shipSpacing * (group.ships.Length - 1);
                Vector3 startingPosition = new Vector3(group.rect.x, 0, group.rect.y) + (group.vertical ? Vector3.forward : Vector3.right) * (reservedSpace / 2.0f);
                Vector3 positionStep     = (group.vertical ? Vector3.back : Vector3.left) * shipSpacing;
                for (int shipIndex = 0; shipIndex < group.ships.Length; shipIndex++)
                {
                    Ship ship = group.ships[shipIndex];
                    ship.transform.localPosition = startingPosition + positionStep * shipIndex;
                    ship.transform.localRotation = new Quaternion(0, 1, 0, group.vertical ? 1 : 0);

                    ship.placementInfo.localShipboxPosition = ship.transform.localPosition;
                    ship.placementInfo.localShipboxRotation = ship.transform.localRotation;
                }

                // GameObject tmp = GameObject.CreatePrimitive(PrimitiveType.Cube);
                // tmp.transform.localScale = new Vector3(group.vertical ? group.rect.height : group.rect.width, 0.1f, group.vertical ? group.rect.width : group.rect.height);
                // tmp.transform.position = new Vector3(group.rect.x, 0, group.rect.y);
            }
        }