create array of NPCs in a game - java

I am taking a Java class right now and we are supposed to be making a text based game. One of the options in our game is to allow the person to choose between 3 ship types; small, medium, and large. Based on their selection, they will be able to choose how many people travel on the ship. Small allows up to 3, medium up to 6, and large up to 9. Once they have made this selection, they are allowed to enter a name for each crew member and select a research specialization. Previously, we had set these as separate menus (select Ship, crew size, research spec, and names) but I'm beginning to think it would be easier to select crew size, names, and research spec in the same menu.
That said, we are working on arrays/ArrayLists right now and we're supposed to use an array to list the characters (NPCs) in the game. So, I need to figure out how to create an array based on how many crew members are chosen (between 1 and 9), have it then prompt for a name, and then allow them to choose from a list of research specs. They will enter a single letter for their choice but the array should put the full name of the research spec in it. I found some code in another post I think might be helpful in this situation but it's barely scratching the surface I think. Any help would be greatly appreciated.
public class Name {
public static void main(String[] args) {
ArrayList<String> name = new ArrayList<String>();
ArrayList<(how do I tell it to select a character option here?)> researchSpec = new ArrayList<(selected option)>();
Scanner sc = new Scanner(System.in);
while (true) {
System.err.println("Please enter the name of your crew member: ");
name.add(sc.next());
System.out.println("Please select a Science Specialization for your crew member");
researchSpec.add(sc.next());
}
}
}

