diff options
| -rw-r--r-- | Занимательное программирование/7/4_solver/assets/1.slv | 6 | ||||
| -rw-r--r-- | Занимательное программирование/7/4_solver/assets/2.slv | 9 | ||||
| -rw-r--r-- | Занимательное программирование/7/4_solver/assets/graphics.png | bin | 0 -> 1337 bytes | |||
| -rw-r--r-- | Занимательное программирование/7/4_solver/assets/numbers.png | bin | 0 -> 2057 bytes | |||
| -rw-r--r-- | Занимательное программирование/7/4_solver/problems.pas | 218 | ||||
| -rw-r--r-- | Занимательное программирование/7/4_solver/solver.lpi | 93 | ||||
| -rw-r--r-- | Занимательное программирование/7/4_solver/solver.lpr | 28 | ||||
| -rw-r--r-- | Занимательное программирование/7/4_solver/solver.lps | 241 | ||||
| -rw-r--r-- | Занимательное программирование/7/4_solver/states.pas | 277 | ||||
| -rw-r--r-- | Занимательное программирование/7/4_solver/unit1.lfm | 185 | ||||
| -rw-r--r-- | Занимательное программирование/7/4_solver/unit1.pas | 177 | ||||
| -rw-r--r-- | Занимательное программирование/README.txt | 4 |
12 files changed, 1236 insertions, 2 deletions
diff --git a/Занимательное программирование/7/4_solver/assets/1.slv b/Занимательное программирование/7/4_solver/assets/1.slv new file mode 100644 index 0000000..9ef8749 --- /dev/null +++ b/Занимательное программирование/7/4_solver/assets/1.slv @@ -0,0 +1,6 @@ +xxxxxxxxxxx
+x x
+x b x
+x p sbxx x
+x x px
+xxxxxxxxxxx
diff --git a/Занимательное программирование/7/4_solver/assets/2.slv b/Занимательное программирование/7/4_solver/assets/2.slv new file mode 100644 index 0000000..3caee9e --- /dev/null +++ b/Занимательное программирование/7/4_solver/assets/2.slv @@ -0,0 +1,9 @@ +xxxxxxxxx
+x x x
+xx x xx
+x x
+x p x x
+x x x
+x b x x
+x s x
+xxxxxxxxx
diff --git a/Занимательное программирование/7/4_solver/assets/graphics.png b/Занимательное программирование/7/4_solver/assets/graphics.png Binary files differnew file mode 100644 index 0000000..39b8af1 --- /dev/null +++ b/Занимательное программирование/7/4_solver/assets/graphics.png diff --git a/Занимательное программирование/7/4_solver/assets/numbers.png b/Занимательное программирование/7/4_solver/assets/numbers.png Binary files differnew file mode 100644 index 0000000..e089747 --- /dev/null +++ b/Занимательное программирование/7/4_solver/assets/numbers.png 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.
+
diff --git a/Занимательное программирование/7/4_solver/solver.lpi b/Занимательное программирование/7/4_solver/solver.lpi new file mode 100644 index 0000000..225be54 --- /dev/null +++ b/Занимательное программирование/7/4_solver/solver.lpi @@ -0,0 +1,93 @@ +<?xml version="1.0" encoding="UTF-8"?> +<CONFIG> + <ProjectOptions> + <Version Value="12"/> + <General> + <SessionStorage Value="InProjectDir"/> + <Title Value="solver"/> + <Scaled Value="True"/> + <ResourceType Value="res"/> + <UseXPManifest Value="True"/> + <XPManifest> + <DpiAware Value="True"/> + </XPManifest> + <Icon Value="0"/> + </General> + <BuildModes> + <Item Name="Default" Default="True"/> + </BuildModes> + <PublishOptions> + <Version Value="2"/> + <UseFileFilters Value="True"/> + </PublishOptions> + <RunParams> + <FormatVersion Value="2"/> + </RunParams> + <RequiredPackages> + <Item> + <PackageName Value="LCL"/> + </Item> + </RequiredPackages> + <Units> + <Unit> + <Filename Value="solver.lpr"/> + <IsPartOfProject Value="True"/> + </Unit> + <Unit> + <Filename Value="unit1.pas"/> + <IsPartOfProject Value="True"/> + <ComponentName Value="Form1"/> + <HasResources Value="True"/> + <ResourceBaseClass Value="Form"/> + <UnitName Value="Unit1"/> + </Unit> + <Unit> + <Filename Value="state.pas"/> + <IsPartOfProject Value="True"/> + <UnitName Value="State"/> + </Unit> + <Unit> + <Filename Value="assets/problems.pas"/> + <IsPartOfProject Value="True"/> + <UnitName Value="Problems"/> + </Unit> + <Unit> + <Filename Value="assets/4.slv"/> + <IsPartOfProject Value="True"/> + </Unit> + </Units> + </ProjectOptions> + <CompilerOptions> + <Version Value="11"/> + <Target> + <Filename Value="solver"/> + </Target> + <SearchPaths> + <IncludeFiles Value="$(ProjOutDir)"/> + <UnitOutputDirectory Value="lib/$(TargetCPU)-$(TargetOS)"/> + </SearchPaths> + <Linking> + <Debugging> + <DebugInfoType Value="dsDwarf3"/> + </Debugging> + <Options> + <Win32> + <GraphicApplication Value="True"/> + </Win32> + </Options> + </Linking> + </CompilerOptions> + <Debugging> + <Exceptions> + <Item> + <Name Value="EAbort"/> + </Item> + <Item> + <Name Value="ECodetoolError"/> + </Item> + <Item> + <Name Value="EFOpenError"/> + </Item> + </Exceptions> + </Debugging> +</CONFIG> diff --git a/Занимательное программирование/7/4_solver/solver.lpr b/Занимательное программирование/7/4_solver/solver.lpr new file mode 100644 index 0000000..d5fc39e --- /dev/null +++ b/Занимательное программирование/7/4_solver/solver.lpr @@ -0,0 +1,28 @@ +program solver; + +{$mode objfpc}{$H+} + +uses + {$IFDEF UNIX} + cthreads, + {$ENDIF} + {$IFDEF HASAMIGA} + athreads, + {$ENDIF} + Interfaces, // this includes the LCL widgetset + Forms, Unit1, States, Problems + { you can add units after this }; + +{$R *.res} + +begin + RequireDerivedFormResource:=True; + Application.Scaled:=True; + {$PUSH}{$WARN 5044 OFF} + Application.MainFormOnTaskbar:=True; + {$POP} + Application.Initialize; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. + diff --git a/Занимательное программирование/7/4_solver/solver.lps b/Занимательное программирование/7/4_solver/solver.lps new file mode 100644 index 0000000..043698c --- /dev/null +++ b/Занимательное программирование/7/4_solver/solver.lps @@ -0,0 +1,241 @@ +<?xml version="1.0" encoding="UTF-8"?>
+<CONFIG>
+ <ProjectSession>
+ <Version Value="12"/>
+ <BuildModes Active="Default"/>
+ <Units>
+ <Unit>
+ <Filename Value="solver.lpr"/>
+ <IsPartOfProject Value="True"/>
+ <EditorIndex Value="-1"/>
+ <CursorPos Y="27"/>
+ <UsageCount Value="80"/>
+ </Unit>
+ <Unit>
+ <Filename Value="unit1.pas"/>
+ <IsPartOfProject Value="True"/>
+ <ComponentName Value="Form1"/>
+ <HasResources Value="True"/>
+ <ResourceBaseClass Value="Form"/>
+ <UnitName Value="Unit1"/>
+ <TopLine Value="60"/>
+ <CursorPos Y="73"/>
+ <UsageCount Value="80"/>
+ <Loaded Value="True"/>
+ <LoadedDesigner Value="True"/>
+ </Unit>
+ <Unit>
+ <Filename Value="state.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="State"/>
+ <EditorIndex Value="-1"/>
+ <CursorPos X="8"/>
+ <UsageCount Value="76"/>
+ </Unit>
+ <Unit>
+ <Filename Value="assets/problems.pas"/>
+ <IsPartOfProject Value="True"/>
+ <UnitName Value="Problems"/>
+ <EditorIndex Value="-1"/>
+ <CursorPos X="14"/>
+ <UsageCount Value="51"/>
+ </Unit>
+ <Unit>
+ <Filename Value="assets/1.slv"/>
+ <EditorIndex Value="3"/>
+ <CursorPos X="8" Y="6"/>
+ <UsageCount Value="31"/>
+ <Loaded Value="True"/>
+ <DefaultSyntaxHighlighter Value="None"/>
+ </Unit>
+ <Unit>
+ <Filename Value="states.pas"/>
+ <UnitName Value="States"/>
+ <EditorIndex Value="4"/>
+ <TopLine Value="219"/>
+ <CursorPos X="13" Y="168"/>
+ <UsageCount Value="38"/>
+ <Loaded Value="True"/>
+ </Unit>
+ <Unit>
+ <Filename Value="unit1.lfm"/>
+ <EditorIndex Value="-1"/>
+ <UsageCount Value="5"/>
+ <DefaultSyntaxHighlighter Value="LFM"/>
+ </Unit>
+ <Unit>
+ <Filename Value="assets/2.slv"/>
+ <EditorIndex Value="-1"/>
+ <CursorPos X="14" Y="9"/>
+ <UsageCount Value="20"/>
+ <DefaultSyntaxHighlighter Value="None"/>
+ </Unit>
+ <Unit>
+ <Filename Value="assets/3.slv"/>
+ <EditorIndex Value="-1"/>
+ <UsageCount Value="21"/>
+ <DefaultSyntaxHighlighter Value="None"/>
+ </Unit>
+ <Unit>
+ <Filename Value="../../../../opt/lazarus/fpc/3.2.2/source/rtl/objpas/fgl.pp"/>
+ <EditorIndex Value="1"/>
+ <TopLine Value="891"/>
+ <CursorPos X="5" Y="905"/>
+ <UsageCount Value="20"/>
+ <Loaded Value="True"/>
+ </Unit>
+ <Unit>
+ <Filename Value="problems.pas"/>
+ <UnitName Value="Problems"/>
+ <EditorIndex Value="2"/>
+ <TopLine Value="118"/>
+ <CursorPos X="63" Y="129"/>
+ <UsageCount Value="25"/>
+ <Loaded Value="True"/>
+ </Unit>
+ <Unit>
+ <Filename Value="assets/4.slv"/>
+ <IsPartOfProject Value="True"/>
+ <IsVisibleTab Value="True"/>
+ <EditorIndex Value="5"/>
+ <CursorPos Y="3"/>
+ <UsageCount Value="36"/>
+ <Loaded Value="True"/>
+ <DefaultSyntaxHighlighter Value="None"/>
+ </Unit>
+ </Units>
+ <JumpHistory HistoryIndex="29">
+ <Position>
+ <Filename Value="problems.pas"/>
+ <Caret Line="138" TopLine="116"/>
+ </Position>
+ <Position>
+ <Filename Value="problems.pas"/>
+ <Caret Line="130" Column="30" TopLine="116"/>
+ </Position>
+ <Position>
+ <Filename Value="states.pas"/>
+ <Caret Line="163" Column="11" TopLine="156"/>
+ </Position>
+ <Position>
+ <Filename Value="states.pas"/>
+ <Caret Line="191" Column="14" TopLine="170"/>
+ </Position>
+ <Position>
+ <Filename Value="states.pas"/>
+ <Caret Line="196" Column="16" TopLine="175"/>
+ </Position>
+ <Position>
+ <Filename Value="states.pas"/>
+ <Caret Line="44" Column="18" TopLine="29"/>
+ </Position>
+ <Position>
+ <Filename Value="states.pas"/>
+ <Caret Line="48" Column="25" TopLine="29"/>
+ </Position>
+ <Position>
+ <Filename Value="states.pas"/>
+ <Caret Line="52" Column="11" TopLine="29"/>
+ </Position>
+ <Position>
+ <Filename Value="states.pas"/>
+ <Caret Line="70" Column="11" TopLine="46"/>
+ </Position>
+ <Position>
+ <Filename Value="states.pas"/>
+ <Caret Line="194" Column="7" TopLine="170"/>
+ </Position>
+ <Position>
+ <Filename Value="states.pas"/>
+ <Caret Line="210" Column="3" TopLine="184"/>
+ </Position>
+ <Position>
+ <Filename Value="states.pas"/>
+ <Caret Line="201" Column="25" TopLine="188"/>
+ </Position>
+ <Position>
+ <Filename Value="states.pas"/>
+ <Caret Line="37" Column="22" TopLine="23"/>
+ </Position>
+ <Position>
+ <Filename Value="problems.pas"/>
+ <Caret Line="114" Column="11" TopLine="107"/>
+ </Position>
+ <Position>
+ <Filename Value="assets/1.slv"/>
+ <Caret Line="3" Column="12"/>
+ </Position>
+ <Position>
+ <Filename Value="problems.pas"/>
+ <Caret Line="42" Column="17" TopLine="35"/>
+ </Position>
+ <Position>
+ <Filename Value="problems.pas"/>
+ <Caret Line="55" Column="24" TopLine="35"/>
+ </Position>
+ <Position>
+ <Filename Value="problems.pas"/>
+ <Caret Line="68" Column="17" TopLine="59"/>
+ </Position>
+ <Position>
+ <Filename Value="problems.pas"/>
+ <Caret Line="13" Column="17"/>
+ </Position>
+ <Position>
+ <Filename Value="problems.pas"/>
+ <Caret Line="53" Column="76" TopLine="40"/>
+ </Position>
+ <Position>
+ <Filename Value="problems.pas"/>
+ <Caret Line="14" Column="20"/>
+ </Position>
+ <Position>
+ <Filename Value="problems.pas"/>
+ <Caret Line="90" Column="42" TopLine="74"/>
+ </Position>
+ <Position>
+ <Filename Value="problems.pas"/>
+ <Caret Line="13" Column="17"/>
+ </Position>
+ <Position>
+ <Filename Value="problems.pas"/>
+ <Caret Line="53" Column="21" TopLine="30"/>
+ </Position>
+ <Position>
+ <Filename Value="problems.pas"/>
+ <Caret Line="13" Column="45"/>
+ </Position>
+ <Position>
+ <Filename Value="problems.pas"/>
+ <Caret Line="56" Column="35" TopLine="29"/>
+ </Position>
+ <Position>
+ <Filename Value="assets/1.slv"/>
+ <Caret Line="10" Column="17"/>
+ </Position>
+ <Position>
+ <Filename Value="problems.pas"/>
+ <Caret Line="120" TopLine="118"/>
+ </Position>
+ <Position>
+ <Filename Value="assets/1.slv"/>
+ <Caret Line="9" Column="9"/>
+ </Position>
+ <Position>
+ <Filename Value="assets/1.slv"/>
+ <Caret Column="9"/>
+ </Position>
+ </JumpHistory>
+ <RunParams>
+ <FormatVersion Value="2"/>
+ <Modes ActiveMode=""/>
+ </RunParams>
+ <HistoryLists>
+ <List Name="WorkingDirectory" Type="File" Count="1"/>
+ <List Name="LaunchingApplication" Type="File" Count="1">
+ <Item1 Value="C:\WINDOWS\system32\cmd.exe /C ${TargetCmdLine}"/>
+ </List>
+ <List Name="CommandLineParameters" Count="1"/>
+ </HistoryLists>
+ </ProjectSession>
+</CONFIG>
diff --git a/Занимательное программирование/7/4_solver/states.pas b/Занимательное программирование/7/4_solver/states.pas new file mode 100644 index 0000000..3dbcc00 --- /dev/null +++ b/Занимательное программирование/7/4_solver/states.pas @@ -0,0 +1,277 @@ +unit States;
+
+{$mode ObjFPC}{$H+}
+
+interface
+
+uses
+ Classes, SysUtils, fgl;
+
+type
+ TPosition = record
+ X, Y: Integer
+ end;
+ TPositions = array of TPosition;
+
+ TState = class
+ private
+ PAgent: TPosition;
+ PWalls, PPlaces, PBoulders: TPositions;
+
+ procedure CopyArrays(AState: TState);
+
+ public
+ constructor Create;
+ constructor Create(ASTate: TState; AAgent: TPosition);
+ constructor Create(ASTate: TState);
+
+ property Walls: TPositions read PWalls;
+ property Places: TPositions read PPlaces;
+ property Boulders: TPositions read PBoulders;
+ property Agent: TPosition read PAgent write PAgent;
+
+ procedure AddWall(AX, AY: Integer);
+ procedure AddPlace(AX, AY: Integer);
+ procedure AddBoulder(AX, AY: Integer);
+ procedure SetAgent(AX, AY: Integer);
+ function Compare(AState: TState): Boolean;
+ end;
+ TStates = array of TState;
+
+ TBoard = class
+ private
+ PWidth, PHeight: Integer;
+ PInitialState: TState;
+
+ public
+ property Width: Integer read PWidth;
+ property Height: Integer read PHeight;
+ property InitialState: TState read PInitialState;
+
+ constructor Create(AWidth, AHeight: Integer; AInitialState: TState);
+ destructor Destroy; override;
+ end;
+
+ INode = interface
+ function GetTotalCost: Integer;
+ end;
+
+ TNode = class(TInterfacedObject, INode)
+ private
+ PState: TState;
+ PNode: TNode;
+ PCost, Heuristic: Integer;
+ public
+ property State: TState read PState;
+ property Node: TNode read PNode;
+ property Cost: Integer read PCost;
+
+ constructor Create(AState: TState; ANode: TNode; ACost, AHeuristic: Integer);
+ destructor Destroy;
+
+ function GetTotalCost: Integer;
+ end;
+ TNodes = array of TNode;
+
+ TNodeList = specialize TFPGList<TNode>;
+ TPriorityQueue = class
+ private
+ Nodes: TNodeList;
+
+ public
+ constructor Create;
+
+ procedure Add(ANode: TNode);
+ function Poll: TNode;
+ function IsEmpty: Boolean;
+ function Count: Integer;
+ end;
+
+ operator = (P1, P2: TPosition) B: Boolean;
+
+ function IsFree(NewPosition: TPosition; Walls: TPositions): Boolean;
+
+implementation
+
+procedure TSTate.CopyArrays(AState: TState);
+begin
+ PWalls := Copy(AState.Walls, 0, Length(ASTate.Walls));
+ PPlaces := Copy(AState.Places, 0, Length(ASTate.Places))
+end;
+
+constructor TState.Create;
+begin
+end;
+
+constructor TState.Create(ASTate: TState; AAgent: TPosition);
+begin
+ PAgent := AAgent;
+ CopyArrays(AState);
+ PBoulders := TPositions.Create
+end;
+
+constructor TState.Create(ASTate: TState);
+begin
+ PAgent := AState.Agent;
+ CopyArrays(AState);
+ PBoulders := Copy(AState.Boulders, 0, Length(AState.Boulders))
+end;
+
+procedure TState.AddWall(AX, AY: Integer);
+var
+ CurrentLength: Integer;
+begin
+ CurrentLength := Length(PWalls);
+ SetLength(PWalls, CurrentLength + 1);
+
+ PWalls[CurrentLength].X := AX;
+ PWalls[CurrentLength].Y := AY
+end;
+
+procedure TState.AddPlace(AX, AY: Integer);
+var
+ CurrentLength: Integer;
+begin
+ CurrentLength := Length(PPlaces);
+ SetLength(PPlaces, CurrentLength + 1);
+
+ PPlaces[CurrentLength].X := AX;
+ PPlaces[CurrentLength].Y := AY
+end;
+
+procedure TState.AddBoulder(AX, AY: Integer);
+var
+ CurrentLength: Integer;
+begin
+ CurrentLength := Length(PBoulders);
+ SetLength(PBoulders, CurrentLength + 1);
+
+ PBoulders[CurrentLength].X := AX;
+ PBoulders[CurrentLength].Y := AY
+end;
+
+procedure TState.SetAgent(AX, AY: Integer);
+begin
+ PAgent.X := AX;
+ PAgent.Y := AY
+end;
+
+function TState.Compare(AState: TState): Boolean;
+var
+ i: Integer;
+begin
+ Result := (Agent = AState.Agent)
+ and (Length(Walls) = Length(AState.Walls))
+ and (Length(Boulders) = Length(AState.Boulders))
+ and (Length(Places) = Length(AState.Places));
+ if not Result then
+ Exit;
+ for i := 0 to High(Walls) do
+ if Walls[i] <> AState.Walls[i] then
+ begin
+ Result := false;
+ Exit
+ end;
+ for i := 0 to High(Boulders) do
+ if Boulders[i] <> AState.Boulders[i] then
+ begin
+ Result := false;
+ Exit
+ end;
+ for i := 0 to High(Places) do
+ if Places[i] <> AState.Places[i] then
+ begin
+ Result := false;
+ Exit
+ end
+end;
+
+constructor TBoard.Create(AWidth, AHeight: Integer; AInitialState: TState);
+begin
+ PWidth := AWidth;
+ PHeight := AHeight;
+ PInitialState := AInitialState
+end;
+
+destructor TBoard.Destroy;
+begin
+ PInitialState.Destroy;
+ inherited
+end;
+
+constructor TNode.Create(AState: TState; ANode: TNode; ACost, AHeuristic: Integer);
+begin
+ PState := AState;
+ PNode := ANode;
+ PCost := ACost;
+ Heuristic := AHeuristic
+end;
+
+destructor TNode.Destroy;
+begin
+ PState.Free;
+ inherited
+end;
+
+function TNode.GetTotalCost: Integer;
+begin
+ Result := Cost + Heuristic
+end;
+
+constructor TPriorityQueue.Create;
+begin
+ Nodes := TNodeList.Create
+end;
+
+procedure TPriorityQueue.Add(ANode: TNode);
+var
+ Index, TotalCost: Integer;
+ CurrentNode: TNode;
+begin
+ Index := 0;
+ TotalCost := ANode.GetTotalCost;
+
+ for CurrentNode in Nodes do
+ begin
+ if CurrentNode.getTotalCost >= TotalCost then
+ break;
+ Inc(Index)
+ end;
+ Nodes.Insert(Index, ANode)
+end;
+
+function TPriorityQueue.Poll: TNode;
+begin
+ Result := Nodes.Extract(Nodes.First)
+end;
+
+function TPriorityQueue.IsEmpty: Boolean;
+begin
+ Result := Count = 0
+end;
+
+function TPriorityQueue.Count: Integer;
+begin
+ Result := Nodes.Count
+end;
+
+operator = (P1, P2: TPosition) B: Boolean;
+begin
+ B := (P1.X = P2.X) and (P1.Y = P2.Y)
+end;
+
+function IsFree(NewPosition: TPosition; Walls: TPositions): Boolean;
+var
+ Wall: TPosition;
+begin
+ for Wall in Walls do
+ if NewPosition = Wall then
+ begin
+ Result := false;
+ Exit
+ end;
+ Result := true
+end;
+
+end.
+
diff --git a/Занимательное программирование/7/4_solver/unit1.lfm b/Занимательное программирование/7/4_solver/unit1.lfm new file mode 100644 index 0000000..2bc893a --- /dev/null +++ b/Занимательное программирование/7/4_solver/unit1.lfm @@ -0,0 +1,185 @@ +object Form1: TForm1 + Left = 284 + Height = 771 + Top = 158 + Width = 695 + Caption = 'Form1' + ClientHeight = 771 + ClientWidth = 695 + DesignTimePPI = 120 + LCLVersion = '4.6.0.0' + OnCreate = FormCreate + OnDestroy = FormDestroy + object OpenLevel: TButton + AnchorSideLeft.Control = Owner + AnchorSideLeft.Side = asrCenter + AnchorSideTop.Control = Owner + AnchorSideRight.Control = Owner + AnchorSideRight.Side = asrCenter + Left = 259 + Height = 40 + Top = 25 + Width = 176 + BorderSpacing.Top = 25 + Caption = 'Открыть уровень' + TabOrder = 0 + OnClick = OpenLevelClick + end + object BackBuffer: TImage + AnchorSideLeft.Control = Screen + AnchorSideTop.Control = Screen + AnchorSideRight.Control = Screen + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = Screen + AnchorSideBottom.Side = asrBottom + Left = 25 + Height = 384 + Top = 90 + Width = 640 + Anchors = [akTop, akLeft, akRight, akBottom] + Visible = False + end + object Screen: TImage + AnchorSideLeft.Control = Owner + AnchorSideTop.Control = OpenLevel + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = Owner + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = Owner + AnchorSideBottom.Side = asrBottom + Left = 25 + Height = 384 + Top = 90 + Width = 640 + BorderSpacing.Left = 25 + BorderSpacing.Top = 25 + end + object MoveList: TListBox + AnchorSideLeft.Control = Owner + AnchorSideTop.Control = Screen + AnchorSideTop.Side = asrBottom + AnchorSideRight.Control = Screen + AnchorSideRight.Side = asrBottom + AnchorSideBottom.Control = Owner + AnchorSideBottom.Side = asrBottom + Left = 25 + Height = 247 + Top = 499 + Width = 640 + Anchors = [akTop, akLeft, akRight, akBottom] + BorderSpacing.Left = 25 + BorderSpacing.Top = 25 + BorderSpacing.Bottom = 25 + ItemHeight = 0 + TabOrder = 1 + end + object OpenDialog: TOpenDialog + Left = 40 + Top = 8 + end + object ImageList: TImageList + Height = 32 + Width = 32 + Left = 552 + Top = 16 + Bitmap = { + 4C7A120000002000000020000000FD0B00000000000078DAED5D69AC5E4519BE + 585A0A29B2191590450A01B4D4564CD8A4501428A48845C2160C06221A8DA450 + 68841A7BCAF2035340220911454C6A950006642948C05E68590C150B365A4591 + 96BD589142955EB09F33F80C9D3B7766CECC39F3CE9CDEFB3EC99BF69EF9BE79 + E6CCFA3EB37D7D7D0C0683C16030180C0683C16030180C46D750F5EC968BB3E7 + 30AAB4F8387D69A1E176217D1AAA205E7F3A9AA6A19E3B24AC791A2A67FCAE32 + 777F2E1D7F5DBD4BC3DF8C3B5D1AC2F8EB9EA7E40F2F6BD7F7366FFED2F99FB7 + FE956E7FDDEA7FCAF4BFA5C79F2E8CBF5DF03FBAE07F75C1FF643018EFB7C0EA + FF6D4EFEEB320ACE4D710FFED70CDB149E96D78CDFF6DC96B6D2FC316918FA79 + 5F19D73F8B2D0BDF7B0DCE53775EB8F229A66E9B79D9678BBF26EF43CBC2FABE + 81E5EECB8B21EFD0E7E6AF4BBF2DFF7DE55DD747B8F2BBAE5C43CD5A86D636E9 + E70C7D3F57FEB8FEDF157E7778FBFCF7C563F62F7D55BB72AFAB9F43EB82D9DF + F9F330455EF8CA3F2ABEC0B4D9DAAFADFC43F22F24AF43FBA490F69FAADD87D6 + 7FBDDFADEBBF43CBDA9F67764D5315E00F2BAF747D43ECFBB7ED975C75BFCEEF + 886DD3E1F5B1DEF7F0F93F756D3374ECADD3D721EF1DDD7745EA0BDFFBFB7C9D + 363E709DD6F1F9A3A9F5500A7E4A0DE4F65BF2694F9E0B62FDCFFA3F9FFEF7E9 + 356AFD1F3E079D58FF379A836FAFFFDBAF41B4D3FF29D6609AEAFFB46B5071FA + 3FF51A5CACFE4FBD0619A3FFA9D66043F53FDD1A7498FEA75A830FD5FF747B00 + C2F43F55FEC7E87FAAFA17AAFFD3EF0188D3FFE9F700C4E9FFD4FD6F53FD9F66 + 0F4073FD9F62FC4DA3FFE3FD8F94FA3FD4FFCAA5FF437C50D6FFACFF198190FD + 570EDB8CE73CFAFBFB9DFAC915C6FCF1FD8BCD3FD1396CFEAB2D2C24EEC004F6 + A22CF1FBBF176768FB4AF8FEEF7F3EB28DC7F2C8B2334D7F1ECB6F8BC38CBB4B + FC75F9D286BF4DFE3BCBDFF4FB3CE5DF26FFAD6393CBF7748C3149F9EBF46F43 + FEE0B1D9A1877CFC6DCA7F107F9F5F0B0D4A0345FE7BB44769FE21F590A2FC1B + F253977F8AF74FC38FF084FC43FA3F6F1AAAC6FD5F70FFEFD4E0D520FE54FDBF + 75FC7169EF9CE3AF873BE5F83B68BF5584FFD5D4CFEA9AFFD7C80726F0FF53CE + 5D506AA4CD59DB763EFF4BD6BFD2FA8BB8FF89EF7FFDFE3F69FF1FE0FF93E9AF + 40FF9F4C7F05FAFF24FA6B18FBFFEC7F8F6CFF3F4E7FD7FBFF24E51FE1FF93CD + 7F04FAFF64FA2BD0FF27D35F99E7DF46FAFC67AEF9E7CEEBAFC2EB0FC5D75F78 + FDAD383FAF3F3372CE47E8FFBAC69398BF6DF16EF22D999FF90BF67B01FDBEB2 + 983D7441DFEB207FA5FB32A9FA7D976F047EEB19EBA0F124603DDAE31F2AFE21 + 3E721D7FE83ED040FEA8F7F7E870EB5ED8007E6BF93BB9ABC8FDD771FC5E9F3F + 622FAE756D3E097F83FDC0C9F3BF8A3B0B95BCFE451849FB6BC09DEAFD63E763 + 9AB6BF8CFD2F8F7FDDF13FD8FF1C41FCA5F51783B5FE48ABEB5A9B67FEB2FC5D + D2FCE4BE4697F989FCCDA0F2F76A8D487DEFD88B3368ED2384BFA9BE37F8AD6B + 6F75FC6DF47DDBF76FABEF2DFC51E5DF56DFFBB456307F0B7D9F8CBFA1BE6F9B + FF6DF57DB2F6D792BB4DFB6BA3EF9BBC7FE9FE97C7BF62FE07FB9FCCCF5ABF58 + 332C0EDBFEAC1093E51AF37D932FE4FBFAF8A9C613DBF72BE3734DD26FC6DFF4 + FD555A5C71E969EE0ABF397E85A423B6FCBBFAFD367718A7F87ED7DABEAB8D51 + 9591AFAE9975B4B2F9D32DBFEF6B7FB6F4BBF8F4EFBBDA78E8F773F25799F2BF + 8B7D44AE36DED5F6DFF5FE5BAF5B6DFD87D8F1BF6E0C6C3A7E878EFF39F9AB00 + 2ECABE9DC7FF6EB6FD2E8FFFA69F6B7B27D7F7FB1CE358E8F85B79BE5FD747A5 + 18FF53F2B76DA3A9FCAF9138FEF30C0883B159CEDB9D2FEC1D0C39FB65E4DD56 + D8ADC630BE5F26EE4F0A5B29EC5DBCFFDB99F96F17F69AB0A3F0776EFEB9C2F6 + D0FE7E3B77F91BE9617EE6677EE61FF6FC8263BCB065866D04FF0AE3F97802FE + 0911B27A027B480C0683C1600C2BDD3F49D80261CF0B1B10F686B0DF420F8F26 + E6FEB236E7F04F618F097B46F33B96081B4BE87F297FEF7BC2B6D2C28E12B61E + 611711F15F81F81F75845F8EF0A544FC5350C6473BC2BF04FEBF15AA97E752BE + 7F0DF716C21E01FFF905F86783FBCF54F5DFC37D1174C85A61FB67E41D25EC7A + BCF7CBC22666E4FEA0B0FB34EDB54746EEED843D09EE5FCBB464E4DE46ABE70B + A9FB7B0BFF4270CB79D05199B9A7817BB5B07105DAF8FDE05F63990730ED6304 + FCCB22F4FFDEEC2931180C068331ACF4FF11C27E099FFB1DE8FFC785CDA2D61E + 22FE399A8FF1127CD1BF6ACF7E27F70710711F069DF35F61E748CD6768E37548 + C33C22FE9F22FE1F39C2E721FC3122FEA9C2BEE65ADB11CFCFA6E40F48DFCDE0 + FF7E464EB91765B2B09F80FB2FC276CAC0BBA7E16FCB3670492E5D22787681EE + 7D0E7D806C17BF17764A81721F2DEC2C61AF222F2E2ED82F4AFC47D8F685D2F0 + 3AD2309560AE45CE2FDE28ECE39ECFA8F9D12309DE4DF5F3731CE1876BEDE1A3 + 04FC17206EF98EA719FDFF24B47D897B89CA7614C65D855731EFFEACB60FE059 + 0AED6FA4E314618BC02FDBFE9B1877BF9B732E8AC16030180C46D1F98029D0C4 + 122767E61E079FA75788FF06F0BE989B5F701D0FCE3B85DD96935FF0EC88F917 + B9E6BB73017EA5F54FC5DFD9F82527B87EA13DCBC28FBC5E8BFAB64301FE7BC0 + 33CD784ECE2FE2FE2A38AEB78491F24BCD0B9DF347B9065E807F56E4B1E69F25 + E63F01EFE8B217D4DC1FFE3E2F733F7C5B89FE9FF9BBC1CF6030180C06836C8C + 1F1BE87B7E8550FBF4B0E6E0DBFB389D887F7FF03F5728FF0F01FFF242FCC7A9 + EB850AF19F0EFE3B0AF17F03FC72EEE1DBB21C84FD1BE73EE4FF2F16B63521FF + 25463B93EB8D8BB106AF20F7856F47C4FF1D61AF609DFF1023EC686DFD7D41A1 + F23909FC722EF24305F84761ED5FE2984279F03CF86710C4FD01ECFB18ED08DF + 4A9B073E94807F35E23EC711AECEFFACD3CF6625E457E7AF5E37F75760EFC75A + 845F4A38FEF66B6D5D9E7BFB8DB03F69CFA40EDF92B88ECBBD0F0FA03CE4F9BF + 7FE01CCAA03D110C0683C1603086DD5CC00C610FE20CBE5C177B1A9A685C06EE + F9F0753662DFE1A39ADFBD845883A9B55FB9DE35497BBE0BD606256611716FA1 + ED819D6A093F02FBD04F27E23FA8F0FC833AE37E55217E75FE429E81F8845CFF + 871EDE20EF3C40BDDC91905F9D379F8F39875538072DE700DED234C18789F897 + 68FAFA4A5D67E03CC20A735F4462FE8710FF1FA416B5844F41F8BB14732022CE + BB10FF0F3DED53F5438711F05F8BB87FECF9CC2AAAF90711E799BEFB35A00D07 + F099C904FC3B68E70B265BC2BF88B037A9EE25D0C61E39DFB7ABF67CBCB60F6D + 3E611F3016ED5D9D33598C33702ADF97528E7F48C396B807E649F43BEBF1FF0B + 29E65D180C0683C16014D5FC4B4BDEFF23FDDE807B8FD4FAD3EE05F2E7544AFD + 13E01B4B2DFA2F8AB3AF01FCEA1EB69905B80F80E65B49B9F6E5E1BF17EF7E62 + 01EE83C1BDAC509F7027F8CF28C0BD27DAFB6B257487FC4923BCFB0F0AE5BD9A + 6F39B600F7AEE0DE5028EF555FFB54A1BCBF0CFC3F2FC4BF00FCD7166EF797B1 + 67C46030180CC6889807F89CDC836DDCFF27CF7DCE143686987B9EB6F6BF18F7 + 51DD8E34483C4175079188F733DAF983238DB09D70F7BAC49544FC7311FF7D8E + F06F22FC6962BDB3D011AEF6E7AF20E23F46FDBE80AD9E8967D720FC860C7EAF + AC7313B11E27F77E9C8735B89729E79E70D7DD6CCCF398B8917ADE0BBF73F10C + DADFC3D86F7217F644CB75C0ABA9EE65C71D6B03E0F9ACA5FDF553DEFFA8DDBD + 5639C2F741F800C51D90DA398F633D9F51FDE0E104FC6B10F7173C9F51F5F2F3 + 04FC6AFFC91535F92FB11B01BFBADF74BD79BE01BFC9B008E10F10D53FB9BFE4 + 266DFC53EDEF0ECC81F55047A8FB00B9F7EF6EEDFEBF7558FF9F4B75F688C160 + 30180C4671FD2FD71C6FC1FD6F03F0036E15F6E90CDC5FD7D6F857E3FCE32A4D + 97CF20E49EA0FDF6E04CE3FEE70BB4BDA71F21E2BFAE46FF2F223E7FFA84DAFF + EE089F4EB926A7FDCEE37447F8BE5A3D1843C0BFACE6FDF7D2F4C75E04FC6A8F + C53D8EF06F69FC9F22E03F50BB67FC5C236C1AB4E706841F483C07D6C3799B5F + E13C400F6BB2EA0CF6BE84FDC0C9D80BF516DEF921DC3D3006FB507A25EEFFC7 + 7CD47BBF8B52685C98ED9B1F4C10FFA1D80336C712B6B5B0BF83FF78C2FCDD88 + 3A7E9CF67C1B8C87120F13E7F1D55AFD5FAE9DC153BF01B27386723E0D757E0D + CE20ACC4DD08DBF631180C4604FE071237534E + } + end +end diff --git a/Занимательное программирование/7/4_solver/unit1.pas b/Занимательное программирование/7/4_solver/unit1.pas new file mode 100644 index 0000000..af58196 --- /dev/null +++ b/Занимательное программирование/7/4_solver/unit1.pas @@ -0,0 +1,177 @@ +unit Unit1; + +{$mode objfpc}{$H+} + +interface + +uses + Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls, ExtCtrls, + States, Problems; + +const + CELL_SIZE = 32; + +type + + { TForm1 } + + TForm1 = class(TForm) + BackBuffer: TImage; + ImageList: TImageList; + MoveList: TListBox; + Screen: TImage; + OpenLevel: TButton; + OpenDialog: TOpenDialog; + procedure FormCreate(Sender: TObject); + procedure FormDestroy(Sender: TObject); + procedure OpenLevelClick(Sender: TObject); + private + + public + + end; + +var + Form1: TForm1; + Board: TBoard; { Игровое поле. } + +implementation + +{$R *.lfm} + +{ Загрузить уровень из файла. } +procedure LoadLevel(FileName: String); +var + f: File of Char; + i, j, ColumnCount, CurrentColumn: Integer; + c: Char; + InitialState: TState; +begin + AssignFile(f, FileName); { Открыть файл. } + Reset(f); + + FreeAndNil(Board); + InitialState := TState.Create; + + { Читаем первую строку, чтобы найти количество столбцов. } + ColumnCount := 0; + repeat + Read(f, c); + if c = 'x' then + InitialState.AddWall(ColumnCount, 0); + Inc(ColumnCount) + until c <> 'x'; + Dec(ColumnCount); + Read(f, c); { Считываем и пропускаем перевод строки. } + j := 1; + + repeat { Цикл по строкам. } + CurrentColumn := 0; + + for i := 0 to ColumnCount - 1 do { Цикл по элементам строки. } + begin + Read(f, c); { Считаем текущий эелемент } + + case c of + 'x': + begin + InitialState.AddWall(i, j); + Inc(CurrentColumn) + end; + 's': + InitialState.SetAgent(i, j); + 'p': { Если это "место". } + InitialState.AddPlace(i, j); + 'b': { Если это "камень". } + InitialState.AddBoulder(i, j) + end + end; + Inc(j); + Read(f, c); { считываем и пропускаем возврат каретки } + Read(f, c) { Считываем и пропускаем перевод строки. } + until CurrentColumn = ColumnCount; + + Board := TBoard.Create(ColumnCount, j, InitialState); + + CloseFile(f) { закрыть файл } +end; + +procedure DrawCell(APosition: TPosition; Bitmap: TBitmap); +begin + Form1.BackBuffer.Canvas.Draw(APosition.X * CELL_SIZE, + APosition.Y * CELL_SIZE, + Bitmap) +end; + +procedure RedrawField; +var + Bitmap: TBitmap; + ScreenRect: TRect; + CurrentPosition: TPosition; +begin + Bitmap := TBitmap.Create; { Объект для временного хранения рисунка. } + + Form1.BackBuffer.Canvas.Brush.Color := clBlack; + Form1.BackBuffer.Canvas.Clear; + + { Рисуем стены. } + Form1.ImageList.GetBitmap(8, bitmap); + for CurrentPosition in Board.InitialState.Walls do + DrawCell(CurrentPosition, bitmap); + { Рисуем агента. } + Form1.ImageList.GetBitmap(4, bitmap); + DrawCell(Board.InitialState.Agent, Bitmap); + + { Рисуем камни. } + Form1.ImageList.GetBitmap(2, bitmap); + for CurrentPosition in Board.InitialState.Boulders do + DrawCell(CurrentPosition, bitmap); + + { Рисуем объекты. } + Form1.ImageList.GetBitmap(1, bitmap); + For CurrentPosition in Board.InitialState.Places do + DrawCell(CurrentPosition, bitmap); + + Bitmap.Free; + + { Копируем содержимое виртуального экрана на основной. } + ScreenRect := Rect(0, 0, Screen.Width, Screen.Height); + Form1.Screen.Canvas.CopyRect(ScreenRect, Form1.BackBuffer.Canvas, ScreenRect) +end; + +{ TForm1 } + +procedure TForm1.OpenLevelClick(Sender: TObject); +var + i: Integer; + Solution: TStates; + SolutionLine: String; +begin + if OpenDialog.Execute then + begin + LoadLevel(OpenDialog.FileName); + RedrawField; + + Solution := Search(Board.InitialState); + for i := 0 to High(Solution) do + begin + SolutionLine := 'Agent: (' + IntToStr(Solution[i].Agent.X) + + ', ' + IntToStr(Solution[i].Agent.Y) + ')'; + FreeAndNil(Solution[i]); + MoveList.AddItem(SolutionLine, nil) + end + end; +end; + +procedure TForm1.FormCreate(Sender: TObject); +begin + Board := nil +end; + +procedure TForm1.FormDestroy(Sender: TObject); +begin + FreeAndNil(Board) +end; + +end. + diff --git a/Занимательное программирование/README.txt b/Занимательное программирование/README.txt index 09b4801..b30f0ee 100644 --- a/Занимательное программирование/README.txt +++ b/Занимательное программирование/README.txt @@ -20,5 +20,5 @@ Lazarus. только теоретические задания. Седьмая глава "Простые компьютерные игры" применяет Lazarus. Изображения -для частично сделаны заново, частично скопированы из приложения книги -и отредактированы. +частично сделаны заново, частично скопированы из приложения книги и +отредактированы. |
