1
\$\begingroup\$

First I would like to thank everyone for whatever help you may offer me. In full disclosure this is a project for a bootcamp class I have. My problem is, and I've asked them about this, that they aren't really teaching anything beyond beginner-level at General Assembly. This seems like the next place to come for the help I require.

I'm looking for mistakes according to best practices. Specifically how I used the classes in JavaScript, but also in general. Any and all feedback, especially with concise explanations will help me actually maximize my learning experience.

This is the code:

const Suits = Object.freeze ({
    CLUBS : ['Clubs', '♣'], 
    DIAMONDS : ['Diamonds', '♦'],    
    HEART : ['Hearts', '♥'],
    SPADES : ['Spades', '♠']
})

const Rank = Object.freeze({
    TWO : ['2', 2],
    THREE : ['3', 3],
    FOUR : ['4', 4],
    FIVE : ['5', 5],
    SIX : ['6', 6],
    SEVEN : ['7', 7],
    EIGHT : ['8', 8],
    NINE : ['9', 9],
    TEN : ['10', 10],
    JACK : ['J', 11],
    QUEEN : ['Q', 12],
    KING : ['K', 13],
    ACE : ['A', 14]

    //I need a way to return the letter value
    ///I need a way 
})


class Card{
    constructor(rank, suit) {
        this.rank = rank
        this.suit = suit
    } 

    get symbol(){
        if(Array.isArray(this.suit)){
            return this.suit[1];
        }
    }

    get symbolWords(){
        if (Array.isArray(this.suit)){
            return this.suit[0];
        }
    }

    get value(){
        if(Array.isArray(this.rank)){
            return this.rank[1];
        }
    }

    get rankString(){
        if(Array.isArray(this.rank)){
            return this.rank[0];
        }
    }

}


class Deck{
    constructor(Rank, Suits){
        this.deck = []
        for(let cardRank in Rank){
            for(let cardSuit in Suits){
                this.deck.push(new Card(Rank[cardRank], Suits[cardSuit]))
            }
        }
        for (let i = this.deck.length - 1; i > 0; i--) {
            let j = Math.floor(Math.random() * i);
            let temp = this.deck[i];
            this.deck[i] = this.deck[j];
            this.deck[j] = temp;
        }
    }

}


class Player{
    constructor(){
        this.hand = [];
        this.gameOver = false;
    }
}


class Game{
    constructor(player1, player2, gameDeck){
        this.player1 = player1;
        this.player2 = player2;
        this.deck = gameDeck;
        this.table = []
    }

    dealCards(){
        while(this.deck.length > 0){
            if (this.deck.length % 2 == 0){
                this.player1.hand.push(this.deck.pop())
            }
            else if(this.deck.length % 2 == 1){
                this.player2.hand.push(this.deck.pop())
            }
            else{
                console.log('Something went wrong with dealing')
            }
        }
    }

    playCards(){
        //deal cards to table
        this.table.push(this.player1.hand.pop())
        this.table.push(this.player2.hand.pop())

   }


   dealWithTie(){
    //each player places three more cards to the table creating a kitty for this round of war
    this.playCards()
    this.playCards()
    this.playCards()
    //show the total kitty for entertainment
    //Deal cards that will be play against each other for this round of evaluation
    this.playCards()
    this.display_cards()
    //Now we want to take the last card (player2 card) and the second to last card(player1 card)
    this.evaluate(this.table[this.table.length-2],this.table[this.table.length-1] )
}

    evaluate(card1, card2){
        //If the round is a tie
        if (card1.value === card2.value){
            console.log(`Tie: ${card1.value}, ${card2.value}`);
            if(this.player1.hand.length >=  4 && this.player2.hand.length >= 4){
                this.dealWithTie()
            }

            else if(this.player1.hand.length < 4){
                while(this.table.length > 0){
                    this.player2.hand.push(this.table.pop())
                }
                while(this.player1.hand.length > 0){
                    this.player2.hand.push(this.player1.hand.pop())
                }
                console.log('Player 1 Loses. Not Enough Cards to complete Round.')
            }

            else if(this.player2.hand.length < 4){
                while(this.table.length > 0){
                    this.player1.hand.push(this.table.pop())
                }
                while(this.player2.hand.length > 0){
                    this.player1.hand.push(this.player2.hand.pop())
                }
                console.log('Player 2 Loses. Not Enough Cards to complete Round.')
            }

            else if(this.player2.hand.length < 4 && this.player2.hand.length < 4){
                console.log('Both Players Tie! Neither player has enough cards to complete round.')
            }

            else {
                console.log("Somehow the tie logic broke")
            }
        }

        //if player 1 wins
        else if(card1.value > card2.value){
            console.log(`PLAYER 1 WINS: ${card1.value}, ${card2.value}`);
            while(this.table.length > 0){
                this.player1.hand.push(this.table.pop());
            }
            
        }

        //if player 2 wins
        else if(card1.value < card2.value){
            console.log(`Player 2 Wins: ${card1.value}, ${card2.value}`);
            while(this.table.length > 0){
                this.player2.hand.push(this.table.pop());
            }
        }

        //if the evaluation has a latent bug
        else{
            console.log("The cards did not properly Evaluate.")
        }

    }

