0

I'm writing the card game "war". my classes:

Main DeckOfCards and Card:

import java.util.*;

public class DeckOfCards
{
        private ArrayList<Card> deck;
        private static final Random randomNumbers=new Random();

        public DeckOfCards()
        {
                deck=new ArrayList<Card>();
        }
        public DeckOfCards(int cardsFaces) //normal deck has 4 faces
        {
                deck=new ArrayList<Card>();
                for (int i=0;i<cardsFaces;i++)
                        for (int j=2;j<=14;j++)
                        {
                                deck.add(new Card(j));                         
                        }
        }
        public void shuffle()
        {
                for(int first=0;first<deck.size();first++)
                {
                        int second =randomNumbers.nextInt(deck.size()-1);
                        Card temp=deck.get(first);
                        deck.set(first, deck.get(second));
                        deck.set(second, temp);
                }
        }
        public Card dealCard()
        {
                if (deck.isEmpty())
                {
                        System.out.println("trying to deal from empty deck");
                        return null;
                }
                else
                        return deck.remove(0);

        }
        public ArrayList<Card> getDeck()
        {
                return deck;
        }
        public String toString()
        {
                String s="";
                for (int i=0;i<deck.size();i++)
                {
                        s=s+deck.get(i)+" ";
                }
                return s;
        }
        public boolean isEmpty()
        {
                return (deck.isEmpty());
        }
        public void addCard(Card c)
        {
                deck.add(c);
        }
        public int size()
        {
                return deck.size();
        }
}

public class Card
{
        private int value;//2-14 , 14 is ace.
        public Card(int value)
        {
                this.value=value;
        }
        public int getValue()
        {
                return value;
        }
        public String toString()
        {
                return Integer.toString(value);
        }
}

public class Main
{

        public static void main(String[] args)
        {
                DeckOfCards gameDeck =new DeckOfCards(1);
                gameDeck.shuffle();
                DeckOfCards p1Deck=new DeckOfCards();//player 1 deck
                DeckOfCards p2Deck=new DeckOfCards(); //player 2 deck
                DeckOfCards sideDeck=new DeckOfCards();
                Card p1Card;
                Card p2Card;
                boolean gameOver=false;
                while(!gameDeck.isEmpty()) //dealing cards to players
                {
                        p1Deck.addCard(gameDeck.dealCard());
                        p2Deck.addCard(gameDeck.dealCard());
                }
                while(gameOver==false)
                {
                        p1Card=p1Deck.dealCard();
                        p2Card=p2Deck.dealCard();
                        System.out.println("p1card="+p1Card.getValue()+" p2card="+p2Card.getValue());
                        if(p1Card.getValue()>p2Card.getValue()) //player 1 won the cards
                        {
                                p1Deck.addCard(p1Card);
                                p1Deck.addCard(p2Card);
                                while(!sideDeck.isEmpty())
                                        p1Deck.addCard(sideDeck.dealCard());
                                System.out.println("player 1 won the cards,deck sizes: p1:"+p1Deck.size()+" p2:"+p2Deck.size());
                        }
                        if(p2Card.getValue()>p1Card.getValue()) //player 2 won the cards
                        {
                                p2Deck.addCard(p1Card);
                                p2Deck.addCard(p2Card);
                                while(!sideDeck.isEmpty())
                                        p2Deck.addCard(sideDeck.dealCard());
                                System.out.println("player 2 won the cards,deck sizes: p1:"+p1Deck.size()+" p2:"+p2Deck.size());
                        }
                        while(p1Card.getValue()==p2Card.getValue()) //war
                        {
                                System.out.println("war");
                                sideDeck.addCard(p1Card);
                                sideDeck.addCard(p2Card);
                                if(p1Deck.size()>3 && p2Deck.size()>3)
                                {
                                        for(int i=0;i<2;i++)
                                        {
                                                sideDeck.addCard(p1Deck.dealCard());
                                                sideDeck.addCard(p2Deck.dealCard());
                                        }
                                }
                                else
                                {
                                        winner(p1Deck,p2Deck,true);
                                }

                        }
                        if(winner(p1Deck,p2Deck,false))
                                gameOver=true;
                }

        }
        public static boolean winner(DeckOfCards p1Deck,DeckOfCards p2Deck,boolean war)
        {
                if(p1Deck.isEmpty()|| p1Deck.isEmpty())
                {
                        if(p1Deck.isEmpty())
                                System.out.println("player 2 is the winner");
                        else
                                System.out.println("player 1 is the winner");
                        return true;
                }
                if (p1Deck.size()<3 && p2Deck.size()<3 && war==true)
                {
                        System.out.println("it's a tie");
                        return true;
                }
                return false;
        }

}

