Skip to content

Commit bb22f7d

Browse files
committed
Replace ValueHistory with CircularBuffer
1 parent a58d43d commit bb22f7d

File tree

6 files changed

+244
-112
lines changed

6 files changed

+244
-112
lines changed

src/openvic-simulation/country/CountryInstance.cpp

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,6 @@ CountryInstance::CountryInstance(
7272
building_type_unlock_levels { building_type_keys },
7373

7474
/* Budget */
75-
balance_history{30, 0},
7675
taxable_income_by_pop_type { pop_type_keys },
7776
effective_tax_rate_by_strata {
7877
strata_keys,

src/openvic-simulation/country/CountryInstance.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include "openvic-simulation/modifier/ModifierSum.hpp"
99
#include "openvic-simulation/politics/Rule.hpp"
1010
#include "openvic-simulation/pop/PopsAggregate.hpp"
11+
#include "openvic-simulation/types/CircularBuffer.hpp"
1112
#include "openvic-simulation/types/ClampedValue.hpp"
1213
#include "openvic-simulation/types/Date.hpp"
1314
#include "openvic-simulation/types/fixed_point/Atomic.hpp"
@@ -19,7 +20,6 @@
1920
#include "openvic-simulation/types/TechnologyUnlockLevel.hpp"
2021
#include "openvic-simulation/types/UnitBranchType.hpp"
2122
#include "openvic-simulation/types/UnitVariant.hpp"
22-
#include "openvic-simulation/types/ValueHistory.hpp"
2323
#include "openvic-simulation/utility/Getters.hpp"
2424
#include "openvic-simulation/utility/Containers.hpp"
2525
#include "openvic-simulation/utility/reactive/DerivedState.hpp"
@@ -144,7 +144,8 @@ namespace OpenVic {
144144

145145
/* Budget */
146146
fixed_point_t cache_stockpile_start_of_tick;
147-
ValueHistory<fixed_point_t> PROPERTY(balance_history);
147+
static constexpr size_t DAYS_OF_BALANCE_HISTORY = 30;
148+
CircularBuffer<fixed_point_t, DAYS_OF_BALANCE_HISTORY> PROPERTY(balance_history);
148149
STATE_PROPERTY(fixed_point_t, gold_income);
149150
atomic_fixed_point_t PROPERTY(cash_stockpile);
150151
std::mutex taxable_income_mutex;

src/openvic-simulation/economy/trading/GoodMarket.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,14 @@
99
#include "openvic-simulation/utility/Containers.hpp"
1010

1111
using namespace OpenVic;
12-
static constexpr size_t MONTHS_OF_PRICE_HISTORY = 36;
1312

1413
GoodMarket::GoodMarket(GameRulesManager const& new_game_rules_manager, GoodDefinition const& new_good_definition)
1514
: game_rules_manager { new_game_rules_manager },
1615
good_definition { new_good_definition },
1716
price { new_good_definition.get_base_price() },
18-
is_available { new_good_definition.get_is_available_from_start() },
19-
price_history { MONTHS_OF_PRICE_HISTORY, new_good_definition.get_base_price() }
17+
is_available { new_good_definition.get_is_available_from_start() }
2018
{
19+
price_history.fill(new_good_definition.get_base_price());
2120
on_use_exponential_price_changes_changed();
2221
update_next_price_limits();
2322
}

src/openvic-simulation/economy/trading/GoodMarket.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44

55
#include "openvic-simulation/economy/trading/BuyUpToOrder.hpp"
66
#include "openvic-simulation/economy/trading/MarketSellOrder.hpp"
7+
#include "openvic-simulation/types/CircularBuffer.hpp"
78
#include "openvic-simulation/types/fixed_point/FixedPoint.hpp"
89
#include "openvic-simulation/types/IndexedFlatMap.hpp"
9-
#include "openvic-simulation/types/ValueHistory.hpp"
1010
#include "openvic-simulation/utility/Containers.hpp"
1111
#include "openvic-simulation/utility/ForwardableSpan.hpp"
1212

@@ -49,7 +49,8 @@ namespace OpenVic {
4949
fixed_point_t PROPERTY(total_demand_yesterday);
5050
fixed_point_t PROPERTY(total_supply_yesterday);
5151
fixed_point_t PROPERTY(quantity_traded_yesterday);
52-
ValueHistory<fixed_point_t> PROPERTY(price_history);
52+
static constexpr size_t MONTHS_OF_PRICE_HISTORY = 36;
53+
CircularBuffer<fixed_point_t, MONTHS_OF_PRICE_HISTORY> PROPERTY(price_history);
5354

5455
void update_next_price_limits();
5556
public:
Lines changed: 236 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,236 @@
1+
#pragma once
2+
3+
#include <algorithm>
4+
#include <array>
5+
#include <cstddef>
6+
#include <iterator>
7+
#include <utility>
8+
9+
namespace OpenVic {
10+
/**
11+
* @brief A fixed-size circular buffer that stores the last N elements, supporting iteration.
12+
*
13+
* @tparam T The type of elements to store.
14+
* @tparam N The maximum capacity (must be > 0).
15+
*/
16+
template <typename T, std::size_t N>
17+
class CircularBuffer {
18+
static_assert(N > 0, "Capacity N must be greater than 0.");
19+
20+
private:
21+
std::array<T, N> storage;
22+
std::size_t head = 0; // Index of the next available slot for insertion
23+
std::size_t current_size = 0;
24+
25+
/**
26+
* @brief Calculates the physical index in storage corresponding to the oldest element.
27+
* @return The physical index of the beginning of the buffer's content.
28+
*/
29+
std::size_t calculate_start_index() const noexcept {
30+
// If the buffer is full, the oldest element is at 'head'.
31+
// If not full, the oldest element is at index 0.
32+
// The expression (head + N - current_size) % N correctly finds the start
33+
return (head + N - current_size) % N;
34+
}
35+
36+
/**
37+
* @brief Encapsulates the logic for advancing the head pointer and updating the size.
38+
* * This logic is common to both push_back overloads.
39+
*/
40+
void increment_head_and_size() noexcept {
41+
// Advance the head pointer, wrapping around the capacity N
42+
head = (head + 1) % N;
43+
44+
// Only increment the size if the buffer wasn't already full
45+
if (current_size < N) {
46+
current_size++;
47+
}
48+
}
49+
50+
public:
51+
CircularBuffer() = default;
52+
53+
template <typename ValueType>
54+
class CircularBufferIterator {
55+
public:
56+
using iterator_category = std::forward_iterator_tag;
57+
using value_type = ValueType;
58+
using difference_type = std::ptrdiff_t;
59+
using pointer = ValueType*;
60+
using reference = ValueType&;
61+
62+
private:
63+
ValueType* data_ptr;
64+
std::size_t logical_index;
65+
std::size_t physical_index;
66+
std::size_t capacity;
67+
68+
CircularBufferIterator(
69+
ValueType* new_data_ptr,
70+
std::size_t start_phys_index,
71+
std::size_t new_logical_index,
72+
std::size_t new_capacity
73+
) :
74+
data_ptr(new_data_ptr),
75+
logical_index(new_logical_index),
76+
capacity(new_capacity)
77+
{
78+
physical_index = (start_phys_index + new_logical_index) % capacity;
79+
}
80+
81+
friend class CircularBuffer<T, N>;
82+
83+
public:
84+
CircularBufferIterator() :
85+
data_ptr(nullptr),
86+
logical_index(0),
87+
physical_index(0),
88+
capacity(0)
89+
{}
90+
91+
reference operator*() const {
92+
return data_ptr[physical_index];
93+
}
94+
95+
pointer operator->() const {
96+
return &data_ptr[physical_index];
97+
}
98+
99+
CircularBufferIterator& operator++() {
100+
if (logical_index < N) {
101+
logical_index++;
102+
physical_index = (physical_index + 1) % capacity;
103+
}
104+
return *this;
105+
}
106+
107+
CircularBufferIterator operator++(int) {
108+
CircularBufferIterator temp = *this;
109+
++(*this);
110+
return temp;
111+
}
112+
113+
bool operator==(const CircularBufferIterator& other) const {
114+
return logical_index == other.logical_index;
115+
}
116+
117+
bool operator!=(const CircularBufferIterator& other) const {
118+
return !(*this == other);
119+
}
120+
};
121+
122+
// Define the iterator types for convenience
123+
using iterator = CircularBufferIterator<T>;
124+
using const_iterator = CircularBufferIterator<const T>;
125+
126+
/**
127+
* @brief Returns an iterator to the beginning of the buffer (the oldest element).
128+
* @return An iterator pointing to the first element in insertion order.
129+
*/
130+
iterator begin() {
131+
return iterator(
132+
storage.data(),
133+
calculate_start_index(),
134+
0,
135+
N
136+
);
137+
}
138+
139+
/**
140+
* @brief Returns a const iterator to the beginning of the buffer.
141+
*/
142+
const_iterator begin() const {
143+
return const_iterator(
144+
storage.data(),
145+
calculate_start_index(),
146+
0,
147+
N
148+
);
149+
}
150+
151+
/**
152+
* @brief Returns an iterator to the end of the buffer (one past the newest element).
153+
* @return An iterator pointing one past the last element.
154+
*/
155+
iterator end() {
156+
return iterator(
157+
storage.data(),
158+
0,
159+
current_size,
160+
N
161+
);
162+
}
163+
164+
/**
165+
* @brief Returns a const iterator to the end of the buffer.
166+
*/
167+
const_iterator end() const {
168+
return const_iterator(
169+
storage.data(),
170+
0,
171+
current_size,
172+
N
173+
);
174+
}
175+
176+
// Convenience for const iteration
177+
const_iterator cbegin() const { return begin(); }
178+
const_iterator cend() const { return end(); }
179+
180+
/**
181+
* @brief Adds an element to the buffer. (Copy version)
182+
* @param value The value to be added.
183+
*/
184+
void push_back(const T& value) {
185+
storage[head] = value;
186+
increment_head_and_size();
187+
}
188+
189+
/**
190+
* @brief Adds an element to the buffer. (Move version)
191+
* @param value The rvalue reference to the value to be added.
192+
*/
193+
void push_back(T&& value) {
194+
storage[head] = std::move(value);
195+
increment_head_and_size();
196+
}
197+
198+
/**
199+
* @brief Fills the entire capacity of the buffer with a single value.
200+
* * After this call, size() will equal capacity(), and the head will be reset to 0.
201+
* @param value The value to copy into every slot of the buffer.
202+
*/
203+
void fill(const T& value) {
204+
// 1. Use std::fill to efficiently assign the value to every element
205+
// in the underlying fixed array.
206+
std::fill(storage.begin(), storage.end(), value);
207+
208+
// 2. Update the internal state to reflect a full buffer.
209+
// The current_size is now equal to the full capacity N.
210+
current_size = N;
211+
212+
// 3. Reset the head index.
213+
// When the buffer is full and non-circularly indexed, the next insertion
214+
// should start overwriting at index 0 (the calculated start index).
215+
// Since current_size is N, calculate_start_index() would correctly return
216+
// (head + N - N) % N == head % N. Setting head to 0 simplifies the state.
217+
head = 0;
218+
}
219+
220+
constexpr std::size_t capacity() const noexcept {
221+
return N;
222+
}
223+
224+
std::size_t size() const noexcept {
225+
return current_size;
226+
}
227+
228+
bool empty() const noexcept {
229+
return current_size == 0;
230+
}
231+
232+
bool full() const noexcept {
233+
return current_size == N;
234+
}
235+
};
236+
}

0 commit comments

Comments
 (0)