Code Monkey home page Code Monkey logo

panoptipong's Introduction

PANOPTIPONG - CRDT Pong

Installation

Run two processes:

# run holochain local dev server
hcdev web

# run react dev server
cd ui-src && npm start

Then go to http://localhost:3000

Running tests

This repo uses an experimental method of running Holochain unit tests. It uses a fork of holochain-proto which adds a new command: hcdev run-js. This allows arbitrary execution of code in the Javascript ribosome, allowing us to use an existing JS test runner (tape in our case) to test app functionality.

Get the fork from https://github.com/maackle/holochain-proto. Then, npm test -- [ZOME_NAME] [TEST_FILENAME].

e.g.: npm test -- voting puretest/index.js

Setup

git clone github.com/maackle/holochain-proto
cd $GOPATH/github.com/maackle/holochain-proto && make

Registration

  • As an agent, when I join I link my key hash to a team membership anchor
  • This is selected by picking the membership anchor with the fewest links
  • An agent should not be able to register more than once
sequenceDiagram
    DHT-->>Agent1: getLinks( anchor('teamLMembership') )
    DHT-->>Agent1: getLinks( anchor('teamRMembership') )
    Agent1-->>Agent1: teamDesignation('teamR')
    Agent1-->>DHT: link( anchor('teamR') -> keyhash)

Cast a vote

  • As an agent on a team the game play UI should appear
  • Retrieve the number of players in each team
  • Commit an entry containing player counts, vote, agentHash, and salt
  • Link to my team's vote anchor
sequenceDiagram
    DHT-->>Agent1: getLinks( anchor('teamLMembership') )
    DHT-->>Agent1: getLinks( anchor('teamRMembership') )
    Agent1-->>DHT: 'vote'
    Agent1-->>DHT: link( anchor('teamRVote') -> vote)

Get the state

  • At any time the UI can query to get the state
  • This retrieves the count of votes cast by each team
  • If count is greater than X, sum the votes cast by each team
  • How do we determine what turn it is?
  • Reduce in to state and return this as an object
    • Calculate ball state as a continuous value by calculating the sum of votes weighted by the reciprocal of the number of players when the vote was cast
    • Calculate each paddle state as the sum of move weighted by the reciprocal of the number of players in that team
sequenceDiagram
    DHT-->>Agent1: getLinks( anchor('teamLVotes') , load)
    DHT-->>Agent1: getLinks( anchor('teamRVotes') , load)

state object

initialState = {
    ball: {
        x: 60,
        y: 50
    },
    paddleL: 50,
    paddleR: 50
}

game constants

var vBall = 2.3; // how far the ball will move in a  'turn'
var vPaddle = 1.3; // how far the paddle can possible move in a 'turn'
var initialBallVelocity =  {x: vBall*Math.sqrt(2), y: vBall*Math.sqrt(2)}

var width=300, height=100;
var paddleHeight = 30;

reducing votes in to state

function reduceState(initialState, votesL, votesR) {

    var paddleL =  votesL.reduce(function(acc, elem) {
        acc += vPaddle * (elem.move / elem.teamL.playerCount);
    }, initialState.paddleL);

    var paddleR = votesR.reduce(function(acc, elem){
        acc += vPaddle * (elem.move / elem.teamR.playerCount);
    }, initialState.paddleR);

    var ballReducer = function(acc, elem, i) {
        acc.x += initialBallVelocity.x / (elem.teamL.playerCount + elem.teamR.playerCount);
        acc.y += initialBallVelocity.y / (elem.teamL.playerCount + elem.teamR.playerCount);
    }

    ballPos = votesR.reduce(ballReducer,
        votesL.reduce(ballReducer, initialState.ball));

    return {
        ball: ballPos,
        paddleL: paddleL,
        paddleR: paddleR
    }
}

Public Methods

/**
 * Register your agent to a team so you can vote
 */
