1

I'm writing a program that should play the game HiLo. HiLo is a game, where a random number is generated and the player has to guess it. If they guess wrong, they are told if the answer is higher or lower than their guess, then they are allowed to guess again. My program should have the ability to limit the number of guesses if needed, set the range of values the answer can be chosen from (and therefore the range of guesses too). I also need to have the option of selecting whether a human will play the game, or if I will let the computer play, and if so, at what skill level (bad, medium, good).

I've written 3 classes: GameManager, HiLo and Player.

The GameManager class allows multiple games to be played and keeps track of the number of wins.

public class GameManager {

private HiLo game;
private int wins;
private int totalRounds;
private int player;

/**
 * Constructor for objects of class GameManager
 */
public GameManager()
{
    player = 2;
}

public void playGames(int rounds)
{
    int i = 0;
    while(i < rounds) {
        game = new HiLo(player);
        game.play();
        wins += game.getHasWon();
        i++;
    }
    totalRounds += rounds;
}

public void changePlayer(int playerType)
{
    player = playerType;
}
} //This should be indented better, but it won't format properly, apologies

The HiLo class basically sets up and plays an individual game of HiLo.

public class HiLo {

private int guessLimit;
private int lowerLimit;
private int upperLimit;
private int guesses;
private int answer;
private boolean hasWon;
private Player player;
private String result;
/**
 * Constructor for objects of class HiLo
 */
public HiLo(int playerType) //Set default values 
{
    guessLimit = 10;
    lowerLimit = 0;
    upperLimit = 50;
    hasWon = false;
    player = new Player(playerType);
}
public HiLo(int guessLimit, int lowerLimit, int upperLimit, int playerType) //Overload
{
    this.guessLimit = guessLimit;
    this.lowerLimit = lowerLimit;
    this.upperLimit = upperLimit;
    hasWon = false;
    player = new Player(playerType);
}
//Returns lower limit
public int getLowerLimit()
{
    return lowerLimit;
}
//Returns upper limit
public int getUpperLimit()
{
    return upperLimit;
}
//Returns 1 if hasWon is true and 0 if hasWon is false
public int getHasWon()
{
    if(hasWon) {
        return 1;
    } else {
        return 0;
    }
}
//Returns the number of guesses so far
public int getGuesses()
{
    return guesses;
}
//Play a game of HiLo
public void play()
{
    answer = randInt(lowerLimit, upperLimit);
    guesses = 0;
    while(guesses < guessLimit) {
        int guess = player.guess(); //This gets a guess from the player
        result = isCorrect(guess); //Was the guess correct?
        if(result.equals("Win")) {
            break; //If the player has won, break from the while loop
        }
        guesses++;
    }

}
//Checks if the guess was correct
public String isCorrect(int guess)
{
    if(guess == answer) {
        hasWon = true;
        return "Win";
    } else if(guess < answer) {
        lowerLimit = guess;
        return "Higher";
    } else {
        upperLimit = guess;
        return "Lower";
    }

}

public int randInt(int min, int max) 
{
    Random rand = new Random();
    int randomNum = rand.nextInt((max - min) + 1) + min;
    return randomNum;
}
}

The Player Class determines which player is playing (0 = human, 1 = badComp, 2 = medComp, 3 = goodComp) and the strategy they are going to use to guess.

import java.util.Random;
import java.util.Scanner;

public class Player {

private int playerType;
private HiLo game;
private int range;
private int lowestLimit; //This is the starting value of lowerLimit

/**
 * Constructor for objects of class Player
 */
public Player(int playerType)
{
   if(playerType < 0 || playerType > 3) {
       System.out.println("ERROR. Please pick a valid playerType");
   }
   this.playerType = playerType;
   range = 0;
   range = setRange();
   lowestLimit = game.getLowerLimit();
}

public int guess()
{   
    if(playerType == 0) {
        return humanPlayer();
    } else if(playerType == 1) {
        return badComputerPlayer();
    } else if(playerType == 2) {
        return mediumComputerPlayer();
    } else {
        return goodComputerPlayer();
    }
}
//Guess from a good computer player
public int goodComputerPlayer()
{    
   return (game.getUpperLimit() - game.getLowerLimit())/2;
}
//Guess from a medium computer player
public int mediumComputerPlayer()
{
    return randInt(game.getLowerLimit(), game.getUpperLimit());
}
//Guess from a bad computer player
public int badComputerPlayer()
{
    return randInt(lowestLimit, range);
}

public int humanPlayer()
{
    Scanner keyboard = new Scanner(System.in);
    System.out.println("enter an integer");
    int myint = keyboard.nextInt();
    return myint;
}
//Generates a random number in the range maz-min
public int randInt(int min, int max) 
{
    Random rand = new Random();
    int randomNum = rand.nextInt((max - min) + 1) + min;
    return randomNum;
}
//Set the range of values to guess from for bad player
public int setRange()
{
    int r = game.getUpperLimit() - game.getLowerLimit();
    return r;
}
}

When I create an object from the HiLo class and input all the arguments, I get a NullPointException:

