How to efficiently store Roll Playing Item properties in Java? - java

How to efficiently store RPG Item properties in Java?
I'm designing a text based RPG where random items are spawned in when looting chests for example.
Currently I'm storing my Item data in a textfile. When a random item has to be generated a scanner reads the file, collects the values, takes probability in account, and then creates a new object accordingly.
Here is an example of some items of the 'Consumable' class.
The values are listed accordingly:
name, probability, level at which the item gets added to the itempool, weight, value, +health, +damage, Effect
example of textfile:
Twinkie 10 1 1 10 10 10 0
Banana 10 1 1 5 5 0 0
Potato 20 1 1 5 5 0 0
Protein_Shake 5 5 1 30 10 10 1
Beer 5 5 1 5 10 10 1
If the Effect value equals 0, a new default Consumable gets created with effect 'null'.
if the Effect value is 1, a function uses a switch(name) statement to find the effect belonging to the item and then
passes the effect as an argument in the 'Consumable constructor'.
I'm certain that this is a very unoriganized and inefficient way to go about this sort of thing. Anyone has a suggestion on how to handle this? I want to do it right.
Should I maybe create an ConsumablePool class where I just create all the items immediatly or store the item information elsewhere?

There are a few ways to optimize your current task, which seems to be:
Consumable nextItem = getRandomConsumable(List<Consumable> candidates);
Perhaps you must also choose based on the current level:
Consumable nextItem = getRandomConsumable(int level, List<Consumable> candidates);
Currently, you state that each time you call getRandomConsumable() you generate the list of candidates from a disk file (you seem to have already written this code). Your instinct is correct. The 'cost' of this operation is relatively high. Reading from disk compared to reading from a memory cache of objects will cause poor performance. Assuming that the disk file does not change during the game, your application should be creating the candidate list once (at startup), and using this List each time the next item must be chosen.
In the case where candidates are based on level, further optimizations can be done. Consider dividing the candidates by level, such as:
List<Consumable> candidates = createCacheFromDisk(); // Run at startup
Map<Integer, List<Consumable>> itemsByLevelMap = candidates .stream().collect(Collectors.groupingBy(Consumable::getLevel));
This will further break out the list of candidates into Lists by level, allowing
List<Consumable> level1Items = itemsByLevelMap.get(1);
This general approach of caching will greatly improve the performance of your application; choosing a random item from a List which has already been generated/cached performs far greater than generating a new List (from disk) each time.

Related

Greedy algorithm to maximize score

