/// <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); }
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; } } }