The aim of this tutorial is to combine techniques learned so far into a mini, text-based, game. It follows the basic premise of Pokemon Go, however in a much simplified format, with tutors being what you are trying to catch.
In the game of Pokemon go, the player throws Pokeballs (standard, great, or ultra) with the hope of capturing a Pokemon. The better the ball, the higher the capture rate. Balls are a fairly simple type, so we can represent them with an enum in Java.
public enum PokeBall {
STANDARD, GREAT, ULTRA
}
The next stage is to create a class to represent a tutor. In Pokemon Go, the Pokemon have different attributes, such of which are known to the player (such as Hit Points, Combat Points, Name, Weight, Height), and others that are not (for example how likely the Pokemon is to flee when you are attempting to catch it). In order to keep our game simple, for our PokeTutors, we will only consider name, combat points and flee rate
PokeTutor
class PokeTutor {
private String name;
private int cp;
private double fleeRate;
}
name
and cp
variables, create accessor (getter) methods. Note that we will not allow the name or combat points to be changed, so we do not require mutators. The flee rate (as in the real game) will not be publically accessible, so it will not have an accessor or mutatorPokeTutor(String name, int maxCP, double fleeRate) {
this.name = name.trim();
this.fleeRate = fleeRate;
//randomly allocate CP value up to the maximum
Random randomGenerator = new Random(); //ensure you add 'import java.util.Random;' to the top of the file
final int randomCP = randomGenerator.nextInt(maxCP);
//use the Math class's max method to ensure the CP is not less than 10
this.cp = Math.max(10,randomCP);
}
The next stage requires us to consider how a tutor can be captured. In the real game, when a player tries to catch a Pokemon, they can either catch it, they can fail (but try again), or the pokemon can flee.
enum CaptureResult {
FAIL, FLEE, CAUGHT
}
private double getEscapeChance(PokeBall ball){
double escapeChance = cp / 1000.0; //max CP will be 1000
switch (ball) {
case GREAT:
escapeChance *= 0.66666;
break;
case ULTRA:
escapeChance *= 0.33333;
}
return escapeChance;
}
In the above example, if CP was 600, then the escape chance would start at 0.6 (60%). Should a great ball be thrown, then this would be reduced to 0.4 (40%). A standard ball would not lower the escape chance, so it has been omitted from the switch's list of cases. Also note that the method is private as we will not call it from outside the class.private boolean shouldFlee(){
Random random = new Random();
boolean flee = random.nextDouble() < fleeRate;
return flee;
}
CaptureResult attemptCapture(PokeBall ball){
final double escapeChance = getEscapeChance(ball);
Random random = new Random();
final double chance = random.nextDouble();
if (chance > escapeChance){
return CaptureResult.CAUGHT;
}
//we will only get this far if it is not caught
if (shouldFlee()) {
return CaptureResult.FLEE;
} else {
return CaptureResult.FAIL;
}
}
Note that this method is not marked as private as we will call it from other places within the packageIn order for the game to work, we need a few more things to be able to happen - we need to be able to
Andrew,98,0.4, Linda,106,0.4, Graham,101,0.8, Phil,304,0.4, Nigel,415,0.3, Serban,414,0.5, Andy,708,0.1, Dominic,803,0.5
private ArrayList<PokeTutor> getAllPokeTutorsFromFile(){
ArrayList<PokeTutor> tutors = new ArrayList<PokeTutor>();
File dataFile = new File("Data.dat");
try{
Scanner fileScanner = new Scanner(dataFile);
fileScanner.useDelimiter(",");
fileScanner.useLocale(Locale.UK); //data supplied uses UK style decimal point, this ensures that no matter what locale the system has, the data is read correctly
while (fileScanner.hasNextLine()) {
final String name = fileScanner.next();
final int cp = fileScanner.nextInt();
final double fleeRate = fileScanner.nextDouble();
PokeTutor tutor = new PokeTutor(name,cp,fleeRate);
tutors.add(tutor);
}
}
catch (FileNotFoundException ex){
System.err.println("Error loading PokeTutors file: " + ex.getMessage());
}
return tutors;
}
Ensure you add the appropriate import statements to the top of the file
private int ballCount;
private int greatBallCount;
private int ultraBallCount;
private ArrayList<PokeTutor> pokeTutorsInWild;
private ArrayList<PokeTutor> caughtPokeTutors;
private PokeTutor foundTutor;
foundTutor
and caughtPokeTutors
private Random randomNumberGenerator;
PokeTutorGame() {
randomNumberGenerator = new Random();
caughtPokeTutors = new ArrayList<PokeTutor>();
ballCount = 5;
greatBallCount = 2;
ultraBallCount = 1;
//load tutors from file
pokeTutorsInWild = getAllPokeTutorsFromFile();
}
String visitPokestop(){
//todo: limit result of this method by time
//todo: use singular of balls when finding only one ball
int noOfStandardBalls = randomNumberGenerator.nextInt(4); //between 0 - 3
int noOfGreatBalls = randomNumberGenerator.nextInt(3);
int noOfUltraBalls = randomNumberGenerator.nextInt(2);
ballCount += noOfStandardBalls;
greatBallCount += noOfGreatBalls;
ultraBallCount += noOfUltraBalls;
return "Gained "+ noOfStandardBalls+ " balls, " + noOfGreatBalls + " great balls and " + noOfUltraBalls + " ultra balls";
}
Ideally players should be limited to the frequency of their visits, which could be done by tracking the time of the last visit, and the wording currently does not consider the singular for when one ball is found, e.g. it would state "Gained 1 balls...". Once you have completed the tutorial, come back and amend these issues.
int getBallCount(PokeBall ballKind){
switch (ballKind){
case GREAT:
return greatBallCount;
case ULTRA:
return ultraBallCount;
case STANDARD:
default:
return ballCount;
}
}
private boolean useBall(PokeBall ball){
switch (ball){
case ULTRA:
if (ultraBallCount > 0) {
ultraBallCount--;
return true;
}
case GREAT:
if (greatBallCount > 0) {
greatBallCount--;
return true;
}
case STANDARD:
default:
if (ballCount > 0){
ballCount--;
return true;
}
}
return false;
}
We don't want the player using this method directly (it will be called from within the method that attempts a capture, so ensure it is marked as private)There's no guarantee if you are looking for a tutor that you will find them (especially if an assignment is due...), and in this game, we also don't get to choose which tutor we find - they should appear at random.
foundTutor
variable, and the code will return true, but if not, it will return false (and the foundTutor variable will be null)boolean lookForPokeTutor() {
double chance = randomNumberGenerator.nextDouble();
if (chance > 0.5){
//randomly get a PokeTutor
int tutorPosition = randomNumberGenerator.nextInt(pokeTutorsInWild.size());
foundTutor = pokeTutorsInWild.get(tutorPosition);
return true;
}
foundTutor = null;
return false;
}
private void replacePokeTutor(){
ArrayList<PokeTutor> allAvailablePokeTutors = getAllPokeTutorsFromFile();
int randomIndex = randomNumberGenerator.nextInt(allAvailablePokeTutors.size());
PokeTutor randomTutor = allAvailablePokeTutors.get(randomIndex);
pokeTutorsInWild.add(randomTutor);
}
The final method of the game class will enable the player to attempt to catch a PokeTutor. Provided the ball required is available, it will attempt to capture the PokeTutor in the foundTutor
variable (using the foundTutor's attemptCapture
method). It will return the CaptureResult returned by the foundTutor instance, unless there are no balls available, in which case it will return null (as a capture attempt cannot be made).
PokeTutor.CaptureResult
):
PokeTutor.CaptureResult attemptToCatchTutor(PokeBall ball){
if (useBall(ball)) {
final PokeTutor.CaptureResult captureResult = foundTutor.attemptCapture(ball);
switch (captureResult) {
case CAUGHT:
caughtPokeTutors.add(foundTutor);
pokeTutorsInWild.remove(foundTutor);
replacePokeTutor();
foundTutor = null;
break;
case FAIL:
break;
case FLEE:
foundTutor = null;
}
return captureResult;
} else {
//no pokeballs
return null;
}
}
The listing below shows all the code for the Main class (excluding imports for ArrayList and Scanner)
public class Main {
public static void main(String[] args) {
PokeTutorGame game = new PokeTutorGame();
Scanner inputScanner = new Scanner(System.in);
while (true) {
System.out.println("What do you want to do? [L]ook for PokeTutor, [F]ound PokeTutors, [I]tems, [V]isit Pokestop, [E]xit");
String input = inputScanner.nextLine();
switch (input.toUpperCase()){
case "L":
findTutor(game);
break;
case "F":
displayInventory(game);
break;
case "V":
System.out.println(game.visitPokestop());
break;
case "I":
displayBallCounts(game);
break;
case "E":
return; //exits method and therefore programme
default:
break; //invalid input does nothing, loop begins again
}
}
}
private static void findTutor(PokeTutorGame game) {
if (game.lookForPokeTutor()) {
PokeTutor tutor = game.getFoundTutor();
System.out.println("Tutor found: " + tutor.getName() + ", CP: " + tutor.getCp());
while (true) {
displayBallCounts(game);
System.out.println("Choose: [B]all, [G]reat ball or [U]ltra ball ([E]xit)");
Scanner inputScanner = new Scanner(System.in);
String ballType = inputScanner.nextLine();
if (ballType.equalsIgnoreCase("E")) {
break;
}
PokeBall ball = PokeBall.STANDARD; //default unless U or G entered
switch (ballType.toUpperCase()) {
case "U":
ball = PokeBall.ULTRA;
break;
case "G":
ball = PokeBall.GREAT;
break;
}
PokeTutor.CaptureResult result = game.attemptToCatchTutor(ball);
if (result == null) {
System.out.println("Attempt failed, no poke balls");
return;
}
switch (result) {
case CAUGHT:
System.out.println("You caught " + tutor.getName() + " CP:" + tutor.getCp());
return; //catch loop exited - tutor caght
case FAIL:
System.out.println("Failed to catch PokeTutor");
break; //catch loop begins again
case FLEE:
System.out.println("The tutor has fled their office");
return; //catch loop exited - tutor fled
}
}
}
else{
System.out.println("Did not find PokeTutor");
}
}
private static void displayBallCounts(PokeTutorGame game){
int ballCount = game.getBallCount(PokeBall.STANDARD);
int greatBallCount = game.getBallCount(PokeBall.GREAT);
int ultraBallCount = game.getBallCount(PokeBall.ULTRA);
System.out.println("You have " + ballCount + " balls, " + greatBallCount + " great balls, and " + ultraBallCount + " ultra balls");
}
private static void displayInventory(PokeTutorGame game) {
ArrayList<PokeTutor> tutors = game.getCaughtPokeTutors();
System.out.println("PokeTutors you have caught");
if (tutors.size() > 0) {
for (PokeTutor tutor : tutors) {
System.out.println(tutor.getName() + ": " + tutor.getCp() + "CP");
}
System.out.println();
}
else{
System.out.println("You have not caught any PokeTutors\n");
}
}
}
//todo
comments