java.lang.NullPointerException
at Player.setRange(Player.java:75)
at Player.<init>(Player.java:26)
at HiLo.<init>(HiLo.java:35)

I'm new to both programming and Java, and this is my first NullPointException error. I've read some of the other posts on them, but I can't figure it out.

I also don't think the player class is doing exactly what I want it too. Each new instance of the Player class is conceptually tied to the instance of the HiLo game that creates that instance, because a HiLo game can only have one player at a time. I want the new instance of the Player class to reference the instance of the HiLo class that created it:

lowestLimit = game.getLowerLimit();

I'm trying to set lowestLimit = to the lower limit of the instance of the HiLo game that created this Player instance, however I think that's I'm just referencing the object game created from the HiLo class, rather than the instance that created this Player instance. Sorry if this is confusing, I'm trying my best to explain :)

I do this in quite a few other methods inside the Player class too. I've read something about using reflection to potentially solve this, but I'm really not sure I understand it yet.

Any help and/or thoughts would be greatly appreciated,

Thanks.

EDIT: Just thought I'd let you know that I'm using the BlueJ environment.

Jason C
  • 38,729
  • 14
  • 126
  • 182
JC2188
  • 337
  • 3
  • 17
  • 2
    You call `setRange()` in the constructor of `Player`. The `game` will instance variable will always be `null` at that time because it has not been set. – August Nov 13 '14 at 23:56
  • `game` doesn't seem to be initialised in `Player`... – MadProgrammer Nov 13 '14 at 23:56
  • [Reflection](https://docs.oracle.com/javase/tutorial/reflect/) is not a "solution" for a null pointer exception. That doesn't make sense. NPE's have [distinct causes](https://docs.oracle.com/javase/7/docs/api/java/lang/NullPointerException.html). To solve them, you look at the line in the stack trace, look through that list of causes, figure out which one is applicable and find out why. – Jason C Nov 13 '14 at 23:59
  • @August Thanks, I get why this doesn't work. However I'm not sure if I even need the instance variable game in the Player class, as per the second part of my question, I'm unsure how to reference the instance of HiLo that has created this Player instance. I hope that makes sense? – JC2188 Nov 14 '14 at 00:01
  • Also see http://sscce.org/; creating a minimal example is helpful both for debugging, and for asking questions here. For example, you could strip down as much of your code as possible until you are left with only the error. E.g. the way you get guesses from a human player is irrelevant, etc. – Jason C Nov 14 '14 at 00:01
  • @JasonC perhaps I wasn't clear in the second part of my question. I understand that reflection and the NPE error are separate, the NPE comes from a variable pointing to null. The reflection part refers to the way I have written the classes, as I'm unsure if I have implemented what I wanted to do correctly. – JC2188 Nov 14 '14 at 00:03
  • @JC2188 If it behaves correctly, then you have implemented what you want to do correctly. Currently you are not using reflection, and I'm not sure how you believe you would fit it in here. If you want a general code review, check out http://codereview.stackexchange.com. – Jason C Nov 14 '14 at 00:04
  • @JC2188 The link I posted earlier (now removed) doesn't apply here; I misread your code. Sorry about that. August's answer below has a good strategy. However, I think you should improve your question by either focusing on the NPE, or on the issue of tracking the `HiLo` from the `Player`, one or the other. At the moment, since it is focused significantly more on the NPE, I am tempted to close as a duplicate of http://stackoverflow.com/questions/218384/what-is-a-null-pointer-exception-and-how-do-i-fix-it – Jason C Nov 14 '14 at 00:08
  • 1
    @JasonC August's answer solved both problems. I realise they are different topics, but we've got the answer for both now so... – JC2188 Nov 14 '14 at 00:22
  • @JC Glad you worked it out! :) – Jason C Nov 14 '14 at 01:58

2 Answers2

2

The setRange() method depends on game not being null. Because setRange() is called in the Player's constructor, it will always throw a NullPointerException because the game instance variable has not been initialized.

One way to fix this is to make Player's constructor accept a HiLo parameter, and set game to that value before calling setRange():

public Player(int playerType, HiLo game) {
    this.game = game;
    // ...
}

Now you need to update the player = new Player(playerType); statement to use the correct parameters. Because that statement is in an instance method of HiLo, we can simply pass this as the second parameter:

player = new Player(playerType, this);
August
  • 12,410
  • 3
  • 35
  • 51
  • 1
    Thanks for explaining this I understand why I'm getting an NPE. Also thanks for explaining how to do the second part. Your code has fixed the problem, but I'm getting a ton of other NPE's when I try to play() the game. I'll have a go at fixing them myself this time. Thanks again! – JC2188 Nov 14 '14 at 00:19
0

Here is the setRange code:

public int setRange() {
    int r = game.getUpperLimit() - game.getLowerLimit();
    return r;
}

There is only one possible explanation for that method throwing an NPE. That is - game is null.

Work with that ... figure out why game is null ... and fix it.

Hint: there are two distinct game fields in your code. You are initializing one of them ... but not the other one.


... and should I use reflection?

Reflection is not a solution to NPE's.

Stephen C
  • 698,415
  • 94
  • 811
  • 1,216