Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
ColdShot
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
15박보승
ColdShot
Commits
fb1a4cdd
Commit
fb1a4cdd
authored
Jan 22, 2020
by
15박보승
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implementing A* with heap
parent
b653e8df
Changes
4
Show whitespace changes
Inline
Side-by-side
Showing
4 changed files
with
132 additions
and
87 deletions
+132
-87
Pathfinding.unity
Assets/Scenes/Pathfinding.unity
+19
-9
PriorityQueue.cs
Assets/Scripts/Generals/PriorityQueue.cs
+80
-58
NodalPathfinding2D.cs
Assets/Scripts/NodalPathfinding/NodalPathfinding2D.cs
+24
-18
NodalPathfinding2DAgent.cs
Assets/Scripts/NodalPathfinding/NodalPathfinding2DAgent.cs
+9
-2
No files found.
Assets/Scenes/Pathfinding.unity
View file @
fb1a4cdd
...
@@ -406,7 +406,7 @@ GameObject:
...
@@ -406,7 +406,7 @@ GameObject:
m_Icon
:
{
fileID
:
0
}
m_Icon
:
{
fileID
:
0
}
m_NavMeshLayer
:
0
m_NavMeshLayer
:
0
m_StaticEditorFlags
:
0
m_StaticEditorFlags
:
0
m_IsActive
:
1
m_IsActive
:
0
---
!u!114
&620200134
---
!u!114
&620200134
MonoBehaviour
:
MonoBehaviour
:
m_ObjectHideFlags
:
0
m_ObjectHideFlags
:
0
...
@@ -422,6 +422,7 @@ MonoBehaviour:
...
@@ -422,6 +422,7 @@ MonoBehaviour:
pathFinder
:
{
fileID
:
178212234
}
pathFinder
:
{
fileID
:
178212234
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
path
:
[]
path
:
[]
moveSpeed
:
0
isGizmos
:
1
isGizmos
:
1
---
!u!61
&620200135
---
!u!61
&620200135
BoxCollider2D
:
BoxCollider2D
:
...
@@ -531,7 +532,7 @@ GameObject:
...
@@ -531,7 +532,7 @@ GameObject:
m_Icon
:
{
fileID
:
0
}
m_Icon
:
{
fileID
:
0
}
m_NavMeshLayer
:
0
m_NavMeshLayer
:
0
m_StaticEditorFlags
:
0
m_StaticEditorFlags
:
0
m_IsActive
:
1
m_IsActive
:
0
---
!u!114
&852157121
---
!u!114
&852157121
MonoBehaviour
:
MonoBehaviour
:
m_ObjectHideFlags
:
0
m_ObjectHideFlags
:
0
...
@@ -547,6 +548,7 @@ MonoBehaviour:
...
@@ -547,6 +548,7 @@ MonoBehaviour:
pathFinder
:
{
fileID
:
178212234
}
pathFinder
:
{
fileID
:
178212234
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
path
:
[]
path
:
[]
moveSpeed
:
0
isGizmos
:
1
isGizmos
:
1
---
!u!61
&852157122
---
!u!61
&852157122
BoxCollider2D
:
BoxCollider2D
:
...
@@ -872,7 +874,7 @@ GameObject:
...
@@ -872,7 +874,7 @@ GameObject:
m_Icon
:
{
fileID
:
0
}
m_Icon
:
{
fileID
:
0
}
m_NavMeshLayer
:
0
m_NavMeshLayer
:
0
m_StaticEditorFlags
:
0
m_StaticEditorFlags
:
0
m_IsActive
:
1
m_IsActive
:
0
---
!u!114
&1119483105
---
!u!114
&1119483105
MonoBehaviour
:
MonoBehaviour
:
m_ObjectHideFlags
:
0
m_ObjectHideFlags
:
0
...
@@ -888,6 +890,7 @@ MonoBehaviour:
...
@@ -888,6 +890,7 @@ MonoBehaviour:
pathFinder
:
{
fileID
:
178212234
}
pathFinder
:
{
fileID
:
178212234
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
path
:
[]
path
:
[]
moveSpeed
:
0
isGizmos
:
1
isGizmos
:
1
---
!u!61
&1119483106
---
!u!61
&1119483106
BoxCollider2D
:
BoxCollider2D
:
...
@@ -997,7 +1000,7 @@ GameObject:
...
@@ -997,7 +1000,7 @@ GameObject:
m_Icon
:
{
fileID
:
0
}
m_Icon
:
{
fileID
:
0
}
m_NavMeshLayer
:
0
m_NavMeshLayer
:
0
m_StaticEditorFlags
:
0
m_StaticEditorFlags
:
0
m_IsActive
:
1
m_IsActive
:
0
---
!u!114
&1304602415
---
!u!114
&1304602415
MonoBehaviour
:
MonoBehaviour
:
m_ObjectHideFlags
:
0
m_ObjectHideFlags
:
0
...
@@ -1013,6 +1016,7 @@ MonoBehaviour:
...
@@ -1013,6 +1016,7 @@ MonoBehaviour:
pathFinder
:
{
fileID
:
178212234
}
pathFinder
:
{
fileID
:
178212234
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
path
:
[]
path
:
[]
moveSpeed
:
0
isGizmos
:
1
isGizmos
:
1
---
!u!61
&1304602416
---
!u!61
&1304602416
BoxCollider2D
:
BoxCollider2D
:
...
@@ -1230,7 +1234,7 @@ GameObject:
...
@@ -1230,7 +1234,7 @@ GameObject:
m_Icon
:
{
fileID
:
0
}
m_Icon
:
{
fileID
:
0
}
m_NavMeshLayer
:
0
m_NavMeshLayer
:
0
m_StaticEditorFlags
:
0
m_StaticEditorFlags
:
0
m_IsActive
:
1
m_IsActive
:
0
---
!u!114
&1353820626
---
!u!114
&1353820626
MonoBehaviour
:
MonoBehaviour
:
m_ObjectHideFlags
:
0
m_ObjectHideFlags
:
0
...
@@ -1246,6 +1250,7 @@ MonoBehaviour:
...
@@ -1246,6 +1250,7 @@ MonoBehaviour:
pathFinder
:
{
fileID
:
178212234
}
pathFinder
:
{
fileID
:
178212234
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
path
:
[]
path
:
[]
moveSpeed
:
0
isGizmos
:
1
isGizmos
:
1
---
!u!61
&1353820627
---
!u!61
&1353820627
BoxCollider2D
:
BoxCollider2D
:
...
@@ -1355,7 +1360,7 @@ GameObject:
...
@@ -1355,7 +1360,7 @@ GameObject:
m_Icon
:
{
fileID
:
0
}
m_Icon
:
{
fileID
:
0
}
m_NavMeshLayer
:
0
m_NavMeshLayer
:
0
m_StaticEditorFlags
:
0
m_StaticEditorFlags
:
0
m_IsActive
:
1
m_IsActive
:
0
---
!u!114
&1546514103
---
!u!114
&1546514103
MonoBehaviour
:
MonoBehaviour
:
m_ObjectHideFlags
:
0
m_ObjectHideFlags
:
0
...
@@ -1371,6 +1376,7 @@ MonoBehaviour:
...
@@ -1371,6 +1376,7 @@ MonoBehaviour:
pathFinder
:
{
fileID
:
178212234
}
pathFinder
:
{
fileID
:
178212234
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
path
:
[]
path
:
[]
moveSpeed
:
0
isGizmos
:
1
isGizmos
:
1
---
!u!61
&1546514104
---
!u!61
&1546514104
BoxCollider2D
:
BoxCollider2D
:
...
@@ -1496,6 +1502,7 @@ MonoBehaviour:
...
@@ -1496,6 +1502,7 @@ MonoBehaviour:
pathFinder
:
{
fileID
:
178212234
}
pathFinder
:
{
fileID
:
178212234
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
path
:
[]
path
:
[]
moveSpeed
:
0
isGizmos
:
1
isGizmos
:
1
---
!u!61
&1592952602
---
!u!61
&1592952602
BoxCollider2D
:
BoxCollider2D
:
...
@@ -1688,7 +1695,7 @@ GameObject:
...
@@ -1688,7 +1695,7 @@ GameObject:
m_Icon
:
{
fileID
:
0
}
m_Icon
:
{
fileID
:
0
}
m_NavMeshLayer
:
0
m_NavMeshLayer
:
0
m_StaticEditorFlags
:
0
m_StaticEditorFlags
:
0
m_IsActive
:
1
m_IsActive
:
0
---
!u!114
&1651584784
---
!u!114
&1651584784
MonoBehaviour
:
MonoBehaviour
:
m_ObjectHideFlags
:
0
m_ObjectHideFlags
:
0
...
@@ -1704,6 +1711,7 @@ MonoBehaviour:
...
@@ -1704,6 +1711,7 @@ MonoBehaviour:
pathFinder
:
{
fileID
:
178212234
}
pathFinder
:
{
fileID
:
178212234
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
path
:
[]
path
:
[]
moveSpeed
:
0
isGizmos
:
1
isGizmos
:
1
---
!u!61
&1651584785
---
!u!61
&1651584785
BoxCollider2D
:
BoxCollider2D
:
...
@@ -1813,7 +1821,7 @@ GameObject:
...
@@ -1813,7 +1821,7 @@ GameObject:
m_Icon
:
{
fileID
:
0
}
m_Icon
:
{
fileID
:
0
}
m_NavMeshLayer
:
0
m_NavMeshLayer
:
0
m_StaticEditorFlags
:
0
m_StaticEditorFlags
:
0
m_IsActive
:
1
m_IsActive
:
0
---
!u!114
&1669406768
---
!u!114
&1669406768
MonoBehaviour
:
MonoBehaviour
:
m_ObjectHideFlags
:
0
m_ObjectHideFlags
:
0
...
@@ -1829,6 +1837,7 @@ MonoBehaviour:
...
@@ -1829,6 +1837,7 @@ MonoBehaviour:
pathFinder
:
{
fileID
:
178212234
}
pathFinder
:
{
fileID
:
178212234
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
path
:
[]
path
:
[]
moveSpeed
:
0
isGizmos
:
1
isGizmos
:
1
---
!u!61
&1669406769
---
!u!61
&1669406769
BoxCollider2D
:
BoxCollider2D
:
...
@@ -1938,7 +1947,7 @@ GameObject:
...
@@ -1938,7 +1947,7 @@ GameObject:
m_Icon
:
{
fileID
:
0
}
m_Icon
:
{
fileID
:
0
}
m_NavMeshLayer
:
0
m_NavMeshLayer
:
0
m_StaticEditorFlags
:
0
m_StaticEditorFlags
:
0
m_IsActive
:
1
m_IsActive
:
0
---
!u!114
&2003659734
---
!u!114
&2003659734
MonoBehaviour
:
MonoBehaviour
:
m_ObjectHideFlags
:
0
m_ObjectHideFlags
:
0
...
@@ -1954,6 +1963,7 @@ MonoBehaviour:
...
@@ -1954,6 +1963,7 @@ MonoBehaviour:
pathFinder
:
{
fileID
:
178212234
}
pathFinder
:
{
fileID
:
178212234
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
destination
:
{
x
:
0
,
y
:
0
,
z
:
0
}
path
:
[]
path
:
[]
moveSpeed
:
0
isGizmos
:
1
isGizmos
:
1
---
!u!61
&2003659735
---
!u!61
&2003659735
BoxCollider2D
:
BoxCollider2D
:
...
...
Assets/Scripts/Generals/PriorityQueue.cs
View file @
fb1a4cdd
using
System.Collections
;
using
System.Collections
;
using
System.Collections.Generic
;
using
System.Collections.Generic
;
public
enum
SortMode
{
ASCENDING
,
DECENDING
}
public
class
PriorityQueue
<
T
>
public
class
PriorityQueue
<
T
>
{
{
private
List
<
T
>
heap
=
new
List
<
T
>()
;
private
Heap
<
T
>
heap
;
private
IComparer
<
T
>
comparer
;
private
SortMode
sortMode
;
public
int
Count
{
get
{
return
heap
.
Count
;
}
}
public
PriorityQueue
(
IComparer
<
T
>
comparer
,
SortMode
sortMode
=
SortMode
.
ASCENDING
)
public
PriorityQueue
(
System
.
Comparison
<
T
>
comparison
)
{
{
this
.
comparer
=
comparer
;
heap
=
new
Heap
<
T
>(
comparison
);
this
.
sortMode
=
sortMode
;
}
}
public
void
Enqueue
(
T
item
)
public
void
Enqueue
(
T
item
)
{
{
InsertHeap
(
item
);
heap
.
Insert
(
item
);
}
}
public
T
Dequeue
()
public
T
Dequeue
()
{
{
T
first
=
heap
[
0
];
return
heap
.
Pop
();
heap
[
0
]
=
heap
[
heap
.
Count
-
1
];
heap
.
RemoveAt
(
heap
.
Count
-
1
);
Heapify
();
return
first
;
}
}
private
void
InsertHeap
(
T
item
)
public
bool
Contains
(
T
item
)
{
{
heap
.
Add
(
item
);
return
heap
.
Contains
(
item
);
int
index
=
heap
.
Count
-
1
;
}
while
(
sortMode
==
SortMode
.
ASCENDING
?
comparer
.
Compare
(
heap
[
index
],
heap
[
index
/
2
])
<=
0
:
comparer
.
Compare
(
heap
[
index
],
heap
[
index
/
2
])
>
0
)
}
class
Heap
<
T
>
{
private
List
<
T
>
list
=
new
List
<
T
>();
private
System
.
Comparison
<
T
>
comparison
;
public
int
Count
{
get
{
return
list
.
Count
;
}
}
public
Heap
(
System
.
Comparison
<
T
>
comparison
)
{
{
T
tmp
=
heap
[
index
];
this
.
comparison
=
comparison
;
heap
[
index
]
=
heap
[
index
/
2
];
}
heap
[
index
/
2
]
=
tmp
;
public
bool
Contains
(
T
item
)
{
return
list
.
Contains
(
item
);
}
public
void
Insert
(
T
item
)
{
list
.
Add
(
item
);
int
index
=
list
.
Count
-
1
;
while
(
comparison
.
Invoke
(
list
[
index
],
list
[
index
/
2
])
>
0
)
{
T
tmp
=
list
[
index
];
list
[
index
]
=
list
[
index
/
2
];
list
[
index
/
2
]
=
tmp
;
index
/=
2
;
index
/=
2
;
}
}
}
}
public
T
Pop
()
{
T
tmp
=
list
[
0
];
list
[
0
]
=
list
[
list
.
Count
-
1
];
list
.
RemoveAt
(
list
.
Count
-
1
);
Heapify
();
return
tmp
;
}
private
void
Heapify
()
private
void
Heapify
()
{
{
int
index
=
0
;
int
index
=
0
;
...
@@ -54,19 +76,19 @@ public class PriorityQueue<T>
...
@@ -54,19 +76,19 @@ public class PriorityQueue<T>
int
right
=
2
*
index
+
2
;
int
right
=
2
*
index
+
2
;
int
target
=
index
;
int
target
=
index
;
if
(
left
<
heap
.
Count
&&
(
sortMode
==
SortMode
.
ASCENDING
?
comparer
.
Compare
(
heap
[
index
],
heap
[
left
])
<=
0
:
comparer
.
Compare
(
heap
[
index
],
heap
[
left
])
>
0
)
)
if
(
left
<
list
.
Count
&&
comparison
.
Invoke
(
list
[
left
],
list
[
index
])
>
0
)
{
{
target
=
left
;
target
=
left
;
}
}
if
(
right
<
heap
.
Count
&&
(
sortMode
==
SortMode
.
ASCENDING
?
comparer
.
Compare
(
heap
[
index
],
heap
[
right
])
<=
0
:
comparer
.
Compare
(
heap
[
index
],
heap
[
right
])
>
0
)
)
if
(
right
<
list
.
Count
&&
comparison
.
Invoke
(
list
[
right
],
list
[
index
])
>
0
)
{
{
target
=
right
;
target
=
right
;
}
}
if
(
index
!=
target
)
if
(
index
!=
target
)
{
{
T
tmp
=
heap
[
index
];
T
tmp
=
list
[
index
];
heap
[
index
]
=
heap
[
target
];
list
[
index
]
=
list
[
target
];
heap
[
target
]
=
tmp
;
list
[
target
]
=
tmp
;
index
=
target
;
index
=
target
;
}
}
else
else
...
...
Assets/Scripts/NodalPathfinding/NodalPathfinding2D.cs
View file @
fb1a4cdd
...
@@ -195,14 +195,13 @@ namespace BS
...
@@ -195,14 +195,13 @@ namespace BS
Node
goal
=
nodes
[
WorldToIndex
(
to
,
from
-
to
)];
Node
goal
=
nodes
[
WorldToIndex
(
to
,
from
-
to
)];
//path.Add(cur.worldPositon);
//path.Add(cur.worldPositon);
List
<
Node
>
queue
=
new
List
<
Node
>(
);
PriorityQueue
<
Node
>
queue
=
new
PriorityQueue
<
Node
>((
a
,
b
)
=>
a
.
score
<
b
.
score
?
1
:
a
.
score
==
b
.
score
?
0
:
-
1
);
List
<
Node
>
closed
=
new
List
<
Node
>();
List
<
Node
>
closed
=
new
List
<
Node
>();
queue
.
Add
(
new
Node
(
start
,
null
)
);
queue
.
Enqueue
(
start
);
while
(
queue
.
Count
>
0
)
while
(
queue
.
Count
>
0
)
{
{
Node
cur
=
queue
[
0
];
Node
cur
=
queue
.
Dequeue
();
queue
.
RemoveAt
(
0
);
if
(
cur
.
gridPosition
==
goal
.
gridPosition
)
if
(
cur
.
gridPosition
==
goal
.
gridPosition
)
{
{
...
@@ -213,27 +212,27 @@ namespace BS
...
@@ -213,27 +212,27 @@ namespace BS
foreach
(
var
adj
in
cur
.
adjacencies
)
foreach
(
var
adj
in
cur
.
adjacencies
)
{
{
Node
adjNode
=
nodes
[
adj
];
Node
adjNode
=
nodes
[
adj
];
Node
alreadyOpened
=
queue
.
Find
(
n
=>
n
.
gridPosition
==
adj
);
bool
isOpened
=
queue
.
Contains
(
nodes
[
adj
]);
Node
alreadyClosed
=
closed
.
Find
(
n
=>
n
.
gridPosition
==
adj
);
bool
isClosed
=
closed
.
Contains
(
nodes
[
adj
]);
if
(
alreadyOpened
!=
null
)
if
(
isOpened
)
{
{
if
(
a
lreadyOpened
.
cost
>
cur
.
cost
+
Vector2
.
Distance
(
cur
.
worldPositon
,
adjNode
.
worldPositon
))
if
(
a
djNode
.
cost
>
cur
.
cost
+
Vector2
.
Distance
(
cur
.
worldPositon
,
adjNode
.
worldPositon
))
{
{
a
lreadyOpened
.
cost
=
cur
.
cost
+
Vector2
.
Distance
(
cur
.
worldPositon
,
adjNode
.
worldPositon
);
a
djNode
.
cost
=
cur
.
cost
+
Vector2
.
Distance
(
cur
.
worldPositon
,
adjNode
.
worldPositon
);
a
lreadyOpened
.
CalculateScore
(
to
);
a
djNode
.
CalculateScore
(
to
);
a
lreadyOpened
.
parent
=
cur
;
a
djNode
.
parent
=
cur
;
}
}
}
}
else
if
(
alreadyClosed
==
null
)
else
if
(
!
isClosed
)
{
{
Node
node
=
new
Node
(
adjNode
,
cur
,
cur
.
cost
+
Vector2
.
Distance
(
cur
.
worldPositon
,
adjNode
.
worldPositon
));
adjNode
.
parent
=
cur
;
node
.
CalculateScore
(
to
);
adjNode
.
cost
=
cur
.
cost
+
Vector2
.
Distance
(
cur
.
worldPositon
,
adjNode
.
worldPositon
);
queue
.
Add
(
node
);
adjNode
.
CalculateScore
(
to
);
queue
.
Enqueue
(
adjNode
);
}
}
}
}
queue
.
Sort
((
a
,
b
)
=>
a
.
score
>=
b
.
score
?
1
:
-
1
);
closed
.
Add
(
cur
);
closed
.
Add
(
cur
);
}
}
...
@@ -251,6 +250,13 @@ namespace BS
...
@@ -251,6 +250,13 @@ namespace BS
}
}
}
}
path
.
Reverse
();
path
.
Reverse
();
foreach
(
var
node
in
nodes
.
Values
)
{
node
.
cost
=
0
;
node
.
parent
=
null
;
node
.
score
=
0
;
}
return
path
;
return
path
;
}
}
...
...
Assets/Scripts/NodalPathfinding/NodalPathfinding2DAgent.cs
View file @
fb1a4cdd
...
@@ -9,6 +9,7 @@ namespace BS {
...
@@ -9,6 +9,7 @@ namespace BS {
public
Vector3
destination
;
public
Vector3
destination
;
public
List
<
Vector3
>
path
=
new
List
<
Vector3
>();
public
List
<
Vector3
>
path
=
new
List
<
Vector3
>();
public
float
moveSpeed
;
float
t
=
0.5f
;
float
t
=
0.5f
;
...
@@ -33,13 +34,14 @@ namespace BS {
...
@@ -33,13 +34,14 @@ namespace BS {
private
void
Update
()
private
void
Update
()
{
{
/*
t -= Time.deltaTime;
t -= Time.deltaTime;
if (t < 0)
if (t < 0)
{
{
MoveTo(new Vector2(Random.Range(pathFinder.bounds.min.x, pathFinder.bounds.max.x), Random.Range(pathFinder.bounds.min.y, pathFinder.bounds.max.y)));
MoveTo(new Vector2(Random.Range(pathFinder.bounds.min.x, pathFinder.bounds.max.x), Random.Range(pathFinder.bounds.min.y, pathFinder.bounds.max.y)));
t = 0.5f;
t = 0.5f;
}
}
*/
if
(
Input
.
GetKeyDown
(
KeyCode
.
Space
))
if
(
Input
.
GetKeyDown
(
KeyCode
.
Space
))
...
@@ -58,6 +60,11 @@ namespace BS {
...
@@ -58,6 +60,11 @@ namespace BS {
this
.
destination
=
destination
;
this
.
destination
=
destination
;
//path = pathFinder.GetPathGreedy(transform.position, destination);
//path = pathFinder.GetPathGreedy(transform.position, destination);
path
=
pathFinder
.
GetPathAstar
(
transform
.
position
,
destination
);
path
=
pathFinder
.
GetPathAstar
(
transform
.
position
,
destination
);
}
public
void
Move
(
Vector2
direction
)
{
}
}
}
}
}
}
\ No newline at end of file
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment