Skip to content
This repository has been archived by the owner on Oct 18, 2022. It is now read-only.

Lesson 6

CiocciV edited this page Dec 13, 2018 · 21 revisions

In this lesson, we will learn about Constants, learn about arrays, introduce a new loop called a "for" loop, reinforce the concept of object oriented programming, and give several new programming tips.

We are going to create a deck of cards, and shuffle them.

Assignment 1

As before, create a new package folder called Lesson06 and create two new classes: Card and Shuffler.

Here is some code to get you started with the Card class.

public class Card {
    public static final int ACE = 1;
    //TODO: create constants for Jack, Queen, King
    
    private Suit suit;
    private int number;

    public Card (Suit suit, int number)
    {
        this.suit = suit;
        this.number = number;
    }

    public int getNumber() { return number; }
    public Suit getSuit() { return suit;}
    public String getName()
    {
        String name = getNumberName() + " of " + suit.name();
        return name;
    }

    private String getNumberName() {
        switch (number)
        {
            case ACE:
                return "Ace";
            //TODO: create case statements for Jack, Queen, King
            default:
                return number + ""; //force the integer to a string
        }
    }

    public enum Suit {
        HEARTS,
        DIAMONDS,
        SPADES,
        CLUBS
    }
}

You can see in this code there are two "TODO" statements for you. First we need to create more constants. We need a constant for King = 13, Queen = 12, Jack = 11. In Java, a constant is defined by "static final." We learned before that static means that anyone can use these values without needing to create an instance of the Card object. The "final" keyword means that the value cannot be changed. Most developers use all upper case values for constants, indicating it is a fixed value.

Constants are often used by programmers to give a friendly name to a value that will be used over and over in your code. Doing so makes it easy to change the value everywhere by only changing the value in one spot: where the constant is defined (Example: setting a max speed). It also helps readability of the code. Putting the number 13 in your code is not as nice as using a constant for "KING." The constant makes it very clear what the code means.

Also, be sure to update the switch statement to return a friendly name for King, Queen, and Jack.

Note

You may have noticed that this switch statement does not include any "break" statements. We said in the last lesson that is is VERY important to include a break at the end of each case statement, otherwise execution will fall through to the next case statement. Except in this situation, each case statement "returns" a value. Execution of the getNumberName() method ends right there, therefore execution cannot fall through.

Also note that switch statement has a "default." This runs if none of the other case statements are hit, just like an "else" statement that would go with an "if - else if" statement. Finally, in that same "default" statement, we return the name of the enum for the suit. This can be very handy in robot programming when you use an enum statement to control a piston and want to keep track of the value as an enum of "IN" and "OUT." Being able to print the name of the enum to the log is handy.

In our default for the switch statement, we can just return the number as the name of the card. Concatenating an empty string to a number forces the integer to turn into a string. This is a shortcut many developers use. The method needs to return a String, but the number is an integer. So we need to turn the integer variable into a String so it can be returned.

Some other interesting facts about this class. The constructor in this class looks differently than all other constructors we have written. This one accepts two parameters. In order to create a new Card object, will will need to supply those parameters, like this.

    Card pickACard = new Card(Card.Suit.HEARTS, 5);

Sometimes you need to pass values to an object when it is created. Sometimes, as in this case, you may want to set initial values that can never change. Notice that our properties of this class are all read only. There is no way to set the card number or suit after the object is created. Only "get" methods exist on this object.

One more interesting thing about the constructor is you can see that the parameter names are the same as the class level variable names (suit and suit - number and number). Many programming languages discourage these situations where two variables have exactly the same name, or the same name with different capitalization. It can create bugs and confusion. Unfortunately, this is a common practice in Java, so it is best to grow accustomed to how to deal with it now. When you have variables with the same name, you can identify the class level variables with the word "this." "This" means "this object." A class level variable is part of the object. So the code is setting the class level variable "number" using the value that is in the constructor parameter "number." Don't worry, this can seem confusing, and it is, but you will build a habit of recognizing these situations.

    this.number = number;

Lastly, did you notice this?

    public int getNumber() { return number; }

All the code is on one line. Some Java editors like IntelliJ will automatically format your code to be on 1 line, if the code block inside the curly brackets only contains a single line of code. Some developers like this, others don't. The code runs the same in either format.

Assignment 2

That was a whole bunch of notes and some important trivia we covered about different coding techniques. Go through the code and add comments for the following things in the correct locations.

  • Explain why the constructor has parameters
  • Explain the default part of the switch statement
  • Explain the use of "this" keyword
  • Add comments to anything else that you want to remember, if you ever look back at this code.

Assignment 3

Now that we have cards, let's create a deck of cards. To do this we will use an array. You can think of an array as a list of things (integers, strings, objects, etc). An array of 10 items will have 10 slots in which you can put a thing. In java, arrays are defined with a type, and everything you put in an array must be of the defined type. For example, if you create an array of integers, only integers can be put in the array.

Arrays are defined by using the datatype followed by "[]". The square brackets tell the compiler that this will be an array; "int[]" means this variable will be an integer array.

