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

How to write in multiple positions in a console application at the same time? C#

我想要与控制台宽度一样多的行同时向下写入一个字符到控制台的高度。我已经完成了大部分,但它是从上到下到右等等...

如果您需要帮助来理解我的意思,请想想矩阵码雨。

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++;
}

如果你只想一次写一行,你可以使用这个:

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

int i = 0;

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

稍微修改一下就可以让代码模拟矩阵码雨。

    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++;
    }

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

我要做的是构造一个 List<string> lines;,其中包含您要写入控制台的行 window,其中每行与控制台宽度一样宽。然后以相反的顺序将列表打印到控制台 window,所以第一行(在 lines[0] 处)将始终是最后打印的,并且始终位于控制台的底部 window.


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

我们可以在用于打印其项目的同一循环中将新项目添加到列表中。然而,在我们添加一行之前,我们首先检查列表中的行数是否已经与控制台中的行数一样多 window (Console.WindowHeight)。如果有,那么我们只需在添加新行之前删除 lines[0] 处的行。这样,List<string> lines连同控制台window.

就是"scrolling"

滚动速度由 Thread.Sleep 控制,但此代码可以很容易地添加到 Timer 中,这样其他工作就可以在后台进行(就像这是为了是 "screensaver",并且您想等待用户输入 "wake up")。但无论我们如何决定实现速度,我决定创建一个 enum,其值表示 Thread.Sleep 实现将使用的毫秒数:

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

我还会创建一个辅助方法,为您创建一个 "random" 行。它可以接受一个指定 "density" 的整数,这意味着您希望在该行中包含多少个字符。 density代表一个百分比,所以如果指定了10,那么我们在0到99之间随机取一个数,如果小于10则我们在字符串中添加一个随机矩阵字符(否则我们添加space 个字符)。

此外,为了更接近地复制矩阵,我还选择了 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();
    }

接下来,我们有 main 方法,它用随机行填充列表(使用 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 重复,但代码版本继续正常滚动):

这个任务看起来像是一项任务,所以我是在指导你,而不是提供实施。如果是作业就给你答案是不道德的。

您正在寻找更适合的算法。所述算法从上到下填充控制台,因为它首先迭代填充 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

这个草案也将是最终的结果。 不是同时填充它们,您实际上需要的是在线程到达下一个平方点后让线程休眠。 (计算机速度如此之快,它可能会在一秒钟内完成所有工作以提供屏幕,黑色控制台 window 会在没有任何通知的情况下消失。)

当时你post答题的时候,我也是从头开始解题。我认为交替填充 X 轴和 Y 轴是解决方案,但是 每次正方形扩展时停止对于获得效果来说更为重要。

在我看来,这也不是线程问题标签。

让我们总结一下上面的模式:

  1. 假设i和j分别是x和y坐标。
  2. 每次迭代需要你从 (i, j) 和 n*2+1 步到达 (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。这是有意为之的,如果您正在做作业,则需要在将其发送给老师之前对其进行配置。 (我没有机会做作业,因为我自学编程:(

(网站不断催促我格式化代码,半个小时了,我就是没做错。所以我决定post一点一点地调试它。最后我已经完成了工作...)