forked from townforge/townforge
4293 lines
162 KiB
C++
4293 lines
162 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 <boost/filesystem.hpp>
|
|
#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"
|
|
#include "cc/cc_mortgage.h"
|
|
#include "cc/fixed.h"
|
|
#include "cc/cc_collectible_fish.h"
|
|
|
|
#define BASE_TEST_FORK 14
|
|
|
|
#define PREDEFINED_BULDINGS 1
|
|
#define TEST_ACCOUNT 5
|
|
|
|
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;
|
|
uint32_t average_slope;
|
|
uint32_t south_facing;
|
|
uint32_t bridge_score;
|
|
uint32_t underwater_area[2];
|
|
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;
|
|
uint32_t mortgage;
|
|
std::vector<uint8_t> tiles;
|
|
|
|
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, uint32_t average_slope, uint32_t south_facing, uint32_t bridge_score, const uint32_t underwater_area[2], 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, uint32_t mortgage): 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), average_slope(average_slope), south_facing(south_facing), bridge_score(bridge_score), economic_power(economic_power), construction_height(construction_height), budget(budget), palette(palette), active(active), ignore(ignore), fire_state(fire_state), name(name), mortgage(mortgage)
|
|
{
|
|
memcpy(this->potential, potential, NUM_POTENTIALS);
|
|
memcpy(this->underwater_area, underwater_area, sizeof(this->underwater_area));
|
|
cc::get_empty_packed_tiles(tiles);
|
|
}
|
|
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 && mortgage == other.mortgage && tiles == other.tiles; }
|
|
};
|
|
|
|
struct shares_t
|
|
{
|
|
uint64_t shares[NUM_ROLES];
|
|
uint64_t payout[NUM_ROLES];
|
|
uint64_t weighted_shares;
|
|
|
|
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; if (weighted_shares != other.weighted_shares) 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 creator;
|
|
uint64_t creation_height;
|
|
std::string name;
|
|
bool ignore;
|
|
bool is_group;
|
|
uint32_t group;
|
|
std::string pdesc;
|
|
std::string sdesc;
|
|
std::vector<uint64_t> user_data;
|
|
|
|
bool operator==(const custom_item_t &other) const { return 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<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 && 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 = 14; 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, const crypto::public_key &pmspk, const crypto::public_key &pmvpk, 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 for_all_cc_accounts(std::function<bool(const cryptonote::cc_account_data_t&)> f) const override
|
|
{
|
|
for (size_t i = 0; i < state.accounts.size(); ++i)
|
|
{
|
|
const auto &account = state.accounts[i];
|
|
if (account.public_key == DEAD_PKEY)
|
|
continue;
|
|
cryptonote::cc_account_data_t ad{};
|
|
ad.id = i;
|
|
ad.public_key = account.public_key;
|
|
ad.balance = account.balance;
|
|
ad.item_balances = account.item_balances;
|
|
ad.flags = account.flags;
|
|
ad.name = account.name;
|
|
ad.ignore = account.ignore;
|
|
if (!f(ad))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
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_data(uint32_t id, const cryptonote::cc_account_data_t &ad) override
|
|
{
|
|
state.accounts[id] = {};
|
|
state.accounts[id].public_key = ad.public_key;
|
|
state.accounts[id].balance = ad.balance;
|
|
state.accounts[id].item_balances = ad.item_balances;
|
|
state.accounts[id].flags = ad.flags;
|
|
state.accounts[id].name = ad.name;
|
|
state.accounts[id].ignore = ad.ignore;
|
|
}
|
|
|
|
virtual bool get_cc_account_balance(uint32_t id, uint64_t &balance) const override
|
|
{
|
|
if (id == 0 || id >= state.accounts.size())
|
|
return false;
|
|
balance = state.accounts[id].balance;
|
|
return true;
|
|
}
|
|
|
|
virtual void set_cc_account_balance(uint32_t id, uint64_t balance) override
|
|
{
|
|
state.accounts[id].balance = balance;
|
|
}
|
|
|
|
virtual bool change_cc_account_balance(uint32_t id, int64_t delta_balance) override
|
|
{
|
|
if (id == 0 || id >= state.accounts.size())
|
|
return false;
|
|
state.accounts[id].balance += delta_balance;
|
|
return true;
|
|
}
|
|
|
|
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, crypto::null_pkey, crypto::null_pkey);
|
|
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.ignore = false;
|
|
ccd.moose = NUM_STARTING_MOOSE;
|
|
ccd.bears = NUM_STARTING_BEARS;
|
|
return true;
|
|
}
|
|
|
|
virtual bool get_cc_city_treasury(uint32_t id, uint32_t &treasury) const override
|
|
{
|
|
cryptonote::cc_city_data_t ccd;
|
|
if (!get_cc_city_data(id, ccd))
|
|
return false;
|
|
treasury = ccd.treasury;
|
|
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.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, uint32_t average_slope, uint32_t south_facing, uint32_t bridge_score, const uint32_t underwater_area[2]) 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);
|
|
state.flags[actual_id] = {true, owner_id, city_id, ROLE_EMPTY, x0, y0, x1, y1, 0, base_height, elevation_bonus, average_slope, south_facing, bridge_score, underwater_area, potential, 0, height(), {}, palette, false, false, 0, "", 0};
|
|
return actual_id;
|
|
}
|
|
|
|
virtual void delete_cc_flag(uint32_t id, bool reserve) override
|
|
{
|
|
cc::get_empty_packed_tiles(state.flags[id].tiles);
|
|
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;
|
|
fd.mortgage = flag.mortgage;
|
|
fd.tiles = flag.tiles;
|
|
if (!f(fd))
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
virtual bool get_cc_flag_data(uint32_t id, cryptonote::cc_flag_data_t &fd) const override
|
|
{
|
|
if (id >= state.flags.size() || !state.flags[id].in_use)
|
|
return false;
|
|
fd = {};
|
|
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;
|
|
fd.mortgage = state.flags[id].mortgage;
|
|
fd.tiles = state.flags[id].tiles;
|
|
return true;
|
|
}
|
|
|
|
virtual void set_cc_flag_data(uint32_t id, const cryptonote::cc_flag_data_t &fd) override
|
|
{
|
|
state.flags[id] = {};
|
|
state.flags[id].in_use = true;
|
|
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;
|
|
state.flags[id].mortgage = fd.mortgage;
|
|
state.flags[id].tiles = fd.tiles;
|
|
}
|
|
|
|
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<uint8_t> &tiles) override
|
|
{
|
|
state.flags[id].tiles = tiles;
|
|
}
|
|
|
|
virtual uint32_t get_highest_cc_flag_id() const override
|
|
{
|
|
return state.flags.size();
|
|
}
|
|
|
|
#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_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];
|
|
}
|
|
sd.weighted_shares = x.weighted_shares;
|
|
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 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 gold, const std::string &coin_design, const std::vector<uint64_t> &user_data, const crypto::hash &hash, bool can_create_more, uint32_t prestige_bonus)
|
|
{
|
|
uint32_t actual_id;
|
|
for (actual_id = 0; actual_id < state.items.size(); ++actual_id)
|
|
if (!state.items[actual_id].in_use)
|
|
break;
|
|
if (actual_id == state.items.size())
|
|
state.items.push_back({});
|
|
state.items[actual_id] = {true, 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)
|
|
{
|
|
id -= ITEM_FIRST_USER;
|
|
state.items[id].in_use = false;
|
|
}
|
|
|
|
bool get_cc_custom_item_data(uint32_t id, cc::cc_custom_item_t &cid) const
|
|
{
|
|
id -= ITEM_FIRST_USER;
|
|
if (!state.items[id].in_use)
|
|
return false;
|
|
cid.id = id + ITEM_FIRST_USER;
|
|
cid.creator = state.items[id].creator;
|
|
cid.creation_height = state.items[id].creation_height;
|
|
cid.name = state.items[id].name;
|
|
cid.ignore = state.items[id].ignore;
|
|
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)
|
|
{
|
|
id -= ITEM_FIRST_USER;
|
|
if (!state.items[id].in_use)
|
|
return;
|
|
state.items[id].ignore = ignore;
|
|
}
|
|
|
|
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 = ITEM_FIRST_USER + i;
|
|
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);
|
|
}
|
|
|
|
virtual bool get_cc_runestones(uint32_t flag, std::vector<cc::runestone_t> &runestones) const
|
|
{
|
|
runestones.clear();
|
|
return true;
|
|
}
|
|
|
|
private:
|
|
std::vector<block_t> blocks;
|
|
state_t state;
|
|
};
|
|
|
|
crypto::public_key TestDB::DEAD_PKEY = crypto::null_pkey;
|
|
|
|
void inc(crypto::public_key &k)
|
|
{
|
|
for (int n = 0; n < 32; ++n)
|
|
{
|
|
++(unsigned char&)k.data[n];
|
|
if (k.data[n] != 0)
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
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;
|
|
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;
|
|
cmd.flag = 0;
|
|
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, 0, 0, c0));
|
|
ASSERT_TRUE(cc::get_new_flag_cost(0, 0, 1, 2, 0, 0, c1));
|
|
ASSERT_GT(c1, c0);
|
|
|
|
// square is cheaper for the same area
|
|
ASSERT_TRUE(cc::get_new_flag_cost(0, 0, 3, 3, 0, 0, c0));
|
|
ASSERT_TRUE(cc::get_new_flag_cost(0, 0, 1, 7, 0, 0, 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;
|
|
std::unique_ptr<TestDB> db(new TestDB());
|
|
cryptonote::BlockchainDB *bdb = db.get();
|
|
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, BASE_TEST_FORK, 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, "", crypto::null_pkey, crypto::null_pkey);
|
|
uint8_t potentials[NUM_POTENTIALS];
|
|
for (int i = 0; i < NUM_POTENTIALS; ++i)
|
|
potentials[i] = 100;
|
|
const uint32_t underwater[2] = {0, 0};
|
|
uint32_t flag_id = bdb->allocate_new_cc_flag(NULL, account_id, city_id, 44, 45, 46, 47, potentials, 32, 33, 34, 35, 36, underwater);
|
|
cryptonote::cc_flag_data_t fd;
|
|
ASSERT_TRUE(bdb->get_cc_flag_data(flag_id, fd));
|
|
ASSERT_EQ(fd.active, false);
|
|
}
|
|
|
|
TEST(cc, buildings_start_not_on_fire)
|
|
{
|
|
cryptonote::cc_command_game_update_t cmd;
|
|
std::unique_ptr<TestDB> db(new TestDB());
|
|
cryptonote::BlockchainDB *bdb = db.get();
|
|
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, BASE_TEST_FORK, 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, "", crypto::null_pkey, crypto::null_pkey);
|
|
uint8_t potentials[NUM_POTENTIALS];
|
|
for (int i = 0; i < NUM_POTENTIALS; ++i)
|
|
potentials[i] = 100;
|
|
const uint32_t underwater[2] = {0, 0};
|
|
uint32_t flag_id = bdb->allocate_new_cc_flag(NULL, account_id, city_id, 44, 45, 46, 47, potentials, 32, 33, 34, 35, 36, underwater);
|
|
cryptonote::cc_flag_data_t fd;
|
|
ASSERT_TRUE(bdb->get_cc_flag_data(flag_id, fd));
|
|
ASSERT_EQ(fd.fire_state, 0);
|
|
}
|
|
|
|
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)14, (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;
|
|
|
|
const uint8_t version = bc->get_current_hard_fork_version();
|
|
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, version, 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, version, 0, 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, version, 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, version, 0, events);
|
|
ASSERT_TRUE(r) << label;
|
|
if (check)
|
|
check(db);
|
|
r = cc::revert_cc_command(*db, cmd, version);
|
|
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;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
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_redeem_account)
|
|
{
|
|
cryptonote::cc_command_redeem_account_t redeem_account;
|
|
std::vector<cryptonote::cc_command_t> setup;
|
|
|
|
cryptonote::keypair keys = cryptonote::keypair::generate(hw::get_device("default"));
|
|
cryptonote::keypair keys2 = cryptonote::keypair::generate(hw::get_device("default"));
|
|
cryptonote::keypair keys3 = cryptonote::keypair::generate(hw::get_device("default"));
|
|
|
|
crypto::secret_key derived_skey;
|
|
crypto::public_key derived_pkey;
|
|
cc::cc_derive_keys(keys.sec, derived_skey, derived_pkey);
|
|
|
|
// setup
|
|
cryptonote::cc_command_create_account_t create_account;
|
|
create_account.public_key = derived_pkey;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE * 2 + 1000;
|
|
create_account.name = "foo";
|
|
create_account.inviting_account = 0;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
setup.push_back(create_account);
|
|
|
|
// create an invitation
|
|
std::string invitation;
|
|
ASSERT_TRUE(cc::make_invitation(keys.sec, TEST_ACCOUNT, 420, 0, crypto::null_pkey, invitation));
|
|
cryptonote::blobdata inner;
|
|
crypto::signature inner_signature;
|
|
uint32_t account;
|
|
uint64_t amount, expiration;
|
|
crypto::public_key pkey;
|
|
crypto::secret_key secret_key;
|
|
crypto::signature secret_key_signature;
|
|
crypto::public_key recipient;
|
|
ASSERT_TRUE(cc::parse_invitation(invitation, inner, inner_signature, account, amount, expiration, pkey, recipient, secret_key, secret_key_signature));
|
|
ASSERT_EQ(account, TEST_ACCOUNT);
|
|
ASSERT_EQ(expiration, 0);
|
|
ASSERT_EQ(amount, 420);
|
|
ASSERT_EQ(recipient, crypto::null_pkey);
|
|
|
|
auto sign_invitation = [](cryptonote::cc_command_redeem_account_t &redeem_account, const crypto::hash *hash, const crypto::public_key &pkey, const crypto::secret_key &secret_key, const cryptonote::keypair *recipient = NULL) {
|
|
cryptonote::cc_command_t cmd = redeem_account;
|
|
cryptonote::cc_command_redeem_account_t &redeem_account2 = boost::get<cryptonote::cc_command_redeem_account_t>(cmd);
|
|
memset(&redeem_account2.invitation_signature, 0, sizeof(redeem_account2.invitation_signature));
|
|
memset(&redeem_account2.recipient_signature, 0, sizeof(redeem_account2.recipient_signature));
|
|
redeem_account2.cc_nonce = 0;
|
|
cryptonote::blobdata blob = cryptonote::t_serializable_object_to_blob(cmd);
|
|
crypto::hash signed_hash;
|
|
if (hash)
|
|
signed_hash = *hash;
|
|
else
|
|
crypto::cn_fast_hash(blob.data(), blob.size(), signed_hash);
|
|
crypto::generate_signature(signed_hash, pkey, secret_key, redeem_account.invitation_signature);
|
|
if (recipient)
|
|
crypto::generate_signature(signed_hash, recipient->pub, recipient->sec, redeem_account.recipient_signature);
|
|
else
|
|
memset(&redeem_account.recipient_signature, 0, sizeof(redeem_account.recipient_signature));
|
|
};
|
|
|
|
// signature on the wrong message
|
|
redeem_account.public_key = keys2.pub;
|
|
redeem_account.name = "redeem-test";
|
|
redeem_account.inviting_account = TEST_ACCOUNT;
|
|
redeem_account.invitation = inner;
|
|
redeem_account.pmspk = crypto::null_pkey;
|
|
redeem_account.pmvpk = crypto::null_pkey;
|
|
sign_invitation(redeem_account, &crypto::null_hash, pkey, secret_key);
|
|
test_commands(false, setup, redeem_account, "signature on the wrong message");
|
|
|
|
// signature with the wrong key
|
|
redeem_account.public_key = keys2.pub;
|
|
redeem_account.name = "redeem-test";
|
|
redeem_account.inviting_account = TEST_ACCOUNT;
|
|
redeem_account.invitation = inner;
|
|
redeem_account.pmspk = crypto::null_pkey;
|
|
redeem_account.pmvpk = crypto::null_pkey;
|
|
sign_invitation(redeem_account, NULL, derived_pkey, derived_skey);
|
|
test_commands(false, setup, redeem_account, "signature with the wrong key");
|
|
|
|
// wrong inviting account
|
|
redeem_account.public_key = keys2.pub;
|
|
redeem_account.name = "redeem-test";
|
|
redeem_account.inviting_account = TEST_ACCOUNT - 1;
|
|
redeem_account.invitation = inner;
|
|
redeem_account.pmspk = crypto::null_pkey;
|
|
redeem_account.pmvpk = crypto::null_pkey;
|
|
sign_invitation(redeem_account, NULL, pkey, secret_key);
|
|
test_commands(false, setup, redeem_account, "wrong inviting account");
|
|
|
|
// miner intercepts
|
|
redeem_account.public_key = keys2.pub;
|
|
redeem_account.name = "redeem-test";
|
|
redeem_account.inviting_account = TEST_ACCOUNT;
|
|
redeem_account.invitation = inner;
|
|
redeem_account.pmspk = crypto::null_pkey;
|
|
redeem_account.pmvpk = crypto::null_pkey;
|
|
sign_invitation(redeem_account, NULL, pkey, secret_key);
|
|
redeem_account.public_key = keys3.pub;
|
|
test_commands(false, setup, redeem_account, "miner intercepts");
|
|
|
|
// empty name
|
|
redeem_account.public_key = keys2.pub;
|
|
redeem_account.name = "";
|
|
redeem_account.inviting_account = TEST_ACCOUNT;
|
|
redeem_account.invitation = inner;
|
|
redeem_account.pmspk = crypto::null_pkey;
|
|
redeem_account.pmvpk = crypto::null_pkey;
|
|
sign_invitation(redeem_account, NULL, pkey, secret_key);
|
|
test_commands(false, setup, redeem_account, "empty name");
|
|
|
|
// invalid name
|
|
redeem_account.public_key = keys2.pub;
|
|
redeem_account.name = " a";
|
|
redeem_account.inviting_account = TEST_ACCOUNT;
|
|
redeem_account.invitation = inner;
|
|
redeem_account.pmspk = crypto::null_pkey;
|
|
redeem_account.pmvpk = crypto::null_pkey;
|
|
sign_invitation(redeem_account, NULL, pkey, secret_key);
|
|
test_commands(false, setup, redeem_account, "invalid name");
|
|
|
|
// good
|
|
redeem_account.public_key = keys2.pub;
|
|
redeem_account.name = "redeem-test";
|
|
redeem_account.inviting_account = TEST_ACCOUNT;
|
|
redeem_account.invitation = inner;
|
|
redeem_account.pmspk = crypto::null_pkey;
|
|
redeem_account.pmvpk = crypto::null_pkey;
|
|
sign_invitation(redeem_account, NULL, pkey, secret_key);
|
|
test_commands(true, setup, redeem_account, "valid");
|
|
|
|
// invitation with a recipient
|
|
ASSERT_TRUE(cc::make_invitation(keys.sec, TEST_ACCOUNT, 420, 0, keys3.pub, invitation));
|
|
ASSERT_TRUE(cc::parse_invitation(invitation, inner, inner_signature, account, amount, expiration, pkey, recipient, secret_key, secret_key_signature));
|
|
ASSERT_EQ(account, TEST_ACCOUNT);
|
|
ASSERT_EQ(expiration, 0);
|
|
ASSERT_EQ(amount, 420);
|
|
ASSERT_EQ(recipient, keys3.pub);
|
|
|
|
// bad recipient
|
|
redeem_account.public_key = keys2.pub;
|
|
redeem_account.name = "redeem-test-with-recipient";
|
|
redeem_account.inviting_account = TEST_ACCOUNT;
|
|
redeem_account.invitation = inner;
|
|
redeem_account.pmspk = crypto::null_pkey;
|
|
redeem_account.pmvpk = crypto::null_pkey;
|
|
sign_invitation(redeem_account, NULL, pkey, secret_key, &keys2);
|
|
test_commands(false, setup, redeem_account, "bad recipient");
|
|
|
|
// invitation with a recipient
|
|
ASSERT_TRUE(cc::make_invitation(keys.sec, TEST_ACCOUNT, 420, 0, keys2.pub, invitation));
|
|
ASSERT_TRUE(cc::parse_invitation(invitation, inner, inner_signature, account, amount, expiration, pkey, recipient, secret_key, secret_key_signature));
|
|
ASSERT_EQ(account, TEST_ACCOUNT);
|
|
ASSERT_EQ(expiration, 0);
|
|
ASSERT_EQ(amount, 420);
|
|
ASSERT_EQ(recipient, keys2.pub);
|
|
|
|
// invalid pmspk
|
|
redeem_account.public_key = keys2.pub;
|
|
redeem_account.name = "redeem-test-with-recipient";
|
|
redeem_account.inviting_account = TEST_ACCOUNT;
|
|
redeem_account.invitation = inner;
|
|
redeem_account.pmspk = crypto::null_pkey;
|
|
do inc(redeem_account.pmspk); while (check_key(redeem_account.pmspk));
|
|
redeem_account.pmvpk = crypto::null_pkey;
|
|
sign_invitation(redeem_account, NULL, pkey, secret_key, &keys2);
|
|
test_commands(false, setup, redeem_account, "invalid pmspk");
|
|
|
|
// invalid pmvpk
|
|
redeem_account.public_key = keys2.pub;
|
|
redeem_account.name = "redeem-test-with-recipient";
|
|
redeem_account.inviting_account = TEST_ACCOUNT;
|
|
redeem_account.invitation = inner;
|
|
redeem_account.pmspk = crypto::null_pkey;
|
|
do inc(redeem_account.pmvpk); while (check_key(redeem_account.pmvpk));
|
|
sign_invitation(redeem_account, NULL, pkey, secret_key, &keys2);
|
|
test_commands(false, setup, redeem_account, "invalid pmvpk");
|
|
|
|
// good
|
|
redeem_account.public_key = keys2.pub;
|
|
redeem_account.name = "redeem-test-with-recipient";
|
|
redeem_account.inviting_account = TEST_ACCOUNT;
|
|
redeem_account.invitation = inner;
|
|
redeem_account.pmspk = crypto::null_pkey;
|
|
redeem_account.pmvpk = crypto::null_pkey;
|
|
sign_invitation(redeem_account, NULL, pkey, secret_key, &keys2);
|
|
test_commands(true, setup, redeem_account, "valid with recipient");
|
|
}
|
|
|
|
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;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
setup.push_back(create_account);
|
|
|
|
// deposit
|
|
transfer.cc_account = TEST_ACCOUNT;
|
|
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;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
setup.push_back(create_account);
|
|
|
|
// withdraw
|
|
transfer.cc_account = TEST_ACCOUNT;
|
|
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;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
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;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
setup.push_back(create_account);
|
|
|
|
// transfer to another
|
|
transfer.cc_account = TEST_ACCOUNT;
|
|
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 + 132*COIN;
|
|
create_account.name = "foo";
|
|
create_account.inviting_account = 0;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
setup.push_back(create_account);
|
|
create_account.public_key = keys2.pub;
|
|
create_account.amount = CRYPTONOTE_CC_NEW_ACCOUNT_FEE + 132*COIN;
|
|
create_account.name = "foo 2";
|
|
create_account.inviting_account = 0;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
setup.push_back(create_account);
|
|
|
|
// good
|
|
buy_land.cc_account = TEST_ACCOUNT;
|
|
buy_land.city = 0;
|
|
buy_land.x = 1000040;
|
|
buy_land.y = 1000050;
|
|
buy_land.wm1 = 8;
|
|
buy_land.hm1 = 8;
|
|
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 = TEST_ACCOUNT;
|
|
|
|
// too costly
|
|
buy_land.wm1 = 255;
|
|
test_commands(false, setup, buy_land, "too costly");
|
|
buy_land.wm1 = 8;
|
|
|
|
// overlapping an existing flag
|
|
auto setup2 = setup;
|
|
buy_land2.cc_account = TEST_ACCOUNT + 1;
|
|
buy_land2.city = 0;
|
|
buy_land2.x = 1000020;
|
|
buy_land2.y = 1000052;
|
|
buy_land2.wm1 = 80;
|
|
buy_land2.hm1 = 8;
|
|
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;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
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;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
setup.push_back(create_account);
|
|
cryptonote::cc_command_buy_land_t buy_land;
|
|
buy_land.cc_account = TEST_ACCOUNT;
|
|
buy_land.city = 0;
|
|
buy_land.x = ox + 140;
|
|
buy_land.y = oy + 50;
|
|
buy_land.wm1 = 11;
|
|
buy_land.hm1 = 11;
|
|
setup.push_back(buy_land);
|
|
cryptonote::cc_command_buy_items_t buy_blocks;
|
|
buy_blocks.cc_account = TEST_ACCOUNT;
|
|
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 = TEST_ACCOUNT;
|
|
building_settings.flag = 1 + PREDEFINED_BULDINGS;
|
|
building_settings.role = ROLE_RESIDENTIAL1;
|
|
building_settings.economic_power = 100;
|
|
building_settings.construction_height = 1;
|
|
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 = TEST_ACCOUNT;
|
|
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;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
setup.push_back(create_account);
|
|
buy_blocks.entries.resize(1);
|
|
|
|
// good
|
|
buy_blocks.cc_account = TEST_ACCOUNT;
|
|
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;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
setup.push_back(create_account);
|
|
buy_labour.entries.resize(1);
|
|
|
|
// good
|
|
buy_labour.cc_account = TEST_ACCOUNT;
|
|
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;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
setup.push_back(create_account);
|
|
cryptonote::cc_command_buy_items_t buy_blocks;
|
|
buy_blocks.cc_account = TEST_ACCOUNT;
|
|
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 = TEST_ACCOUNT;
|
|
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;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
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;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
setup.push_back(create_account);
|
|
buy_blocks.cc_account = TEST_ACCOUNT;
|
|
buy_blocks.entries.push_back({ITEM_BASIC_STONE, 1000});
|
|
buy_blocks.entries.push_back({ITEM_BASIC_WOOD, 1440});
|
|
buy_blocks.entries.push_back({ITEM_LABOUR, 8064});
|
|
setup.push_back(buy_blocks);
|
|
buy_blocks.cc_account = TEST_ACCOUNT + 1;
|
|
setup.push_back(buy_blocks);
|
|
buy_land.cc_account = TEST_ACCOUNT;
|
|
buy_land.city = 0;
|
|
buy_land.x = ox + 140;
|
|
buy_land.y = oy + 50;
|
|
buy_land.wm1 = 11;
|
|
buy_land.hm1 = 11;
|
|
setup.push_back(buy_land);
|
|
buy_land.cc_account = TEST_ACCOUNT + 1;
|
|
buy_land.city = 0;
|
|
buy_land.x = ox + 240;
|
|
buy_land.y = oy + 50;
|
|
buy_land.wm1 = 11;
|
|
buy_land.hm1 = 11;
|
|
setup.push_back(buy_land);
|
|
|
|
// good
|
|
building_settings.cc_account = TEST_ACCOUNT;
|
|
building_settings.flag = 1 + PREDEFINED_BULDINGS;
|
|
building_settings.role = ROLE_RESIDENTIAL1;
|
|
building_settings.economic_power = 100;
|
|
building_settings.construction_height = 1;
|
|
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;
|
|
|
|
// invalid construction_height
|
|
building_settings.construction_height = 2;
|
|
test_commands(false, setup, building_settings, "invalid flag");
|
|
building_settings.construction_height = 1;
|
|
|
|
}
|
|
|
|
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;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
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;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
setup.push_back(create_account);
|
|
buy_items.cc_account = TEST_ACCOUNT;
|
|
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, 8064});
|
|
setup.push_back(buy_items);
|
|
buy_land.cc_account = TEST_ACCOUNT;
|
|
buy_land.city = 0;
|
|
buy_land.x = ox + 140;
|
|
buy_land.y = oy + 50;
|
|
buy_land.wm1 = 11;
|
|
buy_land.hm1 = 11;
|
|
setup.push_back(buy_land);
|
|
buy_land.cc_account = TEST_ACCOUNT + 1;
|
|
buy_land.city = 0;
|
|
buy_land.x = ox + 240;
|
|
buy_land.y = oy + 50;
|
|
buy_land.wm1 = 11;
|
|
buy_land.hm1 = 11;
|
|
setup.push_back(buy_land);
|
|
building_settings.cc_account = TEST_ACCOUNT;
|
|
building_settings.flag = 1 + PREDEFINED_BULDINGS;
|
|
building_settings.role = ROLE_RESIDENTIAL1;
|
|
building_settings.economic_power = 100;
|
|
building_settings.construction_height = 1;
|
|
setup.push_back(building_settings);
|
|
|
|
// good
|
|
assign.cc_account = TEST_ACCOUNT;
|
|
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 = TEST_ACCOUNT;
|
|
assign.flag = 700 + PREDEFINED_BULDINGS;
|
|
assign.items[0] = {1, 1};
|
|
test_commands(false, setup, assign, "wrong flag");
|
|
|
|
// 0 amount
|
|
assign.cc_account = TEST_ACCOUNT;
|
|
assign.flag = 1 + PREDEFINED_BULDINGS;
|
|
assign.items[0] = {1, 0};
|
|
test_commands(false, setup, assign, "0 amount");
|
|
|
|
// too many
|
|
assign.cc_account = TEST_ACCOUNT;
|
|
assign.flag = 1 + PREDEFINED_BULDINGS;
|
|
assign.items[0] = {1, 999999999};
|
|
test_commands(false, setup, assign, "too many");
|
|
|
|
// invalid item
|
|
assign.cc_account = TEST_ACCOUNT;
|
|
assign.flag = 1 + PREDEFINED_BULDINGS;
|
|
assign.items[0] = {1111111111, 1};
|
|
test_commands(false, setup, assign, "invalid item");
|
|
|
|
// duplicate item
|
|
assign.cc_account = TEST_ACCOUNT;
|
|
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;
|
|
create_account.pmspk = crypto::null_pkey;
|
|
create_account.pmvpk = crypto::null_pkey;
|
|
setup.push_back(create_account);
|
|
buy_items.cc_account = TEST_ACCOUNT;
|
|
buy_items.entries.push_back({ITEM_BASIC_STONE, 1000});
|
|
buy_items.entries.push_back({ITEM_BASIC_WOOD, 1440});
|
|
buy_items.entries.push_back({ITEM_LABOUR, 8064});
|
|
setup.push_back(buy_items);
|
|
buy_land.cc_account = TEST_ACCOUNT;
|
|
buy_land.city = 0;
|
|
buy_land.x = ox + 140;
|
|
buy_land.y = oy + 150;
|
|
buy_land.wm1 = 11;
|
|
buy_land.hm1 = 11;
|
|
setup.push_back(buy_land);
|
|
building_settings.cc_account = TEST_ACCOUNT;
|
|
building_settings.flag = 1 + PREDEFINED_BULDINGS;
|
|
building_settings.role = ROLE_RESIDENTIAL1;
|
|
building_settings.economic_power = 100;
|
|
building_settings.construction_height = 1;
|
|
setup.push_back(building_settings);
|
|
|
|
// empty
|
|
palette.cc_account = TEST_ACCOUNT;
|
|
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));
|
|
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);
|
|
cryptonote::cc_flag_data_t fd;
|
|
ASSERT_TRUE(db->get_cc_flag_data(1 + PREDEFINED_BULDINGS, fd));
|
|
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);
|
|
cryptonote::cc_flag_data_t fd;
|
|
ASSERT_TRUE(db->get_cc_flag_data(1 + PREDEFINED_BULDINGS, fd));
|
|
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);
|
|
cryptonote::cc_flag_data_t fd;
|
|
ASSERT_TRUE(db->get_cc_flag_data(1 + PREDEFINED_BULDINGS, fd));
|
|
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];
|
|
uint32_t encoded_len;
|
|
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)
|
|
{
|
|
VoxelModel m;
|
|
std::list<VoxelModel> mm;
|
|
std::string s;
|
|
|
|
m.width = m.height = m.depth = 1;
|
|
m.dx = m.dy = m.dz = 0;
|
|
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)
|
|
{
|
|
VoxelModel m;
|
|
std::list<VoxelModel> 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;
|
|
m.dx = m.dy = m.dz = 0;
|
|
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)
|
|
{
|
|
VoxelModel m0, m1, m2;
|
|
std::list<VoxelModel> mm;
|
|
std::string s;
|
|
|
|
m0.width = 12;
|
|
m0.height = 36;
|
|
m0.depth = 19;
|
|
m0.dx = m0.dy = m0.dz = 0;
|
|
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;
|
|
m1.dx = m1.dy = m1.dz = 0;
|
|
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;
|
|
m2.dx = m2.dy = m2.dz = 0;
|
|
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<VoxelModel>::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)14, (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);
|
|
const uint8_t version = bc->get_current_hard_fork_version();
|
|
cryptonote::tx_verification_context tvc{};
|
|
r = cc::check_cc_command(*db, cmd, version, 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, version, 0, events);
|
|
ASSERT_TRUE(r) << label;
|
|
r = cc::revert_cc_command(*db, cmd, version);
|
|
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;
|
|
std::unique_ptr<TestDB> db(new TestDB());
|
|
cryptonote::BlockchainDB *bdb = db.get();
|
|
cc::add_init_state(*bdb);
|
|
cc::game_events_t events;
|
|
cryptonote::cc_command_game_update_t cg = cc::create_cc_game_update_command(*db, BASE_TEST_FORK, events);
|
|
cryptonote::cc_command_game_update_t cg1 = cc::create_cc_game_update_command(*db, BASE_TEST_FORK, events);
|
|
cg.cc_nonce = cg1.cc_nonce = 0;
|
|
ASSERT_EQ(cg, cg1) << "cg: " << cryptonote::obj_to_json_str(cg) << ", cg1: " << cryptonote::obj_to_json_str(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)
|
|
{
|
|
std::unique_ptr<TestDB> db(new TestDB());
|
|
cryptonote::BlockchainDB *bdb = db.get();
|
|
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, BASE_TEST_FORK, 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, "", crypto::null_pkey, crypto::null_pkey);
|
|
uint8_t potential[NUM_POTENTIALS];
|
|
for (int i = 0; i < NUM_POTENTIALS; ++i)
|
|
potential[i] = 100;
|
|
const uint32_t underwater[2] = {0, 0};
|
|
uint32_t flag_id = bdb->allocate_new_cc_flag(NULL, account_id, city_id, 44, 45, 46, 47, potential, 32, 33, 34, 35, 36, underwater);
|
|
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));
|
|
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));
|
|
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));
|
|
ASSERT_EQ(fd.repair, just_enough_repair);
|
|
|
|
cg = cc::create_cc_game_update_command(*db, BASE_TEST_FORK, 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, BASE_TEST_FORK, 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));
|
|
ASSERT_EQ(fd.repair, MIN_DECAY_RATE);
|
|
|
|
cg = cc::create_cc_game_update_command(*db, BASE_TEST_FORK, 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)
|
|
{
|
|
std::unique_ptr<TestDB> db(new TestDB());
|
|
cryptonote::BlockchainDB *bdb = db.get();
|
|
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;
|
|
item_balances[COIN_ITEM_SETTLEMENT] = 10; // coin
|
|
item_balances[COIN_ITEM_SETTLEMENT+1] = 10; // coin
|
|
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), crypto::null_pkey, crypto::null_pkey);
|
|
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);
|
|
const uint32_t underwater[2] = {0, 0};
|
|
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, 34, 35, 36, underwater);
|
|
bdb->set_cc_flag_role(flags[i], ROLE_RESIDENTIAL1, 100);
|
|
bdb->set_cc_flag_repair(flags[i], DEFAULT_REPAIR);
|
|
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, BASE_TEST_FORK, events);
|
|
|
|
ASSERT_EQ(cg.cities.size(), 3);
|
|
ASSERT_EQ(cg.cities[0].city_id, 0);
|
|
ASSERT_EQ(cg.cities[0].subsidy, 447589049972);
|
|
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, 268553429983);
|
|
|
|
uint64_t expected_full_subsidy = bdb->get_block_already_generated_coins(GAME_UPDATE_FREQUENCY - 1) * BLOCK_REWARD_SUBSIDY;
|
|
ASSERT_EQ(expected_full_subsidy, 719741185884);
|
|
uint64_t expected_available_subsidy = expected_full_subsidy - expected_full_subsidy * COIN_COLLECTION_SUBSIDY_PER_THOUSAND / 1000;
|
|
ASSERT_EQ(expected_available_subsidy, 716142479955);
|
|
|
|
bool found[2] = { false, false };
|
|
for (const auto &e: events)
|
|
{
|
|
if (e.account == treasuries[0] && strstr(e.event.c_str(), "Got 4475.89049972 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(), "Got 2685.53429983 subsidy"))
|
|
found[1] = true;
|
|
}
|
|
ASSERT_TRUE(found[0]);
|
|
ASSERT_TRUE(found[1]);
|
|
}
|
|
|
|
TEST(cc_game, generator)
|
|
{
|
|
cryptonote::cc_command_game_update_t cmd;
|
|
std::unique_ptr<TestDB> db(new TestDB());
|
|
cryptonote::BlockchainDB *bdb = db.get();
|
|
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, BASE_TEST_FORK, 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, "", crypto::null_pkey, crypto::null_pkey);
|
|
uint8_t potential[NUM_POTENTIALS];
|
|
for (int i = 0; i < NUM_POTENTIALS; ++i)
|
|
potential[i] = 255;
|
|
const uint32_t underwater[2] = {0, 0};
|
|
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, 34, 35, 36, underwater);
|
|
const uint8_t S = ITEM_BASIC_STONE;
|
|
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_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));
|
|
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, false, 0, 0, 0, 0, monetary_cost, costs, production, 0));
|
|
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_GE(production.size(), 1);
|
|
ASSERT_LE(production.size(), 3);
|
|
uint32_t basic_equivalent = 0;
|
|
for (const auto &e: production)
|
|
{
|
|
ASSERT_GE(e.first, ITEM_FIRST_WOOD);
|
|
ASSERT_LE(e.first, ITEM_LAST_WOOD);
|
|
basic_equivalent += e.second * (e.first == ITEM_BASIC_WOOD ? 1 : e.first == ITEM_MEDIUM_WOOD ? 3 : 9);
|
|
ASSERT_GT(e.second, 0);
|
|
}
|
|
ASSERT_GT(basic_equivalent, 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, BASE_TEST_FORK, 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);
|
|
|
|
const bool expect_medium_wood = false;
|
|
const bool expect_high_wood = false;
|
|
uint32_t item_id = 0xffffffffu, idx = 0;
|
|
ASSERT_EQ(c0.item_balances.size(), 4 + (expect_medium_wood ? 1 : 0) + (expect_high_wood ? 1 : 0));
|
|
ASSERT_EQ(c0.item_balances[idx].delta_account, account_id);
|
|
item_id += c0.item_balances[idx].delta_item + 1;
|
|
ASSERT_EQ(item_id, ITEM_BASIC_STONE);
|
|
ASSERT_LT(c0.item_balances[idx].delta, 0);
|
|
++idx;
|
|
ASSERT_EQ(c0.item_balances[idx].delta_account, 0);
|
|
item_id += c0.item_balances[idx].delta_item + 1;
|
|
ASSERT_EQ(item_id, ITEM_BASIC_WOOD);
|
|
//ASSERT_GT(c0.item_balances[idx].delta, 0); // heating uses up wood now
|
|
++idx;
|
|
if (expect_medium_wood)
|
|
{
|
|
ASSERT_EQ(c0.item_balances[idx].delta_account, 0);
|
|
item_id += c0.item_balances[idx].delta_item + 1;
|
|
ASSERT_EQ(item_id, ITEM_MEDIUM_WOOD);
|
|
ASSERT_GE(c0.item_balances[idx].delta, 0);
|
|
++idx;
|
|
}
|
|
if (expect_high_wood)
|
|
{
|
|
ASSERT_EQ(c0.item_balances[idx].delta_account, 0);
|
|
item_id += c0.item_balances[idx].delta_item + 1;
|
|
ASSERT_EQ(item_id, ITEM_HIGH_WOOD);
|
|
ASSERT_GE(c0.item_balances[idx].delta, 0);
|
|
++idx;
|
|
}
|
|
ASSERT_EQ(c0.item_balances[idx].delta_account, 0);
|
|
item_id += c0.item_balances[idx].delta_item + 1;
|
|
ASSERT_EQ(item_id, ITEM_LABOUR);
|
|
ASSERT_LT(c0.item_balances[idx].delta, 0);
|
|
++idx;
|
|
ASSERT_EQ(c0.item_balances[idx].delta_account, 0);
|
|
item_id += c0.item_balances[idx].delta_item + 1;
|
|
ASSERT_EQ(item_id, ITEM_FOOD_VEGETABLES);
|
|
ASSERT_LT(c0.item_balances[idx].delta, 0);
|
|
++idx;
|
|
}
|
|
|
|
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_upgrade_building_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x27);
|
|
cmd = cryptonote::cc_command_create_script_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x28);
|
|
cmd = cryptonote::cc_command_start_script_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x29);
|
|
cmd = cryptonote::cc_command_script_choice_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x2a);
|
|
cmd = cryptonote::cc_command_set_script_variables_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x2b);
|
|
cmd = cryptonote::cc_command_create_mortgage_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x2c);
|
|
cmd = cryptonote::cc_command_chop_wood_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x2d);
|
|
cmd = cryptonote::cc_command_carve_runestone_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x2e);
|
|
cmd = cryptonote::cc_command_create_auction_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x2f);
|
|
cmd = cryptonote::cc_command_auction_bid_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x30);
|
|
cmd = cryptonote::cc_command_enable_script_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x31);
|
|
cmd = cryptonote::cc_command_allow_styling_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x32);
|
|
cmd = cryptonote::cc_command_update_item_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x33);
|
|
cmd = cryptonote::cc_command_cancel_nonces_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x34);
|
|
cmd = cryptonote::cc_command_allow_settlers_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x35);
|
|
cmd = cryptonote::cc_command_whisper_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x36);
|
|
cmd = cryptonote::cc_command_fish_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x37);
|
|
cmd = cryptonote::cc_command_name_place_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x38);
|
|
cmd = cryptonote::cc_command_new_texture_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x39);
|
|
cmd = cryptonote::cc_command_licence_texture_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x3a);
|
|
cmd = cryptonote::cc_command_create_items_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x3b);
|
|
cmd = cryptonote::cc_command_set_tax_break_zone_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x3c);
|
|
cmd = cryptonote::cc_command_set_merchant_ship_items_t();
|
|
ASSERT_EQ(get_cc_tag<uint8_t>(cmd), 0x3d);
|
|
}
|
|
|
|
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};
|
|
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 F
|
|
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, 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, 0}.data(), 0, 0, cg, events));
|
|
// agricultural buildings do not get some influence from military buildings
|
|
ASSERT_EQ(100, 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, 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, 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, 0}.data(), 0, 0, cg, events));
|
|
ASSERT_EQ(103, 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, 0}.data(), 0, 0, cg, events));
|
|
ASSERT_EQ(107, 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, 0}.data(), 0, 0, cg, events));
|
|
ASSERT_EQ(107, 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, 0}.data(), 0, 0, cg, events));
|
|
ASSERT_EQ(88, 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, 0}.data(), 0, 0, cg, events));
|
|
}
|
|
|
|
TEST(cc, events)
|
|
{
|
|
std::unique_ptr<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.get());
|
|
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}, {}, 0, 1, 0, "building 1"}, {127, cmd, 4, {}, {}, {}, 0, 2, 0, "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, 10, 0, "account 2"},
|
|
{200, cmd, 4, {}, {1}, {}, 0, 11, 0, "account 4, building 1, event 0"},
|
|
{200, cmd, 5, {}, {2}, {}, 0, 12, 0, "account 5, building 2, event 0"},
|
|
{200, cmd, 4, {}, {1}, {}, 0, 13, 0, "account 4, building 1, event 1"},
|
|
{200, cmd, 4, {}, {1}, {}, 0, 14, 0, "account 4, building 1, event 2"},
|
|
{200, cmd, 5, {}, {3}, {}, 0, 15, 0, "account 5, building 3, event 0"},
|
|
{200, cmd, 6, {}, {4}, {}, 0, 16, 0, "account 6, building 4, event 0"},
|
|
{200, cmd, 4, {}, {5}, {}, 0, 17, 0, "account 4, building 5, event 0"},
|
|
{200, cmd, 4, {}, {}, {}, 0, 18, 0, "account 4"},
|
|
{200, cmd, 1, {}, {}, {}, 0, 19, 0, "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}, {}, 0, 90, 0, "building 1"}, {240, cmd, 4, {}, {}, {}, 0, 91, 0, "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 240
|
|
db->remove_cc_events(240);
|
|
ASSERT_TRUE(db->get_cc_events(240, 0, 0, events));
|
|
ASSERT_EQ(events.size(), 0);
|
|
ASSERT_TRUE(db->get_cc_events(200, 0, 0, events));
|
|
ASSERT_EQ(events.size(), 10);
|
|
ASSERT_TRUE(db->get_cc_events(127, 0, 0, events));
|
|
ASSERT_EQ(events.size(), 2);
|
|
|
|
// we can add 240 back
|
|
db->add_cc_events(240, {{240, cmd, 4, {}, {1}, {}, 0, 90, 0, "building 1"}, {240, cmd, 4, {}, {}, {}, 0, 91, 0, "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);
|
|
}
|
|
|
|
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_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)
|
|
{
|
|
static const uint64_t T = GAME_UPDATE_FREQUENCY;
|
|
|
|
ASSERT_EQ(cc::get_accrual_steps(T-1, T-1), 0);
|
|
ASSERT_EQ(cc::get_accrual_steps(T, T), 1);
|
|
ASSERT_EQ(cc::get_accrual_steps(T+1, T+1), 0);
|
|
ASSERT_EQ(cc::get_accrual_steps(T, T + T), 2);
|
|
ASSERT_EQ(cc::get_accrual_steps(T, T + T-1), 1);
|
|
ASSERT_EQ(cc::get_accrual_steps(T, T + T+1), 2);
|
|
ASSERT_EQ(cc::get_accrual_steps(T, T + T * 8), 9);
|
|
ASSERT_EQ(cc::get_accrual_steps(T, T + T * 8 - 1), 8);
|
|
ASSERT_EQ(cc::get_accrual_steps(10, 10), 0);
|
|
ASSERT_EQ(cc::get_accrual_steps(1, T-1), 0);
|
|
ASSERT_EQ(cc::get_accrual_steps(1, T), 1);
|
|
ASSERT_EQ(cc::get_accrual_steps(1, T+1), 1);
|
|
ASSERT_EQ(cc::get_accrual_steps(1, 2*T), 2);
|
|
ASSERT_EQ(cc::get_accrual_steps(T, 2*T), 2);
|
|
ASSERT_EQ(cc::get_accrual_steps(T+1, 2*T), 1);
|
|
ASSERT_EQ(cc::get_accrual_steps(T+1, 2*T-1), 0);
|
|
|
|
static const constexpr uint64_t max_block = 2400;
|
|
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)
|
|
{
|
|
static const uint64_t T = GAME_UPDATE_FREQUENCY;
|
|
|
|
// 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, T, 0, T-1), 1000);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, 1, T, 0, T-1 + T), 1001);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, 1, T, 0, T-1 + T * 2), 1002);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, 1, T, 1001, T-1 + T * 2), 1001);
|
|
|
|
ASSERT_EQ(cc::get_accrued_price(1000, 1, T, 0, T), 1001);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, 1, T, 0, T + T), 1002);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, 1, T, 0, T + T * 2), 1003);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, 1, T, 1002, T + T * 2), 1002);
|
|
|
|
ASSERT_EQ(cc::get_accrued_price(1000, -1, T, 0, T-1), 1000);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, -1, T, 0, T), 999);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, -1, T, 0, T - T), 1000);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, -1, T, 0, T + T), 998);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, -1, T, 0, T + T * 2), 997);
|
|
ASSERT_EQ(cc::get_accrued_price(1000, -1, T, 998, T + T * 2), 998);
|
|
|
|
ASSERT_EQ(cc::get_accrued_price(std::numeric_limits<uint64_t>::max() - 25, 10, T, 0, T-1 + T * 0), std::numeric_limits<uint64_t>::max() - 25);
|
|
ASSERT_EQ(cc::get_accrued_price(std::numeric_limits<uint64_t>::max() - 25, 10, T, 0, T-1 + T * 1), std::numeric_limits<uint64_t>::max() - 15);
|
|
ASSERT_EQ(cc::get_accrued_price(std::numeric_limits<uint64_t>::max() - 25, 10, T, 0, T-1 + T * 2), std::numeric_limits<uint64_t>::max() - 5);
|
|
ASSERT_EQ(cc::get_accrued_price(std::numeric_limits<uint64_t>::max() - 25, 10, T, 0, T-1 + T * 3), std::numeric_limits<uint64_t>::max());
|
|
ASSERT_EQ(cc::get_accrued_price(std::numeric_limits<uint64_t>::max() - 25, 10, T, 0, T-1 + T * 4), std::numeric_limits<uint64_t>::max());
|
|
|
|
ASSERT_EQ(cc::get_accrued_price(25, -10, T, 0, T-1 + T * 0), 25);
|
|
ASSERT_EQ(cc::get_accrued_price(25, -10, T, 0, T-1 + T * 1), 15);
|
|
ASSERT_EQ(cc::get_accrued_price(25, -10, T, 0, T-1 + T * 2), 5);
|
|
ASSERT_EQ(cc::get_accrued_price(25, -10, T, 0, T-1 + T * 3), 1);
|
|
ASSERT_EQ(cc::get_accrued_price(25, -10, T, 0, T-1 + T * 4), 1);
|
|
|
|
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, max32u), max32i);
|
|
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)
|
|
{
|
|
std::unique_ptr<TestDB> db(new TestDB());
|
|
cryptonote::BlockchainDB *bdb = db.get();
|
|
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, {});
|
|
|
|
const uint8_t version = 14;
|
|
|
|
// 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, version));
|
|
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, version));
|
|
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, version));
|
|
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, version));
|
|
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, version));
|
|
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, version));
|
|
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, special_event_history)
|
|
{
|
|
const uint64_t T1 = GAME_UPDATE_FREQUENCY;
|
|
const uint64_t T2 = GAME_UPDATE_FREQUENCY * 2;
|
|
const uint64_t T3 = GAME_UPDATE_FREQUENCY * 3;
|
|
const uint64_t T4 = GAME_UPDATE_FREQUENCY * 4;
|
|
const uint64_t T5 = GAME_UPDATE_FREQUENCY * 5;
|
|
const uint64_t T6 = GAME_UPDATE_FREQUENCY * 6;
|
|
const uint64_t T7 = GAME_UPDATE_FREQUENCY * 7;
|
|
|
|
ASSERT_EQ(cc::get_special_event_at({}, 0), cc::SPECIAL_EVENT_NONE);
|
|
ASSERT_EQ(cc::get_special_event_at({}, 1), cc::SPECIAL_EVENT_NONE);
|
|
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, 0, 0}}, 0), cc::SPECIAL_EVENT_HEAT_WAVE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, 0, 0}}, 1000000), cc::SPECIAL_EVENT_HEAT_WAVE);
|
|
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, 0, 1}}, 0), cc::SPECIAL_EVENT_HEAT_WAVE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, 0, 1}}, T1 - 1), cc::SPECIAL_EVENT_HEAT_WAVE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, 0, 1}}, T1), cc::SPECIAL_EVENT_NONE);
|
|
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, 0, 2}}, 0), cc::SPECIAL_EVENT_HEAT_WAVE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, 0, 2}}, T1), cc::SPECIAL_EVENT_HEAT_WAVE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, 0, 2}}, T2 - 1), cc::SPECIAL_EVENT_HEAT_WAVE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, 0, 2}}, T2), cc::SPECIAL_EVENT_NONE);
|
|
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, T1, 0}}, 0), cc::SPECIAL_EVENT_NONE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, T1, 0}}, T1 - 1), cc::SPECIAL_EVENT_NONE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, T1, 0}}, T1), cc::SPECIAL_EVENT_HEAT_WAVE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, T1, 0}}, 1000000), cc::SPECIAL_EVENT_HEAT_WAVE);
|
|
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, T1, 2}}, 0), cc::SPECIAL_EVENT_NONE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, T1, 2}}, T1 - 1), cc::SPECIAL_EVENT_NONE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, T1, 2}}, T1), cc::SPECIAL_EVENT_HEAT_WAVE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, T1, 2}}, T2), cc::SPECIAL_EVENT_HEAT_WAVE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, T1, 2}}, T3 - 1), cc::SPECIAL_EVENT_HEAT_WAVE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_HEAT_WAVE, T1, 2}}, T3), cc::SPECIAL_EVENT_NONE);
|
|
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_RATS, T1, 2}, {cc::SPECIAL_EVENT_FLOOD, T3, 1}, {cc::SPECIAL_EVENT_FAD, T5, 1}}, 0), cc::SPECIAL_EVENT_NONE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_RATS, T1, 2}, {cc::SPECIAL_EVENT_FLOOD, T3, 1}, {cc::SPECIAL_EVENT_FAD, T5, 1}}, T1 - 1), cc::SPECIAL_EVENT_NONE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_RATS, T1, 2}, {cc::SPECIAL_EVENT_FLOOD, T3, 1}, {cc::SPECIAL_EVENT_FAD, T5, 1}}, T1), cc::SPECIAL_EVENT_RATS);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_RATS, T1, 2}, {cc::SPECIAL_EVENT_FLOOD, T3, 1}, {cc::SPECIAL_EVENT_FAD, T5, 1}}, T2), cc::SPECIAL_EVENT_RATS);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_RATS, T1, 2}, {cc::SPECIAL_EVENT_FLOOD, T3, 1}, {cc::SPECIAL_EVENT_FAD, T5, 1}}, T3 - 1), cc::SPECIAL_EVENT_RATS);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_RATS, T1, 2}, {cc::SPECIAL_EVENT_FLOOD, T3, 1}, {cc::SPECIAL_EVENT_FAD, T5, 1}}, T3), cc::SPECIAL_EVENT_FLOOD);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_RATS, T1, 2}, {cc::SPECIAL_EVENT_FLOOD, T3, 1}, {cc::SPECIAL_EVENT_FAD, T5, 1}}, T4 - 1), cc::SPECIAL_EVENT_FLOOD);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_RATS, T1, 2}, {cc::SPECIAL_EVENT_FLOOD, T3, 1}, {cc::SPECIAL_EVENT_FAD, T5, 1}}, T4), cc::SPECIAL_EVENT_NONE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_RATS, T1, 2}, {cc::SPECIAL_EVENT_FLOOD, T3, 1}, {cc::SPECIAL_EVENT_FAD, T5, 1}}, T5 - 1), cc::SPECIAL_EVENT_NONE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_RATS, T1, 2}, {cc::SPECIAL_EVENT_FLOOD, T3, 1}, {cc::SPECIAL_EVENT_FAD, T5, 1}}, T5), cc::SPECIAL_EVENT_FAD);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_RATS, T1, 2}, {cc::SPECIAL_EVENT_FLOOD, T3, 1}, {cc::SPECIAL_EVENT_FAD, T5, 2}}, T6), cc::SPECIAL_EVENT_FAD);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_RATS, T1, 2}, {cc::SPECIAL_EVENT_FLOOD, T3, 1}, {cc::SPECIAL_EVENT_FAD, T5, 2}}, T7 - 1), cc::SPECIAL_EVENT_FAD);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_RATS, T1, 2}, {cc::SPECIAL_EVENT_FLOOD, T3, 1}, {cc::SPECIAL_EVENT_FAD, T5, 2}}, T7), cc::SPECIAL_EVENT_NONE);
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_RATS, T1, 2}, {cc::SPECIAL_EVENT_FLOOD, T3, 1}, {cc::SPECIAL_EVENT_FAD, T5, 2}}, 100000), cc::SPECIAL_EVENT_NONE);
|
|
|
|
ASSERT_EQ(cc::get_special_event_at({{cc::SPECIAL_EVENT_RATS, T1, 2}, {cc::SPECIAL_EVENT_FLOOD, T3, 1}, {cc::SPECIAL_EVENT_FAD, T5, 0}}, 100000), cc::SPECIAL_EVENT_FAD);
|
|
}
|
|
|
|
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, 0);
|
|
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"));
|
|
cryptonote::keypair recipient_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;
|
|
crypto::secret_key secret_key;
|
|
crypto::signature secret_key_signature;
|
|
crypto::public_key recipient;
|
|
|
|
ASSERT_TRUE(cc::make_invitation(base_keys.sec, 8, 0, 100, recipient_keys.pub, invitation));
|
|
ASSERT_FALSE(invitation.empty());
|
|
ASSERT_TRUE(cc::parse_invitation(invitation, inner, inner_signature, account, amount, expiration, pkey, recipient, secret_key, secret_key_signature));
|
|
ASSERT_EQ(account, 8);
|
|
ASSERT_EQ(amount, 0);
|
|
ASSERT_EQ(expiration, 100);
|
|
ASSERT_EQ(recipient, recipient_keys.pub);
|
|
}
|
|
|
|
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)
|
|
{
|
|
std::shared_ptr<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 = 16920;
|
|
static const uint64_t mid_mar = 100;
|
|
static const uint64_t late_apr = 2400;
|
|
static const uint64_t early_may = 3000;
|
|
static const uint64_t late_may = 4000;
|
|
static const uint64_t late_jun = 5800;
|
|
static const uint64_t mid_jul = 6840;
|
|
static const uint64_t mid_aug = 8600;
|
|
static const uint64_t mid_sep = 10400;
|
|
static const uint64_t mid_oct = 12000;
|
|
static const uint64_t mid_dec = 15400;
|
|
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, 0, 10000, cc::SPECIAL_EVENT_GOOD_HARVEST, true, true, true, true, true, true, true, true, true, true, true, true, true, 0, 0);
|
|
uint32_t low_amount = cc::get_harvest_amount(0, 0, 31, 31, 100, CROP_GRAIN, 32000, 50, 127, 0, 1000000000, cc::SPECIAL_EVENT_NONE, false, false, false, false, false, false, false, false, false, false, false, false, false, 0, 0);
|
|
ASSERT_GE(high_amount, low_amount);
|
|
|
|
uint32_t amount0 = cc::get_harvest_amount(0, 0, 255, 255, 300, CROP_GRAIN, 65536, 100, 0, 0, 10000, cc::SPECIAL_EVENT_GOOD_HARVEST, true, true, true, true, true, true, true, true, true, true, true, true, true, 0, 0);
|
|
uint32_t amount1 = cc::get_harvest_amount(0, 0, 255, 255, 300, CROP_GRAIN, 65536, 100, 0, 255, 10000, cc::SPECIAL_EVENT_GOOD_HARVEST, true, true, true, true, true, true, true, true, true, true, true, true, true, 0, 0);
|
|
ASSERT_EQ(amount0, amount1);
|
|
|
|
uint32_t prev_amount = 0;
|
|
for (int n = 5; n <= 255; n += 5)
|
|
{
|
|
uint32_t amount = cc::get_harvest_amount(0, 0, n, n, 100 + n / 100 / 164, CROP_GRAIN, 65536, 100, n, 0, 10000, cc::SPECIAL_EVENT_GOOD_HARVEST, true, true, true, true, true, true, true, true, true, true, true, true, true, 0, 0);
|
|
ASSERT_GT(amount, prev_amount);
|
|
prev_amount = 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, {}, 724, false, false, 0), CROP_START_YIELD);
|
|
const uint32_t yield = cc::get_crop_yield(CROP_GRAIN, CROP_START_YIELD, std::vector<int32_t>(10000, 1000), 724, false, false, 0);
|
|
ASSERT_LE(yield, 65536);
|
|
ASSERT_GE(yield, 65536 * 9 / 10);
|
|
const uint32_t yield_1 = cc::get_crop_yield(CROP_GRAIN, CROP_START_YIELD, std::vector<int32_t>(10000, 1000), 724, false, false, 1);
|
|
const uint32_t yield_2 = cc::get_crop_yield(CROP_GRAIN, CROP_START_YIELD, std::vector<int32_t>(10000, 1000), 724, false, false, 2);
|
|
const uint32_t yield_3 = cc::get_crop_yield(CROP_GRAIN, CROP_START_YIELD, std::vector<int32_t>(10000, 1000), 724, false, false, 3);
|
|
ASSERT_TRUE(about_equal(yield * .8, yield_1));
|
|
ASSERT_TRUE(about_equal(yield * .8 * .8, yield_2));
|
|
ASSERT_TRUE(about_equal(yield * .8 * .8 * .8, yield_3));
|
|
const uint32_t yield_1_low = cc::get_crop_yield(CROP_GRAIN, CROP_START_YIELD, std::vector<int32_t>(10000, 1000), 0, false, false, 1);
|
|
ASSERT_LT(yield_1_low, yield_1);
|
|
const uint32_t yield_1_high = cc::get_crop_yield(CROP_GRAIN, CROP_START_YIELD, std::vector<int32_t>(10000, 1000), 1024, false, false, 1);
|
|
ASSERT_GT(yield_1_high, yield_1);
|
|
}
|
|
|
|
static void check_mortgage_progression(uint64_t creation_height, uint32_t num_ticks_delay, uint32_t num_tick_payments)
|
|
{
|
|
uint32_t ticks = 0;
|
|
bool matured = false, paid = false;
|
|
cc::mortgage_phase_t last_phase = cc::mortgage_phase_delay;
|
|
const uint64_t maturity_height = cc::get_mortgage_maturity_height(creation_height, num_ticks_delay, num_tick_payments);
|
|
ASSERT_GE(creation_height, 2 * GAME_UPDATE_FREQUENCY + 8);
|
|
for (uint64_t h = creation_height - (2 * GAME_UPDATE_FREQUENCY + 8); h <= creation_height + (num_ticks_delay + num_tick_payments + 2) * GAME_UPDATE_FREQUENCY; ++h)
|
|
{
|
|
const cc::mortgage_phase_t phase = cc::get_mortgage_phase(h, creation_height, num_ticks_delay, num_tick_payments);
|
|
ASSERT_GE(phase, last_phase);
|
|
ASSERT_EQ(phase == cc::mortgage_phase_maturity, h == maturity_height);
|
|
switch (phase)
|
|
{
|
|
case cc::mortgage_phase_delay: break;
|
|
case cc::mortgage_phase_tick: if (cryptonote::is_game_update_block(h)) ++ticks; break;
|
|
case cc::mortgage_phase_maturity: ASSERT_FALSE(matured); ASSERT_EQ(cc::get_mortgage_maturity_height(creation_height, num_ticks_delay, num_tick_payments), h); matured = true; break;
|
|
case cc::mortgage_phase_paid: paid = true; break;
|
|
}
|
|
last_phase = phase;
|
|
}
|
|
ASSERT_EQ(ticks, num_tick_payments);
|
|
ASSERT_TRUE(matured);
|
|
ASSERT_TRUE(paid);
|
|
}
|
|
|
|
TEST(cc, mortgage)
|
|
{
|
|
ASSERT_EQ(cc::get_mortgage_maturity_height(GAME_UPDATE_FREQUENCY - 1, 0, 0), GAME_UPDATE_FREQUENCY);
|
|
ASSERT_EQ(cc::get_mortgage_maturity_height(GAME_UPDATE_FREQUENCY, 0, 0), GAME_UPDATE_FREQUENCY * 2);
|
|
ASSERT_EQ(cc::get_mortgage_maturity_height(GAME_UPDATE_FREQUENCY + 1, 0, 0), GAME_UPDATE_FREQUENCY * 2);
|
|
|
|
ASSERT_EQ(cc::get_mortgage_maturity_height(GAME_UPDATE_FREQUENCY - 1, 1, 0), GAME_UPDATE_FREQUENCY * 2);
|
|
ASSERT_EQ(cc::get_mortgage_maturity_height(GAME_UPDATE_FREQUENCY, 1, 0), GAME_UPDATE_FREQUENCY * 3);
|
|
ASSERT_EQ(cc::get_mortgage_maturity_height(GAME_UPDATE_FREQUENCY + 1, 1, 0), GAME_UPDATE_FREQUENCY * 3);
|
|
|
|
ASSERT_EQ(cc::get_mortgage_maturity_height(GAME_UPDATE_FREQUENCY - 1, 0, 1), GAME_UPDATE_FREQUENCY * 2);
|
|
ASSERT_EQ(cc::get_mortgage_maturity_height(GAME_UPDATE_FREQUENCY, 0, 1), GAME_UPDATE_FREQUENCY * 3);
|
|
ASSERT_EQ(cc::get_mortgage_maturity_height(GAME_UPDATE_FREQUENCY + 1, 0, 1), GAME_UPDATE_FREQUENCY * 3);
|
|
|
|
ASSERT_EQ(cc::get_mortgage_maturity_height(GAME_UPDATE_FREQUENCY - 1, 1, 1), GAME_UPDATE_FREQUENCY * 3);
|
|
ASSERT_EQ(cc::get_mortgage_maturity_height(GAME_UPDATE_FREQUENCY, 1, 1), GAME_UPDATE_FREQUENCY * 4);
|
|
ASSERT_EQ(cc::get_mortgage_maturity_height(GAME_UPDATE_FREQUENCY + 1, 1, 1), GAME_UPDATE_FREQUENCY * 4);
|
|
|
|
ASSERT_EQ(cc::get_mortgage_phase(GAME_UPDATE_FREQUENCY - 1, GAME_UPDATE_FREQUENCY - 1, 0, 0), cc::mortgage_phase_delay);
|
|
ASSERT_EQ(cc::get_mortgage_phase(GAME_UPDATE_FREQUENCY, GAME_UPDATE_FREQUENCY - 1, 0, 0), cc::mortgage_phase_maturity);
|
|
ASSERT_EQ(cc::get_mortgage_phase(GAME_UPDATE_FREQUENCY, GAME_UPDATE_FREQUENCY - 1, 1, 0), cc::mortgage_phase_delay);
|
|
ASSERT_EQ(cc::get_mortgage_phase(GAME_UPDATE_FREQUENCY, GAME_UPDATE_FREQUENCY - 1, 0, 1), cc::mortgage_phase_tick);
|
|
ASSERT_EQ(cc::get_mortgage_phase(GAME_UPDATE_FREQUENCY * 2, GAME_UPDATE_FREQUENCY - 1, 0, 1), cc::mortgage_phase_maturity);
|
|
ASSERT_EQ(cc::get_mortgage_phase(GAME_UPDATE_FREQUENCY * 3, GAME_UPDATE_FREQUENCY - 1, 0, 1), cc::mortgage_phase_paid);
|
|
|
|
ASSERT_EQ(cc::get_mortgage_maturity_height(GAME_UPDATE_FREQUENCY + 1, 2, 0xffffffff), GAME_UPDATE_FREQUENCY * (0xffffffff + (uint64_t)4));
|
|
|
|
for (int dch = -1; dch <= 1; ++dch)
|
|
for (int d = 0; d < 4; ++d)
|
|
for (int t = 0; t < 4; ++t)
|
|
check_mortgage_progression(8 * GAME_UPDATE_FREQUENCY + dch, d, t);
|
|
}
|
|
|
|
TEST(cc, firewood_labour_starts_at_1)
|
|
{
|
|
ASSERT_GT(cc::get_labour_cost_to_chop_wood(1), 0);
|
|
}
|
|
|
|
TEST(cc, auction_end_tune)
|
|
{
|
|
auto make_empty_auction = [](uint64_t h){ cc::auction_t a{}; a.creation_height = h; a.base_ticks = MIN_AUCTION_BASE_TICKS; return a; };
|
|
|
|
static const uint64_t TICK = GAME_UPDATE_FREQUENCY;
|
|
static const uint64_t AUCTION_TIME = MIN_AUCTION_BASE_TICKS * TICK;
|
|
ASSERT_EQ(cc::get_auction_end_time(make_empty_auction(1), 1), std::make_pair(AUCTION_TIME + TICK, false));
|
|
ASSERT_EQ(cc::get_auction_end_time(make_empty_auction(1), AUCTION_TIME), std::make_pair(AUCTION_TIME + TICK, false));
|
|
ASSERT_EQ(cc::get_auction_end_time(make_empty_auction(TICK - 1), AUCTION_TIME + TICK), std::make_pair(AUCTION_TIME + TICK * 2, false));
|
|
ASSERT_EQ(cc::get_auction_end_time(make_empty_auction(TICK), AUCTION_TIME + TICK), std::make_pair(AUCTION_TIME + TICK * 2, false));
|
|
}
|
|
|
|
TEST(cc, land_tax)
|
|
{
|
|
uint64_t land_tax_d0_l0_c0_a256;
|
|
uint64_t land_tax_d0_l0_c0_a1024;
|
|
uint64_t land_tax_d10000_l0_c0_a256;
|
|
uint64_t land_tax_d10000_l0_c0_a1024;
|
|
uint64_t land_tax_d10000_l0_c1_a256;
|
|
uint64_t land_tax_d10000_l0_c1_a1024;
|
|
uint64_t land_tax_d10000_l1_c1_a256;
|
|
uint64_t land_tax_d10000_l1_c1_a1024;
|
|
uint64_t land_tax_d10000_l1_c1_a1024_tax_break_zone;
|
|
|
|
ASSERT_TRUE(cc::get_land_tax(0, 0, 16, 16, 0, 0, 100, 0, 0, 0, 0, land_tax_d0_l0_c0_a256));
|
|
ASSERT_TRUE(cc::get_land_tax(0, 0, 32, 32, 0, 0, 100, 0, 0, 0, 0, land_tax_d0_l0_c0_a1024));
|
|
ASSERT_TRUE(cc::get_land_tax(10000, 0, 10000+16, 16, 0, 0, 100, 0, 0, 0, 0, land_tax_d10000_l0_c0_a256));
|
|
ASSERT_TRUE(cc::get_land_tax(10000, 0, 10000+32, 32, 0, 0, 100, 0, 0, 0, 0, land_tax_d10000_l0_c0_a1024));
|
|
ASSERT_TRUE(cc::get_land_tax(10000, 0, 10000+16, 16, 0, 0, 100, 0, 1, 0, 0, land_tax_d10000_l0_c1_a256));
|
|
ASSERT_TRUE(cc::get_land_tax(10000, 0, 10000+32, 32, 0, 0, 100, 0, 1, 0, 0, land_tax_d10000_l0_c1_a1024));
|
|
ASSERT_TRUE(cc::get_land_tax(10000, 0, 10000+16, 16, 0, 0, 100, 1, 1, 0, 0, land_tax_d10000_l1_c1_a256));
|
|
ASSERT_TRUE(cc::get_land_tax(10000, 0, 10000+32, 32, 0, 0, 100, 1, 1, 0, 0, land_tax_d10000_l1_c1_a1024));
|
|
ASSERT_TRUE(cc::get_land_tax(10000, 0, 10000+32, 32, 0, 0, 100, 1, 1, 5000, 0, land_tax_d10000_l1_c1_a1024_tax_break_zone));
|
|
|
|
ASSERT_GT(land_tax_d0_l0_c0_a1024, land_tax_d0_l0_c0_a256);
|
|
ASSERT_GT(land_tax_d10000_l0_c0_a1024, land_tax_d10000_l0_c0_a256);
|
|
ASSERT_GT(land_tax_d10000_l0_c0_a256, land_tax_d0_l0_c0_a256);
|
|
ASSERT_GT(land_tax_d10000_l0_c0_a1024, land_tax_d0_l0_c0_a1024);
|
|
ASSERT_GT(land_tax_d10000_l0_c0_a256, land_tax_d10000_l0_c1_a256);
|
|
ASSERT_GT(land_tax_d10000_l0_c0_a1024, land_tax_d10000_l0_c1_a1024);
|
|
ASSERT_GT(land_tax_d10000_l1_c1_a1024, land_tax_d10000_l1_c1_a256);
|
|
ASSERT_GT(land_tax_d10000_l1_c1_a1024, land_tax_d10000_l1_c1_a1024_tax_break_zone);
|
|
}
|
|
|
|
TEST(cc, discovery_prerequisites)
|
|
{
|
|
for (uint32_t d = 0; d < NUM_DISCOVERIES; ++d)
|
|
{
|
|
const char *name, *icon, *desc, *unlock_flag;
|
|
std::vector<uint32_t> prerequisites;
|
|
uint64_t difficulty, patent_time;
|
|
uint32_t item;
|
|
ASSERT_TRUE(cc::get_discovery_data(d, name, icon, desc, prerequisites, difficulty, item, patent_time, unlock_flag));
|
|
ASSERT_TRUE(name != NULL);
|
|
ASSERT_TRUE(icon != NULL);
|
|
ASSERT_TRUE(desc != NULL);
|
|
ASSERT_TRUE(difficulty > 0);
|
|
ASSERT_TRUE(item == 0 || item < NUM_ITEMS);
|
|
for (uint32_t p: prerequisites)
|
|
{
|
|
ASSERT_TRUE(p < NUM_DISCOVERIES);
|
|
}
|
|
}
|
|
}
|
|
|
|
TEST(cc, build_height)
|
|
{
|
|
ASSERT_GE(cc::get_max_build_height_for_level(0), 40);
|
|
for (uint32_t i = 1; i < 200; ++i)
|
|
{
|
|
ASSERT_GE(cc::get_max_build_height_for_level(i), cc::get_max_build_height_for_level(i - 1));
|
|
}
|
|
ASSERT_LE(cc::get_max_build_height_for_level(200), 65535);
|
|
ASSERT_LE(cc::get_max_build_height_for_level(9999999), 65535);
|
|
ASSERT_GE(cc::get_max_build_height_for_level(50), 1500);
|
|
}
|
|
|
|
static bool about_equal(fixed_t x, fixed_t y)
|
|
{
|
|
return std::abs(x.get_float() - y.get_float()) < 0.01f;
|
|
}
|
|
|
|
TEST(cc, fixed_mul)
|
|
{
|
|
const fixed_t two = fixed_t(2);
|
|
const fixed_t m_two = fixed_t(-2);
|
|
const fixed_t four = fixed_t(4);
|
|
const fixed_t m_four = fixed_t(-4);
|
|
|
|
const fixed_t half = fixed_t(1) / fixed_t(2);
|
|
const fixed_t m_half = fixed_t(-1) / fixed_t(2);
|
|
const fixed_t quarter = fixed_t(1) / fixed_t(4);
|
|
const fixed_t m_quarter = fixed_t(-1) / fixed_t(4);
|
|
|
|
ASSERT_TRUE(about_equal(two * two, four));
|
|
ASSERT_TRUE(about_equal(m_two * two, m_four));
|
|
ASSERT_TRUE(about_equal(two * m_two, m_four));
|
|
ASSERT_TRUE(about_equal(m_two * m_two, four));
|
|
|
|
ASSERT_TRUE(about_equal(half * half, quarter));
|
|
ASSERT_TRUE(about_equal(m_half * half, m_quarter));
|
|
ASSERT_TRUE(about_equal(half * m_half, m_quarter));
|
|
ASSERT_TRUE(about_equal(m_half * m_half, quarter));
|
|
}
|
|
|
|
TEST(cc, fixed_sin_cos)
|
|
{
|
|
fixed_t s, c;
|
|
|
|
ASSERT_TRUE(about_equal(fixed_t(0).sin(), fixed_t(0)));
|
|
ASSERT_TRUE(about_equal(fixed_t::PI.sin(), fixed_t(0)));
|
|
ASSERT_TRUE(about_equal((fixed_t::PI / fixed_t(2)).sin(), fixed_t(1)));
|
|
ASSERT_TRUE(about_equal((fixed_t::PI * fixed_t(3) / fixed_t(2)).sin(), fixed_t(-1)));
|
|
ASSERT_TRUE(about_equal((fixed_t(0) - fixed_t::PI / fixed_t(2)).sin(), fixed_t(-1)));
|
|
|
|
ASSERT_TRUE(about_equal(fixed_t(0).cos(), fixed_t(1)));
|
|
ASSERT_TRUE(about_equal(fixed_t::PI.cos(), fixed_t(-1)));
|
|
ASSERT_TRUE(about_equal((fixed_t::PI / fixed_t(2)).cos(), fixed_t(0)));
|
|
ASSERT_TRUE(about_equal((fixed_t::PI * fixed_t(3) / fixed_t(2)).cos(), fixed_t(0)));
|
|
|
|
for (int i = 5; i >= -5; --i)
|
|
{
|
|
fixed_t offset = fixed_t(2) * fixed_t::PI * fixed_t(i);
|
|
|
|
fixed_t(offset).sincos(s, c);
|
|
ASSERT_TRUE(about_equal(s, fixed_t(0)));
|
|
ASSERT_TRUE(about_equal(c, fixed_t(1)));
|
|
|
|
(fixed_t::PI + offset).sincos(s, c);
|
|
ASSERT_TRUE(about_equal(s, fixed_t(0)));
|
|
ASSERT_TRUE(about_equal(c, fixed_t(-1)));
|
|
|
|
(fixed_t::PI / fixed_t(2) + offset).sincos(s, c);
|
|
ASSERT_TRUE(about_equal(s, fixed_t(1)));
|
|
ASSERT_TRUE(about_equal(c, fixed_t(0)));
|
|
}
|
|
}
|
|
|
|
TEST(cc, get_underwater_efficiency_percent)
|
|
{
|
|
for (uint32_t role = 1; role < NUM_ROLES; ++role)
|
|
{
|
|
ASSERT_EQ(cc::get_underwater_efficiency_percent(role, 1000, 1000), role == ROLE_ROAD ? 100 : 0);
|
|
ASSERT_EQ(cc::get_underwater_efficiency_percent(role, 1000, 0), role == ROLE_FISHERY ? 0 : 100);
|
|
}
|
|
// RES2 and RES1 have a small bonus for small amounts of underwater area
|
|
for (int i = 0; i < 2; ++i)
|
|
{
|
|
const uint32_t eff_10 = cc::get_underwater_efficiency_percent(i == 0 ? ROLE_RESIDENTIAL2 : ROLE_RESIDENTIAL3, 1000, 10);
|
|
ASSERT_GT(eff_10, 100);
|
|
ASSERT_LT(eff_10, 200);
|
|
const uint32_t eff_50 = cc::get_underwater_efficiency_percent(i == 0 ? ROLE_RESIDENTIAL2 : ROLE_RESIDENTIAL3, 1000, 500);
|
|
ASSERT_LT(eff_50, 100);
|
|
}
|
|
|
|
// fisheries are best at half underwater
|
|
ASSERT_EQ(cc::get_underwater_efficiency_percent(ROLE_FISHERY, 1000, 0), 0);
|
|
ASSERT_EQ(cc::get_underwater_efficiency_percent(ROLE_FISHERY, 1000, 100), 0);
|
|
ASSERT_EQ(cc::get_underwater_efficiency_percent(ROLE_FISHERY, 1000, 350), 100);
|
|
ASSERT_EQ(cc::get_underwater_efficiency_percent(ROLE_FISHERY, 1000, 500), 100);
|
|
ASSERT_EQ(cc::get_underwater_efficiency_percent(ROLE_FISHERY, 1000, 650), 100);
|
|
ASSERT_EQ(cc::get_underwater_efficiency_percent(ROLE_FISHERY, 1000, 900), 0);
|
|
ASSERT_EQ(cc::get_underwater_efficiency_percent(ROLE_FISHERY, 1000, 1000), 0);
|
|
uint32_t eff = cc::get_underwater_efficiency_percent(ROLE_FISHERY, 1000, 200);
|
|
ASSERT_GT(eff, 50);
|
|
ASSERT_LT(eff, 85);
|
|
eff = cc::get_underwater_efficiency_percent(ROLE_FISHERY, 1000, 800);
|
|
ASSERT_GT(eff, 50);
|
|
ASSERT_LT(eff, 85);
|
|
}
|
|
|
|
TEST(cc, fish_season)
|
|
{
|
|
const size_t n_fish = cc::get_num_collectible_fish();
|
|
for (size_t fish = 0; fish < n_fish; ++fish)
|
|
{
|
|
const cc::cc_collectible_fish_data_t *data = cc::get_collectible_fish_data(fish);
|
|
ASSERT_TRUE(data);
|
|
ASSERT_TRUE(data->name);
|
|
ASSERT_TRUE(data->image);
|
|
ASSERT_TRUE(data->from_month <= 11);
|
|
ASSERT_TRUE(data->to_month <= 11);
|
|
ASSERT_TRUE(data->rarity <= 1000);
|
|
|
|
int present[12];
|
|
memset(present, 0, sizeof(present));
|
|
const uint32_t first_month = data->from_month;
|
|
const uint32_t last_month = data->to_month + (data->from_month <= data->to_month ? 0 : 12);
|
|
for (uint32_t month = first_month; month <= last_month; ++month)
|
|
present[month % 12] = 1;
|
|
for (uint32_t month = 0; month < 12; ++month)
|
|
{
|
|
ASSERT_EQ(present[month], cc::is_time_of_year_for_fish(fish, month));
|
|
if (data->local >= 0 || !present[month])
|
|
continue;
|
|
bool found = false;
|
|
for (int d = 0; d <= 2; ++d)
|
|
{
|
|
bool ret = cc::for_all_fish(month, d, -1, [fish, month, &found](uint32_t f, const cc::cc_collectible_fish_data_t &data) {
|
|
if (!(cc::is_time_of_year_for_fish(f, month)))
|
|
return false;
|
|
if (f == fish)
|
|
found = true;
|
|
return true;
|
|
});
|
|
ASSERT_TRUE(ret);
|
|
}
|
|
ASSERT_TRUE(found);
|
|
}
|
|
}
|
|
}
|