/
d4nil
/
Konenkov_2413_Course_Work
Обзор
Документация
Войти
/
d4nil
/
Konenkov_2413_Course_Work
Код
Запросы
1
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
Game
TapTapDash.Console/ConsoleBufferHelper.cs
228 строк
8 KB
Konenkov Daniel
feat: добавлены тесты
10 янв 2026, 00:34
10 янв 2026, 00:34
8c4388f
Код
Авторство
О чём код?
using System; using System.IO; using System.Runtime.InteropServices; using Microsoft.Win32.SafeHandles; namespace TapTapDash.Console; /// <summary> /// Вспомогательный класс для быстрого вывода в консоль через буфер Win32 API /// Основан на идеях из Program (2).cs /// </summary> public class ConsoleBufferHelper : IDisposable { private SafeFileHandle? _consoleHandle; private bool _isAvailable; // Win32 API импорты для работы с консолью [DllImport("Kernel32.dll", SetLastError = true, CharSet = CharSet.Auto)] private static extern SafeFileHandle CreateFile( string fileName, [MarshalAs(UnmanagedType.U4)] uint fileAccess, [MarshalAs(UnmanagedType.U4)] uint fileShare, IntPtr securityAttributes, [MarshalAs(UnmanagedType.U4)] FileMode creationDisposition, [MarshalAs(UnmanagedType.U4)] int flags, IntPtr template); [DllImport("kernel32.dll", SetLastError = true)] private static extern bool WriteConsoleOutput( SafeFileHandle hConsoleOutput, CharInfo[] lpBuffer, Coord dwBufferSize, Coord dwBufferCoord, ref SmallRect lpWriteRegion); [StructLayout(LayoutKind.Sequential)] private struct Coord { public short X; public short Y; public Coord(short X, short Y) { this.X = X; this.Y = Y; } } [StructLayout(LayoutKind.Explicit)] private struct CharUnion { [FieldOffset(0)] public char UnicodeChar; [FieldOffset(0)] public byte AsciiChar; } [StructLayout(LayoutKind.Explicit)] private struct CharInfo { [FieldOffset(0)] public CharUnion Char; [FieldOffset(2)] public short Attributes; } [StructLayout(LayoutKind.Sequential)] private struct SmallRect { public short Left; public short Top; public short Right; public short Bottom; } // Константы для цветов консоли (foreground) private const short FOREGROUND_BLUE = 0x0001; private const short FOREGROUND_GREEN = 0x0002; private const short FOREGROUND_RED = 0x0004; private const short FOREGROUND_INTENSITY = 0x0008; private const short BACKGROUND_BLUE = 0x0010; private const short BACKGROUND_GREEN = 0x0020; private const short BACKGROUND_RED = 0x0040; private const short BACKGROUND_INTENSITY = 0x0080; /// <summary> /// Конструктор - инициализирует буфер консоли /// </summary> public ConsoleBufferHelper() { // Проверяем, работаем ли мы на Windows if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { try { // Создаем handle для консоли (CONOUT$) _consoleHandle = CreateFile("CONOUT$", 0x40000000, 2, IntPtr.Zero, FileMode.Open, 0, IntPtr.Zero); _isAvailable = !_consoleHandle.IsInvalid; } catch { _isAvailable = false; } } else { _isAvailable = false; } } /// <summary> /// Проверяет, доступен ли быстрый буферный вывод /// </summary> public bool IsAvailable => _isAvailable && _consoleHandle != null && !_consoleHandle.IsInvalid; /// <summary> /// Быстрый вывод игрового поля в консоль через буфер /// </summary> /// <param name="width">Ширина поля</param> /// <param name="height">Высота поля</param> /// <param name="startX">Начальная X координата на экране</param> /// <param name="startY">Начальная Y координата на экране</param> /// <param name="getChar">Функция для получения символа в позиции (x, y)</param> /// <param name="getColor">Функция для получения цвета в позиции (x, y)</param> public void WriteGameField(int width, int height, int startX, int startY, Func<int, int, char> getChar, Func<int, int, ConsoleColor> getColor) { if (!IsAvailable) { // Fallback на обычный вывод, если буфер недоступен WriteGameFieldFallback(width, height, startX, startY, getChar, getColor); return; } try { int bufferWidth = width; int bufferHeight = height; CharInfo[] buffer = new CharInfo[bufferWidth * bufferHeight]; // Заполняем буфер символами и цветами for (int y = 0; y < bufferHeight; y++) { for (int x = 0; x < bufferWidth; x++) { int index = y * bufferWidth + x; char ch = getChar(x, y); ConsoleColor color = getColor(x, y); buffer[index].Char.UnicodeChar = ch; buffer[index].Attributes = GetColorAttribute(color); } } // Записываем буфер в консоль SmallRect rect = new SmallRect { Left = (short)startX, Top = (short)startY, Right = (short)(startX + width - 1), Bottom = (short)(startY + height - 1) }; WriteConsoleOutput(_consoleHandle!, buffer, new Coord { X = (short)bufferWidth, Y = (short)bufferHeight }, new Coord { X = 0, Y = 0 }, ref rect); } catch { // В случае ошибки используем fallback WriteGameFieldFallback(width, height, startX, startY, getChar, getColor); } } /// <summary> /// Обычный вывод (fallback) если буфер недоступен /// </summary> private void WriteGameFieldFallback(int width, int height, int startX, int startY, Func<int, int, char> getChar, Func<int, int, ConsoleColor> getColor) { for (int y = 0; y < height; y++) { System.Console.SetCursorPosition(startX, startY + y); for (int x = 0; x < width; x++) { char ch = getChar(x, y); ConsoleColor color = getColor(x, y); System.Console.ForegroundColor = color; System.Console.Write(ch); } System.Console.ResetColor(); } } /// <summary> /// Преобразует ConsoleColor в атрибут Win32 /// </summary> private short GetColorAttribute(ConsoleColor color) { return color switch { ConsoleColor.Black => 0, ConsoleColor.DarkBlue => FOREGROUND_BLUE, ConsoleColor.DarkGreen => FOREGROUND_GREEN, ConsoleColor.DarkCyan => FOREGROUND_BLUE | FOREGROUND_GREEN, ConsoleColor.DarkRed => FOREGROUND_RED, ConsoleColor.DarkMagenta => FOREGROUND_BLUE | FOREGROUND_RED, ConsoleColor.DarkYellow => FOREGROUND_GREEN | FOREGROUND_RED, ConsoleColor.Gray => FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED, ConsoleColor.DarkGray => FOREGROUND_INTENSITY, ConsoleColor.Blue => FOREGROUND_BLUE | FOREGROUND_INTENSITY, ConsoleColor.Green => FOREGROUND_GREEN | FOREGROUND_INTENSITY, ConsoleColor.Cyan => FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_INTENSITY, ConsoleColor.Red => FOREGROUND_RED | FOREGROUND_INTENSITY, ConsoleColor.Magenta => FOREGROUND_BLUE | FOREGROUND_RED | FOREGROUND_INTENSITY, ConsoleColor.Yellow => FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY, ConsoleColor.White => FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY, _ => FOREGROUND_BLUE | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_INTENSITY }; } /// <summary> /// Освобождение ресурсов /// </summary> public void Dispose() { _consoleHandle?.Dispose(); _consoleHandle = null; _isAvailable = false; } }