Top-down design and decomposition
Top-down design and decomposition is a simple and effective development strategy for projects of any size from a single algorithms to full applications. Start with the ultimate goal and keep breaking it down (decomposing it) into smaller problems until you can solve the small problems and combine them to complete the ultimate goal
- Clearly state the problem (or objective or project)
- Break the problem into the next largest logical pieces
- This could be a list for simpler problems or a diagram for complex problems
- When working in code, these should become comments, functions, objects, and classes
- Repeat for each of those pieces, and the next pieces, until the problems are small enough to code a solution
- Work your way back up completing each of the pieces
It's very much analagous to building something out of legos where you also get to design the pieces.
Below is an example of using this process to build a web-based War card game. War Card Game Repository on Github
War Card Game Example - 1st iteration of process
Task: A web-based War card game player vs. computer
Features and requirements (logical pieces)
- Graphic UI
- Game works and follows War rules
War Card Game Example - 2nd iteration
Task: Game works and follows War rules (this is one of the first two pieces)
Requirements
- Simulate deck of shuffled cards
- Simulate the player's hand
- Simulate computer's hand
- Player can play their next card
- Computer automatically plays next card after player
- Determine winner of round
- Cards added to deck of loser
- Track points
- Determine winner of game
At this point, I'm ready to move to coding. To do that, I will start by outlining these requirements with comments and naming functions to actually carry out the steps. These are just initial ideas and may need to change as the project progresses and I develop a better understanding of how it should work
/** War game */ // Simulate deck of shuffled cards function shuffle() { } // Split deck into player and computer hands function splitDeck() { } // Player plays a card function playerPick() { } // Computer plays a card automatically after player function computerPick() { } // Check who won the round function getRoundWinner() { } // Add cards to the loser's deck function giveCardsToLoser() { } // Add points to winner's score function addPoints() { } // Determine winner of game function getGameWinner() { }
War Card Game Example - 3rd iteration
Task: Simulate a deck of cards (seems like the most logical place to start)
Requirements
- A way to represent each card
- A place to store the deck
- 52 cards in the deck
- A way to randomize the deck
These are small enough that I can see how to solve them. I will use comments and try to give things descriptive names.
Solution to first two requirements
/** War game */ /*** Simulate deck of shuffled cards */ // Each card will be a string like "2H" for 2 of hearts // A global array is a good way to store the shuffled deck let deck = []; // Rather than list all 52 cards, I will combine values from two arrays to make each card string // card = values[i] + suits[j] const suits = ["H", "C", "S", "D"]; const values = ["2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"];
Solution to third requirement
After testing, I realized that this algorithm puts duplicate cards in the array so I put a TODO comment to remind me to fix that
// Shuffle deck function shuffle() { // Generate 52 random cards and add to the deck for (let n = 0; n < 52; n++) { // Pick rand num from 0 to 3 for suit index and pick random suit let rand = Math.floor(Math.random() * 4); let suit = suits[rand]; // Pick rand num from 0 to 13 for value index and pick random value rand = Math.floor(Math.random() * 13); value = values[rand]; // Concatenate strings to make a card like "QD" let card = value + suit; // Add card to deck list deck.push(card); // TODO: check for duplicates, don't want two of the same card // repeat 52 times for full deck } } // Test by printing deck to console after calling shuffle() shuffle(); console.log(deck);
War Card Game - 4th iteration
Task: To fill deck with random cards without duplicates
// Shuffle deck function shuffle() { // Generate 52 random cards and add to the deck // Clear the deck deck = []; for (let n = 0; n < 52; n++) { // Pick rand num from 0 to 3 for suit index and pick random suit let rand = Math.floor(Math.random() * 4); let suit = suits[rand]; // Pick rand num from 0 to 13 for value index and pick random value rand = Math.floor(Math.random() * 13); value = values[rand]; // Concatenate strings to make a card like "QD" let card = value + suit; // Add card to deck list // Only add card to deck if it isn't already there if (deck.indexOf(card) >= 0) { // Card is on the list already // Decrement n because the card shouldn't be counted n--; } else { deck.push(card); } // repeat 52 times for full deck } } // Test by printing deck to console after calling shuffle() shuffle(); console.log(deck);
War Card Game - 5th iteration
Tasks: 1. Simulate the player's hand, 2. Simulate computer's hand, 3. Simulate discard pile
Requirements
- A place (variable) to store the deck for each hand
- A way to split the shuffled deck between the player and computer hands to start the game
// Global arrays representing the different decks to hold the strings representing the cards let playerHand = []; let compHand = []; let discardPile = [];
This algorithm to split the cards is equivalent to "cutting" the deck exactly in half
// Split deck into player and computer hands function splitDeck() { // Clear the player's hand to start clean playerHand = []; // Put the first half of the deck in the player's hand for (let i = 0; i < deck.length / 2; i++){ playerHand.push(deck[i]); } // Clear the computer's hand compHand = []; // Put the second half in the computer's hand for (let i = deck.length / 2; i < deck.length; i++){ compHand.push(deck[i]); } } // Test split deck shuffle(); splitDeck(); console.log(playerHand); console.log(compHand);
War Card Game - 6th iteration
Task: Graphic UI
I am to the point in the script where the next logical step is to start putting together the user interface and
developing the actual gameplay.
Requirements
- A UI element to start a new game
- UI elements for player and computer decks
- UI elements for cards in play
- Animations and messages for game events
Animations and messages needs to be broken down further, but I will create basic versions of the other requirements in this step and refine them later.
I used divs to represent the basic elements of the game and named each element I anticipate needing to find in JavaScript.
<body> <header> <h1>War</h1> </header> <main> <div id="game-area"> <div id="player-deck" onclick="playerPick()" class="card-back"> + +<br>+ + </div> <div id="player-card" class="card-front"> <h3 id="player-card-value">3</h3> </div> <div id="comp-card" class="card-front"> <h3 id="comp-card-value">5</h3> </div> <div id="comp-deck" class="card-back"> + +<br>+ + </div> </div>> <button id="new-button" class="card-back" onclick="start()">Deal</button> </main> </body>
Some CSS styles to make it look somewhat like cards.
body { margin: 0px; background-color: green; } h1 { font-size: 60px; color: red; text-align: center; } #game-area { display: flex; flex-direction: row; justify-content: space-around; padding: 20px; } #new-button { font-size: 60px; margin: 40px auto; } .card-front, .card-back, .card-empty { box-sizing: border-box; display: flex; flex-direction: column; justify-content: center; align-items: center; width: 200px; height: 300px; font-size: 100px; border: 1px solid black; border-radius: 10px; } .card-front { background-color: white; } .card-empty { background-color: inherit; } .card-back { background-color: darkblue; color: cornflowerblue; } #new-button:hover, #player-deck:hover { border: 2px solid cornflowerblue; cursor: pointer; }
Screenshot of user interface
War Card Game - 7th iteration
Task: Player can play their next card
Requirements
- Triggerd by click on player's deck
- Card is next card from player deck
- UI updates with correct card in the correct location
- Triggers computer to take a turn
// Player picks a card - this will be triggered from an onclick event function playerPick() { // Pick next card from player deck playerCard = playerHand.pop(); // Update UI // Get card value and suit from string let value = "", suit = ""; if (playerCard.length == 3) { // 10 cards take two characters for a total of 3 with the suit value = playerCard.substring(0, 2); suit = playerCard.substring(2, 3); } else { value = playerCard.substring(0, 1); suit = playerCard.substring(1, 2); } // Testing that value and suit values are correctly found console.log(value + " of " + suit); // Set properties on player card element let valueElement = document.getElementById("player-card-value"); valueElement.innerHTML = value; if (suit == "H" || suit == "D") { valueElement.style.color = "red"; } else { valueElement.style.color = "black"; } // TODO: Set suit image // Trigger computer move computerPick(); }
War Card Game - 8th iteration
Task: Computer automatically plays card
Requirements
- Card is played after player's turn
- Card is next card from computer deck
- UI updates with correct card in the correct location
Call the computerPick()
function at the end of the playerPick()
function
function playerPick() { /* other code not shown */ // Trigger computer move computerPick(); }
Computer card pick algorithm is essentially the same as the algorithm for the playerPick()
function.
// Computer picks a card function computerPick() { // Pick next card from comp deck compCard = compHand.pop(); // Update UI // Get card value and suit from string same algorithm as in playerPick() let value = "", suit = ""; if (compCard.length == 3) { value = compCard.substring(0, 2); suit = compCard.substring(2, 3); } else { value = compCard.substring(0, 1); suit = compCard.substring(1, 2); } // Set properties on player card element let valueElement = document.getElementById("comp-card-value"); valueElement.innerHTML = value; if (suit == "H" || suit == "D") { valueElement.style.color = "red"; } else { valueElement.style.color = "black"; } // TODO: Set suit image }
War Card Game - 9th iteration
Task: Determine winner of round
Requirements
- Winner based on highest face value with Aces high
- Tie results in playing a second round, loser takes all cards in play
- Round scored automatically after computer takes turn