Robocode HashTable/method modifying values in it issues - java

Currently in RObocode I have a hashtable that has names as the keys and point2D objects as the values. One of the properties of these objects is the double lastSeen which is the time since the robot has been seen. Everytime I scan a robot, I would set this value to 0, this value also helps my radar become the oldest scanned radar.
public void onScannedRobot(ScannedRobotEvent e) {
String name;
EnemyInfo enemy = (EnemyInfo) enemies.get(name = e.getName());
// if the enemy is not already on the hashtable, puts it on
if (enemy == null) {
enemies.put(name, enemy = new EnemyInfo());
}
enemy.bearing = e.getBearing();
enemy.velocity = e.getVelocity();
enemy.heading = e.getHeading();
enemy.energy = e.getEnergy();
enemy.lastSeen = 0;
above is the code that upon scanning a robot, shoves it into the hashtable as an object and sets the object's lastseen property to 0;
I have made a method that increases the value (by 1) of every object's lastSeen variable by returning an enumeration of all object's lastSeen variables and adding one to each of them.
Method is below:
public static void advanceTime(EnemyInfo e) {
if (!enemies.isEmpty()) {
int i = 0;
Enumeration enum3 = enemies.elements();
do {
(e = (EnemyInfo) enum3.nextElement()).lastSeen = (e = (EnemyInfo) enum3
.nextElement()).lastSeen + 1;
i++;
System.out.println("Added one to.." + i);
} while (enum3.hasMoreElements());
}
}
However, I cannot call this method if there is nothing in the hashtable, which is why I put an if to stop the method from executing if there is nothing in the hashtable. Don't know the reason for this..
Any other method for doing this efficiently and effectively??

Storing Calculated Values
In a lot of cases, it is better to store the information to calculate a value rather than a calculated value itself. For instance, instead of storing a person's age (eg 20 years old), one would store the person's birthday (eg 5-5-95). If you store someone's age, you have to update it every year. If you store the birthday, you can simply calculate the age whenever you want.
Robocode
Back to the problem at hand. Instead of storing how old a piece of information is (age), store when the information was created (birthday). ScannedRobotEvent has a getTime() method that you can use to get the "birthday" of the scan event. Store this number. Then if you need to know how old the stored ScannedRobotEvent is, subtract this stored time from current time. This will bypass the need for an advanceTime method. To implement this update enemy.lastSeen to this:
public void onScannedRobot(ScannedRobotEvent e) {
String name;
EnemyInfo enemy = (EnemyInfo) enemies.get(name = e.getName());
// if the enemy is not already on the hashtable, puts it on
if (enemy == null) {
enemies.put(name, enemy = new EnemyInfo());
}
enemy.bearing = e.getBearing();
enemy.velocity = e.getVelocity();
enemy.heading = e.getHeading();
enemy.energy = e.getEnergy();
enemy.lastSeen = e.getTime();
Exception
There are some cases where you want to store a calculated value. GPA is a good example. GPA is a calculated value, but to calculate it you need to access every course someone has taken. In this case, if could be faster to store GPA and remember to update it whenever someone completes a new course.

Related

How to map ArrayList elements with specific values and then print it

ArrayList<Integer> companiesId = new ArrayList<>();
int numberOfCompanies = 10; // Just for example
...
float profit;
Scanner input = new Scanner(System.in);
for(int i = 0; i < numberOfCompanies; i++) {
int companyId = input.nextInt();
if (!companiesId.contains(companyId)) {
companiesId.add(companyId);
}
if (companiesId.get(i) == 1) {
profit = 1000;
} else if (companiesId.get(i) == 2) {
profit = 2000;
}
}
Now I want to print all the companyIds from the ArrayList with the profit entered for each id, but I don't know how to do it with the ArrayList.
It should be:
1 1000
2 2000
...
You cannot do what you ask because part of the information you need to print (profit) is lost. What you need to do first is to create a class that holds a company ID and the profits. With the new version of Java, you can create a record that will hold such information. A Java Record is nothing more than a POJO that is identified with that new keyword (record) and does not require you to create all the boilerplate code. Your record class will look something like this:
public record CompanyRecord(int companyID, float profit) {
}
You don't even need to override toString(). That is, unless you want to print the contents of the record in a different way than the default. Then, you will need to create a list of CompanyRecord objects:
ArrayList<CompanyRecord> companies = new ArrayList<>();
Then, you can do whatever you need. For example, I created this simple demo that create a list of 10 company records and uses the loop counter to set the company ID and as a multiplier for the profits. Lastly, it prints out the record to the console.
public class CompanyRecordDemo {
public static void main(String[] args) {
ArrayList<CompanyRecord> companies = new ArrayList<>();
float profit = 1000.0f;
for (int i = 1; i <= 10; i++) {
CompanyRecord rec = new CompanyRecord(i, profit * i);
companies.add(rec);
System.out.println(rec);
}
// do whatever you need with the list...
}
}
The output of this small program is:
CompanyRecord[companyID=1, profit=1000.0]
CompanyRecord[companyID=2, profit=2000.0]
CompanyRecord[companyID=3, profit=3000.0]
CompanyRecord[companyID=4, profit=4000.0]
CompanyRecord[companyID=5, profit=5000.0]
CompanyRecord[companyID=6, profit=6000.0]
CompanyRecord[companyID=7, profit=7000.0]
CompanyRecord[companyID=8, profit=8000.0]
CompanyRecord[companyID=9, profit=9000.0]
CompanyRecord[companyID=10, profit=10000.0]
This is probably the simplest way to accomplish what you need. You will need to use Java 14 or later to make use of Java Records, but I recommend you use the latest version.
UPDATE: One important thing to note is that Java records are immutable. So, they have no setters (mutator methods). You will have to set the values through the constructor and values cannot be changed afterwards. You can access (get) the property values by calling a method that has the same name as the field. For example, the getter method for profit is profit(). For example rec.profit().

text based undo function in java

I'm trying to figure out how to get a undo function for a small maze game. First I worked out a way to do this by checking what the last direction was and just going back in the opposite direction. But this code was getting way too long since I also had to track back possible item pickups or hidden walls etc.
Background info on the code: I use a String[][] to store the maze as this was the easiest. I use an Arraylist<String[][]> to store all the strings.
After each step the player takes I save the String[][] array to the arraylist. When the player say undo I look at the second last String[][] in the arraylist and want to set the String[][] back to this. But the currentPos never seems to get updated. I'm not sure where the problem lies.
if (direction.equals("north")) {
if (currentPos[i - 1][j].equals("---")) {
continue;
} else {
currentPos[i][j] = " ";
currentPos[i - 2][j] = "P";
break;
}
}
if (direction.equals("undo")) {
currentPos = history.get(history.size()-2);
history.remove(history.size()-1);
break;
}
Without understanding the way you are setting history, I've made the assumption from your question that you are simply adding the current map to the history list. If you aren't careful, you will be simply adding the same Object, populating the history with multiply Object references to the current map state. This would have the effect you are observing with the state not changing, because you the history only contains a reference to the most recent map (Not storing any actual history).
To obtain the value from an Object, you typically need to clone the object (invoking the clone() method). However, cloning a 2-dimensional array is somewhat problematic. Invoking the clone() method on a 2-dimensional array "shallow" clones the object, essentially only cloning the first dimension while leaving the second as a reference to the same object (The reason for this is that the first 1-dimension of the array holds a reference to the second 1-dimension). Changing the value on a shallow copied object will change the value of the original and vice-versa, not what you want if you want to keep the Objects distinct.
To create two distinct objects, you will need to perform a "deep" clone, which can be easily implemented in a helper method. The below code illustrates the importance of ensuring you fully clone the object before storing it in the history list.
public static void main (String args[]) throws Exception {
ArrayList<String[][]> list = new ArrayList<>();
String[][] shallowClonedMap = new String[1][1];
String[][] deepClonedMap = new String[1][1];
shallowClonedMap[0][0] = "Old";
deepClonedMap[0][0] = "Old";
list.add(shallowClonedMap.clone());
list.add(deepClone(deepClonedMap));
shallowClonedMap[0][0] = "New";
deepClonedMap[0][0] = "New";
list.add(shallowClonedMap.clone());
list.add(deepClone(deepClonedMap));
for (String[][] item : list) {
System.out.print(item[0][0]);
}
}
public static String[][] deepClone(String[][] arry) {
if (arry == null) {
return null;
}
String[][] clone = new String[arry.length][];
for (int i = 0; i < arry.length; i++) {
clone[i] = arry[i].clone();
}
return clone;
}
The output for executing this code is : NewOldNewNew whereas the "intended" output is "OldOldNewNew". From this you can see the shallowClonedMap was updated to "New" even after being cloned and added to the list.

Hashmap entry overwrites previous entry despite different key

I'm running into issues with a Hashmap in my program overwriting the value of previous Hashmap entries despeinte the two entries having different keys.
public void drawTheNumbers() {
final int NO_WIN = 0;
int playerOneTicketWinning = 0;
Tuple numberTransporter = new Tuple (0,0);
for(Journal entry : theJournal) {
playerOneTicketWinning = entry.getWinning();
if (winningsPerPlayer.containsKey(entry.getPerson())) {
numberTransporter.playerTotalWinnings = numberTransporter.playerTotalWinnings + playerOneTicketWinning;
numberTransporter.playerSimilarities = numberTransporter.playerSimilarities + entry.getSimilarities();
winningsPerPlayer.put(entry.getPerson(), numberTransporter );
}else {
numberTransporter.playerTotalWinnings = playerOneTicketWinning;
numberTransporter.playerSimilarities = entry.getSimilarities();
winningsPerPlayer.put(entry.getPerson(), numberTransporter);
}
}
for (Map.Entry<String, Tuple> player: winningsPerPlayer.entrySet()) {
int temp1 = numberTransporter.getplayerTotalWinnings();
int temp2 = NO_WIN;
if (temp1 == temp2) {
noWinMessage (player.getKey());
}else {
playerWinningsMessage(player.getKey(), numberTransporter);
}
}
}
When this loop is run the second time around it will overwrite the values of playerTotalWinnings and playerSimilarities in the method above, but also in the previously entry in the Hashmap created by winningsPerPlayer.put
The key is the name of the player entered by the user when running the program. Multiple users can run the program within the same session.
The key might be different. But the value is not -_-°
You need to create a new Tuple inside the loop, instead of constantly modifying the one and only you created before the loop.
Generally something like a Tuple should not need to be modified after its creation, it should be created with the two values it will contain and never changed from that. So it should be made immutable, with final values, so that you avoid that kind of silly mistakes.

Getting the next Value in a ArrayList(Not working)

I'm trying to get the next value in a specific arraylist every time a user presses a button (using Swing).
This is my attempt at it:
private void BNextActionPerformed(java.awt.event.ActionEvent evt) {
int i = 0;
Parse p = new Parse();
temp = p.getTemp();
temp2 = p.getTemp2();
temp3 = p.getTemp3();
if (CBUniversities.getSelectedIndex() == 0) {
LNumStudents.setText("Number of students: " + temp);
Student s = p.getMacList().get(i+1);
jTextField2.setText(s.getFirstName());
TLastName.setText(s.getSurName());
jTextArea1.setText(s.getAddress());
i++;
}
}
Where Parse is a class, containing getter methods for 3 integerstemp,temp2,temp3, and a getter for an ArrayList.
Student s is an object of the Student class, where every student has a firstname, surname and address (initialized in a constructor).
When this if statement is executed it displays a students info in the specified fields, however, this only works for the first two students. After that, the i value never seems to increase?
I tried to place a println check to see it's value during the if statement, but it only changes once, oddly enough.
I also tried to make this into a for loop, yet the value again only seems to change once (of i).
Parse has this getter method I'm using
public ArrayList<Student> getMacList() {
return mac;
}
Also CBUniversities is a variable as such:
private javax.swing.JComboBox CBUniversities;
I'm not sure what's gone wrong here, any ideas?
You declared i within the scope of a method, so every time your method executes it reinitializes i.
Instead, declare an instance variable by putting private int i = 0; outside of your method, but still within the class scope.

How can I avoid using a lot of variables?

I would like to create a simple program that would output the atomic mass of any element entered. I am taking a Java course that I recently started so I don't know how to avoid using over 100 variables each with the elements atomic mass.
Also how could I get a if statement to use the name input from the user (which I know how to store in a string) and match it with one of the elements, in order to output the element's mass (corresponding to method used to store the multiple elements).
How can I condense this example code:
int carbon = 12;
int oxygen = 16;
int hydrogen = 1;
int sulfur = 32;
etc....
Sounds like your first step is to learn about the Map data structure. You can use it to associate the string names to integer values and then look them back up later.
Map<String, Integer> elements = new HashMap<String, Integer>();
elements.put("CARBON", 12);
elements.put("OXYGEN", 16);
//etc
Then if you have some input you can look up the number.
String userInput = scanner.next(); // or however you're getting input
Integer atomicWeight = elements.get(userInput.toUpper());
if (atomicWeight == null) //print element not found etc
Then once you have the program down and working you can learn about whatever technology is appropriate for loading the reference data from outside of the source code, whether that's a file or a database or a webservice or whatever.
I'd likely define an enum if confronted with this problem.
public enum Elements
{
HYDROGEN(1),
...
UNOBTANIUM(666);
public final int atomicWeight;
Elements(int atomicWeight)
{
this.atomicWeight = atomicWeight;
}
}
then to get the right element it's
String name = ...// from user input
Elements e = Elements.valueOf(name.toUpperCase());
I'd recommend using an enum as some have suggested, though i'd do it a little different. Maps have lots of overhead, and since your data is not dynamic it's not a great fit. Atomic mass should be a decimal value (double or BigDecimal depending on what you're using it for), not an int
public enum AtomicElement {
HYDROGEN(1.00794),
HELIUM(4.002602),
...;
private double atomicMass;
private AtomicElement (double atomicMass) {
this.atomicMass = atomicMass;
}
public int getAtomicNumber() {
return ordinal();
}
public double getAtomicMass() {
return atomicMass;
}
public static AtomicElement forAtomicNumber(int atomicNumber) {
return AtomicElement.values()[atomicNumber];
}
public static AtomicElement forElementName(String elementName) {
return AtomicElement.valueOf(elementName);
}
}
Then you can search by atomic number or element name
AtomicElement.forAtomicNumber(2);
AtomicElement.forElementName("CARBON");
This does however assume you're going to represent the entire periodic table with no gaps in the data, since it's using the ordinal() value as the atomic number. If you want gaps, you'll have to have an int field for the atomic number and your "forAtomicNumber" function will have to cycle through the "values()" to find the one with the given number.
You could even extend this if you wanted to include known isotopes, etc... if your requirements dictate that.
Because the atomic mass of the elements is not going to change at any point in your app, you should define them as final:
public class AtomicMass {
public static final int CARBON = 12;
public static final int OXYGEN = 16;
...
}
...or, you could use an enum:
public static enum Element {
carbon(12),
oxygen(16),
hydrogen(1),
sulfur(32);
private int atomicMass;
private Element( int mass ) {
this.atomicMass = mass;
}
}
If you order your elements sequentially (and add an UNKNOWN for 0) you wouldn't even need to explicitly provide the mass.
I like to group related data into arrays or arrayLists.
String[] elements = new String[# of elements in table];
Based on the position of the element you can have the atomic number.
Then I would loop through them to find any element or fill the array.
You can look into Java Scanner class to get input from user.
Create a class called Element that contains attributes like name, atomic number, etc. Each element will correspond to an instance of Element. You can then put all the Elementss in several maps, keyed by name, atomic number, etc. Use a factory class to instantiate and initialize the maps, and provide lookup methods.
If I understand you correctly you just want to only have 1 variable to store all the elements and their masses in which case I would recommend a HashMap. It will not really save on code lines but will let you do number 2 pretty easily. HashMaps store a set of key-value pairs and you can get the value if you have the key so this would create the list:
//Declare a new hashmap and initialize it
HashMap<String, Integer> elements = new HashMap<>();
//Add element information
elements.put("CARBON", 12);
elements.put("OXYGEN", 16);
elements.put("HYDROGEN", 1);
elements.put("SULFUR", 32);
Then for example to get user input from a dialog box and print the result to command line you do something like this:
//Collect user input and convert it to all upper case (in real life you would validate this)
String input = JOptionPane.showInputDialog(null, "Please enter an element name").toUpperCase();
//If element name exists in hashmap print its atomic weight
if(elements.containsKey(input.toUpperCase())){
System.out.println("Atomic Weight: " + elements.get(input));
}
Store your data in a file
Element, Weight
oxygen = 16
carbon, 12
.
.
.
Pseudocode:
//Read data file into a `Map<String, int>`
//Get user input
//Access map
//Output

Categories