如何在控制台应用程序中同时写入多个位置?C#

6
我希望你能帮忙将文本向下一行一个字符地写入控制台,行数与控制台宽度相同。我已经完成了大部分工作,但是它是从上到下、从左到右的...如果你需要帮助来想象我所说的,请想象矩阵代码雨。
int w = Console.WindowWidth;
int h = Console.WindowHeight;

int i = 0;
while (i < w)
{
    int j = 0;
    while (j < h)
    {
        Thread.Sleep(1);
        Console.SetCursorPosition(i, j);
        Console.Write(".");
        j++;
    }
    i++;
}

1
https://www.codeproject.com/Articles/113227/Matrix-Rain - Mick
4个回答

7
我会构建一个名为List<string> lines;的列表,其中包含要写入控制台窗口的行,每行的宽度与控制台宽度相同。然后,按照相反的顺序将列表打印到控制台窗口中,这样第一行(位于lines[0]处)始终是最后一个打印的行,并始终位于控制台窗口底部。

示例实现 -- 有人提到这可能是作业。我不这么认为,但如果是,请先尝试自己实现上述想法。

我们可以在用于打印其项目的循环中添加新项目。但是,在添加行之前,我们首先检查列表中是否已经有与控制台窗口中一样多的行数(Console.WindowHeight)。如果有,那么我们只需在添加新行之前删除lines[0]处的行即可。通过这种方式,List<string> lines随着控制台窗口"滚动"。

滚动速度由Thread.Sleep控制,但是此代码可以轻松地添加到Timer中,以便可以在后台执行其他工作(例如,如果此代码旨在成为"屏幕保护程序",并且您希望等待用户输入来"唤醒")。但是,无论我们决定如何实现速度,我决定创建一个enum,其中的值表示Thread.Sleep实现将使用的毫秒数:

class Program
{
    enum MatrixCodeSpeed
    {
        Fastest = 0,
        Faster = 33,
        Fast = 67,
        Normal = 100,
        Slow = 333,
        Slower = 667,
        Slowest = 1000
    }

我还会创建一个帮助方法,为你创建一个“随机”行。它可以接受一个整数,指定“密度”,即您想要在该行中包含多少个字符。 density 表示百分比,因此如果指定了10,则我们在0到99之间选择一个随机数,如果小于10,则将一个随机矩阵字符添加到字符串中(否则添加一个空格字符)。
此外,为了更好地复制矩阵,我还选择了4个不同的字符进行打印,每个字符都比前一个略微暗淡。这增加了三维效果,使褪色的块看起来比实心的块远得多:
    private static Random rnd = new Random();

    // Add whatever 'matrix' characters you want to this array. If you prefer to have one 
    // character chosen more often than the others, you can write code to favor a specific
    // index, or just add more instances of that character to the array below:

    private static char[] matrixChars = new[] { '░', '▒', '▓', '█' };

    static string GetMatrixLine(int density)
    {            
        var line = new StringBuilder();

        for (int i = 0; i < Console.WindowWidth; i++)
        {
            // Choose a random number from 0-99 and see if it's greater than density
            line.Append(rnd.Next(100) > density 
                ? ' '  // If it is, add a space to reduce line density
                : matrixChars[rnd.Next(matrixChars.Length)]); // Pick a random character
        }

        return line.ToString();
    }

接下来,我们有一个主方法,它使用10%的密度将列表填充了随机行,然后以相反的顺序一次打印出它们,进入无限循环(如果需要,则删除第一行)。
    static void Main()
    {
        var lines = new List<string>();
        var density = 10; // (10% of each line will be a matrix character)
        var speed = MatrixCodeSpeed.Normal;

        // Hide the cursor - set this to 'true' again before accepting user input
        Console.CursorVisible = false;
        Console.ForegroundColor = ConsoleColor.DarkGreen;

        while (true)
        {
            // Once the lines count is greater than the window height,
            // remove the first item, so that the list "scrolls" also
            if (lines.Count >= Console.WindowHeight)
            {
                lines.Remove(lines[0]);
            }

            // Add a new random line to the list, which will be the new topmost line.
            lines.Add(GetMatrixLine(density));
            Console.SetCursorPosition(0, 0);

            // Print the lines out to the console in reverse order so the
            // first line is always last, or on the bottom of the window
            for (int i = lines.Count - 1; i >= 0; i--)
            {
                Console.Write(lines[i]);
            }

            Thread.Sleep(TimeSpan.FromMilliseconds((int)speed));
        }
    }
}

这里是它的演示gif,直到屏幕满了为止(然后gif会重复播放,但代码版本会继续正常滚动): enter image description here

1
你可以选择蓝色药丸——故事就此结束,你会在床上醒来,相信任何你想相信的事情。或者你可以选择红色药丸——留在仙境中,我会带你深入兔子洞。 - LarsTech
感谢您为此付出的所有努力,并使其易于理解。 - sweetroll
@ sweetroll 很有趣! - Rufus L