I get NullPointerException and don't know why. when I press on the exception it refers me to the row:

System.out.println("p1card="+p1Card.getValue()+" p2card="+p2Card.getValue());

when tried to debug I understood that sometimes for some reason my dealCard function returns null although the deck I try to deal from isn't empty.

2 outputs for example:

output 1:

trying to deal from empty deck
p1card=10 p2card=5
player 1 won the cards,deck sizes: p1:8 p2:6
p1card=13 p2card=14
player 2 won the cards,deck sizes: p1:7 p2:7
p1card=11 p2card=2
player 1 won the cards,deck sizes: p1:8 p2:6
p1card=9 p2card=12
player 2 won the cards,deck sizes: p1:7 p2:7
p1card=6 p2card=7
player 2 won the cards,deck sizes: p1:6 p2:8
p1card=4 p2card=8
player 2 won the cards,deck sizes: p1:5 p2:9
Exception in thread "main" java.lang.NullPointerException
    at mmn11q2.Main.main(Main.java:25)

output 2:

trying to deal from empty deck
p1card=8 p2card=10
player 2 won the cards,deck sizes: p1:6 p2:8
p1card=7 p2card=13
player 2 won the cards,deck sizes: p1:5 p2:9
p1card=6 p2card=4
player 1 won the cards,deck sizes: p1:6 p2:8
p1card=12 p2card=11
player 1 won the cards,deck sizes: p1:7 p2:7
p1card=5 p2card=3
player 1 won the cards,deck sizes: p1:8 p2:6
p1card=2 p2card=14
player 2 won the cards,deck sizes: p1:7 p2:7
Exception in thread "main" java.lang.NullPointerException
    at mmn11q2.Main.main(Main.java:25)
  • You can easily add a new Breakpoint that should stop on NPE being thrown. Then just inspect the state of the application and see what has happened. – Rafal G. Nov 08 '14 at 13:11

2 Answers2

0

Look at your own error messages

The game starts with an invalid state in decks, as shown by "trying to deal from empty deck" in the output at very start.

while(!gameDeck.isEmpty()) //dealing cards to players
    {
                        p1Deck.addCard(gameDeck.dealCard());
                        p2Deck.addCard(gameDeck.dealCard());
    }

Checks if the deck has a card, but attempts to deal two - as the source deck has an odd number of cards, it results in p2Deck having a card entry of None. As soon as this entry is accessed (as the last item in that deck, you get the exception.

Unit tests

An useful generic solution to preventing such problems would be to try proper testing.

For example, have a test for the 'shuffle' method that shuffles a deck repeatedly and then checks if the deck suddenly doesn't contain a Null value as one of the items.

Also, defensive programming helps - each function should verify if the inputs are valid, to prevent bugs in other places to cause invalid state in that class. If your addCard method would check if the argument is a valid card (instead of None), then the bug would be obviously visible.

Have a check within the dealCard function that verifies if it isn't accidentally dealing a Null value from the array - that's another possible source of the error.

DRY

"Do not repeat yourself" is a useful mantra. For example, your code has multiple places like "p1Deck.addCard(gameDeck.dealCard());", and each such place has a separate check if the source deck isn't empty as otherwise would be an error and return a null value. It is a sign that maybe you need a method that tries to pass a card between two decks and simply doesn't change them if one becomes empty.

Peteris
  • 3,281
  • 2
  • 25
  • 40
0

Your dealCard method removes card from the Deck:

public Card dealCard()
{
    if (deck.isEmpty())
    {
        System.out.println("trying to deal from empty deck");
        return null;
    }
    else
        return deck.remove(0); // <<<< here

}

So after some dealCard calls, the deck is empty. And dealCard returns null when the Deck is empty (see above), hence the NPE.