Blackjack and Burn
Last updated
Last updated
[
{
"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"]
});
}
}