1
这个任务看起来像是一项作业,所以我会指导你而不是提供实现。如果这是一份作业,向你提供答案是不道德的。
你正在寻找更好的算法匹配。所述算法从顶部到底部填充控制台,因为它首先通过 Y 轴(嵌套循环)迭代填充,然后是 X 轴(外部循环)。
需要做的是交替迭代 X 轴和 Y 轴,使其看起来从左上角到右下角填充。
// 1 step to (0,0)
*
// 3 steps to (1,1)
**
**
// 5 steps for reaching (2,2)
***
***
***
// 7 steps for reaching (3,3)
****
****
****
// 9 steps for reaching (4,4) and 11 steps for (5,5)...
// I do think everyone could get this pattern

这份草稿也将是最终结果的样子。与其同时填满它们,你需要做的实际上是在到达下一个正方形点后使线程休眠。
(计算机速度非常快,可能在一秒内完成所有工作并且黑色控制台窗口会在没有任何通知的情况下消失。)
当您发布问题时,我也从一开始就在解决它。我认为交替填充X和Y轴是解决方案,但停在每个正方形扩展时刻更重要以获得效果。
在我的观点中,这也不是一个线程问题标签。
让我们总结一下上面的模式:
1. 假设i和j分别是x和y坐标。 2. 每次迭代需要经过n*2+1步从(i, j)到达(i+1,j+1) 3. 请注意,在此示例中,我们是从零开始的。
我们即将构建循环:
  • n*2+1步数很有用。它意味着您需要在x轴上填充n次,在y轴上填充n次,最后完成对角线网格(n+1,n+1)。
  • 在每个内部循环中,我们首先沿y轴渲染X前沿,然后沿x轴渲染Y前沿。
  • 假设循环从检查点(n,n)开始,其中n=3,并且我们休息了一会儿,所以现在我们在n=4。
  • 为了实现这一点,我们最好先导航到(n+1,0),然后填充到(n+1,n)
  • 之后,我们导航到(0,n+1),并填充到(n+1,n+1)
  • 然后,我们现在是m=n+1 (听起来像数学证明 :(

循环将是

//calculate how many checkpoints (n)
int checkpoints = 1080;
//n should indicate the actual turn we are instead of naming the total turns like sucks
//The main, the outermost For-loop 
for (int n=0;n<checkpoints;n++)
{
  // The nested step
  for (int y=0;y<n;y++)
  {
    // Just fill in (n+1, y) grid 
    Console.SetCursorPosition(n+1, y);
    Console.Write(".");
  }
  for (int x=0;x<n+1;x++) 
  {         
    // Just fill in (x, n+1) grid
    Console.SetCursorPosition(x, n+1);
    Console.Write(".");
  }
  // Upon completion of each main cycle we have a sleep, yah
  Thread.Sleep(100);
}

好的,我希望程序在控制台大小小于1080x1080时崩溃。

这个算法只能让你填充一个正方形,而分辨率为1920x1080的典型显示器因为是16:9而无法胜任。这是有意的,如果你在做作业,你需要在提交给老师之前进行配置。(我没有机会完成作业,因为我是自学编程的 :(

(网站不断地催促我格式化我的代码,已经半个小时了,我并没有做错事情。所以我决定一点一点地发布来调试它。最终我完成了这项工作...)


这不是一项作业,我希望在我还在学校的时候能有这么酷的作业。这是我每个月或几分钟尝试自己做的事情。 - sweetroll

0
如果您只想逐行编写代码,可以使用以下方法:
int w = Console.WindowWidth;
int h = Console.WindowHeight;

int i = 0;

while (i < h)
{
    Console.WriteLine(new string('.', w-1));
    Thread.Sleep(20);
    i++;
}

我需要对每个字符进行控制,并最终改变每条垂直线向下书写的速度。不幸的是,那样做行不通。 - sweetroll
抱歉,我无法提供帮助。要完整的矩阵效果,请不要忘记使用“Console.ForegroundColor = ConsoleColor.Green;”。 - SBFrancies
哈哈,没问题。等我搞定这个之后,我一定会添加的。 - sweetroll

0

只需要稍微修改一下代码,就可以模拟出矩阵代码雨效果。

    int w = Console.WindowWidth;
    int h = Console.WindowHeight;

    int i = 0;
    while (i < h)
    {
        int j = 0;
        string s = "";
        Thread.Sleep(10);
        while (j < w)
        {
            Console.SetCursorPosition(j, i);
            s += ".";
            j++;
        }
        Console.Write(s);
        i++;
    }

基本上我在这里做的就是对逻辑进行了一些重组,并在正确的位置放置了适当的延迟。希望能有所帮助。

现在它似乎是从上到右再到下。这不是我想要的。我需要所有垂直线同时向下书写。 - sweetroll
那么你可以实例化一个字符串,在第二个while循环中将字符添加到字符串中,然后在第二个循环中打印它,这与@SBFrancies的答案非常相似。 - シアジョナサン

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接