I am trying to implement a method in java which takes as input an ArrayList of objects that have a deadline, a score, and object number. Each is accessible as:
game.get(i).number
game.get(i).score
game.get(i).deadline
Where game is the ArrayList and i is the index.
I want to maximize the score I can achieve while considering the deadline for each game. Each game has to be completed before the deadline. e.g. if deadline is 5, it has to be completed at 4 or earlier.
For example, here is a sample ArrayList with it's corresponding data organized in descending order of the score worth:
Index Game number Score Deadline
0 3 76 3
1 4 66 2
2 1 52 2
3 6 51 4
4 5 47 2
5 2 23 1
Then the following ArrayList could be the result:
maximizedScore = [4,1,3,6] -> here each index represents the hour. Even though game 3 has a higher score than game 4 and game 1, we put game 4 in index 0 and game 1 in index 1 as they both have a deadline of 2 so they must be completed before then. However, game 3 has a deadline of 3 so we can do it at hour 2 in order to maximize the total score. Note that game 5 also has the same deadline as game 4 and 1, but it has less points than both so we sacrifice it.
Edit: Each index (0,1,2,3,4,..) represents the hour while the value at the index represents the game number being completed at that hour.
Edit 2: We can only do one game per hour. So if the latest deadline in the data is x, then we can do a max of x games. I.e. 4 in this example.
This provides a maximum score of 245.
The order of games does not matter as long as they are before the deadlines. For example: [1,4,3,6] would also be acceptable as both game 4 and game 1 are being completed before the deadline.
Unfortunately, I have no idea how to go about implementing this. Hopefully I provided a clear idea of what I need.
Any help is appreciated!
I will try to give a general approach without writing any code yet.
I will call the result you are looking for the "schedule". As you explained in your question, the maximum number of games you can place in your schedule is indicated by the maximum value in the "Deadline" column of your input data. I will call this the "time limit".
You are asking for a greedy algorithm. Such an algorithm will generate and check all game schedules to find the one that maximizes your score.
Basic approach
The most basic approach you can take is to generate all permutations of games of length "time limit" or less. For each one of them, you would then check:
is it indeed a valid schedule (is each game in the tentative schedule payable at the time it is placed?),
does this tentative schedule yield a total score greater than the best score obtained so far ? If so, you "keep" this solution and continue trying other schedules
The main difficulty in this first approach is to correctly generate all the possible schedules. I.E:
0
0 1
0 1 2
0 1 2 3
0 1 2 3 4
0 1 2 4
0 1 2 4 3
0 1 3
....
1
1 0
1 0 2
....
Optimizations
Now, the basic approach above has some ineficiencies. For instance, when you are building the possible schedule and you build one that is impossible due to the deadline criteria. For example:
3 5
is an impossible schedule because game "5" has a deadline of 1 (meaning it can only be played as the first game or not a all). If you can recognize this, then you realize that any schedule that starts with 3 5 (3 5 0 ..., 3 5 1 ...) is also impossible.
You can exploit this fact if you generate the schedule in a clever order and skip the generation of the schedules that you know are not going to be possible.
Tips for algorithm
I would suggest generating the schedules in a recursive manner. You have all your game ids (0, 1, etc) in a collection. You start with the lowest index: 0 , remove it from the collection of games and place it in your tentative schedule:
Schedule: 0
You check if it is a valid schedule (i.e. if the game you just placed in your schedule can be played at that time). Let's assume that it is the case (in this specific case as it is the first game on the schedule, it should always be possible). You also check if this tentative schedule yields a better score than anything you found so far.
Then you try to add the lowest game left in your collection of games left to play. In this case, 1. Same thing you remove 1 from the collection of games and place it on your schedule:
Schedule: 0 1
Same thing, you check if this is a valid schedule. You already know that game 0 can be played first, you check if game 1 can be played in second position. Bad luck, the time limit of game 1 prevents you from doing it. It is not worth exploring the schedules that start with 0 1 anymore. You remove 1 from your schedule, but you do not replace it in the games collection: as it is impossible to play in second position, it is not worth checking it further. Instead, I would place it in a collection of games that were "excluded" at the current exploration level.
You then move on to the next game remaining in the game collection, 2 and follow the same routine. If you are able to place a game on your schedule that is compatible with the time limit, you can continue adding games on you schedule with the ones that remain in the games collection. Be careful to recognize that there may be cases where you completely fill your schedule but there are still games left (or vice versa, you still have holes in your schedule but there are no games that can be played left).
When you have run out of options, it is time to remove games from the schedule. Remove the last game you placed on your schedule and place it back in the games collection. Also place the games that you have excluded at the current level back into the games collections. You can continue the schedule generation. Be careful not to take the game you just removed at this level again though.
First thing to do would be to sort your games by their score, because we want a game with a highest score first:
if you have an ArrayList of object say ArrayList<Game> inputData = new ArrayList<>(); you can sort it like this in java:
inputData.sort(Comparator.comparing(Game::getScore));
now the lowest score is first and the highest one is last.
Since we have to keep track of deadlines I would put them into a separate ArrayList and remove them one by one if needed:
ArrayList<Integer> deadlines = new ArrayList<>();
for(Game game : inputData) {
if (!deadlines.contains(game.deadline))
deadlines.add(game.deadline);
}
After that the idea is to iterate through inputData one by one and keep track of previousDeadline and currentDeadline. If we that the currentDeadline is greather than previousDeadline then that means that we can't accept any deadline that is before that. This can be done by checking deadlines list that we created earlier and removing deadlines from it.
int previousDeadline = -1; // initial value because there is no -1 deadline
for(int i = inputData.size()-1; i >= 0; i--) {
int currentDeadline = inputData.get(i).deadline;
// compare previous and currentDeadline and remove from deadlines list
if(previousDeadline != -1 && previousDeadline < currentDeadline) {
for(int j = 1; j < currentDeadline; j++) {
deadlines.remove(Integer.valueOf(j));
}
}
// add to result only if present in deadlines list and update previous deadline
if (deadlines.contains(currentDeadline) ) {
result.add(inputData.get(i).number);
previousDeadline = inputData.get(i).deadline;
}
}
If you give it a input that you provided you will get a list with [3, 4, 1, 6]
Here is a pastebin with code

A collection which holds a specific number of values before being overwritten

I need to use a Collection type in Java which will allow me to add values input by the user one at a time. When a new value is added it is added at index 0 and the previous index 0 value is moved to index 1 etc. Once there are 20 values I then want it to start replacing the 1st value, then the 2nd value and so on whilst moving the other values up 1 index.
i.e once all 20 values are filled, the next input becomes index 0, index 1 -> index 2 and so on. index 20 will be forgotten.
I have so far used an ArrayList of Integers but now I have come across this problem. I am wondering if another type of Collection would be best? The Collection will more than likely hold duplicate values.
From the 20 values I will want to sort in ascending order and then find the average of the top 8. I am currently doing that by copying to a second Arraylist within a method, sorting and then adding up the top 8 values and dividing by 8. This way the master list remains the same.
I am not sure there is an efficient way to do what I need with an arraylist.

