forked from townforge/townforge
3641 lines
130 KiB
C++
3641 lines
130 KiB
C++
// Copyright (c) 2019, Crypto City
|
|
//
|
|
// All rights reserved.
|
|
//
|
|
// Redistribution and use in source and binary forms, with or without modification, are
|
|
// permitted provided that the following conditions are met:
|
|
//
|
|
// 1. Redistributions of source code must retain the above copyright notice, this list of
|
|
// conditions and the following disclaimer.
|
|
//
|
|
// 2. Redistributions in binary form must reproduce the above copyright notice, this list
|
|
// of conditions and the following disclaimer in the documentation and/or other
|
|
// materials provided with the distribution.
|
|
//
|
|
// 3. Neither the name of the copyright holder nor the names of its contributors may be
|
|
// used to endorse or promote products derived from this software without specific
|
|
// prior written permission.
|
|
//
|
|
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY
|
|
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
|
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
|
|
// THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
|
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
|
// PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
|
|
// STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
|
|
// THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
|
|
#include <vector>
|
|
#include <set>
|
|
#include "gtest/gtest.h"
|
|
#include "cryptonote_basic/cryptonote_basic.h"
|
|
#include "cc/cc.h"
|
|
#include "cc/cc_badge.h"
|
|
#include "cc/cc_palette.h"
|
|
#include "cc/cc_game_update.h"
|
|
#include "cryptonote_core/blockchain.h"
|
|
#include "cryptonote_core/cryptonote_core.h"
|
|
#include "cryptonote_core/tx_pool.h"
|
|
#include "blockchain_db/testdb.h"
|
|
#include "game/selection.h"
|
|
#include "game/magica.h"
|
|
#include "cc/rectanglizer.h"
|
|
#include "cc/cc_influence.h"
|
|
#include "cc/cc_discoveries.h"
|
|
#include "cc/cc_special_events.h"
|
|
#include "cc/cc_game_events.h"
|
|
#include "cc/cc_temperature.h"
|
|
#include "cc/cc_invitation.h"
|
|
#include "cc/cc_potential.h"
|
|
#include "cc/cc_terrain.h"
|
|
#include "cc/cc_food.h"
|
|
#include "cc/cc_calendar.h"
|
|
|
|
#define PREDEFINED_BULDINGS 1
|
|
|
|
namespace
|
|
{
|
|
|
|
class TestDB: public cryptonote::BaseTestDB
|
|
{
|
|
private:
|
|
struct block_t
|
|
{
|
|
size_t weight;
|
|
};
|
|
|
|
struct account_t
|
|
{
|
|
crypto::public_key public_key;
|
|
uint64_t balance;
|
|
std::map<uint32_t, uint32_t> item_balances;
|
|
std::vector<uint32_t> flags;
|
|
std::string name;
|
|
bool ignore;
|
|
|
|
bool operator==(const account_t &other) const { return public_key == other.public_key && balance == other.balance && item_balances == other.item_balances && flags == other.flags && name == other.name && ignore == other.ignore; }
|
|
};
|
|
|
|
struct city_t
|
|
{
|
|
bool in_use;
|
|
uint32_t seed;
|
|
uint32_t mayor;
|
|
uint32_t treasury;
|
|
std::string name;
|
|
bool ignore;
|
|
|
|
bool operator==(const city_t &other) const { return seed == other.seed && mayor == other.mayor && treasury == other.treasury && name == other.name && ignore == other.ignore; }
|
|
};
|
|
|
|
struct flag_t
|
|
{
|
|
bool in_use;
|
|
uint32_t owner;
|
|
uint32_t city;
|
|
uint8_t role;
|
|
uint32_t x0;
|
|
uint32_t y0;
|
|
uint32_t x1;
|
|
uint32_t y1;
|
|
uint32_t repair;
|
|
uint16_t base_height;
|
|
uint32_t elevation_bonus;
|
|
uint8_t potential[NUM_POTENTIALS];
|
|
uint32_t economic_power;
|
|
uint64_t construction_height;
|
|
std::map<uint32_t, uint32_t> budget;
|
|
std::vector<uint16_t> palette;
|
|
uint32_t active: 1;
|
|
uint32_t ignore: 1;
|
|
uint8_t fire_state;
|
|
std::string name;
|
|
|
|
flag_t(bool in_use, uint32_t owner, uint32_t city, uint8_t role, uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, uint32_t repair, uint16_t base_height, uint32_t elevation_bonus, const uint8_t potential[NUM_POTENTIALS], uint32_t economic_power, uint64_t construction_height, const std::map<uint32_t, uint32_t> &budget, const std::vector<uint16_t> &palette, bool active, bool ignore, uint8_t fire_state, const std::string &name): in_use(in_use), owner(owner), city(city), role(role), x0(x0), y0(y0), x1(x1), y1(y1), repair(repair), base_height(base_height), elevation_bonus(elevation_bonus), economic_power(economic_power), construction_height(construction_height), budget(budget), palette(palette), active(active), ignore(ignore), fire_state(fire_state), name(name)
|
|
{
|
|
memcpy(this->potential, potential, NUM_POTENTIALS);
|
|
}
|
|
flag_t(): in_use(false) {}
|
|
|
|
bool operator==(const flag_t &other) const { return owner == other.owner && city == other.city && role == other.role && x0 == other.x0 && y0 == other.y0 && x1 == other.x1 && y1 == other.y1 && repair == other.repair && base_height == other.base_height && elevation_bonus == other.elevation_bonus && !memcmp(potential, other.potential, NUM_POTENTIALS) && economic_power == other.economic_power && construction_height == other.construction_height && budget == other.budget && palette == other.palette && active == other.active && fire_state == other.fire_state && name == other.name && ignore == other.ignore; }
|
|
};
|
|
|
|
struct tile_t
|
|
{
|
|
std::vector<uint8_t> blocks;
|
|
|
|
bool operator==(const tile_t &other) const { return blocks == other.blocks; }
|
|
};
|
|
|
|
struct shares_t
|
|
{
|
|
uint64_t shares[NUM_ROLES];
|
|
uint64_t payout[NUM_ROLES];
|
|
|
|
bool operator==(const shares_t &other) const { for (int i = 0; i < NUM_ROLES; ++i) if (shares[i] != other.shares[i] || payout[i] != other.payout[i]) return false; return true; }
|
|
};
|
|
|
|
struct special_event_t
|
|
{
|
|
uint32_t event;
|
|
uint64_t start_height;
|
|
uint8_t duration;
|
|
|
|
bool operator==(const special_event_t &other) const { return event == other.event && start_height == other.start_height && duration == other.duration; }
|
|
};
|
|
|
|
struct custom_item_t
|
|
{
|
|
bool in_use;
|
|
uint32_t amount;
|
|
uint32_t creator;
|
|
uint64_t creation_height;
|
|
std::string name;
|
|
bool ignore;
|
|
bool is_group;
|
|
uint32_t group;
|
|
std::string pdesc;
|
|
std::string sdesc;
|
|
uint64_t user_data;
|
|
|
|
bool operator==(const custom_item_t &other) const { return amount == other.amount && name == other.name && creator == other.creator && creation_height == other.creation_height && is_group == other.is_group && group == other.group && pdesc == other.pdesc && sdesc == other.sdesc && ignore == other.ignore && user_data == other.user_data; }
|
|
};
|
|
|
|
static crypto::public_key DEAD_PKEY;
|
|
|
|
public:
|
|
struct state_t
|
|
{
|
|
std::vector<account_t> accounts;
|
|
std::vector<city_t> cities;
|
|
std::vector<flag_t> flags;
|
|
std::set<uint64_t> nonces;
|
|
std::map<std::pair<uint32_t, uint32_t>, tile_t> tiles;
|
|
std::map<uint64_t, uint32_t> cc_trade_used;
|
|
std::map<uint64_t, cryptonote::order_t<crypto::hash>> cc_orders;
|
|
std::map<uint64_t, shares_t> shares;
|
|
std::map<uint32_t, std::vector<special_event_t>> special_events;
|
|
std::vector<custom_item_t> items;
|
|
std::map<uint32_t, uint64_t> item_count;
|
|
|
|
bool operator==(const state_t &other) const
|
|
{
|
|
std::map<uint32_t, account_t> a0, a1;
|
|
std::map<uint32_t, city_t> c0, c1;
|
|
std::map<uint32_t, flag_t> f0, f1;
|
|
for (size_t i = 0; i < accounts.size(); ++i) if (accounts[i].public_key != DEAD_PKEY) a0[i] = accounts[i];
|
|
for (size_t i = 0; i < other.accounts.size(); ++i) if (other.accounts[i].public_key != DEAD_PKEY) a1[i] = accounts[i];
|
|
for (size_t i = 0; i < cities.size(); ++i) if (cities[i].in_use) c0[i] = cities[i];
|
|
for (size_t i = 0; i < other.cities.size(); ++i) if (other.cities[i].in_use) c1[i] = cities[i];
|
|
for (size_t i = 0; i < flags.size(); ++i) if (flags[i].in_use) f0[i] = flags[i];
|
|
for (size_t i = 0; i < other.flags.size(); ++i) if (other.flags[i].in_use) f1[i] = flags[i];
|
|
return nonces == other.nonces && a0 == a1 && c0 == c1 && f0 == f1 && tiles == other.tiles && cc_trade_used == other.cc_trade_used && cc_orders == other.cc_orders
|
|
&& shares == other.shares && special_events == other.special_events && items == other.items && item_count == other.item_count;
|
|
}
|
|
};
|
|
|
|
public:
|
|
TestDB()
|
|
{
|
|
cryptonote::keypair keys = cryptonote::keypair::generate(hw::get_device("default"));
|
|
DEAD_PKEY = keys.pub;
|
|
reset();
|
|
m_open = true;
|
|
}
|
|
|
|
state_t get_state() const { return state; }
|
|
|
|
virtual void add_block( const cryptonote::block& blk
|
|
, size_t block_weight
|
|
, uint64_t long_term_block_weight
|
|
, const cryptonote::difficulty_type& cumulative_difficulty
|
|
, const uint64_t& coins_generated
|
|
, uint64_t num_rct_outs
|
|
, const crypto::hash& blk_hash
|
|
) override {
|
|
blocks.push_back({block_weight});
|
|
}
|
|
virtual uint64_t height() const override { return blocks.size(); }
|
|
virtual crypto::hash get_block_hash_from_height(const uint64_t &height) const override {
|
|
crypto::hash hash = crypto::null_hash;
|
|
*(uint64_t*)&hash = height;
|
|
return hash;
|
|
}
|
|
virtual crypto::hash top_block_hash(uint64_t *block_height = NULL) const override {
|
|
uint64_t h = height();
|
|
crypto::hash top = crypto::null_hash;
|
|
if (h)
|
|
*(uint64_t*)&top = h - 1;
|
|
if (block_height)
|
|
*block_height = h - 1;
|
|
return top;
|
|
}
|
|
virtual cryptonote::block get_top_block() const override { cryptonote::block b; b.major_version = b.minor_version = 12; return b; }
|
|
virtual void pop_block(cryptonote::block &blk, std::vector<cryptonote::transaction> &txs) override { blocks.pop_back(); }
|
|
|
|
virtual void reset()
|
|
{
|
|
state = state_t();
|
|
state.accounts.push_back({DEAD_PKEY, 0, {}, {}});
|
|
state.flags.push_back({});
|
|
}
|
|
|
|
virtual uint64_t get_block_already_generated_coins(const uint64_t &height) const
|
|
{
|
|
uint64_t coins = 0;
|
|
for (uint64_t h = 0; h <= height; ++h)
|
|
coins += 10000000000 - h * 9999;
|
|
return coins;
|
|
}
|
|
|
|
// CC
|
|
virtual uint32_t allocate_new_cc_account(const crypto::public_key &public_key, const std::string &name, uint32_t inviting_account = 0) override
|
|
{
|
|
if (name.size() > MAX_CC_NAME_LENGTH)
|
|
throw std::runtime_error("Name is too long");
|
|
|
|
state.accounts.push_back({public_key, 0, {}, {}, name, false});
|
|
return state.accounts.size() - 1;
|
|
}
|
|
|
|
virtual bool lookup_cc_account(const crypto::public_key &public_key, uint32_t &id) const override
|
|
{
|
|
for (size_t i = 1; i < state.accounts.size(); ++i)
|
|
{
|
|
if (state.accounts[i].public_key == public_key)
|
|
{
|
|
id = i;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
virtual void delete_cc_account(uint32_t id) override
|
|
{
|
|
if (id > 0 && id < state.accounts.size())
|
|
{
|
|
state.accounts[id].public_key = DEAD_PKEY;
|
|
}
|
|
}
|
|
|
|
virtual uint32_t get_num_cc_accounts() const override
|
|
{
|
|
size_t c = 0;
|
|
for (size_t i = 1; i < state.accounts.size(); ++i)
|
|
if (state.accounts[i].public_key != DEAD_PKEY)
|
|
++c;
|
|
return c;
|
|
}
|
|
|
|
virtual bool get_cc_account_data(uint32_t id, cryptonote::cc_account_data_t &data) const override
|
|
{
|
|
if (id == 0 || id >= state.accounts.size() || state.accounts[id].public_key == DEAD_PKEY)
|
|
return false;
|
|
data.id = id;
|
|
data.public_key = state.accounts[id].public_key;
|
|
data.balance = state.accounts[id].balance;
|
|
data.item_balances = state.accounts[id].item_balances;
|
|
data.flags = state.accounts[id].flags;
|
|
data.name = state.accounts[id].name;
|
|
data.ignore = state.accounts[id].ignore;
|
|
return true;
|
|
}
|
|
|
|
virtual void set_cc_account_balance(uint32_t id, uint64_t balance) override
|
|
{
|
|
state.accounts[id].balance = balance;
|
|
}
|
|
|
|
virtual void set_cc_account_item_balances(uint32_t id, const std::map<uint32_t, uint32_t> &item_balances) override
|
|
{
|
|
state.accounts[id].item_balances = item_balances;
|
|
}
|
|
|
|
virtual void set_cc_account_ignore(uint32_t id, bool ignore) override
|
|
{
|
|
state.accounts[id].ignore = ignore;
|
|
}
|
|
|
|
virtual void add_cc_account_flag(uint32_t id, uint32_t flag_id) override
|
|
{
|
|
state.accounts[id].flags.push_back(flag_id);
|
|
}
|
|
|
|
virtual void remove_cc_account_flag(uint32_t id, uint32_t flag_id) override
|
|
{
|
|
auto i = std::find(state.accounts[id].flags.begin(), state.accounts[id].flags.end(), flag_id);
|
|
if (i != state.accounts[id].flags.end())
|
|
state.accounts[id].flags.erase(i);
|
|
}
|
|
|
|
virtual uint32_t allocate_new_cc_city(uint32_t seed, uint32_t mayor, const std::string &name) override
|
|
{
|
|
std::string treasury_name = (name.empty() ? ("City " + std::to_string(state.cities.size() - 1)) : name) + " treasury";
|
|
uint32_t treasury = allocate_new_cc_account(crypto::null_pkey, treasury_name);
|
|
state.cities.push_back({true, seed, mayor, treasury, name, false});
|
|
return state.cities.size() - 1;
|
|
}
|
|
|
|
virtual bool get_cc_city_data(uint32_t id, cryptonote::cc_city_data_t &ccd) const override
|
|
{
|
|
if (id >= state.cities.size() || !state.cities[id].in_use)
|
|
return false;
|
|
ccd.id = id;
|
|
ccd.seed = state.cities[id].seed;
|
|
ccd.treasury = state.cities[id].treasury;
|
|
ccd.mayor = state.cities[id].mayor;
|
|
ccd.max_level = 0;
|
|
ccd.specializations = 0;
|
|
ccd.ignore = false;
|
|
ccd.moose = NUM_STARTING_MOOSE;
|
|
ccd.bears = NUM_STARTING_BEARS;
|
|
return true;
|
|
}
|
|
|
|
virtual void delete_cc_city(uint32_t id) override
|
|
{
|
|
state.cities[id].in_use = false;
|
|
}
|
|
|
|
virtual uint32_t get_num_cc_cities() const override
|
|
{
|
|
size_t c = 0;
|
|
for (size_t i = 0; i < state.cities.size(); ++i)
|
|
if (state.cities[i].in_use)
|
|
++c;
|
|
return c;
|
|
}
|
|
|
|
virtual void set_cc_city_ignore(uint32_t id, bool ignore) override
|
|
{
|
|
state.cities[id].ignore = ignore;
|
|
}
|
|
|
|
virtual bool for_all_cc_cities(std::function<bool(const cryptonote::cc_city_data_t &ccd)> f) const override
|
|
{
|
|
for (size_t i = 0; i < state.cities.size(); ++i)
|
|
{
|
|
const auto &city = state.cities[i];
|
|
if (!city.in_use)
|
|
continue;
|
|
cryptonote::cc_city_data_t ccd{};
|
|
ccd.id = i;
|
|
ccd.seed = city.seed;
|
|
ccd.treasury = city.treasury;
|
|
ccd.mayor = city.mayor;
|
|
ccd.max_level = 0;
|
|
ccd.specializations = 0;
|
|
ccd.ignore = false;
|
|
ccd.moose = NUM_STARTING_MOOSE;
|
|
ccd.bears = NUM_STARTING_BEARS;
|
|
if (!f(ccd))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
virtual uint32_t allocate_new_cc_flag(const uint32_t *id, uint32_t owner_id, uint32_t city_id, uint32_t x0, uint32_t y0, uint32_t x1, uint32_t y1, const uint8_t potential[NUM_POTENTIALS], uint16_t base_height, uint32_t elevation_bonus) override
|
|
{
|
|
uint32_t actual_id;
|
|
if (!id)
|
|
{
|
|
state.flags.push_back({});
|
|
actual_id = state.flags.size() - 1;
|
|
}
|
|
else
|
|
{
|
|
actual_id = *id;
|
|
}
|
|
std::vector<uint16_t> palette;
|
|
cc::set_default_palette(palette, height());
|
|
state.flags[actual_id] = {true, owner_id, city_id, ROLE_EMPTY, x0, y0, x1, y1, 0, base_height, elevation_bonus, potential, 0, 0, {}, palette, false, false, 127, ""};
|
|
return actual_id;
|
|
}
|
|
|
|
virtual void delete_cc_flag(uint32_t id) override
|
|
{
|
|
for (uint32_t y = state.flags[id].y0; y <= state.flags[id].y1; ++y)
|
|
{
|
|
for (uint32_t x = state.flags[id].x0; x <= state.flags[id].x1; ++x)
|
|
{
|
|
auto it = state.tiles.find(std::make_pair(x, y));
|
|
if (it != state.tiles.end())
|
|
state.tiles.erase(it);
|
|
}
|
|
}
|
|
state.flags[id].in_use = false;
|
|
}
|
|
|
|
virtual bool for_all_cc_flags(std::function<bool(const cryptonote::cc_flag_data_t&)> f) const override
|
|
{
|
|
for (size_t i = 0; i < state.flags.size(); ++i)
|
|
{
|
|
const auto &flag = state.flags[i];
|
|
if (!flag.in_use)
|
|
continue;
|
|
cryptonote::cc_flag_data_t fd{};
|
|
fd.id = i;
|
|
fd.owner = flag.owner;
|
|
fd.city = flag.city;
|
|
fd.role = flag.role;
|
|
fd.x0 = flag.x0;
|
|
fd.y0 = flag.y0;
|
|
fd.x1 = flag.x1;
|
|
fd.y1 = flag.y1;
|
|
fd.repair = flag.repair;
|
|
fd.base_height = flag.base_height;
|
|
fd.elevation_bonus = flag.elevation_bonus;
|
|
memcpy(fd.potential, flag.potential, sizeof(flag.potential));
|
|
fd.economic_power = flag.economic_power;
|
|
fd.construction_height = flag.construction_height;
|
|
fd.budget = flag.budget;
|
|
fd.active = flag.active;
|
|
fd.fire_state = flag.fire_state;
|
|
fd.ignore = flag.ignore;
|
|
fd.palette = flag.palette;
|
|
if (!f(fd))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
virtual bool get_cc_flag_data(uint32_t id, cryptonote::cc_flag_data_t &fd, std::vector<std::vector<uint8_t>> *tiles) const override
|
|
{
|
|
if (id >= state.flags.size() || !state.flags[id].in_use)
|
|
return false;
|
|
fd.id = id;
|
|
fd.owner = state.flags[id].owner;
|
|
fd.city = state.flags[id].city;
|
|
fd.role = state.flags[id].role;
|
|
fd.x0 = state.flags[id].x0;
|
|
fd.y0 = state.flags[id].y0;
|
|
fd.x1 = state.flags[id].x1;
|
|
fd.y1 = state.flags[id].y1;
|
|
fd.repair = state.flags[id].repair;
|
|
fd.base_height = state.flags[id].base_height;
|
|
fd.elevation_bonus = state.flags[id].elevation_bonus;
|
|
fd.economic_power = state.flags[id].economic_power;
|
|
memcpy(fd.potential, state.flags[id].potential, sizeof(fd.potential));
|
|
fd.construction_height = state.flags[id].construction_height;
|
|
fd.budget = state.flags[id].budget;
|
|
fd.active = state.flags[id].active;
|
|
fd.fire_state = state.flags[id].fire_state;
|
|
fd.ignore = state.flags[id].ignore;
|
|
fd.palette = state.flags[id].palette;
|
|
if (tiles)
|
|
{
|
|
tiles->resize((fd.x1-fd.x0+1) * (fd.y1-fd.y0+1));
|
|
unsigned int idx = 0;
|
|
for (uint32_t y = fd.y0; y <= fd.y1; ++y)
|
|
{
|
|
for (uint32_t x = fd.x0; x <= fd.x1; ++x)
|
|
{
|
|
auto it = state.tiles.find(std::make_pair(x, y));
|
|
if (it != state.tiles.end())
|
|
(*tiles)[idx] = it->second.blocks;
|
|
++idx;
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
virtual void set_cc_flag_data(uint32_t id, const cryptonote::cc_flag_data_t &fd) override
|
|
{
|
|
state.flags[id].owner = fd.owner;
|
|
state.flags[id].city = fd.city;
|
|
state.flags[id].role = fd.role;
|
|
state.flags[id].x0 = fd.x0;
|
|
state.flags[id].y0 = fd.y0;
|
|
state.flags[id].x1 = fd.x1;
|
|
state.flags[id].y1 = fd.y1;
|
|
state.flags[id].repair = fd.repair;
|
|
state.flags[id].economic_power = fd.economic_power;
|
|
state.flags[id].base_height = fd.base_height;
|
|
state.flags[id].elevation_bonus = fd.elevation_bonus;
|
|
memcpy(state.flags[id].potential, fd.potential, sizeof(fd.potential));
|
|
state.flags[id].construction_height = fd.construction_height;
|
|
state.flags[id].budget = fd.budget;
|
|
state.flags[id].active = fd.active;
|
|
state.flags[id].fire_state = fd.fire_state;
|
|
state.flags[id].ignore = fd.ignore;
|
|
state.flags[id].palette = fd.palette;
|
|
}
|
|
|
|
virtual void set_cc_flag_role(uint32_t id, uint8_t role, uint32_t economic_power) override
|
|
{
|
|
state.flags[id].role = role;
|
|
state.flags[id].economic_power = economic_power;
|
|
state.flags[id].construction_height = height() - 1;
|
|
}
|
|
|
|
virtual void set_cc_flag_name(uint32_t id, const std::string &name)
|
|
{
|
|
state.flags[id].name = name;
|
|
}
|
|
|
|
virtual void set_cc_flag_owner(uint32_t id, uint32_t owner) override
|
|
{
|
|
state.flags[id].owner = owner;
|
|
}
|
|
|
|
virtual void set_cc_flag_repair(uint32_t id, uint32_t repair) override
|
|
{
|
|
state.flags[id].repair = repair;
|
|
}
|
|
|
|
virtual void set_cc_flag_budget(uint32_t id, const std::map<uint32_t, uint32_t> &budget) override
|
|
{
|
|
state.flags[id].budget = budget;
|
|
}
|
|
|
|
virtual void set_cc_flag_fire_state(uint32_t id, uint8_t fire_state) override
|
|
{
|
|
state.flags[id].fire_state = fire_state;
|
|
}
|
|
|
|
virtual void set_cc_flag_active(uint32_t id, bool active) override
|
|
{
|
|
state.flags[id].active = active;
|
|
}
|
|
|
|
virtual void set_cc_flag_ignore(uint32_t id, bool ignore) override
|
|
{
|
|
state.flags[id].ignore = ignore;
|
|
}
|
|
|
|
virtual void set_cc_flag_tiles(uint32_t id, const std::vector<std::vector<uint8_t>> &tiles) override
|
|
{
|
|
const uint32_t x0 = state.flags[id].x0;
|
|
const uint32_t y0 = state.flags[id].y0;
|
|
const uint32_t x1 = state.flags[id].x1;
|
|
const uint32_t y1 = state.flags[id].y1;
|
|
unsigned int idx = 0;
|
|
for (uint32_t y = y0; y <= y1; ++y)
|
|
{
|
|
for (uint32_t x = x0; x <= x1; ++x)
|
|
{
|
|
if (tiles[idx].empty())
|
|
{
|
|
state.tiles.erase(std::make_pair(x, y));
|
|
}
|
|
else
|
|
state.tiles[std::make_pair(x, y)].blocks = tiles[idx];
|
|
++idx;
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual void remove_cc_flag_blocks(uint32_t id)
|
|
{
|
|
const uint32_t x0 = state.flags[id].x0;
|
|
const uint32_t y0 = state.flags[id].y0;
|
|
const uint32_t x1 = state.flags[id].x1;
|
|
const uint32_t y1 = state.flags[id].y1;
|
|
unsigned int idx = 0;
|
|
for (uint32_t y = y0; y <= y1; ++y)
|
|
{
|
|
for (uint32_t x = x0; x <= x1; ++x)
|
|
{
|
|
state.tiles.erase(std::make_pair(x, y));
|
|
}
|
|
}
|
|
}
|
|
|
|
virtual uint32_t get_num_cc_flags() const override
|
|
{
|
|
size_t c = 0;
|
|
for (size_t i = 0; i < state.flags.size(); ++i)
|
|
if (state.flags[i].in_use)
|
|
++c;
|
|
return c;
|
|
}
|
|
|
|
#if 0
|
|
virtual void add_cc_used_nonce(uint64_t nonce, uint32_t account, const crypto::hash &txid) override
|
|
{
|
|
state.nonces.insert(nonce);
|
|
}
|
|
|
|
virtual void remove_cc_used_nonce(uint64_t nonce) override
|
|
{
|
|
state.nonces.erase(nonce);
|
|
}
|
|
|
|
virtual bool is_cc_nonce_used(uint64_t nonce, uint32_t *account, crypto::hash *txid) const override
|
|
{
|
|
return state.nonces.find(nonce) != state.nonces.end();
|
|
}
|
|
#endif
|
|
|
|
virtual void set_cc_trade_used(uint64_t nonce, uint32_t used) override
|
|
{
|
|
if (used == 0)
|
|
state.cc_trade_used.erase(nonce);
|
|
else
|
|
state.cc_trade_used[nonce] = used;
|
|
}
|
|
|
|
virtual uint32_t get_cc_trade_used(uint64_t nonce) const override
|
|
{
|
|
auto i = state.cc_trade_used.find(nonce);
|
|
if (i == state.cc_trade_used.end())
|
|
return 0;
|
|
return i->second;
|
|
}
|
|
|
|
virtual void add_cc_order(const crypto::hash &txid, bool mined, uint32_t account, uint64_t nonce, bool bid, uint32_t type, uint32_t id, uint32_t amount, uint64_t price, uint64_t accrual_start_height, int64_t accrual, uint64_t accrual_price_limit, uint64_t expiration) override
|
|
{
|
|
state.cc_orders[nonce] = {txid, mined, account, bid, nonce, type, id, amount, price, accrual_start_height, accrual, accrual_price_limit, expiration};
|
|
}
|
|
|
|
virtual void remove_cc_order(uint64_t nonce)
|
|
{
|
|
state.cc_orders.erase(nonce);
|
|
}
|
|
|
|
virtual void get_cc_orders(std::vector<cryptonote::order_t<crypto::hash>> &trades, bool bids, bool offers, const std::vector<uint32_t> &type, const std::vector<uint32_t> &id) const
|
|
{
|
|
for (const auto &e: state.cc_orders)
|
|
{
|
|
if (e.second.bid && !bids)
|
|
continue;
|
|
if (!e.second.bid && !offers)
|
|
continue;
|
|
if (!type.empty() && std::find(type.begin(), type.end(), e.second.type) == type.end())
|
|
continue;
|
|
if (!id.empty() && std::find(id.begin(), id.end(), e.second.id) == id.end())
|
|
continue;
|
|
trades.push_back({e.second.txid, e.second.mined, e.second.account, e.second.bid, e.first, e.second.type, e.second.id, e.second.amount, e.second.price, e.second.accrual_start_height, e.second.accrual, e.second.accrual_price_limit, e.second.expiration});
|
|
}
|
|
}
|
|
|
|
virtual bool get_cc_transaction_hash_from_nonce(uint64_t nonce, crypto::hash &txid, bool &mined) const override
|
|
{
|
|
auto i = state.cc_orders.find(nonce);
|
|
if (i == state.cc_orders.end())
|
|
return false;
|
|
txid = i->second.txid;
|
|
mined = i->second.mined;
|
|
return true;
|
|
}
|
|
|
|
virtual bool get_cc_transaction_account_from_nonce(uint64_t nonce, uint32_t &account) const override
|
|
{
|
|
auto i = state.cc_orders.find(nonce);
|
|
if (i == state.cc_orders.end())
|
|
return false;
|
|
account = i->second.account;
|
|
return true;
|
|
}
|
|
|
|
virtual bool get_cc_pruned_transaction_blob_from_nonce(uint64_t nonce, cryptonote::blobdata &bd, bool &mined, bool blockchain, bool txpool, cryptonote::relay_category tx_category) const override
|
|
{
|
|
auto i = state.cc_orders.find(nonce);
|
|
if (i == state.cc_orders.end())
|
|
return false;
|
|
if (i->second.mined && !blockchain)
|
|
return false;
|
|
if (!i->second.mined && !txpool)
|
|
return false;
|
|
bd = {};
|
|
mined = i->second.mined;
|
|
return true;
|
|
}
|
|
|
|
virtual void set_cc_shares(const cryptonote::cc_shares_data_t &sd) override
|
|
{
|
|
shares_t s;
|
|
for (int i = 0; i < NUM_ROLES; ++i)
|
|
{
|
|
s.shares[i] = sd.shares[i];
|
|
s.payout[i] = sd.payout[i];
|
|
}
|
|
state.shares[sd.height] = s;
|
|
}
|
|
|
|
virtual void remove_cc_shares(uint32_t city, uint64_t height) override
|
|
{
|
|
state.shares.erase(height);
|
|
}
|
|
|
|
virtual bool get_cc_shares(uint32_t city, uint64_t height, cryptonote::cc_shares_data_t &sd) const override
|
|
{
|
|
if (state.shares.empty())
|
|
return false;
|
|
auto x = state.shares.begin()->second;
|
|
for (auto e: state.shares)
|
|
{
|
|
if (e.first > height)
|
|
break;
|
|
x = e.second;
|
|
}
|
|
for (int i = 0; i < NUM_ROLES; ++i)
|
|
{
|
|
sd.shares[i] = x.shares[i];
|
|
sd.payout[i] = x.payout[i];
|
|
}
|
|
return true;
|
|
}
|
|
|
|
virtual void set_cc_special_events(uint32_t city, const std::vector<cc::special_event_data_t> &sed)
|
|
{
|
|
std::vector<special_event_t> se;
|
|
for (const auto &e: sed)
|
|
se.push_back({e.special_event, e.start_height, e.duration});
|
|
state.special_events[city] = std::move(se);
|
|
}
|
|
|
|
virtual void get_cc_special_events(uint32_t city, std::vector<cc::special_event_data_t> &sed) const
|
|
{
|
|
sed.clear();
|
|
auto i = state.special_events.find(city);
|
|
if (i == state.special_events.end())
|
|
return;
|
|
for (const auto &e: i->second)
|
|
sed.push_back({e.event, e.start_height, e.duration});
|
|
}
|
|
|
|
uint32_t allocate_new_cc_custom_item(uint32_t amount, uint32_t creator, uint64_t creation_height, const std::string &name, bool is_group, bool is_public, uint32_t group, const std::string &primary_description, const std::string &secondary_description, uint64_t user_data)
|
|
{
|
|
uint32_t actual_id;
|
|
state.items.push_back({});
|
|
actual_id = state.items.size() - 1;
|
|
state.items[actual_id] = {true, amount, creator, creation_height, name, false, is_group, group, primary_description, secondary_description, user_data};
|
|
return actual_id + ITEM_FIRST_USER;
|
|
}
|
|
|
|
void delete_cc_custom_item(uint32_t id)
|
|
{
|
|
state.items[id].in_use = false;
|
|
}
|
|
|
|
bool get_cc_custom_item_data(uint32_t id, cc::cc_custom_item_t &cid) const
|
|
{
|
|
if (!state.items[id].in_use)
|
|
return false;
|
|
cid.id = id;
|
|
cid.amount = state.items[id].amount;
|
|
cid.name = state.items[id].name;
|
|
cid.is_group = state.items[id].is_group;
|
|
cid.group = state.items[id].group;
|
|
cid.primary_description = state.items[id].pdesc;
|
|
cid.secondary_description = state.items[id].sdesc;
|
|
cid.user_data = state.items[id].user_data;
|
|
return true;
|
|
}
|
|
|
|
void set_cc_custom_item_ignore(uint32_t id, bool ignore)
|
|
{
|
|
if (!state.items[id].in_use)
|
|
return;
|
|
state.items[id].ignore = ignore;
|
|
}
|
|
|
|
void set_cc_custom_item_amount(uint32_t id, uint32_t amount)
|
|
{
|
|
if (!state.items[id].in_use)
|
|
return;
|
|
state.items[id].amount = amount;
|
|
}
|
|
|
|
bool for_all_cc_custom_items(std::function<bool(const cc::cc_custom_item_t &cid)> f) const
|
|
{
|
|
for (size_t i = 0; i < state.items.size(); ++i)
|
|
{
|
|
const auto &item = state.items[i];
|
|
if (!item.in_use)
|
|
continue;
|
|
cc::cc_custom_item_t cid{};
|
|
cid.id = i;
|
|
cid.amount = item.amount;
|
|
cid.name = item.name;
|
|
cid.is_group = item.is_group;
|
|
cid.group = item.group;
|
|
cid.primary_description = item.pdesc;
|
|
cid.secondary_description = item.sdesc;
|
|
cid.user_data = item.user_data;
|
|
if (!f(cid))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
virtual uint64_t get_cc_item_count(uint32_t id) const
|
|
{
|
|
const auto it = state.item_count.find(id);
|
|
return it == state.item_count.end() ? 0 : it->second;
|
|
}
|
|
|
|
virtual void set_cc_item_count(uint32_t id, uint64_t count)
|
|
{
|
|
state.item_count[id] = count;
|
|
if (state.item_count[id] == 0)
|
|
state.item_count.erase(id);
|
|
}
|
|
|
|
private:
|
|
std::vector<block_t> blocks;
|
|
state_t state;
|
|
};
|
|
|
|
crypto::public_key TestDB::DEAD_PKEY = crypto::null_pkey;
|
|
|
|
}
|
|
|
|
TEST(cc_command, tx_defaults_to_none)
|
|
{
|
|
cryptonote::transaction tx;
|
|
ASSERT_EQ(tx.cc_cmd.type(), typeid(cryptonote::cc_command_none_t));
|
|
}
|
|
|
|
TEST(cc_command, none)
|
|
{
|
|
cryptonote::cc_command_none_t cmd;
|
|
ASSERT_EQ(cc::get_cc_command_cost(cmd), 0);
|
|
uint64_t cc_in = (uint64_t)-1, cc_out = (uint64_t)-1;
|
|
cc::get_cc_command_in_out(cmd, cc_in, cc_out);
|
|
ASSERT_EQ(cc_in, 0);
|
|
ASSERT_EQ(cc_out, 0);
|
|
}
|
|
|
|
TEST(cc_command, create_account)
|
|
{
|
|
cryptonote::cc_command_create_account_t cmd;
|
|
cmd.public_key = crypto::null_pkey;
|
|
cmd.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 42;
|
|
cmd.name = "name";
|
|
cmd.inviting_account = 0;
|
|
ASSERT_EQ(cc::get_cc_command_cost(cmd), 0);
|
|
uint64_t cc_in = (uint64_t)-1, cc_out = (uint64_t)-1;
|
|
cc::get_cc_command_in_out(cmd, cc_in, cc_out);
|
|
ASSERT_EQ(cc_in, 0);
|
|
ASSERT_EQ(cc_out, CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 42);
|
|
}
|
|
|
|
TEST(cc_command, transfer_deposit)
|
|
{
|
|
cryptonote::cc_command_transfer_t cmd;
|
|
cmd.in_amount = 0;
|
|
cmd.public_key = crypto::null_pkey;
|
|
cmd.out_amount = 1000;
|
|
ASSERT_EQ(cc::get_cc_command_cost(cmd), 0);
|
|
uint64_t cc_in = (uint64_t)-1, cc_out = (uint64_t)-1;
|
|
cc::get_cc_command_in_out(cmd, cc_in, cc_out);
|
|
ASSERT_EQ(cc_in, 0);
|
|
ASSERT_EQ(cc_out, 1000);
|
|
}
|
|
|
|
TEST(cc_command, transfer_withdrawal)
|
|
{
|
|
cryptonote::cc_command_transfer_t cmd;
|
|
cmd.in_amount = 1000;
|
|
cmd.public_key = crypto::null_pkey;
|
|
cmd.out_amount = 0;
|
|
ASSERT_EQ(cc::get_cc_command_cost(cmd), 1000);
|
|
uint64_t cc_in = (uint64_t)-1, cc_out = (uint64_t)-1;
|
|
cc::get_cc_command_in_out(cmd, cc_in, cc_out);
|
|
ASSERT_EQ(cc_in, 1000);
|
|
ASSERT_EQ(cc_out, 0);
|
|
}
|
|
|
|
TEST(cc_command, transfer_mixed)
|
|
{
|
|
cryptonote::cc_command_transfer_t cmd;
|
|
cmd.in_amount = 1000;
|
|
cmd.public_key = crypto::null_pkey;
|
|
cmd.out_amount = 1500;
|
|
ASSERT_EQ(cc::get_cc_command_cost(cmd), 1000);
|
|
uint64_t cc_in = (uint64_t)-1, cc_out = (uint64_t)-1;
|
|
cc::get_cc_command_in_out(cmd, cc_in, cc_out);
|
|
ASSERT_EQ(cc_in, 1000);
|
|
ASSERT_EQ(cc_out, 1500);
|
|
}
|
|
|
|
TEST(cc_command, buy_land)
|
|
{
|
|
cryptonote::cc_command_buy_land_t cmd;
|
|
cmd.city = 0;
|
|
cmd.x = 40;
|
|
cmd.y = 50;
|
|
cmd.wm1 = 8;
|
|
cmd.hm1 = 4;
|
|
cmd.cost = 74983743;
|
|
ASSERT_EQ(cc::get_cc_command_cost(cmd), 74983743);
|
|
uint64_t cc_in = (uint64_t)-1, cc_out = (uint64_t)-1;
|
|
cc::get_cc_command_in_out(cmd, cc_in, cc_out);
|
|
ASSERT_EQ(cc_in, 0);
|
|
ASSERT_EQ(cc_out, 0);
|
|
}
|
|
|
|
TEST(cc_command, build)
|
|
{
|
|
cryptonote::cc_command_build_t cmd;
|
|
cmd.flag = 1;
|
|
cmd.dx = 40;
|
|
cmd.dy = 50;
|
|
cmd.wm1 = 8;
|
|
cmd.hm1 = 4;
|
|
cmd.height = 0;
|
|
cmd.remove = false;
|
|
uint64_t cc_in = (uint64_t)-1, cc_out = (uint64_t)-1;
|
|
cc::get_cc_command_in_out(cmd, cc_in, cc_out);
|
|
ASSERT_EQ(cc_in, 0);
|
|
ASSERT_EQ(cc_out, 0);
|
|
}
|
|
|
|
TEST(cc_command, buy_blocks)
|
|
{
|
|
cryptonote::cc_command_buy_items_t cmd;
|
|
cmd.entries.resize(1);
|
|
cmd.entries[0].type = ITEM_BASIC_STONE;
|
|
cmd.entries[0].amount = 1;
|
|
uint64_t c1 = cc::get_cc_command_cost(cmd);
|
|
ASSERT_GT(c1, 0);
|
|
cmd.entries[0].amount = 50;
|
|
uint64_t c50 = cc::get_cc_command_cost(cmd);
|
|
ASSERT_EQ(c50, 50 * c1);
|
|
cmd.entries[0].amount = std::numeric_limits<uint32_t>::max();
|
|
uint64_t cmax32 = cc::get_cc_command_cost(cmd);
|
|
ASSERT_EQ(cmax32, std::numeric_limits<uint32_t>::max() * c1);
|
|
uint64_t cc_in = (uint64_t)-1, cc_out = (uint64_t)-1;
|
|
cc::get_cc_command_in_out(cmd, cc_in, cc_out);
|
|
ASSERT_EQ(cc_in, 0);
|
|
ASSERT_EQ(cc_out, 0);
|
|
}
|
|
|
|
TEST(cc_command, assign_items)
|
|
{
|
|
cryptonote::cc_command_assign_items_t cmd;
|
|
cmd.items.resize(1);
|
|
cmd.items[0].type = ITEM_BASIC_STONE;
|
|
cmd.items[0].amount = 1;
|
|
uint64_t c1 = cc::get_cc_command_cost(cmd);
|
|
ASSERT_EQ(c1, 0);
|
|
uint64_t cc_in = (uint64_t)-1, cc_out = (uint64_t)-1;
|
|
cc::get_cc_command_in_out(cmd, cc_in, cc_out);
|
|
ASSERT_EQ(cc_in, 0);
|
|
ASSERT_EQ(cc_out, 0);
|
|
}
|
|
|
|
TEST(cc_command, repair)
|
|
{
|
|
cryptonote::cc_command_repair_t cmd;
|
|
cmd.flags.push_back({1, 0});
|
|
uint64_t cc_in = (uint64_t)-1, cc_out = (uint64_t)-1;
|
|
cc::get_cc_command_in_out(cmd, cc_in, cc_out);
|
|
ASSERT_EQ(cc_in, 0);
|
|
ASSERT_EQ(cc_out, 0);
|
|
}
|
|
|
|
TEST(cc_command, rename)
|
|
{
|
|
cryptonote::cc_command_rename_t cmd;
|
|
cmd.flag = 1;
|
|
uint64_t cc_in = (uint64_t)-1, cc_out = (uint64_t)-1;
|
|
cc::get_cc_command_in_out(cmd, cc_in, cc_out);
|
|
ASSERT_EQ(cc_in, 0);
|
|
ASSERT_EQ(cc_out, 0);
|
|
}
|
|
|
|
TEST(cc_command, research)
|
|
{
|
|
cryptonote::cc_command_research_t cmd;
|
|
cmd.discovery = DISCOVERY_IMPROVED_YOKE;
|
|
cmd.amount = 1000;
|
|
uint64_t cc_in = (uint64_t)-1, cc_out = (uint64_t)-1;
|
|
cc::get_cc_command_in_out(cmd, cc_in, cc_out);
|
|
ASSERT_EQ(cc_in, 0);
|
|
ASSERT_EQ(cc_out, 0);
|
|
}
|
|
|
|
TEST(cc, generator_types)
|
|
{
|
|
for (uint8_t role = 0; role < NUM_ROLES; ++role)
|
|
{
|
|
ASSERT_EQ(cc::is_generator(role), role == ROLE_STONECUTTER || role == ROLE_SAWMILL || role == ROLE_KILN || role == ROLE_SMELTER || role == ROLE_WORKFORCE);
|
|
}
|
|
}
|
|
|
|
TEST(cc, increasing_costs)
|
|
{
|
|
uint64_t c0, c1;
|
|
|
|
ASSERT_TRUE(cc::get_new_flag_cost(0, 0, 1, 1, c0));
|
|
ASSERT_TRUE(cc::get_new_flag_cost(0, 0, 1, 2, c1));
|
|
ASSERT_GT(c1, c0);
|
|
|
|
// square is cheaper for the same area
|
|
ASSERT_TRUE(cc::get_new_flag_cost(0, 0, 3, 3, c0));
|
|
ASSERT_TRUE(cc::get_new_flag_cost(0, 0, 1, 7, c1));
|
|
ASSERT_GT(c1, c0);
|
|
|
|
for (uint32_t h = 1; h < 65536; ++h)
|
|
{
|
|
ASSERT_GE(cc::get_build_labour_cost_for_height(h), cc::get_build_labour_cost_for_height(h-1));
|
|
}
|
|
}
|
|
|
|
TEST(cc, buildings_start_inactive)
|
|
{
|
|
cryptonote::cc_command_game_update_t cmd;
|
|
TestDB *db = new TestDB();
|
|
cryptonote::BlockchainDB *bdb = db;
|
|
cc::add_init_state(*bdb);
|
|
cc::game_events_t events;
|
|
cryptonote::cc_command_game_update_t cg, cg0 = cc::create_cc_game_update_command(*db, events);
|
|
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, "");
|
|
uint8_t potentials[NUM_POTENTIALS];
|
|
for (int i = 0; i < NUM_POTENTIALS; ++i)
|
|
potentials[i] = 100;
|
|
uint32_t flag_id = bdb->allocate_new_cc_flag(NULL, account_id, city_id, 44, 45, 46, 47, potentials, 32, 33);
|
|
cryptonote::cc_flag_data_t fd;
|
|
ASSERT_TRUE(bdb->get_cc_flag_data(flag_id, fd, NULL));
|
|
ASSERT_EQ(fd.active, false);
|
|
}
|
|
|
|
TEST(cc, buildings_start_not_on_fire)
|
|
{
|
|
cryptonote::cc_command_game_update_t cmd;
|
|
TestDB *db = new TestDB();
|
|
cryptonote::BlockchainDB *bdb = db;
|
|
cc::add_init_state(*bdb);
|
|
cc::game_events_t events;
|
|
cryptonote::cc_command_game_update_t cg, cg0 = cc::create_cc_game_update_command(*db, events);
|
|
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, "");
|
|
uint8_t potentials[NUM_POTENTIALS];
|
|
for (int i = 0; i < NUM_POTENTIALS; ++i)
|
|
potentials[i] = 100;
|
|
uint32_t flag_id = bdb->allocate_new_cc_flag(NULL, account_id, city_id, 44, 45, 46, 47, potentials, 32, 33);
|
|
cryptonote::cc_flag_data_t fd;
|
|
ASSERT_TRUE(bdb->get_cc_flag_data(flag_id, fd, NULL));
|
|
ASSERT_EQ(fd.fire_state, 127);
|
|
}
|
|
|
|
static void test_commands(bool good, const std::vector<cryptonote::cc_command_t> &setup, const cryptonote::cc_command_t &cmd, const char *label, const std::function<void(TestDB*)> &check = [](TestDB*)->void{})
|
|
{
|
|
std::unique_ptr<cryptonote::Blockchain> bc;
|
|
cryptonote::tx_memory_pool txpool(*bc);
|
|
bc.reset(new cryptonote::Blockchain(txpool));
|
|
struct get_test_options {
|
|
const std::pair<uint8_t, uint64_t> hard_forks[2];
|
|
const cryptonote::test_options test_options = {
|
|
hard_forks,
|
|
0,
|
|
};
|
|
get_test_options(): hard_forks{std::make_pair((uint8_t)12, (uint64_t)0), std::make_pair((uint8_t)0, (uint64_t)0)} {}
|
|
} opts;
|
|
cryptonote::Blockchain *blockchain = bc.get();
|
|
ASSERT_TRUE(blockchain != NULL) << label;
|
|
TestDB *db = new TestDB();
|
|
bool r = blockchain->init(db, cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL);
|
|
ASSERT_TRUE(r) << label;
|
|
|
|
for (const auto &s: setup)
|
|
{
|
|
cryptonote::tx_verification_context tvc{};
|
|
const cryptonote::cc_command_base_t *base = cryptonote::get_cc_command_base(s);
|
|
uint64_t balance, cost = cc::get_cc_command_cost(s);
|
|
bool r0 = (base && base->cc_account) ? db->get_cc_account_balance(base->cc_account, balance) : true;
|
|
bool r1 = r0 && cost <= balance;
|
|
bool r2 = r1 && cc::check_cc_command(*db, s, tvc);
|
|
ASSERT_TRUE(r2) << label << " (r0 " << r0 << ", r1 " << r1 << ", r2 " << r2 << ", cost " << cost << ", account " << (base ? base->cc_account : 0) << ", balance " << balance << ")";
|
|
cc::game_events_t events;
|
|
r = cc::execute_cc_command(*db, s, events);
|
|
ASSERT_TRUE(r) << label;
|
|
}
|
|
|
|
cryptonote::tx_verification_context tvc{};
|
|
const cryptonote::cc_command_base_t *base = cryptonote::get_cc_command_base(cmd);
|
|
uint64_t balance, cost = cc::get_cc_command_cost(cmd);
|
|
bool r0 = (base && base->cc_account) ? db->get_cc_account_balance(base->cc_account, balance) : true;
|
|
bool r1 = r0 && cost <= balance;
|
|
bool r2 = r1 && cc::check_cc_command(*db, cmd, tvc);
|
|
ASSERT_EQ(r2, good) << label << " (r0 " << r0 << ", r1 " << r1 << ", r2 " << r2 << ", cost " << cost << ", account " << (base ? base->cc_account : 0) << ", balance " << balance << ")";
|
|
|
|
if (good)
|
|
{
|
|
const TestDB::state_t s0 = db->get_state();
|
|
cc::game_events_t events;
|
|
r = cc::execute_cc_command(*db, cmd, events);
|
|
ASSERT_TRUE(r) << label;
|
|
if (check)
|
|
check(db);
|
|
r = cc::revert_cc_command(*db, cmd);
|
|
ASSERT_TRUE(r) << label;
|
|
const TestDB::state_t s1 = db->get_state();
|
|
ASSERT_EQ(s0, s1) << label;
|
|
}
|
|
}
|
|
|
|
TEST(cc_command, execute_none)
|
|
{
|
|
test_commands(true, {}, cryptonote::cc_command_none_t(), "valid");
|
|
}
|
|
|
|
TEST(cc_command, execute_create_account)
|
|
{
|
|
cryptonote::cc_command_create_account_t create_account;
|
|
|
|
cryptonote::keypair keys = cryptonote::keypair::generate(hw::get_device("default"));
|
|
|
|
// good
|
|
create_account.public_key = keys.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE;
|
|
create_account.name = "create-test";
|
|
create_account.inviting_account = 0;
|
|
test_commands(true, {}, create_account, "valid");
|
|
|
|
// bad key
|
|
create_account.public_key = crypto::null_pkey;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE;
|
|
test_commands(false, {}, create_account, "bad key");
|
|
|
|
// bad name
|
|
create_account.name = " a";
|
|
test_commands(false, {}, create_account, "bad name");
|
|
create_account.name = "create-test";
|
|
|
|
// bad inviting account
|
|
create_account.inviting_account = 83;
|
|
test_commands(false, {}, create_account, "bad name");
|
|
create_account.inviting_account = 0;
|
|
|
|
// low amount
|
|
create_account.public_key = keys.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE - 1;
|
|
test_commands(false, {}, create_account, "low amount");
|
|
|
|
// good, plus funding
|
|
create_account.public_key = keys.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 42;
|
|
test_commands(true, {}, create_account, "with funding");
|
|
}
|
|
|
|
TEST(cc_command, execute_transfer_deposit)
|
|
{
|
|
cryptonote::cc_command_transfer_t transfer;
|
|
std::vector<cryptonote::cc_command_t> setup;
|
|
|
|
cryptonote::keypair keys = cryptonote::keypair::generate(hw::get_device("default"));
|
|
|
|
// setup
|
|
cryptonote::cc_command_create_account_t create_account;
|
|
create_account.public_key = keys.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 1000;
|
|
create_account.name = "foo";
|
|
create_account.inviting_account = 0;
|
|
setup.push_back(create_account);
|
|
|
|
// deposit
|
|
transfer.cc_account = 4;
|
|
transfer.in_amount = 0;
|
|
transfer.public_key = keys.pub;
|
|
transfer.out_amount = 5000;
|
|
test_commands(true, setup, transfer, "valid");
|
|
|
|
// overflow
|
|
transfer.in_amount = 0;
|
|
transfer.public_key = keys.pub;
|
|
transfer.out_amount = std::numeric_limits<uint64_t>::max();
|
|
test_commands(false, setup, transfer, "overflow");
|
|
}
|
|
|
|
TEST(cc_command, execute_transfer_withdrawal)
|
|
{
|
|
cryptonote::cc_command_transfer_t transfer;
|
|
std::vector<cryptonote::cc_command_t> setup;
|
|
|
|
cryptonote::keypair keys = cryptonote::keypair::generate(hw::get_device("default"));
|
|
|
|
// setup
|
|
cryptonote::cc_command_create_account_t create_account;
|
|
create_account.public_key = keys.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 1000;
|
|
create_account.name = "foo";
|
|
create_account.inviting_account = 0;
|
|
setup.push_back(create_account);
|
|
|
|
// withdraw
|
|
transfer.cc_account = 4;
|
|
transfer.in_amount = 1000;
|
|
transfer.public_key = crypto::null_pkey;
|
|
transfer.out_amount = 0;
|
|
test_commands(true, setup, transfer, "valid");
|
|
|
|
// more than balance
|
|
transfer.in_amount = 1001;
|
|
transfer.public_key = crypto::null_pkey;
|
|
transfer.out_amount = 0;
|
|
test_commands(false, setup, transfer, "more than balance");
|
|
|
|
// more than balance
|
|
transfer.in_amount = std::numeric_limits<uint64_t>::max();
|
|
transfer.public_key = crypto::null_pkey;
|
|
transfer.out_amount = 0;
|
|
test_commands(false, setup, transfer, "max value");
|
|
}
|
|
|
|
TEST(cc_command, execute_transfer_mixed)
|
|
{
|
|
cryptonote::cc_command_transfer_t transfer;
|
|
std::vector<cryptonote::cc_command_t> setup;
|
|
|
|
cryptonote::keypair keys1 = cryptonote::keypair::generate(hw::get_device("default"));
|
|
cryptonote::keypair keys2 = cryptonote::keypair::generate(hw::get_device("default"));
|
|
|
|
// setup
|
|
cryptonote::cc_command_create_account_t create_account;
|
|
create_account.public_key = keys1.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 1000;
|
|
create_account.name = "foo";
|
|
create_account.inviting_account = 0;
|
|
setup.push_back(create_account);
|
|
create_account.public_key = keys2.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 2000;
|
|
create_account.name = "foo 2";
|
|
create_account.inviting_account = 0;
|
|
setup.push_back(create_account);
|
|
|
|
// transfer to another
|
|
transfer.cc_account = 4;
|
|
transfer.in_amount = 500;
|
|
transfer.public_key = keys2.pub;
|
|
transfer.out_amount = 500;
|
|
test_commands(true, setup, transfer, "valid");
|
|
|
|
// more than balance
|
|
transfer.in_amount = 1001;
|
|
transfer.public_key = keys2.pub;
|
|
transfer.out_amount = 1001;
|
|
test_commands(false, setup, transfer, "more than balance");
|
|
|
|
// those checks are made at the tx layer, since they're OK for non bare txes
|
|
#if 0
|
|
// receive more than sent
|
|
transfer.in_amount = 500;
|
|
transfer.public_key = keys2.pub;
|
|
transfer.out_amount = 501;
|
|
test_commands(false, setup, transfer, "more than sent");
|
|
|
|
// receive less than sent
|
|
transfer.in_amount = 500;
|
|
transfer.public_key = keys2.pub;
|
|
transfer.out_amount = 499;
|
|
test_commands(false, setup, transfer, "less than sent");
|
|
#endif
|
|
}
|
|
|
|
TEST(cc_command, execute_buy_land)
|
|
{
|
|
cryptonote::cc_command_buy_land_t buy_land;
|
|
cryptonote::cc_command_buy_land_t buy_land2;
|
|
std::vector<cryptonote::cc_command_t> setup;
|
|
|
|
cryptonote::keypair keys1 = cryptonote::keypair::generate(hw::get_device("default"));
|
|
cryptonote::keypair keys2 = cryptonote::keypair::generate(hw::get_device("default"));
|
|
|
|
// setup
|
|
cryptonote::cc_command_create_account_t create_account;
|
|
create_account.public_key = keys1.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 20*COIN;
|
|
create_account.name = "foo";
|
|
create_account.inviting_account = 0;
|
|
setup.push_back(create_account);
|
|
create_account.public_key = keys2.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 20*COIN;
|
|
create_account.name = "foo 2";
|
|
create_account.inviting_account = 0;
|
|
setup.push_back(create_account);
|
|
|
|
// good
|
|
buy_land.cc_account = 4;
|
|
buy_land.city = 0;
|
|
buy_land.x = 1000040;
|
|
buy_land.y = 1000050;
|
|
buy_land.wm1 = 8;
|
|
buy_land.hm1 = 4;
|
|
ASSERT_TRUE(cc::get_new_flag_cost(buy_land.x, buy_land.y, buy_land.x + buy_land.wm1, buy_land.y + buy_land.hm1, buy_land.cost));
|
|
test_commands(true, setup, buy_land, "valid");
|
|
|
|
// too close to edge
|
|
buy_land.x = 50;
|
|
test_commands(false, setup, buy_land, "too close to edge");
|
|
buy_land.x = 1000040;
|
|
|
|
// too close to edge
|
|
buy_land.x = std::numeric_limits<uint32_t>::max() - 500;
|
|
test_commands(false, setup, buy_land, "too close to edge");
|
|
buy_land.x = 1000040;
|
|
|
|
// bad city
|
|
buy_land.city = 1;
|
|
test_commands(false, setup, buy_land, "bad city");
|
|
buy_land.city = 0;
|
|
|
|
// bad owner
|
|
buy_land.cc_account = 42;
|
|
test_commands(false, setup, buy_land, "bad owner");
|
|
buy_land.cc_account = 4;
|
|
|
|
// too costly
|
|
buy_land.wm1 = 255;
|
|
ASSERT_TRUE(cc::get_new_flag_cost(buy_land.x, buy_land.y, buy_land.x + buy_land.wm1, buy_land.y + buy_land.hm1, buy_land.cost));
|
|
test_commands(false, setup, buy_land, "too costly");
|
|
buy_land.wm1 = 4;
|
|
ASSERT_TRUE(cc::get_new_flag_cost(buy_land.x, buy_land.y, buy_land.x + buy_land.wm1, buy_land.y + buy_land.hm1, buy_land.cost));
|
|
|
|
// overlapping an existing flag
|
|
auto setup2 = setup;
|
|
buy_land2.cc_account = 5;
|
|
buy_land2.city = 0;
|
|
buy_land2.x = 1000020;
|
|
buy_land2.y = 1000052;
|
|
buy_land2.wm1 = 80;
|
|
buy_land2.hm1 = 4;
|
|
ASSERT_TRUE(cc::get_new_flag_cost(buy_land2.x, buy_land2.y, buy_land2.x + buy_land2.wm1, buy_land2.y + buy_land2.hm1, buy_land2.cost));
|
|
setup2.push_back(buy_land2);
|
|
test_commands(false, setup2, buy_land, "overlapping");
|
|
}
|
|
|
|
// here
|
|
TEST(cc_command, execute_build)
|
|
{
|
|
cryptonote::cc_command_build_t build;
|
|
std::vector<cryptonote::cc_command_t> setup;
|
|
uint32_t ox, oy;
|
|
cc::get_city_origin(0, ox, oy);
|
|
|
|
cryptonote::keypair keys1 = cryptonote::keypair::generate(hw::get_device("default"));
|
|
cryptonote::keypair keys2 = cryptonote::keypair::generate(hw::get_device("default"));
|
|
|
|
// setup
|
|
cryptonote::cc_command_create_account_t create_account;
|
|
create_account.public_key = keys1.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 1000*COIN;
|
|
create_account.name = "foo";
|
|
create_account.inviting_account = 0;
|
|
setup.push_back(create_account);
|
|
create_account.public_key = keys2.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 1000*COIN;
|
|
create_account.name = "foo 2";
|
|
create_account.inviting_account = 0;
|
|
setup.push_back(create_account);
|
|
cryptonote::cc_command_buy_land_t buy_land;
|
|
buy_land.cc_account = 4;
|
|
buy_land.city = 0;
|
|
buy_land.x = ox + 140;
|
|
buy_land.y = oy + 50;
|
|
buy_land.wm1 = 11;
|
|
buy_land.hm1 = 11;
|
|
ASSERT_TRUE(cc::get_new_flag_cost(buy_land.x, buy_land.y, buy_land.x + buy_land.wm1, buy_land.y + buy_land.hm1, buy_land.cost));
|
|
setup.push_back(buy_land);
|
|
cryptonote::cc_command_buy_items_t buy_blocks;
|
|
buy_blocks.cc_account = 4;
|
|
cryptonote::cc_command_buy_items_t::entry_t e;
|
|
e.type = ITEM_BASIC_STONE;
|
|
e.amount = 800;
|
|
buy_blocks.entries.push_back(e);
|
|
e.type = ITEM_BASIC_WOOD;
|
|
e.amount = 1440;
|
|
buy_blocks.entries.push_back(e);
|
|
e.type = ITEM_LABOUR;
|
|
e.amount = 28000;
|
|
buy_blocks.entries.push_back(e);
|
|
setup.push_back(buy_blocks);
|
|
cryptonote::cc_command_building_settings_t building_settings;
|
|
building_settings.cc_account = 4;
|
|
building_settings.flag = 1 + PREDEFINED_BULDINGS;
|
|
building_settings.role = ROLE_RESIDENTIAL1;
|
|
building_settings.economic_power = 100;
|
|
setup.push_back(building_settings);
|
|
|
|
// good
|
|
static const uint8_t block_data[] = { 2, ITEM_BASIC_STONE, 0, ITEM_BASIC_WOOD, 0x83, 0, 0, ITEM_BASIC_WOOD, 0, 0}; // S0W0000W0
|
|
build.cc_account = 4;
|
|
build.flag = 1 + PREDEFINED_BULDINGS;
|
|
build.dx = 1;
|
|
build.dy = 1;
|
|
build.wm1 = 2;
|
|
build.hm1 = 2;
|
|
build.height = 0;
|
|
build.remove = false;
|
|
build.block_data = std::vector<uint8_t>(block_data, block_data + sizeof(block_data));
|
|
test_commands(true, setup, build, "valid");
|
|
}
|
|
|
|
TEST(cc_command, execute_buy_blocks)
|
|
{
|
|
cryptonote::cc_command_buy_items_t buy_blocks;
|
|
std::vector<cryptonote::cc_command_t> setup;
|
|
|
|
cryptonote::keypair keys = cryptonote::keypair::generate(hw::get_device("default"));
|
|
|
|
// setup
|
|
cryptonote::cc_command_create_account_t create_account;
|
|
create_account.public_key = keys.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 1000*COIN;
|
|
create_account.name = "foo";
|
|
create_account.inviting_account = 0;
|
|
setup.push_back(create_account);
|
|
buy_blocks.entries.resize(1);
|
|
|
|
// good
|
|
buy_blocks.cc_account = 4;
|
|
buy_blocks.entries[0].type = ITEM_BASIC_STONE;
|
|
buy_blocks.entries[0].amount = 1;
|
|
test_commands(true, setup, buy_blocks, "valid");
|
|
|
|
// cannot buy block 0
|
|
buy_blocks.entries[0].type = 0;
|
|
buy_blocks.entries[0].amount = 1;
|
|
test_commands(false, setup, buy_blocks, "block 0");
|
|
|
|
// cannot buy 0 blocks
|
|
buy_blocks.entries[0].type = ITEM_BASIC_STONE;
|
|
buy_blocks.entries[0].amount = 0;
|
|
test_commands(false, setup, buy_blocks, "0 blocks");
|
|
}
|
|
|
|
TEST(cc_command, execute_buy_labour)
|
|
{
|
|
cryptonote::cc_command_buy_items_t buy_labour;
|
|
std::vector<cryptonote::cc_command_t> setup;
|
|
|
|
cryptonote::keypair keys = cryptonote::keypair::generate(hw::get_device("default"));
|
|
|
|
// setup
|
|
cryptonote::cc_command_create_account_t create_account;
|
|
create_account.public_key = keys.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 1000*COIN;
|
|
create_account.name = "foo";
|
|
create_account.inviting_account = 0;
|
|
setup.push_back(create_account);
|
|
buy_labour.entries.resize(1);
|
|
|
|
// good
|
|
buy_labour.cc_account = 4;
|
|
buy_labour.entries[0].type = ITEM_LABOUR;
|
|
buy_labour.entries[0].amount = 12;
|
|
test_commands(true, setup, buy_labour, "valid");
|
|
|
|
// cannot buy 0 labour
|
|
buy_labour.entries[0].type = ITEM_LABOUR;
|
|
buy_labour.entries[0].amount = 0;
|
|
test_commands(false, setup, buy_labour, "0 items");
|
|
}
|
|
|
|
TEST(cc_command, execute_trade_maker)
|
|
{
|
|
cryptonote::cc_command_trade_t trade;
|
|
std::vector<cryptonote::cc_command_t> setup;
|
|
|
|
cryptonote::keypair keys = cryptonote::keypair::generate(hw::get_device("default"));
|
|
|
|
// setup
|
|
cryptonote::cc_command_create_account_t create_account;
|
|
create_account.public_key = keys.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 1000*COIN;
|
|
create_account.name = "foo";
|
|
create_account.inviting_account = 0;
|
|
setup.push_back(create_account);
|
|
cryptonote::cc_command_buy_items_t buy_blocks;
|
|
buy_blocks.cc_account = 4;
|
|
cryptonote::cc_command_buy_items_t::entry_t e;
|
|
e.type = ITEM_BASIC_STONE;
|
|
e.amount = 100;
|
|
buy_blocks.entries.push_back(e);
|
|
e.type = ITEM_BASIC_WOOD;
|
|
e.amount = 10;
|
|
buy_blocks.entries.push_back(e);
|
|
setup.push_back(buy_blocks);
|
|
|
|
// good bid
|
|
trade.cc_account = 4;
|
|
trade.bid = true;
|
|
trade.type = cryptonote::cc_command_trade_t::type_item;
|
|
trade.id = ITEM_BASIC_STONE;
|
|
trade.amount = 10;
|
|
trade.price = 10;
|
|
trade.accrual_start_height = 0;
|
|
trade.accrual = 0;
|
|
trade.accrual_price_limit = 0;
|
|
trade.flag_construction_height = 0;
|
|
trade.expiration = MIN_TRADE_EXPIRATION + 1000;
|
|
trade.cost = 0;
|
|
test_commands(true, setup, trade, "valid bid");
|
|
|
|
// good offer
|
|
trade.bid = false;
|
|
test_commands(true, setup, trade, "valid offer");
|
|
|
|
// bad type
|
|
trade.type = 0xff;
|
|
test_commands(false, setup, trade, "bad type");
|
|
trade.type = cryptonote::cc_command_trade_t::type_item;
|
|
|
|
// bad id
|
|
trade.id = 0;
|
|
test_commands(false, setup, trade, "bad id");
|
|
trade.id = ITEM_BASIC_STONE;
|
|
|
|
// bad expiration
|
|
trade.expiration = 1;
|
|
test_commands(false, setup, trade, "bad expiration");
|
|
trade.expiration = MIN_TRADE_EXPIRATION + 1000;
|
|
}
|
|
|
|
TEST(cc_command, execute_building_settings)
|
|
{
|
|
cryptonote::cc_command_buy_land_t buy_land;
|
|
cryptonote::cc_command_buy_items_t buy_blocks;
|
|
cryptonote::cc_command_building_settings_t building_settings;
|
|
std::vector<cryptonote::cc_command_t> setup;
|
|
uint32_t ox, oy;
|
|
cc::get_city_origin(0, ox, oy);
|
|
|
|
cryptonote::keypair keys4 = cryptonote::keypair::generate(hw::get_device("default"));
|
|
cryptonote::keypair keys5 = cryptonote::keypair::generate(hw::get_device("default"));
|
|
|
|
// setup
|
|
cryptonote::cc_command_create_account_t create_account;
|
|
create_account.public_key = keys4.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 2500*COIN;
|
|
create_account.name = "foo";
|
|
create_account.inviting_account = 0;
|
|
setup.push_back(create_account);
|
|
create_account.public_key = keys5.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 2500*COIN;
|
|
create_account.name = "foo 2";
|
|
create_account.inviting_account = 0;
|
|
setup.push_back(create_account);
|
|
buy_blocks.cc_account = 4;
|
|
buy_blocks.entries.push_back({ITEM_BASIC_STONE, 1000});
|
|
buy_blocks.entries.push_back({ITEM_BASIC_WOOD, 500});
|
|
buy_blocks.entries.push_back({ITEM_LABOUR, 2500});
|
|
setup.push_back(buy_blocks);
|
|
buy_blocks.cc_account = 5;
|
|
setup.push_back(buy_blocks);
|
|
buy_land.cc_account = 4;
|
|
buy_land.city = 0;
|
|
buy_land.x = ox + 140;
|
|
buy_land.y = oy + 50;
|
|
buy_land.wm1 = 8;
|
|
buy_land.hm1 = 4;
|
|
ASSERT_TRUE(cc::get_new_flag_cost(buy_land.x, buy_land.y, buy_land.x + buy_land.wm1, buy_land.y + buy_land.hm1, buy_land.cost));
|
|
setup.push_back(buy_land);
|
|
buy_land.cc_account = 5;
|
|
buy_land.city = 0;
|
|
buy_land.x = ox + 240;
|
|
buy_land.y = oy + 50;
|
|
buy_land.wm1 = 8;
|
|
buy_land.hm1 = 4;
|
|
ASSERT_TRUE(cc::get_new_flag_cost(buy_land.x, buy_land.y, buy_land.x + buy_land.wm1, buy_land.y + buy_land.hm1, buy_land.cost));
|
|
setup.push_back(buy_land);
|
|
|
|
// good
|
|
building_settings.cc_account = 4;
|
|
building_settings.flag = 1 + PREDEFINED_BULDINGS;
|
|
building_settings.role = ROLE_RESIDENTIAL1;
|
|
building_settings.economic_power = 100;
|
|
test_commands(true, setup, building_settings, "valid");
|
|
|
|
// invalid flag
|
|
building_settings.flag = 8 + PREDEFINED_BULDINGS;
|
|
test_commands(false, setup, building_settings, "invalid flag");
|
|
building_settings.flag = 1 + PREDEFINED_BULDINGS;
|
|
|
|
// valid flag owned by someone else
|
|
building_settings.flag = 2 + PREDEFINED_BULDINGS;
|
|
test_commands(false, setup, building_settings, "flag not owned");
|
|
building_settings.flag = 1 + PREDEFINED_BULDINGS;
|
|
|
|
// cannot set to empty
|
|
building_settings.role = ROLE_EMPTY;
|
|
test_commands(false, setup, building_settings, "empty role");
|
|
building_settings.role = ROLE_AGRICULTURAL;
|
|
|
|
// nor to out of range
|
|
building_settings.role = NUM_ROLES;
|
|
test_commands(false, setup, building_settings, "invalid role");
|
|
building_settings.role = ROLE_AGRICULTURAL;
|
|
|
|
// cannot set 0 economic power
|
|
building_settings.economic_power = 0;
|
|
test_commands(false, setup, building_settings, "0 economic power");
|
|
building_settings.economic_power = 100;
|
|
|
|
// nor a non canonical value that's in range
|
|
building_settings.economic_power = 404;
|
|
test_commands(false, setup, building_settings, "404 economic power");
|
|
building_settings.economic_power = 100;
|
|
|
|
// nor silly value
|
|
building_settings.economic_power = 100000;
|
|
test_commands(false, setup, building_settings, "silly economic power");
|
|
building_settings.economic_power = 100;
|
|
}
|
|
|
|
TEST(cc_command, execute_assign_items)
|
|
{
|
|
cryptonote::cc_command_buy_land_t buy_land;
|
|
cryptonote::cc_command_buy_items_t buy_items;
|
|
cryptonote::cc_command_building_settings_t building_settings;
|
|
cryptonote::cc_command_assign_items_t assign;
|
|
std::vector<cryptonote::cc_command_t> setup;
|
|
uint32_t ox, oy;
|
|
cc::get_city_origin(0, ox, oy);
|
|
|
|
cryptonote::keypair keys4 = cryptonote::keypair::generate(hw::get_device("default"));
|
|
cryptonote::keypair keys5 = cryptonote::keypair::generate(hw::get_device("default"));
|
|
|
|
// setup
|
|
cryptonote::cc_command_create_account_t create_account;
|
|
create_account.public_key = keys4.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 2000*COIN;
|
|
create_account.name = "foo";
|
|
create_account.inviting_account = 0;
|
|
setup.push_back(create_account);
|
|
create_account.public_key = keys5.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 2000*COIN;
|
|
create_account.name = "foo 2";
|
|
create_account.inviting_account = 0;
|
|
setup.push_back(create_account);
|
|
buy_items.cc_account = 4;
|
|
buy_items.entries.push_back({ITEM_BASIC_STONE, 1000});
|
|
buy_items.entries.push_back({ITEM_BASIC_WOOD, 1500});
|
|
buy_items.entries.push_back({ITEM_LABOUR, 5200});
|
|
setup.push_back(buy_items);
|
|
buy_land.cc_account = 4;
|
|
buy_land.city = 0;
|
|
buy_land.x = ox + 140;
|
|
buy_land.y = oy + 50;
|
|
buy_land.wm1 = 11;
|
|
buy_land.hm1 = 11;
|
|
ASSERT_TRUE(cc::get_new_flag_cost(buy_land.x, buy_land.y, buy_land.x + buy_land.wm1, buy_land.y + buy_land.hm1, buy_land.cost));
|
|
setup.push_back(buy_land);
|
|
buy_land.cc_account = 5;
|
|
buy_land.city = 0;
|
|
buy_land.x = ox + 240;
|
|
buy_land.y = oy + 50;
|
|
buy_land.wm1 = 8;
|
|
buy_land.hm1 = 4;
|
|
ASSERT_TRUE(cc::get_new_flag_cost(buy_land.x, buy_land.y, buy_land.x + buy_land.wm1, buy_land.y + buy_land.hm1, buy_land.cost));
|
|
setup.push_back(buy_land);
|
|
building_settings.cc_account = 4;
|
|
building_settings.flag = 1 + PREDEFINED_BULDINGS;
|
|
building_settings.role = ROLE_RESIDENTIAL1;
|
|
building_settings.economic_power = 100;
|
|
setup.push_back(building_settings);
|
|
|
|
// good
|
|
assign.cc_account = 4;
|
|
assign.flag = 1 + PREDEFINED_BULDINGS;
|
|
assign.items.push_back({ITEM_BASIC_STONE, 1});
|
|
test_commands(true, setup, assign, "valid");
|
|
|
|
// wrong account
|
|
assign.cc_account = 4444;
|
|
assign.flag = 1 + PREDEFINED_BULDINGS;
|
|
assign.items[0] = {1, 1};
|
|
test_commands(false, setup, assign, "wrong account");
|
|
|
|
// wrong flag
|
|
assign.cc_account = 4;
|
|
assign.flag = 700 + PREDEFINED_BULDINGS;
|
|
assign.items[0] = {1, 1};
|
|
test_commands(false, setup, assign, "wrong flag");
|
|
|
|
// 0 amount
|
|
assign.cc_account = 4;
|
|
assign.flag = 1 + PREDEFINED_BULDINGS;
|
|
assign.items[0] = {1, 0};
|
|
test_commands(false, setup, assign, "0 amount");
|
|
|
|
// too many
|
|
assign.cc_account = 4;
|
|
assign.flag = 1 + PREDEFINED_BULDINGS;
|
|
assign.items[0] = {1, 999999999};
|
|
test_commands(false, setup, assign, "too many");
|
|
|
|
// invalid item
|
|
assign.cc_account = 4;
|
|
assign.flag = 1 + PREDEFINED_BULDINGS;
|
|
assign.items[0] = {1111111111, 1};
|
|
test_commands(false, setup, assign, "invalid item");
|
|
|
|
// duplicate item
|
|
assign.cc_account = 4;
|
|
assign.flag = 1 + PREDEFINED_BULDINGS;
|
|
assign.items[0] = {ITEM_BASIC_STONE, 1};
|
|
assign.items.push_back({ITEM_BASIC_STONE, 1});
|
|
test_commands(false, setup, assign, "duplicate item");
|
|
assign.items.pop_back();
|
|
}
|
|
|
|
TEST(cc_command, execute_palette)
|
|
{
|
|
cryptonote::cc_command_buy_land_t buy_land;
|
|
cryptonote::cc_command_buy_items_t buy_items;
|
|
cryptonote::cc_command_building_settings_t building_settings;
|
|
cryptonote::cc_command_palette_t palette;
|
|
std::vector<cryptonote::cc_command_t> setup;
|
|
uint32_t ox, oy;
|
|
cc::get_city_origin(0, ox, oy);
|
|
|
|
cryptonote::keypair keys4 = cryptonote::keypair::generate(hw::get_device("default"));
|
|
|
|
// setup
|
|
cryptonote::cc_command_create_account_t create_account;
|
|
create_account.public_key = keys4.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 2500*COIN;
|
|
create_account.name = "foo";
|
|
create_account.inviting_account = 0;
|
|
setup.push_back(create_account);
|
|
buy_items.cc_account = 4;
|
|
buy_items.entries.push_back({ITEM_BASIC_STONE, 1000});
|
|
buy_items.entries.push_back({ITEM_BASIC_WOOD, 500});
|
|
buy_items.entries.push_back({ITEM_LABOUR, 2500});
|
|
setup.push_back(buy_items);
|
|
buy_land.cc_account = 4;
|
|
buy_land.city = 0;
|
|
buy_land.x = ox + 140;
|
|
buy_land.y = oy + 150;
|
|
buy_land.wm1 = 8;
|
|
buy_land.hm1 = 4;
|
|
ASSERT_TRUE(cc::get_new_flag_cost(buy_land.x, buy_land.y, buy_land.x + buy_land.wm1, buy_land.y + buy_land.hm1, buy_land.cost));
|
|
setup.push_back(buy_land);
|
|
building_settings.cc_account = 4;
|
|
building_settings.flag = 1 + PREDEFINED_BULDINGS;
|
|
building_settings.role = ROLE_RESIDENTIAL1;
|
|
building_settings.economic_power = 100;
|
|
setup.push_back(building_settings);
|
|
|
|
// empty
|
|
palette.cc_account = 4;
|
|
palette.flag = 1 + PREDEFINED_BULDINGS;
|
|
test_commands(false, setup, palette, "empty");
|
|
|
|
// good - set index 255 to default sandstone
|
|
palette.flag = 1 + PREDEFINED_BULDINGS;
|
|
palette.palette = {{255, cc::BLOCK_VARIANT_SANDSTONE_BASIC}};
|
|
test_commands(true, setup, palette, "good", [](TestDB *db) {
|
|
cryptonote::cc_flag_data_t fd;
|
|
ASSERT_TRUE(db->get_cc_flag_data(1 + PREDEFINED_BULDINGS, fd, NULL));
|
|
ASSERT_EQ(fd.palette.size(), 256);
|
|
ASSERT_EQ(fd.palette[0], cc::BLOCK_VARIANT_NONE);
|
|
ASSERT_EQ(fd.palette[254], cc::BLOCK_VARIANT_NONE);
|
|
ASSERT_EQ(fd.palette[255], cc::BLOCK_VARIANT_SANDSTONE_BASIC);
|
|
});
|
|
|
|
// bad - changes nothing
|
|
palette.palette = {{1, 0}};
|
|
test_commands(false, setup, palette, "no change");
|
|
|
|
// good - set 2 to sandstone cobblestones intead of default granite
|
|
palette.palette = {{2, cc::BLOCK_VARIANT_SANDSTONE_COBBLESTONES - cc::BLOCK_VARIANT_GRANITE_BASIC}};
|
|
test_commands(true, setup, palette, "good", [](TestDB *db) {
|
|
std::vector<uint16_t> default_palette;
|
|
cc::set_default_palette(default_palette, 0);
|
|
cryptonote::cc_flag_data_t fd;
|
|
ASSERT_TRUE(db->get_cc_flag_data(1 + PREDEFINED_BULDINGS, fd, NULL));
|
|
ASSERT_EQ(fd.palette.size(), std::max<size_t>(3, default_palette.size()));
|
|
ASSERT_EQ(fd.palette[0], cc::BLOCK_VARIANT_NONE);
|
|
ASSERT_EQ(fd.palette[1], cc::BLOCK_VARIANT_SANDSTONE_BASIC);
|
|
ASSERT_EQ(fd.palette[2], cc::BLOCK_VARIANT_SANDSTONE_COBBLESTONES);
|
|
});
|
|
|
|
// good - set granite cobblestones instead of default sandstone
|
|
palette.palette = {{1, cc::BLOCK_VARIANT_GRANITE_COBBLESTONES - cc::BLOCK_VARIANT_SANDSTONE_BASIC}};
|
|
test_commands(true, setup, palette, "good", [](TestDB *db) {
|
|
std::vector<uint16_t> default_palette;
|
|
cc::set_default_palette(default_palette, 0);
|
|
cryptonote::cc_flag_data_t fd;
|
|
ASSERT_TRUE(db->get_cc_flag_data(1 + PREDEFINED_BULDINGS, fd, NULL));
|
|
ASSERT_EQ(fd.palette.size(), default_palette.size());
|
|
ASSERT_EQ(fd.palette[0], cc::BLOCK_VARIANT_NONE);
|
|
ASSERT_EQ(fd.palette[1], cc::BLOCK_VARIANT_GRANITE_COBBLESTONES);
|
|
});
|
|
|
|
// bad - invalid variant
|
|
palette.palette = {{1, (int16_t)cc::NUM_BLOCK_VARIANTS}};
|
|
test_commands(false, setup, palette, "invalid variant");
|
|
|
|
// good - removes palette index 1
|
|
palette.palette = {{1, -cc::BLOCK_VARIANT_SANDSTONE_BASIC}};
|
|
test_commands(true, setup, palette, "good", [](TestDB *db) {
|
|
std::vector<uint16_t> default_palette;
|
|
cc::set_default_palette(default_palette, 0);
|
|
cryptonote::cc_flag_data_t fd;
|
|
ASSERT_TRUE(db->get_cc_flag_data(1 + PREDEFINED_BULDINGS, fd, NULL));
|
|
ASSERT_EQ(fd.palette.size(), default_palette.size());
|
|
ASSERT_EQ(fd.palette[0], cc::BLOCK_VARIANT_NONE);
|
|
ASSERT_EQ(fd.palette[1], cc::BLOCK_VARIANT_NONE);
|
|
});
|
|
|
|
// bad - duplicate index
|
|
palette.palette = {{1, 1}, {1, 1}};
|
|
test_commands(false, setup, palette, "duplicate index");
|
|
|
|
// bad - unsorted indices
|
|
palette.palette = {{2, 1}, {1, 1}};
|
|
test_commands(false, setup, palette, "unsorted indices");
|
|
}
|
|
|
|
TEST(cc_block_encoding, empty)
|
|
{
|
|
std::vector<uint8_t> blocks;
|
|
uint8_t encoded[65536], decoded[65536];
|
|
uint32_t encoded_len, decoded_len;
|
|
ASSERT_TRUE(cc::encode_blocks(blocks.data(), blocks.size(), encoded, &encoded_len));
|
|
ASSERT_EQ(encoded_len, 0);
|
|
ASSERT_TRUE(cc::decode_blocks(encoded, encoded_len, decoded, &decoded_len));
|
|
ASSERT_EQ(decoded_len, blocks.size());
|
|
ASSERT_EQ(blocks, std::vector<uint8_t>(decoded, decoded + decoded_len));
|
|
}
|
|
|
|
TEST(cc_block_encoding, 1)
|
|
{
|
|
std::vector<uint8_t> blocks = {3};
|
|
uint8_t encoded[65536], decoded[65536];
|
|
uint32_t encoded_len, decoded_len;
|
|
unsigned int idx = 0;
|
|
ASSERT_TRUE(cc::encode_blocks(blocks.data(), blocks.size(), encoded, &encoded_len));
|
|
ASSERT_EQ(encoded_len, 2);
|
|
ASSERT_EQ(encoded[idx++], 0);
|
|
ASSERT_EQ(encoded[idx++], 3);
|
|
ASSERT_TRUE(cc::decode_blocks(encoded, encoded_len, decoded, &decoded_len));
|
|
ASSERT_EQ(decoded_len, blocks.size());
|
|
ASSERT_EQ(blocks, std::vector<uint8_t>(decoded, decoded + decoded_len));
|
|
}
|
|
|
|
TEST(cc_block_encoding, 4)
|
|
{
|
|
std::vector<uint8_t> blocks = {3, 3, 3, 3};
|
|
uint8_t encoded[65536], decoded[65536];
|
|
uint32_t encoded_len, decoded_len;
|
|
unsigned int idx = 0;
|
|
ASSERT_TRUE(cc::encode_blocks(blocks.data(), blocks.size(), encoded, &encoded_len));
|
|
ASSERT_EQ(encoded_len, 2);
|
|
ASSERT_EQ(encoded[idx++], 0x83);
|
|
ASSERT_EQ(encoded[idx++], 3);
|
|
ASSERT_TRUE(cc::decode_blocks(encoded, encoded_len, decoded, &decoded_len));
|
|
ASSERT_EQ(decoded_len, blocks.size());
|
|
ASSERT_EQ(blocks, std::vector<uint8_t>(decoded, decoded + decoded_len));
|
|
}
|
|
|
|
TEST(cc_block_encoding, 2_2)
|
|
{
|
|
std::vector<uint8_t> blocks = {3, 3, 2, 2};
|
|
uint8_t encoded[65536], decoded[65536];
|
|
uint32_t encoded_len, decoded_len;
|
|
unsigned int idx = 0;
|
|
ASSERT_TRUE(cc::encode_blocks(blocks.data(), blocks.size(), encoded, &encoded_len));
|
|
ASSERT_EQ(encoded_len, 4);
|
|
ASSERT_EQ(encoded[idx++], 0x81);
|
|
ASSERT_EQ(encoded[idx++], 3);
|
|
ASSERT_EQ(encoded[idx++], 0x81);
|
|
ASSERT_EQ(encoded[idx++], 2);
|
|
ASSERT_TRUE(cc::decode_blocks(encoded, encoded_len, decoded, &decoded_len));
|
|
ASSERT_EQ(decoded_len, blocks.size());
|
|
ASSERT_EQ(blocks, std::vector<uint8_t>(decoded, decoded + decoded_len));
|
|
}
|
|
|
|
TEST(cc_block_encoding, 2_1_1_1_1_1_2)
|
|
{
|
|
std::vector<uint8_t> blocks = {3, 3, 1, 2, 3, 4, 5, 2, 2};
|
|
uint8_t encoded[65536], decoded[65536];
|
|
uint32_t encoded_len, decoded_len;
|
|
unsigned int idx = 0;
|
|
ASSERT_TRUE(cc::encode_blocks(blocks.data(), blocks.size(), encoded, &encoded_len));
|
|
ASSERT_EQ(encoded_len, 10);
|
|
ASSERT_EQ(encoded[idx++], 0x81);
|
|
ASSERT_EQ(encoded[idx++], 3);
|
|
ASSERT_EQ(encoded[idx++], 0x4);
|
|
ASSERT_EQ(encoded[idx++], 1);
|
|
ASSERT_EQ(encoded[idx++], 2);
|
|
ASSERT_EQ(encoded[idx++], 3);
|
|
ASSERT_EQ(encoded[idx++], 4);
|
|
ASSERT_EQ(encoded[idx++], 5);
|
|
ASSERT_EQ(encoded[idx++], 0x81);
|
|
ASSERT_EQ(encoded[idx++], 2);
|
|
ASSERT_TRUE(cc::decode_blocks(encoded, encoded_len, decoded, &decoded_len));
|
|
ASSERT_EQ(decoded_len, blocks.size());
|
|
ASSERT_EQ(blocks, std::vector<uint8_t>(decoded, decoded + decoded_len));
|
|
}
|
|
|
|
TEST(cc_block_encoding, 1_1_1_1)
|
|
{
|
|
std::vector<uint8_t> blocks = {1, 2, 3, 4};
|
|
uint8_t encoded[65536], decoded[65536];
|
|
uint32_t encoded_len, decoded_len;
|
|
unsigned int idx = 0;
|
|
ASSERT_TRUE(cc::encode_blocks(blocks.data(), blocks.size(), encoded, &encoded_len));
|
|
EXPECT_EQ(encoded_len, 5);
|
|
EXPECT_EQ(encoded[idx++], 0x3);
|
|
EXPECT_EQ(encoded[idx++], 1);
|
|
EXPECT_EQ(encoded[idx++], 2);
|
|
EXPECT_EQ(encoded[idx++], 3);
|
|
EXPECT_EQ(encoded[idx++], 4);
|
|
ASSERT_TRUE(cc::decode_blocks(encoded, encoded_len, decoded, &decoded_len));
|
|
ASSERT_EQ(decoded_len, blocks.size());
|
|
ASSERT_EQ(blocks, std::vector<uint8_t>(decoded, decoded + decoded_len));
|
|
}
|
|
|
|
TEST(cc_block_encoding, 1_2_1)
|
|
{
|
|
std::vector<uint8_t> blocks = {1, 2, 2, 4};
|
|
uint8_t encoded[65536], decoded[65536];
|
|
uint32_t encoded_len, decoded_len;
|
|
unsigned int idx = 0;
|
|
ASSERT_TRUE(cc::encode_blocks(blocks.data(), blocks.size(), encoded, &encoded_len));
|
|
ASSERT_EQ(encoded_len, 6);
|
|
ASSERT_EQ(encoded[idx++], 0x0);
|
|
ASSERT_EQ(encoded[idx++], 1);
|
|
ASSERT_EQ(encoded[idx++], 0x81);
|
|
ASSERT_EQ(encoded[idx++], 2);
|
|
ASSERT_EQ(encoded[idx++], 0x0);
|
|
ASSERT_EQ(encoded[idx++], 4);
|
|
ASSERT_TRUE(cc::decode_blocks(encoded, encoded_len, decoded, &decoded_len));
|
|
ASSERT_EQ(decoded_len, blocks.size());
|
|
ASSERT_EQ(blocks, std::vector<uint8_t>(decoded, decoded + decoded_len));
|
|
}
|
|
|
|
TEST(cc_block_encoding, 127)
|
|
{
|
|
std::vector<uint8_t> blocks(127, 9);
|
|
uint8_t encoded[65536], decoded[65536];
|
|
uint32_t encoded_len, decoded_len;
|
|
unsigned int idx = 0;
|
|
ASSERT_TRUE(cc::encode_blocks(blocks.data(), blocks.size(), encoded, &encoded_len));
|
|
ASSERT_EQ(encoded_len, 2);
|
|
ASSERT_EQ(encoded[idx++], 0xfe);
|
|
ASSERT_EQ(encoded[idx++], 9);
|
|
ASSERT_TRUE(cc::decode_blocks(encoded, encoded_len, decoded, &decoded_len));
|
|
ASSERT_EQ(decoded_len, blocks.size());
|
|
ASSERT_EQ(blocks, std::vector<uint8_t>(decoded, decoded + decoded_len));
|
|
}
|
|
|
|
TEST(cc_block_encoding, 128)
|
|
{
|
|
std::vector<uint8_t> blocks(128, 9);
|
|
uint8_t encoded[65536], decoded[65536];
|
|
uint32_t encoded_len, decoded_len;
|
|
unsigned int idx = 0;
|
|
ASSERT_TRUE(cc::encode_blocks(blocks.data(), blocks.size(), encoded, &encoded_len));
|
|
ASSERT_EQ(encoded_len, 2);
|
|
ASSERT_EQ(encoded[idx++], 0xff);
|
|
ASSERT_EQ(encoded[idx++], 9);
|
|
ASSERT_TRUE(cc::decode_blocks(encoded, encoded_len, decoded, &decoded_len));
|
|
ASSERT_EQ(decoded_len, blocks.size());
|
|
ASSERT_EQ(blocks, std::vector<uint8_t>(decoded, decoded + decoded_len));
|
|
}
|
|
|
|
TEST(cc_block_encoding, 129)
|
|
{
|
|
std::vector<uint8_t> blocks(129, 9);
|
|
uint8_t encoded[65536], decoded[65536];
|
|
uint32_t encoded_len, decoded_len;
|
|
unsigned int idx = 0;
|
|
ASSERT_TRUE(cc::encode_blocks(blocks.data(), blocks.size(), encoded, &encoded_len));
|
|
ASSERT_EQ(encoded_len, 4);
|
|
ASSERT_EQ(encoded[idx++], 0xff);
|
|
ASSERT_EQ(encoded[idx++], 9);
|
|
ASSERT_EQ(encoded[idx++], 0);
|
|
ASSERT_EQ(encoded[idx++], 9);
|
|
ASSERT_TRUE(cc::decode_blocks(encoded, encoded_len, decoded, &decoded_len));
|
|
ASSERT_EQ(decoded_len, blocks.size());
|
|
ASSERT_EQ(blocks, std::vector<uint8_t>(decoded, decoded + decoded_len));
|
|
}
|
|
|
|
TEST(cc_block_encoding, 384)
|
|
{
|
|
std::vector<uint8_t> blocks(384, 9);
|
|
uint8_t encoded[65536], decoded[65536];
|
|
uint32_t encoded_len, decoded_len;
|
|
unsigned int idx = 0;
|
|
ASSERT_TRUE(cc::encode_blocks(blocks.data(), blocks.size(), encoded, &encoded_len));
|
|
ASSERT_EQ(encoded_len, 6);
|
|
ASSERT_EQ(encoded[idx++], 0xff);
|
|
ASSERT_EQ(encoded[idx++], 9);
|
|
ASSERT_EQ(encoded[idx++], 0xff);
|
|
ASSERT_EQ(encoded[idx++], 9);
|
|
ASSERT_EQ(encoded[idx++], 0xff);
|
|
ASSERT_EQ(encoded[idx++], 9);
|
|
ASSERT_TRUE(cc::decode_blocks(encoded, encoded_len, decoded, &decoded_len));
|
|
ASSERT_EQ(decoded_len, blocks.size());
|
|
ASSERT_EQ(blocks, std::vector<uint8_t>(decoded, decoded + decoded_len));
|
|
}
|
|
|
|
TEST(cc_block_encoding, 65536)
|
|
{
|
|
std::vector<uint8_t> blocks(65536, 9);
|
|
uint8_t encoded[65536], decoded[65536];
|
|
uint32_t encoded_len, decoded_len;
|
|
unsigned int idx = 0;
|
|
ASSERT_TRUE(cc::encode_blocks(blocks.data(), blocks.size(), encoded, &encoded_len));
|
|
ASSERT_EQ(encoded_len, 512*2);
|
|
for (int i = 0; i < 512; ++i)
|
|
{
|
|
ASSERT_EQ(encoded[idx++], 0xff);
|
|
ASSERT_EQ(encoded[idx++], 9);
|
|
}
|
|
ASSERT_TRUE(cc::decode_blocks(encoded, encoded_len, decoded, &decoded_len));
|
|
ASSERT_EQ(decoded_len, blocks.size());
|
|
ASSERT_EQ(blocks, std::vector<uint8_t>(decoded, decoded + decoded_len));
|
|
}
|
|
|
|
TEST(cc_block_encoding, 65537)
|
|
{
|
|
std::vector<uint8_t> blocks(65537, 9);
|
|
uint8_t encoded[65536], decoded[65536];
|
|
uint32_t encoded_len, decoded_len;
|
|
unsigned int idx = 0;
|
|
ASSERT_FALSE(cc::encode_blocks(blocks.data(), blocks.size(), encoded, &encoded_len));
|
|
}
|
|
|
|
TEST(cc_block_encoding, encoding_too_large)
|
|
{
|
|
std::vector<uint8_t> blocks;
|
|
blocks.resize(65500);
|
|
for (int i = 0; i < 65500; ++i)
|
|
blocks[i] = i;
|
|
uint8_t encoded[65536];
|
|
uint32_t encoded_len;
|
|
ASSERT_FALSE(cc::encode_blocks(blocks.data(), blocks.size(), encoded, &encoded_len));
|
|
}
|
|
|
|
TEST(cc_block_encoding, decoding_too_large_new_run)
|
|
{
|
|
std::vector<uint8_t> encoded;
|
|
for (int i = 0; i < 513; ++i)
|
|
{
|
|
encoded.push_back(0xff);
|
|
encoded.push_back(0);
|
|
}
|
|
uint8_t decoded[65537];
|
|
decoded[65536] = 0x42;
|
|
uint32_t decoded_len;
|
|
EXPECT_FALSE(cc::decode_blocks(encoded.data(), encoded.size(), decoded, &decoded_len));
|
|
ASSERT_EQ(decoded[65536], 0x42);
|
|
}
|
|
|
|
TEST(cc_block_encoding, decoding_too_large_in_raw_run)
|
|
{
|
|
std::vector<uint8_t> encoded;
|
|
for (int i = 0; i < 511; ++i)
|
|
{
|
|
encoded.push_back(0xff);
|
|
encoded.push_back(0);
|
|
}
|
|
encoded.push_back(0xfe);
|
|
encoded.push_back(0);
|
|
encoded.push_back(2);
|
|
encoded.push_back(0);
|
|
encoded.push_back(0);
|
|
|
|
uint8_t decoded[65537];
|
|
decoded[65536] = 0x42;
|
|
uint32_t decoded_len;
|
|
EXPECT_FALSE(cc::decode_blocks(encoded.data(), encoded.size(), decoded, &decoded_len));
|
|
ASSERT_EQ(decoded[65536], 0x42);
|
|
}
|
|
|
|
TEST(cc_block_encoding, decoding_too_large_in_rle_run)
|
|
{
|
|
std::vector<uint8_t> encoded;
|
|
for (int i = 0; i < 511; ++i)
|
|
{
|
|
encoded.push_back(0xff);
|
|
encoded.push_back(0);
|
|
}
|
|
encoded.push_back(0xfe);
|
|
encoded.push_back(0);
|
|
encoded.push_back(0x82);
|
|
encoded.push_back(0);
|
|
|
|
uint8_t decoded[65537];
|
|
decoded[65536] = 0x42;
|
|
uint32_t decoded_len;
|
|
EXPECT_FALSE(cc::decode_blocks(encoded.data(), encoded.size(), decoded, &decoded_len));
|
|
ASSERT_EQ(decoded[65536], 0x42);
|
|
}
|
|
|
|
TEST(cc_selection, empty)
|
|
{
|
|
Selection s;
|
|
ASSERT_TRUE(s.is_empty());
|
|
}
|
|
|
|
TEST(cc_selection, union)
|
|
{
|
|
Selection s_empty;
|
|
Selection s_one(4,4,4,4);
|
|
Selection s;
|
|
Selection s_two_diag_slash(4,4,5,5);
|
|
Selection s_two_diag_backslash(4,4,5,5);
|
|
|
|
s_two_diag_slash.clear(4, 4);
|
|
s_two_diag_slash.clear(5, 5);
|
|
ASSERT_EQ(s_two_diag_slash.get_num_selected_points(), 2);
|
|
s_two_diag_backslash.clear(4, 5);
|
|
s_two_diag_backslash.clear(5, 4);
|
|
ASSERT_EQ(s_two_diag_backslash.get_num_selected_points(), 2);
|
|
|
|
s = s_empty;
|
|
s.set_union(s_empty);
|
|
ASSERT_EQ(s, s_empty);
|
|
|
|
s = s_empty;
|
|
s.set_union(s_one);
|
|
ASSERT_EQ(s, s_one);
|
|
|
|
s = s_one;
|
|
s.set_union(s_empty);
|
|
ASSERT_EQ(s, s_one);
|
|
|
|
s = s_one;
|
|
s.set_union(s_one);
|
|
ASSERT_EQ(s, s_one);
|
|
|
|
s = s_empty;
|
|
s.set_union(s_two_diag_slash);
|
|
ASSERT_EQ(s.get_num_selected_points(), 2);
|
|
|
|
s = s_one;
|
|
s.set_union(s_two_diag_slash);
|
|
ASSERT_EQ(s.get_num_selected_points(), 3);
|
|
|
|
s = s_one;
|
|
s.set_union(s_two_diag_backslash);
|
|
ASSERT_EQ(s.get_num_selected_points(), 2);
|
|
ASSERT_EQ(s, s_two_diag_backslash);
|
|
|
|
s = s_two_diag_slash;
|
|
s.set_union(s_two_diag_slash);
|
|
ASSERT_EQ(s.get_num_selected_points(), 2);
|
|
ASSERT_EQ(s, s_two_diag_slash);
|
|
|
|
s = s_two_diag_slash;
|
|
s.set_union(s_two_diag_backslash);
|
|
ASSERT_EQ(s.get_num_selected_points(), 4);
|
|
|
|
Selection s1, s3;
|
|
s1.set_rectangle(1, 1, 1, 1);
|
|
s3.set_rectangle(3, 3, 3, 3);
|
|
s = s1;
|
|
s.set_union(s3);
|
|
ASSERT_EQ(s.get_num_selected_points(), 2);
|
|
ASSERT_TRUE(s.is_selected(1, 1));
|
|
ASSERT_FALSE(s.is_selected(2, 2));
|
|
ASSERT_TRUE(s.is_selected(3, 3));
|
|
|
|
s1.set_rectangle(2, 1, 2, 3);
|
|
s3.set_rectangle(1, 2, 3, 2);
|
|
s = s1;
|
|
s.set_union(s3);
|
|
ASSERT_EQ(s.get_num_selected_points(), 5);
|
|
ASSERT_TRUE(s.is_selected(2, 1));
|
|
ASSERT_TRUE(s.is_selected(2, 2));
|
|
ASSERT_TRUE(s.is_selected(2, 3));
|
|
ASSERT_TRUE(s.is_selected(1, 2));
|
|
ASSERT_TRUE(s.is_selected(3, 2));
|
|
}
|
|
|
|
TEST(cc_selection, intersection)
|
|
{
|
|
Selection s_empty;
|
|
Selection s_one(4,4,4,4);
|
|
Selection s;
|
|
Selection s_two_diag_slash(4,4,5,5);
|
|
Selection s_two_diag_backslash(4,4,5,5);
|
|
|
|
s_two_diag_slash.clear(4, 4);
|
|
s_two_diag_slash.clear(5, 5);
|
|
ASSERT_EQ(s_two_diag_slash.get_num_selected_points(), 2);
|
|
s_two_diag_backslash.clear(4, 5);
|
|
s_two_diag_backslash.clear(5, 4);
|
|
ASSERT_EQ(s_two_diag_backslash.get_num_selected_points(), 2);
|
|
|
|
s = s_empty;
|
|
s.set_intersection(s_empty);
|
|
ASSERT_EQ(s, s_empty);
|
|
|
|
s = s_one;
|
|
s.set_intersection(s_empty);
|
|
ASSERT_EQ(s, s_empty);
|
|
|
|
s = s_empty;
|
|
s.set_intersection(s_one);
|
|
ASSERT_EQ(s, s_empty);
|
|
|
|
s = s_one;
|
|
s.set_intersection(s_one);
|
|
ASSERT_EQ(s, s_one);
|
|
|
|
s = s_one;
|
|
s.set_intersection(s_two_diag_backslash);
|
|
ASSERT_EQ(s, s_one);
|
|
|
|
s = s_one;
|
|
s.set_intersection(s_two_diag_slash);
|
|
ASSERT_EQ(s.get_num_selected_points(), 0);
|
|
|
|
s = s_two_diag_backslash;
|
|
s.set_intersection(s_two_diag_slash);
|
|
ASSERT_EQ(s.get_num_selected_points(), 0);
|
|
|
|
Selection s1, s3;
|
|
s1.set_rectangle(2, 1, 2, 3);
|
|
s3.set_rectangle(1, 2, 3, 2);
|
|
s = s1;
|
|
s.set_intersection(s3);
|
|
ASSERT_EQ(s.get_num_selected_points(), 1);
|
|
ASSERT_TRUE(s.is_selected(2, 2));
|
|
}
|
|
|
|
TEST(cc_selection, difference)
|
|
{
|
|
Selection s_empty;
|
|
Selection s_one(4,4,4,4);
|
|
Selection s;
|
|
Selection s_two_diag_slash(4,4,5,5);
|
|
Selection s_two_diag_backslash(4,4,5,5);
|
|
|
|
s_two_diag_slash.clear(4, 4);
|
|
s_two_diag_slash.clear(5, 5);
|
|
ASSERT_EQ(s_two_diag_slash.get_num_selected_points(), 2);
|
|
s_two_diag_backslash.clear(4, 5);
|
|
s_two_diag_backslash.clear(5, 4);
|
|
ASSERT_EQ(s_two_diag_backslash.get_num_selected_points(), 2);
|
|
|
|
s = s_empty;
|
|
s.set_difference(s_empty);
|
|
ASSERT_EQ(s, s_empty);
|
|
|
|
s = s_one;
|
|
s.set_difference(s_empty);
|
|
ASSERT_EQ(s, s_one);
|
|
|
|
s = s_empty;
|
|
s.set_difference(s_one);
|
|
ASSERT_EQ(s, s_empty);
|
|
|
|
s = s_one;
|
|
s.set_difference(s_one);
|
|
ASSERT_EQ(s.get_num_selected_points(), 0);
|
|
|
|
s = s_one;
|
|
s.set_difference(s_two_diag_backslash);
|
|
ASSERT_EQ(s.get_num_selected_points(), 0);
|
|
|
|
s = s_one;
|
|
s.set_difference(s_two_diag_slash);
|
|
ASSERT_EQ(s, s_one);
|
|
|
|
s = s_two_diag_backslash;
|
|
s.set_difference(s_two_diag_slash);
|
|
ASSERT_EQ(s, s_two_diag_backslash);
|
|
|
|
Selection s1, s3;
|
|
s1.set_rectangle(2, 1, 2, 3);
|
|
s3.set_rectangle(1, 2, 3, 2);
|
|
s = s1;
|
|
s.set_difference(s3);
|
|
ASSERT_EQ(s.get_num_selected_points(), 2);
|
|
ASSERT_TRUE(s.is_selected(2, 1));
|
|
ASSERT_TRUE(s.is_selected(2, 3));
|
|
}
|
|
|
|
TEST(cc_magica, 1x1x1x1)
|
|
{
|
|
MagicaModel m;
|
|
std::list<MagicaModel> mm;
|
|
std::string s;
|
|
|
|
m.width = m.height = m.depth = 1;
|
|
m.data.push_back(1);
|
|
ASSERT_TRUE(save_magica_data({m}, s));
|
|
ASSERT_TRUE(load_magica_data(mm, s));
|
|
ASSERT_EQ(mm.size(), 1);
|
|
ASSERT_EQ(mm.front(), m);
|
|
}
|
|
|
|
TEST(cc_magica, 1x255x255x255)
|
|
{
|
|
MagicaModel m;
|
|
std::list<MagicaModel> mm;
|
|
std::string s;
|
|
|
|
m.width = m.height = m.depth = 255;
|
|
m.data.resize(255*255*255);
|
|
for (size_t i = 0; i < 255*255*255; ++i)
|
|
m.data[i] = rand() & 1;
|
|
ASSERT_TRUE(save_magica_data({m}, s));
|
|
ASSERT_TRUE(load_magica_data(mm, s));
|
|
ASSERT_EQ(mm.size(), 1);
|
|
ASSERT_EQ(mm.front(), m);
|
|
}
|
|
|
|
TEST(cc_magica, 12x36x19_4x8x2_1x1x2)
|
|
{
|
|
MagicaModel m0, m1, m2;
|
|
std::list<MagicaModel> mm;
|
|
std::string s;
|
|
|
|
m0.width = 12;
|
|
m0.height = 36;
|
|
m0.depth = 19;
|
|
for (size_t i = 0; i < 12 * 36 * 19; ++i)
|
|
m0.data.push_back(rand() & 1);
|
|
m1.width = 4;
|
|
m1.height = 8;
|
|
m1.depth = 2;
|
|
for (size_t i = 0; i < 4 * 8 * 2; ++i)
|
|
m1.data.push_back(rand() & 1);
|
|
m2.width = 1;
|
|
m2.height = 1;
|
|
m2.depth = 2;
|
|
for (size_t i = 0; i < 1 * 1 * 2; ++i)
|
|
m2.data.push_back(rand() & 1);
|
|
|
|
ASSERT_TRUE(save_magica_data({m0, m1, m2}, s));
|
|
ASSERT_TRUE(load_magica_data(mm, s));
|
|
ASSERT_EQ(mm.size(), 3);
|
|
std::list<MagicaModel>::const_iterator i = mm.begin();
|
|
ASSERT_EQ(*i++, m0);
|
|
ASSERT_EQ(*i++, m1);
|
|
ASSERT_EQ(*i++, m2);
|
|
}
|
|
|
|
static void test_game_command(TestDB *db, bool good, const cryptonote::cc_command_game_update_t &cmd, const char *label)
|
|
{
|
|
std::unique_ptr<cryptonote::Blockchain> bc;
|
|
cryptonote::tx_memory_pool txpool(*bc);
|
|
bc.reset(new cryptonote::Blockchain(txpool));
|
|
struct get_test_options {
|
|
const std::pair<uint8_t, uint64_t> hard_forks[2];
|
|
const cryptonote::test_options test_options = {
|
|
hard_forks,
|
|
0,
|
|
};
|
|
get_test_options(): hard_forks{std::make_pair((uint8_t)12, (uint64_t)0), std::make_pair((uint8_t)0, (uint64_t)0)} {}
|
|
} opts;
|
|
cryptonote::Blockchain *blockchain = bc.get();
|
|
ASSERT_TRUE(blockchain != NULL) << label;
|
|
bool r = blockchain->init(db, cryptonote::FAKECHAIN, true, &opts.test_options, 0, NULL);
|
|
ASSERT_TRUE(r) << label;
|
|
|
|
uint64_t cost = cc::get_cc_command_cost(cmd);
|
|
ASSERT_EQ(cost, 0);
|
|
cryptonote::tx_verification_context tvc{};
|
|
r = cc::check_cc_command(*db, cmd, tvc);
|
|
ASSERT_EQ(r, good) << label;
|
|
|
|
if (good)
|
|
{
|
|
const TestDB::state_t s0 = db->get_state();
|
|
cc::game_events_t events;
|
|
r = cc::execute_cc_command(*db, cmd, events);
|
|
ASSERT_TRUE(r) << label;
|
|
r = cc::revert_cc_command(*db, cmd);
|
|
ASSERT_TRUE(r) << label;
|
|
const TestDB::state_t s1 = db->get_state();
|
|
ASSERT_EQ(s0, s1) << label;
|
|
}
|
|
}
|
|
|
|
TEST(cc_game, empty)
|
|
{
|
|
cryptonote::cc_command_game_update_t cmd;
|
|
TestDB *db = new TestDB();
|
|
cryptonote::BlockchainDB *bdb = db;
|
|
cc::add_init_state(*bdb);
|
|
cc::game_events_t events;
|
|
cryptonote::cc_command_game_update_t cg = cc::create_cc_game_update_command(*db, events);
|
|
cryptonote::cc_command_game_update_t cg1 = cc::create_cc_game_update_command(*db, events);
|
|
cg.cc_nonce = cg1.cc_nonce = 0;
|
|
ASSERT_EQ(cg, cg1);
|
|
ASSERT_EQ(cg.cities.size(), 1);
|
|
const auto &c = cg.cities[0];
|
|
ASSERT_EQ(c.city_id, 0);
|
|
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)
|
|
{
|
|
TestDB *db = new TestDB();
|
|
cryptonote::BlockchainDB *bdb = db;
|
|
cc::add_init_state(*bdb);
|
|
cc::game_events_t events;
|
|
cryptonote::cc_command_game_update_t cg, cg0 = cc::create_cc_game_update_command(*db, events);
|
|
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, "");
|
|
uint8_t potential[NUM_POTENTIALS];
|
|
for (int i = 0; i < NUM_POTENTIALS; ++i)
|
|
potential[i] = 100;
|
|
uint32_t flag_id = bdb->allocate_new_cc_flag(NULL, account_id, city_id, 44, 45, 46, 47, potential, 32, 33);
|
|
db->set_cc_flag_tiles(flag_id, {{},{},{},{4},{},{6},{},{0,1,2},{}});
|
|
db->set_cc_account_balance(1, 1000*COIN); // mayor
|
|
|
|
cryptonote::cc_flag_data_t fd;
|
|
ASSERT_TRUE(db->get_cc_flag_data(flag_id, fd, NULL));
|
|
ASSERT_EQ(fd.role, ROLE_EMPTY);
|
|
ASSERT_EQ(fd.repair, 0);
|
|
|
|
bdb->set_cc_flag_role(flag_id, ROLE_AGRICULTURAL, 100);
|
|
ASSERT_TRUE(db->get_cc_flag_data(flag_id, fd, NULL));
|
|
ASSERT_EQ(fd.role, ROLE_AGRICULTURAL);
|
|
|
|
const uint32_t just_enough_repair = (MAX_DECAY_RATE * INACTIVE_DECAY_PERCENTAGE / 100) * NO_MILITARY_DECAY_MULTIPLIER / 100 + 1;
|
|
db->set_cc_flag_repair(flag_id, just_enough_repair);
|
|
ASSERT_TRUE(db->get_cc_flag_data(flag_id, fd, NULL));
|
|
ASSERT_EQ(fd.repair, just_enough_repair);
|
|
|
|
cg = cc::create_cc_game_update_command(*db, events);
|
|
ASSERT_NE(cg0, cg) << "cg0: " << cryptonote::obj_to_json_str(cg0) << ", cg: " << cryptonote::obj_to_json_str(cg);
|
|
ASSERT_EQ(cg.cities.size(), 1);
|
|
const auto &c1 = cg.cities[0];
|
|
ASSERT_EQ(c1.derelict.size(), 0);
|
|
ASSERT_EQ(c1.defaulted.size(), 1);
|
|
ASSERT_EQ(c1.defaulted[0].id, flag_id);
|
|
ASSERT_EQ(c1.repair.size(), 0 + PREDEFINED_BULDINGS);
|
|
|
|
// give enough money for land tax
|
|
db->set_cc_flag_repair(flag_id, just_enough_repair);
|
|
bdb->set_cc_account_balance(account_id, 1000*COIN);
|
|
cg = cc::create_cc_game_update_command(*db, events);
|
|
ASSERT_NE(cg0, cg);
|
|
ASSERT_EQ(cg.cities.size(), 1);
|
|
const auto &c2 = cg.cities[0];
|
|
ASSERT_EQ(c2.defaulted.size(), 0);
|
|
ASSERT_EQ(c2.derelict.size(), 0);
|
|
ASSERT_EQ(c2.repair.size(), 1 + PREDEFINED_BULDINGS);
|
|
ASSERT_EQ(PREDEFINED_BULDINGS, 1);
|
|
ASSERT_EQ(c2.repair[0].delta_id, 1);
|
|
ASSERT_EQ(c2.repair[1].delta_id, 0);
|
|
|
|
db->set_cc_flag_repair(flag_id, MIN_DECAY_RATE);
|
|
ASSERT_TRUE(db->get_cc_flag_data(flag_id, fd, NULL));
|
|
ASSERT_EQ(fd.repair, MIN_DECAY_RATE);
|
|
|
|
cg = cc::create_cc_game_update_command(*db, events);
|
|
ASSERT_NE(cg0, cg);
|
|
ASSERT_EQ(cg.cities.size(), 1);
|
|
const auto &c3 = cg.cities[0];
|
|
ASSERT_EQ(c3.defaulted.size(), 0);
|
|
ASSERT_EQ(c3.derelict.size(), 1);
|
|
ASSERT_EQ(c3.derelict[0].id, flag_id);
|
|
ASSERT_EQ(c3.repair.size(), 0 + PREDEFINED_BULDINGS);
|
|
}
|
|
|
|
TEST(cc_game, subsidy)
|
|
{
|
|
TestDB *db = new TestDB();
|
|
cryptonote::BlockchainDB *bdb = db;
|
|
cc::add_init_state(*bdb);
|
|
cc::game_events_t events;
|
|
cryptonote::keypair account_keys[4];
|
|
|
|
// a few players
|
|
std::map<uint32_t, uint32_t> item_balances;
|
|
item_balances[ITEM_FOOD_VEGETABLES] = 10000;
|
|
item_balances[ITEM_BASIC_WOOD] = 10000;
|
|
uint32_t players[sizeof(account_keys) / sizeof(account_keys[0])];
|
|
for (size_t i = 0; i < sizeof(account_keys) / sizeof(account_keys[0]); ++i)
|
|
{
|
|
account_keys[i] = cryptonote::keypair::generate(hw::get_device("default"));
|
|
players[i] = bdb->allocate_new_cc_account(account_keys[i].pub, "Player " + std::to_string(i));
|
|
bdb->set_cc_account_balance(players[i], 100000000000);
|
|
bdb->set_cc_account_item_balances(players[i], item_balances);
|
|
}
|
|
|
|
// two extra cities
|
|
uint32_t city1_id = bdb->allocate_new_cc_city(42, players[1], "city 1");
|
|
uint32_t city2_id = bdb->allocate_new_cc_city(4242, players[2], "city 2");
|
|
uint32_t treasuries[3];
|
|
ASSERT_TRUE(bdb->get_cc_city_treasury(0, treasuries[0]));
|
|
ASSERT_TRUE(bdb->get_cc_city_treasury(city1_id, treasuries[1]));
|
|
ASSERT_TRUE(bdb->get_cc_city_treasury(city2_id, treasuries[2]));
|
|
|
|
// build some stuff in all cities but one
|
|
uint8_t potential[NUM_POTENTIALS];
|
|
for (int i = 0; i < NUM_POTENTIALS; ++i)
|
|
potential[i] = 200;
|
|
uint32_t flags[8];
|
|
uint32_t flag_cities[8] = { 0, 0, 0, 0, 2, 2, 2, 0, };
|
|
uint32_t dx = 100, dy = 100;
|
|
for (size_t i = 0; i < sizeof(flags) / sizeof(flags[0]); ++i)
|
|
{
|
|
uint32_t ox, oy;
|
|
cc::get_city_origin(flag_cities[i], ox, oy);
|
|
flags[i] = bdb->allocate_new_cc_flag(NULL, players[i%(sizeof(players)/sizeof(players[0]))], flag_cities[i], ox + dx, oy + dy, ox + dx + 80 - 1, oy + dy + 80 - 1, potential, 32, 33);
|
|
bdb->set_cc_flag_role(flags[i], ROLE_RESIDENTIAL1, 100);
|
|
bdb->set_cc_flag_repair(flags[i], DEFAULT_REPAIR);
|
|
if (i != 5)
|
|
bdb->set_cc_flag_tiles(flags[i], std::vector<std::vector<uint8_t>>(80*80, std::vector<uint8_t>(1, 1)));
|
|
dx += 100;
|
|
dy += 100;
|
|
}
|
|
|
|
for (int i = 1; i <= GAME_UPDATE_FREQUENCY; ++i)
|
|
{
|
|
db->add_block({}, 0, 0, {}, bdb->get_block_already_generated_coins(i-1), 1, {});
|
|
}
|
|
|
|
cryptonote::cc_command_game_update_t cg = cc::create_cc_game_update_command(*db, events);
|
|
|
|
ASSERT_EQ(cg.cities.size(), 3);
|
|
ASSERT_EQ(cg.cities[0].city_id, 0);
|
|
ASSERT_EQ(cg.cities[0].subsidy, 224959616539);
|
|
ASSERT_EQ(cg.cities[1].city_id, city1_id);
|
|
ASSERT_EQ(cg.cities[1].subsidy, 0);
|
|
ASSERT_EQ(cg.cities[2].city_id, city2_id);
|
|
ASSERT_EQ(cg.cities[2].subsidy, 134975769923);
|
|
|
|
uint64_t expected_subsidy = bdb->get_block_already_generated_coins(GAME_UPDATE_FREQUENCY - 1) * BLOCK_REWARD_SUBSIDY;
|
|
ASSERT_EQ(expected_subsidy, 224959616539 + 134975769923);
|
|
|
|
bool found[2] = { false, false };
|
|
for (const auto &e: events)
|
|
{
|
|
if (e.account == treasuries[0] && strstr(e.event.c_str(), "Gets 2249.59616539 subsidy"))
|
|
found[0] = true;
|
|
ASSERT_FALSE(e.account == treasuries[1] && strstr(e.event.c_str(), "subsidy"));
|
|
if (e.account == treasuries[2] && strstr(e.event.c_str(), "Gets 1349.75769923 subsidy"))
|
|
found[1] = true;
|
|
}
|
|
ASSERT_TRUE(found[0]);
|
|
ASSERT_TRUE(found[1]);
|
|
}
|
|
|
|
TEST(cc_game, generator)
|
|
{
|
|
cryptonote::cc_command_game_update_t cmd;
|
|
TestDB *db = new TestDB();
|
|
cryptonote::BlockchainDB *bdb = db;
|
|
cc::add_init_state(*bdb);
|
|
bdb->set_cc_account_balance(1, 1000*COIN); // mayor
|
|
cc::game_events_t events;
|
|
cryptonote::cc_command_game_update_t cg, cg0 = cc::create_cc_game_update_command(*db, events);
|
|
cryptonote::keypair account_keys = cryptonote::keypair::generate(hw::get_device("default"));
|
|
uint32_t ox, oy;
|
|
cc::get_city_origin(0, ox, oy);
|
|
|
|
uint32_t city_id = 0;
|
|
uint32_t account_id = bdb->allocate_new_cc_account(account_keys.pub, "");
|
|
uint8_t potential[NUM_POTENTIALS];
|
|
for (int i = 0; i < NUM_POTENTIALS; ++i)
|
|
potential[i] = 255;
|
|
uint32_t flag_id = bdb->allocate_new_cc_flag(NULL, account_id, city_id, ox + 44, oy + 44, ox + 47, oy + 47, potential, 32, 33);
|
|
const uint8_t S = ITEM_BASIC_STONE;
|
|
const uint8_t W = ITEM_BASIC_WOOD;
|
|
std::vector<uint8_t> column = {S, S, S, S, S, S, S, S, S, S, S, S, S, S, S, S};
|
|
db->set_cc_flag_tiles(flag_id, {{},{},{},{4},{},{6},{},{0,S,W},{}, {}, column, column, column, {S}, {S}, {}});
|
|
db->set_cc_flag_repair(flag_id, 1000000);
|
|
|
|
bdb->set_cc_flag_role(flag_id, ROLE_SAWMILL, 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(GAME_UPDATE_FREQUENCY, fd.x0, fd.y0, fd.x1, fd.y1, fd.role, fd.economic_power, GAME_UPDATE_FREQUENCY - fd.construction_height, fd.potential, false, 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(), 3);
|
|
ASSERT_EQ(production[0].first, ITEM_HIGH_WOOD);
|
|
ASSERT_EQ(production[1].first, ITEM_MEDIUM_WOOD);
|
|
ASSERT_EQ(production[2].first, ITEM_BASIC_WOOD);
|
|
ASSERT_GT(9 * production[0].second + 3 * production[1].second + production[2].second, costs[0].second);
|
|
|
|
bdb->set_cc_account_balance(account_id, 1000*COIN);
|
|
std::map<uint32_t, uint32_t> item_balances;
|
|
item_balances[ITEM_BASIC_WOOD] = 1000000;
|
|
item_balances[ITEM_BASIC_STONE] = 1000000;
|
|
item_balances[ITEM_LABOUR] = 1000000;
|
|
item_balances[ITEM_FOOD_VEGETABLES] = 1000000;
|
|
bdb->set_cc_account_item_balances(account_id, item_balances);
|
|
|
|
cg = cc::create_cc_game_update_command(*db, events);
|
|
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 + PREDEFINED_BULDINGS);
|
|
ASSERT_EQ(c0.item_balances.size(), 6);
|
|
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_MEDIUM_WOOD);
|
|
ASSERT_GE(c0.item_balances[2].delta, 0);
|
|
ASSERT_EQ(c0.item_balances[3].delta_account, 0);
|
|
item_id += c0.item_balances[3].delta_item + 1;
|
|
ASSERT_EQ(item_id, ITEM_HIGH_WOOD);
|
|
ASSERT_GE(c0.item_balances[3].delta, 0);
|
|
ASSERT_EQ(c0.item_balances[4].delta_account, 0);
|
|
item_id += c0.item_balances[4].delta_item + 1;
|
|
ASSERT_EQ(item_id, ITEM_LABOUR);
|
|
ASSERT_LT(c0.item_balances[4].delta, 0);
|
|
ASSERT_EQ(c0.item_balances[5].delta_account, 0);
|
|
item_id += c0.item_balances[5].delta_item + 1;
|
|
ASSERT_EQ(item_id, ITEM_FOOD_VEGETABLES);
|
|
ASSERT_LT(c0.item_balances[5].delta, 0);
|
|
}
|
|
|
|
TEST(cc, type_tags)
|
|
{
|
|
cryptonote::cc_command_t cmd;
|
|
|
|
cmd = cryptonote::cc_command_none_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x00);
|
|
cmd = cryptonote::cc_command_create_account_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x01);
|
|
cmd = cryptonote::cc_command_transfer_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x02);
|
|
cmd = cryptonote::cc_command_buy_land_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x03);
|
|
cmd = cryptonote::cc_command_build_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x04);
|
|
cmd = cryptonote::cc_command_buy_items_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x05);
|
|
cmd = cryptonote::cc_command_game_update_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x06);
|
|
cmd = cryptonote::cc_command_trade_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x07);
|
|
cmd = cryptonote::cc_command_building_settings_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x08);
|
|
cmd = cryptonote::cc_command_assign_items_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x09);
|
|
cmd = cryptonote::cc_command_repair_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x0a);
|
|
cmd = cryptonote::cc_command_rename_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x0b);
|
|
cmd = cryptonote::cc_command_chat_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x0c);
|
|
cmd = cryptonote::cc_command_demolish_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x0d);
|
|
cmd = cryptonote::cc_command_research_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x0e);
|
|
cmd = cryptonote::cc_command_found_city_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x0f);
|
|
cmd = cryptonote::cc_command_give_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x10);
|
|
cmd = cryptonote::cc_command_match_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x11);
|
|
cmd = cryptonote::cc_command_new_item_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x12);
|
|
cmd = cryptonote::cc_command_dividend_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x13);
|
|
cmd = cryptonote::cc_command_ignore_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x14);
|
|
cmd = cryptonote::cc_command_event_badge_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x15);
|
|
cmd = cryptonote::cc_command_resize_flag_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x16);
|
|
cmd = cryptonote::cc_command_destroy_items_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x17);
|
|
cmd = cryptonote::cc_command_define_attribute_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x18);
|
|
cmd = cryptonote::cc_command_increase_attribute_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x19);
|
|
cmd = cryptonote::cc_command_edit_player_profile_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x1a);
|
|
cmd = cryptonote::cc_command_dice_roll_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x1b);
|
|
cmd = cryptonote::cc_command_hunt_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x1c);
|
|
cmd = cryptonote::cc_command_palette_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x1d);
|
|
cmd = cryptonote::cc_command_fight_fire_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x1e);
|
|
cmd = cryptonote::cc_command_service_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x1f);
|
|
cmd = cryptonote::cc_command_redeem_account_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x20);
|
|
cmd = cryptonote::cc_command_destroy_flag_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x21);
|
|
cmd = cryptonote::cc_command_add_city_specialization_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x22);
|
|
cmd = cryptonote::cc_command_sow_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x23);
|
|
cmd = cryptonote::cc_command_harvest_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x24);
|
|
cmd = cryptonote::cc_command_mint_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x25);
|
|
cmd = cryptonote::cc_command_smelt_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x26);
|
|
cmd = cryptonote::cc_command_cancel_nonce_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x27);
|
|
}
|
|
|
|
TEST(cc, staff)
|
|
{
|
|
ASSERT_EQ(0, cc::get_staff(0));
|
|
ASSERT_EQ(0, cc::get_staff(STAFF_BUILDINGS_PER_PLAYER));
|
|
ASSERT_EQ(1, cc::get_staff(STAFF_BUILDINGS_PER_PLAYER + 1));
|
|
ASSERT_EQ(1, cc::get_staff(STAFF_BUILDINGS_PER_PLAYER + STAFF_BUILDINGS_PER_STAFF));
|
|
ASSERT_EQ(2, cc::get_staff(STAFF_BUILDINGS_PER_PLAYER + STAFF_BUILDINGS_PER_STAFF + 1));
|
|
for (uint32_t buildings = 0; buildings < 1000; ++buildings)
|
|
ASSERT_GE(cc::get_staff(buildings + 1), cc::get_staff(buildings));
|
|
}
|
|
|
|
TEST(cc, distance)
|
|
{
|
|
/* 0 1 2 3 4
|
|
* 0 x x x x x
|
|
* 1 x x x x x
|
|
* 2 x x x x x
|
|
* 3 x x x x x
|
|
* 4 x x x x x
|
|
*/
|
|
ASSERT_EQ(2, cc::get_distance(0, 0, 1, 1, 3, 3, 4, 4));
|
|
ASSERT_EQ(2, cc::get_distance(3, 3, 4, 4, 0, 0, 1, 1));
|
|
ASSERT_EQ(0, cc::get_distance(0, 0, 4, 4, 1, 1, 3, 3));
|
|
ASSERT_EQ(0, cc::get_distance(1, 1, 3, 3, 0, 0, 4, 4));
|
|
|
|
ASSERT_EQ(1, cc::get_distance(0, 0, 1, 1, 2, 2, 4, 4)); // top left
|
|
ASSERT_EQ(1, cc::get_distance(1, 0, 3, 1, 1, 2, 3, 4)); // top
|
|
ASSERT_EQ(1, cc::get_distance(3, 0, 4, 1, 0, 2, 2, 4)); // top right
|
|
ASSERT_EQ(1, cc::get_distance(0, 1, 1, 3, 2, 1, 4, 3)); // left
|
|
ASSERT_EQ(1, cc::get_distance(3, 1, 4, 3, 0, 1, 2, 3)); // right
|
|
ASSERT_EQ(1, cc::get_distance(0, 3, 1, 4, 2, 0, 4, 2)); // bottom left
|
|
ASSERT_EQ(1, cc::get_distance(1, 3, 3, 4, 1, 0, 3, 2)); // bottom
|
|
ASSERT_EQ(1, cc::get_distance(3, 3, 4, 4, 0, 0, 2, 2)); // bottom right
|
|
|
|
ASSERT_EQ(0, cc::get_distance(0, 0, 2, 2, 2, 2, 4, 4)); // top left
|
|
ASSERT_EQ(0, cc::get_distance(1, 0, 3, 2, 1, 2, 3, 4)); // top
|
|
ASSERT_EQ(0, cc::get_distance(2, 0, 4, 2, 0, 2, 2, 4)); // top right
|
|
ASSERT_EQ(0, cc::get_distance(0, 1, 2, 3, 2, 1, 4, 3)); // left
|
|
ASSERT_EQ(0, cc::get_distance(2, 1, 4, 3, 0, 1, 2, 3)); // right
|
|
ASSERT_EQ(0, cc::get_distance(0, 2, 2, 2, 2, 0, 4, 2)); // bottom left
|
|
ASSERT_EQ(0, cc::get_distance(1, 2, 3, 4, 1, 0, 3, 2)); // bottom
|
|
ASSERT_EQ(0, cc::get_distance(2, 2, 4, 4, 0, 0, 2, 2)); // bottom right
|
|
}
|
|
|
|
TEST(cc, connection_squares)
|
|
{
|
|
unsigned d;
|
|
|
|
// disjoint
|
|
ASSERT_EQ(cc::connection_squares(0, 0, 0, 0, 2, 2, 2, 2, d), 0);
|
|
|
|
// corners
|
|
ASSERT_EQ(cc::connection_squares(1, 1, 1, 1, 2, 2, 2, 2, d), 0);
|
|
ASSERT_EQ(cc::connection_squares(1, 1, 1, 1, 2, 2, 2, 2, d), 0);
|
|
ASSERT_EQ(cc::connection_squares(2, 1, 2, 1, 1, 2, 1, 2, d), 0);
|
|
ASSERT_EQ(cc::connection_squares(1, 2, 1, 2, 2, 1, 2, 1, d), 0);
|
|
|
|
// full tile
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 2, 2, 1, 2, 1, 2, d), 1); ASSERT_EQ(d, cc::dir_east);
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 2, 2, 2, 1, 2, 1, d), 1); ASSERT_EQ(d, cc::dir_north);
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 2, 2, 2, 3, 2, 3, d), 1); ASSERT_EQ(d, cc::dir_south);
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 2, 2, 3, 2, 3, 2, d), 1); ASSERT_EQ(d, cc::dir_west);
|
|
|
|
// one tile in the middle
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 2, 2, 1, 1, 1, 3, d), 1); ASSERT_EQ(d, cc::dir_east);
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 2, 2, 1, 1, 3, 1, d), 1); ASSERT_EQ(d, cc::dir_north);
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 2, 2, 1, 3, 3, 3, d), 1); ASSERT_EQ(d, cc::dir_south);
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 2, 2, 3, 1, 3, 3, d), 1); ASSERT_EQ(d, cc::dir_west);
|
|
|
|
// 0 1 2 3 4 5
|
|
// . . . . . . 5
|
|
// . . . . . . 4
|
|
// . . x x . . 3
|
|
// . . x x . . 2
|
|
// . . . . . . 1
|
|
// . . . . . . 0
|
|
|
|
// one tile, corners
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 3, 3, 0, 0, 1, 2, d), 1); ASSERT_EQ(d, cc::dir_east);
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 3, 3, 0, 0, 2, 1, d), 1); ASSERT_EQ(d, cc::dir_north);
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 3, 3, 3, 0, 5, 1, d), 1); ASSERT_EQ(d, cc::dir_north);
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 3, 3, 4, 0, 5, 2, d), 1); ASSERT_EQ(d, cc::dir_west);
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 3, 3, 0, 3, 1, 5, d), 1); ASSERT_EQ(d, cc::dir_east);
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 3, 3, 0, 4, 2, 5, d), 1); ASSERT_EQ(d, cc::dir_south);
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 3, 3, 3, 4, 5, 5, d), 1); ASSERT_EQ(d, cc::dir_south);
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 3, 3, 4, 3, 5, 5, d), 1); ASSERT_EQ(d, cc::dir_west);
|
|
|
|
// full 2
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 3, 3, 0, 0, 1, 5, d), 2); ASSERT_EQ(d, cc::dir_east);
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 3, 3, 0, 0, 5, 1, d), 2); ASSERT_EQ(d, cc::dir_north);
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 3, 3, 4, 0, 5, 5, d), 2); ASSERT_EQ(d, cc::dir_west);
|
|
ASSERT_EQ(cc::connection_squares(2, 2, 3, 3, 0, 4, 5, 5, d), 2); ASSERT_EQ(d, cc::dir_south);
|
|
ASSERT_EQ(cc::connection_squares(0, 0, 1, 5, 2, 2, 3, 3, d), 2); ASSERT_EQ(d, cc::dir_west);
|
|
ASSERT_EQ(cc::connection_squares(0, 0, 5, 1, 2, 2, 3, 3, d), 2); ASSERT_EQ(d, cc::dir_south);
|
|
ASSERT_EQ(cc::connection_squares(4, 0, 5, 5, 2, 2, 3, 3, d), 2); ASSERT_EQ(d, cc::dir_east);
|
|
ASSERT_EQ(cc::connection_squares(0, 4, 5, 5, 2, 2, 3, 3, d), 2); ASSERT_EQ(d, cc::dir_north);
|
|
}
|
|
|
|
TEST(cc_influence, monotonic)
|
|
{
|
|
static const uint8_t potential[] = {0, 80, 105, 180, 255};
|
|
static const uint32_t gap = 4;
|
|
for (uint32_t size = 16; size <= 256; size *= 2)
|
|
{
|
|
uint32_t prev = 0;
|
|
for (uint8_t p: potential)
|
|
{
|
|
const uint8_t dummy_potential[NUM_POTENTIALS] = { p, p, p, p, p, p, p };
|
|
const uint32_t i = cc::get_cc_influence(0, 0, size, size, ROLE_AGRICULTURAL, 100, 0, dummy_potential, cc::SPECIAL_EVENT_NONE);
|
|
ASSERT_GE(i, prev);
|
|
ASSERT_LT(i, 10000); // sanity
|
|
prev = i;
|
|
}
|
|
}
|
|
uint32_t prev = 0;
|
|
for (uint32_t size = 16; size <= 256; size *= 2)
|
|
{
|
|
const uint8_t dummy_potential[NUM_POTENTIALS] = { 100, 100, 100, 100, 100, 100, 100 };
|
|
const uint32_t i = cc::get_cc_influence(0, 0, size, size, ROLE_AGRICULTURAL, 100, 0, dummy_potential, cc::SPECIAL_EVENT_NONE);
|
|
ASSERT_GE(i, prev);
|
|
ASSERT_LT(i, 10000); // sanity
|
|
prev = i;
|
|
}
|
|
uint32_t w = 64, h = 64;
|
|
const uint8_t dummy_potential[NUM_POTENTIALS] = { 100, 100, 100, 100, 100, 100, 100 };
|
|
prev = cc::get_cc_influence(0, 0, w, h, ROLE_AGRICULTURAL, 100, 0, dummy_potential, cc::SPECIAL_EVENT_NONE);
|
|
for (uint32_t scale = 0; scale < 4; ++scale)
|
|
{
|
|
w *= 2;
|
|
h /= 2;
|
|
const uint32_t i = cc::get_cc_influence(0, 0, w, h, ROLE_AGRICULTURAL, 100, 0, dummy_potential, cc::SPECIAL_EVENT_NONE);
|
|
ASSERT_LE(i, prev);
|
|
ASSERT_LT(i, 10000); // sanity
|
|
prev = i;
|
|
}
|
|
|
|
// check no overflow
|
|
prev = 0;
|
|
for (uint32_t sz = 5; sz <= 255; sz += 5)
|
|
{
|
|
const uint8_t dummy_potential[NUM_POTENTIALS] = { (uint8_t)sz, (uint8_t)sz, (uint8_t)sz, (uint8_t)sz, (uint8_t)sz, (uint8_t)sz, (uint8_t)sz };
|
|
const uint32_t i = cc::get_cc_influence(0, 0, sz, sz, ROLE_AGRICULTURAL, 1000, 0, dummy_potential, cc::SPECIAL_EVENT_NONE);
|
|
ASSERT_GT(i, prev);
|
|
prev = i;
|
|
}
|
|
}
|
|
|
|
TEST(cc_influence, roles)
|
|
{
|
|
cc::game_events_t events;
|
|
cryptonote::cc_command_game_update_t cg;
|
|
|
|
// no influence, score stays at 100%
|
|
// E A C I C R1 R2 R3 M C S W K S W R R
|
|
ASSERT_EQ(100, cc::calculate_influence_bonus(ROLE_AGRICULTURAL, std::vector<uint32_t>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}.data(), 0, 0, cg, events));
|
|
// influence by everying but military, agricultural buildings do not care
|
|
ASSERT_EQ(100, cc::calculate_influence_bonus(ROLE_AGRICULTURAL, std::vector<uint32_t>{0, 10, 10, 10, 10, 10, 10, 10, 0, 10, 10, 10, 10, 10, 10, 10, 10}.data(), 0, 0, cg, events));
|
|
// agricultural buildings do get some influence from military buildings
|
|
ASSERT_EQ(105, cc::calculate_influence_bonus(ROLE_AGRICULTURAL, std::vector<uint32_t>{0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 0, 0, 0, 0, 0}.data(), 0, 0, cg, events));
|
|
|
|
// residential buildings get a bonus for a commercial building and get a bonus from one military building, but penalties from more
|
|
ASSERT_EQ( 0, cc::calculate_influence_bonus(ROLE_RESIDENTIAL2, std::vector<uint32_t>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}.data(), 0, 0, cg, events));
|
|
ASSERT_EQ(100, cc::calculate_influence_bonus(ROLE_RESIDENTIAL1, std::vector<uint32_t>{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}.data(), 0, 0, cg, events));
|
|
ASSERT_EQ(105, cc::calculate_influence_bonus(ROLE_RESIDENTIAL1, std::vector<uint32_t>{0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}.data(), 0, 0, cg, events));
|
|
ASSERT_EQ(110, cc::calculate_influence_bonus(ROLE_RESIDENTIAL1, std::vector<uint32_t>{0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}.data(), 0, 0, cg, events));
|
|
ASSERT_EQ(105, cc::calculate_influence_bonus(ROLE_RESIDENTIAL1, std::vector<uint32_t>{0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0}.data(), 0, 0, cg, events));
|
|
ASSERT_EQ(100, cc::calculate_influence_bonus(ROLE_RESIDENTIAL1, std::vector<uint32_t>{0, 0, 0, 0, 1, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0}.data(), 0, 0, cg, events));
|
|
}
|
|
|
|
TEST(cc, events)
|
|
{
|
|
cryptonote::BlockchainDB *db = cryptonote::new_db();
|
|
ASSERT_TRUE(db);
|
|
|
|
boost::filesystem::path path = boost::filesystem::temp_directory_path() / boost::filesystem::unique_path();
|
|
const std::string filename = (path / db->get_db_name()).string();
|
|
db->open(filename, 0);
|
|
|
|
cryptonote::db_wtxn_guard guard(db);
|
|
const uint8_t cmd = get_cc_tag<uint8_t>(cryptonote::cc_command_game_update_t());
|
|
|
|
// starts off empty
|
|
std::vector<cc::game_event_t> events;
|
|
ASSERT_TRUE(db->get_cc_events(0, 0, 0, events));
|
|
ASSERT_TRUE(events.empty());
|
|
|
|
// can delete nothing
|
|
db->remove_cc_events(7434);
|
|
ASSERT_TRUE(db->get_cc_events(0, 0, 0, events));
|
|
ASSERT_TRUE(events.empty());
|
|
|
|
// add a couple at height 127
|
|
db->add_cc_events(127, {{127, cmd, 4, 1, ITEM_NONE, 0, 0, 1, "building 1"}, {127, cmd, 4, 0, ITEM_NONE, 0, 0, 1, "no building"}});
|
|
ASSERT_TRUE(db->get_cc_events(127, 0, 0, events));
|
|
ASSERT_EQ(events.size(), 2);
|
|
|
|
// add a bundle at 200
|
|
db->add_cc_events(200, {
|
|
{200, cmd, 2, 0, ITEM_NONE, 0, 0, 10, "account 2"},
|
|
{200, cmd, 4, 1, ITEM_NONE, 0, 0, 11, "account 4, building 1, event 0"},
|
|
{200, cmd, 5, 2, ITEM_NONE, 0, 0, 12, "account 5, building 2, event 0"},
|
|
{200, cmd, 4, 1, ITEM_NONE, 0, 0, 13, "account 4, building 1, event 1"},
|
|
{200, cmd, 4, 1, ITEM_NONE, 0, 0, 14, "account 4, building 1, event 2"},
|
|
{200, cmd, 5, 3, ITEM_NONE, 0, 0, 15, "account 5, building 3, event 0"},
|
|
{200, cmd, 6, 4, ITEM_NONE, 0, 0, 16, "account 6, building 4, event 0"},
|
|
{200, cmd, 4, 5, ITEM_NONE, 0, 0, 17, "account 4, building 5, event 0"},
|
|
{200, cmd, 4, 0, ITEM_NONE, 0, 0, 18, "account 4"},
|
|
{200, cmd, 1, 0, ITEM_NONE, 0, 0, 19, "account 1"}
|
|
});
|
|
ASSERT_TRUE(db->get_cc_events(200, 0, 0, events));
|
|
ASSERT_EQ(events.size(), 10);
|
|
ASSERT_TRUE(db->get_cc_events(200, 4, 0, events));
|
|
ASSERT_EQ(events.size(), 5);
|
|
ASSERT_TRUE(db->get_cc_events(200, 5, 0, events));
|
|
ASSERT_EQ(events.size(), 2);
|
|
ASSERT_TRUE(db->get_cc_events(200, 6, 0, events));
|
|
ASSERT_EQ(events.size(), 1);
|
|
ASSERT_TRUE(db->get_cc_events(200, 7, 6, events));
|
|
ASSERT_EQ(events.size(), 0);
|
|
ASSERT_TRUE(db->get_cc_events(200, 8, 0, events));
|
|
ASSERT_EQ(events.size(), 0);
|
|
ASSERT_TRUE(db->get_cc_events(200, 4, 4, events));
|
|
ASSERT_EQ(events.size(), 0);
|
|
ASSERT_TRUE(db->get_cc_events(200, 4, 1, events));
|
|
ASSERT_EQ(events.size(), 3);
|
|
ASSERT_TRUE(db->get_cc_events(200, 4, 5, events));
|
|
ASSERT_EQ(events.size(), 1);
|
|
ASSERT_TRUE(db->get_cc_events(200, 5, 1, events));
|
|
ASSERT_EQ(events.size(), 0);
|
|
ASSERT_TRUE(db->get_cc_events(200, 1, 0, events));
|
|
ASSERT_EQ(events.size(), 1);
|
|
|
|
// 127 still exists, unchanged
|
|
ASSERT_TRUE(db->get_cc_events(127, 0, 0, events));
|
|
ASSERT_EQ(events.size(), 2);
|
|
|
|
// add at 240
|
|
db->add_cc_events(240, {{240, cmd, 4, 1, ITEM_NONE, 0, 0, 90, "building 1"}, {240, cmd, 4, 0, ITEM_NONE, 0, 0, 91, "no building"}});
|
|
ASSERT_TRUE(db->get_cc_events(240, 0, 0, events));
|
|
ASSERT_EQ(events.size(), 2);
|
|
ASSERT_TRUE(db->get_cc_events(200, 0, 0, events));
|
|
ASSERT_EQ(events.size(), 10);
|
|
|
|
// delete 200
|
|
db->remove_cc_events(200);
|
|
ASSERT_TRUE(db->get_cc_events(240, 0, 0, events));
|
|
ASSERT_EQ(events.size(), 2);
|
|
ASSERT_TRUE(db->get_cc_events(200, 0, 0, events));
|
|
ASSERT_EQ(events.size(), 0);
|
|
ASSERT_TRUE(db->get_cc_events(127, 0, 0, events));
|
|
ASSERT_EQ(events.size(), 2);
|
|
|
|
// we can add 200 back
|
|
db->add_cc_events(200, {
|
|
{200, cmd, 2, 0, ITEM_NONE, 0, 0, 10, "account 2"},
|
|
{200, cmd, 4, 1, ITEM_NONE, 0, 0, 11, "account 4, building 1, event 0"},
|
|
{200, cmd, 5, 2, ITEM_NONE, 0, 0, 12, "account 5, building 2, event 0"},
|
|
{200, cmd, 4, 1, ITEM_NONE, 0, 0, 13, "account 4, building 1, event 1"},
|
|
{200, cmd, 4, 1, ITEM_NONE, 0, 0, 14, "account 4, building 1, event 2"},
|
|
{200, cmd, 5, 3, ITEM_NONE, 0, 0, 15, "account 5, building 3, event 0"},
|
|
{200, cmd, 6, 4, ITEM_NONE, 0, 0, 16, "account 6, building 4, event 0"},
|
|
{200, cmd, 4, 5, ITEM_NONE, 0, 0, 17, "account 4, building 5, event 0"},
|
|
{200, cmd, 4, 0, ITEM_NONE, 0, 0, 18, "account 4"},
|
|
{200, cmd, 1, 0, ITEM_NONE, 0, 0, 19, "account 1"}
|
|
});
|
|
ASSERT_TRUE(db->get_cc_events(200, 0, 0, events));
|
|
ASSERT_EQ(events.size(), 10);
|
|
}
|
|
|
|
TEST(cc, discoveries)
|
|
{
|
|
std::vector<std::tuple<uint32_t, uint32_t, uint32_t, bool>> buildings, buildings2;
|
|
|
|
ASSERT_EQ(cc::get_research_bonus(buildings), 0);
|
|
buildings.push_back(std::make_tuple(255, 255, 300, false));
|
|
ASSERT_GT(cc::get_research_bonus(buildings), 0);
|
|
buildings2.push_back(std::make_tuple(100, 100, 200, false));
|
|
ASSERT_GE(cc::get_research_bonus(buildings), cc::get_research_bonus(buildings2));
|
|
|
|
ASSERT_EQ(cc::get_research_age_adjusted_difficulty(100 * COIN, 0, 0), 100 * COIN);
|
|
ASSERT_LT(cc::get_research_age_adjusted_difficulty(100 * COIN, 10000, 0), 100 * COIN);
|
|
ASSERT_GT(cc::get_research_age_adjusted_difficulty(100 * COIN, 10000, 0), 0);
|
|
ASSERT_LT(cc::get_research_age_adjusted_difficulty(100 * COIN, 10000, 100 * COIN), 50 * COIN);
|
|
|
|
ASSERT_GT(cc::get_discovery_chance_scaled(100 * COIN, 0, 100 * COIN, 0, 0, 1), RESEARCH_CHANCE_BASE_SCALE / 2);
|
|
ASSERT_GE(cc::get_discovery_chance_scaled(100 * COIN, 0, 100 * COIN, 0, 0, 1), cc::get_discovery_chance_scaled(MIN_RESEARCH_AMOUNT, 100 * COIN, 100 * COIN, 0, 0, 1));
|
|
}
|
|
|
|
static bool about_equal(uint64_t x, uint64_t y)
|
|
{
|
|
return std::abs(double(x) / double(y) - 1.0f) < 0.001;
|
|
}
|
|
|
|
TEST(cc, cities)
|
|
{
|
|
EXPECT_EQ(cc::get_ideal_cities_32(IDEAL_CITIES_SHARE_SCALE * 1) >> 32, 1);
|
|
EXPECT_EQ(cc::get_ideal_cities_32(IDEAL_CITIES_SHARE_SCALE * 2) >> 32, 2);
|
|
EXPECT_EQ(cc::get_ideal_cities_32(IDEAL_CITIES_SHARE_SCALE * 3) >> 32, 2);
|
|
EXPECT_EQ(cc::get_ideal_cities_32(IDEAL_CITIES_SHARE_SCALE * 4) >> 32, 3);
|
|
EXPECT_EQ(cc::get_ideal_cities_32(IDEAL_CITIES_SHARE_SCALE * 7) >> 32, 3);
|
|
EXPECT_EQ(cc::get_ideal_cities_32(IDEAL_CITIES_SHARE_SCALE * 8) >> 32, 4);
|
|
EXPECT_EQ(cc::get_ideal_cities_32(IDEAL_CITIES_SHARE_SCALE * 15) >> 32, 4);
|
|
EXPECT_EQ(cc::get_ideal_cities_32(IDEAL_CITIES_SHARE_SCALE * 16) >> 32, 5);
|
|
EXPECT_EQ(cc::get_ideal_cities_32(IDEAL_CITIES_SHARE_SCALE * 31) >> 32, 5);
|
|
EXPECT_EQ(cc::get_ideal_cities_32(IDEAL_CITIES_SHARE_SCALE * 32) >> 32, 6);
|
|
EXPECT_EQ(cc::get_ideal_cities_32(IDEAL_CITIES_SHARE_SCALE * 63) >> 32, 6);
|
|
EXPECT_EQ(cc::get_ideal_cities_32(IDEAL_CITIES_SHARE_SCALE * 64) >> 32, 7);
|
|
EXPECT_EQ(cc::get_ideal_cities_32(IDEAL_CITIES_SHARE_SCALE * 127) >> 32, 7);
|
|
EXPECT_EQ(cc::get_ideal_cities_32(IDEAL_CITIES_SHARE_SCALE * 128) >> 32, 8);
|
|
|
|
EXPECT_EQ(cc::get_town_level(1), 0);
|
|
EXPECT_EQ(cc::get_town_level(CITY_LEVEL_SHARE_SCALE - 1), 0);
|
|
EXPECT_EQ(cc::get_town_level(CITY_LEVEL_SHARE_SCALE), 1);
|
|
EXPECT_EQ(cc::get_town_level(CITY_LEVEL_SHARE_SCALE * CITY_NEXT_LEVEL_MULTIPLE - 1), 1);
|
|
EXPECT_EQ(cc::get_town_level(CITY_LEVEL_SHARE_SCALE * CITY_NEXT_LEVEL_MULTIPLE), 2);
|
|
EXPECT_EQ(cc::get_town_level(CITY_LEVEL_SHARE_SCALE * CITY_NEXT_LEVEL_MULTIPLE * CITY_NEXT_LEVEL_MULTIPLE - 1), 2);
|
|
EXPECT_EQ(cc::get_town_level(CITY_LEVEL_SHARE_SCALE * CITY_NEXT_LEVEL_MULTIPLE * CITY_NEXT_LEVEL_MULTIPLE), 3);
|
|
EXPECT_EQ(cc::get_town_level(CITY_LEVEL_SHARE_SCALE * CITY_NEXT_LEVEL_MULTIPLE * CITY_NEXT_LEVEL_MULTIPLE * CITY_NEXT_LEVEL_MULTIPLE - 1), 3);
|
|
EXPECT_EQ(cc::get_town_level(CITY_LEVEL_SHARE_SCALE * CITY_NEXT_LEVEL_MULTIPLE * CITY_NEXT_LEVEL_MULTIPLE * CITY_NEXT_LEVEL_MULTIPLE), 4);
|
|
}
|
|
|
|
TEST(cc, accrual_steps)
|
|
{
|
|
ASSERT_EQ(cc::get_accrual_steps(359, 359), 0);
|
|
ASSERT_EQ(cc::get_accrual_steps(360, 360), 1);
|
|
ASSERT_EQ(cc::get_accrual_steps(361, 361), 0);
|
|
ASSERT_EQ(cc::get_accrual_steps(360, 360 + 360), 2);
|
|
ASSERT_EQ(cc::get_accrual_steps(360, 360 + 359), 1);
|
|
ASSERT_EQ(cc::get_accrual_steps(360, 360 + 361), 2);
|
|
ASSERT_EQ(cc::get_accrual_steps(360, 360 + 360 * 8), 9);
|
|
ASSERT_EQ(cc::get_accrual_steps(360, 360 + 360 * 8 - 1), 8);
|
|
ASSERT_EQ(cc::get_accrual_steps(10, 10), 0);
|
|
ASSERT_EQ(cc::get_accrual_steps(1, 359), 0);
|
|
ASSERT_EQ(cc::get_accrual_steps(1, 360), 1);
|
|
ASSERT_EQ(cc::get_accrual_steps(1, 361), 1);
|
|
ASSERT_EQ(cc::get_accrual_steps(1, 720), 2);
|
|
ASSERT_EQ(cc::get_accrual_steps(360, 720), 2);
|
|
ASSERT_EQ(cc::get_accrual_steps(361, 720), 1);
|
|
ASSERT_EQ(cc::get_accrual_steps(361, 719), 0);
|
|
|
|
static const constexpr uint64_t max_block = 1200;
|
|
bool is_update[max_block + 1];
|
|
for (uint64_t i = 1; i <= max_block; ++i)
|
|
is_update[i] = cryptonote::is_game_update_block(i);
|
|
for (uint64_t i = 1; i <= max_block; ++i)
|
|
{
|
|
for (uint64_t j = i; j <max_block; ++j)
|
|
{
|
|
uint64_t count = 0;
|
|
for (uint64_t k = i; k <= j; ++k)
|
|
if (is_update[k])
|
|
++count;
|
|
ASSERT_EQ(cc::get_accrual_steps(i, j), count);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(cc, accrual)
|
|
{
|
|
// base accrual start limit now
|
|
ASSERT_EQ(cc::get_accrued_price(1000, 0, 0, 0, 10000), 1000);
|
|
|
|
ASSERT_EQ(cc::get_accrued_price(1000, 1, 360, 0, 359), 1000);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, 1, 360, 0, 359 + 360), 1001);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, 1, 360, 0, 359 + 360 * 2), 1002);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, 1, 360, 1001, 359 + 360 * 2), 1001);
|
|
|
|
ASSERT_EQ(cc::get_accrued_price(1000, 1, 360, 0, 360), 1001);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, 1, 360, 0, 360 + 360), 1002);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, 1, 360, 0, 360 + 360 * 2), 1003);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, 1, 360, 1002, 360 + 360 * 2), 1002);
|
|
|
|
ASSERT_EQ(cc::get_accrued_price(1000, -1, 360, 0, 359), 1000);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, -1, 360, 0, 360), 999);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, -1, 360, 0, 360 - 360), 1000);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, -1, 360, 0, 360 + 360), 998);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, -1, 360, 0, 360 + 360 * 2), 997);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, -1, 360, 998, 360 + 360 * 2), 998);
|
|
|
|
ASSERT_EQ(cc::get_accrued_price(std::numeric_limits<uint64_t>::max() - 25, 10, 360, 0, 359 + 360 * 0), std::numeric_limits<uint64_t>::max() - 25);
|
|
ASSERT_EQ(cc::get_accrued_price(std::numeric_limits<uint64_t>::max() - 25, 10, 360, 0, 359 + 360 * 1), std::numeric_limits<uint64_t>::max() - 15);
|
|
ASSERT_EQ(cc::get_accrued_price(std::numeric_limits<uint64_t>::max() - 25, 10, 360, 0, 359 + 360 * 2), std::numeric_limits<uint64_t>::max() - 5);
|
|
ASSERT_EQ(cc::get_accrued_price(std::numeric_limits<uint64_t>::max() - 25, 10, 360, 0, 359 + 360 * 3), std::numeric_limits<uint64_t>::max());
|
|
ASSERT_EQ(cc::get_accrued_price(std::numeric_limits<uint64_t>::max() - 25, 10, 360, 0, 359 + 360 * 4), std::numeric_limits<uint64_t>::max());
|
|
|
|
ASSERT_EQ(cc::get_accrued_price(25, -10, 360, 0, 359 + 360 * 0), 25);
|
|
ASSERT_EQ(cc::get_accrued_price(25, -10, 360, 0, 359 + 360 * 1), 15);
|
|
ASSERT_EQ(cc::get_accrued_price(25, -10, 360, 0, 359 + 360 * 2), 5);
|
|
ASSERT_EQ(cc::get_accrued_price(25, -10, 360, 0, 359 + 360 * 3), 0);
|
|
ASSERT_EQ(cc::get_accrued_price(25, -10, 360, 0, 359 + 360 * 4), 0);
|
|
|
|
static_assert(GAME_UPDATE_FREQUENCY > 100);
|
|
ASSERT_EQ(cc::get_accrued_price(100, 1, 100, 200000, 99), 100);
|
|
ASSERT_EQ(cc::get_accrued_price(100, 1, 100, 200000, 100), 100);
|
|
ASSERT_EQ(cc::get_accrued_price(100, 1, 100, 200000, 101), 100);
|
|
ASSERT_EQ(cc::get_accrued_price(100, 1, 100, 200000, 100 + GAME_UPDATE_FREQUENCY), 101);
|
|
|
|
ASSERT_EQ(cc::get_accrued_price(100, 1, GAME_UPDATE_FREQUENCY, 200000, GAME_UPDATE_FREQUENCY - 1), 100);
|
|
ASSERT_EQ(cc::get_accrued_price(100, 1, GAME_UPDATE_FREQUENCY, 200000, GAME_UPDATE_FREQUENCY), 101);
|
|
ASSERT_EQ(cc::get_accrued_price(100, 1, GAME_UPDATE_FREQUENCY, 200000, GAME_UPDATE_FREQUENCY + 1), 101);
|
|
}
|
|
|
|
TEST(cc, add32clamp)
|
|
{
|
|
int32_t min32i = std::numeric_limits<int32_t>::min(), max32i = std::numeric_limits<int32_t>::max();
|
|
uint32_t min32u = std::numeric_limits<uint32_t>::min(), max32u = std::numeric_limits<uint32_t>::max();
|
|
|
|
auto test = [](int32_t value, uint32_t delta) -> int32_t { add32clamp(&value, delta); return value; };
|
|
|
|
ASSERT_EQ(min32i, (int32_t)0x80000000);
|
|
ASSERT_EQ(max32i, 0x7fffffff);
|
|
ASSERT_EQ(min32u, 0);
|
|
ASSERT_EQ(max32u, 0xffffffff);
|
|
|
|
ASSERT_EQ(test(0, 0), 0);
|
|
ASSERT_EQ(test(0, 1), 1);
|
|
ASSERT_EQ(test(-1, 0), -1);
|
|
ASSERT_EQ(test(-1, 1), 0);
|
|
ASSERT_EQ(test(-1, 2), 1);
|
|
ASSERT_EQ(test(0, max32u), max32i);
|
|
ASSERT_EQ(test(0, max32i), max32i);
|
|
ASSERT_EQ(test(1, max32i-1), max32i);
|
|
ASSERT_EQ(test(1, max32u), max32i);
|
|
ASSERT_EQ(test(1, max32i), max32i);
|
|
ASSERT_EQ(test(-1, max32i), max32i - 1);
|
|
ASSERT_EQ(test(-1, max32i+1), max32i);
|
|
ASSERT_EQ(test(-1, max32u), max32i);
|
|
ASSERT_EQ(test(min32i, -min32i), 0);
|
|
ASSERT_EQ(test(min32i, max32u), max32i);
|
|
ASSERT_EQ(test(min32i, max32u - 1), max32i - 1);
|
|
ASSERT_EQ(test(min32i, 1), min32i + 1);
|
|
}
|
|
|
|
TEST(cc, rectanglizer)
|
|
{
|
|
std::vector<std::tuple<uint32_t, uint32_t, uint32_t, uint32_t, uint8_t>> rectangles;
|
|
|
|
ASSERT_FALSE(cc::rectanglizer(1, 1, {}, rectangles));
|
|
ASSERT_FALSE(cc::rectanglizer(0, 0, {1}, rectangles));
|
|
ASSERT_FALSE(cc::rectanglizer(0, 1, {1}, rectangles));
|
|
ASSERT_FALSE(cc::rectanglizer(1, 0, {1}, rectangles));
|
|
|
|
ASSERT_TRUE(cc::rectanglizer(0, 0, {}, rectangles));
|
|
ASSERT_TRUE(rectangles.empty());
|
|
|
|
ASSERT_TRUE(cc::rectanglizer(1, 1, {0}, rectangles));
|
|
ASSERT_TRUE(rectangles.empty());
|
|
|
|
ASSERT_TRUE(cc::rectanglizer(1, 1, {1}, rectangles));
|
|
ASSERT_EQ(rectangles.size(), 1);
|
|
ASSERT_EQ(rectangles[0], std::make_tuple(0, 0, 1, 1, 1));
|
|
|
|
ASSERT_TRUE(cc::rectanglizer(2, 2, {1, 0, 0, 1}, rectangles));
|
|
ASSERT_EQ(rectangles.size(), 2);
|
|
ASSERT_EQ(rectangles[0], std::make_tuple(0, 0, 1, 1, 1));
|
|
ASSERT_EQ(rectangles[1], std::make_tuple(1, 1, 1, 1, 1));
|
|
|
|
ASSERT_TRUE(cc::rectanglizer(2, 2, {0, 0, 0, 1}, rectangles));
|
|
ASSERT_EQ(rectangles.size(), 1);
|
|
ASSERT_EQ(rectangles[0], std::make_tuple(1, 1, 1, 1, 1));
|
|
|
|
ASSERT_TRUE(cc::rectanglizer(2, 2, {0, 0, 1, 1}, rectangles));
|
|
ASSERT_EQ(rectangles.size(), 1);
|
|
ASSERT_EQ(rectangles[0], std::make_tuple(0, 1, 2, 1, 1));
|
|
|
|
ASSERT_TRUE(cc::rectanglizer(2, 2, {0, 1, 0, 1}, rectangles));
|
|
ASSERT_EQ(rectangles.size(), 1);
|
|
ASSERT_EQ(rectangles[0], std::make_tuple(1, 0, 1, 2, 1));
|
|
|
|
ASSERT_TRUE(cc::rectanglizer(2, 2, {1, 1, 1, 1}, rectangles));
|
|
ASSERT_EQ(rectangles.size(), 1);
|
|
ASSERT_EQ(rectangles[0], std::make_tuple(0, 0, 2, 2, 1));
|
|
|
|
ASSERT_TRUE(cc::rectanglizer(8, 8,
|
|
{
|
|
1, 1, 1, 1, 0, 1, 1, 0,
|
|
1, 1, 1, 0, 3, 1, 1, 0,
|
|
1, 0, 1, 1, 3, 1, 1, 0,
|
|
1, 0, 1, 0, 0, 0, 1, 1,
|
|
0, 2, 2, 0, 0, 0, 1, 1,
|
|
2, 2, 2, 1, 0, 0, 1, 1,
|
|
1, 2, 2, 1, 0, 0, 2, 1,
|
|
1, 1, 1, 2, 1, 0, 1, 1,
|
|
|
|
// 0 1 2 3 4 5 6 7
|
|
// 0 a, a, a, a, 0, b, b, 0,
|
|
// 1 c, c, c, 0, d, b, b, 0,
|
|
// 2 e, 0, f, f, d, b, b, 0,
|
|
// 3 e, 0, g, 0, 0, 0, h, h,
|
|
// 4 0, i, i, 0, 0, 0, h, h,
|
|
// 5 j, i, i, k, 0, 0, h, h,
|
|
// 6 l, i, i, k, 0, 0, m, n,
|
|
// 7 l, o, o, p, q, 0, r, n,
|
|
}, rectangles));
|
|
ASSERT_EQ(rectangles.size(), 'r' - 'a' + 1);
|
|
ASSERT_EQ(rectangles['a' - 'a'], std::make_tuple(0, 0, 4, 1, 1));
|
|
ASSERT_EQ(rectangles['b' - 'a'], std::make_tuple(5, 0, 2, 3, 1));
|
|
ASSERT_EQ(rectangles['c' - 'a'], std::make_tuple(0, 1, 3, 1, 1));
|
|
ASSERT_EQ(rectangles['d' - 'a'], std::make_tuple(4, 1, 1, 2, 3));
|
|
ASSERT_EQ(rectangles['e' - 'a'], std::make_tuple(0, 2, 1, 2, 1));
|
|
ASSERT_EQ(rectangles['f' - 'a'], std::make_tuple(2, 2, 2, 1, 1));
|
|
ASSERT_EQ(rectangles['g' - 'a'], std::make_tuple(2, 3, 1, 1, 1));
|
|
ASSERT_EQ(rectangles['h' - 'a'], std::make_tuple(6, 3, 2, 3, 1));
|
|
ASSERT_EQ(rectangles['i' - 'a'], std::make_tuple(1, 4, 2, 3, 2));
|
|
ASSERT_EQ(rectangles['j' - 'a'], std::make_tuple(0, 5, 1, 1, 2));
|
|
ASSERT_EQ(rectangles['k' - 'a'], std::make_tuple(3, 5, 1, 2, 1));
|
|
ASSERT_EQ(rectangles['l' - 'a'], std::make_tuple(0, 6, 1, 2, 1));
|
|
ASSERT_EQ(rectangles['m' - 'a'], std::make_tuple(6, 6, 1, 1, 2));
|
|
ASSERT_EQ(rectangles['n' - 'a'], std::make_tuple(7, 6, 1, 2, 1));
|
|
ASSERT_EQ(rectangles['o' - 'a'], std::make_tuple(1, 7, 2, 1, 1));
|
|
ASSERT_EQ(rectangles['p' - 'a'], std::make_tuple(3, 7, 1, 1, 2));
|
|
ASSERT_EQ(rectangles['q' - 'a'], std::make_tuple(4, 7, 1, 1, 1));
|
|
ASSERT_EQ(rectangles['r' - 'a'], std::make_tuple(6, 7, 1, 1, 1));
|
|
}
|
|
|
|
TEST(cc, special_events)
|
|
{
|
|
TestDB *db = new TestDB();
|
|
cryptonote::BlockchainDB *bdb = db;
|
|
cc::add_init_state(*bdb);
|
|
cc::special_event_data_t event_data;
|
|
|
|
cc::special_event_list_t test_events[2] = {
|
|
{ 0, 0, 0, 0, "none", "" },
|
|
{ 1, 1, 1, 3, "test", "" }
|
|
};
|
|
|
|
// genesis
|
|
db->add_block({}, 0, 0, {}, 0, 1, {});
|
|
|
|
// nothing yet
|
|
ASSERT_FALSE(cc::get_active_special_event(*db, 0, event_data));
|
|
|
|
// to next game update
|
|
do { db->add_block({}, 0, 0, {}, bdb->get_block_already_generated_coins(bdb->height()-1), 1, {}); } while (!cryptonote::is_game_update_block(bdb->height()-1));
|
|
ASSERT_TRUE(cryptonote::is_game_update_block(bdb->height()-1));
|
|
|
|
// update, 100% chance we get the test event
|
|
cc::update_special_events(*db, 0, cc::update_special_events(*db, 0, bdb->top_block_hash(), 1, test_events));
|
|
ASSERT_TRUE(cc::get_active_special_event(*db, 0, event_data));
|
|
ASSERT_EQ(event_data.special_event, 1);
|
|
uint64_t event_start_height = bdb->height() - 1;
|
|
ASSERT_EQ(event_data.start_height, event_start_height);
|
|
ASSERT_EQ(event_data.duration, 0);
|
|
|
|
// to next game update
|
|
do { db->add_block({}, 0, 0, {}, bdb->get_block_already_generated_coins(bdb->height()-1), 1, {}); } while (!cryptonote::is_game_update_block(bdb->height()-1));
|
|
ASSERT_TRUE(cryptonote::is_game_update_block(bdb->height()-1));
|
|
|
|
// update, 100% chance for the test event to lapse
|
|
cc::update_special_events(*db, 0, cc::update_special_events(*db, 0, bdb->top_block_hash(), 1, test_events));
|
|
ASSERT_FALSE(cc::get_active_special_event(*db, 0, event_data));
|
|
|
|
std::vector<cc::special_event_data_t> events;
|
|
db->get_cc_special_events(0, events);
|
|
ASSERT_EQ(events.size(), 1);
|
|
ASSERT_EQ(events[0].special_event, 1);
|
|
ASSERT_EQ(events[0].start_height, GAME_UPDATE_FREQUENCY);
|
|
ASSERT_EQ(events[0].duration, 1);
|
|
|
|
// pop, the event gets back to active
|
|
cryptonote::block blk;
|
|
std::vector<cryptonote::transaction> txs;
|
|
if (cryptonote::is_game_update_block(db->height() - 1))
|
|
cc::revert_special_events(*db, 0);
|
|
bdb->pop_block(blk, txs);
|
|
ASSERT_TRUE(cc::get_active_special_event(*db, 0, event_data));
|
|
ASSERT_EQ(event_data.special_event, 1);
|
|
ASSERT_EQ(event_data.start_height, event_start_height);
|
|
ASSERT_EQ(event_data.duration, 0);
|
|
|
|
// pop enough to get back to before the event start
|
|
for (int i = 0; i < GAME_UPDATE_FREQUENCY; ++i)
|
|
{
|
|
if (cryptonote::is_game_update_block(db->height() - 1))
|
|
cc::revert_special_events(*db, 0);
|
|
bdb->pop_block(blk, txs);
|
|
}
|
|
|
|
// we should have no event active, nor history of any
|
|
ASSERT_FALSE(cc::get_active_special_event(*db, 0, event_data));
|
|
db->get_cc_special_events(0, events);
|
|
ASSERT_EQ(events.size(), 0);
|
|
|
|
// switch the stop probability to 0.001%, check it does end after max duration 3
|
|
test_events[1].stop_one_in = 100000;
|
|
|
|
for (int i = 0; i < 3; ++i)
|
|
{
|
|
do { db->add_block({}, 0, 0, {}, bdb->get_block_already_generated_coins(bdb->height()-1), 1, {}); } while (!cryptonote::is_game_update_block(bdb->height()-1));
|
|
ASSERT_TRUE(cryptonote::is_game_update_block(bdb->height()-1));
|
|
|
|
// update, 100% chance we get the test event
|
|
cc::update_special_events(*db, 0, cc::update_special_events(*db, 0, bdb->top_block_hash(), 1, test_events));
|
|
ASSERT_TRUE(cc::get_active_special_event(*db, 0, event_data));
|
|
ASSERT_EQ(event_data.special_event, 1);
|
|
if (i == 0)
|
|
event_start_height = bdb->height() - 1;
|
|
ASSERT_EQ(event_data.start_height, event_start_height);
|
|
ASSERT_EQ(event_data.duration, 0);
|
|
}
|
|
|
|
// next update, it ends
|
|
do { db->add_block({}, 0, 0, {}, bdb->get_block_already_generated_coins(bdb->height()-1), 1, {}); } while (!cryptonote::is_game_update_block(bdb->height()-1));
|
|
ASSERT_TRUE(cryptonote::is_game_update_block(bdb->height()-1));
|
|
|
|
// update, 100% chance we get the test event
|
|
cc::update_special_events(*db, 0, cc::update_special_events(*db, 0, bdb->top_block_hash(), 1, test_events));
|
|
ASSERT_FALSE(cc::get_active_special_event(*db, 0, event_data));
|
|
db->get_cc_special_events(0, events);
|
|
ASSERT_EQ(events.size(), 1);
|
|
ASSERT_EQ(events[0].special_event, 1);
|
|
ASSERT_EQ(events[0].start_height, GAME_UPDATE_FREQUENCY);
|
|
ASSERT_EQ(events[0].duration, 3);
|
|
|
|
// switch max duration back to 1
|
|
test_events[1].max_duration = 1;
|
|
|
|
// next update, it starts again
|
|
do { db->add_block({}, 0, 0, {}, bdb->get_block_already_generated_coins(bdb->height()-1), 1, {}); } while (!cryptonote::is_game_update_block(bdb->height()-1));
|
|
ASSERT_TRUE(cryptonote::is_game_update_block(bdb->height()-1));
|
|
cc::update_special_events(*db, 0, cc::update_special_events(*db, 0, bdb->top_block_hash(), 1, test_events));
|
|
ASSERT_TRUE(cc::get_active_special_event(*db, 0, event_data));
|
|
ASSERT_EQ(event_data.special_event, 1);
|
|
uint64_t event_start_height_2 = bdb->height() - 1;
|
|
ASSERT_EQ(event_data.start_height, event_start_height_2);
|
|
ASSERT_EQ(event_data.duration, 0);
|
|
|
|
db->get_cc_special_events(0, events);
|
|
ASSERT_EQ(events.size(), 2);
|
|
ASSERT_EQ(events[0].special_event, 1);
|
|
ASSERT_EQ(events[0].start_height, event_start_height);
|
|
ASSERT_EQ(events[0].duration, 3);
|
|
ASSERT_EQ(events[1].special_event, 1);
|
|
ASSERT_EQ(events[1].start_height, event_start_height_2);
|
|
ASSERT_EQ(events[1].duration, 0);
|
|
|
|
// and it stops after one other update
|
|
do { db->add_block({}, 0, 0, {}, bdb->get_block_already_generated_coins(bdb->height()-1), 1, {}); } while (!cryptonote::is_game_update_block(bdb->height()-1));
|
|
ASSERT_TRUE(cryptonote::is_game_update_block(bdb->height()-1));
|
|
cc::update_special_events(*db, 0, cc::update_special_events(*db, 0, bdb->top_block_hash(), 1, test_events));
|
|
ASSERT_FALSE(cc::get_active_special_event(*db, 0, event_data));
|
|
|
|
db->get_cc_special_events(0, events);
|
|
ASSERT_EQ(events.size(), 2);
|
|
ASSERT_EQ(events[0].special_event, 1);
|
|
ASSERT_EQ(events[0].start_height, event_start_height);
|
|
ASSERT_EQ(events[0].duration, 3);
|
|
ASSERT_EQ(events[1].special_event, 1);
|
|
ASSERT_EQ(events[1].start_height, event_start_height_2);
|
|
ASSERT_EQ(events[1].duration, 1);
|
|
}
|
|
|
|
TEST(cc, base_temperature)
|
|
{
|
|
for (uint64_t h = 0; h < 1440 * 7; ++h)
|
|
{
|
|
const int32_t t = cc::get_base_temperature(h);
|
|
ASSERT_GE(t, -100);
|
|
ASSERT_LE(t, +100);
|
|
}
|
|
}
|
|
|
|
TEST(cc, test_heating)
|
|
{
|
|
uint32_t heating = 0xffffffff;
|
|
for (int32_t i = -256; i <= 0; i += 20)
|
|
{
|
|
uint32_t h = cc::get_heating_needs(i, 0, 0, 32, 32, ROLE_RESIDENTIAL1, 100, 100);
|
|
ASSERT_LE(h, heating);
|
|
heating = h;
|
|
}
|
|
}
|
|
|
|
TEST(cc, badge_score)
|
|
{
|
|
for (int i = 0; i < 32; ++i)
|
|
{
|
|
std::vector<uint32_t> badges;
|
|
for (int j = 0; j < NUM_BADGE_LEVELS; ++j)
|
|
badges.push_back(rand() % 8);
|
|
const std::pair<uint64_t, uint32_t> score = cc::get_badge_score(badges);
|
|
ASSERT_EQ(badges, cc::get_badges_from_score(score.first));
|
|
}
|
|
}
|
|
|
|
TEST(cc, invitation)
|
|
{
|
|
std::string invitation;
|
|
cryptonote::keypair base_keys = cryptonote::keypair::generate(hw::get_device("default"));
|
|
uint32_t account;
|
|
uint64_t amount, expiration;
|
|
crypto::public_key pkey;
|
|
cryptonote::blobdata inner;
|
|
crypto::signature inner_signature;
|
|
|
|
ASSERT_TRUE(cc::make_invitation(base_keys.sec, 8, 0, 100, invitation));
|
|
ASSERT_FALSE(invitation.empty());
|
|
ASSERT_TRUE(cc::parse_invitation(invitation, inner, inner_signature, account, amount, expiration, pkey));
|
|
ASSERT_EQ(account, 8);
|
|
ASSERT_EQ(amount, 0);
|
|
ASSERT_EQ(expiration, 100);
|
|
}
|
|
|
|
TEST(cc, city_proximity)
|
|
{
|
|
static const uint32_t n_cities = 1024;
|
|
uint32_t cities[n_cities][2];
|
|
for (uint32_t city = 0; city < n_cities; ++city)
|
|
{
|
|
cc::get_city_origin(city, cities[city][0], cities[city][1]);
|
|
ASSERT_GE(cities[city][0], MIN_CITY_DISTANCE);
|
|
ASSERT_LE(cities[city][0], std::numeric_limits<uint32_t>::max() - MIN_CITY_DISTANCE);
|
|
ASSERT_GE(cities[city][1], MIN_CITY_DISTANCE);
|
|
ASSERT_LE(cities[city][1], std::numeric_limits<uint32_t>::max() - MIN_CITY_DISTANCE);
|
|
for (uint32_t c = 0; c < city; ++c)
|
|
{
|
|
const uint32_t dx = cities[city][0] > cities[c][0] ? (cities[city][0] - cities[c][0]) : (cities[c][0] - cities[city][0]);
|
|
const uint32_t dy = cities[city][1] > cities[c][1] ? (cities[city][1] - cities[c][1]) : (cities[c][1] - cities[city][1]);
|
|
ASSERT_GE(std::max(dx, dy), MIN_CITY_DISTANCE);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(cc, terrain_is_deterministic)
|
|
{
|
|
const cc::cc_potential_state_t *state = cc::get_cc_potential_state(0, 0);
|
|
uint32_t ox, oy;
|
|
cc::get_city_origin(0, ox, oy);
|
|
|
|
uint16_t line0[8001], line1[8001];
|
|
cc::flush_terrain();
|
|
for (int32_t x = -4000; x <= 4000; ++x)
|
|
line0[x + 4000] = cc::get_cc_height(state, ox + x, oy);
|
|
cc::flush_terrain();
|
|
for (int32_t x = 4000; x >= -4000; --x)
|
|
line1[x + 4000] = cc::get_cc_height(state, ox + x, oy);
|
|
ASSERT_EQ(0, memcmp(line0, line1, 8001 * 2));
|
|
}
|
|
|
|
TEST(cc, farming_schedule)
|
|
{
|
|
static const uint64_t mid_jan = 8460;
|
|
static const uint64_t mid_mar = 50;
|
|
static const uint64_t late_apr = 1200;
|
|
static const uint64_t early_may = 1500;
|
|
static const uint64_t late_may = 2000;
|
|
static const uint64_t late_jun = 2900;
|
|
static const uint64_t mid_jul = 3420;
|
|
static const uint64_t mid_aug = 4300;
|
|
static const uint64_t mid_sep = 5200;
|
|
static const uint64_t mid_oct = 6000;
|
|
static const uint64_t mid_dec = 7700;
|
|
uint64_t blocks;
|
|
|
|
ASSERT_TRUE(cc::check_food_calendar_sanity());
|
|
|
|
auto check = [](uint64_t height, bool vegetables_sowing_season, bool vegetables_harvest_season, bool grain_sowing_season, bool grain_harvest_season)
|
|
{
|
|
ASSERT_EQ(cc::is_valid_sowing_time(CROP_VEGETABLES, height), vegetables_sowing_season);
|
|
ASSERT_EQ(cc::is_valid_harvest_time(CROP_VEGETABLES, height), vegetables_harvest_season);
|
|
ASSERT_EQ(cc::is_valid_sowing_time(CROP_GRAIN, height), grain_sowing_season);
|
|
ASSERT_EQ(cc::is_valid_harvest_time(CROP_GRAIN, height), grain_harvest_season);
|
|
};
|
|
|
|
// calendar:
|
|
// start of march: vegetable sowing season starts
|
|
// start of may: vegetable harvest season starts
|
|
// start of may: grain sowing season starts
|
|
// mid may: vegetable sowing season ends
|
|
// mid jun: grain sowing season ends
|
|
// end of june: vegetable harvest season ends
|
|
// start of august: grain harvest season starts
|
|
// end of september: grain harvest season ends
|
|
|
|
// sow/v har/v sow/g har/g
|
|
check(mid_jan, false, false, false, false);
|
|
// vegetable sowing season starts
|
|
check(mid_mar, true, false, false, false);
|
|
check(late_apr, true, false, false, false);
|
|
// vegetable harvest season starts
|
|
// grain sowing season starts
|
|
check(early_may, true, true, true, false);
|
|
// vegetable sowing season ends
|
|
check(late_may, false, true, true, false);
|
|
// grain sowing season ends
|
|
check(late_jun, false, true, false, false);
|
|
// vegetable harvest season ends
|
|
check(mid_jul, false, false, false, false);
|
|
// grain harvest season starts
|
|
check(mid_aug, false, false, false, true );
|
|
check(mid_sep, false, false, false, true );
|
|
// grain harvest season ends
|
|
check(mid_oct, false, false, false, false);
|
|
check(mid_dec, false, false, false, false);
|
|
|
|
for (uint64_t dh = 0; dh < 3; ++dh)
|
|
{
|
|
uint64_t DH = dh * BLOCKS_PER_GAME_YEAR;
|
|
|
|
// vegetables sowing season starts in early march
|
|
blocks = cc::get_blocks_since_sowing_season_start(CROP_VEGETABLES, mid_jan + DH);
|
|
ASSERT_GE(blocks, BLOCKS_PER_GAME_YEAR / 2);
|
|
ASSERT_LT(blocks, BLOCKS_PER_GAME_YEAR);
|
|
|
|
blocks = cc::get_blocks_since_sowing_season_start(CROP_VEGETABLES, mid_mar + DH);
|
|
ASSERT_LE(blocks, BLOCKS_PER_GAME_YEAR / 12);
|
|
|
|
blocks = cc::get_blocks_since_sowing_season_start(CROP_VEGETABLES, late_may + DH);
|
|
ASSERT_GE(blocks, 2 * BLOCKS_PER_GAME_YEAR / 12);
|
|
ASSERT_LE(blocks, 3 * BLOCKS_PER_GAME_YEAR / 12);
|
|
|
|
blocks = cc::get_blocks_since_sowing_season_start(CROP_VEGETABLES, mid_dec + DH);
|
|
ASSERT_GE(blocks, 9 * BLOCKS_PER_GAME_YEAR / 12);
|
|
ASSERT_LE(blocks, 11 * BLOCKS_PER_GAME_YEAR / 12);
|
|
|
|
// vegetables sowing season starts in early march
|
|
blocks = cc::get_blocks_till_sowing_season_start(CROP_VEGETABLES, mid_jan + DH);
|
|
ASSERT_GE(blocks, BLOCKS_PER_GAME_YEAR / 12);
|
|
ASSERT_LE(blocks, 2 * BLOCKS_PER_GAME_YEAR / 12);
|
|
|
|
blocks = cc::get_blocks_till_sowing_season_start(CROP_VEGETABLES, mid_mar + DH);
|
|
ASSERT_GE(blocks, 11 * BLOCKS_PER_GAME_YEAR / 12);
|
|
ASSERT_LT(blocks, BLOCKS_PER_GAME_YEAR);
|
|
|
|
blocks = cc::get_blocks_till_sowing_season_start(CROP_VEGETABLES, mid_dec + DH);
|
|
ASSERT_GE(blocks, 2 * BLOCKS_PER_GAME_YEAR / 12);
|
|
ASSERT_LT(blocks, 4 * BLOCKS_PER_GAME_YEAR / 12);
|
|
|
|
// vegetables sowing season ends in mid may
|
|
blocks = cc::get_blocks_till_sowing_season_end(CROP_VEGETABLES, mid_dec + DH);
|
|
ASSERT_GE(blocks, 4 * BLOCKS_PER_GAME_YEAR / 12);
|
|
ASSERT_LT(blocks, 5 * BLOCKS_PER_GAME_YEAR / 12);
|
|
|
|
blocks = cc::get_blocks_till_sowing_season_end(CROP_VEGETABLES, early_may + DH);
|
|
ASSERT_LT(blocks, BLOCKS_PER_GAME_YEAR / 12);
|
|
|
|
blocks = cc::get_blocks_till_sowing_season_end(CROP_VEGETABLES, late_jun + DH);
|
|
ASSERT_GE(blocks, 10 * BLOCKS_PER_GAME_YEAR / 12);
|
|
ASSERT_LT(blocks, 11 * BLOCKS_PER_GAME_YEAR / 12);
|
|
}
|
|
|
|
static const uint8_t crops[] = { CROP_VEGETABLES, CROP_GRAIN };
|
|
for (const uint8_t crop: crops)
|
|
{
|
|
ASSERT_TRUE(cryptonote::is_game_update_block(GAME_UPDATE_FREQUENCY + cc::get_blocks_till_sowing_season_start(crop, 0)));
|
|
ASSERT_TRUE(cryptonote::is_game_update_block(GAME_UPDATE_FREQUENCY + cc::get_blocks_till_sowing_season_end(crop, 0)));
|
|
ASSERT_TRUE(cryptonote::is_game_update_block(GAME_UPDATE_FREQUENCY + cc::get_blocks_till_harvest_season_start(crop, 0)));
|
|
ASSERT_TRUE(cryptonote::is_game_update_block(GAME_UPDATE_FREQUENCY + cc::get_blocks_till_harvest_season_end(crop, 0)));
|
|
}
|
|
}
|
|
|
|
TEST(cc, harvest_overflow)
|
|
{
|
|
uint32_t high_amount = cc::get_harvest_amount(0, 0, 255, 255, 300, CROP_GRAIN, 65536, 100, 255, 10000, cc::SPECIAL_EVENT_GOOD_HARVEST, true, true, true, true, true, true, true, true, true, true, true, true);
|
|
uint32_t low_amount = cc::get_harvest_amount(0, 0, 31, 31, 100, CROP_GRAIN, 32000, 50, 127, 1000000000, cc::SPECIAL_EVENT_NONE, false, false, false, false, false, false, false, false, false, false, false, false);
|
|
ASSERT_GE(high_amount, low_amount);
|
|
}
|
|
|
|
TEST(cc, nutrients)
|
|
{
|
|
ASSERT_EQ(cc::get_nutrients_recovery_increase(100, false), 0);
|
|
ASSERT_EQ(cc::get_nutrients_recovery_increase(99, false), 1);
|
|
ASSERT_GT(cc::get_nutrients_recovery_increase(0, false), 0);
|
|
ASSERT_GT(cc::get_nutrients_decrease_for_harvest(100), 0);
|
|
ASSERT_EQ(cc::get_nutrients_decrease_for_harvest(0), 0);
|
|
}
|
|
|
|
TEST(cc, farming_temperatures)
|
|
{
|
|
ASSERT_GE(cc::get_growth_temperature(CROP_VEGETABLES), cc::get_damage_temperature(CROP_VEGETABLES));
|
|
ASSERT_GE(cc::get_growth_temperature(CROP_GRAIN), cc::get_damage_temperature(CROP_GRAIN));
|
|
}
|
|
|
|
TEST(cc, crop_yield)
|
|
{
|
|
ASSERT_EQ(cc::get_crop_yield(CROP_GRAIN, CROP_START_YIELD, {}), CROP_START_YIELD);
|
|
const uint32_t yield = cc::get_crop_yield(CROP_GRAIN, CROP_START_YIELD, std::vector<int32_t>(10000, 1000));
|
|
ASSERT_LE(yield, 65536);
|
|
ASSERT_GE(yield, 65536 * 9 / 10);
|
|
}
|