This site requires JavaScript, please enable it in your browser!
Greenfoot back
rocket770
rocket770 wrote ...

2020/12/16

Serialization to save objects into a file

rocket770 rocket770

2020/12/16

#
Hi, I've followed a few tutorials and I'm generating a random maze which is represented in a list of Grid objects. Each Grid has walls which are displayed through drawing lines (This all works fine). My goal to save this array of grids into a file so I can import it for later use and I am trying to achieve this through serialization however I am getting this error: java.io.NotSerializableException: greenfoot.Color I can post the full print out if it helps, it's just extremely long. Here is the code where I am trying to save this (It's in the world-class, which implements Serializable).
1
2
3
4
5
6
7
8
9
try(FileOutputStream in = new FileOutputStream("file.ser");
            ObjectOutput out = new ObjectOutputStream(in)) {
                out.writeObject(grid);
                out.flush();
                out.close();
                System.out.println("Saved Map!");
            }           catch (IOException e) {
                e.printStackTrace();
            }
Where the grid is an ArrayList. Let me know if you need the full code, but the rest of the world-class code is generating and solving the maze. Here is the grid class code from the objects I'm trying to save.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
import greenfoot.*;  // (World, Actor, GreenfootImage, Greenfoot and MouseInfo)
import java.util.Arrays;
import java.util.ArrayList;
import java.util.Random;
import java.util.Stack;
import java.io.*;
 
public class Cell extends Actor implements Serializable {
    int i,j,x,y;
    int size;
    Boolean Walls[] = new Boolean[4];   // top, right, bottom, left
    MyWorld world = ((MyWorld)getWorld());
    boolean visited = false;
     boolean  middle;
    boolean checkedWalls = false;
    Stack<Cell> neighbors = new Stack<Cell>();   
    int f,h,g;
    Cell previous = null;       // Each Cell contains a preivous cell in its path, which contains a preivous cell and so on.
    Boolean wall;
    int number;
    Cell (int i, int j){
        this.i = i;
        this.j = j;
        Arrays.fill(Walls, Boolean.TRUE);
        world = (MyWorld)MyWorld.world;
        size = world.size;
        x = i*size;
        y = j*size;
    }
 
    public void show(){
        if(middle){ // Mark goal as green and remove all walls surrounding it, allows for more path options to get to goal
            showNow(Color.GREEN);
            checkWalls();
        }
        world.getBackground().setColor(Color.BLACK);
        // Check if wall should be drawn, if side value is true
        if (Walls[0]) {
            world.getBackground().drawLine(x, y,x +size, y);
        }
        if (Walls[1]) {
            world.getBackground().drawLine(x+size, y,x +size,y+size);
        }
        if (Walls[2]) {
            world.getBackground().drawLine(x +size, y +size, x,y+size);
        }
        if (Walls[3]) {
            world.getBackground().drawLine(x,y +size,x,y);
        }
    }
     
    void showNow(Color color){
        world.getBackground().setColor(color);
        world.getBackground().fillRect(x, y, size, size);
    }
 
    void checkWalls(){
        if(!checkedWalls){
            removeMiddleWalls();
            setStartSpace();
            checkedWalls = true;
        }
    }
     
    void getNumber(){
        number = (world.bestPath.indexOf(this) - world.bestPath.size())*-1-1 ;
    }
 
    void setStartSpace(){
        for(int i = 0; i<2; i++){
            world.grid.get(i).removeAllWalls();
            world.grid.get(i+world.cols).removeAllWalls();
        }
    }
 
    void removeAllWalls(){
        Arrays.fill(Walls, Boolean.FALSE);
    }
 
    int getIndex(int i, int j){
        if(i<0 || j < 0 || i > world.cols-1 || j > world.rows-1) return -1; // Return null if is not on the gird eg. searching edge of world there is no grid beside it
        return i + j * world.cols;  // Turn multi dimensional array into single, formula found online
    }
 
