Files
paper-racing-gpi/Program.cs
2025-10-20 19:35:38 +05:00

270 lines
9.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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Решение не найдено!");
}
}
}
}