Blackjack and Burn
Instructions
Approve AFFECTION™ with spender 0x1d51a6D59D6C1129492527977581705eE988BFA5
Deposit AFFECTION™ using playerDeposit()
Start a game by calling dealNewHand() with your bet amount
Hit or Stand, try to get 21 or as close as possible without going over
Aces will count when the player or dealer begins to bust
Payouts are 3:2
Max bet allowed is 1-500 tokens for now
Features
True RNG using libAtropaMath v1.1
Total wins/losses/burns/incentives tracking
Burn contract wins + incentivize callers
Events emitted for every player action allows for easy implementation with Ethers.js
Demo
Contract Address
0x1d51a6D59D6C1129492527977581705eE988BFA5
Contract Code
[
{
"inputs": [
{
"internalType": "uint256",
"name": "_minBet",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_maxBet",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_incentivesPercent",
"type": "uint256"
}
],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "DoubledDown",
"type": "error"
},
{
"inputs": [],
"name": "GameEnded",
"type": "error"
},
{
"inputs": [],
"name": "GameStarted",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientAllowance",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientBankReserveBalance",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientPlayerBalance",
"type": "error"
},
{
"inputs": [],
"name": "InsufficientPlayerWalletBalance",
"type": "error"
},
{
"inputs": [],
"name": "InvalidBet",
"type": "error"
},
{
"inputs": [],
"name": "NoReentry",
"type": "error"
},
{
"inputs": [],
"name": "NothingToBurn",
"type": "error"
},
{
"inputs": [],
"name": "Standing",
"type": "error"
},
{
"inputs": [],
"name": "TooLate",
"type": "error"
},
{
"inputs": [],
"name": "Unauthorized",
"type": "error"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Blackjack",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Burn",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "DealNewHand",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Deposit",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "DoubleDown",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
}
],
"name": "Hit",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Incentives",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Loss",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
}
],
"name": "Push",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
}
],
"name": "Stand",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Surrender",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Win",
"type": "event"
},
{
"anonymous": false,
"inputs": [
{
"indexed": true,
"internalType": "address",
"name": "from",
"type": "address"
},
{
"indexed": false,
"internalType": "uint256",
"name": "value",
"type": "uint256"
}
],
"name": "Withdraw",
"type": "event"
},
{
"inputs": [
{
"internalType": "address",
"name": "_address",
"type": "address"
},
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
}
],
"name": "bankWithdraw",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
}
],
"name": "bankWithdrawPLS",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "burn",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_RNGContractAddress",
"type": "address"
},
{
"internalType": "uint256",
"name": "_minBet",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_maxBet",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_burnMode",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "_burnIncentivePercent",
"type": "uint256"
}
],
"name": "config",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_betAmount",
"type": "uint256"
}
],
"name": "dealNewHand",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "doubleDown",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "hit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "string",
"name": "",
"type": "string"
}
],
"name": "house",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [],
"name": "playerBalance",
"outputs": [
{
"internalType": "uint256",
"name": "",
"type": "uint256"
}
],
"stateMutability": "view",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
}
],
"name": "playerDeposit",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "uint256",
"name": "_amount",
"type": "uint256"
}
],
"name": "playerWithdraw",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "_address",
"type": "address"
}
],
"name": "setOwner",
"outputs": [],
"stateMutability": "payable",
"type": "function"
},
{
"inputs": [],
"name": "stand",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "surrender",
"outputs": [],
"stateMutability": "nonpayable",
"type": "function"
},
{
"inputs": [],
"name": "viewGameState",
"outputs": [
{
"components": [
{
"internalType": "int256",
"name": "result",
"type": "int256"
},
{
"internalType": "uint256",
"name": "round",
"type": "uint256"
},
{
"internalType": "string[2][]",
"name": "dealerCards",
"type": "string[2][]"
},
{
"internalType": "uint256",
"name": "dealerScore",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "playerBet",
"type": "uint256"
},
{
"internalType": "string[2][]",
"name": "playerCards",
"type": "string[2][]"
},
{
"internalType": "uint256",
"name": "playerScore",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "playerDoubleDown",
"type": "uint256"
},
{
"internalType": "uint256",
"name": "playerStand",
"type": "uint256"
}
],
"internalType": "struct BlackjackAndBurn.gameState",
"name": "",
"type": "tuple"
}
],
"stateMutability": "view",
"type": "function"
},
{
"stateMutability": "payable",
"type": "receive"
}
]
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.26;
interface IERC20 {
function allowance(address _owner, address _spender) external view returns (uint256);
function approve(address _spender, uint256 _value) external returns (bool);
function balanceOf(address _owner) external view returns (uint256);
function decimals() external view returns (uint256);
function transferFrom(address _sender, address _recipient, uint256 _value ) external returns (bool);
function transfer(address _recipient, uint256 _value) external returns (bool);
}
interface IRNG {
function Generate() external returns (uint64);
}
error DoubledDown();
error GameEnded();
error GameStarted();
error InsufficientAllowance();
error InsufficientBankReserveBalance();
error InsufficientPlayerBalance();
error InsufficientPlayerWalletBalance();
error InvalidBet();
error NoReentry();
error NothingToBurn();
error Standing();
error TooLate();
error Unauthorized();
contract BlackjackAndBurn {
uint256 internal locked;
address ownerAddress;
mapping(address => int256) gameResult;
mapping(address => mapping(string => uint256)) gameData;
mapping(address => mapping(string => string[2][])) gameHands;
mapping(string => uint256) public house;
mapping(string => string[]) cards;
mapping(string => uint256) cardValues;
address RNGContractAddress = 0xa96BcbeD7F01de6CEEd14fC86d90F21a36dE2143;
address currencyContractAddress = 0x24F0154C1dCe548AdF15da2098Fdd8B8A3B8151D;
IRNG RNGContract = IRNG(RNGContractAddress);
IERC20 currencyContract = IERC20(currencyContractAddress);
event Blackjack(address indexed from, uint256 value);
event Burn(address indexed from, uint256 value);
event DealNewHand(address indexed from, uint256 value);
event Deposit(address indexed from, uint256 value);
event DoubleDown(address indexed from, uint256 value);
event Hit(address indexed from);
event Incentives(address indexed from, uint256 value);
event Loss(address indexed from, uint256 value);
event Push(address indexed from);
event Stand(address indexed from);
event Surrender(address indexed from, uint256 value);
event Withdraw(address indexed from, uint256 value);
event Win(address indexed from, uint256 value);
struct gameState {
int256 result;
uint256 round;
string[2][] dealerCards;
uint256 dealerScore;
uint256 playerBet;
string[2][] playerCards;
uint256 playerScore;
uint256 playerDoubleDown;
uint256 playerStand;
}
constructor(
uint256 _minBet,
uint256 _maxBet,
uint256 _incentivesPercent
) {
ownerAddress = msg.sender;
house["payoutWin"] = 100;
house["payoutBlackjack"] = 150;
house["minBet"] = _minBet;
house["maxBet"] = _maxBet;
house["wins"] = 0;
house["losses"] = 0;
house["burns"] = 0;
house["burnsMode"] = 1;
house["incentives"] = 0;
house["incentivesPercent"] = _incentivesPercent;
house["currencyDecimals"] = currencyContract.decimals();
cards["ranks"] = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"];
cards["ranksUpT"] = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K"];
cards["ranksUpA"] = ["2", "3", "4", "5", "6", "7", "8", "9"];
cards["suits"] = [unicode"♠", unicode"♥", unicode"♣", unicode"♦"];
cardValues["2"] = 2;cardValues["3"] = 3; cardValues["4"] = 4; cardValues["5"] = 5; cardValues["6"] = 6; cardValues["7"] = 7; cardValues["8"] = 8; cardValues["9"] = 9; cardValues["10"] = 10; cardValues["J"] = 10; cardValues["Q"] = 10; cardValues["K"] = 10; cardValues["A"] = 11;
}
receive() external payable {}
modifier reentrantLock() {
if (locked != 0)
revert NoReentry();
locked = 1;
_;
locked = 0;
}
modifier onlyOwner() {
if (msg.sender != ownerAddress)
revert Unauthorized();
_;
}
function setOwner(address _address) public payable onlyOwner {
// transfer ownership
ownerAddress = _address;
}
function bankWithdrawPLS(uint256 _amount) public payable onlyOwner {
// withdraw pls from the contract
if (address(this).balance < _amount)
revert InsufficientBankReserveBalance();
payable(address(msg.sender)).transfer(_amount);
}
function bankWithdraw(address _address, uint256 _amount) public payable onlyOwner {
// withdraw tokens from the contract
IERC20 token = IERC20(_address);
if (token.balanceOf(address(this)) < _amount)
revert InsufficientBankReserveBalance();
token.transfer(msg.sender, _amount);
}
function dealCard(uint256 _postBlackjackCheck) internal returns (string[2] memory) {
string memory pile;
// dealer's up card is an ace
if (_postBlackjackCheck == 11) {
pile = "ranksUpA";
// dealer's up card is a 10
} else if (_postBlackjackCheck == 10) {
pile = "ranksUpT";
// normal deck
} else {
pile = "ranks";
}
uint64 rng = RNGContract.Generate();
return [
cards[pile][(rng % cards[pile].length)],
cards["suits"][(rng % cards["suits"].length)]
];
}
function burn() public {
// get burnable amount
int256 burnable = int256(house["wins"]) - int256(house["burns"]) - int256(house["incentives"]);
// check if net burn is enabled
if (house["burnsMode"] == 1)
burnable -= int256(house["losses"]);
// only burn if positive number
if (burnable <= 0)
revert NothingToBurn();
// burn any amount above zero
uint256 incentives = (uint256(burnable) * house["incentivesPercent"]) / 100;
uint256 burning = uint256(burnable) - incentives;
unchecked { house["burns"] += burning; }
unchecked { house["incentives"] += incentives; }
currencyContract.transfer(msg.sender, incentives);
currencyContract.transfer(address(0x000000000000000000000000000000000000dEaD), burning);
emit Burn(msg.sender, burning);
emit Incentives(msg.sender, incentives);
}
function calculatePayout(uint256 _value, uint256 _percent) internal pure returns (uint256) {
// amount with percent bonus
return _value + ((_value * _percent) / 100);
}
function config(address _RNGContractAddress, uint256 _minBet, uint256 _maxBet, uint256 _burnMode, uint256 _burnIncentivePercent) public payable onlyOwner {
// change rng contract address
if (_RNGContractAddress != address(0))
RNGContractAddress = _RNGContractAddress;
// change min/max bet amounts
if (_minBet != 0)
house["minBet"] = _minBet;
if (_maxBet != 0)
house["maxBet"] = _maxBet;
// change burn mode and incentive percent
if (_burnMode != 0)
house["burnsMode"] = _burnMode;
if (_burnIncentivePercent != 0)
house["incentivesPercent"] = _burnIncentivePercent;
}
function countAces(string memory _who) internal returns (bool) {
// subtract one ace at a time until the score is below or equal to 21
string memory whoAces = string.concat(_who, "Aces");
uint256 aces = gameData[msg.sender][whoAces];
for (uint256 a; a < aces; ++a) {
gameData[msg.sender][whoAces] -= 1;
gameData[msg.sender][_who] -= 10;
if (gameData[msg.sender][_who] <= 21)
break;
}
// not enough aces
if (gameData[msg.sender][_who] > 21) {
if (keccak256(bytes(_who)) == keccak256(bytes("Dealer"))) {
// dealer busts, player wins
endGame(1);
} else {
// player busts, dealer wins
endGame(-1);
}
return false;
}
return true;
}
function dealCardTo(string memory _who) internal returns (bool) {
// skip if dealing to dealer and dealer has enough
string[2] memory card;
if (keccak256(bytes(_who)) == keccak256(bytes("Dealer"))) {
if (gameData[msg.sender][_who] >= 17) {
// empty card placeholder
gameHands[msg.sender]["Dealer"].push(["", ""]);
return true;
}
// dealer failed natural blackjack and should be given a different card
card = dealCard(gameData[msg.sender]["Dealer"]);
} else {
card = dealCard(0);
}
// put card in hand
gameHands[msg.sender][_who].push(card);
// count if ace was dealt
uint256 cardValue = cardValues[card[0]];
if (cardValue == 11) {
unchecked { gameData[msg.sender][string.concat(_who, "Aces")] += 1; }
}
// count card value
unchecked { gameData[msg.sender][_who] += cardValue; }
if (gameData[msg.sender][_who] > 21)
return false;
return true;
}
function dealNewHand(uint256 _betAmount) public reentrantLock {
if (gameResult[msg.sender] == 255)
revert GameStarted();
if (_betAmount < house["minBet"] || _betAmount > house["maxBet"])
revert InvalidBet();
if (gameData[msg.sender]["Bank"] < _betAmount)
revert InsufficientPlayerWalletBalance();
gameData[msg.sender]["Round"] = 1;
gameData[msg.sender]["Stand"] = 0;
// activate game state for player
gameResult[msg.sender] = 255;
// remember how much the player has bet
gameData[msg.sender]["Bet"] = _betAmount;
// remove the amount from the player's bank balance
gameData[msg.sender]["Bank"] -= _betAmount;
// player is dealt 1 card
dealNewHandTo("You");
// dealer is dealt 1 card
dealNewHandTo("Dealer");
// player is dealt another card
dealCardTo("You");
emit DealNewHand(msg.sender, _betAmount);
// check if player was dealt a blackjack
if (gameData[msg.sender]["You"] >= 21) {
gameData[msg.sender]["You"] = 21;
// flip dealer's hole card
if (dealCardTo("Dealer")) {
if (gameData[msg.sender]["Dealer"] < 21)
// dealer failed to push
endGame(2);
} else {
// it's a draw
gameData[msg.sender]["Dealer"] = 21;
endGame(0);
}
} else {
// roll to check if dealer got a natural blackjack
string[2] memory card = dealCard(0);
if (gameData[msg.sender]["Dealer"] + cardValues[card[0]] >= 21) {
gameData[msg.sender]["Dealer"] = 21;
gameHands[msg.sender]["Dealer"].push(card);
endGame(-1);
}
}
}
function dealNewHandTo(string memory _who) internal {
// set the initial game state
gameHands[msg.sender][_who] = [dealCard(0)];
string memory whoAces = string.concat(_who, "Aces");
gameData[msg.sender][whoAces] = 0;
gameData[msg.sender]["DoubleDown"] = 0;
// count if ace was dealt
uint256 cardValue = cardValues[gameHands[msg.sender][_who][0][0]];
if (cardValue == 11) {
unchecked { gameData[msg.sender][whoAces] += 1; }
}
gameData[msg.sender][_who] = cardValue;
}
function doubleDown() public {
// double the player's bet
uint256 bet = gameData[msg.sender]["Bet"];
if (gameResult[msg.sender] != 255)
revert GameEnded();
if (gameData[msg.sender]["Round"] != 1)
revert TooLate();
if (gameData[msg.sender]["Bank"] < bet)
revert InsufficientPlayerWalletBalance();
gameData[msg.sender]["Bank"] -= bet;
gameData[msg.sender]["Bet"] = bet * 2;
gameData[msg.sender]["DoubleDown"] = 1;
emit DoubleDown(msg.sender, gameData[msg.sender]["Bet"]);
// take a card
hit();
}
function endGame(int256 _result) internal {
// end the game and calculate rewards
uint256 payout;
if (_result == 2) {
// blackjack
payout = calculatePayout(
gameData[msg.sender]["Bet"],
house[string.concat("payoutBlackjack")]
);
unchecked { house["losses"] += payout; }
emit Blackjack(msg.sender, payout);
} else if (_result == 1) {
// win
payout = calculatePayout(
gameData[msg.sender]["Bet"],
house[string.concat("payoutWin")]
);
unchecked { house["losses"] += payout; }
emit Win(msg.sender, payout);
} else if (_result == 0) {
// push
payout = gameData[msg.sender]["Bet"];
emit Push(msg.sender);
} else if (_result == -1) {
// loss
payout = 0;
unchecked { house["wins"] += gameData[msg.sender]["Bet"]; }
emit Loss(msg.sender, gameData[msg.sender]["Bet"]);
} else if (_result == -2) {
// surrender
payout = gameData[msg.sender]["Bet"] / 2;
unchecked { house["wins"] += payout; }
emit Surrender(msg.sender, payout);
}
// re-add balance to player's contract wallet if they won
if (payout != 0) {
unchecked { gameData[msg.sender]["Bank"] += payout; }
}
// store the result of the game
gameResult[msg.sender] = _result;
}
function hit() public {
if (gameResult[msg.sender] != 255)
revert GameEnded();
if (gameData[msg.sender]["Stand"] != 0)
revert Standing();
if (gameData[msg.sender]["DoubleDown"] != 0)
if (gameData[msg.sender]["Round"] > 1)
revert DoubledDown();
unchecked { gameData[msg.sender]["Round"] += 1; }
emit Hit(msg.sender);
// check if dealer's first turn and flip their hole card
if (gameHands[msg.sender]["Dealer"].length == 1)
dealCardTo("Dealer");
// deal cards
string[2] memory players = ["You", "Dealer"];
for (uint256 p; p < players.length; ++p)
if (!dealCardTo(players[p]))
if (!countAces(players[p]))
return;
// calculate the score
scoreGame();
}
function playerBalance() public view returns (uint256) {
// show player's deposits and wins/losses
return gameData[msg.sender]["Bank"];
}
function playerDeposit(uint256 _amount) public reentrantLock {
// deposit player's tokens to the contract
if (currencyContract.allowance(msg.sender, address(this)) < _amount)
revert InsufficientAllowance();
if (currencyContract.balanceOf(msg.sender) < _amount)
revert InsufficientPlayerBalance();
// transfer inbound
currencyContract.transferFrom(msg.sender, address(this), _amount);
unchecked { gameData[msg.sender]["Bank"] += _amount; }
emit Deposit(msg.sender, _amount);
}
function playerWithdraw(uint256 _amount) public reentrantLock {
// withdraw player's tokens from the contract
if (currencyContract.balanceOf(address(this)) < _amount)
revert InsufficientBankReserveBalance();
if (gameData[msg.sender]["Bank"] < _amount)
revert InsufficientPlayerWalletBalance();
// transfer outbound
currencyContract.transfer(msg.sender, _amount);
gameData[msg.sender]["Bank"] -= _amount;
emit Withdraw(msg.sender, _amount);
}
function scoreGame() internal {
// player stands
if (gameData[msg.sender]["Stand"] == 1) {
// dealer stands
if (gameData[msg.sender]["Dealer"] >= 17)
// player has won
if (gameData[msg.sender]["You"] > gameData[msg.sender]["Dealer"])
endGame(1);
// game ends in a push
if (gameData[msg.sender]["Dealer"] == gameData[msg.sender]["You"])
endGame(0);
// dealer has won
if (gameData[msg.sender]["Dealer"] > gameData[msg.sender]["You"])
endGame(-1);
} else {
// dealer busts
if (gameData[msg.sender]["Dealer"] > 21)
endGame(1);
// player busts
if (gameData[msg.sender]["You"] > 21)
endGame(-1);
// dealer stands
if (gameData[msg.sender]["Dealer"] >= 17) {
// player wins
if (gameData[msg.sender]["You"] > gameData[msg.sender]["Dealer"])
endGame(1);
// game ends in a push
if (gameData[msg.sender]["You"] == gameData[msg.sender]["Dealer"])
endGame(0);
// player cannot hit anymore
if (gameData[msg.sender]["DoubleDown"] == 1)
// dealer wins
if (gameData[msg.sender]["Dealer"] > gameData[msg.sender]["You"])
endGame(-1);
}
}
}
function stand() public {
if (gameResult[msg.sender] != 255)
revert GameEnded();
unchecked { gameData[msg.sender]["Round"] += 1; }
gameData[msg.sender]["Stand"] = 1;
emit Stand(msg.sender);
// empty card placeholder
gameHands[msg.sender]["You"].push(["", ""]);
// check if dealer's first turn and flip their hole card
if (gameHands[msg.sender]["Dealer"].length == 1)
dealCardTo("Dealer");
// calculate score
scoreGame();
// deal card to dealer
if (!dealCardTo("Dealer"))
if (!countAces("Dealer"))
return;
// calculate score
scoreGame();
}
function surrender() public {
if (gameResult[msg.sender] != 255)
revert GameEnded();
if (gameData[msg.sender]["Round"] != 1)
revert TooLate();
// player surrenders half of their bet
endGame(-2);
}
function viewGameState() public view returns (gameState memory) {
// state of the game
return gameState({
result: gameResult[msg.sender],
round: gameData[msg.sender]["Round"],
dealerCards: gameHands[msg.sender]["Dealer"],
dealerScore: gameData[msg.sender]["Dealer"],
playerBet: gameData[msg.sender]["Bet"],
playerCards: gameHands[msg.sender]["You"],
playerDoubleDown: gameData[msg.sender]["DoubleDown"],
playerScore: gameData[msg.sender]["You"],
playerStand: gameData[msg.sender]["Stand"]
});
}
}
Last updated