Line data Source code
1 : #ifndef util_hpp
2 : #define util_hpp
3 :
4 : #include <algorithm>
5 : #include <cassert>
6 : #include <cstdint>
7 : #include <initializer_list>
8 : #include <random>
9 : #include <sstream>
10 : #include <string>
11 : #include <unordered_map>
12 :
13 : // reference to an entity or component
14 : struct ident {
15 : public:
16 1214 : constexpr explicit ident(uint32_t i) : i_{i} {}
17 :
18 0 : operator bool() { return i_ != 0; }
19 :
20 5843 : bool operator==(const ident& other) const { return i_ == other.i_; }
21 0 : bool operator!=(const ident& other) const { return !(*this == other); }
22 :
23 1194 : ident operator++(int) {
24 1194 : uint32_t old = i_++;
25 1194 : return ident{old};
26 : }
27 :
28 : protected:
29 : uint32_t i_;
30 : friend std::hash<ident>;
31 : friend std::string to_string(const ident& id);
32 : };
33 :
34 : constexpr ident invalid_id{0};
35 :
36 : inline std::string to_string(const ident& id) { return std::string("#") + std::to_string(id.i_); }
37 :
38 : namespace std {
39 : // for std::unordered_map<id, ...>
40 : template <> struct hash<::ident> {
41 4335 : size_t operator()(::ident i) const noexcept { return hash<uint32_t>()(i.i_); }
42 : };
43 : }; // namespace std
44 :
45 : // packed-array map for components
46 20 : template <typename T> class container {
47 : public:
48 : using key_type = ident;
49 : using value_type = T;
50 :
51 20 : container() {
52 20 : const int initialElems = 4096;
53 20 : values_.reserve(initialElems);
54 20 : }
55 :
56 : value_type& add(value_type value = {}) {
57 : key_type key = nextKey_++;
58 : size_t index = values_.size();
59 : values_.push_back(std::move(value));
60 : values_.back().id = key;
61 : indices_[key] = index;
62 : return values_.back();
63 : }
64 :
65 10 : value_type& operator[](key_type key) {
66 10 : auto it = indices_.find(key);
67 10 : if (it == indices_.end())
68 0 : return nullElement_;
69 : else
70 10 : return values_[it->second];
71 : }
72 :
73 0 : void remove(key_type key) {
74 0 : auto it = indices_.find(key);
75 0 : assert(it != indices_.end());
76 0 : size_t index = it->second;
77 0 : auto& back = values_.back();
78 0 : indices_[back.id] = index;
79 0 : values_[index] = std::move(back);
80 0 : values_.pop_back();
81 0 : indices_.erase(it);
82 0 : }
83 :
84 50 : std::vector<T>& values() { return values_; }
85 :
86 : protected:
87 : std::unordered_map<key_type, size_t> indices_;
88 : std::vector<T> values_;
89 : key_type nextKey_{1};
90 : T nullElement_{};
91 :
92 : template <typename U> friend std::string to_string(const container<U>& container);
93 : };
94 :
95 : template <typename T> std::string to_string(const container<T>& container) {
96 : std::ostringstream oss;
97 : oss << "[";
98 : for (const auto& v : container.values_) {
99 : oss << "(" << container.indices_.at(v.id) << ":" << to_string(v) << ") ";
100 : }
101 : oss << "]";
102 : return oss.str();
103 : }
104 :
105 : // a buffered, safer version of container
106 : // preserves references to elements until sync() is called
107 20 : template <typename T> class buffered_container : public container<T> {
108 : using super = container<T>;
109 :
110 : public:
111 : using key_type = typename super::key_type;
112 : using value_type = typename super::value_type;
113 :
114 20 : buffered_container() {
115 20 : add_.reserve(max_size());
116 20 : remove_.reserve(max_size());
117 :
118 20 : remove_.clear();
119 20 : add_.clear();
120 20 : }
121 :
122 1234 : constexpr int max_size() const { return 1024; }
123 :
124 0 : int size() const { return (int)add_.size(); }
125 :
126 1194 : value_type& add(value_type value = {}) {
127 1194 : if (add_.size() >= max_size()) {
128 0 : throw std::runtime_error("Exceeded buffer capacity");
129 : }
130 :
131 1194 : buffered_ = true;
132 1194 : key_type key = super::nextKey_++;
133 1194 : add_.push_back(std::move(value));
134 1194 : add_.back().id = key;
135 1194 : return add_.back();
136 : }
137 :
138 0 : void remove(key_type key) {
139 0 : bool inContainer = super::indices_.find(key) != super::indices_.end();
140 0 : bool inBuffer = std::find_if(add_.begin(), add_.end(),
141 0 : [key](T& value) { return value.id == key; }) != add_.end();
142 0 : assert(inContainer || inBuffer);
143 :
144 0 : buffered_ = true;
145 0 : remove_.push_back(key);
146 0 : }
147 :
148 212 : value_type& operator[](key_type key) {
149 212 : if (buffered_) {
150 202 : auto remove_it = std::find(remove_.begin(), remove_.end(), key);
151 202 : if (remove_it != remove_.end())
152 202 : return super::nullElement_;
153 :
154 202 : if (super::indices_.count(key) == 0) {
155 202 : auto add_it = std::find_if(add_.begin(), add_.end(),
156 5717 : [key](const T& t) { return t.id == key; });
157 202 : if (add_it != add_.end())
158 202 : return *add_it;
159 : }
160 : }
161 10 : return super::operator[](key);
162 : }
163 :
164 100 : void sync() {
165 1294 : for (auto& value : add_) {
166 1194 : size_t index = super::values_.size();
167 1194 : super::values_.push_back(std::move(value));
168 1194 : super::indices_[value.id] = index;
169 : }
170 :
171 100 : for (auto& id : remove_) {
172 0 : super::remove(id);
173 : }
174 :
175 100 : add_.clear();
176 100 : remove_.clear();
177 100 : buffered_ = false;
178 100 : }
179 :
180 : protected:
181 : bool buffered_ = false;
182 : std::vector<T> add_{};
183 : std::vector<ident> remove_{};
184 : };
185 :
186 : // Mathematics
187 :
188 : template <typename T> struct vec2 {
189 : T x, y;
190 :
191 0 : template <typename U> explicit operator vec2<U>() {
192 0 : return vec2<U>{static_cast<U>(x), static_cast<U>(y)};
193 : }
194 : };
195 :
196 : using vec2i = vec2<int32_t>;
197 : using vec2d = vec2<double>;
198 :
199 : template <typename T> inline vec2<T> operator-(vec2<T> a) { return {-a.x, -a.y}; }
200 1020828 : template <typename T> inline vec2<T> operator+(vec2<T> a, vec2<T> b) {
201 1020828 : return {a.x + b.x, a.y + b.y};
202 : }
203 327680 : template <typename T> inline vec2<T> operator-(vec2<T> a, vec2<T> b) {
204 327680 : return {a.x - b.x, a.y - b.y};
205 : }
206 0 : template <typename T> inline vec2<T> operator*(vec2<T> a, T m) { return {a.x * m, a.y * m}; }
207 674153 : template <typename T> inline vec2<T> operator/(vec2<T> a, T m) { return {a.x / m, a.y / m}; }
208 0 : template <typename T> inline vec2<T>& operator+=(vec2<T>& a, vec2<T> b) { return a = a + b; }
209 : template <typename T> inline vec2<T>& operator-=(vec2<T>& a, vec2<T> b) { return a = a + b; }
210 0 : template <typename T> inline vec2<T>& operator*=(vec2<T>& a, T b) { return a = a * b; }
211 : template <typename T> inline vec2<T>& operator/=(vec2<T>& a, T b) { return a = a / b; }
212 0 : template <typename T> inline bool operator==(vec2<T> a, vec2<T> b) {
213 0 : return a.x == b.x && a.y == b.y;
214 : }
215 10 : template <typename T> inline bool operator!=(vec2<T> a, vec2<T> b) {
216 10 : return a.x != b.x || a.y != b.y;
217 : }
218 :
219 : struct recti {
220 : int32_t left, top, width, height;
221 665255 : inline bool contains(const vec2i& p) const {
222 665255 : return p.x >= left && p.x < left + width && p.y <= top && p.y > top - height;
223 : }
224 : };
225 :
226 : template <typename T> inline std::string to_string(const vec2<T>& vec) {
227 : using namespace std::string_literals;
228 : return "{"s + std::to_string(vec.x) + ", "s + std::to_string(vec.y) + "}"s;
229 : }
230 :
231 0 : template <typename T> T sign(T t) {
232 0 : if (t > 0)
233 0 : return 1;
234 0 : else if (t < 0)
235 0 : return -1;
236 : else
237 0 : return 0;
238 : }
239 :
240 : // Helpful containers
241 :
242 10 : template <typename T> struct Array2D {
243 : public:
244 10 : Array2D(int width, int height, T nullVal = {})
245 10 : : nullVal_(nullVal), width_(width), height_(height), data_(width * height, nullVal) {}
246 :
247 : void resize(int newWidth, int newHeight) {
248 : width_ = newWidth;
249 : height_ = newHeight;
250 : data_.resize(width_ * height_, nullVal_);
251 : data_.shrink_to_fit();
252 : }
253 :
254 135840 : int width() const { return width_; }
255 :
256 135840 : int height() const { return height_; }
257 :
258 5 : void fill(T val) { std::fill(data_.begin(), data_.end(), val); }
259 :
260 170980 : T& operator()(int x, int y) { return inBounds(x, y) ? data_[x + y * width_] : nullVal_; }
261 :
262 35140 : T& operator()(vec2i p) { return (*this)(p.x, p.y); }
263 :
264 : const T& operator()(int x, int y) const {
265 : return inBounds(x, y) ? data_[x + y * width_] : nullVal_;
266 : }
267 :
268 : const T& operator()(vec2i p) const { return (*this)(p.x, p.y); }
269 :
270 170980 : bool inBounds(int x, int y) const { return x >= 0 && x < width_ && y >= 0 && y < height_; }
271 :
272 : bool inBounds(vec2i p) const { return p.x >= 0 && p.x < width_ && p.y >= 0 && p.y < height_; }
273 :
274 : // Raw data access
275 10 : std::vector<T>& data() { return data_; }
276 :
277 : private:
278 : T nullVal_{};
279 : int width_ = 0;
280 : int height_ = 0;
281 : std::vector<T> data_;
282 : };
283 :
284 : // Random number generation
285 :
286 58738 : inline std::default_random_engine& engine() {
287 58738 : static std::default_random_engine engine_;
288 58738 : return engine_;
289 : }
290 :
291 58738 : inline int32_t randInt(int32_t from, int32_t to) {
292 58738 : return std::uniform_int_distribution<>(from, to)(engine());
293 : }
294 :
295 0 : inline double random(double from = 0.0, double to = 1.0) {
296 0 : return std::uniform_real_distribution<>(from, to)(engine());
297 : }
298 :
299 : inline vec2i randVec2i(vec2i from, vec2i to) {
300 : return {randInt(from.x, to.x), randInt(from.y, to.y)};
301 : }
302 :
303 : template <typename T> T choose(const std::vector<T>& values) {
304 : return values[randInt(0, values.size() - 1)];
305 : }
306 :
307 4615 : template <typename T> T choose(std::initializer_list<T> values) {
308 4615 : return *(values.begin() + randInt(0, (int)values.size() - 1));
309 : }
310 :
311 : #endif
|