A modern, interactive Blackjack game built with Next.js, React, and TypeScript. This project implements a complete casino-style Blackjack game with a 6-deck shoe, standard game rules, and a polished user interface.
-
Full Blackjack Gameplay
- Standard casino rules with 6-deck shoe
- Player actions: Hit, Stand, Double Down, Split
- Dealer AI that follows standard rules (stands on 17)
- Automatic win/loss detection and payout calculation
-
Game Mechanics
- Initial balance: $1,000
- Betting system with customizable bet amounts
- Payout multipliers:
- Blackjack win: 2.5x
- Normal win: 2x
- Push: 1x (bet returned)
- Loss: 0x
-
User Interface
- Responsive design (mobile and desktop)
- Card animations and visual feedback
- Game state indicators
- Payout rules display
- Game statistics tracking
-
Technical Implementation
- TypeScript for type safety
- React Context API for state management
- Custom game engine with Fisher-Yates shuffle algorithm
- Component-based architecture
- Tailwind CSS for styling
- Framework: Next.js 16.0.3
- React: 19.2.0
- TypeScript: 5.x
- Styling: Tailwind CSS 4.1.9
- Build Tool: Next.js built-in bundler
- Node.js 18+ (recommended: latest LTS version)
- npm, yarn, pnpm, or bun
- Clone the repository:
git clone <repository-url>
cd shuffle-blackjack-clone- Install dependencies:
npm install
# or
yarn install
# or
pnpm install
# or
bun install- Run the development server:
npm run dev
# or
yarn dev
# or
pnpm dev
# or
bun dev- Open http://localhost:3000 in your browser to see the game.
npm run build
npm startshuffle-blackjack-clone/
├── app/ # Next.js app directory
│ ├── page.tsx # Main game page
│ ├── layout.tsx # Root layout
│ └── globals.css # Global styles
├── components/ # React components
│ ├── BetButton/ # Betting button component
│ ├── BetInput/ # Bet amount input
│ ├── CardUI/ # Card display component
│ ├── DoubleButton/ # Double down action
│ ├── GameStats/ # Game statistics display
│ ├── GameTable/ # Main game table container
│ ├── HandUI/ # Hand display component
│ ├── HitButton/ # Hit action button
│ ├── PlayButton/ # Start round button
│ ├── SplitButton/ # Split action button
│ ├── StandButton/ # Stand action button
│ └── ui/ # Reusable UI components
├── constants/ # Game constants
│ └── blackjack.ts # Blackjack rules and constants
├── contexts/ # React Context providers
│ └── game-context.tsx # Game state management
├── lib/ # Core game logic
│ ├── game-engine.ts # Game engine implementation
│ └── utils.ts # Utility functions
└── parts/ # Composite components
├── BetControl/ # Betting controls
├── BlackjackActionsWrapper/ # Action buttons wrapper
├── ControlsWrapper/ # Controls container
└── HandsWrapper/ # Hands display container
- Objective: Get as close to 21 as possible without going over, and beat the dealer's hand.
- Card Values:
- Number cards (2-10): Face value
- Face cards (J, Q, K): 10
- Ace: 1 or 11 (automatically chosen for best hand)
- Dealer Rules: Dealer must hit on 16 and stand on 17 or higher.
- Blackjack: 21 with first two cards (Ace + 10-value card) pays 2.5x.
- Double Down: Double your bet and receive exactly one more card.
- Split: When you have two cards of the same rank, you can split them into two separate hands.
Status: Not implemented (UI component exists but disabled)
Requirements:
-
State Management (
contexts/game-context.tsx)- Add support for multiple player hands in
GameState(currently only singleplayerHand) - Add
activeHandIndexor similar to track which hand is currently being played - Add
START_PLAYER_SPLITandFINISH_PLAYER_SPLITactions - Update reducer to handle split logic:
- Split the two-card hand into two separate hands
- Add a second bet equal to the original bet
- Deduct the second bet from balance
- Deal one card to each split hand
- Handle turn-based play for multiple hands
- Add support for multiple player hands in
-
Game Engine (
lib/game-engine.ts)-
canSplit()method already exists and checks eligibility - Add method to split a hand into two hands (if needed)
- Update comparison logic to handle multiple player hands
- Ensure split hands are evaluated independently
-
-
UI Components
- Update
SplitButton(components/SplitButton/index.tsx) to:- Check if split is available using
gameEngine.canSplit() - Enable/disable based on game state and hand eligibility
- Dispatch split action on click
- Check if split is available using
- Update
HandUI(components/HandUI/index.tsx) to display multiple hands - Update
HandsWrapperto show all player hands with active hand indicator - Ensure actions (Hit, Stand, Double) work on the active hand only
- Update
-
Rules to Implement:
- Split can only be done on initial two-card hand
- Each split hand requires an equal bet
- After splitting, player plays each hand independently
- If both split hands win, player wins both bets
- If dealer has blackjack, all hands lose (including split hands)
- Standard rule: Cannot double after split (already enforced in
canDouble)
Current State:
SplitButtoncomponent exists but is disabled (splitButtonDisabled = true)gameEngine.canSplit()method is implemented and functional- Game state only supports single player hand
Status: Not implemented
Requirements:
-
State Management (
contexts/game-context.tsx)- Add
insuranceBetfield toGameState - Add
insuranceOfferedboolean flag - Add
START_INSURANCEandFINISH_INSURANCEactions - Update
FINISH_INITIAL_DEALto check if dealer shows Ace - Add insurance payout logic when dealer has blackjack
- Add
-
Game Engine (
lib/game-engine.ts)- Add method to check if insurance should be offered (dealer's first card is Ace)
- Add method to calculate insurance payout (2:1 if dealer has blackjack)
- Update result comparison to handle insurance bets separately
-
UI Components
- Create
InsuranceButtoncomponent (components/InsuranceButton/index.tsx) - Create
InsuranceInputor similar for insurance bet amount - Display insurance offer when dealer shows Ace
- Show insurance bet amount in game stats
- Display insurance win/loss result
- Create
-
Rules to Implement:
- Insurance is offered when dealer's up card is an Ace
- Insurance bet is up to half of the original bet
- Insurance pays 2:1 if dealer has blackjack
- Insurance bet is independent of main bet outcome
- If dealer has blackjack:
- Main bet loses (unless player also has blackjack = push)
- Insurance bet wins 2:1
- If dealer does not have blackjack:
- Insurance bet loses
- Main hand continues normally
Current State:
- No insurance-related code exists
- Need to detect dealer Ace after initial deal
- Need to pause game flow to offer insurance before revealing dealer's hole card
npm run lintThe project uses ESLint with Next.js configuration. TypeScript strict mode is enabled for type safety.
Game engine implements standard casino Blackjack rules with a 6-deck shoe system.