<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: VS</title>
    <description>The latest articles on DEV Community by VS (@whugithubcodeacademy).</description>
    <link>https://dev.to/whugithubcodeacademy</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4231747%2Fac7ddd1e-e01a-43ad-ba0c-981b38c91886.png</url>
      <title>DEV Community: VS</title>
      <link>https://dev.to/whugithubcodeacademy</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/whugithubcodeacademy"/>
    <language>en</language>
    <item>
      <title>I Built Blackjack in My Terminal with Python (and the Aces Were the Hardest Part)</title>
      <dc:creator>VS</dc:creator>
      <pubDate>Sat, 07 Nov 2026 08:43:57 +0000</pubDate>
      <link>https://dev.to/whugithubcodeacademy/i-built-blackjack-in-my-terminal-with-python-and-the-aces-were-the-hardest-part-4mi5</link>
      <guid>https://dev.to/whugithubcodeacademy/i-built-blackjack-in-my-terminal-with-python-and-the-aces-were-the-hardest-part-4mi5</guid>
      <description>&lt;p&gt;How I planned, built, tested and version-controlled a playable card game in about 300 lines of Python.&lt;/p&gt;

&lt;p&gt;I wanted a first portfolio project that was small enough to finish but still had some real logic in it. A to-do list felt boring. A full web app felt like too much. Blackjack was a good middle ground: everyone knows the rules, it needs the player to make choices, and it has a couple of rules that are trickier to code than they look.&lt;/p&gt;

&lt;p&gt;The result is a game you play in your terminal against a computer dealer. You bet chips, hit, stand or double down, and try to beat the dealer without going over 21.&lt;/p&gt;

&lt;p&gt;👉 Code: github.com/rtfgbnjko/terminal-blackjackv2&lt;/p&gt;

&lt;p&gt;A hand of blackjack being played in the terminal&lt;/p&gt;

&lt;p&gt;Step 1: Brainstorming&lt;/p&gt;

&lt;p&gt;Before writing any code I listed what the game had to do, then split it into "must have" and "nice to have".&lt;/p&gt;

&lt;p&gt;Must have&lt;/p&gt;

&lt;p&gt;A shuffled deck to deal from&lt;br&gt;
A hand total that handles aces correctly&lt;br&gt;
The player choosing to hit or stand using input()&lt;br&gt;
A dealer who follows fixed rules&lt;br&gt;
Working out who won&lt;/p&gt;

&lt;p&gt;Nice to have&lt;/p&gt;

&lt;p&gt;Betting with chips&lt;br&gt;
Doubling down&lt;br&gt;
Drawing the cards as ASCII art&lt;br&gt;
Stats at the end of the session&lt;/p&gt;

&lt;p&gt;I built the "must have" list first so I always had a working game, then added the extras one at a time.&lt;/p&gt;

&lt;p&gt;Step 2: Splitting logic from input&lt;/p&gt;

&lt;p&gt;The most useful decision I made was keeping the game rules apart from the terminal input and output. The top of blackjack.py has three classes that never call print() or input():&lt;/p&gt;

&lt;p&gt;Card: a rank and a suit, plus its blackjack value&lt;br&gt;
Shoe: several decks shuffled together (casinos usually use six)&lt;br&gt;
Hand: the cards someone is holding, plus their total&lt;/p&gt;

&lt;p&gt;Everything that talks to the player sits further down. That made the rules easy to test, because I could check a hand's value without typing anything into a prompt.&lt;/p&gt;

&lt;p&gt;Step 3: The ace problem&lt;/p&gt;

&lt;p&gt;An ace is worth 11, unless that would push you over 21, in which case it's worth 1. A hand can have several aces, and each one can flip separately.&lt;/p&gt;

