Kopiera 2D array med objekt i Java

Permalänk
Entusiast

Kopiera 2D array med objekt i Java

Jag håller på att bli galen på det här nu. Jag försöker skriva ett enkelt Game of Life (https://en.wikipedia.org/wiki/Conway's_Game_of_Life) i Java och har börjat med en textbaserad version. För att representera själva spelbrädet har jag en 2D array. För varje ny generation går jag igenom hela arrayen och kollar vilka närliggande element som lever eller är döda och det är här det skiter sig. Tanken är att jag tittar på en array och sedan skriver alla ändringar till en ny kopia. När jag vandrat igenom hela arrayen så vill jag ersätta den gamla kopian med den nya och sedan kan man börja om med en ny generation.

Problemet är att hur jag än gör så får jag inte en deep copy utan den skriver över även den gamla arrayen vilket göra att programmet inte fungerar alls. Jag har fetmarkerat koden jag använder för att göra en kopia. Har testat både .clone() och Arrays.copyOf() med samma resultat. När jag ändrar något i den nya arrayen newCells så ändras även den gamla arrayen cells. Därför utgår jag från att jag inte får en riktig kopia utan att Java bara kopierar referenserna så att båda arrayerna fortfarande pekar på samma objekt.

Så här ser koden ut nu:

Seed.java ser ut så här och är bara en enum.

package lifetext; public enum Seed { ALIVE, DEAD }

Cell.java sköter en enskild cell

package lifetext; public class Cell { Seed cellState; int row, col; public Cell(int row, int col) { row = this.row; col = this.col; } public void kill(){ cellState = Seed.DEAD; } public void birth(){ cellState = Seed.ALIVE; } public Seed getState() { return cellState; } public void paint() { switch(cellState) { case DEAD: System.out.print(" - "); break; case ALIVE: System.out.print(" O "); break; } } }

Sedan har vi Board.java där felet ligger.

package lifetext; import java.util.Arrays; public class Board { public static final int ROWS = 10; public static final int COLS = 10; Cell cells[][]; int currentRow, currentCol; //Constructor public Board() { cells = new Cell[ROWS][COLS]; for (int row=0; row<ROWS; ++row) { for (int col=0; col<COLS; ++col) { cells[row][col] = new Cell(row, col); } } }//End of constructor //Rensa hela bordet genom att döda alla celler public void init() { for (int row = 0; row < ROWS; ++row) { for (int col = 0; col < COLS; ++col) { cells[row][col].kill(); // clear the cell content } } }//End of init public void setStartState(int[][] startState) { for (int row=0; row<ROWS; ++row) { for (int col=0; col<COLS; ++col) { if (startState[row][col] == 1) { cells[row][col].birth(); } } } }//End of setStartState public void nextGen() { int numberAlive = 0; Cell[][] newCells = new Cell[cells.length][]; newCells = arrayClone(cells); for (int row=1; row<(ROWS-1); ++row){ for (int col=1; col<(COLS-1); ++col) { for (int irow=(row-1); irow<=(row+1); irow++) { for (int icol=(col-1); icol<=(col+1); icol++) { if (cells[irow][icol].getState() == Seed.ALIVE) { numberAlive++; } } } //Ta kolla aktuell cell så den inte räknas if (cells[row][col].getState() == Seed.ALIVE) { numberAlive--; } //kill and give birth to new cells if (numberAlive < 2) { newCells[row][col].kill(); } else if (numberAlive > 3) { newCells[row][col].kill(); } else if (cells[row][col].getState() == Seed.ALIVE && (numberAlive == 2 || numberAlive == 3)) { newCells[row][col].birth(); } else if (cells[row][col].getState() == Seed.DEAD && numberAlive == 3) { newCells[row][col].birth(); } numberAlive = 0; } } cells = arrayClone(cells); }//End of nextGen() public static Cell[][] arrayClone(Cell[][] cells) { Cell[][] newCells = new Cell[cells.length][]; for (int i=0; i<cells.length; i++) { //newCells[i] = cells[i].clone(); newCells[i] = Arrays.copyOf(cells[i], cells.length); } return newCells; } public void paint() { for (int row = 0; row < ROWS; ++row) { for (int col = 0; col < COLS; ++col) { cells[row][col].paint(); // each cell paints itself } System.out.println(); } System.out.println(); } }//End of class

Här har vi main.

package lifetext; public class LifeText { private Board board; private Seed currentPlayer; //private int[][] startState = new int[10][10]; private static Scanner in = new Scanner(System.in); public LifeText() { board = new Board(); int[][] startState = {{0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0}}; board.init(); board.setStartState(startState); for (int gen=0; gen<2; gen++) { board.paint(); board.nextGen(); try { Thread.sleep(500); } catch(InterruptedException ex) { Thread.currentThread().interrupt(); } } } public static void main(String[] args) { //Kör igång spelet new LifeText(); } }

Visa signatur

Q9450, HD4850, 8 GB DDR2 800 MHz, 3x750 GB, Antec 300, Dell 2408WFP, U2410, Qnap TS-419p+ 4x2 TB Samsung F4, Asus UL30A-QX056V, Logitech Z-680, Sennheiser HD380pro, M-Audio FastTrack Pro, Ibanez sa160qm, Ibanez TB 15R, Zoom 505II, Ibanez GSR 200, Ibanez SW 35, Cort AC-15, Squier SD-3 BBL, Yamaha PSR 270, Røde NT1-A, Nikon D200, Nikkor 18-70/3,5-4,5, 70-300VR, 50/1,8, 28/2,8, Tamron 17-50/2,8, 90/2,8, Sigma 30/1,4, SB-800, SB-25, SB-24

Permalänk
Medlem
Skrivet av Zotamedu:

Jag håller på att bli galen på det här nu. Jag försöker skriva ett enkelt Game of Life (https://en.wikipedia.org/wiki/Conway's_Game_of_Life) i Java och har börjat med en textbaserad version. För att representera själva spelbrädet har jag en 2D array. För varje ny generation går jag igenom hela arrayen och kollar vilka närliggande element som lever eller är döda och det är här det skiter sig. Tanken är att jag tittar på en array och sedan skriver alla ändringar till en ny kopia. När jag vandrat igenom hela arrayen så vill jag ersätta den gamla kopian med den nya och sedan kan man börja om med en ny generation.

Problemet är att hur jag än gör så får jag inte en deep copy utan den skriver över även den gamla arrayen vilket göra att programmet inte fungerar alls. Jag har fetmarkerat koden jag använder för att göra en kopia. Har testat både .clone() och Arrays.copyOf() med samma resultat. När jag ändrar något i den nya arrayen newCells så ändras även den gamla arrayen cells. Därför utgår jag från att jag inte får en riktig kopia utan att Java bara kopierar referenserna så att båda arrayerna fortfarande pekar på samma objekt.

Deep copy av objekt är inget jag rekommenderar på en generell nivå. Det finns inget inbyggt i varken Java eller C# som gör det möjligt att göra deep copy. Allt är shadow copies tills nyckelordet "new" används. För att komma runt detta så använder de flesta deep copy-metoderna serialisering och deserialisering.

Det är bra att det inte är lätt att göra en deep copy, speciellt i ett objektorienterat språk. Den största anledningen till detta enligt mig är nog att om du i ditt fall skulle lägga in en referens till Board i varje cell så hade varje cell gjort en deep copy av den också, och det vill du nog inte.

För att lösa ditt problem så hade jag gjort en speciell konstruktor eller metod på Cell-klassen som heter duplicate() eller liknande.

var cellCopy = cell.duplicate();

Där i din duplicate()-metod så kan du välja själv vilka egenskaper som ska kopieras eller inte. Denna metod använder du och bygger upp en ny array med dubbletter av cellerna.

Om du av historiska skäl vill spara undan hur det såg ut under olika generationer så kanske det är bättre att ha en logg på varje cell som sparar State indexerat på generation? Om du vill optimera det vidare så kan du göra så att cellerna endast sparar om de är vid liv då majoriteten av planen kommer vara död. Med andra ord att om inget index finns för en specifik generation i hashen så tolkar man det som att cellen är död.

Visa signatur

ηλί, ηλί, λαμά σαβαχθανί!?

Permalänk
Entusiast
Skrivet av Leedow:

Deep copy av objekt är inget jag rekommenderar på en generell nivå. Det finns inget inbyggt i varken Java eller C# som gör det möjligt att göra deep copy. Allt är shadow copies tills nyckelordet "new" används. För att komma runt detta så använder de flesta deep copy-metoderna serialisering och deserialisering.

Det är bra att det inte är lätt att göra en deep copy, speciellt i ett objektorienterat språk. Den största anledningen till detta enligt mig är nog att om du i ditt fall skulle lägga in en referens till Board i varje cell så hade varje cell gjort en deep copy av den också, och det vill du nog inte.

För att lösa ditt problem så hade jag gjort en speciell konstruktor eller metod på Cell-klassen som heter duplicate() eller liknande.

var cellCopy = cell.duplicate();

Där i din duplicate()-metod så kan du välja själv vilka egenskaper som ska kopieras eller inte. Denna metod använder du och bygger upp en ny array med dubbletter av cellerna.

Om du av historiska skäl vill spara undan hur det såg ut under olika generationer så kanske det är bättre att ha en logg på varje cell som sparar State indexerat på generation? Om du vill optimera det vidare så kan du göra så att cellerna endast sparar om de är vid liv då majoriteten av planen kommer vara död. Med andra ord att om inget index finns för en specifik generation i hashen så tolkar man det som att cellen är död.

Jag behöver inte spara någon historik. Det enda jag behöver är två matriser som motsvarar det nuvarande och det nya tillståndet för alla celler så jag kan göra förändringar i en matris utan att ändra den aktuella och sedan bara byta den gamla mot den nya i varje iterationssteg.

Tack så mycket för tipset om en speciell konstruktor och en metod för att kopiera. Nu verkar det äntligen fungera som jag vill.

Jag kommer inte köra en så stor spelplan och jag kommer ändå behöva lägga in något som väntar mellan varje generations utskrift så man kan följa händelseförloppet så vidare optimeringar är överkurs. Det här projektet är för en introduktionskurs i Java och jag tror jag redan gett mig in på ett mer komplicerat projekt än jag behöver.

Visa signatur

Q9450, HD4850, 8 GB DDR2 800 MHz, 3x750 GB, Antec 300, Dell 2408WFP, U2410, Qnap TS-419p+ 4x2 TB Samsung F4, Asus UL30A-QX056V, Logitech Z-680, Sennheiser HD380pro, M-Audio FastTrack Pro, Ibanez sa160qm, Ibanez TB 15R, Zoom 505II, Ibanez GSR 200, Ibanez SW 35, Cort AC-15, Squier SD-3 BBL, Yamaha PSR 270, Røde NT1-A, Nikon D200, Nikkor 18-70/3,5-4,5, 70-300VR, 50/1,8, 28/2,8, Tamron 17-50/2,8, 90/2,8, Sigma 30/1,4, SB-800, SB-25, SB-24