add web-service
This commit is contained in:
292
ProgramWebService.cs
Normal file
292
ProgramWebService.cs
Normal 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}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user