I'm making a turn-based RPG game, and my method that sorts all "Actor" objects in the order in which they all attack sorts them completely randomly. I, however, want to improve this method so that an "agility" stat that every actor has is able to improve their roll. I've looked at several methods in the Collections class, and Arrays as well, but didn't seem to find anything that does what I want.
Right now, I'm thinking about getting a random int between 1 and 100, and having the agility score improve the odds. I tried separate ArrayLists for the Integers and a HashMap... but a no go on those.
My method as it is now:
// getFriendlies(), getHostiles(), and attack_order are all ArrayLists
public void calculateAttackOrder() {
attack_order.addAll(getFriendlies());
attack_order.addAll(getHostiles());
Collections.shuffle(attack_order);
}
I appreciate the help!
Your question doesn't go into much detail about the requirements about how agility influences your attack order, but I assume that you meant one of these two:
If one unit has higher agility than another, it always attacks first.
If one unit has higher agility than another, it usually attacks first.
If the first of these is true (units with higher agility always attack first), then what you're looking for is a way of permuting the array subject to the restriction that units with higher agility always end up before units of lower agility, but with everything else done randomly. One way to do this is as follows:
If you have N units, assign the numbers 1 ... N to the units at random. Don't assign the same number twice.
Sort the units into ascending order as follows:
Any unit with higher agility than another unit comes first.
Of two untis that are tied, whichever has the higher random number comes first.
You can show that this approach will arrange the units so that all units of a certain agility are randomly permuted relative to one another, but always come before lower-agility units. This takes time O(n log n) and can be done using Collections.sort, Collections.shuffle, and an appropriate Comparator.
If, on the other hand, you want the ordering to be random but influenced by the agility, you may want to think about using some sort of random distribution that can be controlled by some parameter. For example, you might assign each unit a priority drawn from a normal distribution whose mean is the agility and whose standard deviation is some reasonably large number (say, 20). This would mean that units with more agility are more likely to move before units with less agility, though there is a large amount of randomness. The advantage of this approach is that by tweaking the underlying distribution and its parameters (mean and variance in the case of a normal distribution), you can fine-tune to what extent the agility measure factors in.
As an example of a very simple approach, you might model unit speed as
priority = e(agility / 100) + random(1, 2)
Here, the more agility you have, the greater that your priority is. Increasing the amount of randomness changes the extent to which agility matters. Of course, this might be a bit skewed because each marginal increase in agility has more meaning, so you may want to replace the exponential with something like a logistic function.
Hope this helps!
Use Collections.sort() and provide a Comparator that uses a combination of a random number and agility to compute the rank.
Here's an implementation that would work. Note here that Player is the simplest possible interface that will work:
public interface HasAgility {
/** #return value between 0 and 1 */
double getAgility();
}
public class Player implements HasAgility {
double agility;
double getAgility( return agility; );
// rest of class
}
public class MoveComparator implements Comparator<HasAgility> {
/** Need to calculate the agility once, and keep using that same value, for each player */
private Map<HasAgility, Double> playerAgilities = new HashMap<HasAgility, Double>();
private Double getRandomAgility(HasAgility player) {
Double randomAgility = playerAgilities.get(player);
if (randomAgility == null) {
randomAgility = Math.random() * player.getAgility();
playerAgilities.put(player, randomAgility);
}
return randomAgility;
}
public int compare(HasAgility o1, HasAgility o2) {
return getRandomAgility(o1).compareTo(getRandomAgility(o2));
}
}
public static void main(String args[]) {
List<Player> players= new ArrayList<Player>();
Collections.sort(players, new MoveComparator());
}
The important thing to note is that once calculated, a player's random agile score must be reused again for all later comparisons. This Comparator class s designed to be used once only (the map is required to be empty at the start of its use).
If you can't touch the Actor internals, you need a way of associating a 'roll' for each actor for a complete sort (which probably includes many calls to compare), but which is forgotten by the next sort.
In that case, I would do this:
public class ActorComparable {
Actor actor;
int roll;
public ActorComparable(Actor actor) {
this.actor = actor;
roll = Math.Random(100) + actor.getAgility();
}
public getActor() {
return actor;
}
public getRoll() {
return roll;
}
public int compareTo(Actor that) {
return this.getRoll() - that.getRoll();
}
}
Now when you want to sort an ArrayList of Actors, build an ArrayList of ActorComparables, sort those, and build a resulting list out of the ActorComparables.
class Actor implements Comparable {
private int agility;
private int randomAgility;
private int getInitiative() {
//You can change this method as you see fit
return randomAgility + this.agility;
}
public void generateRandomAgility() {
this.randomAgility = (Math.random() * 100);
}
public int compareTo(Object o) {
Actor other = (Actor)other;
return this.getInitiative() - other.getInitiative();
}
}
Then you can call
for (Actor a : attack_order) {
a.generateRandomAgility();
}
Collections.sort(attack_order);
A whole example using a TreeSet (a SortedSet implementations that guarantees the elements are always ordered, using a Comparator in this case):
class Actor {
private int agility;
private double weight = Math.random();
public Actor(int agility) {
this.agility = agility;
}
public double getComputedAgility() {
return agility * weight;
}
}
public class TestRandomActors {
public static void main(String[] args) {
Collection<Actor> collection = new TreeSet<Actor>(
new Comparator<Actor>() {
#Override
public int compare(Actor a1, Actor a2) {
return
a1.getComputedAgility() > a2.getComputedAgility() ? -1 : 1;
}
}
);
collection.add(new Actor(30));
collection.add(new Actor(31));
collection.add(new Actor(60));
for (Actor actor : collection)
System.out.println("Actor with agility = " + actor.getComputedAgility());
}
}
Related
I am making a replica of a Subway restaurant where you would receive an order in a certain sequence and check if the sequence is valid and if the ingredients are in the menu.
The right order is: 1 bread, 0 to 1 meat, 1 cheese, 1 to 3 extras, 1 to 3 sauces.
Meaning that an order can have a minimum of 4 ingredients (bread, cheese, 1 extra, 1 sauce) and a maximum of 9 ingredients (bread, meat, cheese, 3 extras, 3 sauces).
My question is if there is a more optimized/smarter method to go about validating each and every ingredient than mine?
Code:
// Example order
HashMap<String, HashSet<String>> menu = new HashMap<>();
public static void main(String[] args) {
// Example order
List<String> ingredients = Arrays.asList("Wheat", "Veal",
"Yellow", "Cucumbers", "Onions");
if (!isValid(ingredients)) {
// throw exc;
}
boolean isValid(List<String> ingredients) {
if (ingredients.size() < 4 || ingredients.size() > 9) {
return false;
}
int i = 0;
// Bread
if (!Restaurant.menu.get("Bread")
.contains(ingredients.get(i++))) {
System.out.println("Bread");
return false;
}
// Meat
if (!(Restaurant.menu.get("Meat")
.contains(ingredients.get(i)))
&& !Restaurant.menu.get("Cheese")
.contains(ingredients.get(i))) {
System.out.println("Meat");
return false;
}
if (Restaurant.menu.get("Meat")
.contains(ingredients.get(i))) { // Meat case
if ((!Restaurant.menu.get("Cheese")
.contains(ingredients.get(++i)))) {
System.out.println("Cheese");
return false;
}
}
for (int j = ++i; j < ingredients.size(); j++) {
if ((!Restaurant.menu.get("Extras")
.contains(ingredients.get(j)))) { // Extras
if (j == i) {
return false;
} else {
if ((!Restaurant.menu.get("Sauces")
.contains(ingredients.get(j)))) { // Sauces
return false;
}
}
}
}
return true;
}
Note 1: I know about the rule "If it works, don't touch it" but I feel like this code is getting in the territory of spaghetti code with a bunch of ifs essentially checking similar things with lots of cases and just wanted a second opinion if there is a more optimized way I can't think of right now.
Note 2: I chose HashSet over ArrayList for the menu because it is faster to search.
First, there is nothing really wrong with your general approach.
Having said that, there are some errors.
You are referencing your hashMap as a static value via Restaurant where it isn't declared static.
you are calling isValid() from within a static context (Main) but the method is not declared static.
This is how I might approach it. And it's not to say it is the best approach or style either.
I chose to use an enum to hold details about each menu item, and a class to process the order. Enum's easily lend themselves to this type of requirement. I recommend you read up on them (they are too involved to explain them in detail here). But because they are static they are best served in holding values that are not likely to change.
Each item has two arguments are are processed in the enum's constructor.
so each item has a separate range of its item (the ingredient)
the enum also has a validate method to check to see if a supplied count of items meets the requirements.
The actual order is in the MyOrder class.
it contains a map to hold the counts of each ingredient.
an add method to add the current quantity to the map for a given ingredient.
and a display method to print informative information about the order.
enum Menu {MEAT(0,1), BREAD(1,1), CHEESE(1,1), EXTRAS(1,3), SAUCES(1,3);
private int min;
private int max;
private Menu(int min, int max) {
this.min = min;
this.max = max;
}
public int getMax() {
return max;
}
public int getMin() {
return min;
}
public boolean validate(int count) {
return count >= min && count <= max;
}
}
class MyOrder {
private EnumMap<Menu, Integer> counts = new EnumMap<>(Menu.class);
public void display() {
for (Menu item : Menu.values()) {
int count = counts.get(item);
System.out.printf("%-7s: %d (%d,%d) %s%n",item.name(), count, item.getMin(), item.getMax(),
item.validate(count) ? "" : "Item count out of range.");
}
}
public boolean add(Menu item, int quantity) {
return item.validate(counts.merge(item, quantity, Integer::sum));
}
}
public class Restaurant {
public static void main(String[] args) {
boolean isValid;
MyOrder order = new MyOrder();
isValid = order.add(Menu.BREAD,2);
isValid &= order.add(Menu.MEAT,1);
isValid &= order.add(Menu.CHEESE,2);
isValid &= order.add(Menu.EXTRAS,3);
isValid &= order.add(Menu.SAUCES,2);
if (isValid) {
System.out.println("Your order is accepted.");
} else {
System.out.println("Order is not in compliance");
order.display();
}
}
}
prints
Order is not in compliance
MEAT : 1 (0,1)
BREAD : 1 (1,1)
CHEESE : 2 (1,1) Item count out of range.
EXTRAS : 3 (1,3)
SAUCES : 2 (1,3)
Also remember that the result of any if statement is a boolean. So the inequality can be assigned to a boolean and then tested later (if it makes sense to do so). Also notice that I don't check for a legitimate order until the end. Some might prefer to signal a bad order as soon as it occurs. This is a design choice.
For more information check.
Enum
EnumMap
Map.merge
The problem I see with your proposed solution is that it is trying to solve all problems at once, instead of solving them separately. That makes your code hard to read and understand, in my opinion. While it may work, the more business rules you add, the harder this will get.
So what can you do about it? Separate the concerns.
The first concern is cassifying an ingredient: is it bread, cheese, meat, extra, sauce? You could e.g. create a class Menu with a method getCategory() (instead of just using a HashSet for menu) that returns the category, and the return value could be an Enum.
The seond concern is order. You could check the order of the list using a custom Comparator. See this question for details.
The third concern is number of ingredients of a certain category. Given that you can find out the category of an ingredient, you can count how many you have and check if it is the right amount.
There are more things to be said about how to achieve any of this, I just wanted to point you in a possible direction.
I have a question about java collections such as Set or List. More generally objects that you can use in a for-each loop. Is there any requirement that the elements of them actually has to be stored somewhere in a data structure or can they be described only from some sort of requirement and calculated on the fly when you need them? It feels like this should be possible to be done, but I don't see any of the java standard collection classes doing anything like this. Am I breaking any sort of contract here?
The thing I'm thinking about using these for is mainly mathematics. Say for example I want to have a set representing all prime numbers under 1 000 000. It might not be a good idea to save these in memory but to instead have a method check if a particular number is in the collection or not.
I'm also not at all an expert at java streams, but I feel like these should be usable in java 8 streams since the objects have very minimal state (the objects in the collection doesn't even exist until you try to iterate over them or check if a particular object exists in the collection).
Is it possible to have Collections or Iterators with virtually infinitely many elements, for example "all numbers on form 6*k+1", "All primes above 10" or "All Vectors spanned by this basis"? One other thing I'm thinking about is combining two sets like the union of all primes below 1 000 000 and all integers on form 2^n-1 and list the mersenne primes below 1 000 000. I feel like it would be easier to reason about certain mathematical objects if it was done this way and the elements weren't created explicitly until they are actually needed. Maybe I'm wrong.
Here's two mockup classes I wrote to try to illustrate what I want to do. They don't act exactly as I would expect (see output) which make me think I am breaking some kind of contract here with the iterable interface or implementing it wrong. Feel free to point out what I'm doing wrong here if you see it or if this kind of code is even allowed under the collections framework.
import java.util.AbstractSet;
import java.util.Iterator;
public class PrimesBelow extends AbstractSet<Integer>{
int max;
int size;
public PrimesBelow(int max) {
this.max = max;
}
#Override
public Iterator<Integer> iterator() {
return new SetIterator<Integer>(this);
}
#Override
public int size() {
if(this.size == -1){
System.out.println("Calculating size");
size = calculateSize();
}else{
System.out.println("Accessing calculated size");
}
return size;
}
private int calculateSize() {
int c = 0;
for(Integer p: this)
c++;
return c;
}
public static void main(String[] args){
PrimesBelow primesBelow10 = new PrimesBelow(10);
for(int i: primesBelow10)
System.out.println(i);
System.out.println(primesBelow10);
}
}
.
import java.util.Iterator;
import java.util.NoSuchElementException;
public class SetIterator<T> implements Iterator<Integer> {
int max;
int current;
public SetIterator(PrimesBelow pb) {
this.max= pb.max;
current = 1;
}
#Override
public boolean hasNext() {
if(current < max) return true;
else return false;
}
#Override
public Integer next() {
while(hasNext()){
current++;
if(isPrime(current)){
System.out.println("returning "+current);
return current;
}
}
throw new NoSuchElementException();
}
private boolean isPrime(int a) {
if(a<2) return false;
for(int i = 2; i < a; i++) if((a%i)==0) return false;
return true;
}
}
Main function gives the output
returning 2
2
returning 3
3
returning 5
5
returning 7
7
Exception in thread "main" java.util.NoSuchElementException
at SetIterator.next(SetIterator.java:27)
at SetIterator.next(SetIterator.java:1)
at PrimesBelow.main(PrimesBelow.java:38)
edit: spotted an error in the next() method. Corrected it and changed the output to the new one.
Well, as you see with your (now fixed) example, you can easily do it with Iterables/Iterators. Instead of having a backing collection, the example would've been nicer with just an Iterable that takes the max number you wish to calculate primes to. You just need to make sure that you handle the hasNext() method properly so you don't have to throw an exception unnecessarily from next().
Java 8 streams can be used easier to perform these kinds of things nowadays, but there's no reason you can't have a "virtual collection" that's just an Iterable. If you start implementing Collection it becomes harder, but even then it wouldn't be completely impossible, depending on the use cases: e.g. you could implement contains() that checks for primes, but you'd have to calculate it and it would be slow for large numbers.
A (somewhat convoluted) example of a semi-infinite set of odd numbers that is immutable and stores no values.
public class OddSet implements Set<Integer> {
public boolean contains(Integer o) {
return o % 2 == 1;
}
public int size() {
return Integer.MAX_VALUE;
}
public boolean add(Integer i) {
throw new OperationNotSupportedException();
}
public boolean equals(Object o) {
return o instanceof OddSet;
}
// etc. etc.
}
As DwB stated, this is not possible to do with Java's Collections API, as every element must be stored in memory. However, there is an alternative: this is precisely why Java's Stream API was implemented!
Streams allow you to iterate across an infinite amount of objects that are not stored in memory unless you explicitly collect them into a Collection.
From the documentation of IntStream#iterate:
Returns an infinite sequential ordered IntStream produced by iterative application of a function f to an initial element seed, producing a Stream consisting of seed, f(seed), f(f(seed)), etc.
The first element (position 0) in the IntStream will be the provided seed. For n > 0, the element at position n, will be the result of applying the function f to the element at position n - 1.
Here are some examples that you proposed in your question:
public class Test {
public static void main(String[] args) {
IntStream.iterate(1, k -> 6 * k + 1);
IntStream.iterate(10, i -> i + 1).filter(Test::isPrime);
IntStream.iterate(1, n -> 2 * n - 1).filter(i -> i < 1_000_000);
}
private boolean isPrime(int a) {
if (a < 2) {
return false;
}
for(int i = 2; i < a; i++) {
if ((a % i) == 0) {
return false;
}
return true;
}
}
}
I'm currently working on a Bukkit plugin to claim custom areas, and I'm using rectangles (and .intersect()) to check if regions overlap before creating a claim.
I'm trying to figure a way where I don't need to check every single existing claim (of which there eventually will be tens of thousands) as surely this will take quite some time. I'll also need to check for claim owners when players do things such as break blocks or place blocks.
In my current system (which doesn't allow custom claim sizes, only squares) I only need to check at most about 10 claims because I can detect claims within the vicinity of the claim (at most 64 blocks away which is the max radius of claims in this system) but now the claim sizes can be infinitely large in theory with the new system.
Is checking all the rectangles going to take a massive amount of time? Am I being dumb, is there a way to check for rectangles within the vicinity even while the size is unlimited?
First of all checking thousands of rectangles is not gonna be a big deal for java(or your Plugin). Its simple math and should be done in millisecs. To deal with your owner Problem i would recommend you to create my own rectangle and owner class. So your rectangle can have a defined owner and you can simply check if the player is the owner of the area he is in right now.
public class custom_Area extends Rectangle{
private owner o;
public owner getOwner() {
return o;
}
public void setOwner(owner o) {
this.o = o;
}
}
EDIT:
I just tested it by creating 100.000 random rectangles and checking if one of them intersects with others.
--Custom rectangle class
public class area extends Rectangle{
private owner o;
public area(owner o, int i, int i1, int i2, int i3) {
super(i, i1, i2, i3);
this.o = o;
}
public owner getO() {
return o;
}
public void setO(owner o) {
this.o = o;
}
}
--Custom owner class
public class owner {
String name;
public owner(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
--Main class
public class Rectanglesearch {
public static area a[] = new area[100000];
public static owner o[] = new owner[10];
public static int intersectCounter = 0;
public static int ownerCounter = 0;
public static void main(String[] args) {
for(int y = 0; y<10;y++){
o[y] = new owner("y");
}
for (int i = 0; i < 100000; i++) {
a[i] = new area(o[(int)(Math.random() * 10)],random(),random(),random(),random());
}
checkArea(a[10]);
checkOwner(o[3]);
System.out.println("Area a[10] intersects with "+intersectCounter+" out of "+a.length);
System.out.println("Owner o[3] owns "+ownerCounter+" areas out of "+a.length);
}
public static int random(){
return (int)(Math.random() * 100000) + 1;
}
public static void checkArea(area ab){
for (area a1 : a) {
if (ab.intersects(a1)) {
intersectCounter +=1;
}
}
}
public static void checkOwner(owner ob){
for (area a1 : a){
if(a1.getOwner()==ob){
ownerCounter +=1;
}
}
}
}
method checkArea(area ab) returns you how man areas intersects with area ab
method checkOwner(owner ob) return you how man areas are owned my ob
Consider storing your rectangles in an acceleration structure such as a quadtree. To test a new rectangle against the existing set, you'd navigate down the tree to the node that would contain it, testing against the rectangles in each node along the way, but ignoring the rectangles in all the nodes you don't traverse. This quickly eliminates lots of rectangles that can't possibly intersect the new one, without having to test each one individually.
Other acceleration structures are also possible as alternatives, such as binary space partitioning. Read about spatial indexes for a list of several others that may be relevant.
Adding new rectangles to the set doesn't happen very often, so performance probably isn't a big concern. But I'd imagine that your plugin also needs to check whether a specific coordinate (such as a block) is within one of the claimed regions, and that may happen much more often — potentially every frame — so it really does need to be fast. A quadtree or other acceleration structure will be valuable for that.
I should to implmenets an application to control a race of car.
So in a race I have for example 5 car. I want to know the position of this car in every turn, the last time, and best round for all car. Now I wanto to know what structure of data is the best solution for this.
I have create this object:
package Auto;
import java.security.Timestamp;
public class Macchina {
Integer corsia;
Integer giro;
Timestamp ultimoTempo;
Timestamp migliorTempo;
public void setCorsia(Integer corsia) {
this.corsia = corsia;
}
public void setGiro(Integer giro) {
this.giro = giro;
}
public Timestamp getUltimoTempo() {
return ultimoTempo;
}
public void setUltimoTempo(Timestamp ultimoTempo) {
this.ultimoTempo = ultimoTempo;
}
public Timestamp getMigliorTempo() {
return migliorTempo;
}
public void setMigliorTempo(Timestamp migliorTempo) {
this.migliorTempo = migliorTempo;
}
public Macchina(int c, int g)
{
this.corsia=c;
this.giro=g;
}
public int getCorsia(){
return corsia;
}
public int getGiro(){
return giro;
}
}
This class is used to stored the information for the car in the race.
Keep in mind that A car has a speed.
The track has the distance and cars on it.
Your object design should reflect that.
Depending on how many properties a car has, you might not need a car object.
If you plan on extending your car racing program, you might want an abstract vehicle class. Especially if tracks become complicated, and cars become highly varied.
If you just want to find something like the position of a car at a discrete time and when a car reaches a certain distance then following might work for your purposes.
Pseudo code:
track {
int length, time, Maxtime;
array<int> carSpeeds, carPositions;
main(){
Maxtime=100;
for(time=1; time<MaxTime; time++){
for(int j= 0; j < carSpeeds.size(); j++){
carPositions[j] = time*careSpeed // reset for each lap.
// if position is greater than track length, you might have a winner or a tie.
}
}
}
}
You can add in a max distance and other things.
If you want to eventually model warp capable space ship racing to various waypoints, submarine racing between underwater cities, and dirtbike racing with shortcuts, the code should have high cohesion, and low coupling. But the first of many iterations would probably look similar to the above.
I'm trying to implement Dijsktra's Algorithm from CLRS - Introduction to Algorithms book,however, i'm having trouble about implementing a priority queue with Comparator interface. This is my Vertex class as you can see;
public class Vertex {
public boolean explored;
public int vertexID;
public LinkedList<Vertex> adjacencyList;
public LinkedList<Edge> edgeSet;
public int shortestDistance;
public Vertex predecessor;
public Vertex(int vertexID){
this.vertexID = vertexID;
this.explored = false;
this.adjacencyList = new LinkedList<>();
this.edgeSet = new LinkedList<>();
this.shortestDistance = Integer.MAX_VALUE;
this.predecessor = null;
}
}
So initially shortestDistance attribute is declared to Integer.MAX_VALUE. Furthermore, you can see the class which implements from Comparator, is used for priority queue.
public class WeightComparator implements Comparator<Vertex> {
#Override
public int compare(Vertex o1, Vertex o2) {
return Math.min(o1.shortestDistance, o2.shortestDistance);
}
}
I'm sure that the whole implementation doesn't have any logical errors due to my some tests,however, in some tests it fails. I create a reference to my queue with this statement
PriorityQueue<Vertex> queue = new PriorityQueue<>(N, weightComparator);
where N is the size of the queue. So please correct me if i'm wrong about the way how it works. When you poll an item from it, it will remove the item which has least priority ?. Hope it had been clear to understand my problem, and i will appreciate a lot if anyone can help about this. So thanks anyway
Math.min gives you the smaller of two values. Returning that as a compare value is wrong.
A compare return value of <0 means the first value is smaller than the second, but if you return Math.Min(5, 3), you will get 3, which is >0, which means that the comparator will tell it that 3 > 5. Which is wrong.
What you are looking for is:
public int compare(Vertex o1, Vertex o2) {
return Integer.compare(o1.shortestDistance, o2.shortestDistance);
}
Unless shortestDistance can be negative, your comparator can never return a negative number. It is therefore not a correct implementation.
Conceptually, a comparator for primitives should return a subtraction:
return o1.shortestDistance-o2.shortestDistance;
or the other way around if you want descending. But you need to beware of overflow issues.