Skip to content

Commit

Permalink
improve ranking logic, tests pass
Browse files Browse the repository at this point in the history
  • Loading branch information
peersky committed Oct 2, 2023
1 parent 24e2470 commit b402f98
Show file tree
Hide file tree
Showing 11 changed files with 182 additions and 184 deletions.
12 changes: 3 additions & 9 deletions abi/hardhat-diamond-abi/HardhatDiamondABI.sol/BestOfDiamond.json
Original file line number Diff line number Diff line change
Expand Up @@ -1424,12 +1424,6 @@
"internalType": "bytes32",
"name": "votesHidden",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "bytes",
"name": "proof",
"type": "bytes"
}
],
"name": "VoteSubmitted",
Expand Down Expand Up @@ -1564,9 +1558,9 @@
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "proof",
"type": "bytes"
"internalType": "address",
"name": "voter",
"type": "address"
}
],
"name": "submitVote",
Expand Down
12 changes: 3 additions & 9 deletions abi/src/facets/gameMastersFacet.sol/GameMastersFacet.json
Original file line number Diff line number Diff line change
Expand Up @@ -150,12 +150,6 @@
"internalType": "bytes32",
"name": "votesHidden",
"type": "bytes32"
},
{
"indexed": false,
"internalType": "bytes",
"name": "proof",
"type": "bytes"
}
],
"name": "VoteSubmitted",
Expand Down Expand Up @@ -290,9 +284,9 @@
"type": "bytes32"
},
{
"internalType": "bytes",
"name": "proof",
"type": "bytes"
"internalType": "address",
"name": "voter",
"type": "address"
}
],
"name": "submitVote",
Expand Down
12 changes: 1 addition & 11 deletions src/facets/BestOfFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@ contract BestOfFacet is IBestOf, IERC1155Receiver, DiamondReentrancyGuard, IERC7
// LibBestOf.fulfillTokenRequirement(msg.sender, address(this), gameId);
require(gameRank != 0, "game rank not specified");
require(msg.value >= settings.gamePrice, "Not enough payment");
require(IRankToken(settings.rankTokenAddress).balanceOf(msg.sender, gameRank) > 0, "Must have rank token");
BOGInstance storage game = gameId.getGameStorage();
game.createdBy = msg.sender;
settings.numGames += 1;
Expand All @@ -74,10 +73,6 @@ contract BestOfFacet is IBestOf, IERC1155Receiver, DiamondReentrancyGuard, IERC7
) public payable nonReentrant {
createGame(gameMaster, gameId, gameRank);
BOGInstance storage game = gameId.getGameStorage();
BOGSettings storage settings = BOGStorage();
IRankToken rankTokenContract = IRankToken(settings.rankTokenAddress);
rankTokenContract.mint(address(this), 1, gameRank + 1, "");
rankTokenContract.mint(address(this), 3, gameRank, "");
if (additionalRanks.length != 0) {
for (uint256 i = 0; i < additionalRanks.length; i++) {
IRankToken additonalRank = IRankToken(additionalRanks[i]);
Expand All @@ -88,11 +83,6 @@ contract BestOfFacet is IBestOf, IERC1155Receiver, DiamondReentrancyGuard, IERC7
}
game.additionalRanks = additionalRanks;
}

LibCoinVending.ConfigPosition memory emptyConfig;
LibCoinVending.configure(bytes32(gameId), emptyConfig);

emit gameCreated(gameId, gameMaster, msg.sender, gameRank);
}

function createGame(address gameMaster, uint256 gameRank) public payable {
Expand All @@ -111,7 +101,7 @@ contract BestOfFacet is IBestOf, IERC1155Receiver, DiamondReentrancyGuard, IERC7
LibCoinVending.refund(bytes32(gameId), players[i]);
emit PlayerLeft(gameId, players[i]);
}
gameId.closeGame();
// gameId.closeGame();
emit GameClosed(gameId);
}

Expand Down
38 changes: 12 additions & 26 deletions src/facets/gameMastersFacet.sol
Original file line number Diff line number Diff line change
Expand Up @@ -78,15 +78,17 @@ contract GameMastersFacet is DiamondReentrancyGuard, EIP712 {
return checkSignature(message, signature, account);
}

function releaseAndReward(uint256 gameId, address player, address[] memory leaderboard) private {}

