Skip to content

Instantly share code, notes, and snippets.

@KBryan

KBryan/index.rsh Secret

Created June 14, 2021 13:20
Show Gist options
  • Save KBryan/04fd31163fe0a1e79190a75fded66494 to your computer and use it in GitHub Desktop.
Save KBryan/04fd31163fe0a1e79190a75fded66494 to your computer and use it in GitHub Desktop.
support file for project error
'reach 0.1';
//#region Enums
const RSS_ENUM_SIZE = 4;
const PLAYER_COUNT = 3;
const MAXIMUM_BUILDINGS_ON_TILE = 3;
const [isResource, WHEAT, ORE, WOOD, BRICK] = makeEnum(RSS_ENUM_SIZE);
const [isPlayer, pNONE, pALICE, pBOB, pCARL] = makeEnum(4);
const [isGamePhase, RESOURCE_GEN, BUILDING, TRADE] = makeEnum(3);
//#endregion
//#region Player Definitions
const MAP_SIZE = 7;
const Player =
{
...hasRandom,
log: Fun(true, Null),
informTimeout: Fun([], Null),
getSeed: Fun([], UInt),
seeMap: Fun([Array(Object({
rss: UInt,
roll: UInt,
}), MAP_SIZE)], Null),
seeGameState: Fun([Object({
winner: UInt,
roll: UInt,
round: UInt,
turn: UInt,
phase: UInt,
resources: Array(Array(UInt, RSS_ENUM_SIZE), PLAYER_COUNT),
buildings: Array(Array(UInt, MAXIMUM_BUILDINGS_ON_TILE), MAP_SIZE)
})], Null),
placeBuilding: Fun([], Object({ tile: UInt, skip: Bool })),
offerTrade: Fun([], Object({
recievePlayer: UInt,
offer: Array(UInt, RSS_ENUM_SIZE),
payment: Array(UInt, RSS_ENUM_SIZE),
skip: Bool
})),
recieveTradeOffer: Fun([Object({
offerPlayer: UInt,
offer: Array(UInt, RSS_ENUM_SIZE),
payment: Array(UInt, RSS_ENUM_SIZE)
})], Bool),
offerTradeCallback: Fun([Bool], Null)
};
const Alice = {
...Player,
wager: UInt,
testaroonie: UInt,
};
const Bob = {
...Player,
acceptWager: Fun([UInt], Null)
};
//#endregion
export const main = Reach.App(
{}, [Participant('Alice', Alice), Participant('Bob', Bob), Participant('Carl', Bob)], (A, B, C) => {
//#region Alice Presents Wager
// shows alice's wager
A.only(() => {
const wager = declassify(interact.wager);
});
// pays + publish wager
A.publish(wager).pay(wager);
commit();
//#endregion
//#region Bob + Carl Accept Wager
B.only(() => {
interact.acceptWager(wager);
});
B.pay(wager);
//.timeout(DEADLINE, () => closeTo(A, informTimeout));
commit();
C.only(() => {
interact.acceptWager(wager);
});
C.pay(wager);
//.timeout(DEADLINE, () => closeTo(A, informTimeout));
commit();
//#endregion
//#region World Generation
//@TODO: Try putting first world generation step in with the wager step
//@TODO: Delete the two statements below and uncomment the true logic
A.only(() => {
const deleteThisCode = declassify(interact.testaroonie);
});
A.publish(deleteThisCode);
/*
A.only(() => {
const _seedA = interact.getSeed();
const [_commitA, _saltA] = makeCommitment(interact, _seedA);
const commitA = declassify(_commitA);
});
A.publish(commitA);
// .timeout(DEADLINE, () => closeTo(B, informTimeout));
commit();
unknowable(B, A(_seedA, _saltA));
unknowable(C, A(_seedA, _saltA));
B.only(() => {
const _seedB = interact.getSeed();
const [_commitB, _saltB] = makeCommitment(interact, _seedB);
const commitB = declassify(_commitB);
});
B.publish(commitB);
// .timeout(DEADLINE, () => closeTo(B, informTimeout));
commit();
unknowable(A, B(_seedB, _saltB));
unknowable(C, B(_seedB, _saltB));
C.only(() => {
const seedC = declassify(interact.getSeed());
});
C.publish(seedC);
// .timeout(DEADLINE, () => closeTo(A, informTimeout));
commit();
A.only(() => {
const [saltA, seedA] = declassify([_saltA, _seedA]);
});
A.publish(saltA, seedA);
checkCommitment(commitA, saltA, seedA);
commit();
B.only(() => {
const [saltB, seedB] = declassify([_saltB, _seedB]);
});
B.publish(saltB, seedB);
checkCommitment(commitB, saltB, seedB);
*/
const seedA = 324445;
const seedB = 164775;
const seedC = 824649;
// seed is calculated from the (hopefully random) input of each player
const seed = seedA + seedB + seedC;
// decides the world
// didnt want to make algorithm for good random world bc im lazy so instead you get this
const map = array(Object({ rss: UInt, roll: UInt }), [
{ rss: (seed + 6) % RSS_ENUM_SIZE, roll: (seed + 9) % 8 },
{ rss: seed % RSS_ENUM_SIZE, roll: seed % 8 },
{ rss: (seed + 18) % RSS_ENUM_SIZE, roll: (seed + 21) % 8 },
{ rss: (seed + 3) % RSS_ENUM_SIZE, roll: (seed + 6) % 8 },
{ rss: (seed + 15) % RSS_ENUM_SIZE, roll: (seed + 18) % 8 },
{ rss: (seed + 12) % RSS_ENUM_SIZE, roll: (seed + 15) % 8 },
{ rss: (seed + 9) % RSS_ENUM_SIZE, roll: (seed + 12) % 8 },
]);
//#endregion
// ------ GAMEPLAY BEGINS HERE ------
//#region Resources + Actions
// show everyone the map before any interactions start
each([A, B, C], () => {
interact.seeMap(map);
});
function createStarterResourceArray() {
return array(UInt, [2, 2, 2, 2]);
}
function createStarterBuildingArray() {
return array(UInt, [pNONE, pNONE, pNONE]);
}
var gameState = {
winner: pNONE,
// stores the resources of each player.
// 0 - Alice, 1 - Bob, 2 - Carl
resources: array(Array(UInt, RSS_ENUM_SIZE), [
createStarterResourceArray(),
createStarterResourceArray(),
createStarterResourceArray(),
]),
// stores which round that the gameState is in
round: 0,
// stores the roll so that the frontend can be responsive
roll: 4,
// stores which player is supposed to be playing
turn: pALICE,
// stores the player's phase of the game
phase: RESOURCE_GEN,
//buildings
buildings: array(Array(UInt, MAXIMUM_BUILDINGS_ON_TILE), [
createStarterBuildingArray(),
createStarterBuildingArray(),
createStarterBuildingArray(),
createStarterBuildingArray(),
createStarterBuildingArray(),
createStarterBuildingArray(),
createStarterBuildingArray(),
])
};
invariant(
isPlayer(gameState.winner) && //fails
gameState.resources.length == PLAYER_COUNT &&
gameState.roll >= 2 && gameState.roll <= 12 && //fails
gameState.round >= 0 &&
isPlayer(gameState.turn) && // fails
isGamePhase(gameState.phase) && //fails
balance() == wager * PLAYER_COUNT
);
while (gameState.winner == pNONE) {
commit();
// sends the resource data to the frontend
function letPlayersSeeGameState(localGameState) {
each([A, B, C], () => {
interact.seeGameState(localGameState);
});
}
// returns true if the specified player has enough resources in the localGameState
function ensurePlayerHasResources(localGameState, player, resources) {
//resources.length != localGameState.resources[player].length ? false :
return ( //TODO!!: first 0 should be player
localGameState.resources[0][0] >= resources[0] &&
localGameState.resources[0][1] >= resources[1] &&
localGameState.resources[0][2] >= resources[2] &&
localGameState.resources[0][3] >= resources[3]
);
}
// rolls the dice and gives players the correct amount of resources
function diceRollPhase(localGameState, player) {
/**
* We're being lazy here so this is all deterministic based off of the PUBLIC seed
* In other words, user front ends can predict future rolls.
* SOLUTIONS:
* 1. Make a new seed at the beginning but obfuscate it. Probably won't be safe after first roll.
* 2. Ask for a new random from each player each round (annoying and cost ineffective).
* 3. Base rolls calculation off of player interactions to make it different each time (players affect the roll by design).
*/
const localRoll =
((seedA + seedB) * gameState.round) % 6 + ((seedB + seedC) * gameState.round % 6) + 2;
assert(localRoll < 12);
return {
winner: localGameState.winner,
roll: localRoll,
round: localGameState.round + 1,
turn: player,
phase: BUILDING, // transitions to the next phase, which is building
buildings: localGameState.buildings,
// yeah... right now it's just giving resources to player 1
resources: localGameState.resources.set(0, array(UInt, [
localGameState.resources[0][0] + 1,
localGameState.resources[0][1] + 1,
localGameState.resources[0][2] + 1,
localGameState.resources[0][3] + 1
]))
};
}
// returns the game state when attempting to build a building
function attemptBuildingPhase(localGameState, player, buildCmd) {
// returns 3 if there is no building space, otherwise it returns the empty space
function tileHasBuildingSpace(tile) {
const result = 3
- (tile[0] == pNONE ? 1 : 0)
- (tile[1] == pNONE ? 1 : 0)
- (tile[2] == pNONE ? 1 : 0);
return result;
}
//A.interact.log(buildCmd);
// skips if that's what the player wants to do
if(buildCmd.skip) {
//A.interact.log("It got within the skip, so it should be returning winner as 2.");
return {
winner: localGameState.winner,
roll: localGameState.roll,
round: localGameState.round,
turn: player,
phase: TRADE, // transitions to the next phase, which is trade
resources: localGameState.resources,
buildings: localGameState.buildings,
};
}
// issues the command if that's what the player wants to do
else if(buildCmd.tile >= 0 && buildCmd.tile < MAP_SIZE) {
//A.interact.log("It got within the second if condition.");
const buildingSpace = tileHasBuildingSpace(localGameState.buildings[buildCmd.tile]);
//A.interact.log(buildingSpace);
//A.interact.log(MAXIMUM_BUILDINGS_ON_TILE);
// if the tile to build on has space
if(buildingSpace < MAXIMUM_BUILDINGS_ON_TILE) {
//A.interact.log("It got within the third if condition.");
A.only(() => { interact.log("It got within the third if condition.") });
return {
winner: localGameState.winner,
roll: localGameState.roll,
round: localGameState.round,
turn: player,
phase: TRADE, // transitions to the next phase, which is trade
resources: localGameState.resources,
buildings: localGameState.buildings.set(buildCmd.tile, array(UInt, [
buildingSpace == 0 ? player : localGameState.buildings[buildCmd.tile][0],
buildingSpace == 1 ? player : localGameState.buildings[buildCmd.tile][1],
buildingSpace == 2 ? player : localGameState.buildings[buildCmd.tile][2],
]))
};
}
}
// if it hasn't returned at this point, then a faulty command was given
// i would put an else statement here but that doesn't compile
//A.interact.log("A faulty command was given, so we're returning as normal.");
return {
winner: localGameState.winner,
roll: localGameState.roll,
round: localGameState.round,
turn: player,
phase: TRADE, // transitions to the next phase, which is trade
resources: localGameState.resources,
buildings: localGameState.buildings,
};
}
// returns the game state after an attempted trade offer
function attemptTradeOffer(localGameState, player, nextPlayer, tradeResponse, tradeOffer) {
// this doesn't work because of compiler errors but it's how it should work
if(!tradeResponse) {
return {
winner: localGameState.winner,
roll: localGameState.roll,
round: localGameState.round,
turn: nextPlayer, // next player assuming trading is the last step
phase: RESOURCE_GEN, // transitions to the next phase, which is rss generation
buildings: localGameState.buildings,
resources: localGameState.resources,
};
}
else {
const aliceRss = array(UInt, [0, 0, 0, 0]);
const bobRss = array(UInt, [0, 0, 0, 0]);
const carlRss = array(UInt, [0, 0, 0, 0]);
return {
winner: localGameState.winner,
roll: localGameState.roll,
round: localGameState.round,
turn: nextPlayer, // next player assuming trading is the last step
phase: RESOURCE_GEN, // transitions to next phase
buildings: localGameState.buildings,
resources: array(Array(UInt, RSS_ENUM_SIZE), [aliceRss, bobRss, carlRss])
};
}
}
// ALICE: Dice Roll Phase
A.interact.log("Dice Roll Phase");
const gameState1 = diceRollPhase(gameState, pALICE);
letPlayersSeeGameState(gameState1);
// ALICE: Building Phase
A.interact.log("Building Phase");
A.only(() => {
const aBuilding = declassify(interact.placeBuilding());
});
A.publish(aBuilding);
commit();
const gameState2 = attemptBuildingPhase(gameState1, pALICE, aBuilding);
letPlayersSeeGameState(gameState2);
/*
// ALICE: Trade Deal Phase
A.only(() => {
const aTrade = declassify(interact.offerTrade());
interact.log(aTrade);
});
A.publish(aTrade);
const aTradeAllowed =
!aTrade.skip &&
isPlayer(aTrade.recievePlayer) &&
ensurePlayerHasResources(gameState2, pALICE, aTrade.offer) &&
ensurePlayerHasResources(gameState2, aTrade.recievePlayer, aTrade.payment);
const bobCanTrade = aTrade.recievePlayer == pBOB;
const carlCanTrade = aTrade.recievePlayer == pCARL;
commit();
B.only(() => {
const _bTradeResponse = aTradeAllowed && bobCanTrade ? interact.recieveTradeOffer({
offerPlayer: pALICE,
offer: aTrade.offer,
payment: aTrade.payment
}) : false;
const bTradeResponse = declassify(_bTradeResponse);
});
B.publish(bTradeResponse);
const gameState3B = attemptTradeOffer(
gameState2,
pALICE,
pBOB,
aTradeAllowed && bobCanTrade ? bTradeResponse : false,
aTrade);
commit();
C.only(() => {
const _cTradeResponse = aTradeAllowed && carlCanTrade ? interact.recieveTradeOffer({
offerPlayer: pALICE,
offer: aTrade.offer,
payment: aTrade.payment
}) : false;
const cTradeResponse = declassify(_cTradeResponse);
});
C.publish(cTradeResponse);
const gameState3C = attemptTradeOffer(
gameState2,
pALICE,
pCARL,
aTradeAllowed && carlCanTrade ? cTradeResponse : false,
aTrade);
commit();
const gameState3 = bobCanTrade ? gameState3B : gameState3C;
letPlayersSeeGameState(gameState3);
*/
// repeat the last four steps with bob
// repeat again with carl
// check to see if anyone is a winner (haven't finished yet)
// probably will just check for a summation of buildings
A.only(() => {
const test = "test";
});
A.publish(test);
gameState = gameState2;
continue;
}
commit();
//#endregion
// alice always wins in this scenario (pending actual logic)
A.only(() => {
const Apotatoes = declassify(interact.testaroonie);
});
A.publish(Apotatoes);
transfer(wager * PLAYER_COUNT).to(
gameState.winner == pALICE ? A :
gameState.winner == pBOB ? B : C
);
commit();
exit();
}
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment