What I wanted to solve
I wasn't trying to invent a better Blackjack. I was trying to answer a more basic question: can I take rules I understand in my head and turn them into something that actually runs in a browser?
Up to that point, HTML and CSS felt like drawing. JavaScript was the first time the page could decide things—deal a card, disable a button, declare a winner. Blackjack was a good first problem because the rules are strict enough to force real logic, but small enough to finish.
What I built
A single-page Blackjack game:
- player vs computer
- new game / hit / stand
- live score updates
- card images rendered into the DOM
- winner decided with a simple alert
The UI was intentionally plain: Bootstrap buttons, a black header, overlapping card images. The interesting part wasn't the design—it was getting the game state to behave correctly.
How it works
The whole game lives inside a module pattern (miModulo) so the deck, scores, and hands stay private. The page only gets one public method: iniciarJuego().
Deck and cards
Cards are represented as strings like 2C, AH, KS:
- numbers
2–10for pip cards A,J,Q,Kfor specialsC,D,H,Sfor suits
crearDeck() builds the 52-card deck, then Underscore's _.shuffle() randomizes it. Drawing a card is just deck.pop().
That encoding was useful because the same string drives both scoring (valorCarta) and the image path (assets/cartas/${carta}.png).
Scoring
Face cards are worth 10. Aces start as 11 and soft-adjust down to 1 when the hand would bust:
while (puntos > 21 && ases > 0) {
puntos -= 10;
ases--;
}
That was the first “non-obvious” rule I had to encode carefully. Showing the raw sum of card values isn't enough—Blackjack scoring needs memory of how many aces are still soft.
Turns
The player loop is event-driven:
- Pedir carta draws for the player, updates points, renders the card.
- Bust immediately ends the round.
- Hitting exactly 21 (or pressing Detener) hands control to the computer.
- The computer keeps drawing while under the player's score.
determinarGanador()compares the two totals.
The computer turn is a do...while, not a second human-style click loop. Once the player stops, the house plays out automatically.
UI state
Buttons start disabled until a new game begins. After the player busts or stands, hit/stand lock again. Scores live in <small> tags next to each player title, and cards are appended as <img> nodes with a negative margin so they overlap.
What I learned
- Separating state (deck, hands, scores) from rendering (DOM updates) made the game easier to reason about, even in vanilla JS.
- Small rules compound: ace soft-counting, turn order, and button enabling/disabling all have to stay consistent or the game feels broken.
- A module IIFE is a crude but useful way to avoid dumping everything into the global scope.
- Finishing something imperfect teaches more than planning something perfect. This project exists because I shipped it.
Looking back, the code is early-career code—minified build checked in, alerts for outcomes, Bootstrap for layout. But it was the first time I connected interface, state, and rules into one working system. Everything else started there.
