mirror of https://github.com/OpenTTD/OpenTTD
Codechange: Extend industry cargo history to 24 years.
Monthly data is stored for the current 24 months. Quarterly data is stored for a further 2-6 years. Yearly data is stored for a further 6-24 years.pull/14384/head
parent
baad229c47
commit
500f092511
|
@ -1699,7 +1699,7 @@ struct IndustryProductionGraphWindow : BaseCargoGraphWindow {
|
|||
transported.range_bit = 1;
|
||||
transported.dash = 2;
|
||||
|
||||
FillFromHistory<GRAPH_NUM_MONTHS>(p.history, Filler{produced, &Industry::ProducedHistory::production}, Filler{transported, &Industry::ProducedHistory::transported});
|
||||
FillFromHistory<GRAPH_NUM_MONTHS>(p.history, 0, Filler{produced, &Industry::ProducedHistory::production}, Filler{transported, &Industry::ProducedHistory::transported});
|
||||
}
|
||||
|
||||
for (const auto &a : i->accepted) {
|
||||
|
@ -1720,7 +1720,7 @@ struct IndustryProductionGraphWindow : BaseCargoGraphWindow {
|
|||
waiting.range_bit = 3;
|
||||
waiting.dash = 4;
|
||||
|
||||
FillFromHistory<GRAPH_NUM_MONTHS>(a.history, Filler{accepted, &Industry::AcceptedHistory::accepted}, Filler{waiting, &Industry::AcceptedHistory::waiting});
|
||||
FillFromHistory<GRAPH_NUM_MONTHS>(a.history, 0, Filler{accepted, &Industry::AcceptedHistory::accepted}, Filler{waiting, &Industry::AcceptedHistory::waiting});
|
||||
}
|
||||
|
||||
this->SetDirty();
|
||||
|
|
|
@ -2492,25 +2492,44 @@ void GenerateIndustries()
|
|||
_industry_builder.Reset();
|
||||
}
|
||||
|
||||
template <>
|
||||
Industry::ProducedHistory SumHistory(std::span<Industry::ProducedHistory> history)
|
||||
{
|
||||
uint32_t production = std::accumulate(std::begin(history), std::end(history), 0, [](uint32_t r, const auto &p) { return r + p.production; });
|
||||
uint32_t transported = std::accumulate(std::begin(history), std::end(history), 0, [](uint32_t r, const auto &p) { return r + p.transported; });
|
||||
auto count = std::size(history);
|
||||
return {.production = ClampTo<uint16_t>(production / count), .transported = ClampTo<uint16_t>(transported / count)};
|
||||
}
|
||||
|
||||
template <>
|
||||
Industry::AcceptedHistory SumHistory(std::span<Industry::AcceptedHistory> history)
|
||||
{
|
||||
uint32_t accepted = std::accumulate(std::begin(history), std::end(history), 0, [](uint32_t r, const auto &a) { return r + a.accepted; });
|
||||
uint32_t waiting = std::accumulate(std::begin(history), std::end(history), 0, [](uint32_t r, const auto &a) { return r + a.waiting; });;
|
||||
auto count = std::size(history);
|
||||
return {.accepted = ClampTo<uint16_t>(accepted / count), .waiting = ClampTo<uint16_t>(waiting / count)};
|
||||
}
|
||||
|
||||
/**
|
||||
* Monthly update of industry statistics.
|
||||
* @param i Industry to update.
|
||||
*/
|
||||
static void UpdateIndustryStatistics(Industry *i)
|
||||
{
|
||||
auto month = TimerGameEconomy::month;
|
||||
for (auto &p : i->produced) {
|
||||
if (IsValidCargoType(p.cargo)) {
|
||||
if (p.history[THIS_MONTH].production != 0) i->last_prod_year = TimerGameEconomy::year;
|
||||
|
||||
/* Move history from this month to last month. */
|
||||
RotateHistory(p.history);
|
||||
RotateHistory(p.history, month);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto &a : i->accepted) {
|
||||
if (!IsValidCargoType(a.cargo)) continue;
|
||||
a.history[THIS_MONTH].waiting = GetAndResetAccumulatedAverage<uint16_t>(a.accumulated_waiting);
|
||||
RotateHistory(a.history);
|
||||
RotateHistory(a.history, month);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -15,16 +15,45 @@
|
|||
#include "../timer/timer_game_economy.h"
|
||||
#include "history_type.hpp"
|
||||
|
||||
/**
|
||||
* Sum history data between first and last elements.
|
||||
* @note The summation should prevent overflowing, and perform transformations relevant to the type of data.
|
||||
* @tparam T type of history data element.
|
||||
* @param first First element to sum.
|
||||
* @param last Last element to sum.
|
||||
* @return Sum of history elements.
|
||||
*/
|
||||
template <typename T>
|
||||
T SumHistory(typename std::span<T> first);
|
||||
|
||||
/**
|
||||
* Rotate history.
|
||||
* @tparam T type of history data element.
|
||||
* @param history Historical data to rotate.
|
||||
*/
|
||||
template <typename T>
|
||||
void RotateHistory(HistoryData<T> &history)
|
||||
void RotateHistory(HistoryData<T> &history, const HistoryRange &hr, uint age)
|
||||
{
|
||||
std::rotate(std::rbegin(history), std::rbegin(history) + 1, std::rend(history));
|
||||
history[THIS_MONTH] = {};
|
||||
if (age % hr.total_division != 0) return;
|
||||
|
||||
std::move_backward(std::next(std::begin(history), hr.first), std::next(std::begin(history), hr.last - 1), std::next(std::begin(history), hr.last));
|
||||
|
||||
if (hr.division == 1) {
|
||||
history[hr.first] = history[hr.first - 1];
|
||||
} else {
|
||||
auto first = std::next(std::begin(history), hr.first - hr.division);
|
||||
auto last = std::next(first, hr.division);
|
||||
history[hr.first] = SumHistory<T>(std::span{first, last});
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void RotateHistory(HistoryData<T> &history, uint age)
|
||||
{
|
||||
RotateHistory(history, HISTORY_MONTH, age);
|
||||
RotateHistory(history, HISTORY_QUARTER, age);
|
||||
RotateHistory(history, HISTORY_YEAR, age);
|
||||
history.front() = {};
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -40,16 +69,53 @@ T GetAndResetAccumulatedAverage(Taccrued &total)
|
|||
return result;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T GetHistory(const HistoryData<T> &history, const HistoryRange &hr, uint age)
|
||||
{
|
||||
if (hr.hr == nullptr) {
|
||||
if (age < hr.periods) return history[hr.first + age];
|
||||
} else {
|
||||
if (age * hr.division < static_cast<uint>(hr.hr->periods - hr.division)) {
|
||||
std::array<T, HISTORY_MAX_DIVISION> result; // No need to clear as we fill every element we use.
|
||||
uint start = age * hr.division + ((TimerGameEconomy::month / hr.hr->division) % hr.division);
|
||||
for (auto i = start; i != start + hr.division; ++i) {
|
||||
result[i - start] = GetHistory(history, *hr.hr, i);
|
||||
}
|
||||
return SumHistory<T>(std::span{std::begin(result), hr.division});
|
||||
}
|
||||
if (age < hr.periods) return history[hr.first + age - ((hr.hr->periods / hr.division) - 1)];
|
||||
}
|
||||
NOT_REACHED();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get history data for the specified period and age within that period.
|
||||
* @param history History data to extract from.
|
||||
* @param period Period to get.
|
||||
* @param age Age of data to get.
|
||||
* @return Historical value for period and age.
|
||||
*/
|
||||
template <typename T>
|
||||
T GetHistory(const HistoryData<T> &history, uint period, uint age)
|
||||
{
|
||||
switch (period) {
|
||||
case 0: return GetHistory(history, HISTORY_MONTH, age);
|
||||
case 1: return GetHistory(history, HISTORY_QUARTER, age);
|
||||
case 2: return GetHistory(history, HISTORY_YEAR, age);
|
||||
default: NOT_REACHED();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fill some data with historical data.
|
||||
* @param history Historical data to fill from.
|
||||
* @param fillers Fillers to fill with history data.
|
||||
*/
|
||||
template <uint N, typename T, typename... Tfillers>
|
||||
void FillFromHistory(const HistoryData<T> &history, Tfillers... fillers)
|
||||
void FillFromHistory(const HistoryData<T> &history, uint period, Tfillers... fillers)
|
||||
{
|
||||
for (uint i = 0; i != N; ++i) {
|
||||
auto &data = history[N - i];
|
||||
auto data = GetHistory(history, period, N - i - 1);
|
||||
(fillers.Fill(i, data), ...);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,7 +10,40 @@
|
|||
#ifndef HISTORY_TYPE_HPP
|
||||
#define HISTORY_TYPE_HPP
|
||||
|
||||
static constexpr uint8_t HISTORY_RECORDS = 25;
|
||||
#include "../stdafx.h"
|
||||
|
||||
static constexpr uint8_t HISTORY_PERIODS = 24;
|
||||
|
||||
struct HistoryRange {
|
||||
const HistoryRange *hr;
|
||||
const uint8_t periods; ///< Number of periods for this range.
|
||||
const uint8_t records; ///< Number of records needed for this range.
|
||||
const uint8_t first; ///< Index of first element in history data.
|
||||
const uint8_t last; ///< Index of last element in history data.
|
||||
const uint8_t division; ///< Number of divisions of the previous history range.
|
||||
const uint8_t total_division; ///< Number of divisions of the initial history range.
|
||||
|
||||
constexpr HistoryRange(uint8_t periods) :
|
||||
hr(nullptr), periods(periods), records(this->periods), first(1), last(this->first + this->records), division(1), total_division(1)
|
||||
{
|
||||
}
|
||||
|
||||
constexpr HistoryRange(const HistoryRange &hr, uint8_t division, uint8_t periods) :
|
||||
hr(&hr), periods(periods), records(this->periods - ((hr.periods / division) - 1)), first(hr.last), last(this->first + this->records),
|
||||
division(division), total_division(division * hr.total_division)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
static constexpr HistoryRange HISTORY_MONTH{HISTORY_PERIODS};
|
||||
static constexpr HistoryRange HISTORY_QUARTER{HISTORY_MONTH, 3, HISTORY_PERIODS};
|
||||
static constexpr HistoryRange HISTORY_YEAR{HISTORY_QUARTER, 4, HISTORY_PERIODS};
|
||||
|
||||
/** Maximum number of divisions from previous history range. */
|
||||
static constexpr uint8_t HISTORY_MAX_DIVISION = std::max({HISTORY_MONTH.division, HISTORY_QUARTER.division, HISTORY_YEAR.division});
|
||||
|
||||
/** Total number of records require for all history data. */
|
||||
static constexpr uint8_t HISTORY_RECORDS = HISTORY_YEAR.last;
|
||||
|
||||
static constexpr uint8_t THIS_MONTH = 0;
|
||||
static constexpr uint8_t LAST_MONTH = 1;
|
||||
|
|
|
@ -3,6 +3,7 @@ add_test_files(
|
|||
bitmath_func.cpp
|
||||
enum_over_optimisation.cpp
|
||||
flatset_type.cpp
|
||||
history_func.cpp
|
||||
landscape_partial_pixel_z.cpp
|
||||
math_func.cpp
|
||||
mock_environment.h
|
||||
|
|
|
@ -0,0 +1,66 @@
|
|||
/*
|
||||
* This file is part of OpenTTD.
|
||||
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
|
||||
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
/** @file history_func.cpp Test functionality for misc/history_func. */
|
||||
|
||||
#include "../stdafx.h"
|
||||
|
||||
#include "../3rdparty/catch2/catch.hpp"
|
||||
|
||||
#include "../misc/history_type.hpp"
|
||||
#include "../misc/history_func.hpp"
|
||||
|
||||
#include "../safeguards.h"
|
||||
|
||||
template <>
|
||||
uint16_t SumHistory(std::span<uint16_t> history)
|
||||
{
|
||||
uint32_t total = std::accumulate(std::begin(history), std::end(history), 0, [](uint32_t r, const uint16_t &value) { return r + value; });
|
||||
return ClampTo<uint16_t>(total);
|
||||
}
|
||||
|
||||
TEST_CASE("History Rotation and Reporting tests")
|
||||
{
|
||||
HistoryData<uint16_t> history{};
|
||||
|
||||
/* Fill the history with decreasing data points for 24 years of history. This ensures that no data period should
|
||||
* contain the same value as another period. */
|
||||
uint16_t i = 12 * HISTORY_PERIODS;
|
||||
for (uint date = 1; date <= 12 * HISTORY_PERIODS; ++date, --i) {
|
||||
history[THIS_MONTH] = i;
|
||||
RotateHistory(history, date % 12);
|
||||
}
|
||||
|
||||
/* With the decreasing sequence, the expected value is triangle number (x*x+n)/2 and the square of the total divisions.
|
||||
* for quarters: 1 + 2 + 3 = 6, 4 + 5 + 6 = 15, 7 + 8 + 9 = 24, 10 + 11 + 12 = 33
|
||||
* 13 + 14 + 15 = 42, 16 + 17 + 18 = 51, 19 + 20 + 21 = 60, 22 + 23 + 24 = 69...
|
||||
* for years: 6 + 15 + 24 + 33 = 78, 42 + 51 + 60 + 69 = 222...
|
||||
*/
|
||||
for (uint j = 0; j < HISTORY_PERIODS; ++j) {
|
||||
CHECK(GetHistory(history, 0, j) == (( 1 * 1 + 1) / 2) + 1 * 1 * j);
|
||||
CHECK(GetHistory(history, 1, j) == (( 3 * 3 + 3) / 2) + 3 * 3 * j);
|
||||
CHECK(GetHistory(history, 2, j) == ((12 * 12 + 12) / 2) + 12 * 12 * j);
|
||||
}
|
||||
|
||||
/* Double-check quarter history matches summed month history. */
|
||||
CHECK(GetHistory(history, 0, 0) + GetHistory(history, 0, 1) + GetHistory(history, 0, 2) == GetHistory(history, 1, 0));
|
||||
CHECK(GetHistory(history, 0, 3) + GetHistory(history, 0, 4) + GetHistory(history, 0, 5) == GetHistory(history, 1, 1));
|
||||
CHECK(GetHistory(history, 0, 6) + GetHistory(history, 0, 7) + GetHistory(history, 0, 8) == GetHistory(history, 1, 2));
|
||||
CHECK(GetHistory(history, 0, 9) + GetHistory(history, 0, 10) + GetHistory(history, 0, 11) == GetHistory(history, 1, 3));
|
||||
CHECK(GetHistory(history, 0, 12) + GetHistory(history, 0, 13) + GetHistory(history, 0, 14) == GetHistory(history, 1, 4));
|
||||
CHECK(GetHistory(history, 0, 15) + GetHistory(history, 0, 16) + GetHistory(history, 0, 17) == GetHistory(history, 1, 5));
|
||||
CHECK(GetHistory(history, 0, 18) + GetHistory(history, 0, 19) + GetHistory(history, 0, 20) == GetHistory(history, 1, 6));
|
||||
CHECK(GetHistory(history, 0, 21) + GetHistory(history, 0, 22) + GetHistory(history, 0, 23) == GetHistory(history, 1, 7));
|
||||
|
||||
/* Double-check year history matches summed quarter history. */
|
||||
CHECK(GetHistory(history, 1, 0) + GetHistory(history, 1, 1) + GetHistory(history, 1, 2) + GetHistory(history, 1, 3) == GetHistory(history, 2, 0));
|
||||
CHECK(GetHistory(history, 1, 4) + GetHistory(history, 1, 5) + GetHistory(history, 1, 6) + GetHistory(history, 1, 7) == GetHistory(history, 2, 1));
|
||||
CHECK(GetHistory(history, 1, 8) + GetHistory(history, 1, 9) + GetHistory(history, 1, 10) + GetHistory(history, 1, 11) == GetHistory(history, 2, 2));
|
||||
CHECK(GetHistory(history, 1, 12) + GetHistory(history, 1, 13) + GetHistory(history, 1, 14) + GetHistory(history, 1, 15) == GetHistory(history, 2, 3));
|
||||
CHECK(GetHistory(history, 1, 16) + GetHistory(history, 1, 17) + GetHistory(history, 1, 18) + GetHistory(history, 1, 19) == GetHistory(history, 2, 4));
|
||||
CHECK(GetHistory(history, 1, 20) + GetHistory(history, 1, 21) + GetHistory(history, 1, 22) + GetHistory(history, 1, 23) == GetHistory(history, 2, 5));
|
||||
}
|
Loading…
Reference in New Issue