Android game rpg inventory system - java

I am using an ArrayList as my "inventory".
I am having trouble figuring out a way to add multiples of the same item without taking up a spot in the "inventory". For example: I add a potion to my inventory. Now I add ANOTHER potion but this time instead of adding ANOTHER potion to the inventory, it should instead show that I have: Potions x 2, while only taking up one spot in the ArrayList. I have come up with a few solutions but I feel as if they are bad practices. One solution I tried was to add an AMOUNT variable to the item itself and increment that. Help me find a much better solution?
EDIT: Ok please ignore the above. I have gotten pretty good answers for that but what surprised me was that there were almost no tutorials on role playing game inventory systems. I've done a lot of google searching and cannot find any good examples/tutorials/source code. If anyone could point me to some good examples/tutorials/source code (does not matter what language, but preferable java, or even c/c++) I would appreciate it, thanks. Oh, and any books on the subject matter.

The usual way to solve this (using the standard API) is to use a Map<Item, Integer> that maps an item to the number of of such items in the inventory.
To get the "amount" for a certain item, you then just call get:
inventory.get(item)
To add something to the inventory you do
if (!inventory.containsKey(item))
inventory.put(item, 0);
inventory.put(item, inventory.get(item) + 1);
To remove something from the inventory you could for instance do
if (!inventory.containsKey(item))
throw new InventoryException("Can't remove something you don't have");
inventory.put(item, inventory.get(item) - 1);
if (inventory.get(item) == 0)
inventory.remove(item);
This can get messy if you do it in many places, so I would recommend you to encapsulate these methods in an Inventory class.
Good luck!