    display_cards(){
        let card1 = this.table[this.table.length - 2];
        let card2 = this.table[this.table.length - 1];
        console.log(`\n\n${card1.rankString} of ${card1.symbol}\nVS.\n${card2.rankString} of ${card2.symbol}\n\n`)
        
    }
    


}

function runGame(){
    newGame.playCards();

    newGame.display_cards()
    newGame.evaluate(newGame.table[0], newGame.table[1]);
    console.log(`Player One Card Count: ${player1.hand.length},\nPlayer Two Card Count: ${player2.hand.length}`);
    
}

let player1 = new Player;
let player2 = new Player;
let gameDeck = new Deck(Rank, Suits);
let newGame = new Game(player1, player2, gameDeck.deck);

newGame.dealCards();


while(player1.hand.length > 0 && player2.hand.length > 0){
    runGame();
}

THE RULES

The Deal The deck is divided evenly, with each player receiving 26 cards, dealt one at a time, face down. Anyone may deal first. Each player places their stack of cards face down, in front of them.

The Play Each player turns up a card at the same time and the player with the higher card takes both cards and puts them, face down, on the bottom of his stack. If the cards are the same rank, it is War. Each player turns up one card face down and one card face up. The player with the higher cards takes both piles (six cards). If the turned-up cards are again the same rank, each player places another card face down and turns another card face up. The player with the higher card takes all 10 cards, and so on.

How to Keep Score The game ends when one player has won all the cards.

\$\endgroup\$
2
  • 3
    \$\begingroup\$ For those of us that haven't heard of this game (or at least not by this name), could you summarise how it's played? \$\endgroup\$ Dec 7, 2022 at 16:15
  • \$\begingroup\$ Editing now. Refer to post for update with rules. \$\endgroup\$ Dec 7, 2022 at 17:05

1 Answer 1

1
\$\begingroup\$

My answer will focus on one fundamental concepts of object-oriented programming: encapsulation.

What is Encapsulation?

In object-oriented programming (OOP), encapsulation refers to the bundling of data with the methods that operate on that data, or the restricting of direct access to some of an object's components.

Encapsulation (computer programming). (2022, September 4). In Wikipedia.

In short: Hide class fields and use methods.

It's about telling a Story

Encapsulation offers multiple benefits. One of it is that we are able to better express the intent of the code. By using methods instead of fields, we can use phrases instead of magic symbols and numbers. To work with methods is somehow like writing a story.

Now let me try to tell a story of the following code snippet:

dealCards(){
   while(this.deck.length > 0){
       if (this.deck.length % 2 == 0){
           this.player1.hand.push(this.deck.pop())
       }
       else if(this.deck.length % 2 == 1){
           this.player2.hand.push(this.deck.pop())
       }
       else{
           console.log('Something went wrong with dealing')
       }
   }
}

My suggestion is:

dealCards(){
    while(this.deck.hasCards()){
        if (this.deck.isEven()){
            this.player1.drawFrom(this.deck)
        }
        else if(this.deck.isOdd()){
            this.player2.drawFrom(this.deck)
        }
        else{
            console.log('Something went wrong with dealing')
        }
    }
}

For this to work, we need to adjust Deck and Player. For simplicity, I just edited Deck:

class Deck {
    // ...
    
    hasCards() {
        return this.deck.length == 0
    }
    
    isOdd() {
        return this.deck.length % 2 == 0
    }
    
    isEven() {
        return !this.isOdd()
    }
}

Now we can see, how we moved the exposed logic into the class and "encapsulate" it.

\$\endgroup\$
2
  • \$\begingroup\$ Just for clarity, we should aim for better encapsulation in most circumstances by creating necessary methods within the classes? Is for easier reading or for better working code? Thank you Again. \$\endgroup\$ Dec 14, 2022 at 1:34
  • \$\begingroup\$ @InfiniteGrasp, encapsulation is a fundamental principle of OOP. That is why we should reach for better encapsulation (if we want to write code in an oop-style). We do this by creating necessary methods within the classes. The code will not work better because of encapsulation. The computer doesn't care for the quality of your code. But instead, encapsulation will support the readability and maintainability of your code. Or, as Martin Fowler would say: "Any fool can write code that a computer can understand. Good programmers write code that humans can understand." \$\endgroup\$
    – Roman
    Dec 15, 2022 at 9:24

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.