I've posted my code below. I am having a problem on the line declaring the array wrongAnswers. I've been able to get my code working before, but the problem is that some person took it upon themselves to delete all my files. I was able to get it working without using List or ArrayList. I just want to understand how I can get this working now before I try using either of those other methods. I understand that Java arrays are immutable. However, I was still somehow able to get it to work before. If someone could help me figure out what I did previously, I would be most greatful.
private Scanner keyboard = new Scanner(System.in);
private final String[] testAnswers = {
"B","D","A","A","C",
"A","B","A","C","D",
"B","C","D","A","D",
"C","C","B","D","A"};
private String[] studentAnswers = new String[20];
/*
private String[] studentAnswers = {
"B","D","A","A","C",
"A","B","A","C","D",
"B","C","D","A","D",
"C","C","B","D","A"};
*/
private int[] wrongAnswers;
private int answeredCorrectly = 0;
public void getStudentAnswers() {
for(int x = 0; x < 20; x++) {
do {
System.out.print("Enter answer for #" + (x + 1) + " : ");
this.studentAnswers[x] = keyboard.next().toUpperCase();
if (!"A".equals(this.studentAnswers[x]) &&
!"B".equals(this.studentAnswers[x]) &&
!"C".equals(this.studentAnswers[x]) &&
!"D".equals(this.studentAnswers[x])) {
System.out.println("Invalid input.");
}
} while(!"A".equals(this.studentAnswers[x]) &&
!"B".equals(this.studentAnswers[x]) &&
!"C".equals(this.studentAnswers[x]) &&
!"D".equals(this.studentAnswers[x]));
}
}
public int totalCorrect() {
int arrayLocation = 0;
for(int x = 0; x < 20; x++) {
if (this.studentAnswers[x].equals(this.testAnswers[x]))
this.answeredCorrectly++;
else
this.wrongAnswers[arrayLocation++] = x;
}
return this.answeredCorrectly;
}
public int totalIncorrect() {
return 20 - this.answeredCorrectly;
}
public boolean passed() {
return this.answeredCorrectly >= 15;
}
public void questionsMissed() {
if(this.answeredCorrectly != 20) {
for(int x = 0; x < this.wrongAnswers.length; x++) {
System.out.println(this.wrongAnswers[x]);
}
}
}
If code is well written, saving space (which is what you are trying to do) will usually cost performance and vice versa. You can achieve what you want, but you'll lose performance, as you'll see.
I find deduction to be useful when solving similar problems. Conditions:
1) Arrays are immutable
2) You want to allocate the exact amount of space that you need
Point 2 poses a question: how do you know how much space you need? Obvious answer: know how many (in)correct answers you have. Following from there you can do:
public int totalCorrect() {
for(int x = 0; x < 20; x++) {
if (this.studentAnswers[x].equals(this.testAnswers[x]))
this.answeredCorrectly++;
}
this.wrongAnswers = int[20 - this.answeredCorrectly];
// Here you want to create your wrongAnswers, but you have to go over
// the same array twice...
int arrayLocation = 0;
for(int x = 0; x < 20; x++) {
if (!this.studentAnswers[x].equals(this.testAnswers[x]))
this.wrongAnswers[arrayLocation++] = x;
}
return this.answeredCorrectly;
}
There are probably more ways to do something similar and achieve better performance too. At first sight they seem to me like bad approaches and I'd use a List, as has been proposed already, or perhaps a Set, but who knows...
private int[] wrongAnswers = new int [20];
Related
I'm trying to optimize my program by getting rid of duplicate searches or just by generally make things optimized throughout my program and I came across a method in which I can't find any "better" solution what so ever and would love to know if anyone could point me in a direction for refineing it.
First off here is my code that I'm trying to optimize:
public Player spaceBattle(Player player1, Player player2) {
ArrayList<Ship> listOfShipsPlayer1 = this.getShipsOfPlayer(player1);
ArrayList<Ship> listOfShipsPlayer2 = this.getShipsOfPlayer(player2);
Random random = new Random();
int player1hits = 0, player2hits = 0, rolledDie;
for (Ship aShip : listOfShipsPlayer1) {
rolledDie = random.nextInt(10) + 1;
if (rolledDie >= aShip.getShipType().getCombatValue()) {
player1hits += 1;
}
}
for (Ship aShip : listOfShipsPlayer2) {
rolledDie = random.nextInt(10) + 1;
if (rolledDie >= aShip.getShipType().getCombatValue()) {
player2hits += 1;
}
}
for (int i = 0; i < player1hits; ++i) {
if (this.getShipsOfPlayer(player2).size() > 0) {
this.listOfShips.remove(listOfShipsPlayer2.get(i));
} else {
break;
}
}
for (int i = 0; i < player2hits; ++i) {
if (this.getShipsOfPlayer(player1).size() > 0) {
this.listOfShips.remove(listOfShipsPlayer1.get(i));
} else {
break;
}
}
As you can see here I run the same algorithm twice in order to check for first Player1 and then Player2 and add update their respective hits. And then for the amount of hits for each player I then remove a ship.
What I would like to know if its possible to change this bit of code to be able to not have the duplicate code for each player but that it automatically can go through player1 first and then player2 in one loop.
Looking forward to hear from you
You can just create methods.
private int hitShips(List<Ship> ships) {
int result = 0;
for (Ship ship : ships) {
rolledDie = random.nextInt(10) + 1;
if (rolledDie >= ship.getShipType().getCombatValue()) {
result++;
}
}
return result;
}
which makes your code
int player1hits = hitShips(listOfShipsPlayer1);
int player2hits = hitShips(listOfShipsPlayer2);
and similar for the removal of the ships from the list.
void removeShips(List<Ship> ships, int remove) {
int removeCount = Math.max(ships.size(), remove);
Iterator<Ship> it = ships.iterator();
for (int i = 0; i < removeCount; i++) {
it.remove();
}
}
with
removeShips(getShipsOfPlayer(player2), player1hits);
removeShips(getShipsOfPlayer(player1), player2hits);
I'm not sure why you always remove ships from the head of the lists, since combat values seems to be a thing, but that's just a side note.
I wouldn't call this "optimization" so much, but the removal of duplicate code is always good.
You could optimize removeShips as:
void removeShips(List<Ship> ships, int numHits) {
int numToRemove = Math.min(ships.size(), numHits);
if (numToRemove > 0) {
ships.subList(0, numToRemove).clear();
}
}
This method call will result in only one System.arraycopy call, which will shift all remaining elements to the left.
https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html#subList-int-int-
I am creating a concentration game.
I have an buffered image array where I load in a 25 image sprite sheet.
public static BufferedImage[] card = new BufferedImage[25];
0 index being the card back. and 1 - 24 being the values for the face of the cards to check against if the cards match.
What I am tying to do is this I will have 4 difficulties Easy, Normal, Hard, and Extreme. Each difficulty will have a certain amount of cards it will need to draw and then double the ones it chosen. for example the default level will be NORMAL which is 12 matches so it need to randomly choose 12 unique cards from the Buffered Image array and then double each value so it will only have 2 of each cards and then shuffle the results.
This is what I got so far but it always seems to have duplicates about 99% of the time.
//generate cards
Random r = new Random();
int j = 0;
int[] rowOne = new int[12];
int[] rowTwo = new int[12];
boolean[] rowOneBool = new boolean[12];
for(int i = 0; i < rowOneBool.length; i++)
rowOneBool[i] = false;
for(int i = 0; i < rowOne.length; i++){
int typeId = r.nextInt(12)+1;
while(rowOneBool[typeId]){
typeId = r.nextInt(12)+1;
if(rowOneBool[typeId] == false);
}
rowOne[i] = typeId;
j=0;
}
the 3 amounts I will be needing to generate is Easy 6, Normal 12, and Hard 18 extreme will use all of the images except index 0 which is the back of the cards.
This is more or less in the nature of random numbers. Sometimes they are duplicates. You can easily factor that in though if you want them to be more unique. Just discard the number and generate again if it's not unique.
Here's a simple method to generate unique random numbers with a specified allowance of duplicates:
public static void main(String[] args) {
int[] randoms = uniqueRandoms(new int[16], 1, 25, 3);
for (int r : randoms) System.out.println(r);
}
public static int[] uniqueRandoms(int[] randoms, int lo, int hi, int allowance) {
// should do some error checking up here
int range = hi - lo, duplicates = 0;
Random gen = new Random();
for (int i = 0, k; i < randoms.length; i++) {
randoms[i] = gen.nextInt(range) + lo;
for (k = 0; k < i; k++) {
if (randoms[i] == randoms[k]) {
if (duplicates < allowance) {
duplicates++;
} else {
i--;
}
break;
}
}
}
return randoms;
}
Edit: Tested and corrected. Now it works. : )
From what I understand from your question, the answer should look something like this:
Have 2 classes, one called Randp and the other called Main. Run Main, and edit the code to suit your needs.
package randp;
public class Main {
public static void main(String[] args) {
Randp randp = new Randp(10);
for (int i = 0; i < 10; i++) {
System.out.print(randp.nextInt());
}
}
}
package randp;
public class Randp {
private int numsLeft;
private int MAX_VALUE;
int[] chooser;
public Randp(int startCounter) {
MAX_VALUE = startCounter; //set the amount we go up to
numsLeft = startCounter;
chooser = new int[MAX_VALUE];
for (int i = 1; i <= chooser.length; i++) {
chooser[i-1] = i; //fill the array up
}
}
public int nextInt() {
if(numsLeft == 0){
return 0; //nothing left in the array
}
int a = chooser[(int)(Math.random() * MAX_VALUE)]; //picking a random index
if(a == 0) {
return this.nextInt(); //we hit an index that's been used already, pick another one!
}
chooser[a-1] = 0; //don't want to use it again
numsLeft--; //keep track of the numbers
return a;
}
}
This is how I would handle it. You would move your BufferedImage objects to a List, although I would consider creating an object for the 'cards' you're using...
int removalAmount = 3; //Remove 3 cards at random... Use a switch to change this based upon difficulty or whatever...
List<BufferedImage> list = new ArrayList<BufferedImage>();
list.addAll(Arrays.asList(card)); // Add the cards to the list, from your array.
Collections.shuffle(list);
for (int i = 0; i < removalAmount; i++) {
list.remove(list.size() - 1);
}
list.addAll(list);
Collections.shuffle(list);
for (BufferedImage specificCard : list) {
//Do something
}
Ok, I said I'd give you something better, and I will. First, let's improve Jeeter's solution.
It has a bug. Because it relies on 0 to be the "used" indicator, it won't actually produce index 0 until the end, which is not random.
It fills an array with indices, then uses 0 as effectively a boolean value, which is redundant. If a value at an index is not 0 we already know what it is, it's the same as the index we used to get to it. It just hides the true nature of algorithm and makes it unnecessarily complex.
It uses recursion when it doesn't need to. Sure, you can argue that this improves code clarity, but then you risk running into a StackOverflowException for too many recursive calls.
Thus, I present an improved version of the algorithm:
class Randp {
private int MAX_VALUE;
private int numsLeft;
private boolean[] used;
public Randp(int startCounter) {
MAX_VALUE = startCounter;
numsLeft = startCounter;
// All false by default.
used = new boolean[MAX_VALUE];
}
public int nextInt() {
if (numsLeft <= 0)
return 0;
numsLeft--;
int index;
do
{
index = (int)(Math.random() * MAX_VALUE);
} while (used[index]);
return index;
}
}
I believe this is much easier to understand, but now it becomes clear the algorithm is not great. It might take a long time to find an unused index, especially when we wanted a lot of values and there's only a few left. We need to fundamentally change the way we approach this. It'd be better to generate the values randomly from the beginning:
class Randp {
private ArrayList<Integer> chooser = new ArrayList<Integer>();
private int count = 0;
public Randp(int startCounter) {
for (int i = 0; i < startCounter; i++)
chooser.add(i);
Collections.shuffle(chooser);
}
public int nextInt() {
if (count >= chooser.size())
return 0;
return chooser.get(count++);
}
}
This is the most efficient and extremely simple since we made use of existing classes and methods.
public class OneHundredDoors
{
static OneHundredDoors.Door[] doors = new OneHundredDoors.Door[100];
static public class Door
{
public int doorClosed = 0;
public void open ()
{
this.doorClosed = 0;
}
public void close ()
{
this.doorClosed = 1;
}
private void printStatus (int address)
{
if (this.doorClosed == 0)
{
System.out.println("Door: " + address + " is Open!");
}
}
public void printStatusOfAll ()
{
for (int i = 0; i < doors.length; i++)
{
doors[i].printStatus(i);
}
}
public void passDoor (int increment)
{
for (int k = 0; k < doors.length; k += increment)
{
if (doors[k].doorClosed == 0)
{
doors[k].close();
}
else
{
doors[k].close();
}
}
}
}
public static void main (String [] args)
{
for (int i = 0; i < doors.length; i++)
{
doors[i] = new OneHundredDoors.Door ();
}
for (int j = 0; j < doors.length; j++)
{
doors[5].passDoor(j);
}
doors[5].printStatusOfAll();
}
}
My problem here is that the loop for doors[5].passDoor(j) simply does not work at all. No errors come up, neither at runtime or at compile time. Nothing happens. Leaving the program for a while and coming back to it does nothing, signifying that it is not doing anything in the background. Now this code solves the problem if you simply say doors[5].passDoor(2) then 3, then 4 up to 100. The problem is that that's a wasteful thing to do, and hence I instead want to do it with a for loop.
About the static array of objects: sorry about that, I'm doing that to make things easier in the testing stage, and will fix things when I've got it up and running (by making the array private to the class Door).
I'm only really posting this here because I'm at a complete loss for why this is happening. No errors to search for on the internet, no freezes so I know it's probably not an infinite (or long) loop, and no one seems to have similar problems with 100 doors (albeit this may be because they have not taken an object-oriented approach to it as I have done). Also, the code works completely fine if you type it 100 times as I have said (or at least, it APPEARS that it would do so had I the patience to actually type it out 100 times).
Note finally, that the loop here does not work for ANY value of x where j < x. (What I'm missing here must be something obvious and simple therefore).
The reason passDoor won't work is that you pass an increment of 0 to:
for (int k = 0; k < doors.length; k += increment) {
so the values of k never increment causing an infinite loop.
For the code below, it stops running when "n" gets around 100,000. I need it to run until 1 million. I dont know where its going wrong, I am still learning Java so there might be simple mistakes in the code as well.
public class Problem14{
public static void main(String[] args) {
int chainLength;
int longestChain = 0;
int startingNumber = 0;
for(int n =2; n<=1000000; n++)
{
chainLength = getChain(n);
if(chainLength > longestChain)
{
System.out.println("chainLength: "+chainLength+" start: "+n);
longestChain = chainLength;
startingNumber = n;
}
}
System.out.println("longest:"+longestChain +" "+"start:"+startingNumber);
}
public static int getChain(int y)
{
int count = 0;
while(y != 1)
{
if((y%2) == 0)
{
y = y/2;
}
else{
y = (3*y) + 1;
}
count = count + 1;
}
return count;
}
}
Please use long as the data type instead of int
I will want this to come into light, that the number does flung higher than 1000000, so variable y needs long to hold it.
It's the datatype for y. It should be long. Otherwise it wraps round to -2 billion.
I thought I recognised this - it's Euler problem 14. I've done this myself.
getChain() method is causing problem it gets to negative and then it hangs forever in the loop.
I have a function that shrinks the size of a "Food bank" (represented by a rectangle in my GUI) once some of the food has been taken. I have the following function check for this:
public boolean tryToPickUpFood(Ant a)
{
int xCoord = a.getLocation().x;
int yCoord = a.getLocation().y;
for (int i = 0; i < foodPiles.size(); i++)
{
if (foodPiles.get(i).containsPoint(xCoord, yCoord))
{
foodPiles.get(i).decreaseFood();
return true;
}
}
return false;
}
Where decreaseFood shrinks the rectangle..
public void decreaseFood()
{
foodAmount -= 1;
shrinkPile();
}
private void shrinkPile()
{
WIDTH -=1;
HEIGHT = WIDTH;
}
However, whenever one rectangle shrinks, ALL of the rectangles shrink. Why would this be?
edit:
Food piles are being added like such:
addFoodPile(new Food(new Point(200,200)));
addFoodPile(new Food(new Point(400,340)));
public void addFoodPile(Food fp)
{
foodPiles.add(fp);
}
Because the same food pile is in each element of the array? If you are populating it like
FoodPile foodPile = new FoodPile();
for (int i = 0; i < count; i++) {
foodPiles.add(foodPile)
}
you should do this instead:
for (int i = 0; i < count; i++) {
FoodPile foodPile = new FoodPile();
foodPiles.add(foodPile)
}
also, this loop:
for (int i = 0; i < foodPiles.size(); i++)
{
if (foodPiles.get(i).containsPoint(xCoord, yCoord))
{
foodPiles.get(i).decreaseFood();
return true;
}
}
can be more readable if you use foreach syntax:
for (FoodPile foodPile : foodPiles)
{
if (foodPile.containsPoint(xCoord, yCoord))
{
foodPile.decreaseFood();
return true;
}
}
This might be your problem -
private void shrinkPile()
{
WIDTH -=1;
HEIGHT = WIDTH;
}
In the standard Java naming convention, all uppercase names are used for static variables - since you haven't shown their declaration, I can't be sure - but it's certainly suspicious and a place to look.
I'm guessing you thought you were adding lots of separate piles into the List but actually you were adding a reference to the same one over and over. Check that part of your code.
From the code you have shown, it seems like the problem lies in where and how often the tryToPickUpFood() is used. Unless you've used the same reference of foodPiles, as pointed in the other answers