文件 1 的 1:Main.sol
pragma solidity ^0.4.24;
library MathLib {
function add(uint256 a, uint256 b) internal pure returns (uint256 c) {
c = a + b;
assert(c >= a);
}
function sub(uint256 a, uint256 b) internal pure returns (uint256 c) {
assert(b <= a);
c = a - b;
}
function mul(uint256 a, uint256 b) internal pure returns (uint256 c) {
if (a == 0 || b == 0) {
c = 0;
} else {
c = a * b;
assert(c / a == b);
}
}
function div(uint256 a, uint256 b) internal pure returns (uint256 c) {
c = a / b;
}
}
interface IMultiOwnable {
function owners() external view returns (address[]);
function transferOwnership(address newOwner) external;
function appointHeir(address heir) external;
function succeedOwner(address owner) external;
event OwnershipTransfer(address indexed owner, address indexed newOwner);
event HeirAppointment(address indexed owner, address indexed heir);
event OwnershipSuccession(address indexed owner, address indexed heir);
}
library AddressLib {
using AddressLib for AddressLib.Set;
function isEmpty(address value) internal pure returns (bool) {
return value == address(0);
}
function isSender(address value) internal view returns (bool) {
return value == msg.sender;
}
struct Set {
address[] vals;
mapping(address => uint256) seqs;
}
function values(Set storage set) internal view returns (address[]) {
return set.vals;
}
function count(Set storage set) internal view returns (uint256) {
return set.vals.length;
}
function first(Set storage set) internal view returns (address) {
require(set.count() > 0, "Set cannot be empty");
return set.vals[0];
}
function last(Set storage set) internal view returns (address) {
require(set.count() > 0, "Set cannot be empty");
return set.vals[set.vals.length - 1];
}
function contains(Set storage set, address value) internal view returns (bool) {
return set.seqs[value] > 0;
}
function add(Set storage set, address value) internal {
if (!set.contains(value)) {
set.seqs[value] = set.vals.push(value);
}
}
function remove(Set storage set, address value) internal {
if (set.contains(value)) {
uint256 seq = set.seqs[value];
if (seq < set.count()) {
address lastVal = set.last();
set.vals[seq - 1] = lastVal;
set.seqs[lastVal] = seq;
}
set.vals.length--;
set.seqs[value] = 0;
}
}
}
contract MultiOwnable is IMultiOwnable {
using AddressLib for address;
using AddressLib for AddressLib.Set;
AddressLib.Set private _owners;
mapping(address => address) private _heirs;
modifier onlyOwner {
require(_owners.contains(msg.sender), "Only allowed for a owner");
_;
}
constructor (address[] owners) internal {
for (uint256 i = 0; i < owners.length; i++) {
_owners.add(owners[i]);
}
}
function owners() external view returns (address[]) {
return _owners.values();
}
function transferOwnership(address newOwner) external onlyOwner {
_transferOwnership(msg.sender, newOwner);
emit OwnershipTransfer(msg.sender, newOwner);
}
function appointHeir(address heir) external onlyOwner {
_heirs[msg.sender] = heir;
emit HeirAppointment(msg.sender, heir);
}
function succeedOwner(address owner) external {
require(_heirs[owner].isSender(), "Only heir may succeed owner");
_transferOwnership(owner, msg.sender);
emit OwnershipSuccession(owner, msg.sender);
}
function _transferOwnership(address owner, address newOwner) private {
_owners.remove(owner);
_owners.add(newOwner);
_heirs[owner] = address(0);
}
}
contract Geo {
enum Class { District, Zone, Target }
enum Status { Locked, Unlocked, Owned }
struct Area {
Class class;
Status status;
uint256 parent;
uint256[] siblings;
uint256[] children;
address owner;
uint256 cost;
uint256 unlockTime;
}
mapping(uint256 => Area) internal areas;
constructor () internal { }
function initAreas() internal {
areas[0].class = Class.Target;
areas[1].class = Class.District;
areas[1].parent = 46;
areas[1].siblings = [2,3];
areas[2].class = Class.District;
areas[2].parent = 46;
areas[2].siblings = [1,3];
areas[3].class = Class.District;
areas[3].parent = 46;
areas[3].siblings = [1,2,4,6,8,9,11,13];
areas[4].class = Class.District;
areas[4].parent = 46;
areas[4].siblings = [3,5,6,9];
areas[5].class = Class.District;
areas[5].parent = 46;
areas[5].siblings = [4,6,7,9,37,38,39,41];
areas[6].class = Class.District;
areas[6].parent = 46;
areas[6].siblings = [3,4,5,7,13,22];
areas[7].class = Class.District;
areas[7].parent = 46;
areas[7].siblings = [5,6,21,22,26,38];
areas[8].class = Class.District;
areas[8].parent = 46;
areas[9].class = Class.District;
areas[9].parent = 47;
areas[9].siblings = [3,4,5,10,11,12,39,41];
areas[10].class = Class.District;
areas[10].parent = 47;
areas[10].siblings = [9,11,12];
areas[11].class = Class.District;
areas[11].parent = 47;
areas[11].siblings = [3,9,10,14];
areas[12].class = Class.District;
areas[12].parent = 47;
areas[12].siblings = [9,10];
areas[13].class = Class.District;
areas[13].parent = 47;
areas[13].siblings = [3,6,15,16,17,22];
areas[14].class = Class.District;
areas[14].parent = 47;
areas[15].class = Class.District;
areas[15].parent = 47;
areas[16].class = Class.District;
areas[16].parent = 47;
areas[17].class = Class.District;
areas[17].parent = 48;
areas[17].siblings = [13,18,19,22,23];
areas[18].class = Class.District;
areas[18].parent = 48;
areas[18].siblings = [17,19];
areas[19].class = Class.District;
areas[19].parent = 48;
areas[19].siblings = [17,18,20,21,22,25];
areas[20].class = Class.District;
areas[20].parent = 48;
areas[20].siblings = [19,21,24,27];
areas[21].class = Class.District;
areas[21].parent = 48;
areas[21].siblings = [7,19,20,22,26,27];
areas[22].class = Class.District;
areas[22].parent = 48;
areas[22].siblings = [6,7,13,17,19,21];
areas[23].class = Class.District;
areas[23].parent = 48;
areas[24].class = Class.District;
areas[24].parent = 48;
areas[25].class = Class.District;
areas[25].parent = 48;
areas[26].class = Class.District;
areas[26].parent = 49;
areas[26].siblings = [7,21,27,28,31,38];
areas[27].class = Class.District;
areas[27].parent = 49;
areas[27].siblings = [20,21,26,28,29,32,33,34,36];
areas[28].class = Class.District;
areas[28].parent = 49;
areas[28].siblings = [26,27,30,31,35];
areas[29].class = Class.District;
areas[29].parent = 49;
areas[29].siblings = [27];
areas[30].class = Class.District;
areas[30].parent = 49;
areas[30].siblings = [28,31,37,42];
areas[31].class = Class.District;
areas[31].parent = 49;
areas[31].siblings = [26,28,30,37,38];
areas[32].class = Class.District;
areas[32].parent = 49;
areas[32].siblings = [27];
areas[33].class = Class.District;
areas[33].parent = 49;
areas[33].siblings = [27];
areas[34].class = Class.District;
areas[34].parent = 49;
areas[35].class = Class.District;
areas[35].parent = 49;
areas[36].class = Class.District;
areas[36].parent = 49;
areas[37].class = Class.District;
areas[37].parent = 50;
areas[37].siblings = [5,30,31,38,39,40,42,45];
areas[38].class = Class.District;
areas[38].parent = 50;
areas[38].siblings = [5,7,26,31,37];
areas[39].class = Class.District;
areas[39].parent = 50;
areas[39].siblings = [5,9,37,40,41,43,44];
areas[40].class = Class.District;
areas[40].parent = 50;
areas[40].siblings = [37,39,42,43];
areas[41].class = Class.District;
areas[41].parent = 50;
areas[41].siblings = [5,9,39];
areas[42].class = Class.District;
areas[42].parent = 50;
areas[42].siblings = [30,37,40,43];
areas[43].class = Class.District;
areas[43].parent = 50;
areas[43].siblings = [39,40,42];
areas[44].class = Class.District;
areas[44].parent = 50;
areas[45].class = Class.District;
areas[45].parent = 50;
areas[46].class = Class.Zone;
areas[46].children = [1,2,3,4,5,6,7,8];
areas[47].class = Class.Zone;
areas[47].children = [9,10,11,12,13,14,15,16];
areas[48].class = Class.Zone;
areas[48].children = [17,18,19,20,21,22,23,24,25];
areas[49].class = Class.Zone;
areas[49].children = [26,27,28,29,30,31,32,33,34,35,36];
areas[50].class = Class.Zone;
areas[50].children = [37,38,39,40,41,42,43,44,45];
}
}
contract Configs {
address[] internal GAME_MASTER_ADDRESSES = [
0x33e03f9F3eFe593D10327245f8107eAaD09730B7,
0xbcec8fc952776F4F83829837881092596C29A666,
0x4Eb1E2B89Aba03b7383F07cC80003937b7814B54,
address(0),
address(0)
];
address internal constant ROYALTY_ADDRESS = 0x8C1A581a19A08Ddb1dB271c82da20D88977670A8;
uint256 internal constant AREA_COUNT = 51;
uint256 internal constant TARGET_AREA = 0;
uint256 internal constant SOURCE_AREA = 1;
uint256 internal constant ZONE_START = 46;
uint256 internal constant ZONE_COUNT = 5;
uint256[][] internal UNLOCKED_CONFIGS = [
[uint256(16 * 10**15), 0, 0, 0, 5, 4],
[uint256(32 * 10**15), 0, 0, 0, 4, 3],
[uint256(128 * 10**15), 0, 0, 0, 3, 2]
];
uint256[][] internal OWNED_CONFIGS = [
[uint256(90), 2, 3, 5, 4],
[uint256(80), 0, 5, 4, 3],
[uint256(99), 0, 1, 3, 2]
];
uint256 internal constant DISTRICT_UNLOCK_TIME = 1 minutes;
uint256 internal constant ZONE_UNLOCK_TIME = 3 minutes;
uint256 internal constant TARGET_UNLOCK_TIME = 10 minutes;
uint256 internal constant END_TIME_COUNTDOWN = 6 hours;
uint256 internal constant DISTRICT_END_TIME_EXTENSION = 30 seconds;
uint256 internal constant ZONE_END_TIME_EXTENSION = 1 minutes;
uint256 internal constant TARGET_END_TIME_EXTENSION = 3 minutes;
uint256 internal constant LAST_OWNER_SHARE = 55;
uint256 internal constant TARGET_OWNER_SHARE = 30;
uint256 internal constant SOURCE_OWNER_SHARE = 5;
uint256 internal constant ZONE_OWNERS_SHARE = 10;
}
contract Main is Configs, Geo, MultiOwnable {
using MathLib for uint256;
uint256 private endTime;
uint256 private countdown;
address private lastOwner;
event Settings(uint256 lastOwnerShare, uint256 targetOwnerShare, uint256 sourceOwnerShare, uint256 zoneOwnersShare);
event Summary(uint256 currentTime, uint256 endTime, uint256 prize, address lastOwner);
event Reset();
event Start();
event Finish();
event Unlock(address indexed player, uint256 indexed areaId, uint256 unlockTime);
event Acquisition(address indexed player, uint256 indexed areaId, uint256 price, uint256 newPrice);
event Post(address indexed player, uint256 indexed areaId, string message);
event Dub(address indexed player, string nickname);
modifier onlyHuman {
uint256 codeSize;
address sender = msg.sender;
assembly { codeSize := extcodesize(sender) }
require(sender == tx.origin, "Sorry, human only");
require(codeSize == 0, "Sorry, human only");
_;
}
constructor () public MultiOwnable(GAME_MASTER_ADDRESSES) { }
function init() external onlyOwner {
require(countdown == 0 && endTime == 0, "Game has already been initialized");
initAreas();
reset();
emit Settings(LAST_OWNER_SHARE, TARGET_OWNER_SHARE, SOURCE_OWNER_SHARE, ZONE_OWNERS_SHARE);
}
function start() external onlyOwner {
require(areas[SOURCE_AREA].status == Status.Locked, "Game has already started");
areas[SOURCE_AREA].status = Status.Unlocked;
emit Start();
}
function finish() external onlyOwner {
require(endTime > 0 && now >= endTime, "Cannot end yet");
uint256 unitValue = address(this).balance.div(100);
uint256 zoneValue = unitValue.mul(ZONE_OWNERS_SHARE).div(ZONE_COUNT);
for (uint256 i = 0; i < ZONE_COUNT; i++) {
areas[ZONE_START.add(i)].owner.transfer(zoneValue);
}
lastOwner.transfer(unitValue.mul(LAST_OWNER_SHARE));
areas[TARGET_AREA].owner.transfer(unitValue.mul(TARGET_OWNER_SHARE));
areas[SOURCE_AREA].owner.transfer(unitValue.mul(SOURCE_OWNER_SHARE));
emit Finish();
for (i = 0; i < AREA_COUNT; i++) {
delete areas[i].cost;
delete areas[i].owner;
delete areas[i].status;
delete areas[i].unlockTime;
}
reset();
}
function acquire(uint256 areaId) external payable onlyHuman {
require(endTime == 0 || now < endTime, "Game has ended");
Area storage area = areas[areaId];
if (area.status == Status.Unlocked) {
area.cost = getInitialCost(area);
}
require(area.status != Status.Locked, "Cannot acquire locked area");
require(area.unlockTime <= now, "Cannot acquire yet");
require(area.owner != msg.sender, "Cannot acquire already owned area");
require(area.cost == msg.value, "Incorrect value for acquiring this area");
uint256 unitValue = msg.value.div(100);
uint256 ownerShare;
uint256 parentShare;
uint256 devShare;
uint256 inflationNum;
uint256 inflationDenom;
(ownerShare, parentShare, devShare, inflationNum, inflationDenom) = getConfigs(area);
if (ownerShare > 0) {
area.owner.transfer(unitValue.mul(ownerShare));
}
if (parentShare > 0 && areas[area.parent].status == Status.Owned) {
areas[area.parent].owner.transfer(unitValue.mul(parentShare));
}
if (devShare > 0) {
ROYALTY_ADDRESS.transfer(unitValue.mul(devShare));
}
area.cost = area.cost.mul(inflationNum).div(inflationDenom);
area.owner = msg.sender;
if (area.class != Class.Target) {
lastOwner = msg.sender;
}
emit Acquisition(msg.sender, areaId, msg.value, area.cost);
if (area.status == Status.Unlocked) {
area.status = Status.Owned;
countdown = countdown.sub(1);
if (area.class == Class.District) {
tryUnlockSiblings(area);
tryUnlockParent(area);
} else if (area.class == Class.Zone) {
tryUnlockTarget();
} else if (area.class == Class.Target) {
endTime = now.add(END_TIME_COUNTDOWN);
}
} else if (area.status == Status.Owned) {
if (endTime > 0) {
if (area.class == Class.District) {
endTime = endTime.add(DISTRICT_END_TIME_EXTENSION);
} else if (area.class == Class.Zone) {
endTime = endTime.add(ZONE_END_TIME_EXTENSION);
} else if (area.class == Class.Target) {
endTime = endTime.add(TARGET_END_TIME_EXTENSION);
}
}
if (endTime > now.add(END_TIME_COUNTDOWN)) {
endTime = now.add(END_TIME_COUNTDOWN);
}
}
emit Summary(now, endTime, address(this).balance, lastOwner);
}
function post(uint256 areaId, string message) external onlyHuman {
require(areas[areaId].owner == msg.sender, "Cannot post message on other's area");
emit Post(msg.sender, areaId, message);
}
function dub(string nickname) external onlyHuman {
emit Dub(msg.sender, nickname);
}
function reset() private {
delete endTime;
countdown = AREA_COUNT;
delete lastOwner;
emit Reset();
}
function tryUnlockSiblings(Area storage area) private {
for (uint256 i = 0; i < area.siblings.length; i++) {
Area storage sibling = areas[area.siblings[i]];
if (sibling.status == Status.Locked) {
sibling.status = Status.Unlocked;
sibling.unlockTime = now.add(DISTRICT_UNLOCK_TIME);
emit Unlock(msg.sender, area.siblings[i], sibling.unlockTime);
}
}
}
function tryUnlockParent(Area storage area) private {
Area storage parent = areas[area.parent];
for (uint256 i = 0; i < parent.children.length; i++) {
Area storage child = areas[parent.children[i]];
if (child.status != Status.Owned) {
return;
}
}
parent.status = Status.Unlocked;
parent.unlockTime = now.add(ZONE_UNLOCK_TIME);
emit Unlock(msg.sender, area.parent, parent.unlockTime);
}
function tryUnlockTarget() private {
if (countdown == 1) {
areas[TARGET_AREA].status = Status.Unlocked;
areas[TARGET_AREA].unlockTime = now.add(TARGET_UNLOCK_TIME);
emit Unlock(msg.sender, TARGET_AREA, areas[TARGET_AREA].unlockTime);
}
}
function getInitialCost(Area storage area) private view returns (uint256) {
return UNLOCKED_CONFIGS[uint256(area.class)][0];
}
function getConfigs(Area storage area) private view returns (uint256, uint256, uint256, uint256, uint256) {
uint256 index = uint256(area.class);
if (area.status == Status.Unlocked) {
return (UNLOCKED_CONFIGS[index][1], UNLOCKED_CONFIGS[index][2], UNLOCKED_CONFIGS[index][3], UNLOCKED_CONFIGS[index][4], UNLOCKED_CONFIGS[index][5]);
} else if (area.status == Status.Owned) {
return (OWNED_CONFIGS[index][0], OWNED_CONFIGS[index][1], OWNED_CONFIGS[index][2], OWNED_CONFIGS[index][3], OWNED_CONFIGS[index][4]);
}
}
}