Similar to aioobe's solution, you can use TObjectIntHashMap.
TObjectIntHashMap<Item> bag = new TObjectIntHashMap<Item>();
// to add `toAdd`
bag.adjustOrPutValue(item, toAdd, toAdd);
// to get the count.
int count = bag.get(item);
// to remove some
int count = bag.get(item);
if (count < toRemove) throw new IllegalStateException();
bag.adjustValue(item, -toRemove);
// to removeAll
int count = bag.remove(item);
You can create a multiples class.
class MultipleOf<T> {
int count;
final T t;
}
List bag = new ArrayList();
bag.add(new Sword());
bag.add(new MultipleOf(5, new Potion());
Or you can use a collection which records multiples by count.
e.g. a Bag
Bag bag = new HashBag() or TreeBag();
bag.add(new Sword());
bag.add(new Potion(), 5);
int count = bag.getCount(new Potion());

You're probably better off creating a class called InventorySlot, with a quantity and contents field. This also give you the flexibility of adding other properties, such as what the inventory slot can contain, should you decide to create a 'potions' only sack or something like that.
Alternatively, a StackCount and a boolean IsStackable, or perhaps MaxStack property is used in quite a few MMO's, it's a perfectly valid technique too.

or an class InventoryField with an item and an integer for the amount.
public class InventoryField{
int count;
Item item;
}
public class Inventory extends ArrayList<InventoryField>{
...
}

How about the following
public class Item{
int count;
String name;
}
Then have a list representing the inventory
public class Player {
List<Item> inventory = new ArrayLis<Item>();
}

Related

How to find the largest Count of Objects assosiated with the same Object

I have a Mentor class in which I have an ID for each mentor and an ArrayList of mentee IDs, like this:
public class Mentor {
int mentorId;
ArrayList<Integer> mentees = new ArrayList<>();
public Mentor(int mentorId, ArrayList<Integer> mentees) {
this.mentorId = mentorId;
this.mentees = mentees ;
}
}
The problem is that some mentees can be mentors too.
I would like to somehow get a count of all of the mentees associated to the top mentor as well as how many mentors are under the top mentor.
So, basically, if a mentor has a mentee, who is also a mentor, then this mentor's mentees are also associated to the top mentor.
So, my thinking was to loop through the mentee-list and see if any of the id's match an ID of Mentor. If true, Add this mentor's list of mentees to a list and loop again, but this will not work dynamically.
My main class looks something like this:
ArrayList<Mentor> mentors = new ArrayList<>();
ArrayList<Integer> mentees = new ArrayList<>();
ArrayList<Integer> mentees2 = new ArrayList<>();
mentees.add(2);
mentees.add(3);
mentees2.add(4);
mentees2.add(5);
//[1,{2,3}]
mentors.add(new Mentor(1, mentees));
//[2,{4,5}]
mentors.add(new Mentor(2, mentees2));
int mentorCount = 0;
int menteeCount = 0;
for (Mentor mentor : mentors) {
for (Integer mentee : mentees) {
mentorCount++;
if (mentee == mentor.mentorId){
mentorCount++;
//add each mentee to arraylist and start the process again, but is there an easier way to do this.
}
}
}
I was wondering if there is a way of solving this, maybe using streams?
I would recommend using good object oriented design, you shouldn't just use integer id's like that, because in this situation you could simply make an ArrayList of Person objects Where Mentees, and Mentors Inherit from Person. Then you can check if a Person is an instance of Mentee versus Mentor:
for (Person p : people) {
if (p instanceof Mentor)
{
// Mentor logic
}
if (p instanceof Mentee)
{
// Mentee Logic
}
}
Firstly, let's briefly recap the task.
You have a set of mentors, each mentor has a collection of mentees. Some of them might happen also to be mentors and so on.
Class design
From the perspective of class design, the solution is pretty simple: you need only one class to describe this relationship - Mentor. And each Mentor should hold a reference to a collection of Mentors (not integer ids).
In your domain model, as you described it, there's no substantial difference between a mentor and a mentee. Mentor points to other mentors - is a simplest way to model that. Mentee is just an edge case, a mentor which has an empty collection of mentors.
You don't need to include classes and features in your application that doesn't bring benefit.
Data structure
From the perspective of data structures, this problem can be described very well with an acyclic disjointed Graph.
Acyclic because if we consider the relationship between mentors when a mentor could indirectly point at themself (i.e. a mentor N has a mentee, with in tern points to another mentee that happens to be also a mentor of mentor N) the task becomes ambiguous. Therefore, I'm making an assumption that no one can be a mentor to himself.
I also depicted this data structure as disjointed because mentors in this model (as well as in real life) can form isolated groups, which in graph theory called connected components. That means that there could be several mentors with the same count of mentee, which happens to be the largest.
Depth first search
In order to find all the vertices (mentors) connected with a particular mentor, we have a classic traversal algorithm, which is called Depth first search (DFS). The core idea of DFS is to peek a single neighbor of the given vertex, then in turn peek one of its neighbors and so on until the hit the vertex that doesn't point to the other vertex (i.e. maximum depth is reached). And then it should be done with every other neighbors of the previously visited vertices.
There are two ways to implement DFS.
Iterative approach
For that, we need a stack. It will store all unvisited neighbors of the previously encountered vertexes. The last vertex from each list of neighbors in every branch will be explored first because it will be placed on the top of the stack. Then it will get removed from the stack and it's neighbors will be placed on the stack. This process repeats in a loop until the stack contains at least one element.
The most performant choice of collection for the stack is ArrayDeque.
Because this approach require continuous modification of the stack by adding and removing vertices, it isn't suitable to be implemented with Stream IPA.
Recursive approach
The overall principle is the same as described above, but we don't need to provide a stack explosively. The call stack of the JVM will be utilized for that purpose.
With this approach, there's also some room to apply streams. For that reason, I've chosen the recursive implementation. Also, its code is probably a bit easier to understand. But keep in mind that recursion has certain limitations, especially in Java, and not suitable for processing a large set of data.
Recursion
A quick recap on recursion.
Every recursive implementation consists of two parts:
Base case - that represents a simple edge-case for which the outcome is known in advance. For this task, the base case is the given vertex has no neighbors. That means menteesCount of this vertex needs to be set to 0 because it has no mentee. And the return value for the base case is 1 because this vertex, in turn, is a valid mentee of another vertex that holds a reference to it.
Recursive case - a part of a solution where recursive calls a made and where the main logic resides.
The recursive case could be implemented using streams and entails recursive invocation of the for every neighbor of the given vertex. Each of these values will contribute to the menteesCount of the given vertex.
The value returned by the method will be menteesCount + 1 because for the vertex which triggered this method call, the given vertex will be a mentee as well as its mentees.
Implementation
Class mentor basically serves as a vertex of the graph. Each vertex has a unique id and collection of adjacent vertexes.
Also, in order to reuse values obtained by performing DFS I've added a field menteesCount which is initially initialized to -1 in order to distinguish between vertices that has no adjacent vertices (i.e. menteesCount has to be 0) and vertices which value wasn't calculated. Every value will be established only ones and then reused (another approach will be to utilize a map for that purpose).
Method getTopMentors() iterates over the collection of vertices and invokes DFS for every vertex which value wasn't calculated yet. This method returns a list of mentors with the highest number of associated mentees
Method addMentor() that takes a vertex id, and id of its neighbors (if any) was added in order to interact with the graph in a convenient way.
Map mentorById contains every vertex that was added in the graph and, as its name suggests, allows retrieving it based on the vertex id.
public class MentorGraph {
private Map<Integer, Mentor> mentorById = new HashMap<>();
public void addMentor(int mentorId, int... menteeIds) {
Mentor mentor = mentorById.computeIfAbsent(mentorId, Mentor::new);
for (int menteeId: menteeIds) {
mentor.addMentee(mentorById.computeIfAbsent(menteeId, Mentor::new));
}
}
public List<Mentor> getTopMentors() {
List<Mentor> topMentors = new ArrayList<>();
for (Mentor mentor: mentorById.values()) {
if (mentor.getCount() != -1) continue;
performDFS(mentor);
if (topMentors.isEmpty() || mentor.getCount() == topMentors.get(0).getCount()) {
topMentors.add(mentor);
} else if (mentor.getCount() > topMentors.get(0).getCount()) {
topMentors.clear();
topMentors.add(mentor);
}
}
return topMentors;
}
private int performDFS(Mentor mentor) {
if (mentor.getCount() == -1 && mentor.getMentees().isEmpty()) { // base case
mentor.setCount(0);
return 1;
}
int menteeCount = // recursive case
mentor.getMentees().stream()
.mapToInt(m -> m.getCount() == -1 ? performDFS(m) : m.getCount() + 1)
.sum();
mentor.setCount(menteeCount);
return menteeCount + 1;
}
public static class Mentor {
private int id;
private Set<Mentor> mentees = new HashSet<>();
private int menteesCount = -1;
public Mentor(int id) {
this.id = id;
}
public boolean addMentee(Mentor mentee) {
return mentees.add(mentee);
}
// getters, setter for menteesCount, equeals/hashCode
}
}
An example of the graph used as a demo.
main() - the code models the graph shown above
public static void main(String[] args) {
MentorGraph graph = new MentorGraph();
graph.addMentor(1, 3, 4);
graph.addMentor(2, 5, 6, 7);
graph.addMentor(3, 8, 9);
graph.addMentor(4, 10);
graph.addMentor(5, 11, 12);
graph.addMentor(6);
graph.addMentor(7, 13, 14);
graph.addMentor(8);
graph.addMentor(9, 16, 17, 18);
graph.addMentor(10);
graph.addMentor(11, 18);
graph.addMentor(12);
graph.addMentor(13);
graph.addMentor(14, 19, 20);
graph.addMentor(15);
graph.addMentor(16, 21, 22);
graph.addMentor(17);
graph.addMentor(18);
graph.addMentor(19);
graph.addMentor(20);
graph.addMentor(21);
graph.addMentor(22);
graph.getTopMentors()
.forEach(m -> System.out.printf("mentorId: %d\tmentees: %d\n", m.getId(), m.getCount()));
}
Output
mentorId: 1 mentees: 10
mentorId: 2 mentees: 10
Use Person and Mentor and Mentee subclasses as suggested by acornTime, define mentees as a list of Person and the information you want becomes simple to get:
import java.util.*;
import java.util.stream.Stream;
public class Main{
public static void main(String[] args) {
ArrayList<Person> mentees = new ArrayList<>();
mentees.add(new Mentee(11));
mentees.add(new Mentee(12));
mentees.add(new Mentor(2, new ArrayList<>()));
mentees.add(new Mentee(13));
mentees.add(new Mentee(14));
mentees.add(new Mentor(3, new ArrayList<>()));
mentees.add(new Mentor(4, new ArrayList<>()));
mentees.add(new Mentor(5, new ArrayList<>()));
Mentor mentor = new Mentor(1, mentees);
System.out.println(mentor.menteesCount());
System.out.println(mentor.mentorsInMentees().count());
}
}
interface Person {
int getId();
}
class Mentor implements Person{
private final int mentorId;
private List<Person> mentees = new ArrayList<>();
public Mentor(int id, ArrayList<Person> mentees) {
mentorId = id;
this.mentees = mentees ;
}
#Override
public int getId() {
return mentorId;
}
public List<Person> getMentees() {
return mentees;
}
public int menteesCount() {
return mentees.size();
}
public Stream<Person> mentorsInMentees(){
return mentees.stream().filter(m -> (m instanceof Mentor));
}
}
class Mentee implements Person{
private final int menteeId;
public Mentee(int id) {
menteeId = id;
}
#Override
public int getId() {
return menteeId;
}
}
Test it online here
You should do something like a depth-first or breadth-first search (*):
Maintain a Set<Integer> containing all the people you have already seen.
Maintain a queue of some kind (e.g. an ArrayDeque), of people you are going to check.
Put the first person (or any number of people, actually) into this queue.
Then, while the queue is not empty:
Take the next person in the queue
If you've already seen them, go to the next item in the queue
If you've not already seen them, put the person into the seen set; add all of their mentees into the queue
That's it. The number of people at the end is the size of the seen set.
(*) Whether you do depth-first or breadth-first search depends on which end of the queue you add mentees to: adding them to the same end that you remove them from results in depth-first search; adding them to the other end results in breadth-first search. If you don't care which, choose either.

Checking if multiple hashmaps are empty

I'm trying to find if multiple HashMaps are empty.
To give some context. I have a hashmap declared here.
static Map<Integer, College> tblColleges = new HashMap<Integer, College>();
For each college object:
Map<Integer, Department> tblDepartments = new HashMap<Integer, Department>();
I'm trying to add a major. Majors can only exist as an attribute of Department.
Here's what I have right now.
int numberofColleges = Databases.tblColleges.size();
int emptyColleges = 0;
for(int key: Databases.tblColleges.keySet()) {
if(Databases.getTblColleges(key).tblDepartments.isEmpty()) {
emptyColleges++;
}
}
if(numberofColleges == emptyColleges) {
System.out.println("Invalid. Requires at least 1 department.");
}
I should only be able to create a Major if at least 1 college has a department.
Essentially for each college object that exists in the tblColleges, I'm checking to see if it's department hashmap is empty. If it is empty, then I increment the number of empty colleges.
Afterward, I compare the number of college objects with empty college objects found, if they are equal then I print an error.
I was wondering if there was a better more efficient way to do this, maybe with some function that exists that I'm not familiar with rather than using variables.
Q: Can you do the check "more efficiently"?
A: You could optimize it a bit:
boolean nonEmptyColleges = false;
for (int key: Databases.tblColleges.keySet()) {
if (!Databases.getTblColleges(key).tblDepartments.isEmpty()) {
nonEmptyColleges = true;
break;
}
}
The above short circuits as soon as it finds a College with a Department. That will be a substantial improvement in a lot of cases.
Then, assuming that Databases.tblColleges is a Map:
boolean nonEmptyColleges = false;
for (int college: Databases.tblColleges.values()) {
if (!college.tblDepartments.isEmpty()) {
nonEmptyColleges = true;
break;
}
}
Q: Can you do the check with less code?
A: Using Java 8 streams you could write the last as:
boolean nonEmptyColleges = Databases.tblColleges.values().stream()
.anyMatch(c -> !c.tblDepartments.isEmpty());
(I think ...)
Q: But is this the right approach?
A: IMO, no.
It seems that you intend to do this check each time you add a major. That's not necessary.
Majors can only exist as an attribute of Department.
The key thing that you need to check is that the Department you want to add the major for exists.
If the Department doesn't exist you can't add the major to it.
If the Department does exist you can the major to it, whether or not it is currently a department of a college1.
The bigger point here is that any data model is going to have a variety of data integrity rules / constraints on it. But that does mean that you need to explicitly check all of them each time the model is changed. You only need to check the preconditions for the change (e.g. that the Department exists) and any constraints that could be invalidated by the change.
1 - The "not" case assumes that there may be some other way of finding a Department. It could be a separate table of Department objects, or it could be that you are in the process of creating and building a new Department and haven't added it to its College yet.

How do I store information in a method so I can use it in a different method later?

I am very new to Java, so sorry if this is stupid and obvious or worded poorly. I don't really know enough yet to know what I don't know.
So I decided that since I have to learn Java, I'd just jump in head first and try to figure it out as I go. So far, it's worked decently. I'm trying to reinforce some basic concepts I already know by writing small programs that do trivial stuff.
I decided I'd write a little text based adventure game and it's working well so far. I'm using Scanners and Switches to call methods that use Scanners and Switches to call other methods. That's all working fine.
So far it's a very linear straight line, like an old choose your own adventure book. But, I wanted to add a player inventory. I have a very vague idea of how to do it, but I have a pretty solid idea of what I want it to do.
So, basically I want to store a piece of information that says the player has a specific item. I want to be able to test for the presence of more than one item at once. And I want to be able to tell the player what items he has at any point in the game.
I don't really know how to ask the question better.
My best guess is doing something like
int key, potion;
key = 0
potion = 2
and then testing the values of each one
if (key = 0) {
System.out.println("you don't have the key ");
}
if (key > 0) {
System.out.prinln("You unlock the door");
}
I'm doing each new room as a separate method, so the whole game is just a big chain of methods. So my hope is that the information about items can be stored in a separate method that I can access through switches or if/else in the current room method the player is in. So, the player is unlocking a door in room2, which is its own method, and he picked up the key in room1, which is its own method, and the key is stored as an integer in the inventory method. But the key was one use, so the key integer is set to 0 and the method room3 starts. If that makes any sense.
Again, sorry if this is really stupid basic stuff. I'm very new to programming.
No problem and I applaud you for choosing to learn programming. This is basic data structures. If you want to hold a value, in most programming languages, you'll have an array. I think breaking your logic down is a good idea i.e., (store an item, test for > 1, list items). The first step is making this as simple as possible and than maybe adding getters/setters later through refactors. Ultimately, your goal is to make the most basic code work first (like this) and than refactoring towards an object oriented class with getters/setters and/or a HashMap.
1:
public class PlayerInventory
{
private String[] inventoryStr = new String[20]; // basic implementation
inventoryStr[0] = "Phone";
inventoryStr[0] = "Book";
}
2:
int arrayLength = inventoryStr.length;
3:
for(int i=0; i < inventoryStr.length; i++) {
System.out.println( inventoryStr[i] );
}
Refactor (after you write unit tests for this)
1*: (with a list)
import java.util.*;
import java.util.*;
public class CollectionGetterSetter {
private List<String> playerInventory;
public void setPlayerInventory(List<String> inv) {
this.playerInventory = inv;
}
public List<String> getPlayerInventory() {
return this.playerInventory;
}
public static void main(String[] args) {
CollectionGetterSetter app = new CollectionGetterSetter();
List<String> PlayerInventory = new ArrayList();
PlayerInventory.add("phone");
PlayerInventory.add("book");
PlayerInventory.add("glasses");
PlayerInventory.add("nav");
app.setPlayerInventory(PlayerInventory);
System.out.println("Player 1: " + PlayerInventory);
List<String> PlayerInventory2 = new ArrayList();
PlayerInventory2.add("cap");
PlayerInventory2.add("gown");
PlayerInventory2.add("foo");
PlayerInventory2.add("bar");
}
}

Java: Merging two lists from complex objects with duplicates to one ordered list

First off my problem is similiar to this already answered question Merging two arrayLists into a new arrayList, with no duplicates and in order, in Java.
However the difference here is that I tried to merge two lists with more than just a String together. The intention on my side was to merge two objects of the following kind (to simplify things I striped unnecessary infos from the example):
public class Data{
private int count;
private Type type;
private Key uniqueKey;
}
So that I get a new oject which has a summarized count out. This will eliminate unwanted duplicates because the uniqueKey on these objects was identical. The reason for this is that I mapped several business types to only one technical type.
The problem here is that you have to account for every possiblity in order to handle the merge correctly and don't miss an original object.
Here are some cases I tried to cover with unit test:
One normal, followed by two duplicates, and one normal = normal, merged, normal
Two duplicates, followed by two normal = merged, normal, normal
Two normal, followed by two duplicates = normal, normal, merged
and so on and so forth...
So how to solve that without going crazy?
Since I spent half a day with that problem, I thought the simple answer might be useful to someone else.
So what did I try:
I decided not to go recursive since I avoid it if I can for obvious reasons and used two nested loops
I wrote unit tests for every case I could think of
Then I tried step by step to make them all green
I banged my head against the table because everytime I made one green another one went red
I asked a colleague
He let me state the problem without showing him my "solution"
Here's the magic 15 min solution:
public static LinkedList<Data> merge(final LinkedList<Data> source) {
final HashMap<Data, Integer> temp = new HashMap<>();
for (final Data data : source) {
final int count = data.getCount();
data.setCount(0);
if (temp.get(data) == null) {
temp.put(data, count);
}
else {
temp.put(data, temp.get(data) + count);
}
}
final Set<Entry<Data, Integer>> set = temp.entrySet();
final LinkedList<Data> result = new LinkedList<>();
for (final Entry<Data, Integer> entry : set) {
final Data mergedData = entry.getKey();
mergedData.setCount(entry.getValue());
result.add(mergedData);
}
Collections.sort(result, new DataComparator());
return result;
}

What method do I need to use to convert a Producer into a Generator

Assume I have a loop that produces items:
for (int i = 0; true; i++) {
ComplexObject co = new ComplexObject(i);
System.out.println(co);
}
The standard behaviour would be to print a description about the objects, but that does not make that much sense for a real application.
I wish to write a custom iterator (generator) now via which I can obtain the elements.
So I need to write the next() method in such a way that it will return one ComplexObject on every next() call, so it is like one step in a loop.
How would I go about doing that? What general mechanism would you advise me to use?
Keep in mind that I dumbed down the real issue to make it explainable, in reality it is of course not as easy as I have stated here and that is why I need to achieve the exact same mechanism that I have asked.
Regards.
ps. (small rant) What is this bleep about only being allowed to post 6 questions per 24-hour period?
For the example you've given above, you'd need to save some state each time an element was retrieved in order to know what sort of state the next element should have.
Here's an example which does precisely that. I have not tested this, but it should clearly demonstrate what I'm suggesting.
public class ComplexObjectIterator implements Iterator<ComplexObject>{
//track start, so you can tell how many elements were retrieved if you want.
private final int start;
private final int end;
private int position;
public ComplexObjectIterator(int start, int end){
if(start>end) throw new Exception("Start must be less "+
"than or equal to end.");
this.start = start;
this.end = end;
this.position = start;
}
public boolean hasNext(){
return position < end;
}
public ComplexObject next(){
if(position >= end)
throw new Exception("No more elements");
ComplexObject obj = new Complexobject(this.position);
position+=1;
return obj;
}
public void remove(){
if(position >= end)
throw new Exception("No more elements");
position+=1;
}
}
If you truly want an endless iterator, this code can easily be changed to provide that kind of behavior.
Note that iterators are typically used to return elements from an underlying data structure. Because here you have no underlying structure, I'd create a new interface called Generator for your purposes to prevent confusing anyone looking at your code.
Far as the limit on questions, its probably there to prevent malicious users from attacking the site with an overload of questions. Not that you're doing that, just offering a possible explanation.

Categories

Resources