Chopping of java array list

I am trying to write Java code for the following problem.But I am not able to find out a optimized way to solve this .
I have an array list of time and consumption of coffee as follows. I want to calculate the consumption of coffee every hours and if there is no consumption for a particular hour the next hours first entry will be the total consumption for that hour.
For Example :
I have the following array list
Time consumption of coffee
2:15 5 cups
2:30 6 cups
2:45 7 cups
3:05 2 cups
3:45 6 cups
5:05 1 cups
5:30 2 cups
7:15 1 cup
so I want to calculate what is the total consumption for hour 2 which will be in that case 18 cups from 2:00 to 3:00 .again for 3:00 to 4:00 it will be 8 cups. As there is no entry from 4:00 to 5:00 amount of consumption in that case should be amount of coffee consumed at 5:05 which is 1 cup.I want my result till 7 o'clock. As we don't have anything at 6:00 -7 :00 then it will be 1 cup which was value at 7:15.
So I want a final result of total consumption from 2:00 to 7:00 distributed every hour as array list as following object
obj1 = T<2:00,3:00,18>
obj2 = T<3:00,4:00,8>
obj3 = T<4:00,5:00,1>
obj4 = T<5:00,6:00,3>
obj5 = T<6:00,7:00,1>
finalList = <obj1,obj2,obj3,obj4,obj5>
I am not able to get how to chop the list in hourly way and look at the next value as well.
You completely lost me as to the logic of filling an empty hour with first value from the next hour. But I'll give it a shot anyways.
My approach assumes your input data is in chronological order from earlier to later.
Define a CoffeeConsumption class with two members, LocalTime and Integer for your two values.
Create a Map, perhaps a SortedMap such as TreeMap. The key is of type Integer, and represents the hour of the day from 0 to 23. The value is of type Set, perhaps a SortedSet such as TreeSet to store your custom objects defined above.
For each of you custom objects, grab its LocalTime and call getHour. Use that hour number to find the matching key in the Map, retrieve the Set, and add your item.
To get totals, loop the Map, and loop each Set. To save the totals, create a new SortedMap with Integer as key for hour-of-day and Integer as value for the hour’s total number of cups consumed.
Note that there is no need to save explicitly the end-of-hour time as you showed in your Question. You can always calculate that value: call LocalTime::plusHours( 1 ).
To support that move-first-item-to-previous-hour-if-empty feature, follow the logic I just outlined in that hyphenated title. When adding a coffee object to the Map with no existing key yet put there for that hour, first subtract one from the hour and look in the Map for that subtracted number as key. If the subtracted key is not present in the Map, put it, create a fresh empty Set in which to deposit the coffee object that would otherwise have gone to its own hour. Caveat: This feature has a bad smell and suggests something is wrong with your design or the requirement is misunderstood.

Implementing A Nondeterminisic Finite Automaton(NFA)

I'm trying to a develop a simulation that executes a non deterministic finite automaton in Java. The first command line argument is a text file that defines the machine. The second argument is an input string. If it accepts the string, it prints to standard output "accept" followed by a list of accept states it can end in. If it rejects, it outputs "reject" followed by a list of all possible end states.
For example, the text:
state 1 start
state 2
state 7 accept
transition 1 0 1
transition 1 1 1
transition 1 0 2
transition 2 0 2
transition 2 0 1
transition 2 1 1
transition 2 0 7
transition 7 0 7
transition 7 1 7
which looks like:
with an input string of 10 would output
reject 1 2
and with an input string of 000 would output
accept 7
I need advice on what data structures to use. I thought about using hashmaps and sets but I'm not sure.
I think you should transform your automaton into a deterministic one and then do the obvious.
There are three common ways of implementing a finite state machine in software:
Represent the transition function as a table (2D array) and upon each character read, look up the next state in it.
Use nested switch statements for the current state and input character to explicitly define the next state in code.
Use the State Pattern: Represent each state as a class with a method returning the next state, given an input character.
Since you'll need to build your automaton at run-time, the last two options don't really apply, so you should go with the lookup table. If your alphabet is small, you can write down all transitions. If the alphabet is huge, this might waste a lot of space and you can think about table compression which used to be an active field of research.
For the Downvoters: You cannot write a deterministic program that “executes” a non-deterministic automaton. However, by a rather fundamental theorem of theoretical computer science, you can transform any non-deterministic finite state automaton into a deterministic one by a fully automated procedure, that is, a deterministic algorithm that can easily be implemented in software. Every computer science student should have done this procedure at least once using pencil and paper. If you do so, you'll quickly see how to keep track of which states of the original automaton went into which states of the deterministic one. Then, simply simulate the latter one, see in what state it ends up and output the states of the original non-deterministic automaton that constitute it.
NFA means you can have set of states at a time. So to represent current state of NFA use Set interface.
Set interface guarantees that there won't be any duplicates of state in . This is important as NFA has more than one state at a time. If you use any other data set this gurrentee is not there.
In case of NFA chance of having duplicate state in each transition is exponential. But set of state is always finite. Set interface guarantees that your current set will be filled with duplicates.
For space and performance you can use Enumset as Enumset use bit vectors to store state internally.
Algorithm:
initialise to start state
Process string from right to left starting from index 0;
for character at update the using the state transition;
If for any of this transition leads to final state which means that string is accepted.

