add web-service

This commit is contained in:
2025-10-20 23:07:10 +05:00
parent a74b8bbb5d
commit 4f9417a032
10 changed files with 1266 additions and 282 deletions

292
ProgramWebService.cs Normal file
View File

@@ -0,0 +1,292 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using PaperRacing.AStar;
namespace PaperRacing.WebService
{
// Классы для запросов и ответов API
public class SolveRequest
{
public int[][]? map { get; set; }
public int? maxIterations { get; set; } // Опциональный параметр
public int? timeoutSeconds { get; set; } // Опциональный тайм-аут
}
public class SolveResponse
{
public bool success { get; set; }
public int[][]? solution { get; set; }
public string? error { get; set; }
public SolveStatistics? statistics { get; set; }
}
public class SolveStatistics
{
public int steps { get; set; }
public int checkpoints { get; set; }
public int iterations { get; set; }
public double computeTimeSeconds { get; set; }
public int maxSpeed { get; set; }
}
public class HealthResponse
{
public string status { get; set; } = "healthy";
public string version { get; set; } = "1.0.0";
public DateTime timestamp { get; set; } = DateTime.UtcNow;
}
// Сервис для решения задачи
public class RacingSolverService
{
public SolveResponse Solve(SolveRequest request, int maxIterations = 5000000)
{
try
{
if (request.map == null || request.map.Length == 0)
{
return new SolveResponse
{
success = false,
error = "Map data is required"
};
}
var startTime = DateTime.Now;
// Загружаем карту из переданного JSON
var (width, height, start, checkpoints, obstacles, cellTypes) = ParseMap(request.map);
// Создаем трек
var track = new RaceTrack(width, height, start, checkpoints, obstacles, cellTypes);
// Находим решение
var solution = track.FindSolution();
var elapsed = DateTime.Now - startTime;
if (solution != null)
{
// Конвертируем решение в формат JSON
var accelerations = ConvertPathToAccelerations(solution);
// Статистика
int maxSpeed = CalculateMaxSpeed(solution);
return new SolveResponse
{
success = true,
solution = accelerations,
statistics = new SolveStatistics
{
steps = accelerations.Length,
checkpoints = checkpoints.Count,
iterations = 0, // Можно добавить счетчик итераций
computeTimeSeconds = elapsed.TotalSeconds,
maxSpeed = maxSpeed
}
};
}
else
{
return new SolveResponse
{
success = false,
error = "No solution found within the iteration limit"
};
}
}
catch (Exception ex)
{
return new SolveResponse
{
success = false,
error = $"Error solving: {ex.Message}"
};
}
}
private (int width, int height, Point start, Dictionary<int, Point> checkpoints, HashSet<Point> obstacles, Dictionary<Point, int> cellTypes)
ParseMap(int[][] map)
{
int height = map.Length;
int width = map[0].Length;
var checkpoints = new Dictionary<int, Point>();
var obstacles = new HashSet<Point>();
var cellTypes = new Dictionary<Point, int>();
Point? start = null;
int checkpointId = 1;
// Проходим по карте (JSON карта идет сверху вниз, поэтому инвертируем Y)
for (int jsonY = 0; jsonY < height; jsonY++)
{
for (int x = 0; x < width; x++)
{
int cellType = map[jsonY][x];
// Инвертируем Y координату для правильного отображения
int y = height - 1 - jsonY;
var point = new Point(x, y);
// Сохраняем тип клетки
cellTypes[point] = cellType;
switch (cellType)
{
case 0: // Дорога
if (start == null)
start = point;
break;
case 1: // Камень (препятствие)
obstacles.Add(point);
break;
case 2: // Снег
break;
case 3: // Лёд
break;
case 4: // Чекпоинт
checkpoints[checkpointId++] = point;
break;
case 5: // Старт (приоритетнее чем тип 0)
start = point;
break;
}
}
}
if (start == null)
throw new Exception("Start position not found (cell type 0 or 5)");
return (width, height, start, checkpoints, obstacles, cellTypes);
}
private int[][] ConvertPathToAccelerations(List<Point> path)
{
var accelerations = new List<int[]>();
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;
accelerations.Add(new int[] { acceleration.X, -acceleration.Y });
prevVelocity = velocity;
}
return accelerations.ToArray();
}
private int CalculateMaxSpeed(List<Point> solution)
{
int maxSpeed = 0;
for (int i = 1; i < solution.Count; i++)
{
var velocity = solution[i] - solution[i - 1];
int speed = Math.Abs(velocity.X) + Math.Abs(velocity.Y);
maxSpeed = Math.Max(maxSpeed, speed);
}
return maxSpeed;
}
}
class Program
{
static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
// Добавляем сервисы
builder.Services.AddSingleton<RacingSolverService>();
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowAll", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
});
});
// Настраиваем JSON опции
builder.Services.ConfigureHttpJsonOptions(options =>
{
options.SerializerOptions.PropertyNamingPolicy = JsonNamingPolicy.CamelCase;
options.SerializerOptions.WriteIndented = true;
});
var app = builder.Build();
// Middleware
app.UseCors("AllowAll");
// Health check endpoint
app.MapGet("/health", () => new HealthResponse());
// Информация об API
app.MapGet("/", () => new
{
service = "Paper Racing A* Solver",
version = "1.0.0",
endpoints = new
{
health = "GET /health",
solve = "POST /solve",
info = "GET /"
},
documentation = "POST /solve with JSON body containing 'map' field (2D array of integers)"
});
// Основной endpoint для решения задачи
app.MapPost("/solve", (SolveRequest request, RacingSolverService solver) =>
{
Console.WriteLine($"\n[{DateTime.Now:yyyy-MM-dd HH:mm:ss}] Received solve request");
if (request.map == null || request.map.Length == 0)
{
Console.WriteLine(" ❌ Invalid request: map is empty");
return Results.BadRequest(new SolveResponse
{
success = false,
error = "Map data is required"
});
}
Console.WriteLine($" Map size: {request.map[0].Length}x{request.map.Length}");
var response = solver.Solve(request, request.maxIterations ?? 5000000);
if (response.success)
{
Console.WriteLine($" ✓ Solution found: {response.statistics?.steps} steps in {response.statistics?.computeTimeSeconds:F2}s");
}
else
{
Console.WriteLine($" ❌ Solution not found: {response.error}");
}
return response.success ? Results.Ok(response) : Results.Ok(response);
});
// Запускаем сервер
var port = Environment.GetEnvironmentVariable("PORT") ?? "5000";
Console.WriteLine("╔════════════════════════════════════════════════════════════╗");
Console.WriteLine("║ Paper Racing A* Solver - Web Service ║");
Console.WriteLine("╚════════════════════════════════════════════════════════════╝");
Console.WriteLine($"\n🚀 Server starting on http://localhost:{port}");
Console.WriteLine($"\nEndpoints:");
Console.WriteLine($" GET http://localhost:{port}/ - API info");
Console.WriteLine($" GET http://localhost:{port}/health - Health check");
Console.WriteLine($" POST http://localhost:{port}/solve - Solve racing map");
Console.WriteLine($"\nPress Ctrl+C to stop\n");
app.Run($"http://0.0.0.0:{port}");
}
}
}