I started today to program Conways Game of Life. In a first step, I just want the user to input the length of the (squadratic) field which is then displayed on the screen. But I'm getting a NullPointerException in the printGrid() method. Here are the necessary code examples:
public class Grid {
private Cell[][]grid;
public Grid (int feldlänge) {
grid = new Cell[feldlänge][feldlänge];
int x, y;
for (y = 0; y < feldlänge; y = y + 1) {
for (x = 0; x < feldlänge; x = x + 1) {
Cell cell;
cell = new Cell(x,y);
cell.setLife(false);
} // for
} // for
} // Konstruktor Grid
public String printGrid () {
String ausgabe = "";
int x, y;
for (y = 0; y < grid.length; y = y + 1) {
for (x = 0; x < grid.length; x = x + 1) {
if (grid[x][y].isAlive()) { // Here's the NullPointerException
ausgabe = ausgabe + "■";
}
if (!grid[x][y].isAlive()) {
ausgabe = ausgabe + "□";
}
}
ausgabe = ausgabe + "\n";
}
return ausgabe;
}
public class Cell {
private int x, y;
private boolean isAlive;
public Cell (int pX, int pY) {
x = pX;
y = pY;
} // Konstruktor Cell
public void setLife (boolean pLife) {
isAlive = pLife;
} // Methode setLife
public int getX () {
return x;
} // Methode getX
public int getY () {
return y;
} // Methode getY
public boolean isAlive () {
return isAlive;
}
}
It's kind of embarrassing I can't find the mistake by myself. I guess I'm overlooking something simple.
Already thanks a lot for any help!
Update: Already solved!
I just didn't add the cell to the array. It works now.
You don't seem to add the cell into your grid array.
public Grid (int feldlänge) {
grid = new Cell[feldlänge][feldlänge];
int x, y;
for (y = 0; y < feldlänge; y = y + 1) {
for (x = 0; x < feldlänge; x = x + 1) {
Cell cell;
cell = new Cell(x,y);
cell.setLife(false);
grid[x][y] = cell; //put the cell in the grid.
} // for
} // for
} // Konstruktor Grid
You have to add cell to your array. (german field = english array)
Also: instead of
if( someBoolean){}
if( !someBoolean){}
you should use
if( someBoolean){}
else {}
This makes it more clear what the code does
Related
I'm trying to create a program that will detect a blue line and black out all of the pixels above it, but not below. See method below:
public static void findBlueLine(Picture p) {
Pixel[][] pixels = p.getPixels2D();
for(int y = 0; y < p.getHeight(); y++) {
for (int x = 0; x < p.getWidth(); x++) {
if (isBlueLine(pixels[y][x])) {
pixels[y][x].setColor(Color.BLACK);
//for(int m = y; m < p.getHeight(); m++) {
//pixels[m][x].setColor(Color.BLACK);
//}
}
}
}
}
I am making the assumption that you have one blue line on your screen. This blue line can be at any angle and start and end at any coordinates. I am assuming the line does not exist off of the screen.
This code should work for all lines.
public static void findBlueLine(Picture p) {
Pixel[][] pixels = p.getPixels2D();
Bool aboveLine = false;
for (int x = 0; x < p.getWidth(); x++) {
aboveLine = false;
for(int y = p.getHeight-1; 0 <= y ; y--) {
if(aboveLine == true) {
pixels[y][x].setColor(Color.BLACK);
}
if (isBlueLine(pixels[y][x])) {
aboveLine = true;
}
}
}
}
I have a grid, grid is and 2-demension array of Cell object.
public class Cell
{
int x;
int y;
ArrayList<Cell> nighbors=new ArrayList<Cell>();
public void addNeighbor(Cell cell)
{
this.neighbors.add(cell);
}
}
Every cell have 8 neighbors:
And also, one more, field is looped, like on the picture below:
So the neighbors for Cell(0,1) are also cells (5,0), (5,1), (5,2).
Now I fill neigbors like that:
public void addNeigbors(int x, int y)
{
Cell curentCell=grid[x][y];
if(x==0)
{
if(y==0)
{
curentCell.addNiegbor(this.cells[this.width-1][this.height-1]);
curentCell.addNiegbor(this.cells[x][this.height-1]);
curentCell.addNiegbor(this.cells[x+1][this.height-1]);
curentCell.addNiegbor(this.cells[x+1][y]);
curentCell.addNiegbor(this.cells[x+1][y+1]);
curentCell.addNiegbor(this.cells[x][y+1]);
curentCell.addNiegbor(this.cells[this.width-1][y+1]);
curentCell.addNiegbor(this.cells[this.width-1][y]);
}
else if(y==this.height-1)
{
// similar code
}
else
{
// and so on
}
}
// and so on
}
This code make my cry but I have no idea to make it better.
What can you advise me?
Storing references to each neighbor in Cell is a waste. IF cells need to access their neighbors, then put a reference to the grid array in each cell, and let cell calculate its neighbor indexes on the fly when necessary.
You could add a method like this:
Cell getNeighbor(int dx, int dy)
{
int w = grid.length;
int h = grid[x].length;
return grid[(x+w+dx)%w][(y+h+dy)%h];
}
If a cell needs to iterate through all of its neighbors, you can do it like this:
for (int dy=-1;dy<=1;++dy) {
for(int dx=-1;dx<=1;++dx) {
if (dx!=0 || dy!=0) {
processNeighbor(getNeighbor(dx,dy));
}
}
}
Something like this :
public void addNeigbors(int x, int y)
{
Cell curentCell=grid[x][y];
int xp1 = (x+1)%width;
int xm1 = (x-1+width)%width;
int yp1 = (y+1)%height;
int ym1 = (y-1+height)%height;
curentCell.addNiegbor(this.cells[xp1][y]);
curentCell.addNiegbor(this.cells[xp1][yp1]);
curentCell.addNiegbor(this.cells[xp1][ym1]);
curentCell.addNiegbor(this.cells[x][yp1]);
curentCell.addNiegbor(this.cells[x][ym1]);
curentCell.addNiegbor(this.cells[xm1][y]);
curentCell.addNiegbor(this.cells[xm1][yp1]);
curentCell.addNiegbor(this.cells[xm1][ym1]);
}
Create an (static) array that conains the x/y delta for all 8 neighbors so you can loop through it an add the delta to your cells x/y coordinates to get the neighbor coordinates and use mod -> % to deal with the borders:
public class Cell {
int x;
int y;
private static int delta[][] = {{-1,0},{-1,-1},{0,-1},{1,-1},{1,0},{1,1},{0,1},{-1,1}};
ArrayList < Cell > neighbors = new ArrayList < Cell > ();
public void addNeighbors() {
for (int i = 0; i < delta.length; i++) {
this.neighbors.add(this.cells
[Math.floorMod(this.x + delta[i][0], width)]
[Math.floorMod(this.y + delta[i][1], height)]
);
}
}
}
Loop over possible variations on x (dx) and y (dy), avoid the special case dx = dy = 0 and use mod to wrap around the grid.
public void addNeigbors(int x, int y) {
for(int dx = -1; dx <= 1; dx++) {
for(int dy = -1; dy <= 1; dy++) {
if(dx != 0 || dy != 0) {
int nx = Math.floorMod(x + dx, this.width);
int ny = Math.floorMod(y + dy, this.height);
this.cells[x][y].addNeighbors(this.cells[nx][ny]);
}
}
}
}
I'm writing the drawing system for a roguelike game based on ascii characters (graphics similar to dwarf fortress). I'm using the AsciiPanel from here. My problem is that when I draw entities on my map, they seem to blink, when they should be solid.
In this gif, the r characters in the top row are the entities.
This is the map's draw method that is called every frame.
public void draw(final Display display) {
for (int x = getViewportX(); x < getViewportX() + viewportWidthInTiles; x++) {
for (int y = viewportY; y < viewportY + viewportHeightInTiles; y++) {
final char character = background[x][y].getCharacter();
final Color foreground = background[x][y].getForeground();
final Color backgroundColor = background[x][y].getBackground();
final AsciiCharacterData data = new AsciiCharacterData(
character, foreground, backgroundColor);
display.setCharacterAt(x - getViewportX(), y - viewportY,
background[x][y].getDrawingLayer(), data);
}
}
display.clearLayer(DrawingLayer.PRIMARY);
for (int i = 0; i < entities.size(); i++) {
final Entity e = entities.get(i);
final char character = e.getCharacter();
final Color foreground = e.getForeground();
final Color backgroundColor = e.getBackground();
final AsciiCharacterData data = new AsciiCharacterData(character,
foreground, backgroundColor);
display.setCharacterAt(e.getX() - getViewportX(), e.getY()
- viewportY, e.getDrawingLayer(), data);
}
}
I think I know what causes the problem, because if I write display.clearLayer(DrawingLayer.BACKGROUND); (the layer the tiles are drawn to) before I draw the background tiles, it creates something even more ridiculous.
This is the Display class, where I think I am making some mistake.
public class Display {
private static final char TRANSPARENT_CHARACTER = ' ';
private final AsciiPanel displayPanel;
private final int widthInCharacters, heightInCharacters;
private final static int Z_LEVELS = DrawingLayer.values().length;
private final AsciiCharacterData[][][] characterMap;
public Display(final AsciiPanel panel) {
displayPanel = panel;
widthInCharacters = panel.getWidthInCharacters();
heightInCharacters = panel.getHeightInCharacters();
characterMap = new AsciiCharacterData[widthInCharacters][heightInCharacters][Z_LEVELS];
for (int x = 0; x < widthInCharacters; x++) {
for (int y = 0; y < heightInCharacters; y++) {
for (int z = 0; z < Z_LEVELS; z++) {
characterMap[x][y][z] = new AsciiCharacterData(
TRANSPARENT_CHARACTER,
displayPanel.getDefaultForegroundColor(),
displayPanel.getDefaultBackgroundColor());
}
}
}
}
public void setCharacterAt(final int x, final int y, final DrawingLayer z,
final AsciiCharacterData c) {
if (x < 0 || x >= widthInCharacters || y < 0 || y >= heightInCharacters)
return;
characterMap[x][y][z.layer] = c;
// if z is not the top level
if (z.layer != Z_LEVELS - 1) {
// check all levels above
for (int i = z.layer + 1; i < Z_LEVELS; i++) {
// if there is an opaque character
if (characterMap[x][y][i].character != TRANSPARENT_CHARACTER)
// we dont need to draw anything
return;
}
}
if (c.character == TRANSPARENT_CHARACTER) {
// loop through all characters under the transparent character
for (int i = z.layer - 1; i >= 0; i--) {
// if we find a non transparent character
if (characterMap[x][y][i].character != TRANSPARENT_CHARACTER) {
// display that one instead
displayPanel.write(characterMap[x][y][i].character, x, y,
characterMap[x][y][i].foregroundColor,
characterMap[x][y][i].backgroundColor);
return;
}
}
// if there were no non trasparent characters
displayPanel.write(TRANSPARENT_CHARACTER, x, y);
// if we are a highlighter, we draw the below character and then
// just draw on top
} else {
displayPanel.write(c.character, x, y, c.foregroundColor,
c.backgroundColor);
}
displayPanel.repaint();
}
public AsciiCharacterData getCharacterAt(final int x, final int y,
final DrawingLayer z) {
return characterMap[x][y][z.layer];
}
public int getWidth() {
return widthInCharacters;
}
public int getHeight() {
return heightInCharacters;
}
public void clearLayer(final DrawingLayer layer) {
for (int x = 0; x < widthInCharacters; x++) {
for (int y = 0; y < heightInCharacters; y++) {
setCharacterAt(x, y, layer,
new AsciiCharacterData(TRANSPARENT_CHARACTER,
displayPanel.getDefaultForegroundColor(),
displayPanel.getDefaultBackgroundColor()));
}
}
}
}
Solved! It was one line in the setCharacterAt method. I was repainting every time I set a character which (while inefficient) also creates that flicker.
I've made this code that successfully creates a 16x12 grid by 50x50 squares on a 800x600px board.
As you can see, the player moves to the coordinates of the players mouseclick.
Each square of the grid has an object of Felt (field) on it, which can be laast (locked). If a fields lock attribute is set to 1, the player should not be moved to that position.
How do i detect the field a player tries to move on to achieve this?
public class SimpleGame extends BasicGame{
private Image plane;
private float planeX;
private float planeY;
public SimpleGame()
{
super("SpilTest");
}
#Override
public void init(GameContainer gc) throws SlickException {
plane = new Image("figur.png");
}
#Override
public void update(GameContainer gc, int delta) throws SlickException {
Input input = gc.getInput();
if (input.isMousePressed(input.MOUSE_LEFT_BUTTON)) {
this.planeX = input.getMouseX() - 30;
this.planeY = input.getMouseY() - 50;
}
}
public void render(GameContainer gc, Graphics g) throws SlickException {
Felt board[][] = nytGrid();
int distancex = 0;
int distancey = 0;
int counter = 0;
for (int i=0; i < board.length ; i++) {
for (int j=0; j < board[i].length ; j++) {
if (board[i][j].getLaast() == 1) {
g.setColor(Color.red);
g.fillRect(distancex, distancey, 50, 50);
}
distancex += 50;
counter++;
if (counter == 16) {
distancey += 50;
distancex = 0;
counter = 0;
}
}
}
g.drawImage(plane, planeX, planeY);
}
public static void main(String[] args) throws SlickException {
AppGameContainer app = new AppGameContainer(new SimpleGame());
app.setDisplayMode(800, 600, false);
app.setTargetFrameRate(60);
app.start();
}
public Felt[][] nytGrid() {
Felt [][] board = new Felt[16][12];
for (int i=0; i < board.length ; i++) {
for (int j=0; j < board[i].length ; j++) {
int x = i;
int y = j;
board[i][j] = new Felt(x, y);
if (i == 5 && j == 5) {
board[i][j].setLaast(1);
}
}
}
return board;
}
}
First off, you should probably initialize the board in the init() method instead of render, so it doesn't have to do it every frame, and move the declaration for the grid next to the plane, planeX and planeY declarations in the class.
Now to disable movement into a locked square, first add a method to check if a square at certain coordinates is locked, so something along the lines of:
private boolean isLocked(int x, int y) {
int square = board[x/50][y/50];
if (square == 1) return true;
else return false;
}
Next modify the part of your update() method where you update the plane coordinates, so vaguely something like:
if (input.isMousePressed(input.MOUSE_LEFT_BUTTON)) {
int destX = input.getMouseX() - 30;
int destY = input.getMouseY() - 50;
if (!isLocked(destX, destY)) {
this.planeX = destX;
this.planeY = destY;
}
}
It's easy!
int mx = Mouse.getX();
int my = Mouse.getY();
But, it gives you the world cordinates, and you have to translate it to pixels:
int mx = Mouse.getX();
int my = Mouse.getY() * -1 + (Window.WIDTH / 2) + 71;
I'm using Processing to divide a large image into a series of smaller, rectangular nodes.
Processing stores the color value for the pixels of a PImage in a pixels array, which I am accessing to break up the image into smaller parts. For some reason, I am getting this output, when my intent was for the entire image to be displayed when the nodes are arranged in draw().
Here is my main class:
ArrayList node = new ArrayList();
PImage grid;
PVector nodeDimensions = new PVector(210, 185);
PVector gridDimensions = new PVector(2549, 3300);
String name = "gridscan.jpeg";
void setup() {
size(500, 500);
grid = loadImage(name);
grid.loadPixels();
fillPixels();
noLoop();
}
void fillPixels() {
int nodeNum = 0;
for (int startX = 0; startX < 2549 - nodeDimensions.x; startX += nodeDimensions.x) {
for (int startY = 0; startY < 3300 - nodeDimensions.y; startY += nodeDimensions.y) {
node.add(new Node());
sendPixels(new PVector(startX, startY), nodeNum);
nodeNum++;
}
}
}
void sendPixels(PVector start, int nodeNum) {
for (int x = int(start.x); x < start.x + nodeDimensions.x; x++) {
for (int y = int(start.y); y < start.x + nodeDimensions.y; y++) {
Node _node = (Node) node.get(node.size() - 1);
_node.fillPixel(new PVector(x, y), grid.pixels[int(y*gridDimensions.x+x)]);
}
}
}
void draw() {
drawNodes();
}
void drawNodes() {
int nodeNum = 0;
for (int x = 0; x < width; x += nodeDimensions.x) {
for (int y = 0; y < height; y += nodeDimensions.y) {
Node _node = (Node) node.get(nodeNum);
_node.drawMe(new PVector(x - (nodeDimensions.x/2), y - (nodeDimensions.y/2)));
nodeNum++;
}
}
}
And here is the Node class:
class Node {
color[] pixel;
Node() {
pixel = new color[int(nodeDimensions.x * nodeDimensions.y)];
}
void fillPixel(PVector pos, color pixelValue) {
if(int(pos.y * nodeDimensions.y + pos.x) < 38850) pixel[int(pos.y * nodeDimensions.y + pos.x)] = pixelValue;
}
void drawMe(PVector centerPos) {
pushMatrix();
translate(centerPos.x, centerPos.y);
for(int x = 0; x < nodeDimensions.x; x++) {
for(int y = 0; y < nodeDimensions.y; y++) {
stroke(getPixelColor(new PVector(x, y)));
point(x,y);
}
}
popMatrix();
}
color getPixelColor(PVector pos) {
return pixel[int(pos.y * nodeDimensions.x + pos.x)];
}
}
Hopefully my code makes sense. I suspect the issue is in the sendPixels() method of the main class.
I used this this page from the Processing reference as a guide for creating that function, and I'm not sure where my logic is wrong.
Any help would be appreciated, and please let me know if I can clarify something.
According to getPixelColor(), it seems that it uses rows.
So if you have a 5x5 square image then 2x2 would be 7.
To get the index you use this formula:
index = (y - 1) * width + x
Explained this way it's look pretty simple, doesn't it?
Alternatively, you may be able to use getSubimage() on the BufferedImage returned by the getImage method of PImage. There's a related example here.