generators basics

This commit is contained in:
Crypto City 2019-09-27 20:42:00 +00:00
parent fc59d306d0
commit 774904cf9d
11 changed files with 448 additions and 69 deletions

View File

@ -57,15 +57,28 @@ static const char *block_names[NUM_ITEMS] = {
"<empty>",
"stone",
"wood",
"brick",
"metal",
};
static const constexpr uint64_t last_resort_block_cost[256] = {
0, // empty
1000000, // stone
500000, // wood
400000, // wood
1500000, // brick
4200000, // metal
0, // all others
};
// 1024 * level * (1+(level-1)/80.)
static const constexpr uint32_t generator_input_per1024[20] = {
1024, 2073, 3148, 4249, 5376, 6528, 7705, 8908, 10137, 11392, 12672, 13977, 15308, 16665, 18048, 19456, 20889, 22348, 23833, 25344
};
// 1024 * level * (1+(level-1)/80.) * 1.004 ** (level-1)
static const constexpr uint32_t generator_output_per1024[20] = {
1024, 2081, 3174, 4300, 5462, 6659, 7892, 9161, 10466, 11808, 13188, 14605, 16060, 17553, 19085, 20656, 22267, 23818, 25609, 27341
};
namespace cc
{
@ -83,6 +96,11 @@ const char *get_role_name(uint8_t role)
case ROLE_RESIDENTIAL3: return "luxury residential";
case ROLE_MILITARY: return "military";
case ROLE_CULTURAL: return "cultural";
case ROLE_STONECUTTER: return "stonecutter";
case ROLE_SAWMILL: return "sawmill";
case ROLE_KILN: return "kiln";
case ROLE_SMELTER: return "smelter";
case ROLE_WORKFORCE: return "workforce";
default: MERROR("Invalid role: " << (unsigned)role); return "INVALID ROLE";
}
}
@ -174,7 +192,96 @@ bool get_build_settings_requirements(uint32_t x0, uint32_t y0, uint32_t x1, uint
budget.push_back(std::make_pair(2, scale_with_economic_power(area_factor * 3, economic_power)));
budget.push_back(std::make_pair(ITEM_LABOUR, num_blocks(budget) * get_build_labour_cost_for_height(10)));
return true;
#warning TODO
case ROLE_STONECUTTER:
budget.push_back(std::make_pair(1, scale_with_economic_power(area_factor * 6, economic_power)));
budget.push_back(std::make_pair(2, scale_with_economic_power(area_factor * 6, economic_power)));
budget.push_back(std::make_pair(ITEM_LABOUR, num_blocks(budget) * get_build_labour_cost_for_height(15)));
return true;
case ROLE_SAWMILL:
budget.push_back(std::make_pair(1, scale_with_economic_power(area_factor * 4, economic_power)));
budget.push_back(std::make_pair(2, scale_with_economic_power(area_factor * 6, economic_power)));
budget.push_back(std::make_pair(ITEM_LABOUR, num_blocks(budget) * get_build_labour_cost_for_height(10)));
return true;
case ROLE_KILN:
budget.push_back(std::make_pair(1, scale_with_economic_power(area_factor * 6, economic_power)));
budget.push_back(std::make_pair(2, scale_with_economic_power(area_factor * 6, economic_power)));
budget.push_back(std::make_pair(ITEM_LABOUR, num_blocks(budget) * get_build_labour_cost_for_height(10)));
return true;
case ROLE_SMELTER:
budget.push_back(std::make_pair(1, scale_with_economic_power(area_factor * 12, economic_power)));
budget.push_back(std::make_pair(2, scale_with_economic_power(area_factor * 6, economic_power)));
budget.push_back(std::make_pair(ITEM_LABOUR, num_blocks(budget) * get_build_labour_cost_for_height(18)));
return true;
case ROLE_WORKFORCE:
budget.push_back(std::make_pair(1, scale_with_economic_power(area_factor * 2, economic_power)));
budget.push_back(std::make_pair(2, scale_with_economic_power(area_factor * 6, economic_power)));
budget.push_back(std::make_pair(ITEM_LABOUR, num_blocks(budget) * get_build_labour_cost_for_height(5)));
return true;
default:
MERROR("Unsupported role: " << (unsigned)role);
return false;
}
}
bool get_building_cost_production(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint8_t role, uint32_t economic_power, uint64_t &monetary_cost, std::vector<std::pair<uint32_t, uint32_t>> &cost, std::vector<std::pair<uint32_t, uint32_t>> &production)
{
monetary_cost = 0;
cost.clear();
production.clear();
const uint32_t w = x1 - x0 + 1;
const uint32_t h = y1 - y0 + 1;
const uint32_t area_factor = w * h;
auto scale_generator_input_with_economic_power = [](uint32_t value, uint32_t economic_power) -> uint32_t {
const uint32_t level = economic_power / 50;
return value * (uint64_t)generator_input_per1024[std::max<uint32_t>(1, std::min<uint32_t>(20, level)) - 1] / 16384;
};
auto scale_generator_output_with_economic_power = [](uint32_t value, uint32_t economic_power) -> uint32_t {
const uint32_t level = economic_power / 50;
return value * (uint64_t)generator_output_per1024[std::max<uint32_t>(1, std::min<uint32_t>(20, level)) - 1] / 16384;
};
switch (role)
{
case ROLE_EMPTY:
case ROLE_AGRICULTURAL:
case ROLE_CRAFT:
case ROLE_INDUSTRIAL:
case ROLE_COMMERCIAL:
case ROLE_RESIDENTIAL1:
case ROLE_RESIDENTIAL2:
case ROLE_RESIDENTIAL3:
case ROLE_MILITARY:
case ROLE_CULTURAL:
return true;
case ROLE_STONECUTTER:
cost.push_back(std::make_pair(ITEM_BASIC_WOOD, scale_generator_input_with_economic_power(area_factor * 15, economic_power)));
cost.push_back(std::make_pair(ITEM_BASIC_STONE, scale_generator_input_with_economic_power(area_factor * 2, economic_power)));
cost.push_back(std::make_pair(ITEM_LABOUR, scale_generator_input_with_economic_power(area_factor * 15, economic_power)));
production.push_back(std::make_pair(ITEM_BASIC_WOOD, scale_generator_output_with_economic_power(area_factor * 15, economic_power)));
return true;
case ROLE_SAWMILL:
cost.push_back(std::make_pair(ITEM_BASIC_WOOD, scale_generator_input_with_economic_power(area_factor * 1, economic_power)));
cost.push_back(std::make_pair(ITEM_BASIC_STONE, scale_generator_input_with_economic_power(area_factor * 1, economic_power)));
cost.push_back(std::make_pair(ITEM_LABOUR, scale_generator_input_with_economic_power(area_factor * 15, economic_power)));
production.push_back(std::make_pair(ITEM_BASIC_WOOD, scale_generator_output_with_economic_power(area_factor * 20, economic_power)));
return true;
case ROLE_KILN:
cost.push_back(std::make_pair(ITEM_BASIC_WOOD, scale_generator_input_with_economic_power(area_factor * 12, economic_power)));
cost.push_back(std::make_pair(ITEM_BASIC_STONE, scale_generator_input_with_economic_power(area_factor * 5, economic_power)));
cost.push_back(std::make_pair(ITEM_LABOUR, scale_generator_input_with_economic_power(area_factor * 18, economic_power)));
production.push_back(std::make_pair(ITEM_BASIC_BRICK, scale_generator_output_with_economic_power(area_factor * 12, economic_power)));
return true;
case ROLE_SMELTER:
cost.push_back(std::make_pair(ITEM_BASIC_WOOD, scale_generator_input_with_economic_power(area_factor * 24, economic_power)));
cost.push_back(std::make_pair(ITEM_BASIC_STONE, scale_generator_input_with_economic_power(area_factor * 10, economic_power)));
cost.push_back(std::make_pair(ITEM_LABOUR, scale_generator_input_with_economic_power(area_factor * 45, economic_power)));
production.push_back(std::make_pair(ITEM_BASIC_METAL, scale_generator_output_with_economic_power(area_factor * 12, economic_power)));
return true;
case ROLE_WORKFORCE:
monetary_cost = 100000000000;
production.push_back(std::make_pair(ITEM_LABOUR, scale_generator_output_with_economic_power(area_factor * 100, economic_power)));
return true;
default:
MERROR("Unsupported role: " << (unsigned)role);
return false;

View File

@ -44,6 +44,7 @@ const char *get_role_name(uint8_t role);
const char *get_item_name(uint32_t idx);
uint64_t get_last_resort_price(uint32_t idx);
bool get_build_settings_requirements(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint8_t role, uint32_t economic_power, std::vector<std::pair<uint32_t, uint32_t>> &budget);
bool get_building_cost_production(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint8_t role, uint32_t economic_power, uint64_t &monetary_cost, std::vector<std::pair<uint32_t, uint32_t>> &cost, std::vector<std::pair<uint32_t, uint32_t>> &production);
bool get_flag(const cryptonote::BlockchainDB &db, uint32_t x, uint32_t y, uint32_t &id);
bool has_flag(const cryptonote::BlockchainDB &db, uint32_t x, uint32_t y);
bool get_new_flag_cost(uint32_t ex0, uint32_t ey0, uint32_t ex1, uint32_t ey1, uint64_t &cost);

View File

@ -74,6 +74,15 @@ bool cc_command_handler_game_update::check(const cryptonote::BlockchainDB &db, c
}
sum += (uint64_t)b.delta;
}
for (const auto &b: c.item_balances)
{
if (b.delta == std::numeric_limits<int64_t>::min())
{
MERROR("Max negative amount not allowed");
return false;
}
sum += (uint64_t)b.delta;
}
if (sum != 0)
{
MERROR("Balance changes do not balance");
@ -122,6 +131,23 @@ static bool execute_city(cryptonote::BlockchainDB &db, const cryptonote::cc_comm
db.set_cc_flag_active(id, true);
for (uint32_t id: city.deactivated)
db.set_cc_flag_active(id, false);
account = 0;
uint32_t item = 0xffffffffu;
for (const auto &adj: city.item_balances)
{
uint32_t item_balances[NUM_ITEMS];
account += adj.delta_account;
if (adj.delta_account == 0)
item = 0xffffffffu;
item += adj.delta_item + 1;
if (!db.get_cc_account_item_balances(account, item_balances))
{
MERROR("Failed to get account " << account << " item balances");
return false;
}
item_balances[item] += adj.delta;
db.set_cc_account_item_balances(account, item_balances);
}
cryptonote::cc_shares_data_t sd;
CHECK_AND_ASSERT_MES(db.get_cc_shares(city.city_id, db.height() - 1, sd), false, "Failed to get shares data");
@ -171,6 +197,24 @@ static bool revert_city(cryptonote::BlockchainDB &db, const cryptonote::cc_comma
}
db.set_cc_account_balance(account, balance - adj.delta);
}
account = 0;
uint32_t item = 0xffffffffu;
for (const auto &adj: city.item_balances)
{
uint32_t item_balances[NUM_ITEMS];
account += adj.delta_account;
if (adj.delta_account == 0)
item = 0xffffffffu;
item += adj.delta_item + 1;
if (!db.get_cc_account_item_balances(account, item_balances))
{
MERROR("Failed to get account " << account << " item balances");
return false;
}
item_balances[item] -= adj.delta;
db.set_cc_account_item_balances(account, item_balances);
}
auto restore = [&db](const cryptonote::cc_command_game_update_t::flag_t &f, bool building_only)->bool {
uint32_t city_ox, city_oy;
CHECK_AND_ASSERT_MES(db.get_cc_city_origin(f.city, city_ox, city_oy), false, "Failed to get city origin");

View File

@ -70,6 +70,16 @@ enum BuildingRole
#define PAYOUT_MILITARY 0
ROLE_CULTURAL,
#define PAYOUT_CULTURAL 0
ROLE_STONECUTTER,
#define PAYOUT_STONECUTTER 0
ROLE_SAWMILL,
#define PAYOUT_SAWMILL 0
ROLE_KILN,
#define PAYOUT_KILN 0
ROLE_SMELTER,
#define PAYOUT_SMELTER 0
ROLE_WORKFORCE,
#define PAYOUT_WORKFORCE 0
NUM_ROLES
};
@ -77,6 +87,10 @@ enum Item
{
ITEM_NONE = 0,
ITEM_FIRST_BLOCK = 1,
ITEM_BASIC_STONE = 1,
ITEM_BASIC_WOOD = 2,
ITEM_BASIC_BRICK = 3,
ITEM_BASIC_METAL = 4,
ITEM_LAST_BLOCK = 255,
ITEM_LABOUR,
NUM_ITEMS

View File

@ -78,6 +78,7 @@ static void add_city(BlockchainDB &db, cc_command_game_update_t &cg, uint32_t ci
Quadtree<FlagState> q;
std::vector<std::shared_ptr<FlagState>> v;
std::map<uint32_t, int64_t> balance_deltas;
std::map<std::pair<uint32_t, uint32_t>, int32_t> item_balance_deltas;
std::map<uint32_t, int32_t> repair_deltas;
// gather all flags
@ -178,13 +179,71 @@ static void add_city(BlockchainDB &db, cc_command_game_update_t &cg, uint32_t ci
}
const uint32_t score = cc::calculate_influence(flag.role, flag.cumulative_influence);
const bool active = score > 0;
bool active = score > 0;
uint64_t monetary_cost = 0;
std::vector<std::pair<uint32_t, uint32_t>> costs, production;
uint32_t item_balances[NUM_ITEMS];
if (active)
{
CHECK_AND_ASSERT_THROW_MES(db.get_cc_account_item_balances(flag.owner, item_balances), "Failed to get item balances");
CHECK_AND_ASSERT_THROW_MES(cc::get_building_cost_production(flag.x0, flag.y0, flag.x1, flag.y1, flag.role, flag.economic_power, monetary_cost, costs, production),
"Failed to get flag cost/production");
if (monetary_cost > 0)
{
uint64_t balance;
CHECK_AND_ASSERT_THROW_MES(db.get_cc_account_balance(flag.owner, balance), "Failed to get balance");
if (balance < monetary_cost)
{
MINFO("Flag " << flag.id << " deactivated due to not enough money to tick");
active = false;
}
}
if (active && !costs.empty())
{
for (const auto &e: costs)
{
if (item_balances[e.first] < e.second)
{
MINFO("Flag " << flag.id << " deactivated due to not enough items of type " << cc::get_item_name(e.first) << " to tick");
active = false;
break;
}
}
}
}
if (active != flag.active)
{
flag.active = active;
(active ? cg.cities.back().activated : cg.cities.back().deactivated).push_back(flag.id);
db.set_cc_flag_active(flag.id, active);
}
if (active)
{
if (monetary_cost)
{
MINFO("Flag " << flag.id << " pays " << cryptonote::print_money(monetary_cost) << " to tick");
uint64_t balance;
CHECK_AND_ASSERT_THROW_MES(db.get_cc_account_balance(flag.owner, balance), "Failed to get balance");
balance -= monetary_cost;
db.set_cc_account_balance(flag.owner, balance);
balance_deltas[flag.owner] -= monetary_cost;
balance_deltas[treasury] += monetary_cost;
}
for (const auto &e: costs)
{
MINFO("Flag " << flag.id << " pays " << e.second << " " << cc::get_item_name(e.first));
item_balance_deltas[std::make_pair(flag.owner, e.first)] -= e.second;
}
for (const auto &e: production)
{
MINFO("Flag " << flag.id << " receives " << e.second << " " << cc::get_item_name(e.first));
item_balance_deltas[std::make_pair(flag.owner, e.first)] += e.second;
}
}
}
// decay
@ -241,7 +300,12 @@ static void add_city(BlockchainDB &db, cc_command_game_update_t &cg, uint32_t ci
treasury_balance * PAYOUT_RESIDENTIAL2,
treasury_balance * PAYOUT_RESIDENTIAL3,
treasury_balance * PAYOUT_MILITARY,
treasury_balance * PAYOUT_CULTURAL
treasury_balance * PAYOUT_CULTURAL,
treasury_balance * PAYOUT_STONECUTTER,
treasury_balance * PAYOUT_SAWMILL,
treasury_balance * PAYOUT_KILN,
treasury_balance * PAYOUT_SMELTER,
treasury_balance * PAYOUT_WORKFORCE,
};
uint64_t total_shares[NUM_ROLES] = {0};
uint64_t share_payout[NUM_ROLES] = {0};
@ -310,6 +374,19 @@ static void add_city(BlockchainDB &db, cc_command_game_update_t &cg, uint32_t ci
cg.cities.back().repair.push_back({e.first - id - 1, e.second});
id = e.first;
}
id = 0;
uint32_t id2 = 0xffffffff;
for (const auto &e: item_balance_deltas)
{
if (e.second == 0)
continue;
CHECK_AND_ASSERT_THROW_MES(e.first.first >= id+1 || (e.first.first == id && e.first.second >= id2+1), "Unsorted IDs in item_balance_deltas");
if (e.first.first != id)
id2 = 0xffffffff;
cg.cities.back().item_balances.push_back({e.first.first - id, e.first.second - id2 - 1, e.second});
id = e.first.first;
id2 = e.first.second;
}
cryptonote::cc_shares_data_t sd;
CHECK_AND_ASSERT_THROW_MES(db.get_cc_shares(city, db.height(), sd), "Failed to get shares data");

View File

@ -60,40 +60,60 @@ static const Rules influence_rules[NUM_ROLES] =
{},
// AGR
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL
{{ 0, 0, 0, 0, 0, 0, 0, 0, BONUS(1), 0, }},
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL STO SAW KILN SME WOR
{{ 0, 0, 0, 0, 0, 0, 0, 0, BONUS(1), 0, 0, 0, 0, 0, 0 }},
// CRAFT
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL
{{ 0, NEED(1), 0, 0, 0, BONUS(1), 0, 0, BONUS(1), 0, }},
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL STO SAW KILN SME WOR
{{ 0, NEED(1), 0, 0, 0, BONUS(1), 0, 0, BONUS(1), 0, BONUS(1), BONUS(1), 0, 0, 0 }},
// IND
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL
{{ 0, NEED(1), 0, BONUS(2), BONUS(1), BONUS(1), 0, 0, BONUS(1), 0, }},
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL STO SAW KILN SME WOR
{{ 0, NEED(1), 0, BONUS(2), BONUS(1), BONUS(1), 0, 0, BONUS(1), 0, BONUS(1), BONUS(1), BONUS(1), BONUS(1) }},
// COM
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL
{{ 0, NEED(1), NEED(1), 0, 0, 0, 0, 0, BONUS(1)|PENALTY(3), 0, }},
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL STO SAW KILN SME WOR
{{ 0, NEED(1), NEED(1), 0, 0, 0, 0, 0, BONUS(1)|PENALTY(3), 0, 0, 0, 0, 0, 0 }},
// RES1
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL
{{ 0, 0, 0, PENALTY(1), NEED(1), BONUS(1), BONUS(2), BONUS(1), BONUS(1)|PENALTY(3), BONUS(2), }},
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL STO SAW KILN SME WOR
{{ 0, 0, 0, PENALTY(1), NEED(1), BONUS(1), BONUS(2), BONUS(1), BONUS(1)|PENALTY(3), BONUS(2), 0, 0, 0, 0, 0 }},
// RES2
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL
{{ 0, 0, BONUS(1), PENALTY(3), NEED(1), 0, BONUS(3), BONUS(1), BONUS(1)|PENALTY(3), BONUS(2), }},
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL STO SAW KILN SME WOR
{{ 0, 0, BONUS(1), PENALTY(3), NEED(1), 0, BONUS(3), BONUS(1), BONUS(1)|PENALTY(3), BONUS(2), 0, 0, 0, 0, 0 }},
// RES3
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL
{{ 0, 0, 0, PENALTY(99), NEED(1), PENALTY(1), 0, 0, BONUS(1)|PENALTY(3), BONUS(4), }},
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL STO SAW KILN SME WOR
{{ 0, 0, 0, PENALTY(99), NEED(1), PENALTY(1), 0, 0, BONUS(1)|PENALTY(3), BONUS(4), 0, 0, 0, 0, 0 }},
// MIL
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL
{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }},
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL STO SAW KILN SME WOR
{{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }},
// CUL
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL
{{ 0, 0, 0, PENALTY(99), 0, 0, 0, 0, PENALTY(99), 0, }},
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL STO SAW KILN SME WOR
{{ 0, 0, 0, PENALTY(99), 0, 0, 0, 0, PENALTY(99), 0, 0, 0, 0, 0, 0 }},
// STO
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL STO SAW KILN SME WOR
{{ 0, 0, 0, 0, 0, 0, 0, 0, BONUS(1), 0, 0, 0, 0, 0, 0 }},
// SAW
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL STO SAW KILN SME WOR
{{ 0, 0, 0, 0, 0, 0, 0, 0, BONUS(1), 0, 0, 0, 0, 0, 0 }},
// KILN
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL STO SAW KILN SME WOR
{{ 0, 0, 0, 0, 0, 0, 0, 0, BONUS(1), 0, 0, 0, 0, 0, 0 }},
// SME
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL STO SAW KILN SME WOR
{{ 0, 0, 0, 0, 0, 0, 0, 0, BONUS(1), 0, 0, 0, 0, 0, 0 }},
// WOR
// AGR CRAFT IND COM RES1 RES2 RES3 MIL CUL STO SAW KILN SME WOR
{{ 0, 0, 0, 0, 0, 0, 0, 0, BONUS(1), 0, 0, 0, 0, 0, 0 }},
};
namespace cc
@ -135,6 +155,13 @@ uint32_t get_cc_influence(uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, ui
case ROLE_CULTURAL:
base *= 4;
break;
case ROLE_STONECUTTER:
case ROLE_SAWMILL:
case ROLE_KILN:
case ROLE_SMELTER:
case ROLE_WORKFORCE:
base *= 8;
break;
}
return base / 80000;
}