function _endGame(uint256 gameId, address[] memory leaderboard, address[] memory players) internal nonReentrant {
IBestOf.BOGInstance storage game = gameId.getGameStorage();
gameId.closeGame();
emitRankRewards(gameId, leaderboard);

for (uint256 i = 0; i < players.length; i++) {
LibCoinVending.release(bytes32(gameId), game.createdBy, leaderboard[0], players[i]);
gameId.removeAndUnlockPlayer(players[i]);
}
// gameId.closeGame();
gameId.emitRankRewards(leaderboard);
(, uint256[] memory scores) = gameId.getScores();
emit GameOver(gameId, players, scores);
}
Expand All @@ -105,15 +107,9 @@ contract GameMastersFacet is DiamondReentrancyGuard, EIP712 {
address proposer;
}

event VoteSubmitted(
uint256 indexed gameId,
uint256 indexed turn,
address indexed player,
bytes32 votesHidden,
bytes proof
);
event VoteSubmitted(uint256 indexed gameId, uint256 indexed turn, address indexed player, bytes32 votesHidden);

function submitVote(uint256 gameId, bytes32 votesHash, bytes memory proof) public {
function submitVote(uint256 gameId, bytes32 votesHash, address voter) public {
LibBestOf.enforceIsGM(gameId);
gameId.enforceGameExists();
gameId.enforceHasStarted();
Expand All @@ -126,13 +122,13 @@ contract GameMastersFacet is DiamondReentrancyGuard, EIP712 {
// votesHidden[1],
// votesHidden[2]
// );
IBestOf.BOGInstance storage game = gameId.getGameStorage();
// IBestOf.BOGInstance storage game = gameId.getGameStorage();
require(!gameId.isGameOver(), "Game over");
require(gameId.getTurn() > 1, "No proposals exist at turn 1: cannot vote");
// game.votesHidden[msg.sender].votedFor = votesHidden;
game.votesHidden[msg.sender].proof = proof;
gameId.playerMove(msg.sender); // This will enforce player is in in the game
emit VoteSubmitted(gameId, gameId.getTurn(), msg.sender, votesHash, proof);
// game.votesHidden[voter].proof = bytes memory (0);
gameId.playerMove(voter); // This will enforce player is in in the game
emit VoteSubmitted(gameId, gameId.getTurn(), voter, votesHash);
}

function submitProposal(ProposalParams memory proposalData) public {
Expand Down Expand Up @@ -201,10 +197,8 @@ contract GameMastersFacet is DiamondReentrancyGuard, EIP712 {
for (uint256 i = 0; i < players.length; i++) {
game.proposalCommitmentHashes[players[i]] = bytes32(0);
game.ongoingProposals[i] = "";
game.votesHidden[players[i]].votedFor[0] = bytes32(0);
game.votesHidden[players[i]].votedFor[1] = bytes32(0);
game.votesHidden[players[i]].votedFor[2] = bytes32(0);
delete game.votesHidden[players[i]].proof;
game.votesHidden[players[i]].hash = bytes32(0);
// delete game.votesHidden[players[i]].proof;
}
}

Expand Down Expand Up @@ -262,12 +256,4 @@ contract GameMastersFacet is DiamondReentrancyGuard, EIP712 {
_nextTurn(gameId, newProposals);
}

function emitRankRewards(uint256 gameId, address[] memory leaderboard) private {
IBestOf.BOGInstance storage game = gameId.getGameStorage();
IBestOf.BOGSettings storage settings = LibBestOf.BOGStorage();
RankToken rankTokenContract = RankToken(settings.rankTokenAddress);
rankTokenContract.safeTransferFrom(address(this), leaderboard[0], game.rank + 1, 1, "");
rankTokenContract.safeTransferFrom(address(this), leaderboard[1], game.rank, 2, "");
rankTokenContract.safeTransferFrom(address(this), leaderboard[2], game.rank, 1, "");
}
}
9 changes: 5 additions & 4 deletions src/initializers/BestOfInit.sol
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@ import {IERC165} from "../vendor/interfaces/IERC165.sol";
import {LibEIP712WithStorage} from "../libraries/LibEIP712Storage.sol";
import "@openzeppelin/contracts/utils/cryptography/ECDSA.sol";
import {IBestOf} from "../interfaces/IBestOf.sol";
import {IRankToken} from "../interfaces/IRankToken.sol";
import {LibTBG} from "../libraries/LibTurnBasedGame.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
// import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import "hardhat/console.sol";

// It is expected that this contract is customized if you want to deploy your diamond
Expand Down Expand Up @@ -80,10 +81,10 @@ contract BestOfInit {
IBestOf.BOGSettings storage _BOG = BOGStorage();
_BOG.gamePrice = initializer.gamePrice;
_BOG.joinGamePrice = initializer.joinGamePrice;
IERC1155 ERC1155Contract = IERC1155(initializer.rankTokenAddress);
IRankToken rankContract = IRankToken(initializer.rankTokenAddress);
require(
ERC1155Contract.supportsInterface(type(IERC1155).interfaceId),
"BestOfGame->init: rank token address does not support IERC1155 interface"
rankContract.supportsInterface(type(IRankToken).interfaceId),
"BestOfGame->init: rank token address does not support Rank interface"
);
_BOG.rankTokenAddress = initializer.rankTokenAddress;
_BOG.contractInitialized = true;
Expand Down
4 changes: 2 additions & 2 deletions src/interfaces/IBestOf.sol
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,8 @@ interface IBestOf {
// }

struct VoteHidden {
bytes32[] votedFor;
bytes proof;
bytes32 hash;
// bytes proof;
}

struct BOGInstance {
Expand Down
25 changes: 22 additions & 3 deletions src/libraries/LibBestOf.sol
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,23 @@ library LibBestOf {
}
}

function emitRankReward(uint256 gameId, address[] memory leaderboard, address rankTokenAddress) private {
IBestOf.BOGInstance storage game = getGameStorage(gameId);
IRankToken rankTokenContract = IRankToken(rankTokenAddress);
rankTokenContract.safeTransferFrom(address(this), leaderboard[0], game.rank + 1, 1, "");
rankTokenContract.safeTransferFrom(address(this), leaderboard[1], game.rank, 2, "");
rankTokenContract.safeTransferFrom(address(this), leaderboard[2], game.rank, 1, "");
}

function emitRankRewards(uint256 gameId, address[] memory leaderboard) internal {
IBestOf.BOGInstance storage game = getGameStorage(gameId);
IBestOf.BOGSettings storage settings = LibBestOf.BOGStorage();
emitRankReward(gameId, leaderboard, settings.rankTokenAddress);
for (uint256 i = 0; i < game.additionalRanks.length; i++) {
emitRankReward(gameId, leaderboard, game.additionalRanks[i]);
}
}

function _releaseRankToken(address player, uint256 gameRank, address rankTokenAddress) private {
IRankToken rankToken = IRankToken(rankTokenAddress);
rankToken.unlockFromInstance(player, gameRank, 1);
Expand All @@ -83,9 +100,11 @@ library LibBestOf {
gameId.removePlayer(player);
IBestOf.BOGSettings storage settings = BOGStorage();
IBestOf.BOGInstance storage game = getGameStorage(gameId);
_releaseRankToken(player, game.rank, settings.rankTokenAddress);
for (uint256 i = 0; i < game.additionalRanks.length; i++) {
_releaseRankToken(player, game.rank, game.additionalRanks[i]);
if (game.rank > 1) {
_releaseRankToken(player, game.rank, settings.rankTokenAddress);
for (uint256 i = 0; i < game.additionalRanks.length; i++) {
_releaseRankToken(player, game.rank, game.additionalRanks[i]);
}
}
}
}
31 changes: 16 additions & 15 deletions src/libraries/LibTurnBasedGame.sol
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import {IERC20} from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import {IERC1155} from "@openzeppelin/contracts/token/ERC1155/IERC1155.sol";
import {IERC721} from "@openzeppelin/contracts/token/ERC721/IERC721.sol";
import {LibArray} from "../libraries/LibArray.sol";
import {LibCoinVending} from "../libraries/LibCoinVending.sol";
import {Math} from "@openzeppelin/contracts/utils/math/Math.sol";

library LibTBG {
Expand All @@ -34,12 +33,14 @@ library LibTBG {
uint256 turnStartedAt;
uint256 registrationOpenAt;
bool hasStarted;
bool hasEnded;
EnumerableSet.AddressSet players;
mapping(address => bool) madeMove;
uint256 numPlayersMadeMove;
mapping(address => uint256) score;
bytes32 implemenationStoragePointer;
bool isOvertime;
address[] leaderboard;
}

struct TBGStorageStruct {
Expand Down Expand Up @@ -129,7 +130,7 @@ library LibTBG {
GameInstance storage _game = _getGame(gameId);
require(gameExists(gameId), "game does not exist");
require(tbg.playerInGame[participant] == gameId, "Not in the game");
require(_game.hasStarted == false, "Cannot leave once started");
require(_game.hasStarted == false || _game.hasEnded == true, "Cannot leave once started");
tbg.playerInGame[participant] = 0;
_game.players.remove(participant);
}
Expand Down Expand Up @@ -246,6 +247,7 @@ library LibTBG {
require(gameId != 0, "startGame->Game not found");
require(_game.players.length() >= tbg.settings.minPlayersSize, "startGame->Not enough players");
_game.hasStarted = true;
_game.hasEnded = false;
_game.currentTurn = 1;
_game.turnStartedAt = block.number;
_resetPlayerStates(_game);
Expand Down Expand Up @@ -295,13 +297,6 @@ library LibTBG {
return _game.hasStarted;
}

function closeGame(uint256 gameId) internal {
TBGStorageStruct storage tbg = TBGStorage();
address[] memory players = getPlayers(gameId);
for (uint256 i = 0; i < players.length; i++) {
tbg.playerInGame[players[i]] = 0;
}
}

function nextTurn(uint256 gameId) internal returns (bool, bool, bool, address[] memory) {
GameInstance storage _game = _getGame(gameId);
Expand All @@ -314,11 +309,13 @@ library LibTBG {
bool _isOvertime = _game.isOvertime;
address[] memory sortedLeaders = new address[](getPlayers(gameId).length);
if (_isLastTurn || _game.isOvertime || isGameOver(gameId)) {
(_isOvertime, sortedLeaders) = isLeadersScoresEqual(gameId);
(_isOvertime, sortedLeaders) = isSortedLeadersEqual(gameId);
_game.isOvertime = _isOvertime;
}
_game.leaderboard = sortedLeaders;
_game.hasEnded = isGameOver(gameId);

return (_isLastTurn, _isOvertime, isGameOver(gameId), sortedLeaders);
return (_isLastTurn, _isOvertime, _game.hasEnded, sortedLeaders);
}

function getDataStorage() internal pure returns (bytes32 pointer) {
Expand Down Expand Up @@ -365,7 +362,7 @@ library LibTBG {
_game.isOvertime = false;
}

function isLeadersScoresEqual(uint256 gameId) internal view returns (bool, address[] memory) {
function isSortedLeadersEqual(uint256 gameId) internal view returns (bool, address[] memory) {
TBGStorageStruct storage tbg = TBGStorage();
(address[] memory players, uint256[] memory scores) = getScores(gameId);

Expand Down Expand Up @@ -397,15 +394,19 @@ library LibTBG {
GameInstance storage _game = _getGame(gameId);
// uint256 proposalIdx = game.playersOngoingProposalIdx[proposer];
uint256 score = 0;
TBGStorageStruct storage tbg = TBGStorage();
for (uint256 i = 0; i < players.length; i++) {
uint256 creditsUsed = 0;
if (!_game.madeMove[players[i]]) {
TBGStorageStruct storage tbg = TBGStorage();
score += tbg.maxQuadraticVote;
creditsUsed = tbg.settings.voteCredits;
} else {
uint256[] memory playerVote = votes[i];
require(playerVote[proposerIdx] == 0, "Voted for himself");
score += playerVote[proposerIdx] * playerVote[proposerIdx];
require(playerVote[i] == 0, "Voted for himself");
score += playerVote[proposerIdx];
creditsUsed += playerVote[proposerIdx] * playerVote[proposerIdx];
}
require(creditsUsed <= tbg.settings.voteCredits, "Quadratic: vote credits overrun");
}
return score;
}
Expand Down
2 changes: 1 addition & 1 deletion src/tokens/RankToken.sol
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ contract RankToken is ERC1155, Ownable, IRankToken {
}

function lockInInstance(address account, uint256 id, uint256 amount) public onlyRankingInstance {
require(balanceOf(account, id) > lockedAmounts[account][id] + amount, "not enough balance");
require(balanceOf(account, id) >= lockedAmounts[account][id] + amount, "not enough balance");
lockedAmounts[account][id] += amount;
emit TokensLocked(account, id, amount);
}
Expand Down
Loading

0 comments on commit b402f98

Please sign in to comment.