init
This commit is contained in:
269
Program.cs
Normal file
269
Program.cs
Normal file
@@ -0,0 +1,269 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace PaperRacing
|
||||
{
|
||||
// Представляет точку на поле
|
||||
public record Point(int X, int Y)
|
||||
{
|
||||
public static Point operator +(Point a, Point b) => new(a.X + b.X, a.Y + b.Y);
|
||||
public static Point operator -(Point a, Point b) => new(a.X - b.X, a.Y - b.Y);
|
||||
|
||||
public double DistanceTo(Point other)
|
||||
{
|
||||
return Math.Sqrt(Math.Pow(X - other.X, 2) + Math.Pow(Y - other.Y, 2));
|
||||
}
|
||||
}
|
||||
|
||||
// Состояние игры (позиция, скорость, посещенные чекпоинты)
|
||||
public class GameState
|
||||
{
|
||||
public Point Position { get; init; }
|
||||
public Point Velocity { get; init; }
|
||||
public HashSet<int> VisitedCheckpoints { get; init; }
|
||||
public List<Point> Path { get; init; }
|
||||
|
||||
public GameState(Point position, Point velocity, HashSet<int> visitedCheckpoints, List<Point> path)
|
||||
{
|
||||
Position = position;
|
||||
Velocity = velocity;
|
||||
VisitedCheckpoints = visitedCheckpoints;
|
||||
Path = path;
|
||||
}
|
||||
|
||||
// Уникальный ключ для состояния (без учета пути)
|
||||
public string GetKey()
|
||||
{
|
||||
var checkpointsMask = string.Join(",", VisitedCheckpoints.OrderBy(x => x));
|
||||
return $"{Position.X},{Position.Y}|{Velocity.X},{Velocity.Y}|{checkpointsMask}";
|
||||
}
|
||||
}
|
||||
|
||||
// Игровое поле
|
||||
public class RaceTrack
|
||||
{
|
||||
private readonly int _width;
|
||||
private readonly int _height;
|
||||
private readonly HashSet<Point> _obstacles;
|
||||
private readonly Dictionary<int, Point> _checkpoints;
|
||||
private readonly Point _start;
|
||||
|
||||
public RaceTrack(int width, int height, Point start, Dictionary<int, Point> checkpoints, HashSet<Point> obstacles)
|
||||
{
|
||||
_width = width;
|
||||
_height = height;
|
||||
_start = start;
|
||||
_checkpoints = checkpoints;
|
||||
_obstacles = obstacles;
|
||||
}
|
||||
|
||||
// Проверка, находится ли точка в границах поля
|
||||
private bool IsInBounds(Point p) => p.X >= 0 && p.X < _width && p.Y >= 0 && p.Y < _height;
|
||||
|
||||
// Проверка пересечения отрезка с препятствиями (алгоритм Брезенхема)
|
||||
private bool IntersectsObstacle(Point from, Point to)
|
||||
{
|
||||
int x0 = from.X, y0 = from.Y;
|
||||
int x1 = to.X, y1 = to.Y;
|
||||
|
||||
int dx = Math.Abs(x1 - x0);
|
||||
int dy = Math.Abs(y1 - y0);
|
||||
int sx = x0 < x1 ? 1 : -1;
|
||||
int sy = y0 < y1 ? 1 : -1;
|
||||
int err = dx - dy;
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (_obstacles.Contains(new Point(x0, y0)))
|
||||
return true;
|
||||
|
||||
if (x0 == x1 && y0 == y1)
|
||||
break;
|
||||
|
||||
int e2 = 2 * err;
|
||||
if (e2 > -dy)
|
||||
{
|
||||
err -= dy;
|
||||
x0 += sx;
|
||||
}
|
||||
if (e2 < dx)
|
||||
{
|
||||
err += dx;
|
||||
y0 += sy;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Поиск оптимального решения методом BFS
|
||||
public List<Point>? FindSolution()
|
||||
{
|
||||
var queue = new Queue<GameState>();
|
||||
var visited = new HashSet<string>();
|
||||
|
||||
var initialState = new GameState(_start, new Point(0, 0), new HashSet<int>(), new List<Point> { _start });
|
||||
queue.Enqueue(initialState);
|
||||
visited.Add(initialState.GetKey());
|
||||
|
||||
int iterations = 0;
|
||||
const int maxIterations = 1000000;
|
||||
|
||||
while (queue.Count > 0 && iterations < maxIterations)
|
||||
{
|
||||
iterations++;
|
||||
var current = queue.Dequeue();
|
||||
|
||||
// Проверяем, собрали ли все чекпоинты
|
||||
if (current.VisitedCheckpoints.Count == _checkpoints.Count)
|
||||
{
|
||||
Console.WriteLine($"Решение найдено за {iterations} итераций");
|
||||
Console.WriteLine($"Количество ходов: {current.Path.Count - 1}");
|
||||
return current.Path;
|
||||
}
|
||||
|
||||
// Генерируем все возможные ускорения (-1, 0, +1 по каждой оси)
|
||||
for (int dx = -1; dx <= 1; dx++)
|
||||
{
|
||||
for (int dy = -1; dy <= 1; dy++)
|
||||
{
|
||||
var acceleration = new Point(dx, dy);
|
||||
var newVelocity = current.Velocity + acceleration;
|
||||
var newPosition = current.Position + newVelocity;
|
||||
|
||||
// Проверяем границы
|
||||
if (!IsInBounds(newPosition))
|
||||
continue;
|
||||
|
||||
// Проверяем препятствия
|
||||
if (IntersectsObstacle(current.Position, newPosition))
|
||||
continue;
|
||||
|
||||
// Проверяем чекпоинты
|
||||
var newCheckpoints = new HashSet<int>(current.VisitedCheckpoints);
|
||||
foreach (var (id, checkpoint) in _checkpoints)
|
||||
{
|
||||
if (!newCheckpoints.Contains(id) && newPosition.Equals(checkpoint))
|
||||
{
|
||||
newCheckpoints.Add(id);
|
||||
}
|
||||
}
|
||||
|
||||
var newPath = new List<Point>(current.Path) { newPosition };
|
||||
var newState = new GameState(newPosition, newVelocity, newCheckpoints, newPath);
|
||||
|
||||
var key = newState.GetKey();
|
||||
if (!visited.Contains(key))
|
||||
{
|
||||
visited.Add(key);
|
||||
queue.Enqueue(newState);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine($"Решение не найдено после {iterations} итераций");
|
||||
return null;
|
||||
}
|
||||
|
||||
// Визуализация поля
|
||||
public void Visualize(List<Point>? path = null)
|
||||
{
|
||||
var pathSet = path != null ? new HashSet<Point>(path) : new HashSet<Point>();
|
||||
|
||||
for (int y = _height - 1; y >= 0; y--)
|
||||
{
|
||||
Console.Write($"{y:00}|");
|
||||
for (int x = 0; x < _width; x++)
|
||||
{
|
||||
var point = new Point(x, y);
|
||||
|
||||
if (point.Equals(_start))
|
||||
Console.Write("S ");
|
||||
else if (_checkpoints.Values.Contains(point))
|
||||
Console.Write($"{_checkpoints.First(kv => kv.Value.Equals(point)).Key} ");
|
||||
else if (_obstacles.Contains(point))
|
||||
Console.Write("# ");
|
||||
else if (pathSet.Contains(point))
|
||||
Console.Write(". ");
|
||||
else
|
||||
Console.Write(" ");
|
||||
}
|
||||
Console.WriteLine();
|
||||
}
|
||||
}
|
||||
|
||||
// Показать путь с векторами скорости
|
||||
public void ShowPath(List<Point> path)
|
||||
{
|
||||
Console.WriteLine("\nПуть решения:");
|
||||
Point? prevVelocity = new Point(0, 0);
|
||||
|
||||
for (int i = 0; i < path.Count; i++)
|
||||
{
|
||||
Point velocity = i > 0 ? path[i] - path[i - 1] : new Point(0, 0);
|
||||
Point acceleration = velocity - prevVelocity;
|
||||
|
||||
Console.WriteLine($"Шаг {i}: Позиция={path[i]}, Скорость={velocity}, Ускорение={acceleration}");
|
||||
prevVelocity = velocity;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Program
|
||||
{
|
||||
static void Main(string[] args)
|
||||
{
|
||||
Console.WriteLine("=== Гонки на бумаге ===\n");
|
||||
|
||||
// Создаем поле 15x15
|
||||
int width = 42;
|
||||
int height = 42;
|
||||
|
||||
// Стартовая позиция
|
||||
var start = new Point(1, 1);
|
||||
|
||||
// Чекпоинты (нужно посетить все в любом порядке)
|
||||
var checkpoints = new Dictionary<int, Point>
|
||||
{
|
||||
{ 1, new Point(5, 5) },
|
||||
{ 2, new Point(10, 10) },
|
||||
{ 3, new Point(12, 3) }
|
||||
};
|
||||
|
||||
// Препятствия
|
||||
var obstacles = new HashSet<Point>();
|
||||
|
||||
// Горизонтальная стена
|
||||
for (int x = 3; x <= 8; x++)
|
||||
obstacles.Add(new Point(x, 7));
|
||||
|
||||
// Вертикальная стена
|
||||
for (int y = 2; y <= 6; y++)
|
||||
obstacles.Add(new Point(8, y));
|
||||
|
||||
// Создаем трек
|
||||
var track = new RaceTrack(width, height, start, checkpoints, obstacles);
|
||||
|
||||
Console.WriteLine("Начальное поле:");
|
||||
Console.WriteLine("S - старт, 1,2,3 - чекпоинты, # - препятствия\n");
|
||||
track.Visualize();
|
||||
|
||||
Console.WriteLine("\nПоиск решения...\n");
|
||||
var solution = track.FindSolution();
|
||||
|
||||
if (solution != null)
|
||||
{
|
||||
Console.WriteLine("\n=== РЕШЕНИЕ НАЙДЕНО ===\n");
|
||||
track.Visualize(solution);
|
||||
track.ShowPath(solution);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("\nРешение не найдено!");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user