View File

@ -29,6 +29,7 @@
#pragma once
#include <stdint.h>
#include <algorithm>
namespace cc
{

View File

@ -287,6 +287,19 @@ namespace cryptonote
END_SERIALIZE()
};
struct item_balance_t
{
uint32_t delta_account;
uint32_t delta_item;
int64_t delta;
BEGIN_SERIALIZE_OBJECT()
VARINT_FIELD(delta_account)
VARINT_FIELD(delta_item)
VARINT_FIELD_SIGNED(delta)
END_SERIALIZE()
};
struct shares_t
{
uint8_t role;
@ -307,6 +320,7 @@ namespace cryptonote
std::vector<flag_t> defaulted;
std::vector<balance_t> balances;
std::vector<repair_t> repair;
std::vector<item_balance_t> item_balances;
std::vector<uint32_t> activated;
std::vector<uint32_t> deactivated;
std::vector<shares_t> shares;
@ -317,6 +331,7 @@ namespace cryptonote
FIELD(defaulted)
FIELD(balances)
FIELD(repair)
FIELD(item_balances)
FIELD(activated)
FIELD(deactivated)
END_SERIALIZE()

View File

@ -379,6 +379,12 @@ void UIBuildingSettingsDialog::SetBudget(const Map *map, const GameState *gameSt
AddRow(rightLayout, "Estimated cost\n(at full price)", cryptonote::print_money(last_resort_cost).c_str());
AddRow(rightLayout, "Influence", String(cc::get_cc_influence(flag->x0, flag->y0, flag->x1, flag->y1, role, economic_power, flag->potential)));
Text *t = new Text(context_);
t->SetText("Recurring");
t->SetTextAlignment(HA_CENTER);
rightLayout->AddChild(t);
t->SetStyleAuto(style);
const CityState &city = gameState->cityState;
const uint32_t shares = get_shares(map, flag, role, economic_power);
uint64_t den = city.last_payout_shares[role];
@ -392,6 +398,28 @@ void UIBuildingSettingsDialog::SetBudget(const Map *map, const GameState *gameSt
const uint64_t estimated_income = shares * city.last_payout[role] / den;
AddRow(rightLayout, "Estimated income", cryptonote::print_money(estimated_income).c_str());
}
uint64_t monetary_cost = 0;
std::vector<std::pair<uint32_t, uint32_t>> costs, production;
cc::get_building_cost_production(flag->x0, flag->y0, flag->x1, flag->y1, role, economic_power, monetary_cost, costs, production);
if (monetary_cost || !costs.empty() || !production.empty())
{
t = new Text(context_);
t->SetText("Production costs:");
rightLayout->AddChild(t);
t->SetStyleAuto(style);
if (monetary_cost > 0)
AddRow(rightLayout, "Money", cryptonote::print_money(monetary_cost).c_str());
for (const auto &e: costs)
AddRow(rightLayout, cc::get_item_name(e.first), String(e.second));
t = new Text(context_);
t->SetText("Production:");
rightLayout->AddChild(t);
t->SetStyleAuto(style);
for (const auto &e: production)
AddRow(rightLayout, cc::get_item_name(e.first), String(e.second));
}
}
void UIBuildingSettingsDialog::RefreshBudget(const Map *map, const GameState *gameState)

View File

@ -458,7 +458,7 @@ bool gen_cc_tx_validation_base::generate_with(std::vector<test_event_entry>& eve
cryptonote::cc_command_buy_items_t buy_blocks;
buy_blocks.cc_account = cc_account;
static const uint32_t buy_amounts[] = { 1, STARTING_BLOCK_1, 2, STARTING_BLOCK_2, ITEM_LABOUR, STARTING_LABOUR };
static const uint32_t buy_amounts[] = { ITEM_BASIC_STONE, STARTING_BLOCK_1, ITEM_BASIC_WOOD, STARTING_BLOCK_2, ITEM_LABOUR, STARTING_LABOUR };
for (size_t i = 0; i < sizeof(buy_amounts) / sizeof(buy_amounts[0]); i += 2)
{
cryptonote::cc_command_buy_items_t::entry_t e;
@ -1462,13 +1462,13 @@ bool gen_cc_tx_valid_cc_bare_buy_blocks::generate(std::vector<test_event_entry>&
blocks_cost += cc::get_last_resort_price(e.type) * e.amount;
cmds.push_back(std::make_tuple("", cmd, BARE_TX_FEE));
e.type = 2;
e.type = ITEM_BASIC_WOOD;
e.amount = 180;
cmd.entries[0] = e;
blocks_cost += cc::get_last_resort_price(e.type) * e.amount;
cmds.push_back(std::make_tuple("", cmd, BARE_TX_FEE));
e.type = 1;
e.type = ITEM_BASIC_STONE;
e.amount = 40;
cmd.entries[0] = e;
blocks_cost += cc::get_last_resort_price(e.type) * e.amount;
@ -1489,7 +1489,7 @@ bool gen_cc_tx_valid_cc_bare_buy_blocks::check_final_state(cryptonote::core &c,
&& expect_cc_account(c, 6, 10, initial_funding[6])
&& expect_cc_account(c, 7, 11, initial_funding[7])
&& expect_treasury_balance(c, expected_treasury_balance + blocks_cost)
&& expect_item_balances(c, 4, {std::make_pair(1, 120), std::make_pair(2, 180)});
&& expect_item_balances(c, 4, {std::make_pair(ITEM_BASIC_STONE, 120), std::make_pair(ITEM_BASIC_WOOD, 180)});
}
bool gen_cc_tx_invalid_cc_bare_buy_blocks_type_0::generate(std::vector<test_event_entry>& events) const
@ -1520,7 +1520,7 @@ bool gen_cc_tx_invalid_cc_bare_buy_blocks_amount_0::generate(std::vector<test_ev
cryptonote::cc_command_buy_items_t cmd;
cmd.cc_account = 4;
cryptonote::cc_command_buy_items_t::entry_t e;
e.type = 1;
e.type = ITEM_BASIC_STONE;
e.amount = 0;
cmd.entries.push_back(e);
cmds.push_back(std::make_tuple("", cmd, BARE_TX_FEE));
@ -1538,7 +1538,7 @@ bool gen_cc_tx_invalid_cc_bare_buy_blocks_balance_overflow::generate(std::vector
cryptonote::cc_command_buy_items_t cmd;
cmd.cc_account = 4;
cryptonote::cc_command_buy_items_t::entry_t e;
e.type = 1;
e.type = ITEM_BASIC_STONE;
e.amount = 0x1fffffff;
cmd.entries.push_back(e);
cmds.push_back(std::make_tuple("", cmd, BARE_TX_FEE));
@ -1556,10 +1556,10 @@ bool gen_cc_tx_invalid_cc_bare_buy_blocks_balance_overflow_in_aggregate::generat
cryptonote::cc_command_buy_items_t cmd;
cmd.cc_account = 4;
cryptonote::cc_command_buy_items_t::entry_t e;
e.type = 1;
e.type = ITEM_BASIC_STONE;
e.amount = 30000000;
cmd.entries.push_back(e);
e.type = 2;
e.type = ITEM_BASIC_WOOD;
e.amount = 30000000;
cmd.entries.push_back(e);
cmds.push_back(std::make_tuple("", cmd, BARE_TX_FEE));
@ -1577,7 +1577,7 @@ bool gen_cc_tx_invalid_cc_bare_buy_blocks_duplicate_type::generate(std::vector<t
cryptonote::cc_command_buy_items_t cmd;
cmd.cc_account = 4;
cryptonote::cc_command_buy_items_t::entry_t e;
e.type = 1;
e.type = ITEM_BASIC_STONE;
e.amount = 0x100;
cmd.entries.push_back(e);
cmd.entries.push_back(e);
@ -1597,7 +1597,7 @@ bool gen_cc_tx_valid_cc_bare_trade_bid_blocks::generate(std::vector<test_event_e
trade.cc_account = 4;
trade.bid = true;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -1632,7 +1632,7 @@ bool gen_cc_tx_valid_cc_bare_trade_offer_blocks::generate(std::vector<test_event
trade.cc_account = 4;
trade.bid = false;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -1667,7 +1667,7 @@ bool gen_cc_tx_invalid_cc_bare_trade_bid_invalid_type::generate(std::vector<test
trade.cc_account = 4;
trade.bid = true;
trade.type = 0xffffff;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -1730,7 +1730,7 @@ bool gen_cc_tx_invalid_cc_bare_trade_bid_expired::generate(std::vector<test_even
trade.cc_account = 4;
trade.bid = true;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 10;
@ -1751,7 +1751,7 @@ bool gen_cc_tx_invalid_cc_bare_trade_bid_not_enough_money::generate(std::vector<
trade.cc_account = 4;
trade.bid = true;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 1;
trade.price = (uint64_t)1000000000000000;
trade.expiration = 1000;
@ -1772,7 +1772,7 @@ bool gen_cc_tx_invalid_cc_bare_trade_bid_amount_0::generate(std::vector<test_eve
trade.cc_account = 4;
trade.bid = true;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 0;
trade.price = 1000;
trade.expiration = 1000;
@ -1793,7 +1793,7 @@ bool gen_cc_tx_invalid_cc_bare_trade_offer_amount_0::generate(std::vector<test_e
trade.cc_account = 4;
trade.bid = false;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 0;
trade.price = 1000;
trade.expiration = 1000;
@ -1814,7 +1814,7 @@ bool gen_cc_tx_valid_cc_bare_match_blocks_matching_amounts::generate(std::vector
trade.bid = false;
trade.cc_account = 4;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -1824,7 +1824,7 @@ bool gen_cc_tx_valid_cc_bare_match_blocks_matching_amounts::generate(std::vector
trade.bid = true;
trade.cc_account = 5;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -1861,8 +1861,8 @@ bool gen_cc_tx_valid_cc_bare_match_blocks_matching_amounts::check_final_state(cr
&& expect_cc_account(c, 6, 10, initial_funding[6])
&& expect_cc_account(c, 7, 11, initial_funding[7])
&& expect_treasury_balance(c, expected_treasury_balance)
&& expect_item_balances(c, 4, {std::make_pair(1, STARTING_BLOCK_1 - 20), std::make_pair(2, STARTING_BLOCK_2), std::make_pair(ITEM_LABOUR, STARTING_LABOUR)})
&& expect_item_balances(c, 5, {std::make_pair(1, 20)});
&& expect_item_balances(c, 4, {std::make_pair(ITEM_BASIC_STONE, STARTING_BLOCK_1 - 20), std::make_pair(ITEM_BASIC_WOOD, STARTING_BLOCK_2), std::make_pair(ITEM_LABOUR, STARTING_LABOUR)})
&& expect_item_balances(c, 5, {std::make_pair(ITEM_BASIC_STONE, 20)});
}
bool gen_cc_tx_valid_cc_bare_match_blocks_partial_buy::generate(std::vector<test_event_entry>& events) const
@ -1876,7 +1876,7 @@ bool gen_cc_tx_valid_cc_bare_match_blocks_partial_buy::generate(std::vector<test
trade.bid = false;
trade.cc_account = 4;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -1886,7 +1886,7 @@ bool gen_cc_tx_valid_cc_bare_match_blocks_partial_buy::generate(std::vector<test
trade.bid = true;
trade.cc_account = 5;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 12;
trade.price = 1000;
trade.expiration = 1000;
@ -1923,8 +1923,8 @@ bool gen_cc_tx_valid_cc_bare_match_blocks_partial_buy::check_final_state(crypton
&& expect_cc_account(c, 6, 10, initial_funding[6])
&& expect_cc_account(c, 7, 11, initial_funding[7])
&& expect_treasury_balance(c, expected_treasury_balance)
&& expect_item_balances(c, 4, {std::make_pair(1, STARTING_BLOCK_1 - 12), std::make_pair(2, STARTING_BLOCK_2), std::make_pair(ITEM_LABOUR, STARTING_LABOUR)})
&& expect_item_balances(c, 5, {std::make_pair(1, 12)});
&& expect_item_balances(c, 4, {std::make_pair(ITEM_BASIC_STONE, STARTING_BLOCK_1 - 12), std::make_pair(ITEM_BASIC_WOOD, STARTING_BLOCK_2), std::make_pair(ITEM_LABOUR, STARTING_LABOUR)})
&& expect_item_balances(c, 5, {std::make_pair(ITEM_BASIC_STONE, 12)});
}
bool gen_cc_tx_valid_cc_bare_match_blocks_partial_sell::generate(std::vector<test_event_entry>& events) const
@ -1938,7 +1938,7 @@ bool gen_cc_tx_valid_cc_bare_match_blocks_partial_sell::generate(std::vector<tes
trade.cc_account = 4;
trade.bid = false;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -1948,7 +1948,7 @@ bool gen_cc_tx_valid_cc_bare_match_blocks_partial_sell::generate(std::vector<tes
trade.cc_account = 5;
trade.bid = true;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 25;
trade.price = 1000;
trade.expiration = 1000;
@ -1985,8 +1985,8 @@ bool gen_cc_tx_valid_cc_bare_match_blocks_partial_sell::check_final_state(crypto
&& expect_cc_account(c, 6, 10, initial_funding[6])
&& expect_cc_account(c, 7, 11, initial_funding[7])
&& expect_treasury_balance(c, expected_treasury_balance)
&& expect_item_balances(c, 4, {std::make_pair(1, STARTING_BLOCK_1 - 20), std::make_pair(2, STARTING_BLOCK_2), std::make_pair(ITEM_LABOUR, STARTING_LABOUR)})
&& expect_item_balances(c, 5, {std::make_pair(1, 20)});
&& expect_item_balances(c, 4, {std::make_pair(ITEM_BASIC_STONE, STARTING_BLOCK_1 - 20), std::make_pair(ITEM_BASIC_WOOD, STARTING_BLOCK_2), std::make_pair(ITEM_LABOUR, STARTING_LABOUR)})
&& expect_item_balances(c, 5, {std::make_pair(ITEM_BASIC_STONE, 20)});
}
bool gen_cc_tx_invalid_cc_bare_match_0::generate(std::vector<test_event_entry>& events) const
@ -2000,7 +2000,7 @@ bool gen_cc_tx_invalid_cc_bare_match_0::generate(std::vector<test_event_entry>&
trade.cc_account = 4;
trade.bid = false;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -2010,7 +2010,7 @@ bool gen_cc_tx_invalid_cc_bare_match_0::generate(std::vector<test_event_entry>&
trade.cc_account = 5;
trade.bid = true;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -2046,7 +2046,7 @@ bool gen_cc_tx_invalid_cc_bare_match_more_than_trade::generate(std::vector<test_
trade.cc_account = 4;
trade.bid = false;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -2056,7 +2056,7 @@ bool gen_cc_tx_invalid_cc_bare_match_more_than_trade::generate(std::vector<test_
trade.cc_account = 5;
trade.bid = true;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -2092,7 +2092,7 @@ bool gen_cc_tx_invalid_cc_bare_trade_from_account_0::generate(std::vector<test_e
trade.cc_account = 4;
trade.bid = false;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -2126,7 +2126,7 @@ bool gen_cc_tx_invalid_cc_bare_match_non_trade_tx::generate(std::vector<test_eve
trade.cc_account = 5;
trade.bid = true;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -2160,7 +2160,7 @@ bool gen_cc_tx_invalid_cc_bare_match_duplicate_trade_tx::generate(std::vector<te
trade.cc_account = 4;
trade.bid = false;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -2170,7 +2170,7 @@ bool gen_cc_tx_invalid_cc_bare_match_duplicate_trade_tx::generate(std::vector<te
trade.cc_account = 5;
trade.bid = false;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 40;
trade.price = 1000;
trade.expiration = 1000;
@ -2205,7 +2205,7 @@ bool gen_cc_tx_valid_cc_bare_match_two_buys_from_one_sell::generate(std::vector<
trade.cc_account = 4;
trade.bid = false;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -2215,7 +2215,7 @@ bool gen_cc_tx_valid_cc_bare_match_two_buys_from_one_sell::generate(std::vector<
trade.cc_account = 5;
trade.bid = true;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 15;
trade.price = 1020;
trade.expiration = 1000;
@ -2225,7 +2225,7 @@ bool gen_cc_tx_valid_cc_bare_match_two_buys_from_one_sell::generate(std::vector<
trade.cc_account = 6;
trade.bid = true;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 5;
trade.price = 1050;
trade.expiration = 1000;
@ -2266,9 +2266,9 @@ bool gen_cc_tx_valid_cc_bare_match_two_buys_from_one_sell::check_final_state(cry
&& expect_cc_account(c, 6, 10, initial_funding[6])
&& expect_cc_account(c, 7, 11, initial_funding[7])
&& expect_treasury_balance(c, expected_treasury_balance)
&& expect_item_balances(c, 4, {std::make_pair(1, STARTING_BLOCK_1 - 20), std::make_pair(2, STARTING_BLOCK_2), std::make_pair(ITEM_LABOUR, STARTING_LABOUR)})
&& expect_item_balances(c, 5, {std::make_pair(1, 15)})
&& expect_item_balances(c, 6, {std::make_pair(1, 5)});
&& expect_item_balances(c, 4, {std::make_pair(ITEM_BASIC_STONE, STARTING_BLOCK_1 - 20), std::make_pair(ITEM_BASIC_WOOD, STARTING_BLOCK_2), std::make_pair(ITEM_LABOUR, STARTING_LABOUR)})
&& expect_item_balances(c, 5, {std::make_pair(ITEM_BASIC_STONE, 15)})
&& expect_item_balances(c, 6, {std::make_pair(ITEM_BASIC_STONE, 5)});
}
bool gen_cc_tx_valid_cc_bare_maker_no_fee::generate(std::vector<test_event_entry>& events) const
@ -2282,7 +2282,7 @@ bool gen_cc_tx_valid_cc_bare_maker_no_fee::generate(std::vector<test_event_entry
trade.bid = false;
trade.cc_account = 4;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -2303,7 +2303,7 @@ bool gen_cc_tx_invalid_cc_bare_taker_no_fee::generate(std::vector<test_event_ent
trade.bid = false;
trade.cc_account = 4;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;
@ -2313,7 +2313,7 @@ bool gen_cc_tx_invalid_cc_bare_taker_no_fee::generate(std::vector<test_event_ent
trade.bid = true;
trade.cc_account = 5;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 20;
trade.price = 1000;
trade.expiration = 1000;

View File

@ -1150,7 +1150,7 @@ TEST(cc_command, execute_trade_maker)
trade.cc_account = 4;
trade.bid = true;
trade.type = cryptonote::cc_command_trade_t::type_item;
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
trade.amount = 10;
trade.price = 10;
trade.expiration = 1000;
@ -1169,7 +1169,7 @@ TEST(cc_command, execute_trade_maker)
// bad id
trade.id = 0;
test_commands(false, setup, trade, "bad id");
trade.id = 1;
trade.id = ITEM_BASIC_STONE;
// bad expiration
trade.expiration = 1;
@ -1921,6 +1921,7 @@ TEST(cc_game, empty)
ASSERT_EQ(c.derelict.size(), 0);
ASSERT_EQ(c.repair.size(), 0);
ASSERT_EQ(c.balances.size(), 0);
ASSERT_EQ(c.item_balances.size(), 0);
}
TEST(cc_game, derelict)
@ -1986,6 +1987,70 @@ TEST(cc_game, derelict)
ASSERT_EQ(c3.repair.size(), 0);
}
TEST(cc_game, generator)
{
cryptonote::cc_command_game_update_t cmd;
TestDB *db = new TestDB();
cryptonote::BlockchainDB *bdb = db;
cc::add_init_state(*bdb);
cryptonote::cc_command_game_update_t cg, cg0 = cc::create_cc_game_update_command(*db);
cryptonote::keypair account_keys = cryptonote::keypair::generate(hw::get_device("default"));
uint32_t city_id = 0;
uint32_t account_id = bdb->allocate_new_cc_account(account_keys.pub);
uint32_t flag_id = bdb->allocate_new_cc_flag(NULL, account_id, city_id, 44, 45, 46, 47, 48, 49);
db->set_cc_flag_tiles(flag_id, {{},{},{},{4},{},{6},{},{0,1,2},{}});
db->set_cc_flag_repair(flag_id, 1000000);
bdb->set_cc_flag_role(flag_id, ROLE_SAWMILL, 100, 100);
cryptonote::cc_flag_data_t fd;
ASSERT_TRUE(db->get_cc_flag_data(flag_id, fd, NULL));
ASSERT_EQ(fd.role, ROLE_SAWMILL);
uint64_t monetary_cost;
std::vector<std::pair<uint32_t, uint32_t>> costs, production;
ASSERT_TRUE(cc::get_building_cost_production(fd.x0, fd.y0, fd.x1, fd.y1, fd.role, fd.economic_power, monetary_cost, costs, production));
ASSERT_EQ(monetary_cost, 0);
ASSERT_EQ(costs.size(), 3);
ASSERT_EQ(costs[0].first, ITEM_BASIC_WOOD);
ASSERT_GT(costs[0].second, 0);
ASSERT_EQ(costs[1].first, ITEM_BASIC_STONE);
ASSERT_GT(costs[1].second, 0);
ASSERT_EQ(costs[2].first, ITEM_LABOUR);
ASSERT_GT(costs[2].second, 0);
ASSERT_EQ(production.size(), 1);
ASSERT_EQ(production[0].first, ITEM_BASIC_WOOD);
ASSERT_GT(production[0].second, costs[0].second);
bdb->set_cc_account_balance(account_id, 1000000000000000);
uint32_t item_balances[NUM_ITEMS] = {0};
item_balances[ITEM_BASIC_WOOD] = 1000000;
item_balances[ITEM_BASIC_STONE] = 1000000;
item_balances[ITEM_LABOUR] = 1000000;
bdb->set_cc_account_item_balances(account_id, item_balances);
cg = cc::create_cc_game_update_command(*db);
ASSERT_EQ(cg.cities.size(), 1);
const auto &c0 = cg.cities[0];
ASSERT_EQ(c0.defaulted.size(), 0);
ASSERT_EQ(c0.derelict.size(), 0);
ASSERT_EQ(c0.repair.size(), 1);
ASSERT_EQ(c0.item_balances.size(), 3);
uint32_t item_id = 0xffffffffu;
ASSERT_EQ(c0.item_balances[0].delta_account, account_id);
item_id += c0.item_balances[0].delta_item + 1;
ASSERT_EQ(item_id, ITEM_BASIC_STONE);
ASSERT_LT(c0.item_balances[0].delta, 0);
ASSERT_EQ(c0.item_balances[1].delta_account, 0);
item_id += c0.item_balances[1].delta_item + 1;
ASSERT_EQ(item_id, ITEM_BASIC_WOOD);
ASSERT_GT(c0.item_balances[1].delta, 0);
ASSERT_EQ(c0.item_balances[2].delta_account, 0);
item_id += c0.item_balances[2].delta_item + 1;
ASSERT_EQ(item_id, ITEM_LABOUR);
ASSERT_LT(c0.item_balances[2].delta, 0);
}
TEST(cc, type_tags)
{
cryptonote::cc_command_t cmd;