Консольная диаграмма

Мне нужен способ нарисовать Dictionary в консольном приложении, например

 Dictionary chartList = new Dictionary() { {50,31}, // x = 50, y = 31 {71,87}, {25,66}, {94,15}, {33,94} }; DrawChart(chartList); 

должно привести к чему-то вроде

введите описание изображения здесь

Я зашел так далеко, но я застрял в методе IsHit , который определяет, следует ли в текущих координатах установить точку или нет. Может ли кто-нибудь помочь мне в этот момент? Он всегда возвращает true.

 public static void DrawChart(Dictionary dict) { int consoleWidth = 78; int consoleHeight = 20; Console.WriteLine(dict.Max(x => x.Key).ToString()); Func IsHit = (hx, hy) => dict.Any(dct => dct.Key / dict.Max(x => x.Key) == hx / dict.Max(x => x.Key) && dct.Value / dict.Max(x => x.Value) == hy / dict.Max(x => x.Value)); for (int i = 0; i < consoleHeight; i++) { Console.Write(i == 0 ? '┌' : '│'); for (int j = 0; j  x.Key) + "/" + dict.Min(x => x.Value)).PadRight(consoleWidth / 3)); Console.Write((dict.Max(x => x.Value) / 2).ToString().PadLeft(consoleWidth / 3 / 2).PadRight(consoleWidth / 3)); Console.WriteLine(dict.Max(x => x.Value).ToString().PadLeft(consoleWidth / 3)); } 

Код ниже должен дать вам некоторую идею. Сначала нужно ввести Point , потому что работа с Dictionary и его свойствами Key и Value вместо обычных имен, таких как X и Y является кошмаром. Кроме того, в словаре вы не можете хранить несколько точек с одинаковой координатой X , что мало смысла.

 public struct Point { public Point(int x, int y) { this.X = x; this.Y = y; } public int X { get; } public int Y { get; } } 

Затем немного изменил DrawChart :

  public static void DrawChart(List dict) { int consoleWidth = 78; int consoleHeight = 20; int actualConsoleHeight = consoleHeight * 2; var minX = dict.Min(c => cX); var minY = dict.Min(c => cY); var maxX = dict.Max(c => cX); var maxY = dict.Max(c => cY); Console.WriteLine(maxX); // normalize points to new coordinates var normalized = dict. Select(c => new Point(cX - minX, cY - minY)). Select(c => new Point((int)Math.Round((float) (cX) / (maxX - minX) * (consoleWidth - 1)), (int)Math.Round((float) (cY) / (maxY - minY) * (actualConsoleHeight - 1)))).ToArray(); Func IsHit = (hx, hy) => { return normalized.Any(c => cX == hx && cY == hy); }; for (int y = actualConsoleHeight - 1; y > 0; y -= 2) { Console.Write(y == actualConsoleHeight - 1 ? '┌' : '│'); for (int x = 0; x < consoleWidth; x++) { bool hitTop = IsHit(x, y); bool hitBottom = IsHit(x, y - 1); if (hitBottom && hitTop) { Console.ForegroundColor = ConsoleColor.Red; Console.BackgroundColor = ConsoleColor.Black; Console.Write('█'); } else if (hitTop) { Console.ForegroundColor = ConsoleColor.Red; Console.BackgroundColor = ConsoleColor.Black; Console.Write('▀'); } else if (hitBottom) { Console.ForegroundColor = ConsoleColor.Black; Console.BackgroundColor = ConsoleColor.Red; Console.Write('▀'); } else { Console.ForegroundColor = ConsoleColor.Black; Console.BackgroundColor = ConsoleColor.Black; Console.Write('▀'); } } Console.ResetColor(); Console.WriteLine(); } Console.WriteLine('└' + new string('─', (consoleWidth / 2) - 1) + '┴' + new string('─', (consoleWidth / 2) - 1) + '┘'); Console.Write((dict.Min(x => xX) + "/" + dict.Min(x => xY)).PadRight(consoleWidth / 3)); Console.Write((dict.Max(x => xY) / 2).ToString().PadLeft(consoleWidth / 3 / 2).PadRight(consoleWidth / 3)); Console.WriteLine(dict.Max(x => xY).ToString().PadLeft(consoleWidth / 3)); } 

И использование:

 static void Main(string[] args) { var chartList = new List { new Point(50, 31), // x = 50, y = 31 new Point(71, 87), new Point(71, 89), new Point(25, 66), new Point(94, 15), new Point(33, 94) }; DrawChart(chartList); Console.ReadKey(); } 

Результат:

Результат

Реальная ошибка имеет очень длинную однострочную функцию. Это затрудняет чтение, и это создает проблемы даже для умного человека, подобного вам.

IsHit () – довольно сложная функция, поэтому раскройте ее …

Вам нужно масштабировать координаты, чтобы они вписывались в консольное окно.
Я предположил, что координаты windows (1,1) – (consoleWidth, consoleHeight)

  private static bool IsConsoleHit(Dictionary dict, int consoleWidth, int consoleHeight, int hx, int hy) { int minX = dict.Min(x => x.Key); int maxX = dict.Max(x => x.Key); int minY = dict.Min(x => x.Value); int maxY = dict.Max(x => x.Value); foreach (KeyValuePair pair in dict) { // (x,y) starting at (0,0) int x = pair.Key - minX; int y = pair.Value - minY; // convert to (0..1.0, 0..1.0) double dx = x / Math.Max(maxX - minX, 1.0); double dy = y / Math.Max(maxY - minY, 1.0); // Scale to (1,1) upto (consoleWidth, consoleHeight) int sx = (int)(dx * (consoleWidth - 1)) + 1; int sy = (int)(dy * (consoleHeight - 1)) + 1; if (hx == sx && hy == sy) { return true; // Its a hit } } return false; } 

А затем сделать из него lambda-функцию:

  Func IsHit = ((hx, hy) => IsConsoleHit(dict, consoleWidth, consoleHeight, hx, hy); 

Поэтому в будущем я бы никогда не использовал однострочные функции для вычисления вещей. Используйте обычный метод, предоставляя вам пространство для распространения и работы.

По крайней мере, у меня нет ответа, который решает вашу проблему, но несколько советов, которые могут вызвать проблемы:

  1. Ваша система координат ошибочна. Метод IsHit() получает в верхнем левом углу (где вы начинаете рисовать) координаты 0/0, но он должен быть 0/19 и для последнего элемента 77/19, но он должен быть 77/0.
  2. Для расчета вы делите целые числа, что приводит к тому, что все дроби теряются, и ваш запрос всегда возвращает true.

Может быть, вам следует написать метод перевода, который будет переводиться с позиции курсора на значение x / y, а затем проверять переведенную позицию вместо того, чтобы пытаться сделать это за один шаг. Я думаю, используя этот подход, вы сможете решить проблему самостоятельно.