TrinityCore
Loading...
Searching...
No Matches
Containers.h
Go to the documentation of this file.
1/*
2 * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License as published by the
6 * Free Software Foundation; either version 2 of the License, or (at your
7 * option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful, but WITHOUT
10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
12 * more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17
18#ifndef TRINITY_CONTAINERS_H
19#define TRINITY_CONTAINERS_H
20
21#include "Define.h"
22#include "MapUtils.h"
23#include "Random.h"
24#include <algorithm>
25#include <iterator>
26#include <stdexcept>
27#include <type_traits>
28#include <utility>
29#include <vector>
30
31namespace Trinity
32{
33 template <class T>
35 {
36 public:
37 using iterator_category = std::output_iterator_tag;
38 using value_type = void;
39 using pointer = T*;
40 using reference = T&;
41 using difference_type = std::ptrdiff_t;
42
43 CheckedBufferOutputIterator(T* buf, size_t n) : _buf(buf), _end(buf+n) {}
44
45 T& operator*() const { check(); return *_buf; }
48
49 size_t remaining() const { return (_end - _buf); }
50
51 private:
52 T* _buf;
53 T* _end;
54 void check() const
55 {
56 if (!(_buf < _end))
57 throw std::out_of_range("index");
58 }
59 };
60
61 namespace Containers
62 {
63 // resizes <container> to have at most <requestedSize> elements
64 // if it has more than <requestedSize> elements, the elements to keep are selected randomly
65 template<class C>
66 void RandomResize(C& container, std::size_t requestedSize)
67 {
68 static_assert(std::is_base_of<std::forward_iterator_tag, typename std::iterator_traits<typename C::iterator>::iterator_category>::value, "Invalid container passed to Trinity::Containers::RandomResize");
69 if (std::size(container) <= requestedSize)
70 return;
71 auto keepIt = std::begin(container), curIt = std::begin(container);
72 uint32 elementsToKeep = requestedSize, elementsToProcess = std::size(container);
73 while (elementsToProcess)
74 {
75 // this element has chance (elementsToKeep / elementsToProcess) of being kept
76 if (urand(1, elementsToProcess) <= elementsToKeep)
77 {
78 if (keepIt != curIt)
79 *keepIt = std::move(*curIt);
80 ++keepIt;
81 --elementsToKeep;
82 }
83 ++curIt;
84 --elementsToProcess;
85 }
86 container.erase(keepIt, std::end(container));
87 }
88
89 template<class C, class Predicate>
90 void RandomResize(C& container, Predicate&& predicate, std::size_t requestedSize)
91 {
93 C containerCopy;
94 std::copy_if(std::begin(container), std::end(container), std::inserter(containerCopy, std::end(containerCopy)), predicate);
95
96 if (requestedSize)
97 RandomResize(containerCopy, requestedSize);
98
99 container = std::move(containerCopy);
100 }
101
102 /*
103 * Select a random element from a container.
104 *
105 * Note: container cannot be empty
106 */
107 template<class C>
108 inline auto SelectRandomContainerElement(C const& container) -> typename std::add_const<decltype(*std::begin(container))>::type&
109 {
110 auto it = std::begin(container);
111 std::advance(it, urand(0, uint32(std::size(container)) - 1));
112 return *it;
113 }
114
115 /*
116 * Select a random element from a container where each element has a different chance to be selected.
117 *
118 * @param container Container to select an element from
119 * @param weights Chances of each element to be selected, must be in the same order as elements in container.
120 * Caller is responsible for checking that sum of all weights is greater than 0.
121 *
122 * Note: container cannot be empty
123 */
124 template<class C>
125 inline auto SelectRandomWeightedContainerElement(C const& container, std::vector<double> weights) -> decltype(std::begin(container))
126 {
127 auto it = std::begin(container);
128 std::advance(it, urandweighted(weights.size(), weights.data()));
129 return it;
130 }
131
132 /*
133 * Select a random element from a container where each element has a different chance to be selected.
134 *
135 * @param container Container to select an element from
136 * @param weightExtractor Function retrieving chance of each element in container, expected to take an element of the container and returning a double
137 *
138 * Note: container cannot be empty
139 */
140 template<class C, class Fn>
141 auto SelectRandomWeightedContainerElement(C const& container, Fn weightExtractor) -> decltype(std::begin(container))
142 {
143 std::vector<double> weights;
144 weights.reserve(std::size(container));
145 double weightSum = 0.0;
146 for (auto& val : container)
147 {
148 double weight = weightExtractor(val);
149 weights.push_back(weight);
150 weightSum += weight;
151 }
152 if (weightSum <= 0.0)
153 weights.assign(std::size(container), 1.0);
154
155 return SelectRandomWeightedContainerElement(container, weights);
156 }
157
165 template<class C>
166 inline void RandomShuffle(C& container)
167 {
168 std::shuffle(std::begin(container), std::end(container), RandomEngine::Instance());
169 }
170
183 template<class Iterator1, class Iterator2>
184 bool Intersects(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
185 {
186 while (first1 != last1 && first2 != last2)
187 {
188 if (*first1 < *first2)
189 ++first1;
190 else if (*first2 < *first1)
191 ++first2;
192 else
193 return true;
194 }
195
196 return false;
197 }
198
199 namespace Impl
200 {
201 template <typename Container, typename Predicate>
202 void EraseIfMoveAssignable(Container& c, Predicate p)
203 {
204 auto wpos = c.begin();
205 for (auto rpos = c.begin(), end = c.end(); rpos != end; ++rpos)
206 {
207 if (!p(*rpos))
208 {
209 if (rpos != wpos)
210 std::ranges::swap(*rpos, *wpos);
211 ++wpos;
212 }
213 }
214 c.erase(wpos, c.end());
215 }
216
217 template <typename Container, typename Predicate>
219 {
220 for (auto it = c.begin(); it != c.end();)
221 {
222 if (p(*it))
223 it = c.erase(it);
224 else
225 ++it;
226 }
227 }
228 }
229
230 template <typename Container, typename Predicate>
231 void EraseIf(Container& c, Predicate p)
232 {
233 if constexpr (std::is_move_assignable_v<decltype(*c.begin())>)
234 Impl::EraseIfMoveAssignable(c, std::ref(p));
235 else
236 Impl::EraseIfNotMoveAssignable(c, std::ref(p));
237 }
238 }
240}
242
243#endif
uint32_t uint32
Definition Define.h:133
uint32 urandweighted(size_t count, double const *chances)
Definition Random.cpp:87
uint32 urand(uint32 min, uint32 max)
Definition Random.cpp:42
static RandomEngine & Instance()
Definition Random.cpp:93
CheckedBufferOutputIterator operator++(int)
Definition Containers.h:47
CheckedBufferOutputIterator & operator++()
Definition Containers.h:46
std::output_iterator_tag iterator_category
Definition Containers.h:37
CheckedBufferOutputIterator(T *buf, size_t n)
Definition Containers.h:43
void EraseIfMoveAssignable(Container &c, Predicate p)
Definition Containers.h:202
void EraseIfNotMoveAssignable(Container &c, Predicate p)
Definition Containers.h:218
auto SelectRandomWeightedContainerElement(C const &container, std::vector< double > weights) -> decltype(std::begin(container))
Definition Containers.h:125
void RandomShuffle(C &container)
Reorder the elements of the container randomly.
Definition Containers.h:166
auto SelectRandomContainerElement(C const &container) -> typename std::add_const< decltype(*std::begin(container))>::type &
Definition Containers.h:108
bool Intersects(Iterator1 first1, Iterator1 last1, Iterator2 first2, Iterator2 last2)
Definition Containers.h:184
void EraseIf(Container &c, Predicate p)
Definition Containers.h:231
void RandomResize(C &container, std::size_t requestedSize)
Definition Containers.h:66