IGRODELOFF

SimpleGameMap_v3

Feb 27th, 2026
37
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 48.41 KB | Gaming | 0 0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Text;
  4.  
  5. namespace SimpleGameMap
  6. {
  7.     internal static class Program
  8.     {
  9.         private struct Record
  10.         {
  11.             public int Width;
  12.             public int Height;
  13.             public int TreasureCount;
  14.             public TimeSpan Time;
  15.             public int Difficulty;
  16.             public int VisibilityMode;
  17.         }
  18.  
  19.         private const char Wall = '#';
  20.         private const char Floor = '.';
  21.         private const char Treasure = 'T';
  22.         private const char Player = '@';
  23.         private const char Enemy = 'E';
  24.  
  25.         private const char BorderTopLeft = '╔';
  26.         private const char BorderTopRight = '╗';
  27.         private const char BorderBottomLeft = '╚';
  28.         private const char BorderBottomRight = '╝';
  29.         private const char BorderHorizontal = '═';
  30.         private const char BorderVertical = '║';
  31.  
  32.         private const char InfoBorderTopLeft = '┌';
  33.         private const char InfoBorderTopRight = '┐';
  34.         private const char InfoBorderBottomLeft = '└';
  35.         private const char InfoBorderBottomRight = '┘';
  36.         private const char InfoBorderHorizontal = '─';
  37.         private const char InfoBorderVertical = '│';
  38.  
  39.         private static char[,] map;
  40.         private static int playerRow;
  41.         private static int playerColumn;
  42.  
  43.         private static int currentScore;
  44.         private static int totalTreasures;
  45.         private static int mapHeight;
  46.         private static int mapWidth;
  47.         private static int treasureCount;
  48.  
  49.         private static DateTime startTime;
  50.         private static TimeSpan playTime;
  51.  
  52.         private static int difficulty = 2;
  53.  
  54.         private static int visibilityMode = 1;
  55.         private const int FogRadius = 6;
  56.         private const int FlashlightRadius = 8;
  57.  
  58.         private enum Facing { Up, Down, Left, Right }
  59.         private static Facing facing = Facing.Up;
  60.  
  61.         private static List<(int row, int column)> enemies = new List<(int row, int column)>();
  62.         private static int enemyCount = 0;
  63.         private static int enemyMoveEvery = 2;
  64.         private static int stepCounter = 0;
  65.  
  66.         private static readonly Random random = new Random();
  67.         private static readonly List<Record> records = new List<Record>();
  68.  
  69.         private static int mapStartColumn;
  70.         private static int mapStartRow;
  71.  
  72.         private static int hudLeft;
  73.         private static int hudTop;
  74.         private static int hudWidth;
  75.  
  76.         private static int previousPlayerRow;
  77.         private static int previousPlayerColumn;
  78.         private static Facing previousFacing;
  79.  
  80.         static void Main()
  81.         {
  82.             Console.OutputEncoding = Encoding.UTF8;
  83.             Console.CursorVisible = false;
  84.  
  85.             bool exitProgram = false;
  86.  
  87.             while (exitProgram == false)
  88.             {
  89.                 Console.Clear();
  90.  
  91.                 int menuWidth = 30;
  92.                 int menuHeight = 7;
  93.                 int startColumn = Math.Max(0, (Console.WindowWidth - menuWidth) / 2);
  94.                 int startRow = Math.Max(0, (Console.WindowHeight - menuHeight) / 2);
  95.  
  96.                 DrawFrame(startColumn, startRow, menuWidth, menuHeight);
  97.  
  98.                 ConsoleColor originalColor = Console.ForegroundColor;
  99.                 Console.ForegroundColor = ConsoleColor.Cyan;
  100.  
  101.                 string title = "ГЛАВНОЕ МЕНЮ";
  102.  
  103.                 Console.SetCursorPosition(startColumn + (menuWidth - title.Length) / 2, startRow + 1);
  104.                 Console.Write(title);
  105.  
  106.                 Console.ForegroundColor = originalColor;
  107.  
  108.                 string newGameText = "1. Новая игра";
  109.                 string recordsText = "2. История побед";
  110.                 string exitText = "3. Выход";
  111.  
  112.                 Console.SetCursorPosition(startColumn + (menuWidth - newGameText.Length) / 2, startRow + 3);
  113.                 Console.Write(newGameText);
  114.  
  115.                 Console.SetCursorPosition(startColumn + (menuWidth - recordsText.Length) / 2, startRow + 4);
  116.                 Console.Write(recordsText);
  117.  
  118.                 Console.SetCursorPosition(startColumn + (menuWidth - exitText.Length) / 2, startRow + 5);
  119.                 Console.Write(exitText);
  120.  
  121.                 ConsoleKeyInfo keyInfo = Console.ReadKey(true);
  122.  
  123.                 switch (keyInfo.KeyChar)
  124.                 {
  125.                     case '1':
  126.                         StartNewGame();
  127.                         break;
  128.                     case '2':
  129.                         ShowRecords();
  130.                         break;
  131.                     case '3':
  132.                         exitProgram = true;
  133.                         break;
  134.                 }
  135.             }
  136.  
  137.             Console.Clear();
  138.             Console.CursorVisible = true;
  139.         }
  140.  
  141.         private static void StartNewGame()
  142.         {
  143.             difficulty = GetIntFromUser("Введите сложность", 1, 3);
  144.             ApplyDifficultyParams();
  145.  
  146.             visibilityMode = GetVisibilityModeFromUser();
  147.             facing = Facing.Up;
  148.  
  149.             int requestedWidth = GetIntFromUser("Введите ширину карты", 5, Math.Max(5, Console.WindowWidth - 2));
  150.             int requestedHeight = GetIntFromUser("Введите высоту карты", 5, Math.Max(5, Console.WindowHeight - 6));
  151.  
  152.             int maxTreasures = (requestedHeight - 2) * (requestedWidth - 2) / 2;
  153.  
  154.             if (maxTreasures < 1)
  155.             {
  156.                 maxTreasures = 1;
  157.             }
  158.  
  159.             int requestedTreasures = GetIntFromUser("Введите количество сокровищ", 1, maxTreasures);
  160.  
  161.             mapWidth = requestedWidth;
  162.             mapHeight = requestedHeight;
  163.             treasureCount = requestedTreasures;
  164.  
  165.             bool generated = GenerateMapWithAttempts(25);
  166.  
  167.             if (generated == false)
  168.             {
  169.                 ShowMessageBox("Ошибка", "Не удалось сгенерировать подходящую карту.\nПопробуйте меньший размер или меньше сокровищ.");
  170.  
  171.                 return;
  172.             }
  173.  
  174.             currentScore = 0;
  175.             stepCounter = 0;
  176.  
  177.             bool victory = RunGame();
  178.  
  179.             if (victory == true)
  180.             {
  181.                 Record record = new Record
  182.                 {
  183.                     Width = mapWidth,
  184.                     Height = mapHeight,
  185.                     TreasureCount = totalTreasures,
  186.                     Time = playTime,
  187.                     Difficulty = difficulty,
  188.                     VisibilityMode = visibilityMode
  189.                 };
  190.  
  191.                 records.Add(record);
  192.                 ShowRecords();
  193.             }
  194.         }
  195.  
  196.         private static int GetVisibilityModeFromUser()
  197.         {
  198.             bool isSelected = false;
  199.             int selectedMode = 1;
  200.  
  201.             while (isSelected == false)
  202.             {
  203.                 Console.Clear();
  204.  
  205.                 string title = "РЕЖИМ ВИДИМОСТИ";
  206.                 string line1 = "1. Без тумана (видно всё)";
  207.                 string line2 = "2. Туман (радиус " + FogRadius + ")";
  208.                 string line3 = "3. Фонарик (конус + радиус " + FlashlightRadius + ")";
  209.                 string hint = "Введите 1/2/3 и нажмите Enter:";
  210.  
  211.                 int maxLineLength = title.Length;
  212.  
  213.                 if (line1.Length > maxLineLength)
  214.                 {
  215.                     maxLineLength = line1.Length;
  216.                 }
  217.  
  218.                 if (line2.Length > maxLineLength)
  219.                 {
  220.                     maxLineLength = line2.Length;
  221.                 }
  222.  
  223.                 if (line3.Length > maxLineLength)
  224.                 {
  225.                     maxLineLength = line3.Length;
  226.                 }
  227.  
  228.                 if (hint.Length > maxLineLength)
  229.                 {
  230.                     maxLineLength = hint.Length;
  231.                 }
  232.  
  233.                 int frameWidth = Math.Min(Console.WindowWidth, maxLineLength + 6);
  234.                 int frameHeight = Math.Min(Console.WindowHeight, 10);
  235.  
  236.                 int frameLeft = Math.Max(0, (Console.WindowWidth - frameWidth) / 2);
  237.                 int frameTop = Math.Max(0, (Console.WindowHeight - frameHeight) / 2);
  238.  
  239.                 DrawFrame(frameLeft, frameTop, frameWidth, frameHeight);
  240.  
  241.                 ConsoleColor originalColor = Console.ForegroundColor;
  242.                 Console.ForegroundColor = ConsoleColor.Cyan;
  243.  
  244.                 Console.SetCursorPosition(frameLeft + (frameWidth - title.Length) / 2, frameTop + 1);
  245.                 Console.Write(title);
  246.  
  247.                 Console.ForegroundColor = originalColor;
  248.  
  249.                 Console.SetCursorPosition(frameLeft + 2, frameTop + 3);
  250.                 Console.Write(line1);
  251.  
  252.                 Console.SetCursorPosition(frameLeft + 2, frameTop + 4);
  253.                 Console.Write(line2);
  254.  
  255.                 Console.SetCursorPosition(frameLeft + 2, frameTop + 5);
  256.                 Console.Write(line3);
  257.  
  258.                 Console.SetCursorPosition(frameLeft + 2, frameTop + 7);
  259.                 Console.Write(hint);
  260.  
  261.                 Console.SetCursorPosition(frameLeft + 2, frameTop + 8);
  262.                 Console.CursorVisible = true;
  263.  
  264.                 string input = Console.ReadLine();
  265.  
  266.                 Console.CursorVisible = false;
  267.  
  268.                 int parsedMode;
  269.                 bool parsedOk = int.TryParse(input, out parsedMode);
  270.  
  271.                 if (parsedOk == true && parsedMode >= 1 && parsedMode <= 3)
  272.                 {
  273.                     selectedMode = parsedMode;
  274.                     isSelected = true;
  275.                 }
  276.                 else
  277.                 {
  278.                     Console.ForegroundColor = ConsoleColor.Red;
  279.                     Console.SetCursorPosition(frameLeft + 2, frameTop + 8);
  280.                     Console.Write("Ошибка: введите 1, 2 или 3.".PadRight(Math.Max(0, frameWidth - 4)));
  281.                     Console.ForegroundColor = originalColor;
  282.                     Console.ReadKey(true);
  283.                 }
  284.             }
  285.  
  286.             return selectedMode;
  287.         }
  288.  
  289.         private static void ApplyDifficultyParams()
  290.         {
  291.             if (difficulty == 1)
  292.             {
  293.                 enemyCount = 1;
  294.                 enemyMoveEvery = 3;
  295.             }
  296.             else if (difficulty == 2)
  297.             {
  298.                 enemyCount = 2;
  299.                 enemyMoveEvery = 2;
  300.             }
  301.             else
  302.             {
  303.                 enemyCount = 3;
  304.                 enemyMoveEvery = 1;
  305.             }
  306.         }
  307.  
  308.         private static bool GenerateMapWithAttempts(int maxAttempts)
  309.         {
  310.             int attemptIndex = 0;
  311.  
  312.             while (attemptIndex < maxAttempts)
  313.             {
  314.                 InitializeMap();
  315.                 SpawnPlayer();
  316.  
  317.                 bool[,] reachableMap = BuildReachableMapFromPlayer();
  318.                 int reachableCount = CountReachableNonWall(reachableMap);
  319.  
  320.                 if (reachableCount >= 1 + treasureCount)
  321.                 {
  322.                     PlaceTreasures();
  323.                     SpawnEnemies();
  324.  
  325.                     return true;
  326.                 }
  327.  
  328.                 attemptIndex++;
  329.             }
  330.  
  331.             InitializeMap();
  332.             SpawnPlayer();
  333.             PlaceTreasures();
  334.             SpawnEnemies();
  335.  
  336.             return totalTreasures > 0;
  337.         }
  338.  
  339.         private static bool RunGame()
  340.         {
  341.             Console.CursorVisible = false;
  342.             Console.Clear();
  343.  
  344.             startTime = DateTime.Now;
  345.  
  346.             ComputeOffsets();
  347.             DrawStaticFrameOnce();
  348.             DrawHudFrameOnce();
  349.             UpdateHudText();
  350.  
  351.             previousPlayerRow = playerRow;
  352.             previousPlayerColumn = playerColumn;
  353.             previousFacing = facing;
  354.  
  355.             DrawOverlaysInArea(0, 0, mapHeight - 1, mapWidth - 1);
  356.  
  357.             bool isRunning = true;
  358.             bool victory = false;
  359.  
  360.             while (isRunning == true)
  361.             {
  362.                 EnsureWindowFitsOrPauseAndRedrawIfNeeded();
  363.  
  364.                 ConsoleKeyInfo keyInfo = Console.ReadKey(true);
  365.  
  366.                 if (keyInfo.Key == ConsoleKey.Escape)
  367.                 {
  368.                     PauseMenuResult pauseResult = ShowPauseMenu();
  369.                     if (pauseResult == PauseMenuResult.ExitToMainMenu)
  370.                     {
  371.                         isRunning = false;
  372.                     }
  373.                     else
  374.                     {
  375.                         RedrawAllBecauseConsoleClearedByMenu();
  376.                     }
  377.  
  378.                     continue;
  379.                 }
  380.  
  381.                 bool moved;
  382.                 bool facingChanged;
  383.                 ProcessPlayerInput(keyInfo, out moved, out facingChanged);
  384.  
  385.                 if (moved == false && facingChanged == false)
  386.                 {
  387.                     continue;
  388.                 }
  389.  
  390.                 List<(int row, int column)> enemiesBeforeMove = CopyEnemiesList();
  391.  
  392.                 if (moved == true)
  393.                 {
  394.                     stepCounter++;
  395.  
  396.                     if (enemyCount > 0 && enemyMoveEvery > 0 && (stepCounter % enemyMoveEvery == 0))
  397.                     {
  398.                         MoveEnemies();
  399.                     }
  400.                 }
  401.  
  402.                 if (IsEnemyOnPlayer() == true)
  403.                 {
  404.                     victory = false;
  405.                     isRunning = false;
  406.                 }
  407.                 else if (currentScore == totalTreasures && totalTreasures > 0)
  408.                 {
  409.                     victory = true;
  410.                     isRunning = false;
  411.                 }
  412.  
  413.                 UpdateScreenAfterChanges(previousPlayerRow, previousPlayerColumn, previousFacing, enemiesBeforeMove);
  414.  
  415.                 previousPlayerRow = playerRow;
  416.                 previousPlayerColumn = playerColumn;
  417.                 previousFacing = facing;
  418.             }
  419.  
  420.             playTime = DateTime.Now - startTime;
  421.  
  422.             if (victory == true)
  423.             {
  424.                 ShowVictory();
  425.                 Console.CursorVisible = true;
  426.  
  427.                 return true;
  428.             }
  429.  
  430.             ShowGameInterrupted();
  431.             Console.CursorVisible = true;
  432.  
  433.             return false;
  434.         }
  435.  
  436.         private static List<(int row, int column)> CopyEnemiesList()
  437.         {
  438.             List<(int row, int column)> copied = new List<(int row, int column)>(enemies.Count);
  439.  
  440.             int enemyIndex = 0;
  441.  
  442.             while (enemyIndex < enemies.Count)
  443.             {
  444.                 copied.Add(enemies[enemyIndex]);
  445.                 enemyIndex++;
  446.             }
  447.  
  448.             return copied;
  449.         }
  450.  
  451.         private static void RedrawAllBecauseConsoleClearedByMenu()
  452.         {
  453.             Console.Clear();
  454.  
  455.             ComputeOffsets();
  456.             DrawStaticFrameOnce();
  457.             DrawHudFrameOnce();
  458.             UpdateHudText();
  459.             DrawOverlaysInArea(0, 0, mapHeight - 1, mapWidth - 1);
  460.         }
  461.  
  462.         private static void EnsureWindowFitsOrPauseAndRedrawIfNeeded()
  463.         {
  464.             int neededWidth = mapWidth + 4;
  465.             int neededHeight = mapHeight + 8;
  466.  
  467.             if (Console.WindowWidth < neededWidth || Console.WindowHeight < neededHeight)
  468.             {
  469.                 Console.Clear();
  470.                 Console.CursorVisible = false;
  471.  
  472.                 Console.WriteLine("Окно слишком маленькое для текущей карты.");
  473.                 Console.WriteLine("Нужно минимум: " + neededWidth + "x" + neededHeight);
  474.                 Console.WriteLine("Сейчас:        " + Console.WindowWidth + "x" + Console.WindowHeight);
  475.                 Console.WriteLine();
  476.                 Console.WriteLine("Увеличьте окно и нажмите любую клавишу...");
  477.                 Console.ReadKey(true);
  478.  
  479.                 RedrawAllBecauseConsoleClearedByMenu();
  480.             }
  481.         }
  482.  
  483.         private static void UpdateScreenAfterChanges(
  484.             int oldPlayerRow,
  485.             int oldPlayerColumn,
  486.  
  487.             Facing oldFacing,
  488.  
  489.             List<(int row, int column)> oldEnemies)
  490.         {
  491.             UpdateHudText();
  492.  
  493.             if (visibilityMode == 1)
  494.             {
  495.                 RedrawSingleCell(oldPlayerRow, oldPlayerColumn);
  496.                 RedrawSingleCell(playerRow, playerColumn);
  497.  
  498.                 int oldEnemyIndex = 0;
  499.  
  500.                 while (oldEnemyIndex < oldEnemies.Count)
  501.                 {
  502.                     RedrawSingleCell(oldEnemies[oldEnemyIndex].row, oldEnemies[oldEnemyIndex].column);
  503.                     oldEnemyIndex++;
  504.                 }
  505.  
  506.                 int newEnemyIndex = 0;
  507.  
  508.                 while (newEnemyIndex < enemies.Count)
  509.                 {
  510.                     RedrawSingleCell(enemies[newEnemyIndex].row, enemies[newEnemyIndex].column);
  511.                     newEnemyIndex++;
  512.                 }
  513.  
  514.                 return;
  515.             }
  516.  
  517.             int visibleRange = (visibilityMode == 2) ? FogRadius : FlashlightRadius;
  518.  
  519.             int areaTopRow = Math.Min(oldPlayerRow, playerRow) - (visibleRange + 1);
  520.             int areaBottomRow = Math.Max(oldPlayerRow, playerRow) + (visibleRange + 1);
  521.             int areaLeftColumn = Math.Min(oldPlayerColumn, playerColumn) - (visibleRange + 1);
  522.             int areaRightColumn = Math.Max(oldPlayerColumn, playerColumn) + (visibleRange + 1);
  523.  
  524.             ClampArea(ref areaTopRow, ref areaLeftColumn, ref areaBottomRow, ref areaRightColumn);
  525.  
  526.             RedrawBaseInArea(areaTopRow, areaLeftColumn, areaBottomRow, areaRightColumn);
  527.             DrawOverlaysInArea(areaTopRow, areaLeftColumn, areaBottomRow, areaRightColumn);
  528.         }
  529.  
  530.         private static void ClampArea(ref int topRow, ref int leftColumn, ref int bottomRow, ref int rightColumn)
  531.         {
  532.             if (topRow < 0)
  533.             {
  534.                 topRow = 0;
  535.             }
  536.  
  537.             if (leftColumn < 0)
  538.             {
  539.                 leftColumn = 0;
  540.             }
  541.  
  542.             if (bottomRow >= mapHeight)
  543.             {
  544.                 bottomRow = mapHeight - 1;
  545.             }
  546.  
  547.             if (rightColumn >= mapWidth)
  548.             {
  549.                 rightColumn = mapWidth - 1;
  550.             }
  551.         }
  552.  
  553.         private static void ComputeOffsets()
  554.         {
  555.             int mapTotalWidth = mapWidth + 2;
  556.  
  557.             mapStartColumn = Math.Max(0, (Console.WindowWidth - mapTotalWidth) / 2);
  558.             mapStartRow = 0;
  559.  
  560.             hudWidth = Math.Min(70, Console.WindowWidth - 2);
  561.  
  562.             if (hudWidth < 34)
  563.             {
  564.                 hudWidth = 34;
  565.             }
  566.  
  567.             hudLeft = mapStartColumn + (mapTotalWidth - hudWidth) / 2;
  568.  
  569.             if (hudLeft < 0)
  570.             {
  571.                 hudLeft = 0;
  572.             }
  573.  
  574.             hudTop = mapStartRow + mapHeight + 2;
  575.         }
  576.  
  577.         private static void DrawStaticFrameOnce()
  578.         {
  579.             Console.SetCursorPosition(mapStartColumn, mapStartRow);
  580.             Console.Write(BorderTopLeft);
  581.             Console.Write(new string(BorderHorizontal, mapWidth));
  582.             Console.Write(BorderTopRight);
  583.  
  584.             int rowIndex = 0;
  585.  
  586.             while (rowIndex < mapHeight)
  587.             {
  588.                 Console.SetCursorPosition(mapStartColumn, mapStartRow + 1 + rowIndex);
  589.                 Console.Write(BorderVertical);
  590.  
  591.                 int columnIndex = 0;
  592.  
  593.                 while (columnIndex < mapWidth)
  594.                 {
  595.                     DrawBaseCell(rowIndex, columnIndex);
  596.                     columnIndex++;
  597.                 }
  598.  
  599.                 Console.Write(BorderVertical);
  600.                 rowIndex++;
  601.             }
  602.  
  603.             int bottomLine = mapStartRow + mapHeight + 1;
  604.  
  605.             Console.SetCursorPosition(mapStartColumn, bottomLine);
  606.             Console.Write(BorderBottomLeft);
  607.             Console.Write(new string(BorderHorizontal, mapWidth));
  608.             Console.Write(BorderBottomRight);
  609.         }
  610.  
  611.         private static void DrawHudFrameOnce()
  612.         {
  613.             Console.SetCursorPosition(hudLeft, hudTop);
  614.             Console.Write(InfoBorderTopLeft);
  615.             Console.Write(new string(InfoBorderHorizontal, hudWidth - 2));
  616.             Console.Write(InfoBorderTopRight);
  617.  
  618.             Console.SetCursorPosition(hudLeft, hudTop + 1);
  619.             Console.Write(InfoBorderVertical);
  620.             Console.Write(new string(' ', hudWidth - 2));
  621.             Console.Write(InfoBorderVertical);
  622.  
  623.             Console.SetCursorPosition(hudLeft, hudTop + 2);
  624.             Console.Write(InfoBorderVertical);
  625.             Console.Write(new string(' ', hudWidth - 2));
  626.             Console.Write(InfoBorderVertical);
  627.  
  628.             Console.SetCursorPosition(hudLeft, hudTop + 3);
  629.             Console.Write(InfoBorderBottomLeft);
  630.             Console.Write(new string(InfoBorderHorizontal, hudWidth - 2));
  631.             Console.Write(InfoBorderBottomRight);
  632.         }
  633.  
  634.         private static void UpdateHudText()
  635.         {
  636.             int innerWidth = hudWidth - 2;
  637.  
  638.             string scoreText = "Счёт: " + currentScore + "  Осталось: " + Math.Max(0, totalTreasures - currentScore);
  639.             string infoText = "Сложность: " + difficulty + "   Враги: " + enemies.Count + "   Видимость: " + GetVisibilityName();
  640.             string helpText = "↑↓←→/WASD - ход, Esc - меню";
  641.  
  642.             Console.SetCursorPosition(hudLeft + 1, hudTop + 1);
  643.             Console.Write(CenterText(scoreText, innerWidth));
  644.  
  645.             Console.SetCursorPosition(hudLeft + 1, hudTop + 2);
  646.             Console.Write(CenterText(infoText + "   " + helpText, innerWidth));
  647.         }
  648.  
  649.         private static void RedrawBaseInArea(int topRow, int leftColumn, int bottomRow, int rightColumn)
  650.         {
  651.             int rowIndex = topRow;
  652.  
  653.             while (rowIndex <= bottomRow)
  654.             {
  655.                 int columnIndex = leftColumn;
  656.  
  657.                 while (columnIndex <= rightColumn)
  658.                 {
  659.                     Console.SetCursorPosition(mapStartColumn + 1 + columnIndex, mapStartRow + 1 + rowIndex);
  660.                     DrawBaseCell(rowIndex, columnIndex);
  661.                     columnIndex++;
  662.                 }
  663.  
  664.                 rowIndex++;
  665.             }
  666.         }
  667.  
  668.         private static void DrawOverlaysInArea(int topRow, int leftColumn, int bottomRow, int rightColumn)
  669.         {
  670.             int enemyIndex = 0;
  671.  
  672.             while (enemyIndex < enemies.Count)
  673.             {
  674.                 int enemyRow = enemies[enemyIndex].row;
  675.                 int enemyColumn = enemies[enemyIndex].column;
  676.  
  677.                 bool inside =
  678.                     enemyRow >= topRow && enemyRow <= bottomRow &&
  679.                     enemyColumn >= leftColumn && enemyColumn <= rightColumn;
  680.  
  681.                 if (inside == true && IsVisible(enemyRow, enemyColumn) == true)
  682.                 {
  683.                     Console.SetCursorPosition(mapStartColumn + 1 + enemyColumn, mapStartRow + 1 + enemyRow);
  684.                     Console.ForegroundColor = ConsoleColor.Red;
  685.                     Console.BackgroundColor = ConsoleColor.Black;
  686.                     Console.Write(Enemy);
  687.                     Console.ResetColor();
  688.                 }
  689.  
  690.                 enemyIndex++;
  691.             }
  692.  
  693.             bool playerInside =
  694.                 playerRow >= topRow && playerRow <= bottomRow &&
  695.                 playerColumn >= leftColumn && playerColumn <= rightColumn;
  696.  
  697.             if (playerInside == true)
  698.             {
  699.                 Console.SetCursorPosition(mapStartColumn + 1 + playerColumn, mapStartRow + 1 + playerRow);
  700.                 Console.ForegroundColor = ConsoleColor.Green;
  701.                 Console.BackgroundColor = ConsoleColor.Black;
  702.                 Console.Write(Player);
  703.                 Console.ResetColor();
  704.             }
  705.         }
  706.  
  707.         private static void RedrawSingleCell(int row, int column)
  708.         {
  709.             if (row < 0 || row >= mapHeight || column < 0 || column >= mapWidth)
  710.             {
  711.                 return;
  712.             }
  713.  
  714.             Console.SetCursorPosition(mapStartColumn + 1 + column, mapStartRow + 1 + row);
  715.  
  716.             DrawBaseCell(row, column);
  717.  
  718.             if (row == playerRow && column == playerColumn)
  719.             {
  720.                 Console.SetCursorPosition(mapStartColumn + 1 + column, mapStartRow + 1 + row);
  721.                 Console.ForegroundColor = ConsoleColor.Green;
  722.                 Console.BackgroundColor = ConsoleColor.Black;
  723.                 Console.Write(Player);
  724.                 Console.ResetColor();
  725.  
  726.                 return;
  727.             }
  728.  
  729.             if (IsEnemyAt(row, column) == true)
  730.             {
  731.                 Console.SetCursorPosition(mapStartColumn + 1 + column, mapStartRow + 1 + row);
  732.                 Console.ForegroundColor = ConsoleColor.Red;
  733.                 Console.BackgroundColor = ConsoleColor.Black;
  734.                 Console.Write(Enemy);
  735.                 Console.ResetColor();
  736.             }
  737.         }
  738.  
  739.         private static void DrawBaseCell(int row, int column)
  740.         {
  741.             if (IsVisible(row, column) == false)
  742.             {
  743.                 Console.ForegroundColor = ConsoleColor.Black;
  744.                 Console.BackgroundColor = ConsoleColor.Black;
  745.                 Console.Write(' ');
  746.                 Console.ResetColor();
  747.  
  748.                 return;
  749.             }
  750.  
  751.             if (map[row, column] == Wall)
  752.             {
  753.                 Console.ForegroundColor = ConsoleColor.Black;
  754.                 Console.BackgroundColor = ConsoleColor.White;
  755.                 Console.Write(' ');
  756.                 Console.ResetColor();
  757.  
  758.                 return;
  759.             }
  760.  
  761.             if (map[row, column] == Treasure)
  762.             {
  763.                 Console.ForegroundColor = ConsoleColor.Yellow;
  764.                 Console.BackgroundColor = ConsoleColor.Black;
  765.                 Console.Write(Treasure);
  766.                 Console.ResetColor();
  767.  
  768.                 return;
  769.             }
  770.  
  771.             Console.ForegroundColor = ConsoleColor.Gray;
  772.             Console.BackgroundColor = ConsoleColor.Black;
  773.             Console.Write(' ');
  774.             Console.ResetColor();
  775.         }
  776.  
  777.         private static string GetVisibilityName()
  778.         {
  779.             if (visibilityMode == 1)
  780.             {
  781.                 return "без тумана";
  782.             }
  783.  
  784.             if (visibilityMode == 2)
  785.             {
  786.                 return "туман(" + FogRadius + ")";
  787.             }
  788.  
  789.             return "фонарик(" + FlashlightRadius + ")";
  790.         }
  791.  
  792.         private static bool IsVisible(int row, int column)
  793.         {
  794.             if (visibilityMode == 1)
  795.             {
  796.                 return true;
  797.             }
  798.  
  799.             if (row == playerRow && column == playerColumn)
  800.             {
  801.                 return true;
  802.             }
  803.  
  804.             int deltaRow = row - playerRow;
  805.             int deltaColumn = column - playerColumn;
  806.             int manhattanDistance = Math.Abs(deltaRow) + Math.Abs(deltaColumn);
  807.  
  808.             if (visibilityMode == 2)
  809.             {
  810.                 return manhattanDistance <= FogRadius;
  811.             }
  812.  
  813.             if (manhattanDistance > FlashlightRadius)
  814.             {
  815.                 return false;
  816.             }
  817.  
  818.             if (facing == Facing.Up)
  819.             {
  820.                 return (-deltaRow) >= 0 && (-deltaRow) >= Math.Abs(deltaColumn);
  821.             }
  822.  
  823.             if (facing == Facing.Down)
  824.             {
  825.                 return (deltaRow) >= 0 && (deltaRow) >= Math.Abs(deltaColumn);
  826.             }
  827.  
  828.             if (facing == Facing.Left)
  829.             {
  830.                 return (-deltaColumn) >= 0 && (-deltaColumn) >= Math.Abs(deltaRow);
  831.             }
  832.  
  833.             if (facing == Facing.Right)
  834.             {
  835.                 return (deltaColumn) >= 0 && (deltaColumn) >= Math.Abs(deltaRow);
  836.             }
  837.  
  838.             return false;
  839.         }
  840.  
  841.         private static void InitializeMap()
  842.         {
  843.             map = new char[mapHeight, mapWidth];
  844.  
  845.             int rowIndex = 0;
  846.  
  847.             while (rowIndex < mapHeight)
  848.             {
  849.                 int columnIndex = 0;
  850.  
  851.                 while (columnIndex < mapWidth)
  852.                 {
  853.                     map[rowIndex, columnIndex] = Wall;
  854.                     columnIndex++;
  855.                 }
  856.  
  857.                 rowIndex++;
  858.             }
  859.  
  860.             GenerateMaze();
  861.  
  862.             if (difficulty == 1)
  863.             {
  864.                 PunchExtraHoles(15);
  865.             }
  866.             else if (difficulty == 2)
  867.             {
  868.                 PunchExtraHoles(7);
  869.             }
  870.             else
  871.             {
  872.                 PunchExtraHoles(0);
  873.             }
  874.         }
  875.  
  876.         private static void GenerateMaze()
  877.         {
  878.             int startRowIndex = random.Next(1, mapHeight - 1);
  879.             int startColumnIndex = random.Next(1, mapWidth - 1);
  880.             map[startRowIndex, startColumnIndex] = Floor;
  881.  
  882.             List<(int row, int column)> frontierCells = new List<(int row, int column)>();
  883.             AddFrontierCells(startRowIndex, startColumnIndex, frontierCells);
  884.  
  885.             while (frontierCells.Count > 0)
  886.             {
  887.                 int randomIndex = random.Next(frontierCells.Count);
  888.                 (int currentRow, int currentColumn) = frontierCells[randomIndex];
  889.                
  890.                 frontierCells.RemoveAt(randomIndex);
  891.  
  892.                 int passageNeighbourCount = 0;
  893.  
  894.                 int[] rowOffsets = { -1, 1, 0, 0 };
  895.                 int[] columnOffsets = { 0, 0, -1, 1 };
  896.  
  897.                 int directionIndex = 0;
  898.  
  899.                 while (directionIndex < 4)
  900.                 {
  901.                     int neighbourRow = currentRow + rowOffsets[directionIndex];
  902.                     int neighbourColumn = currentColumn + columnOffsets[directionIndex];
  903.  
  904.                     bool inBounds =
  905.                         neighbourRow >= 0 && neighbourRow < mapHeight &&
  906.                         neighbourColumn >= 0 && neighbourColumn < mapWidth;
  907.  
  908.                     if (inBounds == true && map[neighbourRow, neighbourColumn] == Floor)
  909.                     {
  910.                         passageNeighbourCount++;
  911.                     }
  912.  
  913.                     directionIndex++;
  914.                 }
  915.  
  916.                 if (passageNeighbourCount == 1)
  917.                 {
  918.                     map[currentRow, currentColumn] = Floor;
  919.                     AddFrontierCells(currentRow, currentColumn, frontierCells);
  920.                 }
  921.             }
  922.         }
  923.  
  924.         private static void AddFrontierCells(int row, int column, List<(int row, int column)> frontierCells)
  925.         {
  926.             int[] rowOffsets = { -1, 1, 0, 0 };
  927.             int[] columnOffsets = { 0, 0, -1, 1 };
  928.             int directionIndex = 0;
  929.  
  930.             while (directionIndex < 4)
  931.             {
  932.                 int neighbourRow = row + rowOffsets[directionIndex];
  933.                 int neighbourColumn = column + columnOffsets[directionIndex];
  934.  
  935.                 bool inBounds =
  936.                     neighbourRow >= 0 && neighbourRow < mapHeight &&
  937.                     neighbourColumn >= 0 && neighbourColumn < mapWidth;
  938.  
  939.                 if (inBounds == true && map[neighbourRow, neighbourColumn] == Wall)
  940.                 {
  941.                     bool alreadyAdded = frontierCells.Contains((neighbourRow, neighbourColumn));
  942.  
  943.                     if (alreadyAdded == false)
  944.                     {
  945.                         frontierCells.Add((neighbourRow, neighbourColumn));
  946.                     }
  947.                 }
  948.  
  949.                 directionIndex++;
  950.             }
  951.         }
  952.  
  953.         private static void PunchExtraHoles(int percent)
  954.         {
  955.             if (percent <= 0)
  956.             {
  957.                 return;
  958.             }
  959.  
  960.             int rowIndex = 1;
  961.  
  962.             while (rowIndex < mapHeight - 1)
  963.             {
  964.                 int columnIndex = 1;
  965.  
  966.                 while (columnIndex < mapWidth - 1)
  967.                 {
  968.                     if (map[rowIndex, columnIndex] == Wall)
  969.                     {
  970.                         int randomPercent = random.Next(100);
  971.  
  972.                         if (randomPercent < percent)
  973.                         {
  974.                             bool hasNeighbourFloor =
  975.                                 map[rowIndex - 1, columnIndex] == Floor ||
  976.                                 map[rowIndex + 1, columnIndex] == Floor ||
  977.                                 map[rowIndex, columnIndex - 1] == Floor ||
  978.                                 map[rowIndex, columnIndex + 1] == Floor;
  979.  
  980.                             if (hasNeighbourFloor == true)
  981.                             {
  982.                                 map[rowIndex, columnIndex] = Floor;
  983.                             }
  984.                         }
  985.                     }
  986.  
  987.                     columnIndex++;
  988.                 }
  989.  
  990.                 rowIndex++;
  991.             }
  992.         }
  993.  
  994.         private static List<(int row, int column)> GetAllNonWallCells()
  995.         {
  996.             List<(int row, int column)> cells = new List<(int row, int column)>();
  997.  
  998.             int rowIndex = 0;
  999.  
  1000.             while (rowIndex < mapHeight)
  1001.             {
  1002.                 int columnIndex = 0;
  1003.  
  1004.                 while (columnIndex < mapWidth)
  1005.                 {
  1006.                     if (map[rowIndex, columnIndex] != Wall)
  1007.                     {
  1008.                         cells.Add((rowIndex, columnIndex));
  1009.                     }
  1010.  
  1011.                     columnIndex++;
  1012.                 }
  1013.                 rowIndex++;
  1014.             }
  1015.  
  1016.             return cells;
  1017.         }
  1018.  
  1019.         private static void SpawnPlayer()
  1020.         {
  1021.             List<(int row, int column)> freeCells = GetAllNonWallCells();
  1022.  
  1023.             if (freeCells.Count == 0)
  1024.             {
  1025.                 playerRow = 1;
  1026.                 playerColumn = 1;
  1027.                 map[playerRow, playerColumn] = Floor;
  1028.  
  1029.                 return;
  1030.             }
  1031.  
  1032.             int randomIndex = random.Next(freeCells.Count);
  1033.  
  1034.             playerRow = freeCells[randomIndex].row;
  1035.             playerColumn = freeCells[randomIndex].column;
  1036.         }
  1037.  
  1038.         private static void RemoveAllTreasures()
  1039.         {
  1040.             int rowIndex = 0;
  1041.  
  1042.             while (rowIndex < mapHeight)
  1043.             {
  1044.                 int columnIndex = 0;
  1045.  
  1046.                 while (columnIndex < mapWidth)
  1047.                 {
  1048.                     if (map[rowIndex, columnIndex] == Treasure)
  1049.                     {
  1050.                         map[rowIndex, columnIndex] = Floor;
  1051.                     }
  1052.  
  1053.                     columnIndex++;
  1054.                 }
  1055.  
  1056.                 rowIndex++;
  1057.             }
  1058.         }
  1059.  
  1060.         private static bool[,] BuildReachableMapFromPlayer()
  1061.         {
  1062.             bool[,] reachable = new bool[mapHeight, mapWidth];
  1063.  
  1064.             Queue<(int row, int column)> queueCells = new Queue<(int row, int column)>();
  1065.  
  1066.             queueCells.Enqueue((playerRow, playerColumn));
  1067.             reachable[playerRow, playerColumn] = true;
  1068.  
  1069.             int[] rowOffsets = { -1, 1, 0, 0 };
  1070.             int[] columnOffsets = { 0, 0, -1, 1 };
  1071.  
  1072.             while (queueCells.Count > 0)
  1073.             {
  1074.                 (int currentRow, int currentColumn) = queueCells.Dequeue();
  1075.                 int directionIndex = 0;
  1076.  
  1077.                 while (directionIndex < 4)
  1078.                 {
  1079.                     int nextRow = currentRow + rowOffsets[directionIndex];
  1080.                     int nextColumn = currentColumn + columnOffsets[directionIndex];
  1081.  
  1082.                     bool inBounds =
  1083.                         nextRow >= 0 && nextRow < mapHeight &&
  1084.                         nextColumn >= 0 && nextColumn < mapWidth;
  1085.  
  1086.                     if (inBounds == true &&
  1087.                         reachable[nextRow, nextColumn] == false &&
  1088.                         map[nextRow, nextColumn] != Wall)
  1089.                     {
  1090.                         reachable[nextRow, nextColumn] = true;
  1091.                         queueCells.Enqueue((nextRow, nextColumn));
  1092.                     }
  1093.  
  1094.                     directionIndex++;
  1095.                 }
  1096.             }
  1097.  
  1098.             return reachable;
  1099.         }
  1100.  
  1101.         private static int CountReachableNonWall(bool[,] reachable)
  1102.         {
  1103.             int count = 0;
  1104.             int rowIndex = 0;
  1105.  
  1106.             while (rowIndex < mapHeight)
  1107.             {
  1108.                 int columnIndex = 0;
  1109.  
  1110.                 while (columnIndex < mapWidth)
  1111.                 {
  1112.                     if (reachable[rowIndex, columnIndex] == true && map[rowIndex, columnIndex] != Wall)
  1113.                     {
  1114.                         count++;
  1115.                     }
  1116.  
  1117.                     columnIndex++;
  1118.                 }
  1119.                 rowIndex++;
  1120.             }
  1121.  
  1122.             return count;
  1123.         }
  1124.  
  1125.         private static void ShuffleCells(List<(int row, int column)> cells)
  1126.         {
  1127.             int index = 0;
  1128.  
  1129.             while (index < cells.Count)
  1130.             {
  1131.                 int swapIndex = random.Next(index, cells.Count);
  1132.                 (cells[index], cells[swapIndex]) = (cells[swapIndex], cells[index]);
  1133.                 index++;
  1134.             }
  1135.         }
  1136.  
  1137.         private static void PlaceTreasures()
  1138.         {
  1139.             RemoveAllTreasures();
  1140.  
  1141.             bool[,] reachable = BuildReachableMapFromPlayer();
  1142.             List<(int row, int column)> freeCells = new List<(int row, int column)>();
  1143.  
  1144.             int rowIndex = 0;
  1145.  
  1146.             while (rowIndex < mapHeight)
  1147.             {
  1148.                 int columnIndex = 0;
  1149.  
  1150.                 while (columnIndex < mapWidth)
  1151.                 {
  1152.                     bool canUse =
  1153.                         reachable[rowIndex, columnIndex] == true &&
  1154.                         map[rowIndex, columnIndex] != Wall &&
  1155.                         (rowIndex == playerRow && columnIndex == playerColumn) == false;
  1156.  
  1157.                     if (canUse == true)
  1158.                     {
  1159.                         freeCells.Add((rowIndex, columnIndex));
  1160.                     }
  1161.  
  1162.                     columnIndex++;
  1163.                 }
  1164.  
  1165.                 rowIndex++;
  1166.             }
  1167.  
  1168.             int treasuresToPlace = treasureCount;
  1169.  
  1170.             if (treasuresToPlace > freeCells.Count)
  1171.             {
  1172.                 treasuresToPlace = freeCells.Count;
  1173.             }
  1174.  
  1175.             ShuffleCells(freeCells);
  1176.  
  1177.             int treasureIndex = 0;
  1178.  
  1179.             while (treasureIndex < treasuresToPlace)
  1180.             {
  1181.                 int treasureRow = freeCells[treasureIndex].row;
  1182.                 int treasureColumn = freeCells[treasureIndex].column;
  1183.                 map[treasureRow, treasureColumn] = Treasure;
  1184.                 treasureIndex++;
  1185.             }
  1186.  
  1187.             totalTreasures = treasuresToPlace;
  1188.         }
  1189.  
  1190.         private static void SpawnEnemies()
  1191.         {
  1192.             enemies.Clear();
  1193.  
  1194.             if (enemyCount <= 0)
  1195.             {
  1196.                 return;
  1197.             }
  1198.  
  1199.             bool[,] reachable = BuildReachableMapFromPlayer();
  1200.  
  1201.             List<(int row, int column)> candidates = new List<(int row, int column)>();
  1202.  
  1203.             int rowIndex = 0;
  1204.  
  1205.             while (rowIndex < mapHeight)
  1206.             {
  1207.                 int columnIndex = 0;
  1208.  
  1209.                 while (columnIndex < mapWidth)
  1210.                 {
  1211.                     bool canPlace =
  1212.                         reachable[rowIndex, columnIndex] == true &&
  1213.                         map[rowIndex, columnIndex] != Wall &&
  1214.                         (rowIndex == playerRow && columnIndex == playerColumn) == false &&
  1215.                         map[rowIndex, columnIndex] != Treasure;
  1216.  
  1217.                     if (canPlace == true)
  1218.                     {
  1219.                         candidates.Add((rowIndex, columnIndex));
  1220.                     }
  1221.  
  1222.                     columnIndex++;
  1223.                 }
  1224.  
  1225.                 rowIndex++;
  1226.             }
  1227.  
  1228.             if (candidates.Count == 0)
  1229.             {
  1230.                 return;
  1231.             }
  1232.  
  1233.             ShuffleCells(candidates);
  1234.  
  1235.             int toSpawn = enemyCount;
  1236.  
  1237.             if (toSpawn > candidates.Count)
  1238.             {
  1239.                 toSpawn = candidates.Count;
  1240.             }
  1241.  
  1242.             int enemyIndex = 0;
  1243.  
  1244.             while (enemyIndex < toSpawn)
  1245.             {
  1246.                 enemies.Add(candidates[enemyIndex]);
  1247.                 enemyIndex++;
  1248.             }
  1249.         }
  1250.  
  1251.         private static void MoveEnemies()
  1252.         {
  1253.             if (enemies.Count == 0)
  1254.             {
  1255.                 return;
  1256.             }
  1257.  
  1258.             int[] rowOffsets = { -1, 1, 0, 0 };
  1259.             int[] columnOffsets = { 0, 0, -1, 1 };
  1260.  
  1261.             int enemyIndex = 0;
  1262.  
  1263.             while (enemyIndex < enemies.Count)
  1264.             {
  1265.                 int enemyRow = enemies[enemyIndex].row;
  1266.                 int enemyColumn = enemies[enemyIndex].column;
  1267.  
  1268.                 List<(int row, int column)> possibleMoves = new List<(int row, int column)>(4);
  1269.  
  1270.                 int directionIndex = 0;
  1271.  
  1272.                 while (directionIndex < 4)
  1273.                 {
  1274.                     int nextRow = enemyRow + rowOffsets[directionIndex];
  1275.                     int nextColumn = enemyColumn + columnOffsets[directionIndex];
  1276.  
  1277.                     bool inBounds =
  1278.                         nextRow >= 0 && nextRow < mapHeight &&
  1279.                         nextColumn >= 0 && nextColumn < mapWidth;
  1280.  
  1281.                     if (inBounds == true && map[nextRow, nextColumn] != Wall)
  1282.                     {
  1283.                         possibleMoves.Add((nextRow, nextColumn));
  1284.                     }
  1285.  
  1286.                     directionIndex++;
  1287.                 }
  1288.  
  1289.                 if (possibleMoves.Count > 0)
  1290.                 {
  1291.                     int decision = random.Next(100);
  1292.                     (int row, int column) target;
  1293.  
  1294.                     if (decision < 50)
  1295.                     {
  1296.                         target = ChooseMoveCloserToPlayer(possibleMoves);
  1297.                     }
  1298.                     else
  1299.                     {
  1300.                         target = possibleMoves[random.Next(possibleMoves.Count)];
  1301.                     }
  1302.  
  1303.                     enemies[enemyIndex] = target;
  1304.                 }
  1305.  
  1306.                 enemyIndex++;
  1307.             }
  1308.         }
  1309.  
  1310.         private static (int row, int column) ChooseMoveCloserToPlayer(List<(int row, int column)> moves)
  1311.         {
  1312.             int bestDistance = int.MaxValue;
  1313.             (int row, int column) bestMove = moves[0];
  1314.  
  1315.             int moveIndex = 0;
  1316.  
  1317.             while (moveIndex < moves.Count)
  1318.             {
  1319.                 int distance = Math.Abs(moves[moveIndex].row - playerRow) + Math.Abs(moves[moveIndex].column - playerColumn);
  1320.  
  1321.                 if (distance < bestDistance)
  1322.                 {
  1323.                     bestDistance = distance;
  1324.                     bestMove = moves[moveIndex];
  1325.                 }
  1326.                 moveIndex++;
  1327.             }
  1328.  
  1329.             return bestMove;
  1330.         }
  1331.  
  1332.         private static bool IsEnemyOnPlayer()
  1333.         {
  1334.             int enemyIndex = 0;
  1335.  
  1336.             while (enemyIndex < enemies.Count)
  1337.             {
  1338.                 if (enemies[enemyIndex].row == playerRow && enemies[enemyIndex].column == playerColumn)
  1339.                 {
  1340.                     return true;
  1341.                 }
  1342.  
  1343.                 enemyIndex++;
  1344.             }
  1345.  
  1346.             return false;
  1347.         }
  1348.  
  1349.         private static bool IsEnemyAt(int row, int column)
  1350.         {
  1351.             int enemyIndex = 0;
  1352.  
  1353.             while (enemyIndex < enemies.Count)
  1354.             {
  1355.                 if (enemies[enemyIndex].row == row && enemies[enemyIndex].column == column)
  1356.                 {
  1357.                     return true;
  1358.                 }
  1359.  
  1360.                 enemyIndex++;
  1361.             }
  1362.  
  1363.             return false;
  1364.         }
  1365.  
  1366.         private static void ProcessPlayerInput(ConsoleKeyInfo keyInfo, out bool moved, out bool facingChanged)
  1367.         {
  1368.             moved = false;
  1369.             facingChanged = false;
  1370.  
  1371.             int newPlayerRow = playerRow;
  1372.             int newPlayerColumn = playerColumn;
  1373.  
  1374.             bool isDirectionKey = true;
  1375.  
  1376.             switch (keyInfo.Key)
  1377.             {
  1378.                 case ConsoleKey.UpArrow:
  1379.                 case ConsoleKey.W:
  1380.                     newPlayerRow--;
  1381.                     if (facing != Facing.Up) { facing = Facing.Up; facingChanged = true; }
  1382.                     break;
  1383.  
  1384.                 case ConsoleKey.DownArrow:
  1385.                 case ConsoleKey.S:
  1386.                     newPlayerRow++;
  1387.                     if (facing != Facing.Down) { facing = Facing.Down; facingChanged = true; }
  1388.                     break;
  1389.  
  1390.                 case ConsoleKey.LeftArrow:
  1391.                 case ConsoleKey.A:
  1392.                     newPlayerColumn--;
  1393.                     if (facing != Facing.Left) { facing = Facing.Left; facingChanged = true; }
  1394.                     break;
  1395.  
  1396.                 case ConsoleKey.RightArrow:
  1397.                 case ConsoleKey.D:
  1398.                     newPlayerColumn++;
  1399.                     if (facing != Facing.Right) { facing = Facing.Right; facingChanged = true; }
  1400.                     break;
  1401.  
  1402.                 default:
  1403.                     isDirectionKey = false;
  1404.                     break;
  1405.             }
  1406.  
  1407.             if (isDirectionKey == false)
  1408.             {
  1409.                 return;
  1410.             }
  1411.  
  1412.             bool inBounds =
  1413.                 newPlayerRow >= 0 && newPlayerRow < mapHeight &&
  1414.                 newPlayerColumn >= 0 && newPlayerColumn < mapWidth;
  1415.  
  1416.             if (inBounds == false)
  1417.             {
  1418.                 return;
  1419.             }
  1420.  
  1421.             if (map[newPlayerRow, newPlayerColumn] == Wall)
  1422.             {
  1423.                 return;
  1424.             }
  1425.  
  1426.  
  1427.             if (map[newPlayerRow, newPlayerColumn] == Treasure)
  1428.             {
  1429.                 currentScore++;
  1430.                 map[newPlayerRow, newPlayerColumn] = Floor;
  1431.             }
  1432.  
  1433.             playerRow = newPlayerRow;
  1434.             playerColumn = newPlayerColumn;
  1435.  
  1436.             moved = true;
  1437.         }
  1438.  
  1439.         private enum PauseMenuResult
  1440.         {
  1441.             Continue,
  1442.             ExitToMainMenu
  1443.         }
  1444.  
  1445.         private static PauseMenuResult ShowPauseMenu()
  1446.         {
  1447.             int menuWidth = 34;
  1448.             int menuHeight = 8;
  1449.             int startColumn = Math.Max(0, (Console.WindowWidth - menuWidth) / 2);
  1450.             int startRow = Math.Max(0, (Console.WindowHeight - menuHeight) / 2);
  1451.  
  1452.             Console.Clear();
  1453.             DrawFrame(startColumn, startRow, menuWidth, menuHeight);
  1454.  
  1455.             ConsoleColor originalColor = Console.ForegroundColor;
  1456.             Console.ForegroundColor = ConsoleColor.Cyan;
  1457.  
  1458.             string title = "МЕНЮ";
  1459.  
  1460.             Console.SetCursorPosition(startColumn + (menuWidth - title.Length) / 2, startRow + 1);
  1461.             Console.Write(title);
  1462.             Console.ForegroundColor = originalColor;
  1463.  
  1464.             string continueText = "1. Продолжить";
  1465.             string exitToMainText = "2. Выйти в главное меню";
  1466.             string escHint = "Esc - вернуться в игру";
  1467.  
  1468.             Console.SetCursorPosition(startColumn + (menuWidth - continueText.Length) / 2, startRow + 3);
  1469.             Console.Write(continueText);
  1470.  
  1471.             Console.SetCursorPosition(startColumn + (menuWidth - exitToMainText.Length) / 2, startRow + 4);
  1472.             Console.Write(exitToMainText);
  1473.  
  1474.             Console.SetCursorPosition(startColumn + (menuWidth - escHint.Length) / 2, startRow + 6);
  1475.             Console.Write(escHint);
  1476.  
  1477.             bool waitingForChoice = true;
  1478.  
  1479.             PauseMenuResult result = PauseMenuResult.Continue;
  1480.  
  1481.             while (waitingForChoice == true)
  1482.             {
  1483.                 ConsoleKeyInfo key = Console.ReadKey(true);
  1484.  
  1485.                 if (key.Key == ConsoleKey.D1 || key.Key == ConsoleKey.NumPad1 || key.Key == ConsoleKey.Escape)
  1486.                 {
  1487.                     result = PauseMenuResult.Continue;
  1488.                     waitingForChoice = false;
  1489.                 }
  1490.                 else if (key.Key == ConsoleKey.D2 || key.Key == ConsoleKey.NumPad2)
  1491.                 {
  1492.                     result = PauseMenuResult.ExitToMainMenu;
  1493.                     waitingForChoice = false;
  1494.                 }
  1495.             }
  1496.  
  1497.             Console.Clear();
  1498.  
  1499.             return result;
  1500.         }
  1501.  
  1502.         private static string CenterText(string text, int width)
  1503.         {
  1504.             if (text.Length >= width)
  1505.             {
  1506.                 return text.Substring(0, width);
  1507.             }
  1508.  
  1509.             int leftPadding = (width - text.Length) / 2;
  1510.             int rightPadding = width - text.Length - leftPadding;
  1511.  
  1512.             return new string(' ', leftPadding) + text + new string(' ', rightPadding);
  1513.         }
  1514.  
  1515.         private static void DrawFrame(int left, int top, int width, int height)
  1516.         {
  1517.             if (width < 2) width = 2;
  1518.             if (height < 2) height = 2;
  1519.  
  1520.             Console.SetCursorPosition(left, top);
  1521.             Console.Write('┌' + new string('─', Math.Max(0, width - 2)) + '┐');
  1522.  
  1523.             int lineIndex = 1;
  1524.  
  1525.             while (lineIndex < height - 1)
  1526.             {
  1527.                 Console.SetCursorPosition(left, top + lineIndex);
  1528.                 Console.Write('│' + new string(' ', Math.Max(0, width - 2)) + '│');
  1529.                 lineIndex++;
  1530.             }
  1531.  
  1532.             Console.SetCursorPosition(left, top + height - 1);
  1533.             Console.Write('└' + new string('─', Math.Max(0, width - 2)) + '┘');
  1534.         }
  1535.  
  1536.         private static void ShowVictory()
  1537.         {
  1538.             Console.Clear();
  1539.  
  1540.             int menuWidth = 50;
  1541.             int menuHeight = 12;
  1542.             int startColumn = Math.Max(0, (Console.WindowWidth - menuWidth) / 2);
  1543.             int startRow = Math.Max(0, (Console.WindowHeight - menuHeight) / 2);
  1544.  
  1545.             DrawFrame(startColumn, startRow, menuWidth, menuHeight);
  1546.  
  1547.             ConsoleColor originalColor = Console.ForegroundColor;
  1548.             Console.ForegroundColor = ConsoleColor.Green;
  1549.  
  1550.             string title = "ПОБЕДА!";
  1551.  
  1552.             Console.SetCursorPosition(startColumn + (menuWidth - title.Length) / 2, startRow + 1);
  1553.             Console.Write(title);
  1554.  
  1555.             Console.ForegroundColor = originalColor;
  1556.  
  1557.             string info1 = "Размер карты: " + mapWidth + " x " + mapHeight;
  1558.             string info2 = "Сокровищ собрано: " + currentScore + " из " + totalTreasures;
  1559.             string info3 = "Сложность: " + difficulty;
  1560.             string info4 = "Видимость: " + GetVisibilityName();
  1561.             string info5 = "Время: " + playTime.ToString(@"mm\:ss");
  1562.  
  1563.             Console.SetCursorPosition(startColumn + (menuWidth - info1.Length) / 2, startRow + 3);
  1564.             Console.Write(info1);
  1565.  
  1566.             Console.SetCursorPosition(startColumn + (menuWidth - info2.Length) / 2, startRow + 4);
  1567.             Console.Write(info2);
  1568.  
  1569.             Console.SetCursorPosition(startColumn + (menuWidth - info3.Length) / 2, startRow + 5);
  1570.             Console.Write(info3);
  1571.  
  1572.             Console.SetCursorPosition(startColumn + (menuWidth - info4.Length) / 2, startRow + 6);
  1573.             Console.Write(info4);
  1574.  
  1575.             Console.SetCursorPosition(startColumn + (menuWidth - info5.Length) / 2, startRow + 7);
  1576.             Console.Write(info5);
  1577.  
  1578.             string continueText = "Нажмите любую клавишу для продолжения...";
  1579.  
  1580.             Console.SetCursorPosition(startColumn + (menuWidth - continueText.Length) / 2, startRow + 9);
  1581.             Console.Write(continueText);
  1582.  
  1583.             Console.ReadKey(true);
  1584.             Console.Clear();
  1585.         }
  1586.  
  1587.         private static void ShowGameInterrupted()
  1588.         {
  1589.             Console.Clear();
  1590.  
  1591.             int menuWidth = 50;
  1592.             int menuHeight = 10;
  1593.             int startColumn = Math.Max(0, (Console.WindowWidth - menuWidth) / 2);
  1594.             int startRow = Math.Max(0, (Console.WindowHeight - menuHeight) / 2);
  1595.  
  1596.             DrawFrame(startColumn, startRow, menuWidth, menuHeight);
  1597.  
  1598.             ConsoleColor originalColor = Console.ForegroundColor;
  1599.             Console.ForegroundColor = ConsoleColor.Red;
  1600.  
  1601.             string title = (IsEnemyOnPlayer() == true) ? "ВАС ПОЙМАЛ ВРАГ" : "ИГРА ПРЕРВАНА";
  1602.  
  1603.             Console.SetCursorPosition(startColumn + (menuWidth - title.Length) / 2, startRow + 1);
  1604.             Console.Write(title);
  1605.  
  1606.             Console.ForegroundColor = originalColor;
  1607.  
  1608.             string scoreText = "Ваш счёт: " + currentScore + " из " + totalTreasures;
  1609.             string timeText = "Время: " + playTime.ToString(@"mm\:ss");
  1610.             string continueText = "Нажмите любую клавишу для возврата...";
  1611.  
  1612.             Console.SetCursorPosition(startColumn + (menuWidth - scoreText.Length) / 2, startRow + 3);
  1613.             Console.Write(scoreText);
  1614.  
  1615.             Console.SetCursorPosition(startColumn + (menuWidth - timeText.Length) / 2, startRow + 4);
  1616.             Console.Write(timeText);
  1617.  
  1618.             Console.SetCursorPosition(startColumn + (menuWidth - continueText.Length) / 2, startRow + 6);
  1619.             Console.Write(continueText);
  1620.  
  1621.             Console.ReadKey(true);
  1622.             Console.Clear();
  1623.         }
  1624.  
  1625.         private static void ShowMessageBox(string title, string message)
  1626.         {
  1627.             Console.Clear();
  1628.  
  1629.             List<string> messageLines = SplitLines(message);
  1630.  
  1631.             int maxLineLength = title.Length;
  1632.             int lineIndex = 0;
  1633.  
  1634.             while (lineIndex < messageLines.Count)
  1635.             {
  1636.                 if (messageLines[lineIndex].Length > maxLineLength)
  1637.                 {
  1638.                     maxLineLength = messageLines[lineIndex].Length;
  1639.                 }
  1640.  
  1641.                 lineIndex++;
  1642.             }
  1643.  
  1644.             int frameWidth = maxLineLength + 6;
  1645.             int frameHeight = messageLines.Count + 6;
  1646.  
  1647.             if (frameWidth > Console.WindowWidth)
  1648.             {
  1649.                 frameWidth = Console.WindowWidth;
  1650.             }
  1651.  
  1652.             if (frameHeight > Console.WindowHeight)
  1653.             {
  1654.                 frameHeight = Console.WindowHeight;
  1655.             }
  1656.  
  1657.             int frameLeft = Math.Max(0, (Console.WindowWidth - frameWidth) / 2);
  1658.             int frameTop = Math.Max(0, (Console.WindowHeight - frameHeight) / 2);
  1659.  
  1660.             DrawFrame(frameLeft, frameTop, frameWidth, frameHeight);
  1661.  
  1662.             ConsoleColor originalColor = Console.ForegroundColor;
  1663.             Console.ForegroundColor = ConsoleColor.Cyan;
  1664.  
  1665.             Console.SetCursorPosition(frameLeft + (frameWidth - title.Length) / 2, frameTop + 1);
  1666.             Console.Write(title);
  1667.  
  1668.             Console.ForegroundColor = originalColor;
  1669.  
  1670.             int messageIndex = 0;
  1671.  
  1672.             while (messageIndex < messageLines.Count)
  1673.             {
  1674.                 string line = messageLines[messageIndex];
  1675.  
  1676.                 if (line.Length > frameWidth - 4)
  1677.                 {
  1678.                     line = line.Substring(0, frameWidth - 4);
  1679.                 }
  1680.  
  1681.                 Console.SetCursorPosition(frameLeft + 2, frameTop + 3 + messageIndex);
  1682.                 Console.Write(line.PadRight(frameWidth - 4));
  1683.  
  1684.                 messageIndex++;
  1685.             }
  1686.  
  1687.             Console.SetCursorPosition(frameLeft + 2, frameTop + frameHeight - 2);
  1688.             Console.Write("Нажмите любую клавишу...");
  1689.  
  1690.             Console.ReadKey(true);
  1691.             Console.Clear();
  1692.         }
  1693.  
  1694.         private static List<string> SplitLines(string text)
  1695.         {
  1696.             List<string> lines = new List<string>();
  1697.             StringBuilder current = new StringBuilder();
  1698.  
  1699.             int index = 0;
  1700.  
  1701.             while (index < text.Length)
  1702.             {
  1703.                 char symbol = text[index];
  1704.                 if (symbol == '\r')
  1705.                 {
  1706.                 }
  1707.                 else if (symbol == '\n')
  1708.                 {
  1709.                     lines.Add(current.ToString());
  1710.                     current.Clear();
  1711.                 }
  1712.                 else
  1713.                 {
  1714.                     current.Append(symbol);
  1715.                 }
  1716.                 index++;
  1717.             }
  1718.  
  1719.             lines.Add(current.ToString());
  1720.  
  1721.             return lines;
  1722.         }
  1723.  
  1724.         private static void ShowRecords()
  1725.         {
  1726.             Console.Clear();
  1727.  
  1728.             int menuWidth = 76;
  1729.             int menuHeight = Math.Max(12, records.Count + 6);
  1730.  
  1731.             int startColumn = Math.Max(0, (Console.WindowWidth - menuWidth) / 2);
  1732.             int startRow = Math.Max(0, (Console.WindowHeight - menuHeight) / 2);
  1733.  
  1734.             DrawFrame(startColumn, startRow, menuWidth, menuHeight);
  1735.  
  1736.             ConsoleColor originalColor = Console.ForegroundColor;
  1737.             Console.ForegroundColor = ConsoleColor.Cyan;
  1738.  
  1739.             string title = "ИСТОРИЯ ПОБЕД";
  1740.  
  1741.             Console.SetCursorPosition(startColumn + (menuWidth - title.Length) / 2, startRow + 1);
  1742.             Console.Write(title);
  1743.  
  1744.             Console.ForegroundColor = originalColor;
  1745.  
  1746.             if (records.Count == 0)
  1747.             {
  1748.                 string noRecordsText = "Пока нет записей";
  1749.  
  1750.                 Console.SetCursorPosition(startColumn + (menuWidth - noRecordsText.Length) / 2, startRow + 3);
  1751.                 Console.Write(noRecordsText);
  1752.             }
  1753.             else
  1754.             {
  1755.                 string header = "Ширина Высота Сокровища Сложн.  Видимость        Время";
  1756.  
  1757.                 Console.SetCursorPosition(startColumn + (menuWidth - header.Length) / 2, startRow + 3);
  1758.                 Console.Write(header);
  1759.  
  1760.                 List<Record> sorted = new List<Record>(records.Count);
  1761.  
  1762.                 int copyIndex = 0;
  1763.  
  1764.                 while (copyIndex < records.Count)
  1765.                 {
  1766.                     sorted.Add(records[copyIndex]);
  1767.                     copyIndex++;
  1768.                 }
  1769.  
  1770.                 SortRecordsByTimeThenDifficulty(sorted);
  1771.  
  1772.                 int recordIndex = 0;
  1773.  
  1774.                 while (recordIndex < sorted.Count && (startRow + 4 + recordIndex) < (startRow + menuHeight - 2))
  1775.                 {
  1776.                     Record record = sorted[recordIndex];
  1777.  
  1778.                     string visibilityText;
  1779.  
  1780.                     if (record.VisibilityMode == 1)
  1781.                     {
  1782.                         visibilityText = "без тумана";
  1783.                     }
  1784.                     else if (record.VisibilityMode == 2)
  1785.                     {
  1786.                         visibilityText = "туман(" + FogRadius + ")";
  1787.                     }
  1788.                     else
  1789.                     {
  1790.                         visibilityText = "фонарик(" + FlashlightRadius + ")";
  1791.                     }
  1792.  
  1793.                     string line =
  1794.                         string.Format("{0,7} {1,6} {2,9} {3,6}  {4,-14} {5}",
  1795.                             record.Width,
  1796.                             record.Height,
  1797.                             record.TreasureCount,
  1798.                             record.Difficulty,
  1799.                             visibilityText,
  1800.                             record.Time.ToString(@"mm\:ss"));
  1801.  
  1802.                     Console.SetCursorPosition(startColumn + 2, startRow + 4 + recordIndex);
  1803.                     Console.Write(line.PadRight(menuWidth - 4));
  1804.  
  1805.                     recordIndex++;
  1806.                 }
  1807.             }
  1808.  
  1809.             string backText = "Нажмите любую клавишу для возврата...";
  1810.  
  1811.             Console.SetCursorPosition(startColumn + (menuWidth - backText.Length) / 2, startRow + menuHeight - 2);
  1812.             Console.Write(backText);
  1813.  
  1814.             Console.ReadKey(true);
  1815.             Console.Clear();
  1816.         }
  1817.  
  1818.         private static void SortRecordsByTimeThenDifficulty(List<Record> recordList)
  1819.         {
  1820.             int firstIndex = 0;
  1821.  
  1822.             while (firstIndex < recordList.Count - 1)
  1823.             {
  1824.                 int bestIndex = firstIndex;
  1825.                 int secondIndex = firstIndex + 1;
  1826.  
  1827.                 while (secondIndex < recordList.Count)
  1828.                 {
  1829.                     bool better =
  1830.                         recordList[secondIndex].Time < recordList[bestIndex].Time ||
  1831.                         (recordList[secondIndex].Time == recordList[bestIndex].Time &&
  1832.                          recordList[secondIndex].Difficulty < recordList[bestIndex].Difficulty);
  1833.  
  1834.                     if (better == true)
  1835.                     {
  1836.                         bestIndex = secondIndex;
  1837.                     }
  1838.  
  1839.                     secondIndex++;
  1840.                 }
  1841.  
  1842.                 if (bestIndex != firstIndex)
  1843.                 {
  1844.                     Record temp = recordList[firstIndex];
  1845.                     recordList[firstIndex] = recordList[bestIndex];
  1846.                     recordList[bestIndex] = temp;
  1847.                 }
  1848.  
  1849.                 firstIndex++;
  1850.             }
  1851.         }
  1852.  
  1853.         private static int GetIntFromUser(string prompt, int min, int max)
  1854.         {
  1855.             int result = 0;
  1856.             bool valid = false;
  1857.  
  1858.             while (valid == false)
  1859.             {
  1860.                 Console.Clear();
  1861.  
  1862.                 string title = "ВВОД ПАРАМЕТРА";
  1863.                 string promptFull = prompt + " (от " + min + " до " + max + "):";
  1864.  
  1865.                 int maxLength = title.Length;
  1866.                 if (promptFull.Length > maxLength) maxLength = promptFull.Length;
  1867.  
  1868.                 int menuWidth = Math.Min(Console.WindowWidth, maxLength + 6);
  1869.                 if (menuWidth < 20) menuWidth = 20;
  1870.  
  1871.                 int menuHeight = 8;
  1872.  
  1873.                 int startColumn = Math.Max(0, (Console.WindowWidth - menuWidth) / 2);
  1874.                 int startRow = Math.Max(0, (Console.WindowHeight - menuHeight) / 2);
  1875.  
  1876.                 DrawFrame(startColumn, startRow, menuWidth, menuHeight);
  1877.  
  1878.                 ConsoleColor originalColor = Console.ForegroundColor;
  1879.                 Console.ForegroundColor = ConsoleColor.Cyan;
  1880.  
  1881.                 Console.SetCursorPosition(startColumn + (menuWidth - title.Length) / 2, startRow + 1);
  1882.                 Console.Write(title);
  1883.  
  1884.                 Console.ForegroundColor = originalColor;
  1885.  
  1886.                 Console.SetCursorPosition(startColumn + (menuWidth - promptFull.Length) / 2, startRow + 3);
  1887.                 Console.Write(promptFull);
  1888.  
  1889.                 Console.SetCursorPosition(startColumn + 2, startRow + 5);
  1890.                 Console.Write(new string(' ', menuWidth - 4));
  1891.  
  1892.                 Console.SetCursorPosition(startColumn + 2, startRow + 5);
  1893.                 Console.CursorVisible = true;
  1894.  
  1895.                 string input = Console.ReadLine();
  1896.  
  1897.                 Console.CursorVisible = false;
  1898.  
  1899.                 string errorMessage = null;
  1900.  
  1901.                 if (string.IsNullOrWhiteSpace(input) == true)
  1902.                 {
  1903.                     errorMessage = "Ошибка: ввод не может быть пустым.";
  1904.                 }
  1905.                 else
  1906.                 {
  1907.                     int parsedValue;
  1908.                     bool parseOk = int.TryParse(input, out parsedValue);
  1909.  
  1910.                     if (parseOk == false)
  1911.                     {
  1912.                         errorMessage = "Ошибка: введено не число.";
  1913.                     }
  1914.                     else if (parsedValue < min || parsedValue > max)
  1915.                     {
  1916.                         errorMessage = "Ошибка: число должно быть от " + min + " до " + max + ".";
  1917.                     }
  1918.                     else
  1919.                     {
  1920.                         result = parsedValue;
  1921.                         valid = true;
  1922.                     }
  1923.                 }
  1924.  
  1925.                 if (errorMessage != null)
  1926.                 {
  1927.                     Console.ForegroundColor = ConsoleColor.Red;
  1928.                     Console.SetCursorPosition(startColumn + 2, startRow + 6);
  1929.  
  1930.                     string display = errorMessage;
  1931.  
  1932.                     if (display.Length > menuWidth - 4)
  1933.                     {
  1934.                         display = display.Substring(0, menuWidth - 4);
  1935.                     }
  1936.  
  1937.                     Console.Write(display.PadRight(menuWidth - 4));
  1938.                     Console.ForegroundColor = originalColor;
  1939.  
  1940.                     Console.ReadKey(true);
  1941.                 }
  1942.             }
  1943.  
  1944.             return result;
  1945.         }
  1946.     }
  1947. }
Add Comment
Please, Sign In to add comment