From discussions in slack I came to the conclusion that contracts are most useful to check for conditions instead of executing code that queues intents.
This way contracts require an implementation from the person fulfilling the contract, which not only allows the contractor to solve it in their own way and optimize it for stuff like CPU, they can be seen as some kind of quest or mission.
I completely understand the need for manual review, but even though I'm repeating myself, limiting it to manual interaction only is a mistake in my opinion.
When going for parameterized contracts you could easily create a checksum of the module and require to review the module at least once, after that your code is free to accept any contracts with the same checksum.
Additionally your code can verify whether it actually wants to accept a contract based on those parameters.
In the long term it would be nice being able to accept a contract without manual review, either by a well established list of tested contracts or by adding the exact same contract module to your own code.
Example contract
Following is a scenario of how I expect the process to look like when working with contracts.
Player A: contractee
Player B: contractor
- The contractee create a contract module, in this case it's a module that requires a nuke to be sent by the contractor to a specific position:
const { x, y, roomName } = Game.contract.parameters;
const pos = new RoomPosition(x, y, room);
const hasVision = roomName => roomName in Game.rooms;
const isMyRoom = roomName => _.get(Game.rooms, [roomName, 'controller', 'my'], false);
module.exports.loop = function() {
if (!hasVision(roomName)) {
return false;
}
const nukes = pos.lookFor(LOOK_NUKE);
if (!nukes) {
return false;
}
return nukes.some(({ launchRoomName }) => isMyRoom(launchRoomName));
}
- The contractee creates a contract based on this module:
function requestNuke(pos, reward) {
const { x, y, roomName } = pos;
return Game.contracts.create('contract-nuke.js', reward, { x, y, roomName });
}
requestNuke(new RoomPosition(25, 25, 'N0E0'), 100000);
-
The potential contractor ( Player B ) views all of the available contracts in the UI, reviews the contract's code and approves it, the checksum is then added to their list of allowed contracts.
-
Player B's code views a list of available contracts and chooses appropriately, afterwards every accepted contract is getting executed (contracts won't be accepted until the next tick). If the contract hasn't been reviewed yet an appropriate error code will get returned.
const CHECKSUM = '1b1774169003e863931fa3c5ddbfa7f1';
const MINIMUM_REWARD = 80000;
const myRoom = room => _.get(room, ['controller', 'my'], false);
const getNuker = room => room.find(FIND_STRUCTURES, { filter: { structureType: STRUCTURE_NUKER }});
const hasNuker = room => !!getNuker(room);
const inNukeRange = (room, target) => Game.map.getRoomLinearDistance(room.name, target) <= NUKE_RANGE;
function getFirstNukerInRange(targetRoom) {
return _.first(_.values(Game.rooms), room => myRoom(room) && hasNuker(room) && inNukeRange(room, targetRoom));
}
const isNukeContract = contract => contract.checksum === CHECKSUM;
const isNukeContractValuable = contract => contract.reward >= MINIMUM_REWARD;
const isNukeContractInRange = contract => !!getFirstNukerInRange(contract.parameters.roomName);
function acceptNukeContracts(contract) {
if (!isNukeContract(contract)) {
return;
}
if (!isNukeContractValuable(contract)) {
return;
}
if (!isNukeContractInRange(contract)) {
return;
}
Game.contracts.accept(contract.id);
}
function executeNukeContracts(contract) {
if (!isNukeContract(contract)) {
return;
}
const { x, y, roomName } = contract.parameters;
const nuker = getFirstNukerInRange(roomName);
nuker.launchNuke(new RoomPosition(x, y, roomName));
}
module.exports.loop = function loop() {
_.forEach(Game.contracts.available, acceptNukeContracts);
_.forEach(Game.contracts.accepted, executeNukeContracts);
}
This is a fairly simple implementation (but devastating none the less!) but I hope this example shows how automation would be able to handle contracts.
Contracts API Draft
The following is a little draft of how the contract API could look like, it's probably a good idea to replace the properties under Game.contracts
with a function similar to Game.market.getAllOrders
.
property |
type |
description |
id |
string |
A unique identifier, can be used to interact with a contract |
checksum |
string |
Checksum of the contained code, each checksum will have to be allowed manually once by reviewing the associated code |
contractee |
string |
The player of which the contract was created |
contractor optional |
string |
The player that accepted the contract, might not be set yet |
reward |
number |
The amount of credits that the contractee pays the contractor once the contract has been fulfilled |
parameters otpional |
object |
Any valid json object that can be used to modify the modules behaviour |
-
Game.constract
: Is only available from within a contract, contains the currently executed Contract
object.
-
Game.contracts.available
: Array of available Contract
objects.
-
Game.contracts.accepted
: Array of Contract
objects that you accepted.
-
Game.contracts.created
: Array of Contract
objects that you created but are not accepted yet.
-
Game.contracts.running
: Array of Contract
objects that you created any were accepted by someone else.
-
Game.contracts.create(moduleName, reward, [parameters])
: Create a new contract
parameter |
type |
description |
moduleName |
string |
Name of the module you want to create as a contract |
reward |
number |
The amount of credits that the contractee pays the contractor once the contract has been fulfilled |
parameters otpional |
object |
Any valid json object that can be used to modify the modules behaviour |
Game.contracts.accept(id)
: Accept an available contract
parameter |
type |
description |
id |
string |
A unique identifier of the contract you want to accept |