/// <summary> /// Вернуть блоки шаблона железной дороги в модель /// </summary> /// <param name="template">Шаблон железной дороги</param> /// <param name="model">Модель</param> public static void ReturnBlocksToModel(this IRailwayTemplate template, Model model) { foreach (var item in template.GetRailways()) { model.Blocks.Find(_ => _.Name == item.Name).Count++; } }
private void Next() { if (Cursor1Enabled) { if (_current1.Next != null) { _current1 = _current1.Next; } else { _current1 = _chain; } } if (Cursor2Enabled) { if (_current2.Next != null) { _current2 = _current2.Next; } else { _current2 = _chain; } } }
private void RemoveAt(IRailwayTemplate cursor) { if (!(cursor is Railway railway && railway.Type == RailwayType.L0)) { // Возвращаем блоки в список доступных cursor.ReturnBlocksToModel(_answer); if (cursor.Prev != null) { cursor.Prev.Next = cursor.Next; } if (cursor.Next != null) { cursor.Next.Prev = cursor.Prev; cursor.Next.Start = cursor.Prev?.End ?? Point.Zero; } if (cursor == _current1) { _current1 = cursor.Prev; } else if (cursor == _current2) { _current2 = cursor.Prev; } } }
/// <summary> /// Преобразовать двусвязный список шаблонов железной дороги в модель по ТЗ /// </summary> /// <param name="head">Указатель на начало списка</param> /// <param name="initial">Исходные данные модели (блоки DATA, ROUTES)</param> /// <returns></returns> public static Model ConvertToModel(this IRailwayTemplate head, Model initial = null) { // Копируем исходные данные var model = Model.Copy(initial); model.Topology.Clear(); model.Order.Clear(); var k = 0; var template = head; do { // Разбиваем шаблон железной дороги на отдельные блоки var railways = template.GetRailways(); for (var i = 0; i < railways.Count; i++) { var railway = railways[i]; if (railway.Type == RailwayType.L0) { continue; } var source = k + i - 1; var destination = source + 1; // Добавляем каждый блок в модель model.Order.Add(railway.Name); var topology = new TopologyItem(source, destination, railway.Direction); model.Topology.Add(topology); } template = template.Next; k += railways.Count; if (template == null) { var topology = new TopologyItem(k - 1, k, railways.Last().Direction); model.Topology.Add(topology); } }while (template != null); model.Topology.Last().SecondBlock = 0; return(model); }
/// <summary> /// Цепочка блоков рельсов /// </summary> /// <param name="railways">Блоки рельсов</param> public RailwayChain(params IRailwayTemplate[] railways) { for (var i = 0; i < railways.Length; i++) { if (i == 0) { _head = railways[i]; _tail = _head; continue; } _tail = _tail.Append(railways[i]); } Dimensions = new TemplateDimensions(this, false); }
private void Prev() { if (Cursor1Enabled) { if (!(_current1 is Railway railway && railway.Type == RailwayType.L0)) { _current1 = _current1.Prev; } else { while (_current1?.Next != null) { _current1 = _current1.Next; } } }
/// <summary> /// Симметричное добавление шаблона к текущему шаблону /// </summary> /// <param name="destination">Текущий шаблон (шаблон назначения)</param> /// <param name="template">Образец присоединяемого шаблона</param> /// <exception cref="InvalidOperationException" /> public static void AppendSymmetric(this IRailwayTemplate destination, IRailwayTemplate template) { if (destination.Symmetric == null) { throw new InvalidOperationException("Отсутствует симметричная точка для назначенного шаблона"); } // Создаём копии шаблона var forward = template.Clone(); var backward = template.Clone(); // Добавляем шаблоны в две точки разрыва destination.Append(forward); destination.Symmetric.Append(backward); // Связываем добавленные блоки связью симметрии forward.Symmetric = backward; backward.Symmetric = forward; }
/// <summary> /// Присоединить шаблон железной дороги к существующему шаблону железной дороги /// </summary> /// <param name="first">Существующий шаблон железной дороги</param> /// <param name="second">Присоединяемый шаблон железной дороги</param> /// <returns></returns> public static IRailwayTemplate Append(this IRailwayTemplate first, IRailwayTemplate second) { var next = first.Next; first.Next = second; second.Prev = first; // Если у существующего шаблона было продолжение, // то присоединяем его к присоединяемому шаблону // TODO: Если у присоединяемого блока есть продолжение, то оно потеряется! if (next != null && next != second) { second.Next = next; second.Next.Prev = second; } second.Start = first.End; return(second); }
/// <summary> /// Размеры шаблона /// </summary> /// <param name="railway">Шаблон</param> /// <param name="shouldClone">Стоит ли клонировать объект</param> public TemplateDimensions(IRailwayTemplate railway, bool shouldClone = true) { var template = shouldClone ? railway.Clone() : railway; var railways = template.GetRailways(); var points = railways.Select(_ => _.Start).ToList(); points.AddRange(railways.Select(_ => _.End)); if (points.Count > 0) { Min = new Point(points.Min(_ => _.X), points.Min(_ => _.Y)); Max = new Point(points.Max(_ => _.X), points.Max(_ => _.Y)); } Output = new Point( template.End.X - template.Start.X, template.End.Y - template.Start.Y, template.End.Angle - template.Start.Angle); Width = Max.X - Min.X; Height = Max.Y - Min.Y; }
private void Add(string blueprint) { if (Cursor1Enabled) { if (RailwayFactory.Default.TryBuildTemplate(out var template, out var error, blueprint, _answer)) { _current1.Append(template); if (_current1?.Next != null) { _current1 = _current1.Next; } } else { Context.ErrorMessage = error; } } if (Cursor2Enabled) { if (RailwayFactory.Default.TryBuildTemplate(out var template, out var error, blueprint, _answer)) { _current2.Append(template); if (_current2?.Next != null) { _current2 = _current2.Next; } } else { Context.ErrorMessage = error; } } }
/// <inheritdoc /> /// <exception cref="ArgumentNullException" /> public FinalAnswer Solve(Model model, IDirectTaskSolver checker) { _model = model; // ?? throw new ArgumentNullException(nameof(model)); var factory = new RailwayFactory(); // Разброс точек маршрута var routesSize = GetRoutesSize(model); // Создать стартовую трассу - кольцо var cycle = RailwayTemplates.CreateCircle(RailwayType.T4R); // Функция отображения модели void DisplayStep(IRailwayTemplate t) { _answer = t.ConvertToModel(_model); OnStepEvent?.Invoke(this, new FinalAnswer(_answer, checker.Solve(_answer))); } DisplayStep(cycle); var compass = RailwayTemplates.Compass(cycle); var l = 2; const int CYCLES_COUNT = 5; IRailwayTemplate head = cycle; for (var k = 0; k < CYCLES_COUNT; k++) { // Left for (var i = 0; i < l; i++) { if (!head.TryScale(Direction.E, Railway.L3)) { break; } DisplayStep(cycle); } // Up var chain1 = factory.BuildTemplate("t2T1L6", model); head.TryMutate(chain1); DisplayStep(cycle); // Up for (var i = 0; i < l; i++) { if (!chain1.TryScale(Direction.N, Railway.L3)) { break; } DisplayStep(cycle); } // Right var chain2 = factory.BuildTemplate("t2T1L6", model); chain1.TryMutate(chain2); DisplayStep(cycle); l += 3; // Right for (var i = 0; i < l; i++) { if (!chain2.TryScale(Direction.W, Railway.L3)) { break; } DisplayStep(cycle); } // Down var chain3 = factory.BuildTemplate("t2T1L6", model); chain2.TryMutate(chain3); DisplayStep(cycle); // Down for (var i = 0; i < l; i++) { if (!chain3.TryScale(Direction.S, Railway.L3)) { break; } DisplayStep(cycle); } // Right var chain4 = factory.BuildTemplate("t2T1L6", model); chain3.TryMutate(chain4); DisplayStep(cycle); head = chain4; l += 3; } // Первый этап - расширяем кольцо вверх, вниз и в сторону /* * const int count = 16; * for (var i = 0; i < count; i++) * { * // Блок, к которому добавляем L1 * var dest = compass.W; * * // Расширение по сторонам * if (i < count / 4) dest = compass.W; * //else if (i < 2 * count / 4) dest = compass.NW; * else if (i < 3 * count / 4) dest = compass.N; * //else if (i < 4 * count / 4) dest = compass.NE; * * // Чередование расширений по сторонам * //if (i % 4 == 0) dest = compass.W; * //else if (i % 4 == 1) dest = compass.NW; * //else if (i % 4 == 2) dest = compass.N; * //else if (i % 4 == 3) dest = compass.NE; * * var chain = new RailwayChain(new[] * { * new Railway(RailwayType.T8L), * new Railway(RailwayType.T8R), * }); * //dest.AppendSymmetric(chain); * * dest.AppendSymmetric(Railway.L1); * * DisplayStep(cycle); * } */ return(new FinalAnswer { Model = _answer, Price = checker.Solve(_answer), }); }
/// <summary> /// Вызвать изменение размера /// в указанном или любом направлении /// </summary> /// <param name="item">Шаблон масштабирования</param> /// <param name="direction">Направление</param> /// <param name="template">Шаблон для вставки. Если null - то вставить любой блок</param> /// <returns>True - если получилось, иначе - false</returns> public static bool TryScale(this IRailwayTemplate item, Direction direction, IRailwayTemplate template = null) { return(item.TryScale(direction.ToAngle(), template)); }
/// <summary> /// Является ли данный элемент начальным (L0) /// </summary> /// <param name="item"></param> /// <returns></returns> public static bool IsHead(this IRailwayTemplate item) { return(item is Railway railway && railway.Type == RailwayType.L0); }
/// <summary> /// Найти все пересечения блоков рельс /// </summary> /// <param name="template"></param> /// <param name="includeBridges">Включая пересечения под мостами</param> /// <returns>Список точек пересечения или пустой список</returns> public static List <Point> FindCrosses(this IRailwayTemplate template, bool includeBridges = false) { var sync = new object(); var answer = new List <Point>(); var railways = template.GetRailways().ToArray(); Parallel.For(0, railways.Length, (i) => { for (var j = i + 2; j < railways.Length; j++) { if (railways[i].IsHead() || i == 1 && j == railways.Length - 1) { continue; } if (MathFunctions.CrossPoint( railways[i].Start, railways[i].End, railways[j].Start, railways[j].End) is Point p) { if (!includeBridges) { var bridge = (railways[i].Type == RailwayType.B1) ? railways[i] : (railways[j].Type == RailwayType.B1) ? railways[j] : null; if (bridge == null) { lock (sync) { answer.Add(p); } continue; } var center = new Point( x: (bridge.Start.X + bridge.End.X) / 2, y: (bridge.Start.Y + bridge.End.Y) / 2 ); if (p != center) { lock (sync) { answer.Add(p); } } } else { lock (sync) { answer.Add(p); } } } } }); return(answer); }
public bool TryBuildTemplate(out IRailwayTemplate template, out string error, string blueprint, Model model) { // Копируем список блоков var blocks = model?.Blocks.Select(block => (Block)block.Clone()).ToList(); error = null; var regex = new Regex("((?'type'L|T|t|B)(?'lenght'\\d+))"); var railways = new List <Railway>(); foreach (Match match in regex.Matches(blueprint)) { var type = match.Groups["type"].Value; var length = int.Parse(match.Groups["lenght"].Value); switch (type) { case "L": { for (var k = 4; k > 0; k--) { var lineBlocks = blocks?.FirstOrDefault(_ => _.Name == $"L{k}"); while (lineBlocks?.Count > 0 && length >= k) { length -= k; lineBlocks.Count--; var railway = k == 4 ? Railway.L4 : k == 3 ? Railway.L3 : k == 2 ? Railway.L2 : Railway.L1; railways.Add(railway); } } break; } case "T": case "t": { var amount = 0; switch (length) { case 1: amount = 8; break; // T1 = 8 x T8 case 2: amount = 4; break; // T2 = 4 x T8 case 4: amount = 2; break; // T4 = 2 x T8 case 8: amount = 1; break; // T8 = 1 x T8 default: error = $"Некорректная длина блока {type}{length} в шаблоне {blueprint}"; template = null; return(false); } var t4Blocks = blocks?.FirstOrDefault(_ => _.Name == "T4"); var t8Blocks = blocks?.FirstOrDefault(_ => _.Name == "T8"); if (t8Blocks?.Price < t4Blocks?.Price) { while (t8Blocks?.Count > 0 && amount >= 1) { if (t8Blocks?.Count == 1 && amount == 2) { break; } amount -= 1; t8Blocks.Count--; railways.Add(type == "T" ? Railway.T8R : Railway.T8L); } } while (t4Blocks?.Count > 0 && amount >= 2) { amount -= 2; t4Blocks.Count--; railways.Add(type == "T" ? Railway.T4R : Railway.T4L); } while (t8Blocks?.Count > 0 && amount > 0) { amount -= 1; t8Blocks.Count--; railways.Add(type == "T" ? Railway.T8R : Railway.T8L); } if (amount == 0) { length = 0; } break; } case "B": { if (length != 1) { throw new ArgumentException( $"Некорректная длина блока {type}{length} в шаблоне {blueprint}"); } var bridgeBlocks = blocks?.FirstOrDefault(_ => _.Name == "B1"); if (bridgeBlocks?.Count > 0) { length--; bridgeBlocks.Count--; railways.Add(Railway.B1); } break; } } if (length != 0) { error = $"Недостаточно блоков для производства {match.Value} в шаблоне {blueprint}"; template = null; return(false); } } var chain = new RailwayChain(railways.Cast <IRailwayTemplate>().ToArray()); // Создаём автоматические связи симметрии // TODO переделать var directions = chain.GetDirections(); if (directions.ContainsKey(Direction.N) && directions.ContainsKey(Direction.S)) { foreach (var block in directions[Direction.N]) { block.Symmetric = directions[Direction.S].First(); } } if (directions.ContainsKey(Direction.W) && directions.ContainsKey(Direction.E)) { foreach (var block in directions[Direction.W]) { block.Symmetric = directions[Direction.E].First(); } } // Если произвести блок удалось // применяем изменение количества блоков model?.Blocks.Clear(); model?.Blocks.AddRange(blocks); if (chain.GetRailways().Count == 1) { template = chain[0]; return(true); } template = chain; return(true); }