function register() {
  // get the number of agents in each team so far
  var membersL = getLinks(anchor('members', 'L'), '');
  var membersR = getLinks(anchor('members', 'R'), '');

  // check the agent is not in any team already
  var inL = membersL.some(function(elem) {
    return elem.Hash ===  App.Key.Hash;
  });
  var inR = membersR.some(function(elem) {
    return elem.Hash ===  App.Key.Hash;
  });

  if( inL || inR ) {
    return "AlreadyRegistered";
  }

  var team;
  if(membersL.length <= membersR.length) {
    team = 'L';
  } else {
    team = 'R';
  }
  joinTeam(team);
  return team;
}
/**
 * Cast a vote
 * @param       {integer}  vote   An integer in [-1,0,+1] representing how you want to move the paddle
 */
function vote(payload) {
  var move = payload.move;

  var nPlayersL = getLinks(anchor('members', 'L'), '').length;
  var nPlayersR = getLinks(anchor('members', 'R'), '').length;

  var nVotesL =countVotes("L");
  var nVotesR =countVotes("R");

  var vote = {
    move: move,
    teamL: {playerCount: nPlayersL, voteCount: nVotesL},
    teamR: {playerCount: nPlayersR, voteCount: nVotesR},
    agentHash: App.Key.Hash,
    randomSalt: ""+Math.random(),
    teamID: getTeam()
  };

  return castVote(vote);
}

function castVote(vote){
  if(anchorExists(vote.teamID,"GameID")==="false"){
    anchor(vote.teamID,"GameID");
  }

  voteHash = commit("vote",vote);
  // On the DHT, puts a link on my anchor to the new post
  commit('voteLink', {
    Links: [{ Base: anchor(vote.teamID,"GameID"), Link: voteHash, Tag: 'vote' }]
  });

  return voteHash;
}

getState()

returns the state based on all votes seen so far

/**
 * Retrieve the state as an object
 * @returns {object} an object that fully defines the game state to show
 * {ball: {x, y}, paddleL: pos, paddleR: pos}
*/
function reduceState(initialState, votesL, votesR) {

    var paddleL =  votesL.reduce(function(acc, elem) {
      if(elem.move!=-2){
        debug("paddleL move-> "+elem.move);
        acc += vPaddle * (elem.move / elem.teamL.playerCount);
      }
      return acc;
    }, initialState.paddleL);

    var paddleR = votesR.reduce(function(acc, elem){
      if(elem.move!=-2){

          debug("paddleR move-> "+elem.move);
        acc += vPaddle * (elem.move / elem.teamR.playerCount);
      }
      return acc;
    }, initialState.paddleR);

    var ballReducer = function(acc, elem, i) {
        acc.x += initialBallVelocity.x / (elem.teamL.playerCount + elem.teamR.playerCount);
        acc.y += initialBallVelocity.y / (elem.teamL.playerCount + elem.teamR.playerCount);
        return acc;
    };

    ballPos = votesR.reduce(ballReducer,
        votesL.reduce(ballReducer, initialState.ball));

    return {
        ball: ballPos,
        paddleL: paddleL,
        paddleR: paddleR
    };
}

getTeam

returns the teamDesignation object stored in local chain by an agent

function getTeam() {
  var response = query({
    Return: {
      Entries: true
    },
    Constrain: {
      EntryTypes: ["teamDesignation"],
      Count: 1
    }
  });

  return response[0] || "NotRegistered";
}

Schemas

vote

{
    "title": "Vote Schema",
    "type": "object",
    "properties" : {
        "move" : {
            "type": "integer",
            "minimum": -1,
            "maximum": 1
        },
        "teamL": {
            "playerCount" : {"type": "integer"},
            "voteCount" : {"type": "integer"}
        },
        "teamR": {
            "playerCount" : {"type": "integer"},
            "voteCount" : {"type": "integer"}
        },
        "agentHash" : {"type": "string"},
        "randomSalt" : {"type": "string"}
    },
    "required": ["move", "teamL", "teamR", "agentHash", "randomSalt"]
}

panoptipong's People

Contributors

dtholmes187 avatar jetttech avatar maackle avatar willemolding avatar zo-el avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.