When you have a small, predefined set of possible values for something, an enum is often ideal for representing those.
public enum Specialization {
PHYSICS,
CHEMISTRY,
BIOLOGY
}
Now, you can define variables of the type Specialization and lists of the type List<Specialization>, and refer to the values as Specialization.PHYSICS etc. This is better than using a String because you're not running the risk of e.g. misspelling a specialization somewhere in the code, such as adding "Pysics" to the list in one place and wondering why the if (specialization.equals("Physics")) never works.
However, you still need to have some ifs, a switch, or a Map for the mapping from letters to specializations - unless you get fancy with the enums. An enum is actually a class, but you can't use new on it - instead, there's automatically one instance per identifier (in this case, PHYSICS, CHEMISTRY, and BIOLOGY are the three only instances). You can add fields, constructors, and methods to an enum class, so that you can endow each value with different properties:
public enum Specialization {
PHYSICS('p', "Physics"),
CHEMISTRY('c', "Chemistry"),
BIOLOGY('b', "Biology");
private char menuLetter;
private String displayName;
private Specialization(char menuLetter, String displayName) {
this.menuLetter = menuLetter;
this.displayName = displayName;
}
public char getMenuLetter() { return menuLetter; }
public String getDisplayName() { return displayName; }
}
You can use values() to get a list of all of the values. For example, this will generate the menu options (I'll leave the selection as an exercise for you):
for (Specialization s : Specialization.values()) {
System.out.println(s.getMenuLetter() + ": " + s.getDisplayName());
}
Edit: Or with a regular loop:
Specialization[] specializations = Specializations.values();
for (int i = 0; i < specializations.length; i++) {
System.out.println(specializations[i].getMenuLetter() + ": " + specializations[i].getDisplayName());
}

Creating an Array based on the selection and then filling it would look like this:
NPC[] npcs;
if (amount > maximum) {// both values prompted
System.our.println("Too much crew members.");
return;
}
npcs = new NPC[amount];
for (int i = 0; i < npcs.length; i++) {
String name = // prompt name
Specialization s = // prompt that somehow -> enum solution by Aasmund Eldhuset
npcs[i] = new NPC(name, s);
}
The NPC class would look like this:
public class NPC {
public (final?) String name;
public (final?) Specialization spc;
public NPC(String name, Specialization s) {
this.name = name;
this.spc = s;
}
}

Related

Using an array to store multiple variables from user input

I am relatively new to Java and would like to know how to store variables separately from a single line of user input.
At the minute the user is prompted to enter football results in the following format
home_name : away_name : home_score : away_score
and I am using a while loop to continue to ask user for input until they enter "stop"
(while (input != "stop))
Once the loop is broken I would like my program to output a variety of data such as total games played, but I'm struggling to store the home_name, away_name etc.. especially if the user wishes to enter multiple lines of results.
Two mainstream ways to store a "record" are:
Maps
Data objects
A map is more generic:
Map<String,String> match = new HashMap<>();
match.put("home_name", "Alvechurch Villa");
match.put("away_name", "Leamington");
match.put("home_score", "0");
match.put("away_score", "6");
You can add a map to a list:
List<Map<String,String>> matches = new ArrayList<>();
matches.add(list);
... and retrieve them:
Map<String,String> match = matches.get(0);
System.out.println(match.get("away_score"));
A data object is more tuned to your data format, but you have to write the class yourself.
public class Match {
public String homeName;
public String awayName;
public int homeScore;
public int awayScore;
}
Now you can use this class:
Match match = new Match();
match.homeName = "Studley";
// etc.
You can add and retrieve these from lists too:
List<Match> matches = new ArrayList<>();
matches.add(match);
Match aMatch = matches.get(0);
This is simple, but it's considered bad practice to have public fields like this - it's better to get at them via methods. For brevity, here's a data class with only one field:
public class Player {
private String name;
public Player(String name) {
this.name = name;
}
public String name() {
return name;
}
}
Player neilStacey = new Player("Neil Stacey");
You can use the same technique with all the fields in Match.
(A common style is to name a method like this getName(), and also to have a setName(). I have used a different style and made the object immutable, in an effort to set a good example!)
One advantage of the data object is that it has different types for different fields: homeName is a String, homeScore is an integer. All the fields in the Map are Strings. You can get around this by using Map<String,Object> but then as a consumer you have to cast to the right type when you read.
String homeName = (String) match.get("home_name");
Data objects allow the compiler to do a lot of compile-time checking that helps you know your code is correct. If you use a map, you won't find out until runtime.
Prompt the user separately for each input.
System.out.println("home_name: ");
String hN = scan.next();
System.out.println("away_name: ");
String aN = scan.next();
System.out.println("home_score: ");
String hS = scan.next();
System.out.println("away_score: ");
String aS = scan.next();

Filling an Object with multiple variables in Java?

Is it possible to add multiple attributes to an Object attribute in a class? For example, I have a queue for a bar where you can order drinks by providing: drink name, drink quantity and table number. Do I have to create a variable for each or can I store multiple attributes in a single Object foo? Ty!
You will have to define a class (i.e. class DrinkOrder) and enumerate the fields of that class -- i.e. a String drinkName, int quantity, String tableIdentifier, etc.). Then when you instantiate that class into an instance, each instance can hold as many values as you have defined fields.
// Should protect the fields with accessors, implement Comparable, etc.
public class DrinkOrder {
public String drinkName;
public int quantity;
}
// Somewhere else
DrinkOrder alex = new DrinkOrder();
alex.drinkName = "Beer";
alex.quantity = 1;
DrinkOrder andy = new DrinkOrder();
andy.drinkName = "Amaro Averna";
andy.quantity = 1;
System.out.println("Andy wants " + andy.quantity + " " + andy.drinkName); // => Andy wants 1 Amaro Averna
You should take advantage of Java training (plentiful on the web) such as the original Java Tutorials or Josh Bloch's "Effective Java" or Kathy Sierra's "Head First Java"

Adding strings to an Array in Abstract class A, from an extended class B in Java

I'm writing a small program to demonstrate the factory design pattern. It's a food court application which serves different types of food like Chinese, Italian, Sushi etc. I've created an array in the abstract class below, and I'm trying to populate it by adding a string via an extended class.
abstract public class FoodCourt
{
String name; //e.g. italian, chinese etc...
static String [] dailySpecial = new String[10];
int idx = new Random().nextInt(dailySpecial.length);
String random = (dailySpecial[idx]);
public String getName()
{
return name;
}
public void takeOrder()
{
System.out.println("You ordered " + random + " from our list of Daily Specials");
}
public void serve()
{
System.out.println(" -> Serving " + random + " from our " + name + " menu"); //Serving Chow Mein from our Chinese menu
}
public void printList()
{
for (String name : dailySpecial)
{
System.out.println(name);
}
}
}
Extended class
public class Chinese extends FoodCourt
{
public Chinese()
{
name = "Chinese";
String s = "Chicken Chow Mein";
dailySpecial[0] = s;
}
}
Each extended class (there are 4) will add a special dish to the array but it is being output to the screen as follows:
You ordered null from our list of Daily Specials
-> Serving null from our Chinese menu
Chinese food served
When it should be something like
You ordered Chicken Chow Mein from our list of Daily Specials
-> Serving Chicken Chow Mein from our Chinese menu
Chinese food served
If anyone could help out and see why nothing is being added in that would be great. (If necessary I can post the rest of the classes).
Your OOP structure is way off and you look to be misusing inheritance as this is not how inheritance works or should work. Your Chinese class should not extend FoodCourt since it does not satisfy the "is-a" rule or the Liskov substitution principle. I recommend a re-design with a view towards composition:
FoodCourt should hold a List of objects that extend Restaurant, say called restaurantList. Do it right from the beginning.
You could give Restaurant a getDailySpecial() method that each subclass extends
FoodCourt will call this method when iterating through the list.
You would make Restaurant abstract and your factory would create concrete Restaurant instances.
The parent class shouldn't have a fixed array as you're implementing things.
To get a random daily special, simply get a random Restaurant from the list and call its get daily special method: restaurantList.get(random.nextInt(restaurantList.size())).getDailySpecial();
The problem are those lines:
static String [] dailySpecial = new String[10];
int idx = new Random().nextInt(dailySpecial.length);
String random = (dailySpecial[idx]);
The dailySpecial.length is 10. And in your description, you said that you have 4 extended classes, you are using just 4 position of this array. But you are generating a random integer from 0 to 9.
So, your string random can be null if the idx is greater than 3.

Calling an Instance method when user inputs the instance name as a String

In a small project I am working on I've gotten stuck. The user enters a command that may be "xp Speed", my command handler class finds that it wants to the XP value of the Speed Instance. In this case it needs to return the value of Skill.Speed.currentXP back to the user.
Small Part of the program:
//Example Instance initialization there is over 40 of these
Skill Speed = (new SkillSpeed(Skills.SKILL_SPEED,Skills.SKILL_SPEED_MODIFIER));
//Constructor for skill class
public Skill(String skillName, double modifier) {
this.name = skillName;
this.minLevel = Skills.MIN_SKILL_LEVEL;
this.Modifier = 1f;
this.currentLevel = (int)calculateLevel();
this.currentXP = 1;
this.leaderboard = getCurrentLeaderboard();
this.ID = getNextID();
}
Now, theres one way i could do this. by having a switch statement with case value being the string entered. However I'm sure having 40+ cases in one switch statement must be avoidable. The other theory I have had is creating a array of all current instances then iterating through that list, finding if the user inputted string is equal to the name of that instance, then returning the instance itself. This is what I came up with:
//method inside another classs that attempts to return the appropriate skill Instance
public Skill getSkillFromName(String Name) {
for(int i = 0; i < Skill.SkillArray.length; i++) {
final String SkillName = Skill.SkillArray[i].getName();
if(SkillName.equalsIgnoreCase(Name)) {
return Skill.SkillArray[i];
}
}
return null;
}
So here's what I need help with:
Creating a array of all initialized instances
Creating the method that will return Skill."InsertRandomInstanceDependingOnUserInputHere".currentXP
Fixing any problems you see in the getSkillFromName() method
Or perhaps I have overlooked a far easier way of doing this, and you can help me with that.
Thanks for the help,
BigDaveNz
If the names of the skills excatly match method names you might find the aswer at "How do I invoke a Java method when given the method name as a string?".
For finding instances by name you can still use Map's.
You can use a Map for this. E.g.:
Map<String, Skill> skills = new HashMap<String, Skill>();
To insert the values you put the values into the Map:
skills.put(skill.getName(), skill);
To retrieve your skill you can get the skill by name:
Skill skill = skills.get(name);

How do I add or subtract String values to an object, and set the value to "none" when it's empty?

I have a class Passengers which has member properties String name, int health, and String disease with setter and getter methods. The disease variable will initially hold null. Here's that class
public class Passengers
{
private String name;
private int health;
private String disease;
public Passengers(String _name, int _health, String _disease)
{
name = _name;
health = _health;
disease = _disease;
}
public void setHealth(int _health)
{
health = _health;
}
public void setDisease(String _disease)
{
disease = _disease;
}
public String getName()
{
return name;
}
public int getHealth()
{
return health;
}
public String getDisease()
{
return disease;
}
}
What I want to know is how I could add new strings onto this variable, and then how to take away. For example, a passenger Bill starts at null for his diseases, and then contracts malaria and the cold. Bill's disease variable should now hold malaria, cold. Now say the user chooses to treat Bill's malaria. How would I
1) add malaria and cold
2) subtract just malaria from disease?
Whenever I attempt to change the disease with
passengers[index].setDisease() = null;
it says "error: method setDisease in class Passengers cannot be applied to given types:
required: String
found: no arguments"
I would reccomend making disease a Set of Strings.
Set<String> diseases = new HashSet<String>();
void addDisease(String disease) {
diseases.add(disease);
}
void removeDisease(String deisease) {
diseases.remove(disease);
}
Sets are "better", in this case, than other Collections because they cannot hold duplicates.
You should give the class a List<String> such as an ArrayList<String> and put the diseases in this List.
Better still, create a class or enum of Disease and have your Passenger class use a List<Disease> and avoid over-use of String. You could then give the class public addDisease(Disease disease) and removeDisease(Disease disease) methods.
Incidentally, your class above should be named Passenger, the singular, not Passengers, the plural, since it represents the concept of a single Passenger.
For your requirement if you are using List like ArrayList you can access your elements(disease names) by index, but it will allow duplicate data to be inserted(same disease may be added multiple times, it will unnecessary increase in number of diseases and may arise some problems).
If you use Set like HashSet it will allow unique element only, so no issues related to duplicated entries but at the same time you can't access a particular disease by index (if you need so, as of now I am not aware of your further requirement).
So as best of my knowledge I suggest you to use LinkedHashSet(HashSet with Linked approach) it will provide you FIFO order without duplicate insertion problem.

Categories

Resources