/// <summary> /// Конструктор /// </summary> /// <param name="parRoot">Родиельский узел</param> /// <param name="parLevel">Уровень</param> public SolutionThree(Move parMove, SolutionThree parRoot, int parLevel) { _Root = parRoot; _Level = parLevel; _Move = parMove; _ChildNodes = new List<SolutionThree>(); }
/// <summary> /// Проверка тупика /// </summary> /// <param name="parSolutionThree">проверяемый узел дерева</param> /// <returns>true - если узел тупиковый </returns> public override bool Deadlock(SolutionThree parSolutionThree) { bool deadlock = false; if ((parSolutionThree.Level >= MaxAlowableDepth)) { deadlock = true; } return deadlock; }
/// <summary> /// метод наилучшего частичного пути /// </summary> /// <param name="parSolutionThree">Корневой узел</param> /// <param name="parLocalDepth">величина углубления в ширину</param> /// <returns></returns> private bool PartialPath(SolutionThree parSolutionThree, int parLocalDepth) { //флаг нахождения решения bool complete = false; //флаг завершение алгоритма bool done = false; int localDepth = 0; SolutionThree solutionThree = parSolutionThree; //инициализируем список вершин дерева решений на текущем уровне List<SolutionThree> currentLevelNodes = new List<SolutionThree>(); //и на следующем уровне List<SolutionThree> nextLevelNodes = new List<SolutionThree>(); //заносим корневую ситуацию в список вершин текущего уровня currentLevelNodes.Add(solutionThree); //если корневой узел является целевым if (GoalIsReached(solutionThree)) { //решение найдено complete = true; } //в противном случае начинаем алгоритм поиска else { //пока алгоритм не завершен выволняем действия while (!done) { //рассматриваем каждый узел на текущем уровне for (int i = 0; (i < currentLevelNodes.Count) && (!done); i++) { //если пора углубляться if (localDepth == parLocalDepth) { complete = PartialPath(currentLevelNodes[i], parLocalDepth); done = complete; currentLevelNodes[i] = null; } else { //получаем список возможных ходов List<Move> availableMove = GetAvailableMoves(currentLevelNodes[i]); //для каждого возможного хода for (int j = 0; (j < availableMove.Count) && (!done); j++) { //совершаем ход MakeMove(availableMove[j], currentLevelNodes[i]); //добавляем его в дерево решений currentLevelNodes[i].AddChildNode(availableMove[j]); _Info.GeneratedNodesCount++; SolutionThree newNode = currentLevelNodes[i].ChildNodes[currentLevelNodes[i].ChildNodes.Count - 1]; //запоминаем максимальную глубину поиска _Info.MaxDepth = newNode.Level; //если сгенерированный узел является целевым if (GoalIsReached(newNode)) { //решение найдено complete = true; //алгоритм завершен done = true; //запоминаем длину пути и получаем список ходов до цели _Info.PathLenght = newNode.Level; _Info.MovesToGoal = GetMovesToGoal(newNode); } else { //если сгенерированный узел не тупиковый, if (!Deadlock(newNode)) { //добавляем его в список узлов следующего уровня nextLevelNodes.Add(newNode); } } } } } //если решение не было достигнуто if (!complete) { //если на следующем уровен нету узлов if (nextLevelNodes.Count == 0) { //алгоритм закончен done = true; } else { //если следующий уровень является максимально допустимым if (nextLevelNodes[0].Level >= MaxAlowableDepth) { //алгоритм закончен done = true; } //иначе else { //переходим на следующий уровень currentLevelNodes = nextLevelNodes; nextLevelNodes = new List<SolutionThree>(); localDepth++; if (localDepth == parLocalDepth) { //сортируем по оценочной функции SortThreeNodes(currentLevelNodes); } } } } } } return complete; }
/// <summary> /// Поиск в ширину /// </summary> /// <returns></returns> public bool BreadthSolve() { //Засекаем время _Info.ProcessTime = new Stopwatch(); _Info.ProcessTime.Start(); //Задаем начальные сведения _Info.MaxDepth = 0; _Info.PathLenght = 0; _Info.GeneratedNodesCount = 0; _Info.MovesToGoal = new List<Move>(); //флаг нахождения решения bool complete = false; //флаг завершение алгоритма bool done = false; //создаем первый ход Move firstMove = CreateFirstMove(); //добавляем узел в дерево решений SolutionThree solutionThree = new SolutionThree(firstMove, null, 0); //инициализируем список вершин дерева решений на текущем уровне List<SolutionThree> currentLevelNodes = new List<SolutionThree>(); //и на следующем уровне List<SolutionThree> nextLevelNodes = new List<SolutionThree>(); //заносим корневую ситуацию в список вершин текущего уровня currentLevelNodes.Add(solutionThree); //если корневой узел является целевым if (GoalIsReached(solutionThree)) { //решение найдено complete = true; } //в противном случае начинаем алгоритм поиска else { //пока алгоритм не завершен выволняем действия while (!done) { //рассматриваем каждый узел на текущем уровне for (int i = 0; (i < currentLevelNodes.Count) && (!done); i++) { //получаем список возможных ходов List<Move> availableMove = GetAvailableMoves(currentLevelNodes[i]); //для каждого возможного хода for (int j = 0; (j < availableMove.Count) && (!done); j++) { //совершаем ход MakeMove(availableMove[j], currentLevelNodes[i]); //добавляем его в дерево решений currentLevelNodes[i].AddChildNode(availableMove[j]); _Info.GeneratedNodesCount++; SolutionThree newNode = currentLevelNodes[i].ChildNodes[currentLevelNodes[i].ChildNodes.Count - 1]; //запоминаем максимальную глубину поиска _Info.MaxDepth = newNode.Level; //если сгенерированный узел является целевым if (GoalIsReached(newNode)) { //решение найдено complete = true; //алгоритм завершен done = true; //запоминаем длину пути и получаем список ходов до цели _Info.PathLenght = newNode.Level; _Info.MovesToGoal = GetMovesToGoal(newNode); } else { //если сгенерированный узел не тупиковый, if (!Deadlock(newNode)) { //добавляем его в список узлов следующего уровня nextLevelNodes.Add(newNode); } } } } //если решение не было достигнуто if (!complete) { //если на следующем уровен нету узлов if (nextLevelNodes.Count == 0) { //алгоритм закончен done = true; } else { //если следующий уровень является максимально допустимым if (nextLevelNodes[0].Level >= MaxAlowableDepth) { //алгоритм закончен done = true; } //иначе else { //переходим на следующий уровень currentLevelNodes = nextLevelNodes; nextLevelNodes = new List<SolutionThree>(); } } } } } //Фиксируем время выполнения _Info.ProcessTime.Stop(); //Записываем результат выполнения _Info.Complete = complete; _Info.Method = "Поиск в ширину"; //если решение было найдено if (complete) { //рассчитываем разветвленность и направленность _Info.Branching = (double)_Info.GeneratedNodesCount / (double)_Info.PathLenght; _Info.Directionality = Math.Pow(_Info.GeneratedNodesCount, ((double)1 / _Info.PathLenght)); } //если решение небыло найдено else { //указываем что длина пути решения, разветвленность и направленность не вычеслены числом -1 _Info.PathLenght = -1; _Info.Branching = -1; _Info.Directionality = -1; } return complete; }
/// <summary> /// Определение тупика в указанной вершине /// </summary> /// <param name="parSolutionThree">проверяемая вершина дерева</param> /// <returns>true - если вершина тупиковая</returns> public abstract bool Deadlock(SolutionThree parSolutionThree);
/// <summary> /// Отмена хода /// </summary> /// <param name="parMove"></param> /// <param name="parSolutionThree"></param> public override void CancelMove(Move parMove, SolutionThree parSolutionThree) { _Task.Mirrors = parMove.Mirrors; for (int i = 0; i < _Task.Mirrors.Count; i++) { if ((_Task.Mirrors[i].Left == parMove.ToPos.X) && (_Task.Mirrors[i].Top == parMove.ToPos.Y)) { parMove.Mirrors[i] = new Square(parMove.FromPos.Y, parMove.FromPos.Y + LasersGame.COORDINATE_RATIO, parMove.FromPos.X, parMove.FromPos.X + LasersGame.COORDINATE_RATIO); _Task.CalculateLaser(); } } }
/// <summary> /// Совершение хода /// </summary> /// <param name="parMove">Совершаемый ход</param> /// <param name="parSolutionThree">узел дерева, из которого совершаем ход</param> public override void MakeMove(Move parMove, SolutionThree parSolutionThree) { //активизируем поле _Task.Mirrors = GetMirrorsCopy(parMove.Mirrors); //проходим по всем зеркалам for (int i = 0; i < _Task.Mirrors.Count; i++) { //находим зеркало указанное в ходе и совершием ход if ((_Task.Mirrors[i].Left == parMove.FromPos.X) && (_Task.Mirrors[i].Top == parMove.FromPos.Y)) { parMove.Mirrors[i] = new Square(parMove.ToPos.Y, parMove.ToPos.Y + LasersGame.COORDINATE_RATIO, parMove.ToPos.X, parMove.ToPos.X + LasersGame.COORDINATE_RATIO); } } _Task.Mirrors = GetMirrorsCopy(parMove.Mirrors); //пересчитываем траекторию лазера _Task.CalculateLaser(); }
/// <summary> /// Проверка достижения цели /// </summary> /// <param name="parSolutionThree">проверяемый узел дерева</param> /// <returns>true - если узел целевой</returns> public override bool GoalIsReached(SolutionThree parSolutionThree) { return _Task.GoalReached(); }
/// <summary> /// Метод наилучшего частичного пути /// </summary> /// <param name="parLocalDepth">Величина углубления в ширину</param> /// <returns></returns> public bool PartialPath(int parLocalDepth) { //Засекаем время _Info.ProcessTime = new Stopwatch(); _Info.ProcessTime.Start(); //Задаем начальные сведения _Info.MaxDepth = 0; _Info.PathLenght = 0; _Info.GeneratedNodesCount = 0; _Info.MovesToGoal = new List<Move>(); //флаг нахождения решения bool complete = false; //создаем первый ход Move firstMove = CreateFirstMove(); //добавляем узел в дерево решений SolutionThree solutionThree = new SolutionThree(firstMove, null, 0); complete = PartialPath(solutionThree, parLocalDepth); //Фиксируем время выполнения _Info.ProcessTime.Stop(); //Записываем результат выполнения _Info.Complete = complete; _Info.Method = "Метод наилучшего частичного пути"; //если решение было найдено if (complete) { //рассчитываем разветвленность и направленность _Info.Branching = (double)_Info.GeneratedNodesCount / (double)_Info.PathLenght; _Info.Directionality = Math.Pow(_Info.GeneratedNodesCount, ((double)1 / _Info.PathLenght)); } //если решение небыло найдено else { //указываем что длина пути решения, разветвленность и направленность не вычеслены числом -1 _Info.PathLenght = -1; _Info.Branching = -1; _Info.Directionality = -1; } return complete; }
/// <summary> /// Совершение хода /// </summary> /// <param name="parMove">Ход</param> /// <param name="parSolutionThree"> узелд дерева из которого совершается ход</param> public abstract void MakeMove(Move parMove, SolutionThree parSolutionThree);
/// <summary> /// Определение существования указанной ситуации /// </summary> /// <param name="parSolutionThree">вершина дерева решиня с проверяемой ситуацией</param> /// <returns></returns> public abstract bool IsExistsSituation(List<Situation> parSituations, SolutionThree parSolutionThree);
/// <summary> /// Определение достижения цели /// </summary> /// <param name="parSolutionThree">проверяемый узел дерева</param> /// <returns>true - если вершина целевая</returns> public abstract bool GoalIsReached(SolutionThree parSolutionThree);
/// <summary> /// ПОлучение списка возможных ходов из указанной вершины дерева /// </summary> /// <param name="parSolutionThree">вершина дерева</param> /// <returns>Список возможных ходов</returns> public abstract List<Move> GetAvailableMoves(SolutionThree parSolutionThree);
/// <summary> /// Оценочная функция /// </summary> /// <param name="parMove">оцениваемый ход</param> /// <param name="parSolutionThree"> узелд дерева из которого совершается ход</param> /// <returns>оценка хода</returns> public abstract int Estimator(Move parMove, SolutionThree parSolutionThree);
/// <summary> /// Оценочная функция /// </summary> /// <param name="parMove">оцениваемый ход</param> /// <param name="parSolutionThree"> узелд дерева из которого совершается ход</param> /// <returns>rколичество ходов, прошедших с момента последненго перемещения зеркала, указанного в ходе</returns> public override int Estimator(Move parMove, SolutionThree parSolutionThree) { int rating = MaxAlowableDepth + 1; //получаем исходное положение перемещаемого зеркала Point mirrorPosition = parMove.FromPos; //и узел дерева из которого совершаем ход SolutionThree solutionNode = parSolutionThree; //пока не добрались до корня дерва поиска while (solutionNode.Root != null) { //если ход, приведший к текущему узлу дерева, перемещал проверяемое зеркало if (solutionNode.Move.ToPos == mirrorPosition) { //считаем сколько ходов назад это было rating = parSolutionThree.Level - solutionNode.Level + 1; break; } //иначе else { //и перемещаемся вверх по дереву. solutionNode = solutionNode.Root; } } return rating; }
/// <summary> /// Получение списка возможных ходов /// </summary> /// <param name="parSolutionThree"></param> /// <returns></returns> public override List<Move> GetAvailableMoves(SolutionThree parSolutionThree) { List<Move> availableMove = new List<Move>(); //получаем последний совершенный ход Move lastMove = parSolutionThree.Move; //активизируем поле, указанное в ходе _Task.Mirrors = GetMirrorsCopy(lastMove.Mirrors); //проходим по всез зеркалам for (int k = 0; k < _Task.Mirrors.Count; k++) { //в качаестве начальной точки берем положение очередного зеркала Point fromPos = new Point(_Task.Mirrors[k].Left, _Task.Mirrors[k].Top); //проходим по всем координатам поля for (int i = 0; i < _FieldHeight; i += LasersGame.COORDINATE_RATIO) for (int j = 0; j < _FieldWidth; j += LasersGame.COORDINATE_RATIO) { //если ячейка не занята и не являетяс дырой if ( (!_Task.ExistsHole(new Point(j, i))) && (!_Task.ExistsMirror(new Point(j, i))) ) { //выбираем конечную точку Point toPos = new Point(j, i); //и добавляем ход из начально точки в конечную в список возможных ходов availableMove.Add(new Move(fromPos, toPos, GetMirrorsCopy(_Task.Mirrors))); } } } //возвращаем список возможных ходов return availableMove; }
/// <summary> /// Сортировка списка возможных ходов согласно оценочной функции /// </summary> /// <param name="parAvailableMoves"></param> /// <param name="parSolutionThree"> узелд дерева из которого совершается ход</param> public abstract void SortMoves(List<Move> parAvailableMoves, SolutionThree parSolutionThree);
/// <summary> /// Определение существования указанной ситуации /// </summary> /// <param name="parSolutionThree">вершина дерева решиня с проверяемой ситуацией</param> /// <returns></returns> public override bool IsExistsSituation(List<Situation> parSituations, SolutionThree parSolutionThree) { bool isExist = false; List<Square> mirrors = parSolutionThree.Move.Mirrors; foreach(Situation situation in parSituations) { int mirrorsOnPlace = 0; foreach (Square mirror in mirrors) { foreach (Point cell in situation.OcceupedCells) { if ((mirror.Left == cell.X) && (mirror.Top == cell.Y)) { mirrorsOnPlace++; } } } if (mirrorsOnPlace == situation.OcceupedCells.Count) { isExist = true; return isExist; } } return isExist; }
/// <summary> /// Поиск в градиенту /// </summary> /// <returns></returns> public bool SteepestRiseSolve() { //Засекаем время _Info.ProcessTime = new Stopwatch(); _Info.ProcessTime.Start(); //Задаем начальные сведения _Info.MaxDepth = 0; _Info.PathLenght = 0; _Info.GeneratedNodesCount = 0; _Info.MovesToGoal = new List<Move>(); //решение еще не найдено bool complete = false; //создаем первый ход Move firstMove = CreateFirstMove(); //добавляем узел в дерево решений SolutionThree solutionThree = new SolutionThree(firstMove, null, 0); complete = SteepestRiseSolve(firstMove, solutionThree); //Фиксируем время выполнения _Info.ProcessTime.Stop(); //Записываем результат выполнения _Info.Complete = complete; _Info.Method = "Поиск по градиенту"; //если решение было найдено if (complete) { //рассчитываем разветвленность и направленность _Info.Branching = (double)_Info.GeneratedNodesCount / (double)_Info.PathLenght; _Info.Directionality = Math.Pow(_Info.GeneratedNodesCount, ((double)1 / _Info.PathLenght)); } //если решение небыло найдено else { //указываем что длина пути решения, разветвленность и направленность не вычеслены числом -1 _Info.PathLenght = -1; _Info.Branching = -1; _Info.Directionality = -1; } //возвращаем результат поиска return complete; }
/// <summary> /// Сортировка списка возможных ходов согласно оценочной функции /// </summary> /// <param name="parAvailableMoves"></param> /// <param name="parSolutionThree"> узелд дерева из которого совершается ход</param> public override void SortMoves(List<Move> parAvailableMoves, SolutionThree parSolutionThree) { List<Move> availableMoves = parAvailableMoves; List<int> movesMarks = new List<int>(); //для каждого возможного хода for (int i = 0; i < availableMoves.Count; i++) { //получаем его оценку int rating = Estimator(availableMoves[i], parSolutionThree); movesMarks.Add(rating); } //сортируем возможные ходы согласно их оценкам for (int i = 0; i < availableMoves.Count; i++) { for (int j = 0; j < i; j++) { if (movesMarks[i] > movesMarks[j]) { int mark = movesMarks[i]; movesMarks.RemoveAt(i); movesMarks.Insert(j, mark); Move move = availableMoves[i]; availableMoves.RemoveAt(i); availableMoves.Insert(j, move); } } } }
/// <summary> /// Поиск по градиенту /// </summary> /// <param name="parMove">Ход</param> /// <param name="parSolutionThree">дерево решений</param> /// <returns>true если решение найдено, false в противном случае</returns> public bool SteepestRiseSolve(Move parMove, SolutionThree parSolutionThree) { //решение еще не найдено bool complete = false; //дерево решений SolutionThree solutionThree = parSolutionThree; //последний совершенный ход Move lastMove = solutionThree.Move; //если цель достигнута if (GoalIsReached(solutionThree)) { complete = true; //записываем длину пути и получаем список ходов _Info.PathLenght = solutionThree.Level; _Info.MovesToGoal = GetMovesToGoal(solutionThree); } else // ессли тупик if (Deadlock(solutionThree)) { complete = false; } //если цель не достигнута и ситуация не тупиковая else { //получаем список возможных ходщв List<Move> availableMoves = GetAvailableMoves(solutionThree); //упорядочиваем список возможных ходов согласно оценочной функции SortMoves(availableMoves, solutionThree); //проходим по всем узлам for (int i = 0; i < availableMoves.Count; i++) { //совершаем очередной ход MakeMove(availableMoves[i], solutionThree); //добавляем узел в дерево решений solutionThree.AddChildNode(availableMoves[i]); _Info.GeneratedNodesCount++; //Если уровень узла нового узла дерева больше чем сохраненный в логе, if (solutionThree.ChildNodes[0].Level > _Info.MaxDepth) { //то записываем его в лог _Info.MaxDepth = solutionThree.ChildNodes[0].Level; } // вызываем рекурсивный метод поиска в глубину complete = SteepestRiseSolve(availableMoves[i], solutionThree.ChildNodes[0]); //если решение было найдено if (complete) { //заканчиваем просмотр break; } //в противном случае else { //отменяем ход и удаляем рассмотренную ветку дерева CancelMove(availableMoves[i], solutionThree); solutionThree.DeleteChildNode(0); } } } //возвращаем результат выполнения return complete; }
/// <summary> /// Получение ходов до целевой вершины /// </summary> /// <param name="parSolutionNode">целевая вершина</param> /// <returns></returns> private List<Move> GetMovesToGoal(SolutionThree parSolutionNode) { List<Move> movesToGoal = new List<Move>(); SolutionThree currentSolutionNode = parSolutionNode; while (currentSolutionNode.Root != null) { movesToGoal.Insert(0, currentSolutionNode.Move); currentSolutionNode = currentSolutionNode.Root; } return movesToGoal; }
/// <summary> /// Отмена хода /// </summary> /// <param name="parMove">Ход</param> /// <param name="parSolutionThree">узел дерева у которого отменяется ход</param> public abstract void CancelMove(Move parMove, SolutionThree parSolutionThree);