diff options
Diffstat (limited to 'Занимательное программирование/7/4_solver/problems.pas')
| -rw-r--r-- | Занимательное программирование/7/4_solver/problems.pas | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/Занимательное программирование/7/4_solver/problems.pas b/Занимательное программирование/7/4_solver/problems.pas new file mode 100644 index 0000000..1046603 --- /dev/null +++ b/Занимательное программирование/7/4_solver/problems.pas @@ -0,0 +1,218 @@ +unit Problems;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, States;
+
+function GetNeighbours(AState: TState): TStates;
+function Search(InitialState: TState): TStates;
+{ Можно ли передвинуть камень в указанном направлении. }
+function CanPush(Element, Delta: TPosition; Walls, Boulders: TPositions): Boolean;
+function IsGoal(ANode: TNode): Boolean;
+
+function AtSomePosition(Needle: TPosition; Heap: TPositions): Boolean;
+{ Дистанция между двумя позициями не учитывая стен. }
+function ManhattanDistance(A, B: TPosition): Integer;
+function CalculateHeuristic(AState: TState): Integer;
+function ReconstructPath(CameFrom: TNode): TStates;
+
+implementation
+
+function GetNeighbours(AState: TState): TStates;
+const
+ { Вниз, вверх, вправо, влево. }
+ Moves: array[1..4] of TPosition = (
+ (X: 0; Y: 1),
+ (X: 0; Y: -1),
+ (X: 1; Y: 0),
+ (X: -1; Y: 0)
+ );
+var
+ NewState: TState;
+ NewPosition, Move, Boulder: TPosition;
+begin
+ Result := TStates.Create;
+
+ for Move in Moves do
+ begin
+ NewPosition.X := AState.Agent.X + Move.X;
+ NewPosition.Y := AState.Agent.Y + Move.Y;
+
+ if IsFree(NewPosition, AState.Walls) then
+ begin
+ NewState := TState.Create(AState, NewPosition);
+
+ { Если можно передвинуть камень, добавляем новую позицию камня.
+ Иначе, оставляем камень на месте. }
+ for Boulder in AState.Boulders do
+ if Boulder = NewPosition then
+ if CanPush(Boulder, Move, AState.Walls, AState.Boulders) then
+ NewState.AddBoulder(Boulder.X + Move.X, Boulder.Y + Move.Y)
+ else
+ begin
+ FreeAndNil(NewState);
+ break
+ end
+ else
+ NewState.AddBoulder(Boulder.X, Boulder.Y);
+
+ if NewState <> nil then
+ begin
+ SetLength(Result, Length(Result) + 1);
+ Result[High(Result)] := NewState
+ end
+ end
+ end
+end;
+
+function CanPush(Element, Delta: TPosition; Walls, Boulders: TPositions): Boolean;
+var
+ NewPosition: TPosition;
+begin
+ NewPosition.X := Element.X + Delta.X;
+ NewPosition.Y := Element.Y + Delta.Y;
+ Result := IsFree(NewPosition, Walls) and IsFree(NewPosition, Boulders)
+end;
+
+function IsGoal(ANode: TNode): Boolean;
+var
+ CurrentPlace: TPosition;
+begin
+ Result := true;
+
+ for CurrentPlace in ANode.State.Places do
+ begin
+ Result := AtSomePosition(CurrentPlace, ANode.State.Boulders);
+ if not Result then
+ Exit
+ end
+end;
+
+function Search(InitialState: TState): TStates;
+var
+ Frontier: TPriorityQueue;
+ Heuristics, i: Integer;
+ NextState, CurrentState: TState;
+ NextNode, CurrentNode: TNode;
+ Neighbours: TStates;
+ Visited: TStates;
+begin
+ Visited := TStates.Create;
+ Result := TStates.Create;
+
+ Frontier := TPriorityQueue.Create;
+ try
+ Heuristics := CalculateHeuristic(initialState);
+ { Создаем начало графа. }
+ NextState := TState.Create(InitialState);
+ NextNode := TNode.Create(NextState, nil, 0, Heuristics);
+ Frontier.Add(NextNode);
+
+ while not Frontier.IsEmpty do
+ begin
+ Heuristics := Frontier.Count;
+ CurrentNode := Frontier.Poll;
+
+ SetLength(Visited, Length(Visited) + 1);
+ Visited[High(Visited)] := CurrentNode.State;
+
+ if IsGoal(CurrentNode) then
+ begin
+ Result := ReconstructPath(CurrentNode);
+ Exit
+ end;
+ Neighbours := GetNeighbours(CurrentNode.State);
+
+ for i := 0 to High(Neighbours) do
+ begin
+ NextState := Neighbours[i];
+ for CurrentState in Visited do
+ if CurrentState.Compare(NextState) then
+ begin
+ FreeAndNil(NextState);
+ break
+ end;
+
+ if NextState <> nil then
+ begin
+ Heuristics := CalculateHeuristic(NextState);
+ NextNode := TNode.Create(NextState, CurrentNode, CurrentNode.Cost + 1, Heuristics);
+ Frontier.Add(NextNode)
+ end
+ end
+ end
+ finally
+ FreeAndNil(Frontier)
+ end;
+end;
+
+function AtSomePosition(Needle: TPosition; Heap: TPositions): Boolean;
+var
+ CurrentPosition: TPosition;
+begin
+ Result := false;
+ for CurrentPosition in Heap do
+ if CurrentPosition = Needle then
+ begin
+ Result := true;
+ Exit
+ end
+end;
+
+function ManhattanDistance(A, B: TPosition): Integer;
+begin
+ Result := Abs(A.X - B.X) + Abs(A.Y - B.Y)
+end;
+
+function CalculateHeuristic(AState: TState): Integer;
+var
+ CurrentPlace, CurrentBoulder: TPosition;
+ Distance, MinimalDistance: Integer;
+begin
+ Result := 0;
+ for CurrentBoulder in AState.Boulders do
+ begin
+ MinimalDistance := Integer.MaxValue;
+ for CurrentPlace in AState.Places do
+ begin
+ Distance := ManhattanDistance(CurrentBoulder, CurrentPlace);
+ if (not AtSomePosition(CurrentPlace, AState.Boulders)) and (Distance < MinimalDistance) then
+ MinimalDistance := Distance
+ end;
+ Inc(Result, MinimalDistance)
+ end
+end;
+
+function ReconstructPath(CameFrom: TNode): TStates;
+var
+ Temporary: TState;
+ CurrentLength, CurrentSegment: Integer;
+begin
+ Result := TStates.Create;
+ CurrentLength := 0;
+
+ while CameFrom <> nil do
+ begin
+ SetLength(Result, CurrentLength + 1);
+ Result[CurrentLength] := TState.Create(CameFrom.State);
+ CameFrom := CameFrom.Node;
+ Inc(CurrentLength)
+ end;
+ Dec(CurrentLength);
+ CurrentSegment := 0;
+
+ while CurrentSegment < CurrentLength do
+ begin
+ Temporary := Result[CurrentSegment];
+ Result[CurrentSegment] := Result[CurrentLength];
+ Result[CurrentLength] := Temporary;
+ Dec(CurrentLength);
+ Inc(CurrentSegment)
+ end
+end;
+
+end.
+
|
