diff --git a/docs/content/guides/developer/app-examples/blackjack.mdx b/docs/content/guides/developer/app-examples/blackjack.mdx index 05116cdae9e8e..a66173b7d9a5c 100644 --- a/docs/content/guides/developer/app-examples/blackjack.mdx +++ b/docs/content/guides/developer/app-examples/blackjack.mdx @@ -4,7 +4,7 @@ title: Blackjack The following documentation goes through an example implementation of the popular casino game blackjack on Sui. This guide walks through its components, providing a detailed look at the module's functions, structures, constants, and their significance in the overall gameplay mechanism. -Building an on-chain blackjack game shares a lot of similarities with a coin flip game. For that reason, this example covers only the smart contracts (Move modules) and frontend logic. +Building an on-chain blackjack game shares a lot of similarities with a coin flip game. This example covers the smart contracts (Move modules), backend logic (using serverless functions), and frontend logic. :::info @@ -15,9 +15,12 @@ You can also find the [full repository for this example here](https://github.com ::: ## Gameplay -In this single-player version of blackjack, the player competes against a dealer, which is automated by the system. The dealer is equipped with a public BLS key that plays a central role in the game's mechanics. Players generate randomness for the game by interacting with their mouse on the screen, after which they place their bet to start the game. Upon initiating the game, the dealer's backend, actively listening for the start transaction, processes it by signing and subsequently dealing two cards to the player and one to itself. -The player has the options to 'Hit' or 'Stand.' Selecting 'Stand' triggers the dealer to draw cards until the total reaches 17 or higher. After the dealer stops, typically at a sum of 17 or more, the smart contract steps in to compare the totals and declare the winner. On the other hand, choosing 'Hit' prompts the dealer to draw an additional card for the player. +In this single-player version of blackjack, the player competes against a dealer, which is automated by the system. The dealer is equipped with a public BLS key that plays a central role in the game's mechanics. The dealer's actions are triggered by HTTP requests to serverless functions. Players generate randomness for the game by interacting with their mouse on the screen, after which they place their bet to start the game. Upon initiating the game, a request is made to the backend (dealer), which processes it by signing and subsequently dealing two cards to the player and one to themselves. + +The player has the option to 'Hit' or 'Stand.' Selecting 'Stand' triggers the dealer to draw cards until the total reaches 17 or higher. After the dealer stops, the smart contract steps in to compare the totals and declare the winner. On the other hand, choosing 'Hit' prompts the dealer to draw an additional card for the player. + +Note that more complex blackjack rules, such as splitting, are considered out of scope for this example, and are therefore not implemented. ## Smart contracts @@ -32,43 +35,87 @@ The [`single_player_blackjack.move`](https://github.com/MystenLabs/blackjack-sui There are also constants for error handling, such as `EInvalidBlsSig`, `EInsufficientBalance`, and others, ensuring robust game mechanics. -Structs like `GameCreatedEvent`, `GameOutcomeEvent`, `HitRequested`, and `StandRequested` capture the various events and actions within a game. `HouseAdminCap` and `HouseData` are crucial for maintaining the house's data, including balance and public key, while the `Game` struct contains all the necessary information about each game, such as player data, cards, and the current status. +Structs like `GameCreatedEvent`, `GameOutcomeEvent`, and `HitDoneEvent` capture the various events and actions within a game. The `HitRequest` and `StandRequest` structs ensure that a move (hit/stand) can be performed by the house only if the player has already requested it. `HouseAdminCap` and `HouseData` are crucial for maintaining the house's data, including balance and public key, while the `Game` struct contains all the necessary information about each game, such as player data, cards, and the current status. The module's functions can be broadly categorized into initialization, game management, and utility functions. The `init` function sets up the house admin capability, while `initialize_house_data` prepares the house for the game by setting up the balance and public key. `place_bet_and_create_game` is the entry point for players to start a new game, involving a bet and random input. The functions `first_deal`, `hit`, and `stand` govern the core gameplay, handling the dealing of cards and player choices. Utility functions like `get_next_random_card` and `get_card_sum` are essential for the game's mechanics, generating random cards and calculating hand values. The module also includes accessors for retrieving various pieces of game and house data. -For testing purposes, the module provides special functions like `set_init_hash_for_testing`, `get_house_admin_cap_for_testing`, and `destroy_for_testing`, ensuring that developers can thoroughly test the game mechanics and house data handling. +For testing purposes, the module provides special functions like `get_house_admin_cap_for_testing`, `player_won_post_handling_for_test` and `house_won_post_handling_for_test`, ensuring that developers can thoroughly test the game mechanics and house data handling. ### Counter module -The next module, [`counter_nft.move`](https://github.com/MystenLabs/blackjack-sui/blob/main/move/blackjack/sources/counter_nft.move), introduces the Counter NFT, a key component in the game's mechanics. It serves as the [Verifiable Random Function (VRF)](../cryptography/ecvrf.mdx) input, ensuring uniqueness in each game. The Counter NFT's value increases after each use, maintaining its distinctiveness for every new game. For first-time players, the creation of a Counter NFT is mandatory. To enhance user experience, the user interface can automate this process by integrating the Counter NFT's creation with the game initiation in a single transaction block. This seamless integration simplifies the process for the player, allowing them to focus on the gameplay. This counter serves the same purpose as the one in the [Coin Flip example](./coin-flip.mdx). +The next module, [`counter_nft.move`](https://github.com/MystenLabs/blackjack-sui/blob/main/move/blackjack/sources/counter_nft.move), introduces the Counter NFT, a key component in the game's mechanics. It serves as the [verifiable random function (VRF)](../cryptography/ecvrf.mdx) input, ensuring uniqueness in each game. The Counter NFT's value increases after each use, maintaining its distinctiveness for every new request. For first-time players, the creation of a Counter NFT is mandatory. To enhance user experience, the user interface can automate this process by integrating the Counter NFT's creation with the game initiation in a single transaction block. This seamless integration simplifies the process for the player, allowing them to focus on the gameplay. This counter serves the same purpose as the one in the [Coin Flip example](./coin-flip.mdx). -## Frontend +## Backend + +The backend is used for all the transactions that are executed by the dealer. The backend can be completely stateless, and for that reason serverless functions are utilized. As a result, the corresponding code lies under the [app/src/app/api/ directory](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/app/api). + +### Directories structure + +The backend code is split in the following sub directories: + +- [games/](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/app/api/games): + The main code of the backend endpoints. Each file that is named `route.ts` is served as an endpoint, in the path defined by the project structure (see [Route Segments on NextJS App Router](https://nextjs.org/docs/app/building-your-application/routing#route-segments) for details) +- [health/](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/app/api/health): A simple healthcheck endpoint to check the API availability +- [helpers/](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/app/api/helpers): Multiple helper functions used in various endpoints +- [services/](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/app/api/services): The core logic of the backend, that signs and executes the transactions on the Sui blockchain +- [utils/](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/app/api/utils): Reusable methods for signing the transactions as the dealer, and sponsoring them with Shinami, to avoid gas coins equivocation + +### High-Level endpoints specification + +| HTTP Method | Path | Description | Request Body | +| ----------- | --------------------- | ------------------------------------------------------------- | ------------------------------------------------- | +| GET | /api/health | A simple healthcheck endpoint to check the API availability | txDigest | +| POST | /api/games/{id}/deal | Executes the initial deal transaction after the game creation | txDigest | +| POST | /api/games/{id}/hit | Executes a hit move | txDigest, id of corresponding HitRequest object | +| POST | /api/games/{id}/stand | Executes a stand move | txDigest, id of corresponding StandRequest object | -The [`BlackjackBoard` component](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/app/Blackjack.tsx), a central element of the blackjack game's frontend module, is structured to create an interactive and responsive gaming experience. Written in React, it integrates several features and functions to handle the game's logic and user interactions effectively. +### Need of usage of waitForTransactionBlock -### State management and setup +An interesting aspect of developing a dApp on Sui, that is coupled to using a full node with/without a load balancer, and requires attention by the developer, is the occurrence of read-after-write and write-after-write cases. -- **State Hooks:** The module uses React's `useState` to manage various game states, including `playerHand`, `dealerHand`, `playerTotal`, `dealerTotal`, and more. These states are vital for tracking the current game status and updating the UI accordingly. +#### Initial deal transaction -- **Randomness Generation:** A unique feature of this component is the generation of randomness based on the user's mouse movements (`handleMouseMove`), used to ensure fair game outcomes. The `userCreatedRandomness` state stores this data, which is then converted to a hexadecimal string (`randomnessHex`) for use in the game. +As an example, in the blackjack game, just after the create-game transaction that the user executes, the dealer executes the initial-deal transaction. This one accepts an argument and modifies the game object, meaning that you are using an object that was just created. -- **Modal for Game Initialization:** A modal is implemented to facilitate the creation of a new game. It captures the randomness and uses `handlePlayGame` from the `useGame` hook to initiate the game with the backend. +To ensure that the game object is available in the Full node that the dealer is using, we need to call waitForTransactionBlock after the create-game transaction. -- **Card Management:** The component generates a deck of cards and maps them to a `Map` object (`cards`) for easy retrieval and display. +#### Hit and stand transactions -### UI components and styling +In the same way, every time you re-fetch the game object in the frontend, make sure that the previous transaction that modified the game object is already available in the Full node. -- **Card Display:** Cards are rendered using a combination of JSX and CSS, displaying the suit symbols and values, with conditional styling (`getSuitColor`) based on the card suit. +This leads to the need of exchanging the `txDigest` between the frontend and the backend, and use `waitForTransactionBlock` on each write-after-write or read-after-write case. -- **Game Status Updates:** The UI provides immediate feedback on game events, such as a bust or game completion, using toasts (`toast`) and loading indicators (`Hearts` from `react-loader-spinner`). +## Frontend + +The [`page` component](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/app/page.tsx), a central element of the blackjack game's frontend module, is structured to create an interactive and responsive gaming experience. Written in React, it integrates several features and functions to handle the game's logic and user interactions effectively. + +### Directories structure + +The frontend is a NextJS project, that follows the NextJS App Router [project structure](https://nextjs.org/docs/app/building-your-application/routing). +The main code of the frontend lies under the [app/src/](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/app/) directory. +The main sub-directories are: + - [app/](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/app/): The main code of the pages and the API endpoints. + - [components/](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/components): The reusable components of the app, organized in sub-directories. + - [hooks/](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/hooks): The custom hooks used in the app. + - [helpers/](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/helpers), [utils/](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/utils), [lib/](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/lib): Multiple helper functions and utilities. + - [types/](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/types): The types/interfaces used in the app. + - [styles/](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/styles): The global css files to style our app. + +### Components and custom hooks for state management -### User interaction and feedback +- **Custom Hooks:** To keep the code as structured as possible, multiple custom hooks are utilized to manage the complex state of the game board at each step. The [useBlackjackGame](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/hooks/useBlackjackGame.ts) custom hook encapsulates the game state and logic, exposing all the required information (with fields such as `game`, `isInitialDealLoading`), and the required functionality (with methods such as `handleCreateGame` and `handleHit`) to display and play the game. Multiple additional custom hooks, such as [useCreateBlackjackGame](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/hooks/useCreateBlackjackGame.ts), and [useMakeMoveInBlackjackGame](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/hooks/useMakeMoveInBlackjackGame.ts) are encapsulating their own piece of state and logic to make the code readable and maintainable. -- **Game Creation and Exploration:** Players can start new games using a dedicated button that triggers the `openNewGameModal` function. Links are also provided for players to view game details on the Sui explorer, enhancing transparency and engagement. +- **Component for Game Initialization:** The [StartGame](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/components/home/StartGame.tsx) component is implemented to facilitate the creation of a new game. It renders the [CollectMouseRandomness](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/components/home/CollectMouseRandomness.tsx) to capture the randomness and uses the `handleCreateGame` function of the [useBlackjackGame](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/hooks/useBlackjackGame.ts) hook to execute the create-game transaction. -- **Mouse Movement Tracking:** The module includes functionality to track and utilize mouse movements, contributing to the game's randomness generation, which is crucial for fair play and unpredictability. +- **Randomness Generation:** Fair game outcomes are also ensured by the [CollectMouseRandomness](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/components/home/CollectMouseRandomness.tsx) component. This component is using the [useMouseRandomness](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/hooks/useMouseRandomness.ts) custom hook, and is in charge of capturing some user's mouse movements and generating a random bytes array. This array is converted to a hexadecimal string (`randomness`) and used in the create-game transaction. + +- **Card Displaying and Management:** The [DealerCards](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/components/home/DealerCards.tsx) and the [PlayerCards](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/components/home/PlayerCards.tsx) components are used to display the total points and the cards owned by the dealer and the player respectively. + +- **Game Actions:** The [GameActions](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/components/home/GameActions.tsx) component is used to display the Hit and Stand buttons, and trigger the corresponding actions, as they are exported by the [useBlackjackGame](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/hooks/useBlackjackGame.ts) hook to execute the corresponding transactions. + +- **BlackjackBanner**: The [BlackjackBanner](https://github.com/MystenLabs/blackjack-sui/blob/main/app/src/components/home/BlackjackBanner.tsx) component is used as a custom view to display when the player wins with a blackjack. ## Comparison: Blackjack and Coin Flip @@ -90,7 +137,7 @@ The [`BlackjackBoard` component](https://github.com/MystenLabs/blackjack-sui/blo - **Backend Processing:** In Blackjack, the dealer is automated (the machine), and the player's actions directly influence game outcomes. In the Coin Flip game, the house (smart contract) plays a more passive role, primarily in initializing and finalizing the game based on the player's guess. -- **Module Structure and Focus:** The Blackjack game focuses more on frontend interactions and real-time updates. The Satoshi Coin Flip game, however, delves deeper into backend logic, with structures like `HouseCap` and `house_data` for initializing and managing game data securely on the blockchain. +- **Module Structure and Focus:** The Blackjack game focuses more on frontend interactions and real-time updates. The Satoshi Coin Flip game, delves into backend logic, with structures like `HouseCap` and `house_data` for initializing and managing game data securely on the blockchain. - **Multi-Version Implementation:** The Satoshi Coin Flip game mentions two versions – one susceptible to MEV attacks and another that is resistant, indicating a focus on security and user experience variations. Such variations aren't implemented in Blackjack. @@ -98,4 +145,4 @@ The [`BlackjackBoard` component](https://github.com/MystenLabs/blackjack-sui/blo The complete app example can be found in [the balackjack-sui repo](https://github.com/MystenLabs/blackjack-sui). -::: \ No newline at end of file +:::