Presidential Seating Backtracking

I have a problem in front of me that I can't grasp, even though I grasp the concept of backtracking well. I have been unable to find any information on this specific problem, and I am unaware if it goes by another name. The problem is to construct a backtracking algorithm that will print out the list of combinations of seating that N number of "presidents" can sit at a round table without sitting next to the same person twice. For example, you give it an input 8, it gives you the output,
1 2 3 4 5 6 7 8
1 3 5 2 6 8 4 7
1 4 2 7 5 8 3 6
Now I have constructed my own backtracking algorithms for simple games (checkers, n-queens, tic-tac-toe), however these that I have worked with have started with a blank canvas and didn't require this type of implementation. What I am asking for is a bit of guidance and insight, or links to more information to this problem as all that was given was about a page and a half, mostly talking about how the combination 1 2 3 4 is effectively the same as 1 4 3 2 in this particular problem. This needs to be done using a backtracking method and that's what is making me confused. If we were give free rein to find solutions I could easily do it. Any help is appreciated.
Thanks!
I'm thinking you'll have to add information about who sat next to who already to the backtracking algorithm state:
You start with, say 1. You seat 2 next to him, and record this "pairing" in the current partial solution. (Literally just an unordered pair of president IDs. An easy way of implementing an unordered pair of numbers is to have the constructor of that class store the smaller ID in the first element of the pair, and the greater ID in the second element.) So you have a partial solution 1 2 …, with the pairing 1-2.
When you reach to a complete solution, you add all its pairings to a "global" list of pairings. This will be used in future searches. I.e. if you get the solution 1 2 3 4 5 6 7 8, you save the pairings 1-2, 2-3, 3-4, 4-5, 5-6, 6-7, 7-8, 1-8.
Now, when doing a search, you have a partial solution x y z … – the last president seated so far is z, and the pairings in this partial solution are x-y and y-z. To seat the next president, you need to look at every president that's:
Not seated so far. (And thus not included in the partial solution's pairings.)
Not paired with z in the global pairings.
If no such president is available, you discard the partial solution and backtrack.
That said, I'm working off the top of my head here, so you'd be well advised to actually doublecheck if I missed any edge cases by using a straightforward brute force implementation and comparing the results.
A very rough pseudocode will look like this:
global flag // I know it is a bad practice but can't think of a better idea at this point
back (PresidentList, chairNumber, computedList)
if (PresidentList.size() == 0)
if (computedList doesn't violate any global distribution)
add computedList to globalList
add distirbution of computedList to global distirbution
set flag;
else return;
else if (flag is set and chairNumber > 2) return;
else if (flag is set and chairNumber = 2) un set flag; return;
else if (chairNumber == maxChairs-1)
// at this point there should be only one president in PresidentList
if (PresidentList.back() can sit with computedList.back() and computedList.front())
remove president from PreseidentList, add president to computedList
back(PresidenList, chairNumber+1,computedList)
else return;
else
for every president in PresidentList
if (president can sit with computedList.back())
remove president from PreseidentList, add president to computedList
back(PresidentList,chairNumber+1,computedList);
You always check if a president can sit near some other president only according to the global distribution variable.
In the main function just run a loop for PresidentList that will start back with that president being as the first one.
The idea of the flag is as follows: when you add a distribution and a list to the global list, back runs already a lot of branches which have the same prefix as the list you just added and hence are already bad. To close all those lists you force the back to stop branching and return to the point when it can set a new 2nd element in the list to compute i.e. get back to the loop if the last else branch of the function.

Categories

Resources