&lt;p&gt;My first attempt counted every ace as 1 and then tried to add 10 "if it fit". It worked until I got two aces. The cleaner way is the other way round: count every ace as 11, then while the hand is over 21 and there's still an ace counted as 11, take 10 off.&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
def _total_and_soft_aces(self):&lt;br&gt;
    total = sum(card.value for card in self.cards)&lt;br&gt;
    aces_as_eleven = sum(1 for card in self.cards if card.rank == "A")&lt;br&gt;
    while total &amp;gt; 21 and aces_as_eleven:&lt;br&gt;
        total -= 10&lt;br&gt;
        aces_as_eleven -= 1&lt;br&gt;
    return total, aces_as_eleven&lt;/p&gt;

&lt;p&gt;The leftover count also says whether the hand is "soft" (an ace still counting as 11). A+6 is a soft 17. That matters because you can safely hit a soft hand.&lt;/p&gt;

&lt;p&gt;Step 4: Making input() safe&lt;/p&gt;

&lt;p&gt;input() gives back whatever the user typed, and people type all sorts of things. I wrapped it in a few small helpers:&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
def ask_choice(prompt, choices):&lt;br&gt;
    while True:&lt;br&gt;
        answer = ask(prompt)&lt;br&gt;
        if answer in choices:&lt;br&gt;
            return choices[answer]&lt;br&gt;
        print("  Sorry, I didn't get that. Try again.")&lt;/p&gt;

&lt;p&gt;choices is a dictionary, so h and hit both mean "hit". The betting prompt checks that the bet is a whole number you can afford, and it also accepts all and q. Pressing Ctrl+C prints a goodbye message instead of a wall of error text.&lt;/p&gt;

&lt;p&gt;Step 5: The dealer and the payouts&lt;/p&gt;

&lt;p&gt;The dealer has no choices to make. They hit on 16 or less and stand on 17 or more, which is one line of code:&lt;/p&gt;

&lt;p&gt;python&lt;br&gt;
def dealer_should_hit(hand):&lt;br&gt;
    return hand.value &amp;lt; 17&lt;/p&gt;

&lt;p&gt;Working out the winner is a list of checks, and the order matters. A player who busts loses even if the dealer busts afterwards. A dealer blackjack beats a player's 21 made with three cards. A natural blackjack pays 3 to 2, so a 10-chip bet wins 15.&lt;/p&gt;

&lt;p&gt;Step 6: Testing&lt;/p&gt;

&lt;p&gt;I wrote 20 unit tests with Python's built-in unittest. They cover the cases that are easy to get wrong: two aces (12), four aces (14), A+6+9 (16, not 26), blackjack against blackjack, and the dealer standing on a soft 17.&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
python3 -m unittest -v&lt;/p&gt;

&lt;p&gt;I also added a --seed option so the shuffle can be repeated exactly. That made bugs much easier to reproduce, because I could replay the same game.&lt;/p&gt;

&lt;p&gt;Step 7: Git and the command line&lt;/p&gt;

&lt;p&gt;The whole project lives in a Git repository. I committed after each working step: README first, then the game, then the tests, then this post. That way I could always get back to a version that worked. The basic loop:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
git add blackjack.py&lt;br&gt;
git commit -m "Add game loop with betting and player input"&lt;br&gt;
git push&lt;/p&gt;

&lt;p&gt;I also used the command line for everything else: making the folder, running the game, running the tests, and moving around with cd and ls.&lt;/p&gt;

&lt;p&gt;Conclusion: what I learned&lt;br&gt;
Plan before coding. The must-have list kept me from getting lost in extras.&lt;br&gt;
Keep logic separate from input/output. It made testing easy and the code easier to read.&lt;br&gt;
Edge cases are where the bugs are. Nearly every bug was about aces or the order of the win checks.&lt;br&gt;
Small commits save you. When something broke, I could see exactly what had changed.&lt;/p&gt;

&lt;p&gt;Next, I want to add splitting pairs, the obvious missing feature. After that I'd like to save the chip count between sessions and add a hint that tells you the "basic strategy" move for your hand.&lt;/p&gt;

&lt;p&gt;If you want to try it, clone the repo and run python3 blackjack.py. Good luck, and remember: the house always wins... eventually.&lt;/p&gt;

</description>
      <category>beginners</category>
      <category>cli</category>
      <category>python</category>
      <category>sideprojects</category>
    </item>
  </channel>
</rss>
