I am working on a Towers of Hanoi problem in Java. I chose to use Stacks as the pegs and have everything working except for the move method. I have the specification and a JUnit test class and am currently passing 6 of the 7 tests but am failing on the move test. The specs are as follows:
Here is my Towers class:
package edu.metrostate.ics240.p2.towers;
import java.util.Stack;
public class Towers {
private static final int DEFAULT_SIZE = 5;
private static final int MAX_SIZE = 64;
private static final int MIN_PEG = 1;
private static final int MAX_PEG = 3;
private static Stack<Integer>[] tower = new Stack[4];
private int numOfRings;
public Towers(int n) {
if (n < 1 || n > MAX_SIZE)
throw new IllegalArgumentException(
String.format("Number of rings (%s) cannot be less than 1 or exceed 64 ", n));
numOfRings = n;
tower[1] = new Stack<Integer>();
tower[2] = new Stack<Integer>();
tower[3] = new Stack<Integer>();
for (int i = 1; i <= numOfRings; i++)
tower[1].push(i);
}
public Towers() {
numOfRings = DEFAULT_SIZE;
tower[1] = new Stack<Integer>();
tower[2] = new Stack<Integer>();
tower[3] = new Stack<Integer>();
for (int i = 1; i <= numOfRings; i++)
tower[1].push(i);
}
private static void pegCheck(int pegNumber){
if (pegNumber < MIN_PEG || pegNumber > MAX_PEG)
throw new IllegalArgumentException(
String.format("Peg number (%s) cannot be less than 1 or exceed 3 ", pegNumber));
}
public int getRingCount(int pegNumber) {
pegCheck(pegNumber);
switch (pegNumber) {
case 1:
if (tower[1].isEmpty())
return 0;
else
return tower[1].size();
case 2:
if (tower[2].isEmpty())
return 0;
else
return tower[2].size();
case 3:
if (tower[3].isEmpty())
return 0;
else
return tower[3].size();
default:
return 0;
}
}
public int getTopDiameter(int pegNumber) {
pegCheck(pegNumber);
switch (pegNumber) {
case 1:
if(getRingCount(1) > 0){
return tower[1].get(tower[1].peek() - tower[1].size());
}else
return 0;
case 2:
if(getRingCount(2) > 0){
return tower[2].get(tower[2].peek() - tower[2].size());
}else
return 0;
case 3:
if(getRingCount(3) > 0){
return tower[3].get(tower[3].peek() - tower[3].size());
}else
return 0;
default:
return 0;
}
}
public boolean move(int startPeg, int endPeg) {
pegCheck(startPeg);
pegCheck(endPeg);
Stack<Integer> startTower = tower[startPeg];
Stack<Integer> endTower = tower[endPeg];
if (getRingCount(startPeg) > 0 && endPeg != startPeg && getRingCount(endPeg) > 0 && getTopDiameter(startPeg) < getTopDiameter(endPeg)) {
int topRing = startTower.pop();
endTower.push(topRing);
return true;
}else
return false;
}
}
and finally the JUnit test(s):
import static org.junit.Assert.*;
import org.junit.Test;
import edu.metrostate.ics240.p2.towers.*;
import java.util.Random;
public class TowersTest {
private static final int MAX_NUM_RINGS = 64;
private static final long SEED = 20170604001L;
private static final Random RAND = new Random(SEED);
#Test
public void testDefaultConstruction() {
Towers t = new Towers();
assertEquals(5, t.getRingCount(1));
assertEquals(0, t.getRingCount(2));
assertEquals(0, t.getRingCount(3));
assertEquals(1, t.getTopDiameter(1));
assertEquals(0, t.getTopDiameter(2));
assertEquals(0, t.getTopDiameter(3));
}
#Test
public void testConstruction() {
int numRings = RAND.nextInt(MAX_NUM_RINGS);
Towers t = new Towers(numRings);
assertEquals(numRings, t.getRingCount(1));
assertEquals(0, t.getRingCount(2));
assertEquals(0, t.getRingCount(3));
assertEquals(1, t.getTopDiameter(1));
assertEquals(0, t.getTopDiameter(2));
assertEquals(0, t.getTopDiameter(3));
}
#Test
public void testMove() {
int numRings = RAND.nextInt(64);
Towers t = new Towers(numRings);
assertTrue(t.move(1, 2));
assertEquals(numRings - 1, t.getRingCount(1));
assertEquals(1, t.getRingCount(2));
assertEquals(0, t.getRingCount(3));
assertEquals(2, t.getTopDiameter(1));
assertEquals(1, t.getTopDiameter(2));
assertEquals(0, t.getTopDiameter(3));
assertTrue(t.move(1, 3));
assertEquals(numRings - 2, t.getRingCount(1));
assertEquals(1, t.getRingCount(2));
assertEquals(1, t.getRingCount(3));
assertEquals(3, t.getTopDiameter(1));
assertEquals(1, t.getTopDiameter(2));
assertEquals(2, t.getTopDiameter(3));
}
#Test
public void testInvalidConstructor(){
Towers t = null;
try {
t = new Towers(0); // illegal value
fail("Expected exception");
} catch (IllegalArgumentException iae) {
// expected
}
try {
t = new Towers(MAX_NUM_RINGS + 1); // illegal value
fail("Expected exception");
} catch (IllegalArgumentException iae) {
// expected
}
}
#Test
public void testPreconditionGetRingCount() {
Towers t = new Towers();
try {
t.getRingCount(0);
fail("Exception expected");
} catch (IllegalArgumentException iae) {
// expected
}
try {
t.getRingCount(4);
fail("Exception expected");
} catch (IllegalArgumentException iae) {
// expected
}
}
#Test
public void testPreconditionTopRing() {
Towers t = new Towers();
try {
t.getTopDiameter(0);
fail("Exception expected");
} catch (IllegalArgumentException iae) {
// expected
}
try {
t.getTopDiameter(4);
fail("Exception expected");
} catch (IllegalArgumentException iae) {
// expected
}
}
#Test
public void testIllegalMoves(){
Towers t = new Towers();
t.move(1, 2);
t.move(1, 3);
assertFalse(t.move(1, 1)); // can't move to itself
assertFalse(t.move(1, 2)); // moving larger ring to smaller
assertFalse(t.move(1, 3)); // moving larger ring to smaller
assertFalse(t.move(3, 2));
}
}
I think I know where my issue lies. The precondition of getTopDiameter() returns the top ring size if getRingCount(pegNum) > 0 but returns 0 if the stack is empty or there are no rings on the peg. Since tower[1] is the only peg that gets initialized with rings and the other two do not, getTopDiameter() returns 0 since there are no rings currently on tower[2] and tower[3]. In the move() method one of the preconditions requires that getTopdiameter(startPeg) be less than getTopDiamater(endPeg) but if the endPeg was initialized with 0 rings and is therefore empty, getTopDiamater(endPeg) returns 0 which is obviously not less than 1 in this case. I just cannot figure this bit out. Any help is greatly appreciated, thank you in advance!
UPDATE
revised code that is passing all test cases:
package edu.metrostate.ics240.p2.towers;
import java.util.Stack;
public class Towers {
private static final int DEFAULT_SIZE = 5;
private static final int MAX_SIZE = 64;
private static final int MIN_PEG = 1;
private static final int MAX_PEG = 3;
#SuppressWarnings("unchecked")
private static Stack<Integer>[] tower = new Stack[4];
private int numOfRings;
public Towers(int n) {
if (n < 1 || n > MAX_SIZE)
throw new IllegalArgumentException(
String.format("Number of rings (%s) cannot be less than 1 or exceed 64 ", n));
numOfRings = n;
tower[1] = new Stack<Integer>();
tower[2] = new Stack<Integer>();
tower[3] = new Stack<Integer>();
for (int i = numOfRings; i >= 1; i--)
tower[1].push(i);
}
public Towers() {
numOfRings = DEFAULT_SIZE;
tower[1] = new Stack<Integer>();
tower[2] = new Stack<Integer>();
tower[3] = new Stack<Integer>();
for (int i = numOfRings; i >= 1; i--)
tower[1].push(i);
}
private static void pegCheck(int pegNumber) {
if (pegNumber < MIN_PEG || pegNumber > MAX_PEG)
throw new IllegalArgumentException(
String.format("Peg number (%s) cannot be less than 1 or exceed 3 ", pegNumber));
}
public int getRingCount(int pegNumber) {
pegCheck(pegNumber);
if (tower[pegNumber].isEmpty()) {
return 0;
} else
return tower[pegNumber].size();
}
public int getTopDiameter(int pegNumber) {
pegCheck(pegNumber);
if (getRingCount(pegNumber) > 0) {
return tower[pegNumber].get(tower[pegNumber].size() - 1);
}
return 0;
}
public boolean move(int startPeg, int endPeg) {
pegCheck(startPeg);
pegCheck(endPeg);
if (endPeg != startPeg) {
if (getRingCount(startPeg) > 0) {
if (getRingCount(endPeg) == 0 || getTopDiameter(startPeg) < getTopDiameter(endPeg)) {
int topRing = tower[startPeg].pop();
tower[endPeg].push(topRing);
return true;
}
}
}
return false;
}
}
You say:
In the move() method one of the preconditions requires that getTopdiameter(startPeg) be less than getTopDiamater(endPeg) but if the endPeg was initialized with 0 rings and is therefore empty, getTopDiamater(endPeg) returns 0 which is obviously not less than 1 in this case
But if you read the preconditions in the image you provide - it is stating that getTopdiameter(startPeg) be less than getTopDiamater(endPeg) if endPeg has at least one ring so to write this as conditions you need
getRingCount(endPeg) > 0 && getTopdiameter(startPeg) < getTopDiamater(endPeg))
-- Edit --
You need to separate the conditions into different if statements (or have and or condition also) to handle the case when the towers have no pegs - currently with your conditions as is, it fails on the first move as the condition getRingCount(endPeg) > 0 will be false. If getRingCount == 0 then you can just do the move without needing to check if the diameters are compatible. For readability I would suggest you separate your conditions out initially - you can always combine them as required later - something like this pseudo code
if not same peg
if start peg has rings
if end peg is empty or (end peg has rings and diameters are compatible)
do move and return true
return false
Related
We define balanced number as number which has the same number of even and odd dividers e.g (2 and 6 are balanced numbers). I tried to do task for polish SPOJ however I always exceed time.
The task is to find the smallest balance number bigger than given on input.
There is example input:
2 (amount of data set)
1
2
and output should be:
2
6
This is my code:
import java.math.BigDecimal;
import java.util.Scanner;
public class Main {
private static final BigDecimal TWO = new BigDecimal("2");
public static void main(String[] args) throws java.lang.Exception {
Scanner in = new Scanner(System.in);
int numberOfAttempts = in.nextInt();
for (int i = 0; i < numberOfAttempts; i++) {
BigDecimal fromNumber = in.nextBigDecimal();
findBalancedNumber(fromNumber);
}
}
private static boolean isEven(BigDecimal number){
if(number.remainder(new BigDecimal("2")).compareTo(BigDecimal.ZERO) != 0){
return false;
}
return true;
}
private static void findBalancedNumber(BigDecimal fromNumber) {
BigDecimal potentialBalancedNumber = fromNumber.add(BigDecimal.ONE);
while (true) {
int evenDivider = 0;
int oddDivider = 1; //to not start from 1 as divisor, it's always odd and divide potentialBalancedNumber so can start checking divisors from 2
if (isEven(potentialBalancedNumber)) {
evenDivider = 1;
} else {
oddDivider++;
}
for (BigDecimal divider = TWO; (divider.compareTo(potentialBalancedNumber.divide(TWO)) == -1 || divider.compareTo(potentialBalancedNumber.divide(TWO)) == 0); divider = divider.add(BigDecimal.ONE)) {
boolean isDivisor = potentialBalancedNumber.remainder(divider).compareTo(BigDecimal.ZERO) == 0;
if(isDivisor){
boolean isEven = divider.remainder(new BigDecimal("2")).compareTo(BigDecimal.ZERO) == 0;
boolean isOdd = divider.remainder(new BigDecimal("2")).compareTo(BigDecimal.ZERO) != 0;
if (isDivisor && isEven) {
evenDivider++;
} else if (isDivisor && isOdd) {
oddDivider++;
}
}
}
if (oddDivider == evenDivider) { //found balanced number
System.out.println(potentialBalancedNumber);
break;
}
potentialBalancedNumber = potentialBalancedNumber.add(BigDecimal.ONE);
}
}
}
It seems to work fine but is too slow. Can you please help to find way to optimize it, am I missing something?
As #MarkDickinson suggested, answer is:
private static void findBalancedNumberOptimized(BigDecimal fromNumber) { //2,6,10,14,18,22,26...
if(fromNumber.compareTo(BigDecimal.ONE) == 0){
System.out.println(2);
}
else {
BigDecimal result = fromNumber.divide(new BigDecimal("4")).setScale(0, RoundingMode.HALF_UP).add(BigDecimal.ONE);
result = (TWO.multiply(result).subtract(BigDecimal.ONE)).multiply(TWO); //2(2n-1)
System.out.println(result);
}
}
and it's finally green, thanks Mark!
I am working on a maze solver. It runs very fast on my first 2 mazes, however, my third maze takes forever. I am supposed to be able to do it in under a minute, on reasonable hardware.
The solve method takes an immense amount of time on my high-end gaming rig.
Here is the relevant source code
import java.awt.Point;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.LinkedList;
import java.util.Queue;
/**
* Created by jphamlett on 6/16/17.
*/
public class main {
static class fileIO {
public static String readFile(String path, Charset encoding)
throws IOException {
byte[] encoded = Files.readAllBytes(Paths.get(path));
return new String(encoded, encoding);
}
}
static class mazeNode {
private Point point;
private int dist;
public Point getPoint() {
return point;
}
public void setPoint(Point point) {
this.point = point;
}
public int getDist() {
return dist;
}
public void setDist(int dist) {
this.dist = dist;
}
public mazeNode(Point point, int dist) {
setPoint(point);
setDist(dist);
}
}
static class Solver {
private String[] pathGrid;
private int[][] gridLength;
public void setPath(String path) {
try {
this.pathGrid = generatePath(fileIO.readFile(path, Charset.defaultCharset()));
} catch (IOException e) {
e.printStackTrace();
}
}
public Point findA() {
for (int row = 0; row < pathGrid.length; row++) {
int pos = pathGrid[row].indexOf("A");
if (pos != -1) {
return new Point(row, pos);
}
}
return null; // Something went wrong
}
public Point findB() {
for (int row = 0; row < pathGrid.length; row++) {
int pos = pathGrid[row].indexOf("B");
if (pos != -1) {
return new Point(row, pos);
}
}
return null; // Something went wrong
}
public Boolean canMove(char symbol) {
return symbol != '#';
}
public String[] generatePath(String path) {
return path.split("\n");
}
public String[] getPath() {
return this.pathGrid;
}
// Use BFS to solve the maze
public int[][] solve(int[][] gridLength, Point src, Point dest) {
if (src == null || dest == null) {
return null;
}
gridLength[src.x][src.y] = 0; // Distance to self is 0
Boolean visited[][] = new Boolean[gridLength.length][gridLength[0].length]; //Set all booleans to false
for (Boolean[] booleans : visited) {
Arrays.fill(booleans, Boolean.FALSE);
}
//System.out.println("Finished making visited array");
visited[src.x][src.y] = Boolean.TRUE;
Queue<mazeNode> queue = new LinkedList<>();
mazeNode initialNode = new mazeNode(src, 0);
queue.add(initialNode);
while (!queue.isEmpty()) {
mazeNode currentNode = queue.peek();
Point currentPoint = currentNode.getPoint();
//System.out.println("Point: " + currentPoint);
visited[currentPoint.x][currentPoint.y] = Boolean.TRUE;
if (currentPoint.equals(dest)) {
return gridLength;
}
queue.poll();
// Add adjacent valid cells
try {
if (canMove(pathGrid[currentPoint.x].charAt(currentPoint.y - 1)) && !visited[currentPoint.x][currentPoint.y - 1]) {
gridLength[currentPoint.x][currentPoint.y - 1] = currentNode.getDist() + 1;
queue.add(new mazeNode(new Point(currentPoint.x, currentPoint.y - 1), currentNode.getDist() + 1));
}
} catch (IndexOutOfBoundsException e) {
}
try {
if (canMove(pathGrid[currentPoint.x].charAt(currentPoint.y + 1)) && !visited[currentPoint.x][currentPoint.y + 1]) {
gridLength[currentPoint.x][currentPoint.y + 1] = currentNode.getDist() + 1;
queue.add(new mazeNode(new Point(currentPoint.x, currentPoint.y + 1), currentNode.getDist() + 1));
}
} catch (IndexOutOfBoundsException e) {
}
try {
if (canMove(pathGrid[currentPoint.x - 1].charAt(currentPoint.y)) && !visited[currentPoint.x - 1][currentPoint.y]) {
gridLength[currentPoint.x - 1][currentPoint.y] = currentNode.getDist() + 1;
queue.add(new mazeNode(new Point(currentPoint.x - 1, currentPoint.y), currentNode.getDist() + 1));
}
} catch (IndexOutOfBoundsException e) {
}
try {
if (canMove(pathGrid[currentPoint.x + 1].charAt(currentPoint.y)) && !visited[currentPoint.x + 1][currentPoint.y]) {
gridLength[currentPoint.x + 1][currentPoint.y] = currentNode.getDist() + 1;
queue.add(new mazeNode(new Point(currentPoint.x + 1, currentPoint.y), currentNode.getDist() + 1));
}
} catch (IndexOutOfBoundsException e) {
}
}
return null; // Cannot be reached
}
public Solver(String path) {
setPath(path);
}
}
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
Solver solver = new Solver("mazes/maze3.txt");
int[][] path = solver.solve(new int[solver.getPath().length][solver.getPath()[0].length()], solver.findA(), solver.findB());
long endTime = System.currentTimeMillis();
long totalTime = endTime - startTime;
System.out.println(totalTime);
for (int[] i : path) {
for (int j : i) {
System.out.print(j + " ");
}
System.out.println();
}
endTime = System.currentTimeMillis();
totalTime = endTime - startTime;
System.out.println(totalTime);
}
}
Here is maze2.txt
###############B#############################################
##.....########.#......................................#...##
##.###.#........####################################.#.#.#.##
##.###.#.#########..........#########.......########.#.#.#.##
##.#####...........########.#.......#.#####.########.#.#.#.##
##.########################.#.#####.#.#...#.########.#.#.#.##
##............................#####.#.##.##.########.#.#.#.##
##.###.############################.#.##.##.########.#.#.#.##
##.###.##...#...#...#...#...#.......#.##.##.########.#.#.#.##
##.###....#...#...#...#...#...#######.##.##.########.#.#.#.##
##.##################################.##.##.########.#.#.#.##
##.......................................##.########.#.#.#.##
###########################################.########.#.#.#.##
###...............................#########..........#.#.#.##
########################.###########################.#.#.#.##
#........................#...........................#.#.#.##
#.######################.#############################.#.#.##
#.#..........#.........................................#.#.##
#.#.########.#.#########################################.#.##
#.#........#.#.#.........................................#.##
#.##########.#.#.#########################################.##
#............#.#.##........................................##
##############.#.#############################.#####.########
#..............................................#####........#
########################A####################################
I have attached maze3 because the formatting here makes it shift oddly.
https://pastebin.com/c4LhG5hT
Your problem is the visited array.
First, a minor issue: The visited array should not be a Boolean[][]. Just make it a boolean[][], which is automatically initialized to all false values, so that initialization loop can be eliminated too.
Now, the main problem is that visited is not marked true until you actually process that point. This means that the same point is added many times to the queue.
Example maze:
#####################
#...#...#...#...#...#
A.#1..#2..#3..#4..#5B
#...#...#...#...#...#
#####################
In this case, point 1 is added twice to the queue. Each point up to point 2 will also be added twice. Point 2 will be added 4 times, point 3 8 times, point 4 16 times, and point 5 32 times.
As you can see, that is an exponential number of queue items for each round1 to process, doubling each time multiple paths meet.
Solution: Rename visited to queued, and mark point true at the same time you add it to the queue, thus preventing the addition of the same point multiple times.
Result: Code completes in less then 50 milliseconds for maze 3.
1) By "round" I mean processing of all queued points that is one step further away from start (distance).
I 'm looking for a way to read a range of elements in an array of unknown dimension ( not length).
The client can send a read request for an object and specify the range to read. The input String could be like this : "1:2:3:2,2:3:1:4" for example. This would mean he wants to read the elements in the range from [1][2][3][2] to [2][3][1][4] of an array.
To read a concrete element I created this function:
public Object readValue(Object obj,int[] positions ) {
Object value = null; //Result
int objDimension = getDimension(obj); //Dimesion of the array
System.out.println("Dimension: " + objDimension );
try {
Object[] aux = (Object[]) obj;
for (int i = 0; i < objDimension - 1; i++) {
int pos = positions[i];
aux = (Object[]) aux[pos];
}
value = aux[positions[objDimension - 1]];
System.out.println("Result: " + value);
} catch (ArrayIndexOutOfBoundsException e) {
// TODO: Send a fault to the client.
System.out.println("Error: "+e.getMessage());
}
return value;
}
public static int getDimension(Object value) {
Class<?> clazz = value.getClass();
String className = clazz.getName();
int dimension = 0;
for (int i = 0; i < className.length(); i++) {
if (className.charAt(i) != '[') {
dimension = i;
break;
}
}
return dimension;
}
//Example.
public static void main(String[] args) {
// TODO code application logic here
TestMultiDimensioNRead test = new TestMultiDimensioNRead();
Integer[][][][] testSubject = new Integer[5][2][4][];
testSubject[0][0][2] = new Integer[8];
testSubject[0][0][0] = new Integer[15];
testSubject[0][0][1] = new Integer[20];
testSubject[0][0][3] = new Integer[2];
testSubject[1][1][2] = new Integer[7];
testSubject[1][1][2][0] = 80;
test.readValue(testSubject,new int[]{1, 1, 2, 0});
}
I was thinking a good way may be to calculate the differens between each dimension length.
If anyone can come with a good idea, I would really appreciatee.
Thanks in advance.
EDIT 1: The code posted in this question does read the value of a given position in an array of unknown dimension. My problem is to read all the elements that are between to given points. This might not have been clear in the initial question.
You could use a recursive solution:
public class Test {
private class TestMultiDimensioNRead {
public Integer readValue(Object testSubject, int[] coordinates) {
return readValue(testSubject, coordinates, 0);
}
private Integer readValue(Object testSubject, int[] coordinates, int which) {
if (testSubject instanceof Object[]) {
Object[] subject = (Object[]) testSubject;
if (coordinates.length > which + 1) {
return readValue(subject[coordinates[which]], coordinates, which + 1);
} else {
return (Integer) subject[coordinates[which]];
}
} else {
// Throw some sort of exception?
return -1;
}
}
public Iterator<Integer> readValues(Object testSubject, int[] coordinates, int count) {
return readValues(testSubject, coordinates, count, 0);
}
private Iterator<Integer> readValues(Object testSubject, int[] coordinates, int count, int level) {
if (testSubject instanceof Object[]) {
Object[] subject = (Object[]) testSubject;
if (coordinates.length > level + 1) {
return readValues(subject[coordinates[level]], coordinates, count, level + 1);
} else {
return new Iterator<Integer>() {
int i = 0;
Integer[] intSubject = (Integer[]) subject;
#Override
public boolean hasNext() {
return i <= count;
}
#Override
public Integer next() {
return intSubject[coordinates[level] + (i++)];
}
};
}
} else {
// Throw some sort of exception?
return null;
}
}
}
public void test() {
TestMultiDimensioNRead test = new TestMultiDimensioNRead();
Integer[][][][] testSubject = new Integer[5][2][4][];
testSubject[0][0][2] = new Integer[8];
testSubject[0][0][0] = new Integer[15];
testSubject[0][0][1] = new Integer[20];
testSubject[0][0][3] = new Integer[2];
testSubject[1][1][2] = new Integer[7];
testSubject[1][1][2][0] = 80;
testSubject[1][1][2][1] = 79;
testSubject[1][1][2][2] = 78;
Iterator<Integer> them = test.readValues(testSubject, new int[]{1, 1, 2, 0}, 3);
for (Integer x = them.next(); them.hasNext(); x = them.next()) {
System.out.println(x);
}
System.out.println();
}
public static void main(String args[]) {
try {
new Test().test();
} catch (Throwable t) {
t.printStackTrace(System.err);
}
}
}
Prints 80 as expected.
There's probably more to do in terms of sanity checks but this seems to work.
Found the way to do it, maybe it's helpfull at somepoint for someone.
I didn't include any checks, this is more a test case to see that is works.
public class TestReadMultiDimensionArray {
private int[] startPosition; //Start position.
private int[] endPosition; //End position.
private boolean inRange = false; //If the current position is in range.
private List<Object> result; //List to store the values we find.
public TestReadMultiDimensionArray() {
result = new ArrayList<>();
}
public static void main(String[] args) {
TestReadMultiDimensionArray test = new TestReadMultiDimensionArray();
Integer[][][][] testSubject = new Integer[2][2][4][];
//(0,0,y,z)
testSubject[0][0][0] = new Integer[]{1}; //(0,0,0,0)
testSubject[0][0][1] = new Integer[]{2}; //(0,0,1,0)
testSubject[0][0][2] = new Integer[]{3}; //(0,0,2,0)
testSubject[0][0][3] = new Integer[]{4}; //(0,0,3,0)
//(0,1,y,z)
testSubject[0][1][0] = new Integer[]{5}; //(0,1,0,0)
testSubject[0][1][1] = new Integer[]{6}; //(0,1,1,0)
testSubject[0][1][2] = new Integer[]{7, 8, 9}; //(0,1,2,0) (0,1,2,1) (0,1,2,2)
testSubject[0][1][3] = new Integer[]{10}; //(0,1,3,0)
//(1,0,y,z)
testSubject[1][0][0] = new Integer[]{11, 12}; //(1,0,0,0)..
testSubject[1][0][1] = new Integer[]{13, 14, 15};
testSubject[1][0][2] = new Integer[]{16, 17, 18};
testSubject[1][0][3] = new Integer[]{19, 20, 21}; //..(1,0,3,2)
//(1,1,y,z)
testSubject[1][1][0] = new Integer[]{22, 23}; //(1,1,0,0)..
testSubject[1][1][1] = new Integer[]{24, 25, 26};
testSubject[1][1][2] = new Integer[]{27, 28, 29, 30, 31, 32, 33, 34};
testSubject[1][1][3] = new Integer[]{35, 36}; //..(1,1,3,1)
//Launch the test.
test.readValue(testSubject);
}
/**
*
* #param obj The Array from where we want to get the data.
*/
public void readValue(Object obj) {
//Where should it start.
startPosition = new int[]{0, 1, 0, 0};
//Where should it stop.
endPosition = new int[]{1, 1, 1, 2};
System.out.println("Start Position:" + Arrays.toString(startPosition) + " End Position:" + Arrays.toString(endPosition));
int[] currentPosition = new int[]{-1, -1, -1, -1};
//Call to the method.
testRead((Object[]) obj, 0, currentPosition);
//Result to array.
Object[] arrayToReturn = result.toArray(new Object[0]);
System.out.println("Result: " + Arrays.toString(arrayToReturn));
}
/**
* Recursive method that looks for the values in a multi-dimensional array, in a given range. /!\ No checks are implemented here, wrong input can end in a
* StackOverFlow.
*
* #param obj The array in Object[] form.
* #param currentDimension The dimension we are currently in.
* #param result The reference to the list that will store all the values we found.
* #param currentPosition The current position we are in.
*/
private void testRead(Object[] obj, int currentDimension, int[] currentPosition) {
for (int i = 0; i < obj.length; i++) {
currentPosition[currentDimension] = i;
if (Arrays.equals(startPosition, currentPosition) && currentDimension == (currentPosition.length - 1)) {
//Found the start position.
System.out.println("############ START ############");
inRange = true;
}
if ((i >= startPosition[currentDimension] && i <= endPosition[currentDimension]) || inRange == true) {
//We are in the write track to get to the values we are looking for.
if (obj[i] instanceof Object[]) {
//The data contained in the cell is an array.
testRead((Object[]) obj[i], currentDimension + 1, currentPosition);
} else {
//The data contained in the cell is a scalar. This is what we where looking for.
System.out.println(Arrays.toString(currentPosition) + " Data: " + obj[i]);
result.add(obj[i]);
}
}
if (Arrays.equals(endPosition, currentPosition) && currentDimension == (currentPosition.length - 1)) {
//Found the end position.
System.out.println("############ END ############");
inRange = false;
}
}
}
}
Any question or idea to better the code is welcome.
I am creating a program in Java to simulate evolution. The way I have it set up, each generation is composed of an array of Organism objects. Each of these arrays is an element in the ArrayList orgGenerations. Each generation, of which there could be any amount before all animals die, can have any amount of Organism objects.
For some reason, in my main loop when the generations are going by, I can have this code without errors, where allOrgs is the Organism array of the current generation and generationNumber is the number generations since the first.
orgGenerations.add(allOrgs);
printOrgs(orgGenerations.get(generationNumber));
printOrgs is a method to display an Organism array, where speed and strength are Organism Field variables:
public void printOrgs(Organism[] list)
{
for (int x=0; x<list.length; x++)
{
System.out.println ("For organism number: " + x + ", speed is: " + list[x].speed + ", and strength is " + list[x].strength + ".");
}
}
Later on, after this loop, when I am trying to retrieve the data to display, I call this very similar code:
printOrgs(orgGenerations.get(0));
This, and every other array in orgGenerations, return a null pointer exception on the print line of the for loop. Why are the Organism objects loosing their values?
Alright, here is all of the code from my main Simulation class. I admit, it might be sort of a mess. The parts that matter are the start and simulator methods. The battle ones are not really applicable to this problem. I think.
import java.awt.FlowLayout;
import java.util.*;
import javax.swing.JFrame;
public class Simulator {
//variables for general keeping track
static Organism[] allOrgs;
static ArrayList<Organism[]> orgGenerations = new ArrayList <Organism[]>();
ArrayList<Integer> battleList = new ArrayList<Integer>();
int deathCount;
boolean done;
boolean runOnce;
//setup
Simulator()
{
done = false;
Scanner asker = new Scanner(System.in);
System.out.println("Input number of organisms for the simulation: ");
int numOfOrgs = asker.nextInt();
asker.close();
Organism[] orgArray = new Organism[numOfOrgs];
for (int i=0; i<numOfOrgs; i++)
{
orgArray[i] = new Organism();
}
allOrgs = orgArray;
}
//graphsOrgs
public void graphOrgs() throws InterruptedException
{
JFrame f = new JFrame("Evolution");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(1000,500);
f.setVisible(true);
Drawer bars = new Drawer();
//System.out.println(orgGenerations.size());
for (int iterator=0;iterator<(orgGenerations.size()-1); iterator++)
{
printOrgs(orgGenerations.get(0));
//The 0 can be any number, no matter what I do it wont work
//System.out.println("first");
f.repaint();
bars.data = orgGenerations.get(iterator);
f.add(bars);
//System.out.println("before");
Thread.sleep(1000);
//System.out.println("end");
}
}
//prints all Orgs and their statistics
public void printOrgs(Organism[] list)
{
System.out.println("Number Of Organisms: " + list.length);
for (int x=0; x<list.length; x++)
{
System.out.println ("For organism number: " + x + ", speed is: " + list[x].speed + ", and strength is " + list[x].strength + ".");
}
System.out.println();
}
//general loop for the organisms lives
public void start(int reproductionTime) throws InterruptedException
{
int generationNumber = 0;
orgGenerations.add(allOrgs);
printOrgs(orgGenerations.get(0));
generationNumber++;
while(true)
{
deathCount = 0;
for(int j=0; j<reproductionTime; j++)
{
battleList.clear();
for(int m=0; m<allOrgs.length; m++)
{
if (allOrgs[m].alive == true)
oneYearBattleCheck(m);
}
battle();
}
reproduction();
if (done == true)
break;
orgGenerations.add(allOrgs);
printOrgs(orgGenerations.get(generationNumber));
generationNumber++;
}
printOrgs(orgGenerations.get(2));
}
//Checks if they have to fight this year
private void oneYearBattleCheck(int m)
{
Random chaos = new Random();
int speedMod = chaos.nextInt(((int)Math.ceil(allOrgs[m].speed/5.0))+1);
int speedSign = chaos.nextInt(2);
if (speedSign == 0)
speedSign--;
speedMod *= speedSign;
int speed = speedMod + allOrgs[m].speed;
if (speed <= 0)
speed=1;
Random encounter = new Random();
boolean battle = false;
int try1 =(encounter.nextInt(speed));
int try2 =(encounter.nextInt(speed));
int try3 =(encounter.nextInt(speed));
int try4 =(encounter.nextInt(speed));
if (try1 == 0 || try2 == 0 || try3 == 0 || try4 == 0 )
{
battle = true;
}
if(battle == true)
{
battleList.add(m);
}
}
//Creates the matches and runs the battle
private void battle()
{
Random rand = new Random();
if (battleList.size()%2 == 1)
{
int luckyDuck = rand.nextInt(battleList.size());
battleList.remove(luckyDuck);
}
for(int k=0; k<(battleList.size()-1);)
{
int competitor1 = rand.nextInt(battleList.size());
battleList.remove(competitor1);
int competitor2 = rand.nextInt(battleList.size());
battleList.remove(competitor2);
//Competitor 1 strength
int strengthMod = rand.nextInt(((int)Math.ceil(allOrgs[competitor1].strength/5.0))+1);
int strengthSign = rand.nextInt(2);
if (strengthSign == 0)
strengthSign--;
strengthMod *= strengthSign;
int comp1Strength = strengthMod + allOrgs[competitor1].strength;
//Competitor 2 strength
strengthMod = rand.nextInt(((int)Math.ceil(allOrgs[competitor2].strength/5.0))+1);
strengthSign = rand.nextInt(2);
if (strengthSign == 0)
strengthSign--;
strengthMod *= strengthSign;
int comp2Strength = strengthMod + allOrgs[competitor2].strength;
//Fight!
if (comp1Strength>comp2Strength)
{
allOrgs[competitor1].life ++;
allOrgs[competitor2].life --;
}
else if (comp2Strength>comp1Strength)
{
allOrgs[competitor2].life ++;
allOrgs[competitor1].life --;
}
if (allOrgs[competitor1].life == 0)
{
allOrgs[competitor1].alive = false;
deathCount++;
}
if (allOrgs[competitor2].life == 0)
{
allOrgs[competitor2].alive = false;
deathCount ++ ;
}
}
}
//New organisms
private void reproduction()
{
//System.out.println("Number of deaths: " + deathCount + "\n");
if (deathCount>=(allOrgs.length-2))
{
done = true;
return;
}
ArrayList<Organism> tempOrgs = new ArrayList<Organism>();
Random chooser = new Random();
int count = 0;
while(true)
{
int partner1 = 0;
int partner2 = 0;
boolean partnerIsAlive = false;
boolean unluckyDuck = false;
//choose partner1
while (partnerIsAlive == false)
{
partner1 = chooser.nextInt(allOrgs.length);
if (allOrgs[partner1] != null)
{
if (allOrgs[partner1].alive == true)
{
partnerIsAlive = true;
}
}
}
count++;
//System.out.println("Count 2: " + count);
partnerIsAlive = false;
//choose partner2
while (partnerIsAlive == false)
{
if (count+deathCount == (allOrgs.length))
{
unluckyDuck=true;
break;
}
partner2 = chooser.nextInt(allOrgs.length);
if (allOrgs[partner2] != null)
{
if (allOrgs[partner2].alive == true)
{
partnerIsAlive = true;
}
}
}
if (unluckyDuck == false)
count++;
//System.out.println("count 2: " + count);
if (unluckyDuck == false)
{
int numOfChildren = (chooser.nextInt(4)+1);
for (int d=0; d<numOfChildren; d++)
{
tempOrgs.add(new Organism(allOrgs[partner1].speed, allOrgs[partner2].speed, allOrgs[partner1].strength, allOrgs[partner2].strength ));
}
allOrgs[partner1] = null;
allOrgs[partner2] = null;
}
if (count+deathCount == (allOrgs.length))
{
Arrays.fill(allOrgs, null);
allOrgs = tempOrgs.toArray(new Organism[tempOrgs.size()-1]);
break;
}
//System.out.println(count);
}
}
}
Main method:
public class Runner {
public static void main(String[] args) throws InterruptedException {
Simulator sim = new Simulator();
int lifeSpan = 20;
sim.start(lifeSpan);
sim.graphOrgs();
}
}
Organism class:
import java.util.Random;
public class Organism {
static Random traitGenerator = new Random();
int life;
int speed;
int strength;
boolean alive;
Organism()
{
speed = (traitGenerator.nextInt(49)+1);
strength = (50-speed);
life = 5;
alive = true;
}
Organism(int strength1, int strength2, int speed1, int speed2)
{
Random gen = new Random();
int speedMod = gen.nextInt(((int)Math.ceil((speed1+speed2)/10.0))+1);
int speedSign = gen.nextInt(2);
if (speedSign == 0)
speedSign--;
speedMod *= speedSign;
//System.out.println(speedMod);
int strengthMod = gen.nextInt(((int)Math.ceil((strength1+strength2)/10.0))+1);
int strengthSign = gen.nextInt(2);
if (strengthSign == 0)
strengthSign--;
strengthMod *= strengthSign;
//System.out.println(strengthMod);
strength = (((int)((strength1+strength2)/2.0))+ strengthMod);
speed = (((int)((speed1+speed2)/2.0))+ speedMod);
alive = true;
life = 5;
}
}
The problem lies in the graphOrgs class when I try to print to check if it is working in preparation for graphing the results. This is when it returns the error. When I try placing the print code in other places in the Simulator class the same thing occurs, a null pointer error. This happens even if it is just after the for loop where the element has been established.
You have code that sets to null elements in your allOrgs array.
allOrgs[partner1] = null;
allOrgs[partner2] = null;
Your orgGenerations list contains the same allOrgs instance multiple times.
Therefore, when you write allOrgs[partner1] = null, the partner1'th element becomes null in all the list elements of orgGenerations, which is why the print method fails.
You should create a copy of the array (you can use Arrays.copy) each time you add a new generation to the list (and consider also creating copies of the Organism instances, if you want each generation to record the past state of the Organisms and not their final state).
I am making a robot maze where the robot reaches a target automatically without crashing into walls. I want the robot to do the maze once, learn the correct route and then the second time be able to get there straight away without going to any deadends. I thought I could do this by making three arraylists.
One for all the squares the robot visits.
Two for all the squares that lead to a deadend.
Three for all the directions the robot goes.
If the squares that lead to a dead end are found in the first arraylist then i can delete the same indexes in the third arraylist. That way, the second time, i can just iterate the third Arraylist.
My full code is below:
import java.util.ArrayList;
import java.util.*;
import java.util.Iterator;
import java.util.stream.IntStream;
public class Explorer {
private int pollRun = 0; // Incremented after each pass.
private RobotData robotData; // Data store for junctions.
private ArrayList<Integer> nonWallDirections;
private ArrayList<Integer> passageDirections;
private ArrayList<Integer> beenbeforeDirections;
private Random random = new Random();
int [] directions = {IRobot.AHEAD, IRobot.LEFT, IRobot.RIGHT, IRobot.BEHIND};
private ArrayList<Square> correctSquares;
private ArrayList<Square> wrongSquares;
private ArrayList<Integer> correctDirections;
public void controlRobot (IRobot robot) {
// On the first move of the first run of a new maze.
if ((robot.getRuns() == 0) && (pollRun ==0))
robotData = new RobotData();
pollRun++; /* Increment poll run so that the data is not reset
each time the robot moves. */
int exits = nonwallExits(robot);
int direction;
if ((robot.getRuns() != 0))
direction = grandfinale(robot);
nonWallDirections = new ArrayList<Integer>();
passageDirections = new ArrayList<Integer>();
beenbeforeDirections = new ArrayList<Integer>();
correctSquares = new ArrayList<Square>();
correctDirections = new ArrayList<Integer>();
// Adding each direction to the appropriate state ArrayList.
for(int item : directions) {
if(robot.look(item) != IRobot.WALL) {
nonWallDirections.add(item);
}
}
for(int item : directions) {
if(robot.look(item) == IRobot.PASSAGE) {
passageDirections.add(item);
}
}
for(int item : directions) {
if(robot.look(item) == IRobot.BEENBEFORE) {
beenbeforeDirections.add(item);
}
}
// Calling the appropriate method depending on the number of exits.
if (exits < 2) {
direction = deadEnd(robot);
} else if (exits == 2) {
direction = corridor(robot);
} else {
direction = junction(robot);
robotData.addJunction(robot);
robotData.printJunction(robot);
}
robot.face(direction);
addcorrectSquares(robot);
correctDirections.add(direction);
}
/* The specification advised to have to seperate controls: Explorer and Backtrack
and a variable explorerMode to switch between them.
Instead, whenever needed I shall call this backtrack method.
If at a junction, the robot will head back the junction as to when it first approached it.
When at a deadend or corridor, it will follow the beenbefore squares until it
reaches an unexplored path. */
public int backtrack (IRobot robot) {
if (nonwallExits(robot) > 2) {
addwrongSquares(robot);
return robotData.reverseHeading(robot);
} else {
do {
addwrongSquares(robot);
return nonWallDirections.get(0);
} while (nonwallExits(robot) == 1);
}
}
// Deadend method makes the robot follow the only nonwall exit.
public int deadEnd (IRobot robot) {
return backtrack(robot);
}
/* Corridor method will make the robot follow the one and only passage.
The exception is at the start. Sometimes, the robot will start with
two passages available to it in which case it will choose one randomly.
If there is no passage, it will follow the beenbefore squares
until it reaches an unexplored path.*/
public int corridor (IRobot robot) {
if (passageExits(robot) == 1) {
return passageDirections.get(0);
} else if (passageExits(robot) == 2) {
int randomPassage = random.nextInt(passageDirections.size());
return passageDirections.get(randomPassage);
} else {
return backtrack(robot);
}
}
/* Junction method states if there is more than one passage, it will randomly select one.
This applies to crossroads as well as essentially they are the same.
If there is no passage, it will follow the beenbefore squares until it reaches an unexplored
path. */
public int junction(IRobot robot) {
if (passageExits(robot) == 1) {
return passageDirections.get(0);
} else if (passageExits(robot) > 1) {
int randomPassage = random.nextInt(passageDirections.size());
return passageDirections.get(randomPassage);
} else {
return backtrack(robot);
}
}
// Calculates number of exits.
private int nonwallExits (IRobot robot) {
int nonwallExits = 0;
for(int item : directions) {
if(robot.look(item) != IRobot.WALL) {
nonwallExits++;
}
}
return nonwallExits;
}
// Calculates number of passages.
private int passageExits (IRobot robot) {
int passageExits = 0;
for(int item : directions) {
if(robot.look(item) == IRobot.PASSAGE) {
passageExits++;
}
}
return passageExits;
}
// Calculates number of beenbefores.
private int beenbeforeExits (IRobot robot) {
int beenbeforeExits = 0;
for(int item : directions) {
if(robot.look(item) == IRobot.PASSAGE) {
beenbeforeExits++;
}
}
return beenbeforeExits;
}
// Resets Junction Counter in RobotData class.
public int reset() {
return robotData.resetJunctionCounter();
}
public void addcorrectSquares(IRobot robot) {
Square newSquare = new Square(robot.getLocation().x, robot.getLocation().y);
correctSquares.add(newSquare);
}
public void addwrongSquares(IRobot robot) {
Square badSquare = new Square(robot.getLocation().x, robot.getLocation().y);
wrongSquares.add(badSquare);
}
public int grandfinale (IRobot robot) {
IntStream.range(0, correctSquares.size())
.map(index -> correctSquares.size() - index - 1)
.filter(index -> (((wrongSquares.x).contains(correctSquares.x)) && ((wrongSquares.y).contains(correctSquares.y))).get(index))
.forEach(index -> correctDirections.remove(index));
Iterator<Integer> routeIterator = correctDirections.iterator();
while (routeIterator.hasNext()) {
break;
}
return (routeIterator.next());
}
}
class RobotData {
/* It was advised in the specification to include the variable:
private static int maxJunctions = 10000;
However, as I am not using arrays, but ArrayLists, I do not
need this. */
private static int junctionCounter = 0;
private ArrayList<Junction> junctionList = new ArrayList<Junction>();
// Resets the Junction counter.
public int resetJunctionCounter() {
return junctionCounter = 0;
}
// Adds the current junction to the list of arrays.
public void addJunction(IRobot robot) {
Junction newJunction = new Junction(robot.getLocation().x, robot.getLocation().y, robot.getHeading());
junctionList.add(newJunction);
junctionCounter++;
}
// Gets the junction counter for Junction info method in Junction class.
public int getJunctionCounter (IRobot robot) {
return junctionCounter;
}
// Prints Junction info.
public void printJunction(IRobot robot) {
String course = "";
switch (robot.getHeading()) {
case IRobot.NORTH:
course = "NORTH";
break;
case IRobot.EAST:
course = "EAST";
break;
case IRobot.SOUTH:
course = "SOUTH";
break;
case IRobot.WEST:
course = "WEST";
break;
}
System.out.println("Junction " + junctionCounter + " (x=" + robot.getLocation().x + ", y=" + robot.getLocation().y +") heading " + course);
}
/* Iterates through the junction arrayList to find the
heading of the robot when it first approached the junction.
It does this by finding the first junction in the ArrayList
that has the same x and y coordinates as the robot.*/
public int searchJunction(IRobot robot) {
Junction currentJunction = null;
Iterator<Junction> junctionIterator = junctionList.iterator();
while (junctionIterator.hasNext()) {
currentJunction = junctionIterator.next();
if ((((currentJunction.x)==(robot.getLocation().x))) && ((currentJunction.y)==(robot.getLocation().y)))
break;
}
return currentJunction.arrived;
}
// Returns the reverse of the heading the robot had when first approaching the junction.
public int reverseHeading(IRobot robot) {
int firstHeading = searchJunction(robot);
int reverseHeading = 1; // Random integer to Iniitalise variable.
switch (firstHeading) {
case IRobot.NORTH:
if (robot.getHeading() == IRobot.NORTH)
reverseHeading = IRobot.BEHIND;
else if (robot.getHeading() == IRobot.EAST)
reverseHeading = IRobot.RIGHT;
else if (robot.getHeading() == IRobot.SOUTH)
reverseHeading = IRobot.AHEAD;
else
reverseHeading = IRobot.LEFT;
break;
case IRobot.EAST:
if (robot.getHeading() == IRobot.NORTH)
reverseHeading = IRobot.LEFT;
else if (robot.getHeading() == IRobot.EAST)
reverseHeading = IRobot.BEHIND;
else if (robot.getHeading() == IRobot.SOUTH)
reverseHeading = IRobot.RIGHT;
else
reverseHeading = IRobot.AHEAD;
break;
case IRobot.SOUTH:
if (robot.getHeading() == IRobot.NORTH)
reverseHeading = IRobot.AHEAD;
else if (robot.getHeading() == IRobot.EAST)
reverseHeading = IRobot.LEFT;
else if (robot.getHeading() == IRobot.SOUTH)
reverseHeading = IRobot.BEHIND;
else
reverseHeading = IRobot.RIGHT;
break;
case IRobot.WEST:
if (robot.getHeading() == IRobot.NORTH)
reverseHeading = IRobot.RIGHT;
else if (robot.getHeading() == IRobot.EAST)
reverseHeading = IRobot.AHEAD;
else if (robot.getHeading() == IRobot.SOUTH)
reverseHeading = IRobot.LEFT;
else
reverseHeading = IRobot.BEHIND;
break;
}
return reverseHeading;
}
}
class Junction {
int x;
int y;
int arrived;
public Junction(int xcoord, int ycoord, int course) {
x = xcoord;
y = ycoord;
arrived = course;
}
}
class Square {
int x;
int y;
public Square(int cordx, int cordy){
x = cordx;
y = cordy;
}
}
IntStream.range(0, al1.length)
.filter(index -> al2.contains(al1.get(index)))
.forEach(index -> al3.remove(index));
Slightly more complex than this if removing elements from al3 shifts them left but in that case just reverse the stream before the .filter- then it will delete from the end. The easiest way to do that is:
.map(index -> al1.length - index - 1)
Without Streams the equivalent would be
for (int i = 0; i < al1.length; i++) {
if (al2.contains(al1.get(i))) {
al3.remove(i);
}
}
Similarly, if you need to delete from the right then the for loop would need to count down rather than up.
Without further details on arraylist structure it's hard to give any more hints.