/// <summary>
        /// Creates a jagged array that represents a Pyramid of Glass objects, each Glass object's Left
        /// and Right properties are connected to the Glasses that supports it. One of the Glasses is to be
        /// marked as the target.
        /// </summary>
        /// <param name="glassRows">The number of rows to create for the Pyramid of Glass objects</param>
        /// <param name="glassRowTarget">The row number (1-based) for the Glass object to mark as the target</param>
        /// <param name="glassColTarget">The column number (1-based) for the Glass object to mark as the target</param>
        /// <returns></returns>
        public static Glass[][] PyramidCreate(int glassRowTarget, int glassColTarget)
        {
            // create a jagged array that will represent our Pyramid of Glass objects
            var   result      = new Glass[glassRowTarget][];
            Glass glassTarget = null;

            for (var y = 0; y < glassRowTarget; y++)
            {
                result[y] = new Glass[y + 1];
                for (var x = 0; x <= y; x++)
                {
                    var isTarget = y + 1 == glassRowTarget && x + 1 == glassColTarget;
                    result[y][x] = new Glass()
                    {
                        _isTarget = isTarget
                    };
                    if (isTarget)
                    {
                        glassTarget = result[y][x];
                    }
                }
            }

            // each Glass stands on a left and right Glass, and have a left and a right parent Glass
            // (if not last/first row) - anyhow, setup this connection
            for (var y = 0; y < result.Length - 1; y++)
            {
                for (var x = 0; x < result[y].Length; x++)
                {
                    result[y][x]._childLeft          = result[y + 1][x];
                    result[y + 1][x]._parentRight    = result[y][x];
                    result[y][x]._childRight         = result[y + 1][x + 1];
                    result[y + 1][x + 1]._parentLeft = result[y][x];
                }
            }

            // starting with the taget, we visit each parent to set _isRelatedToTarget on the Glasses that need processing
            bool tagItem(Glass g)
            {
                g._isRelatedToTarget = true;
                if ((g._parentLeft != null) && (!g._parentLeft._isRelatedToTarget))
                {
                    tagItem(g._parentLeft);
                }
                if ((g._parentRight != null) && (!g._parentRight._isRelatedToTarget))
                {
                    tagItem(g._parentRight);
                }
                return(false);
            }

            tagItem(glassTarget);

            // return the top glass
            return(result);
        }
Example #2
0
        public static void Main()
        {
            // init
            Console.WriteLine("Fredrik's Pyramid of Glasses v1.0. Copyright (c) by Fredrik Johansson 2019 ([email protected])\n");

            // index (1-based) of the row and column of the glass that we're targeting
            // glassRow also equals to the number of rows in our glass pyramid, first row has 1 glass, second row has 2, and so on
            int glassRow, glassCol;

            // prompt for parameters (just ENTER will exit)
            if (!Prompt($"Enter the number of rows (2 - 50) for the Pyramid, then press ENTER: ", out glassRow, 2, 50))
            {
                return;
            }

            if (!Prompt($"Enter the index of the glass on the last row to target (1 - {glassRow}), then press ENTER: ", out glassCol, 1, glassRow))
            {
                return;
            }

            // compute the answer by testing different solutions on glassPyramid in the range secondsMin-secondsMax
            Console.WriteLine($"Computing the number of seconds it takes to fill glass {glassCol} on the lowest row, in a glass pyramid with {glassRow} rows.");
            var    debugTickStartMain = Environment.TickCount;
            double secondsMin = 0, secondsMax = Glass.PyramidMaxCapacity(glassRow);
            double secondsTry   = secondsMax;
            var    glassPyramid = Glass.PyramidCreate(glassRow, glassCol);
            var    glassTop     = glassPyramid[0][0];

            while (true)
            {
                // create a pyramid and test to see if the seconds is to much or to little
                Console.Write($"Answer is between {secondsMin,20:0.000} and {secondsMax,20:0.000}. Testing {secondsTry,20:0.000}... ");

                var debugTickStart = Environment.TickCount;
                Glass.PyramidReset(glassPyramid);
                var addRes           = glassTop.Add(secondsTry);
                var debugTickElapsed = Environment.TickCount - debugTickStart;

                double secondsPrev = secondsTry;
                if (addRes)
                {
                    // overflow
                    // set the next seconds to try to the mid point between secondsMin and secondsMax
                    Console.WriteLine($"To much (took {debugTickElapsed} msec to compute)");
                    secondsMax = secondsTry;
                    secondsTry = secondsMin + (secondsMax - secondsMin) / 2;
                }
                else
                {
                    // set the next seconds to try to the mid point between secondsMin and secondsMax
                    Console.WriteLine($"To little (took {debugTickElapsed} msec to compute)");
                    secondsMin = secondsTry;
                    secondsTry = secondsMin + (secondsMax - secondsMin) / 2;
                }

                if (Math.Round(secondsTry, 3) == Math.Round(secondsPrev, 3))
                {
                    // solution found (down to 3 decimals)
                    var debugTickElapsedMain = Environment.TickCount - debugTickStartMain;

                    Console.WriteLine();
                    Glass.PyramidRender(glassPyramid, glassRow, glassCol);
                    Console.WriteLine($"Finished! On a glass pyramid with {glassRow} rows, the glass on row {glassRow}, at column {glassCol} is entirely filled after {secondsTry:0.000} seconds (took {debugTickElapsedMain} msec to compute the solution).");
                    Console.WriteLine($"NOTE: In the pyramid above, the glass is highlighted in green, and the glasses used to compute this solution are highlighted in yellow.\n");
                    Console.WriteLine("Press ENTER to exit...");
                    Console.ReadLine();
                    break;
                }
            }
        }