I'm working through a programming book for class. The current project is a terminal based adventure game.
Currently I have everything compiling and the program does run but it returns 'null' for the item description.
How do I go about actually putting the items specified in the item class into rooms / make it so that rooms are created with items inside?
Is there a way to get this to be done randomly?
below is my item class.
import java.util.ArrayList;
import java.util.HashMap;
public class Item
{
private String idescription;
private int iweight;
private HashMap items;
public Item(String idescription, int iweight)
{
this.idescription = idescription;
this.iweight = iweight;
items=new HashMap();
}
private void addItems()
{
items.put("flier", new Item("there is a flier for a club on the floor",
0));
items.put("baccy", new Item("there is a pouch of tobacco on\n" +
"the table, your friends say you may help yourself if you can find\n" +
"rolling papers, filter tips and a lighter.",2));
//...
}
}
This is my Room class
import java.util.HashMap;
import java.util.Set;
import java.util.Iterator;
public class Room
{
private String rdescription;
private HashMap<String, Room> exits;
private HashMap items;
private Item itemroom;
private Item item;
public Room(String rdescription)
{
items = new HashMap();
this.rdescription = rdescription;
exits = new HashMap<String, Room>(); //creates the rooms
}
public void addItems(String item_name, String idescription, int iweight)
{
items.put(item_name, new Item(idescription, iweight));
}
public void setExits(String direction, Room neighbour)
{
exits.put(direction, neighbour);
}
public Room getExit(String direction)
{
return exits.get(direction);
}
public String getExitString()
{
String returnString = "Exits: ";
Set<String> keys = exits.keySet();
for(String exit: keys)
{
returnString += " " + exit;
}
return returnString;
}
public String getDescription()
{
return rdescription;
}
public String getLongDescription()
{
return "You are at the" + rdescription + ".\n" +
getExitString()+". \n" +
items.getIdescription();
}
}
I hope someone can help. Still very much a beginner and going through many phases of utter panic and confusion.
I am a little confused by your code, a common mistake for beginners it to over-complicate things. From what I can make out, rooms are supposed to contain items. So, we have 2 classes: Room and Item.
The minimum for the Room class could be something like this:
public class Room {
private List<Item> items = new ArrayList<Item>();
public void addItem(Item item) {
items.add(item);
}
public List<Item> getItems() {
return Collections.unmodifiableList(items);
}
}
That way, the Room contais a list of Items and has methods allowing you to add an item, or list the items.
I took a stab at this problem. It is quite a bit of code to go through. My logic was instead create a ItemList class that houses all of the different items. You can add methods to add a new item to the list of available items or delete an existing item. Then I would just make methods to return a random Item or List<Item> and then just call these methods from your room class. Something like this:
public class ItemList {
private List<Item> items = new ArrayList<>();
private void addItems(){
//I would keep this the same but instead put them in the itemList variable
items.add(new Item());
}
public Item randomItem(){
return items.get(new Random().nextInt(items.size()));
}
public List<Item> randomItemList(int numOfRandomItems){
List<Item> output = new ArrayList<>();
for (int i=0; i<numOfRandomItems; i++){
output.add(randomItem());
}
return output;
}
}
class Item{
//Made these public for example. I would continue using constructor, get/set for the fields.
String idescription;
int iweight;
}
With this, you can even set it up so that rooms have different ItemList so different rooms can choose a random item from different lists. Hope this helps.
Related
I know this sounds a bit stupid but I am extending a text-based game and I'm trying to figure out a way to print out the items that are in the room. I have made an arrayList for the room and added and "Item" into it by using a separate class called Item.
import java.util.ArrayList;
import java.util.List;
public class Item
{
private String itemName;
private Boolean pickUp;
private Integer itemWeight;
/**
* Constructor for objects of class Items
*/
public Item(String itemName, Boolean pickUp, Integer itemWeight)
{
this.itemName = itemName;
this.pickUp = pickUp;
this.itemWeight = itemWeight;
}
/**
* Returns the name of the item
*/
public String getItemName()
{
return itemName;
}
/**
* Returns the pickUp ability of the item
*/
public Boolean getPickUp()
{
return pickUp;
}
/**
* Returns the weight of the item
*/
public Integer getItemWeight()
{
return itemWeight;
}
}
The part I'm stuck on now is printing those elements out when entering the room. Theres a function in the code that keeps track of the current room and I am trying to use that function to then print out the elements of that corresponding rooms arrayList. Each room in the game is an object from the "Room" class.
public class Room {
private String description;
private String listName;
private HashMap<String, Room> exits; // stores exits of this room.
public Room(String description, String listName)
{
this.description = description;
this.listName = listName;
exits = new HashMap<>();
}
public String getListName()
{
return listName;
}
private void createRooms()
{
Room playground, assemblyHall, groundHallway, lunchHall, ITRoom, groundToilets, headteacherOffice, footballCage, stairs, groundClassroom, firstFloorClassroom, exitGate;
// create the rooms
assemblyHall = new Room("in the assembly hall", "assemblyHallList");
playground = new Room("in the main playground of the school", "playgroundList");
.......
currentRoom = assemblyHall;
ArrayList<Item> assemblyHallList = new ArrayList<Item>();
assemblyHallList.add(new Item("Piece of paper", true, 1));
These last 2 lines are an example of what I will be doing for every room with more items. I feel like I am going about this the wrong way but this was the only idea I had. The problem I have is printing out the elements when I enter a room as I would need to call on the method to get the name of the arrayList then use that name to then access the list and print out the elements in it but I have no idea how to use the string to accesss the array as I would have to use
currentRoom.getListName()
to get the name of the list but when I try to print it out it prints the name itself, not the elements. Any help is appreciated and I can send more parts of the class if needed. Didn't include it all as its very big and a lot of it is irrelevant to my problem
To answer your question, you can't just directly access a variable from a string containing its name. Technically, you could make a static Hashmap<String, ArrayList<Item>> to keep track of all the lists and access them by their name.
However, I don't see why you couldn't just make the list itself part of the constructor and then store the list as an instance variable of the Room.
Something like this would work better:
ArrayList<Item> items = new ArrayList<>();
items.add(new Item("Piece of paper", true, 1));
Room room = new Room("best room", items);
public class Room {
private String description;
private ArrayList<Item> items;
private HashMap<String, Room> exits;
public Room(String description, ArrayList<Item> items)
{
this.description = description;
this.items = items;
exits = new HashMap<>();
}
// Other members not shown
}
You create a ArrayList<Item> assemblyHallList, then you create
Room assemblyHall = new Room("in the assembly hall", "assemblyHallList");
And now you expect that room to have some reference to the List.
That's not how this works. The Room now has the String value "assemblyHallList" in the listName field. But there is no reference to the List. And since assemblyHallList is a local variable in your createRooms method, there's no way of resolving it.
The Solution is this: Don't assign the name of the List variable to the Room (why would that work?), instead assign the List itself:
List<Item> assemblyHallList = ...;
Room assemblyHall = new Room("in the assembly hall", assemblyHallList);
Of course you need to change the type of the constructor's parameter and Room's listName field to List<Item>.
I'm creating a simple RPG console game, I'm at the stage of creating an inventory and loot system. Present in the program, both class Player and class Monster have the arrayList Backpack properties, when the program creates an object of the monster class, items in the monster's backpack are also automatically created, after killing the monster, you can take them to your backpack, and this is where my problem begins how to elegantly prevent duplication of items in the backpack, each item is a class too, now this function works by checking in a nested loop each item one by one to see if it is already in the backpack if it is instead of adding it once moreover, it increases its amount property, if I don't have this item in my backpack, it just adds to the list, the solution works, but definitely it is not the right solution, because with many of items this checking mechanism will grow a lot, if anyone has any valuable tips I will be grateful.
I also have a second idea to create a boolean Is_it_in_Backpack variable, and somehow connect it with the loot collecting mechanism
Below some code sample
public class Player {
public static ArrayList<Item> Backpack = new ArrayList<>()
}
and the class Skieleton:
public class Skieleton extends Monsters {
public static ArrayList<Item> Backpack;
public Skieleton() {
Backpack = new ArrayList<>();
Backpack.add(new Weapon("Rusty sword", "Just a rusty sword", 3, 2 ));
Backpack.add(new Armor("Leather Armor", "Old leather armor", 6, 3));
}
class item:
public class Item {
public String ItemName;
public String Description;
public int ItemSize;
public int ItemWeight;
public int Amount;
public Item(String ItemName, String Description, int ItemSize, int ItemWeight)
{
this.ItemName = ItemName;
this.Description = Description;
this.ItemSize = ItemSize;
this.ItemWeight = ItemWeight;
}
public Item() {
}
}
I recommend you use a class that extends java.util.Set:
If order is not important for you, you can use HashSet;
If order of insertion is important, you can use LinkedHashSet;
If natural order is important (alphabetical by name or other property), you can use TreeSet and implement the interface Comparable onto the class inserted in the collection.
However, regardless of your choice, it's recommended you implement hashCode() (for optimization) and equals() (to let collection identify which item is equal to other and avoid duplication).
If you can use third party libraries, I'd recommend using a Bag from Eclipse Collections.
With your Item class implementing equals and hashCode on ItemName field, your example usage could look like:
final MutableBag<Item> backPack = new HashBag<>();
final Item rustySword = new Item("Rusty sword", "Just a rusty sword", 3, 2);
final Item leatherArmour = new Item("Leather Armor", "Old leather armor", 6, 3);
backPack.add(rustySword);
backPack.add(leatherArmour);
backPack.add(rustySword);
System.out.println(backPack.toMapOfItemToCount()); // prints {Item[ItemName='Rusty sword']=2, Item[ItemName='Leather Armor']=1}
System.out.println(backPack.occurrencesOf(rustySword)); // prints 2
The API is rich, and provides a lot more:
https://www.eclipse.org/collections/javadoc/11.0.0/org/eclipse/collections/api/bag/Bag.html
I would use a Map.
Here's my suggestion:
import java.util.*;
class Player {
public Backpack backpack= new Backpack();
}
class Monster { }
class Skieleton extends Monster {
public Backpack backpack= new Backpack();
public Skieleton() {
backpack.add(new Weapon("Rusty sword", "Just a rusty sword", 3, 2 ));
backpack.add(new Armor("Leather Armor", "Old leather armor", 6, 3));
}
}
class Backpack {
private HashMap<Item,Item> items = new HashMap<>();
public Item add(Item item){
if (items.containsKey(item)){
items.get(item).Amount=+ item.Amount;
return items.get(item);
} else {
items.put(item,item);
return item;
}
}
public Item get(Item item){
return items.getOrDefault(item, null);
}
}
class Item {
public String ItemName;
public String Description;
public int ItemSize;
public int ItemWeight;
public int Amount;
public Item(String ItemName, String Description, int ItemSize, int ItemWeight)
{
this.ItemName = ItemName;
this.Description = Description;
this.ItemSize = ItemSize;
this.ItemWeight = ItemWeight;
}
public Item() {
}
public boolean equals(Object o){
if (o instanceof Item){
return ItemName.equals( ((Item)o).ItemName);
}
return false;
}
}
You can use a HashMap for storing the items. First, I would like to change the Item class to not have amount field in it. Let an item denote what an item is (name, description, size and weight).
Here's the updated backpack:
Map<Item, Integer> backpack = new HashMap<>();
To add an item, you can use Map#merge method.
public void addItemToBackpack(Item item, int quantity) {
backpack.merge(item, quantity, (oldQuantity, newQuantity) -> oldQuantity + newQuantity);
}
We insert the item and its quantity. If the item is not already present, it will be inserted with the passed quantity.
If it is already present, then the BiFunction which is a mapping function will be called with the existing value (oldQuantity) and the new value which we tried to insert (newQuantity). We sum them both, return it and the item's value (quantity) will be updated with this value.
Using method references, we can write the above as,
backpack.merge(item, quantity, Integer::sum);
I am having some issues even starting with this task so I thought some of you might help. Basically I am given a .csv file like in the example below and it should be displayed like int the example output.
Here is the content of the .csv file:
The first is the ID, Second menuname, third is Parent ID (if its a number it should be that number's child), and if Hidden is true it should not display.
ID;MenuName;ParentID;isHidden;LinkURL
1;Company;NULL;False;/company
2;About Us;1;False;/company/aboutus
3;Mission;1;False;/company/mission
4;Team;2;False;/company/aboutus/team
5;Client 2;10;False;/references/client2
6;Client 1;10;False;/references/client1
7;Client 4;10;True;/references/client4
8;Client 5;10;True;/references/client5
10;References;NULL;False;/references
Here is the output:
. Company
.... About Us
....... Team
.... Mission
. References
.... Client 1
.... Client 2
The menu items should be indented depending on the parent they belong under.
Some items are hidden, and should not be presented
The items should be ordered alphabetically
Can you guys point me out how to start and what data structures to use (I was having in mind Stack) but I am not sure. Thanks!
Here is my code so far.
import java.io.BufferedReader;
import java.io.FileReader;
import java.io.IOException;
import java.util.Stack;
public class Main {
static void Function() {
}
public static void main(String[] args) throws IOException {
// write your code here
BufferedReader csvReader = new BufferedReader(new FileReader("Navigation.csv"));
String row;
int parent_id = 0;
Stack stack = new Stack();
while ((row = csvReader.readLine()) != null) {
String[] data = row.split(";");
stack.push(row);
for(int i=0; i<4; i++){
System.out.print(data[i] + " ");
if(data[2].equals("NULL")){
parent_id = Integer.parseInt(data[0]);
}
}
System.out.println(parent_id);
}
csvReader.close();
for(int i=0;i<10;i++){
System.out.println(stack.pop());
}
}
}
For parsing the CSV in java, you can use a third party library like Apache Commons. It provides a mechanism to fetch via Headers.
As a data structure to store this, you can use Map with Key as int (assuming ID is int) and Value as a POJO which has a class structure as follows:
class MenuItem {
Node node;
List<Node> childNodes;
}
Where Node is defined as:
class Node {
String menuName;
Boolean isHidden;
String url;
}
For root nodes, childNodes will be null
NOTE: The second half of the solution is based on the assumption that your Node can only be one layer deep (as per the example provided).
This what you can do:
Define a class Item (you can use Node)
class Item {
int id;
String name;
int parentId;
String link;
boolean hidden;
List<Item> children;
}
Read the CSV and create a list of items Item (we are doing is step 2 and step 3 separately as in your CSV the parent items are in random order)
List<Item> items = new ArrayList<Item>();
items.add(new Item(id, name, parentId, link, hidden)); // get these values from CSV, set parentId as `0` if it is null
items = items.stream().sorted((a, b) -> a.parentId - b.parentId)).collect(Collectors.toList()); // sort item by ascending order of parentId
Create a new list with hierarchy
List<Item> treeHireachy = new ArrayList<Item>();
// iterate through the previous list and add only first level items to this list.
// then add remaining items as child nodes/item to based on the appropriate parent item
It is a bit difficult to do this with a single collection of items.
Not only do you need a mapping for each item (id --> MenuItem), but also a way to sort MenuItems by their MenuName. On top of that, you need to maintain a tree structure of the hierarchy.
I propose two collections: a Map<Integer, MenuItem> and a SortedSet<MenuItem>. In order to build the hierarchy tree, loop through the set and add children to their parents until you're left with only the root MenuItems - the ones without a parent.
The actual printing should be clear enough - you recursively print all subtrees of MenuItems.
Here is my solution. This is how you run it: java MenuItems < "your_csv_file"
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Scanner;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
public class MenuItems {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
// skip the header line
sc.nextLine();
// Read all menu items
SortedSet<MenuItem> menuItemsSet = new TreeSet<MenuItem>();
Map<Integer, MenuItem> menuItemsMap = new HashMap<Integer, MenuItem>();
while (sc.hasNextLine()) {
MenuItem nextItem = new MenuItem(sc.nextLine().split(";"));
menuItemsSet.add(nextItem);
menuItemsMap.put(nextItem.id, nextItem);
}
// build tree structure
Set<MenuItem> reunitedChildren = new HashSet<MenuItem>();
for (MenuItem nextItem : menuItemsSet) {
// if an item has a parent
if (nextItem.parentId != -1) {
menuItemsMap.get(nextItem.parentId).addChild(nextItem);
reunitedChildren.add(nextItem);
}
}
menuItemsSet.removeAll(reunitedChildren);
// print result:
printMenu(" ", menuItemsSet);
}
private static void printMenu(String indent, SortedSet<MenuItem> subtree) {
for (MenuItem nextItem : subtree) {
if (!nextItem.isHidden) {
System.out.println(indent + " " + nextItem.menuName);
if (!nextItem.children.isEmpty()) {
printMenu(indent + " ", nextItem.children);
}
}
}
}
}
class MenuItem implements Comparable<MenuItem> {
int id;
String menuName;
int parentId;
boolean isHidden;
String linkUrl;
SortedSet<MenuItem> children;
public MenuItem(String[] fields) {
this(
Integer.parseInt(fields[0]),
fields[1],
fields[2].equals("NULL") ? -1 : Integer.parseInt(fields[2]),
Boolean.parseBoolean(fields[3]),
fields[4]
);
}
public MenuItem(
int id,
String menuName,
int parentId,
boolean isHidden,
String linkUrl
) {
this.id = id;
this.menuName = menuName;
this.parentId = parentId;
this.isHidden = isHidden;
this.linkUrl = linkUrl;
this.children = new TreeSet<MenuItem>();
}
public void addChild(MenuItem child) {
this.children.add(child);
}
#Override
public boolean equals(Object obj) {
MenuItem that = (MenuItem) obj;
return this.id == that.id;
}
#Override
public int hashCode() {
return this.id;
}
#Override
public int compareTo(MenuItem that) {
return this.menuName.compareTo(that.menuName);
}
}
I want to get the model numbers from the list only
['brand: Samsung, model number: VA2210-MH, size: 21.5', 'brand: Philipe, model number: 244E1SB, size: 21.5']
And I set create attributes and getter and setter of all attributes(only model number will be shown) in Monitor
public class Monitor{
public String brand;
public String modelNumber;
public double size;
public Monitor(String brand, String modelNumber, double size){
this.brand = brand;
this.modelNumber = modelNumber;
this.size = size;
}
public void setModelNumber(String amodelNumber){
modelNumber = amodelNumber;
}
public String getModelNumber(){
return modelNumber;
}
}
so I create a list and add the information into the list
and a method to create a set with model number by the method modelNumberSet()
import java.util.*;
public class ComputerShop{
private List<Monitor> monitorList = new ArrayList<>();
public void addMonitor(String brand, String modelNumber, double size){
Monitor newMonitor = new Monitor(brand, modelNumber, size);
monitorList.add(newMonitor);
}
public Set<Monitor> modelNumberSet(){
Set<Monitor> NewSet = new HashSet<>();
for (Monitor m : monitorList) {
NewSet.add(m.getModelNumber());
}
return NewSet;
}
}
I hope the model number will be added into a new set, the output looks like
[VA2210-MH, 244E1SB]
So I use for loop to incase I will add more information in the future, but the error comes out when I use add(). Why the array cannot be added into the new set? Am I using the wrong function?
Is there a better solution I should use?
Change Set<Monitor> to Set<String>. You are adding model numbers to the set and their types are String. You are trying to put a String where a Monitor is expected. Square peg in a round hole.
Fix the modelNumberSet() method as follows:
public Set<String> modelNumberSet(){
Set<String> newSet = new HashSet<>();
for (Monitor m : monitorList) {
newSet.add(m.getModelNumber());
}
return newSet;
}
Disclaimer: I am new to programming so my code might be noobish. Anyway, I'm working on a reference data type class and one of the instructions says;
Add a method to add one ingredient(I'm assuming to the ingredients array) at a time (use a single String variable as the parameter. Do not pass it an array).
the way I have it right now there is an array, but I cant quite figure out how to create this method with just a single string variable. Again I'm still very new to this.
P.S. the assignment is to create compliable reference data type class not a running program(yet). There more to the code but this is the part I thought was most relevant.
import java.util.Arrays;
import java.util.List;
import java.util.ArrayList;
//default constructor
public class Recipe{
private String recipeName="";
private String [] ingredients = new String[20];
private String [] instructions = new String[20];
public Recipe(){
for(int i=0;i<ingredients.length;i++){
recipeName="";
ingredients[i]="";
instructions[i]="";
}
}
//getset recipeName
public String getName(){
return recipeName;
}
public void setName(String inName){
recipeName = inName;
}
//getset ingredients
public String[] getIngredients(){
return ingredients;
}
public void setIngredients(String[] inIngredients){
ingredients = inIngredients;
}
If you would like to add ingredients one by one to your recipe and get the list of ingredients, the following should help.
private List<String> ingredients = new ArrayList<String>();
...
public void setIngredients(String ingredient){
ingredients.add(ingredient);
}
public List<String> getIngredients(){
return ingredients;
}
Change your ingredients array to ArrayList
private List<String> ingredients = new ArrayList<String>();
Then create methods to add one ingredient to it:
public boolean addIngredient(String ingredient){
return ingredients.add(ingredient);
}
Hope you can use arrayList which are better, but if you must use array:
public void addIngredient(String ingredient){
for(int i=0; i<ingredients.length;i++){
if("".equals(ingredients[i])}{
ingredients[i]=ingredient;
return; //no need to add more than once
}
}
}