In The Prisoner's Dilemma, two agents are given a choice: cooperate with their partner for mutual reward, or defect for individual reward.

The dilemma faced by the agents is that, regardless of what their opponent does, they are always better off defecting than cooperating. But the combined outcome when both defect is worse than if they cooperate.

For a purely rational agent with no prior knowledge, always defecting is the correct choice.

A variation on this is an iterated prisoner's dilemma, multiple rounds are played, and the previous responses of each agent are given as an input to the players. This vastly widens the space of feasible strategies, and can lead to some diverse and unexpected strategies performing best.

We wanted to build a platform for running these kinds of challenges.

Players can create strategies which contain logic to compete in challenges. Strategies are smart contracts and exist on-chain. Each turn, they are given the history of their opponent's moves, as well as their opponent's address. This allows strategies to make decisions based on their opponent's previous choices. Passing the address of their opponent allows the strategy to call them directly, and probe it with previously unseen input.

Challenges are run on chain through Challenges contracts. Strategies can be registered for challenges, where they join the pool of competing strategies, and are scored and ranked by the Challenge contract.

For the Prisoner's Dilemma, one of the simplest strategies you might consider is to defect every time. This can be implemented using the following smart contract.

contract AlwaysDefect is Bot {
    constructor(uint256 owner_profile, string memory name) Bot (owner_profile, name) {}

    function play(address, bool[] calldata, bool[] calldata) external pure override returns (bool) {
            return true;
    }
}

We can register AlwaysDefect on a specific challenge, where it will be automatically run against all other strategies registers on the challenge.

alwaysDefect.register(0x...)

For a single turn of Prisoner's Dilemma, this strategy is optimal. However, in an iterated game, this may not be the case.

This strategy defects on the first move, and plays the opponents last move every turn thereafter.

contract TitForTat is Bot {
    constructor(uint256 owner_profile, string memory name) Bot (owner_profile, name) {}

    function play(
        address,
        bool[] calldata opponent_previous_plays,
        bool[] calldata) external pure override returns (bool) {
            if (opponent_previous_plays.length == 0) {
                return true;
            }
            return opponent_previous_plays[opponent_previous_plays.length - 1];
    }
}

You can also call your opponent's strategy to see what they would play in certain situation.

contract Advanced is Bot {
    constructor(uint256 owner_profile, string memory name) Bot (owner_profile, name) {}

    function play(
        address opponent,
        bool[] calldata opponent_previous_plays,
        bool[] calldata bot_previous_plays
    ) external override virtual returns (bool) {
        return Bot(opponent).play(address(this), bot_previous_plays, opponent_previous_plays);
    }
}

Challenges and Strategies are registered to an owner, which is a profile on Lens. This allows users to share challenges, comment on interesting strategy battles, and collaborate to find optimal strategies.

A Web3 front end allows users to find all challenges, and explore previous battles of strategies, and see overall scores for challenges.

How we built it

We built the smart contract infrastructure using Solidity on the Polygon blockchain We used Lens protocol to build the decentralized social graph that allows users to link challenges and strategies to profiles, and share content. We used Flutter for a portable, neat client that can be used to interface with Web3.

Challenges we ran into

We ran into some issues with how constructors and objects are handled in solidity, which took a fair amount of time to debug.

UI work is hard.

Accomplishments that we’re proud of

Web3 application that can read from both lens via their hosted GraphQL api, and the polygon blockchain directly for interfacing with our challenge infrastructure smart contracts.

Entirely on chain platform that allows users to submit strategies and have them automatically compete in two player perfect information games..

Platform portable web3 client.

What we learned

UI work is hard. How to interact with blockchain. Smart contract development. Using an interface like GraphQL to interact with blockchain system can be much easier to develop for and to use as a user. Idea generation is hard. Finding ways to parallelize development much more efficient. Peer programming can be an effective way to debug code.

Built With

Share this project:

Updates