When you initialize an array, you must specify the size of the it. If you want a variable called "test" to hold an array of 10 integers you would write this:

    int[] test = new int[10];

Let's create a "createDeck" method in our Shuffler class and make an array of cards. This is easy to do.

    public Card[] createDeck() {
        Card[] deck = new Card[52];
        deck[0] = new Card(Card.Suit.CLUBS, 1);
        deck[0] = new Card(Card.Suit.CLUBS, 2);
    }

As you can see, we could go through and put a new card for every card in the 52 card deck like this, but that would be a lot of code. There must be a better way right? Introducing the "for loop." We can use a "for loop" to populate our deck. In fact, we are going to use two for loops, nested one inside the other.

A "for loop" has 3 parts [create counter variable, and initialize it], [check to see if the loop should stop], [increment the counter].

Most of the time you see a for loop like this

    for (int i = 0; i < 10; i++)

Programmers love the letter "i" for loops. Probably because "i" comes from integer. If developers have a double or triple nested loop, the next loop variables are usually j and k. But don't let that stop you from making those variables anything you want. Using x, y, z or larry, moe, curly would be just as legitimate, but not as familiar to other developers.

Also, programmers start counting at 0. The first position in an array is not 1, it is 0. So, our "for loops" also start with 0. The first item is in spot 0 and the last item is in spot 9; this is why our for loop continues as long as i is less than 10, instead of less than or equal to 10.

In the following code we are using two loops, one inside the other. In the outside loop we are looping 4 times to pick a suit, the inner loop is looping for 13 times, one for each card in the suit. You may note that the "cardSuit" variable is set in the outside loop and used in the inside loop.

    public Card[] createDeck() {
        Card[] deck = new Card[52];
        for (int i = 0; i < 4; i++) { //loop 4 times, once for each suit
            Card.Suit cardSuit = Card.Suit.HEARTS; //i will start at 0
            if (i == 1) {
                cardSuit = Card.Suit.DIAMONDS;
            } else if (i == 2) {
                cardSuit = Card.Suit.SPADES;
            } else if (i == 3) {
                cardSuit = Card.Suit.CLUBS;
            }
            
            for(int j = 0; j < 13; j++) { //loop 13 times, once for each number A 2 3 4 5 6 7 8 9 10 J Q K
                int pos = (i * 13) + j; //hearts go in first 13 spots, hearts go in next 13, etc, etc
                int cardNumber = j+1; //j starts at 0, there is no 0 card, so add 1
                deck[pos] = new Card(cardSuit, cardNumber); 
            }
        }
        return deck;
    }

Does this nested "for loop" make sense? If not, ask for help. At this point we have a deck that should contain every card in a standard deck of cards.

Assignment 4

Now that you know how to use arrays, and how to use for loops, your next task is to loop through the deck and print out all the cards to the command line. You can use the method "getName()" on the Card object to get the string you need for System.out.println();

Just like our previous lessons, we want to keep our shuffler class nice and clean. Only calculations, no input or output (IO). Create a Lesson6Runner class and add a run() method, just as you have for the previous lessons. In the run method, you can declare a new variable called "deck" and set it by calling the Shuffler's method "createDeck". Remember, you will also need to create a Shuffler object, before you can call its methods.

In the runner class, create a private method called "printDeck" to print the deck. You will need to pass the deck variable to the method as a parameter. You could just write your print code after creating the deck in the run method, but you will need to print out the deck multiple times soon. Having a method means you can just call the method multiple times; every time you want to print the deck.

Note

We do not want to add "System.out.println()" statements in our Shuffler. Just like with our MultiCalculator class from lesson 3, we want to keep the input/output (IO) in our runner class, and only have logic in our Shuffler class.

Assignment 5

There is one last exercise to get more practice with loops, arrays, and even a little Google practice. Create a "shuffleDeck" method in the Shuffler class to loop through the deck. At each position, randomly pick a card from another position, and swap the cards in the deck. Use google to figure out how to find a random number in Java. Your new shuffle method should accept a parameter of a array of Card objects. It will randomize the array you pass it.

Now call your "printDeck" method after you swap all the cards to print the new order. Check to make sure there are no bugs by looking for duplicate cards.

Note

It is not common to find "for loops" or arrays in most robot code. However, advanced autonomous code will often use arrays to determine all the positions a robot should drive. If you need to keep a log of where the robot has been, then print that later, you will probably be using a for loop or while loop on your robot, to dump that debug log to the console.

Check Your Work

Your final solution should look something like this class diagram. You should only have IO (input & output) in your Runner class. If you have input and print statements in your shuffler or card class, move them to your runner.

Lesson 6 Classes

Bonus

What you have done in this lesson is a very objected oriented way of programming. Each card was it's own object. There are ways you could have created the exact same output to the command prompt without creating an object for each card. You could have just made an array of strings such as "3 of HEARTS" and shuffled that array. But objects offer more flexibility. For example can you write a method that will put the cards back in order? Maybe ordering by numbers then suits? That is much easier to accomplish with objects.