    void removeMiddleWalls(){
        removeAllWalls();                                                   // get middle cell and remove walls around
        // Need to remove neighbouring walls aswell in slim chance it will generate a wall for it instead. Override
        world.grid.get(world.middleIndex-world.cols).Walls[2] = false;      // to the top cell, remove bottom wall
        world.grid.get(world.middleIndex+world.cols).Walls[0] = false;      // to the bottom cell, remove top wall
        world.grid.get(world.middleIndex-1).Walls[1] = false;               // to the left cell, remove right wall
        world.grid.get(world.middleIndex+1).Walls[3] = false;               // to the right cell, remove left wall
    }
    // Look for walls at each direction
    boolean checkTop(){
        if(world.grid.get(world.grid.indexOf(this)-world.cols).Walls[2] == true){  // find location of each neighbor
            return true;
        return false;
    }
 
    boolean checkBelow(){
        if(world.grid.get(world.grid.indexOf(this)+world.cols).Walls[0] == true){  // find location of each neighbor
            return true;
        return false;
    }
 
    boolean checkRight(){
        if(world.grid.get(world.grid.indexOf(this)-1).Walls[1] == true){  // find location of each neighbor
            return true;
        return false;
    }
 
    boolean checkLeft(){
        if(world.grid.get(world.grid.indexOf(this)+1).Walls[3] == true){  // find location of each neighbor
            return true;
        return false;
    }
 
 
    // Get the cells artound current cell that is unvisited
    Cell checkNeighbors(){
        ArrayList<Cell> neighborsList = new ArrayList<Cell>();
        Cell top;
        Cell right;
        Cell bottom;
        Cell left;
        // Avoid java being weird about returning -1 as null
        if(getIndex(i,j-1) != -1){  // find location of each neighbor
            top = world.grid.get(getIndex(i,j-1));
        } else top = null;
        if(getIndex(i+1,j) != -1){
            right = world.grid.get(getIndex(i+1,j));
        } else right = null;
        if(getIndex(i,j+1) != -1){
            bottom = world.grid.get(getIndex(i,j+1));
        } else bottom = null;
        if(getIndex(i-1,j) != -1){
            left = world.grid.get(getIndex(i-1,j));
        } else left = null;
        // If it actually exists and hasn't been visted add it to the possible options of neighbors
        if(top != null && !top.visited) neighborsList.add(top);
        if(right != null && !right.visited) neighborsList.add(right);
        if(bottom != null && !bottom.visited ) neighborsList.add(bottom);
        if(left != null && !left.visited ) neighborsList.add(left);
        // Pick random nieghbour, if there is any unvisted ones
        if (neighborsList.size() > 0) {
            Random r = new Random();
            int random = Greenfoot.getRandomNumber(neighborsList.size());
            return neighborsList.get(random);
        }else return null// must reutrn something if no neighbours, start backtrack if null
    }
 
    void addNeighbors(){
        if(i < world.cols - 1){ 
            neighbors.push(world.grid.get((getIndex(i+1,j))));
        }
        if(i > 0 ){ 
            neighbors.push(world.grid.get((getIndex(i-1,j))));
        }
        if(j < world.cols - 1){ 
            neighbors.push(world.grid.get((getIndex(i,j+1))));
        
        if(j > 0){ 
            neighbors.push(world.grid.get((getIndex(i,j-1))));
        
    }
 
}
Thanks!
Jackylein250 Jackylein250

2020/12/21

#
I have never worked with the Serializable interface, but I have also written something like an import/export functionality for a Grid of Cells. Essentially you have to define which properties of the Cell you want to save. Try to minimize the amount of them as much as possible. Depending on these properties you have to decide which format of file you want to use. Maybe you will have to look into YML/JSON parser. I have worked with JSEFA and YML once for config files. In the example with the Grid of Cells I had a few types of Cells, so I needed to save only the type of the Cell. For that purpose I decided that a .txt file would be completely fine. I defined a char (stored in a String) for each Cell type so every Cell could be parsed into and from this char. The position of the Cells could be computed through the position of the char in the text file. The code for reading / saving the file is located in my World class and the fillWorld() method essentially places the Cells in the World among some other things. Every time I load the World I can read a specific text file and voilà my grid & world is filled.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
* get a {@link BufferedWriter} for the given file
    *
    * @param filePath
    * @return
    * @throws IOException
    */
   private BufferedWriter getBufferedWriter(String filePath) throws IOException {
       FileOutputStream outputStream = new FileOutputStream(filePath);
       OutputStreamWriter outputStreamWriter = new OutputStreamWriter(outputStream, "UTF-8");
       return new BufferedWriter(outputStreamWriter);
   }
 
   /**
    * Saves the current worlds grid in a text file from where it can be loaded
    * again.<br>
    * source:codejava.com
    *
    * Saves the file in the "worlds" folder
    *
    * @param fileName name of the file without file type (no ".txt")
    */
   public void saveWorldToTextFile(String fileName) {
       String filePath = "./worlds/" + fileName + ".txt";
       try (BufferedWriter bufferedWriter = getBufferedWriter(filePath)) {
           for (int y = 0; y < grid[0].length; y++) {
               char[] chars = new char[grid.length];
               for (int x = 0; x < grid.length; x++) {
                   Cell cell = grid[x][y];
                   if(cell instanceof StartPathCell) {
                       chars[x] = START_CELL_PARSE_CHAR;
                   } else if(cell instanceof EndPathCell) {
                       chars[x] = END_CELL_PARSE_CHAR;
                   } else if(cell instanceof NormalPathCell) {
                       chars[x] = PATH_CELL_PARSE_CHAR;
                   } else if(cell instanceof TowerCell) {
                       chars[x] = TOWER_CELL_PARSE_CHAR;
                   } else if(cell instanceof NormalCell) {
                       chars[x] = NORMAL_CELL_PARSE_CHAR;
                   }
               }
               bufferedWriter.write(chars);
               bufferedWriter.newLine();
           }
       } catch (IOException e) {
           System.err.println("Unable to write file " + filePath);
           e.printStackTrace();
       } // BufferedWriter, FileOutputStream and OutputStreamWriter auto close themselves
   }
 
   /**
    * get a {@link BufferedReader} for a given file
    *
    * @param filePath
    * @return
    * @throws IOException
    */
   private BufferedReader getBufferedReader(String filePath) throws IOException {
       FileInputStream inputStream = new FileInputStream(filePath);
       InputStreamReader inputStreamReader = new InputStreamReader(inputStream, "UTF-8");
       return new BufferedReader(inputStreamReader);
   }
 
   /**
    * Loads a grid from a text file into the current world.<br>
    *
    * Loads the file from the "worlds" folder<br>
    * source:codejava.com
    *
    * @param fileName name of the file without file type (no ".txt")
    */
   public void loadWorldFromTextFile(String fileName) {
       String filePath = "./worlds/" + fileName + ".txt";
       try (BufferedReader bufferedReader = getBufferedReader(filePath)) {
           String line;
 
           List<List<String>> chars = new ArrayList<>();
           while ((line = bufferedReader.readLine()) != null) {
               String[] letters = line.split("");
               chars.add(Arrays.asList(letters));
           }
           int lineCount = chars.size();
           int maxCharCount = 0;
           for (int i = 0; i < lineCount; i++) {
               maxCharCount = Math.max(maxCharCount, chars.get(i).size());
           }
           if(maxCharCount > GRID_SIZE_X || lineCount > GRID_SIZE_Y) {
               System.err.println("The grid that is in file " + fileName + " is too big." + maxCharCount + "x" + lineCount + ">" + GRID_SIZE_X + "x"
                       + GRID_SIZE_Y);
               return;
           }
           Cell[][] cells = new Cell[GRID_SIZE_X][GRID_SIZE_Y];
           for (int y = 0; y < GRID_SIZE_Y; y++) {
               for (int x = 0; x < GRID_SIZE_X; x++) {
                   if(x < maxCharCount && y < lineCount) { // as long as grid in file smaller than world grid, parse it
                       char c = chars.get(y).get(x).charAt(0);
                       if(c == PATH_CELL_PARSE_CHAR) {
                           cells[x][y] = new NormalPathCell(x, y);
                       } else if(c == START_CELL_PARSE_CHAR) {
                           cells[x][y] = new StartPathCell(x, y);
                       } else if(c == END_CELL_PARSE_CHAR) {
                           cells[x][y] = new EndPathCell(x, y);
                       } else if(c == TOWER_CELL_PARSE_CHAR) {
                           cells[x][y] = new TowerCell(x, y);
                       } else { // includes c == NORMAL_CELL_PARSE_CHAR
                           cells[x][y] = new NormalCell(x, y);
                       }
                   } else { // if grid in file is smaller than world grid fill the rest up
                       cells[x][y] = new NormalCell(x, y);
                   }
               }
           }
           this.grid = cells;
           fillWorld();
       } catch (IOException e) {
           System.err.println("Unable to read file " + filePath);
           e.printStackTrace();
       } // BufferedReader, InputStreamReader and FileInputStream auto close themselves
   }
I hope this helps.
rocket770 rocket770

2020/12/22

#
Hi, thanks! Based on that idea, I wrote a file Parser that stores only the Walls Array for each cell as a byte.
1
2
3
4
5
6
7
8
9
10
11
12
try(FileOutputStream in = new FileOutputStream("file.bin"))
            {
                for(int i = 0; i<grid.size(); i++){
                    for(int j = 0; j<3;j++){
                        in.write(grid.get(i).Walls[j] ? 1 : 0);
                    }
                }
                System.out.println("Saved!");
                in.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
This seems to be working ok when I read the file into a byte array rather than a char array and convert it to its boolean value so thanks!
You need to login to post a reply.