I have these two pieces of code:
int prevY = 0;
// this is the function InsertionSort applied to i
StdDraw.setPenColor(Color.blue);
for (int i = 1; i <= N; i++) {
int x = i;
int y = runInsertion(i);
int prevX = i - 1;
StdDraw.setPenRadius(lineRadius);
StdDraw.line(prevX, prevY, x, y);
StdDraw.setPenRadius(pointRadius);
StdDraw.point(x, y);
prevY = y;
}
prevY = 0;
// this is the function SelectionSort applied to i
StdDraw.setPenColor(Color.black);
for (int i = 1; i <= N; i++) {
int x = i;
int y = runSelection(i);
int prevX = i - 1;
StdDraw.setPenRadius(lineRadius);
StdDraw.line(prevX, prevY, x, y);
StdDraw.setPenRadius(pointRadius);
StdDraw.point(x, y);
prevY = y;
}
They both do the same thing except for a minor change in the color that will be used and in the sorting algorithm that will be used.
Is there any way to make an array for the colors and the sorting algorithms like:
String[] algorithms = {"runInsertion", "runSelection"}
String[] colors = {"blue", "black"};
And then with a for loop call the appropriate index so that the code has been shortened.
I know this will not work as I proposed it, but I just want to know if there is a way or a specific method that lets me do this.
Just extract your logic into method with required parameters:
void work(Color color, Function<Integer, Integer> algorithm) {
int prevY = 0;
StdDraw.setPenColor(color);
for (int i = 1; i <= N; i++) {
int x = i;
int y = algorithm.apply(i);
int prevX = i - 1;
StdDraw.setPenRadius(lineRadius);
StdDraw.line(prevX, prevY, x, y);
StdDraw.setPenRadius(pointRadius);
StdDraw.point(x, y);
prevY = y;
}
}
and call it:
work(Color.blue, this::runInsertion);
work(Color.black, this::runSelection);
or for arrays:
List<Function<Integer, Integer>> algorithms = Arrays.asList(this::runInsertion, this::runSelection);
List<Color> colors = Arrays.asList(Color.blue, Color.black);
for (int i = 0; i < algorithms.size(); i++) {
work(colors.get(i), algorithms.get(i));
}
The 'Design pattern' approach to this problem is called the Template method. It involves creating an abstract class for your algorithm, which would define and implement, for example, methods step1() and step3(), and an abstract method step2() to be implemented in different ways by different implementations of the algorithm. However, that seems like overkill for your purposes. It might be simplest just to reduce some of your duplication by using, say, a draw(x, y) method to contain the lines:
StdDraw.setPenRadius(lineRadius);
StdDraw.line(prevX, prevY, x, y);
StdDraw.setPenRadius(pointRadius);
StdDraw.point(x, y);
You can simply make a method which you can call with parameters to invoke the options that you want. In java 8 you can even refer to the different algorithms (runInsertion(i) and runSelection(i)) as method references which work as lambdas.
public void sortWithAlgorithm(Color color, Function<Integer, Integer> algorithm) {
int prevY = 0;
StdDraw.setPenColor(color);
for (int i = 1; i <= N; i++) {
int x = i;
int y = algorithm.apply(i);
int prevX = i - 1;
StdDraw.setPenRadius(lineRadius);
StdDraw.line(prevX, prevY, x, y);
StdDraw.setPenRadius(pointRadius);
StdDraw.point(x, y);
prevY = y;
}
}
And then to invoke the two, you can make a couple convenience methods:
void sortInsertion (){
sortWithAlgorithm(Color.Blue, this::runInsertion);
}
void sortSelection() {
sortWithAlgorithm(Color.Black, this::runSelection);
}
Yes, one possible way is using a Function object (unfortunately, it is not as easy as inputing the method variable name since methods are not first class objects as they are in a language like Javascript).
A Function class in Java looks like this:
public class Function<T,V> { // T is a generic for your input type
abstract V apply(T input); // V is a generic for your output type
}
Then it is as easy as extracting your code into a method where you input a color string and the corresponding Function object (Sounds like a good job for a Map!) The only caveat with the following implementation is that I'm not exactly sure how runSelection and runInsertion are implemented, but I'm going off the idea that they take in an int and output an int.
// Make the map somewhere earlier in the code
Map<String, Function<Integer, Integer> map = new HashMap();
map.put("blue", new Function<Integer, Integer>(){
Integer apply(Integer input) {
return runInsertion(input);
}
}
map.put("black", new Function<Integer, Integer>(){
Integer apply(Integer input) {
return runSelection(input);
}
}
for (Entry<String, Function<Integer, Integer>> entry : map.entrySet()) {
doExtractedMethod(entry.getKey(), entry.getValue());
}
...
public void doExtractedMethod(String color, Function<Integer, Integer> function) {
int prevY = 0;
// this is the function InsertionSort applied to i
StdDraw.setPenColor(color); // COLOR USED HERE
for (int i = 1; i <= N; i++) {
int x = i;
int y = function.apply(i); // FUNCTION USED HERE
int prevX = i - 1;
StdDraw.setPenRadius(lineRadius);
StdDraw.line(prevX, prevY, x, y);
StdDraw.setPenRadius(pointRadius);
StdDraw.point(x, y);
prevY = y;
}
}
Related
I have a class
public class Coordinates {
public int x,y,z;
public Coordinates (x, y, z) {
this.x = x;
this.y = y;
this.z = z;
}
}
In another class' method I want to get access on the coordinates, but I have to do the same long code for each coordinate x,y and z. Is there a way to use variables for the parameters such as :
Coordinates coords0 = new Coordinates(1, 2, 3);
Coordinates coords1 = new Coordinates(4, 1, 3);
Coordinates coords2 = new Coordinates(5, 1, 3);
Coordinates coords3 = new Coordinates(1, 1, 3);
Coordinates coords4 = new Coordinates(1, 2, 0);
Set<Coordinates> set = new HashSet<Coordinates>();
set.add(coords0);
set.add(coords1);
set.add(coords2);
set.add(coords3);
set.add(coords4);
int sum = 0;
for (int i = 0; i < 3; i++) {
String param = "";
if(i == 0) param = "x";
if(i == 1) param = "y";
if(i == 2) param = "z";
Iterator<Coordinates> it = set.iterator();
while(it.hasNext()) {
Coordinates currentCoords = it.next();
sum += currentCoords.param;
}
}
This does not work obviously, but is there a way to achieve something similar to this ?
As far as I understand, the problem is that you must apply the same code to a collection of coordinates, and this same code must be done for all the x of the coordinates, then to all the y of the coordinates, then for all the z of the coordinates.
So you probably need a method looking like this:
private void complexCode(Collection<Coordinates> collection, ToIntFunction<Coordinates> axisAccessor) {
// ...
for (Coordinates c : collection) {
int axis = axisAccessor.applyAsInt(c);
//...
}
// ...
}
You would then call your function three times using
complexCode(collection, coordinates -> coordinates.x);
complexCode(collection, coordinates -> coordinates.y);
complexCode(collection, coordinates -> coordinates.z);
Your current attempt is pretty close, only you don't need a param variable, println (and based on a comment, eliminate the i value). Like,
Coordinates coords = new Coordinates(1, 2, 3);
System.out.println("Coordinate x is " + coords.x);
System.out.println("Coordinate y is " + coords.y);
System.out.println("Coordinate z is " + coords.z);
or format it a little differently, and override toString() in Coordinates. Oh, and fix the constructor.
public class Coordinates {
public int x, y, z;
public Coordinates(int x, int y, int z) {
this.x = x;
this.y = y;
this.z = z;
}
#Override
public String toString() {
return String.format("x=%d, y=%d, z=%d", x, y, z);
}
}
Then you could print it like
Coordinates coords = new Coordinates(1, 2, 3);
System.out.println("Coordinate " + coords);
What you want is a getter method. It is a good idea to create setter methods as well. Heres what they look like for x. You'll need to make similar methods for y and z.
public int getX(){
return x;
}
public void setX(int newX){
x = newX;
}
you create these in your Coordinates class then from your other class you can do
if(i == 0) param = coords.getX();
You can use reflection like this:
int paramInt = Coordinates.class.getField(param).getInt(coords);
System.out.println("Coordinate "+param+" is "+paramInt);
Don't.
It's a simplified code snippet I need to use in a Conway's Game of Life implementation. I have a HashMap where the Coord Objects ( cells in Game of Life ) are stored. Coord objects are pairs of X, Y coordinates which I want to print on a simple 2D grid.
I have problems with the printGrid(HashMap<Coord, Integer> map) method, mainly:
Is there a simpler and more elegant way to print the Coord objects on a 2-dimensinal grid?
If not, is there a way to check if there is a Coord object with given X,Y parameters in the HashMap without creating an instance of the Coord object for every X,Y position on the grid like I'm doing here:
if (map.containsKey(new Coord(column,row))) {
grid += map.get(new Coord(column,row));
} else {
grid += "#";
}
The code:
public class Main {
static class Coord {
int x;
int y;
public boolean equals(Object o) {
Coord c = (Coord) o;
return c.x == x && c.y == y;
}
public Coord(int x, int y) {
super();
this.x = x;
this.y = y;
}
public int hashCode() {
return x * 3 + y * 5;
}
}
public static String printGrid(HashMap<Coord, Integer> map) {
int grid_width = 10;
int grid_height = 10;
String grid = "";
for(int row =0; row < grid_height; row++) {
for(int column=0; column< grid_width; column++) {
if(map.containsKey(new Coord(column,row))) {
grid += map.get(new Coord(column,row));
} else {
grid += "#";
}
}
grid += "\n"; // next row
}
return grid;
}
public static void main(String args[]) {
HashMap<Coord, Integer> map = new HashMap<Coord, Integer>();
map.put(new Coord(0, 0), 1);
map.put(new Coord(1, 2), 5);
map.put(new Coord(3, 4), 1);
map.put(new Coord(4, 5), 3);
map.put(new Coord(4, 6), 2);
System.out.println(printGrid(map));
}
}
I think the following code match your requirements.Basically you can create a StringBuilder object and initialize it with all '#'. Then, based on Coord objects in the map replace the corresponding character in the builder with the value in the map.It is simple and you don't need to create a Coord object for each grid cell.
public static String printGrid(HashMap<Coord, Integer> map) {
int width = 10;
int height = 10;
int lenght = height * width ;
StringBuilder grid = new StringBuilder(lenght);
// Initialize the builder
int i = 0 ;
while(i<lenght){
if(i >= width && i%(width)==0){
grid.append('\n');
}
grid.append('#');
i++;
}
for(Coord c : map.keySet()){
int index = c.x *(width + 1) + c.y;
grid.setCharAt(index, map.get(c).toString().charAt(0));
}
return grid.toString();
}
output:
1#########
##5#######
##########
####1#####
#####32###
##########
##########
##########
##########
##########
hope this can help.
Your code is close to what you want, but:
Since the grid is very big, how would printGrid() know the size? You should pass the size in as parameters too.
Don't create the Coord object twice. Only create it once and assign to variable, so it can be used twice.
This point is negated by the next bullet, but point well made, right?
Don't call containsKey(), then get(). Since your map will not have null values (according to the way you used it), the null returned by get() is enough of an existence test.
Do not do string += string in a loop. Use a StringBuilder. Huge performance difference when string gets large.
So, your code becomes:
public static String printGrid(HashMap<Coord, Integer> map, int width, int height) {
StringBuilder grid = new StringBuilder(height * (width + 1)); // Pre-sizing is optional, but
// may improve performance a bit
for (int row = 0; row < height; row++) {
for (int column = 0; column < width; column++) {
Integer value = map.get(new Coord(column, row));
if (value != null) {
grid.append(value);
} else {
grid.append('#');
}
}
grid.append('\n'); // next row
}
return grid.toString();
}
Considering Andreas' suggestion, here is my proposition
Add int neighbours in the Coord class
static class Coord {
int x;
int y;
int neighbours;
public Coord(int x, int y) {
this(x, y, 0);
}
public Coord(int x, int y, int neighbours) {
this.x = x;
this.y = y;
this.neighbours = neighbours;
}
// getters and setters here
// Equals method here
}
Add a method to retrieve a Coord object from coordinates
Coord getCoord(HashSet<Coord> data, int x, int y) {
Coord coord = new Coord(x, y);
if (data.contains(coord)) {
for (Coord c : data) {
if (c.equals(coord)){
return c;
}
}
}
return null;
}
Your printGrid method will be like this
String printGrid(HashSet<Coord> data, int width, int height) {
StringBuilder grid = new StringBuilder(height * (width + 1));
Coord coord;
for (int row = 0; row < height; row++) {
for (int column = 0; column < width; column++) {
coord = getCoord(data, row, column);
if(coord != null) {
grid.append(coord.getNeighbours());
} else {
grid.append('#');
}
}
grid.append('\n'); // next row
}
return grid.toString();
}
And in your main method you will have this
HashSet<Coord> data = new HashSet<>();
data.add(new Coord(0, 0, 1));
data.add(new Coord(1, 2, 5));
data.add(new Coord(3, 4, 1));
data.add(new Coord(4, 5, 3));
data.add(new Coord(4, 6, 2));
System.out.println(printGrid(data, 10, 10));
You can also use ArrayList like this
Coord getCoordWithList(ArrayList<Coord> data, int x, int y) {
Coord coord = new Coord(x, y);
int index = data.indexOf(coord);
if (index > -1) {
return data.get(index);
}
return null;
}
Well instead of having a map with Coord object as key, you can use a String as key. Your String key will be the concatenation of x and y values of your Coord.
So you will have this
for (int row = 0; row < grid_height; row++) {
for (int column = 0; column < grid_width; column++) {
String key = column + "#" + row;
if(map.containsKey(key)) {
grid += map.get(key) + "";
} else {
grid += "#";
}
}
grid += "\n"; // next row
}
And for the initialisation of the map you will have this
HashMap<String, Integer> map = new HashMap<String, Integer>();
map.put("0#0", 1);
map.put("1#2", 5);
map.put("3#4", 1);
map.put("4#5", 3);
map.put("4#6", 2);
System.out.println(printGrid(map));
Edit
Instead of using a HashMap to store your data, you can just use a HashSet for it. The Integer value of the map will be an attribute of your Coord class
I have a method which takes in 4 floats and modifies the value. Now I would like to set the floats of the parent class to the value that the method took in. Some code:
public void influence(float x, float y, float w, float h){
x += 5; y += 5; w += 5; h += 5;
}
Now that would be called from the parent class like this:
float x = 5, y = 5, w = 5, h = 5;
influence(x, y, w, h);
I would like to set the floats in the parent to the modified method. I know I could return a float[] and then manually set the floats of the parent class to that array but is there a better way to do this? I could also use a class like Rect but that would be like an array.
Java does not have a way to pass a reference to a primitive. However, in your situation it would probably be better to avoid it anyway: it appears that the four variables are related to each other.
When several variables are related, they should be instance variables of a single object. Your object probably looks like a rectangular box that has the origin of (x, y), height h, and width w. Make a class for it, and let the method change that class, or return a new instance:
class Rect {
private final float x, y, w, h;
public float getX() { return x; }
public float getY() { return y; }
public float getW() { return w; }
public float getH() { return h; }
public Rect(float x, float y, float w, float h) {
this.x = x;
this.y = y;
this.w = w;
this.h = h;
}
}
Then you can write a method returning a new Rect
public Rect influence(Rect inputRect) {
...
}
or make Rect mutable, and let influence modify its values.
You can't change primitives in Java like that because everything in Jave is passed by value - but you can store the value in an array and pass it:
public void influence(float[] arr){
for (int i=0; i<arr.length; i++){
arr[i] += 5;
}
}
Ok so I have been experimenting with java for a few weeks now, following both in class, and online tutorials. I made a simple game where squares fall towards the bottom of the screen, while the player controls a small ball, only moving on the x-axis and trys to avoid them.
The problem that I am having is that the squares start out falling too fast. Right now I have them set as follows:
ix = 0;
iy = 1;
Then in my move() method, I have the following:
hitbox.x += ix;
hitbox.y += iy;
In this example, ix and iy are both integers.
My first assumption was to change the ints to floats, then use:
ix= 0;
iy = 0.5;
and then:
hitbox.x += ix;
hitbox.y += 0.5f;
But this just freezes the objects in their tracks . I believe that this is because cords are taken as integers, so I figured that if I modified my getX() and getY() methods, maybe I could manipulate them somehow to use decimal numbers? But I am not quite sure how to. Any help/hints/solutions to this problem would be Greatly appreciated.
Here is some revelent code, let me know if anymore is needed!
Enemy Manager:
public class EnemyManager {
private int amount;
private List<Enemy> enemies = new ArrayList<Enemy>();
private GameInJavaOne instance;
public EnemyManager(GameInJavaOne instance, int a){
this.amount = a;
this.instance = instance;
spawn();
}
private void spawn(){
Random random = new Random();
int ss = enemies.size();
// If the current amount of enemies is less than the desired amount, we spawn more enemies.
if(ss < amount){
for(int i = 0; i < amount - ss; i++){
enemies.add(new Enemy(instance, random.nextInt(778), random.nextInt(100)+1));
}
// If its greater than the desired number of enemies, remove them.
}else if (ss > 20){
for(int i = 0; i < ss - amount; i++){
enemies.remove(i);
}
}
}
public void draw(Graphics g){
update();
for(Enemy e : enemies) e.draw(g);
}
private void update() {
boolean re = false;
for(int i = 0; i < enemies.size(); i ++){
if(enemies.get(i).isDead()){
enemies.remove(i);
re = true;
}
}
if(re) spawn();
}
public boolean isColliding(Rectangle hitbox){
boolean c =false;
for(int i = 0; i < enemies.size(); i ++){
if(hitbox.intersects(enemies.get(i).getHitbox())) c = true;
}
return c;
}
}
Entity:
public abstract class Entity {
protected int x, y, w, h;
protected boolean removed = false;
public Entity(int x, int y){
this.x = x;
this.y = y;
}
public void Draw(Graphics g){
}
public int getX() { return x; }
public int getY() { return y; }
public int getH() { return h; }
public int getW() { return w; }
}
and the enemy class:
public class Enemy extends Entity{
private Rectangle hitbox;
private int ix, iy;
private boolean dead = false;
private GameInJavaOne instance;
public Enemy(GameInJavaOne instance, int x, int y){
super(x, y);
this.instance = instance;
hitbox = new Rectangle(x, y, 32, 32);
ix = 0;
iy = 1;
}
private void move(){
if(instance.getStage().isCollided(hitbox)){
iy =0;
dead = true;
}
hitbox.x += ix;
hitbox.y += iy;
}
public boolean isDead() {return dead;}
public Rectangle getHitbox() {return hitbox; }
public void draw(Graphics g){
move();
g.setColor(Color.MAGENTA);
g.fillRect(hitbox.x, hitbox.y, hitbox.height, hitbox.width);
}
}
You are using a Rectangle class to represent the position of your box (even though you call it the hitbox), Rectangle does indeed have members x and y which are integers and so when you call
rectangle.x+=0.5f;
What you are really calling is rectangle.x+=(int)0.5f; and (int)0.5f==0.
The Rectangle class is simply inappropriate for holding the position of the box if you want float precision. Consider holding the box's real position as a double or float and casting to int to render it.
So your rendering code would become;
g.fillRect((int)positionX,(int)positionY, hitbox.height, hitbox.width);
where positionX and positionY are doubles. (You could also use Vector2d if you'd prefer to keep x and y together)
Other points
You seem to be extending an Entity class with x,y,w and h and yet never use them, this seems dangerous, why are you extending Entity but recreating your own positional code.
Your game loop isn't shown, however, I can see that you hard code the change in x and y. This is presumably because in your game loop you 'ask for' some frame speed, say 60fps and assume you'll get it. This works fine on a resource rich system, but as soon as you have any resource shortage you will start getting frames that are shorter than 60fps. In most games you don't even notice this because it just makes a larger jump to compensate but here you assume 60fps. It is wise to get an actual frame time and multiply that by a velocity to get you change in x and y.
Sorry if I'm not explaining good for it's 12:34 am and i'm doing late night programming but I need help. Btw this is in LWJGL Here's my Code:
I keep getting a null pointer error for the addAt() and the draw(); Basicly there is a couple classes that make it so when I click it will run addAt(mousex,mousey); and in the render loop it will keep drawing. The class that is new Block(x,y) is a class that will draw the QUAD.
//beggining
public class Grid {
Block[][] blocks = new Block[25][25];
public Grid(){
for (int x = 0; x < 25 - 1; x++) {
for (int y = 0; y < 16 - 1; y++) {
blocks[x][y] = new Block(x,y);
}
}
}
public void draw(){
for (int x = 0; x < 25;x++){
for (int y = 0; y < 25;y++){
blocks[x][y].draw();
}
}
}
public void addAt(int x,int y){
blocks[x][y] = new Block(x,y);
}
}
//end
basicly the Main is just making a Display and running the draw loop and the input listener.
Then the Block class is just making a quad at the defined x and y.
Sorry if I disobeyed a stack overflow rule. This is my First post and it's at a late time.:) Thanks in advance!
While adding to block array your looping is from 0 to (25-1) and (16-1). While processing the block the looping is from 0 to 25. That would most probably lead to NPE. Try initializing you blocks from 0 to 25 (for both x and y values).
I tried your code and it works fine (I don't get any exception). Here is my code:
public class Test {
private class Block {
int x, y;
private Block(int x, int y) {
this.x = x;
this.y = y;
}
void draw() {
}
}
Block[][] blocks = new Block[25][25];
public Test(){
for (int x = 0; x < 25 - 1; x++) {
for (int y = 0; y < 16 - 1; y++) {
blocks[x][y] = new Block(x,y);
}
}
}
public void draw(){
for (int x = 0; x < 25;x++){
for (int y = 0; y < 25;y++){
blocks[x][y].draw();
}
}
}
public void addAt(int x,int y){
blocks[x][y] = new Block(x,y);
}
public static void main(String[] args) {
Test t = new Test();
t.addAt(4,5);
}
}
=> The problem seems to be the late night programming ;-)