~fedevera/pictogame/Pictogame

« back to all changes in this revision

Viewing changes to picto/utils/Picto.java

  • Committer: Federico Vera
  • Date: 2011-11-20 21:23:28 UTC
  • Revision ID: dktcoding@gmail.com-20111120212328-eqbxb8kth9hxs3zw
FirstĀ codeĀ upload

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 *                      ..::PictoGame::..
 
3
 *
 
4
 * Copyright (C) Federico Vera 2010-2011 <dktcoding [at] gmail>
 
5
 *
 
6
 * PictoGame is free software: you can redistribute it and/or modify it
 
7
 * under the terms of the GNU General Public License as published by the
 
8
 * Free Software Foundation, either version 3 of the License, or any later
 
9
 * version.
 
10
 *
 
11
 * PictoGame is distributed in the hope that it will be useful, but
 
12
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 
14
 * See the GNU General Public License for more details.
 
15
 *
 
16
 * You should have received a copy of the GNU General Public License along
 
17
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 
18
 */
 
19
 
 
20
package picto.utils;
 
21
 
 
22
import java.io.BufferedReader;
 
23
import java.io.File;
 
24
import java.io.FileNotFoundException;
 
25
import java.io.FileReader;
 
26
import java.io.IOException;
 
27
import java.io.InputStream;
 
28
import java.io.InputStreamReader;
 
29
import java.io.Serializable;
 
30
import java.util.ArrayList;
 
31
import java.util.Arrays;
 
32
import java.util.logging.Level;
 
33
import java.util.logging.Logger;
 
34
import picto.app.interfaces.Flushable;
 
35
 
 
36
/**
 
37
 * This class represents the pattern file.<br>
 
38
 * The pattern files contain the following information:<br><ol>
 
39
 * <li><b>Title</b>  -- The name of the pattern
 
40
 * <li><b>Author</b> -- Who created the pattern
 
41
 * <li><b>Date</b>   -- The date the pattern was added to the game
 
42
 * <li><b>X size</b> -- Number of columns of the pattern
 
43
 * <li><b>y size</b> -- Number of rows of the pattern
 
44
 * <li><i>blank line</i>
 
45
 * <li><b>Data row 1</b>
 
46
 * <li><b>Data row 2</b>
 
47
 * <li><b>Data row ...</b>
 
48
 * <li><b>Data row n</b></ol>
 
49
 * The data is represented row by row with <tt>64-bit</tt> numbers in hex
 
50
 * (without the <tt>'0x'</tt> identifier), as you know each number is
 
51
 * represented by bits in this case each <tt>1</tt> means that the cell should
 
52
 * be painted and as you might gather each <tt>0</tt> means that the cell should
 
53
 * not be painted.
 
54
 *
 
55
 * @author Fido <dktcoding [at] gmail>
 
56
 */
 
57
public final class Picto implements Serializable, Flushable {
 
58
    private static final long serialVersionUID = -535901729081793261L;
 
59
    private transient byte [][] columnHeaders;
 
60
    private transient byte [][] rowHeaders;
 
61
    private transient boolean[][] board;
 
62
    private transient long[] columns;
 
63
    private long[] data;
 
64
    private String title = "";
 
65
    private String author = "";
 
66
    private String date = "";
 
67
    private byte numberOfRows;
 
68
    private byte numberOfColumns;
 
69
    private int numberOfMarkedCells;
 
70
 
 
71
    public Picto(int i, int j){
 
72
        this((byte)i, (byte)j);
 
73
    }
 
74
 
 
75
    public Picto(byte i, byte j){
 
76
        board = new boolean[i][j];
 
77
        numberOfRows = i;
 
78
        numberOfColumns = j;
 
79
    }
 
80
 
 
81
    /**
 
82
     * Creates a new Picto board from a file
 
83
     *
 
84
     * @param input input file
 
85
     * @param justInfo this flag tells the constructor if you are using
 
86
     * the Picto to get information or to play.
 
87
     */
 
88
    public Picto(InputStream input, boolean justInfo){
 
89
        loadBoardFromPatternFile(new BufferedReader(new InputStreamReader(input)));
 
90
        if (!justInfo){
 
91
            loadHeaders();
 
92
        }
 
93
    }
 
94
 
 
95
    public Picto(BufferedReader buff){
 
96
        loadBoardFromPatternFile(buff);
 
97
    }
 
98
 
 
99
    public Picto(File file){
 
100
        if (file.getName().toLowerCase().endsWith("sgriddler")){
 
101
            loadBoardFromSGriddlerFile(file);
 
102
            loadHeaders();
 
103
        } else if (file.getName().toLowerCase().endsWith("pattern")){
 
104
            try {
 
105
                loadBoardFromPatternFile(new BufferedReader(new FileReader(file)));
 
106
                loadHeaders();
 
107
            } catch (FileNotFoundException ex) {
 
108
                Logger.getLogger(Picto.class.getName()).log(Level.SEVERE, null, ex);
 
109
            }
 
110
        }
 
111
    }
 
112
 
 
113
    /**
 
114
     * Generates the headers and the board
 
115
     */
 
116
    public void regenerate(){
 
117
        loadHeaders();
 
118
    }
 
119
 
 
120
    /**
 
121
     * Remove all unnecessary things. This method is called when you finish
 
122
     * playing a game
 
123
     */
 
124
    @Override 
 
125
    public void flush(){
 
126
        columnHeaders = null;
 
127
        rowHeaders = null;
 
128
        columns = null;
 
129
        board = null;
 
130
    }
 
131
 
 
132
    public long getRow(int i){
 
133
        return data[i];
 
134
    }
 
135
 
 
136
    public long getColumn(int i){
 
137
        if (columns == null){
 
138
            calculateColumns();
 
139
        }
 
140
        return columns[i];
 
141
    }
 
142
 
 
143
    public void setElement(int i, int j, boolean e){
 
144
        board[i][j] = e;
 
145
    }
 
146
 
 
147
    public boolean getElement(int i, int j){
 
148
        return board[i][j];
 
149
    }
 
150
 
 
151
    public byte getColumnHeaderNum(){
 
152
        return (byte)columnHeaders[0].length;
 
153
    }
 
154
 
 
155
    public byte getRowHeaderNum(){
 
156
        return (byte)rowHeaders[0].length;
 
157
    }
 
158
 
 
159
    public byte getColumnHeadersElement(int i, int j) {
 
160
        return columnHeaders[i][j];
 
161
    }
 
162
 
 
163
    public byte getRowHeadersElement(int i, int j) {
 
164
        return rowHeaders[i][j];
 
165
    }
 
166
 
 
167
    public byte getNumberOfColumns() {
 
168
        return numberOfColumns;
 
169
    }
 
170
 
 
171
    public void setNumberOfColumns(byte numberOfColumns) {
 
172
        this.numberOfColumns = numberOfColumns;
 
173
    }
 
174
 
 
175
    public byte getNumberOfRows() {
 
176
        return numberOfRows;
 
177
    }
 
178
 
 
179
    public void setNumberOfRows(byte numberOfRows) {
 
180
        this.numberOfRows = numberOfRows;
 
181
    }
 
182
 
 
183
    public String getTitle() {
 
184
        return title;
 
185
    }
 
186
 
 
187
    public void setTitle(String title) {
 
188
        this.title = title;
 
189
    }
 
190
 
 
191
    public String getAuthor() {
 
192
        return author == null || author.isEmpty() ? "Unknown" : author;
 
193
    }
 
194
 
 
195
    public void setAuthor(String author) {
 
196
        this.author = author;
 
197
    }
 
198
 
 
199
    public String getDate() {
 
200
        return date;
 
201
    }
 
202
 
 
203
    public void setDate(String date) {
 
204
        this.date = date;
 
205
    }
 
206
 
 
207
    private static final int TITLE_LENGTH = "# Title: " .length();
 
208
    private static final int AUTHOR_LENGTH = "# Author: " .length();
 
209
    private static final int DATE_LENGTH = "# Date: " .length();
 
210
    private static final int X_SIZE_LENGTH = "# X size: " .length();
 
211
    private static final int Y_SIZE_LENGTH = "# Y size: " .length();
 
212
    /**
 
213
     * Loads a pattern from a panel
 
214
     *
 
215
     * @param file the file to read
 
216
     */
 
217
    private void loadBoardFromPatternFile(BufferedReader file){
 
218
        String line;
 
219
        try {
 
220
            setTitle (file.readLine().substring(TITLE_LENGTH));
 
221
            setAuthor(file.readLine().substring(AUTHOR_LENGTH));
 
222
            setDate  (file.readLine().substring(DATE_LENGTH));
 
223
            line = file.readLine().substring(X_SIZE_LENGTH);
 
224
            setNumberOfColumns(Byte.valueOf(line));
 
225
            line = file.readLine().substring(Y_SIZE_LENGTH);
 
226
            setNumberOfRows(Byte.valueOf(line));line = file.readLine();
 
227
            data = new long[numberOfRows];
 
228
            numberOfMarkedCells = 0;
 
229
            
 
230
            for(byte i = 0; i < numberOfRows; i++){
 
231
                data[i] = Long.valueOf(file.readLine(), 16);
 
232
                numberOfMarkedCells += Long.bitCount(data[i]);
 
233
            }
 
234
            
 
235
        } catch (IndexOutOfBoundsException ex) {
 
236
            Logger.getLogger(Picto.class.getName()).log(Level.SEVERE, null, ex);
 
237
        } catch (IOException ex) {
 
238
            Logger.getLogger(Picto.class.getName()).log(Level.SEVERE, null, ex);
 
239
        }
 
240
    }
 
241
 
 
242
    public static Picto loadBoardFromSGriddlerFile(File file_name){
 
243
        Picto temp = null;
 
244
        String line;
 
245
        try {
 
246
            BufferedReader file = new BufferedReader(new FileReader(file_name));
 
247
            file.readLine();
 
248
            file.readLine();
 
249
            line = file.readLine();
 
250
            String[] spLine = line.split(" ");
 
251
            temp = new Picto(Byte.valueOf(spLine[1]),
 
252
                             Byte.valueOf(spLine[0]));
 
253
            file.readLine();
 
254
            temp.data = new long[temp.numberOfRows];
 
255
            temp.numberOfMarkedCells = 0;
 
256
            
 
257
            for(byte i = 0, n = (byte)temp.numberOfRows; i < n; i++){
 
258
                line = file.readLine();
 
259
                line = line.replace(" ", "" );
 
260
                line = line.replace('#', '1');
 
261
                line = line.replace('.', '0');
 
262
                temp.data[i] = Long.valueOf(line, 2);
 
263
                temp.numberOfMarkedCells += Long.bitCount(temp.data[i]);
 
264
            }
 
265
            
 
266
            temp.loadPanel();
 
267
            temp.loadColumnHeaders();
 
268
            temp.loadRowHeaders();
 
269
            file.close();
 
270
        } catch (IndexOutOfBoundsException ex) {
 
271
            Logger.getLogger(Picto.class.getName()).log(Level.SEVERE, null, ex);
 
272
        } catch (IOException ex) {
 
273
            Logger.getLogger(Picto.class.getName()).log(Level.SEVERE, null, ex);
 
274
        }
 
275
        return temp;
 
276
    }
 
277
 
 
278
    /**
 
279
     * Load the headers and the binary panel
 
280
     */
 
281
    private void loadHeaders(){
 
282
        if (data == null){
 
283
            loadData();
 
284
        }
 
285
        if (board == null){
 
286
            loadPanel();
 
287
        }
 
288
        loadColumnHeaders();
 
289
        loadRowHeaders();
 
290
    }
 
291
 
 
292
    /**
 
293
     * Loads the binary panel from the data
 
294
     */
 
295
    private void loadPanel(){
 
296
        board = new boolean[numberOfRows][numberOfColumns];
 
297
        for(int i = 0; i < numberOfRows; i++){
 
298
            for (int j = numberOfColumns - 1, k = 0; j >= 0; j--){
 
299
                board[i][k++] = (data[i] & (1L << j)) != 0;
 
300
            }
 
301
        }
 
302
    }
 
303
 
 
304
    /**
 
305
     * Load the row headers from the data
 
306
     *
 
307
     * @TODO clean up and optimize this a "little" bit
 
308
     */
 
309
    private void loadRowHeaders(){
 
310
        ArrayList<ArrayList<Byte>> rows = new ArrayList<ArrayList<Byte>>(numberOfRows);
 
311
        int max_e = 0;
 
312
        byte counter;
 
313
        
 
314
        for (boolean[] row : board){
 
315
            ArrayList<Byte> temp = new ArrayList<Byte>(5);
 
316
            counter = 0;
 
317
            
 
318
            for (boolean cell : row){
 
319
                if (cell){
 
320
                    counter++;
 
321
                } else if (counter != 0){
 
322
                    temp.add(counter);
 
323
                    counter = 0;
 
324
                }
 
325
            }
 
326
            
 
327
            if (counter != 0){
 
328
                temp.add(counter);
 
329
            }
 
330
            
 
331
            if (max_e < temp.size()){
 
332
                max_e = temp.size();
 
333
            }
 
334
            rows.add(temp);
 
335
        }
 
336
        
 
337
        counter = 0;
 
338
        rowHeaders = new byte[numberOfRows][max_e];
 
339
        
 
340
        for (ArrayList<Byte> foo : rows){
 
341
            for (int j = foo.size()-1, pos = max_e; j >= 0; j--){
 
342
                rowHeaders[counter][--pos] = foo.get(j);
 
343
            }
 
344
            counter++;
 
345
        }
 
346
 
 
347
    }
 
348
 
 
349
    /**
 
350
     * Load the column headers from the data
 
351
     *
 
352
     * @TODO clean up and optimize this a "little" bit
 
353
     */
 
354
    private void loadColumnHeaders(){
 
355
        ArrayList<ArrayList<Byte>> cols = new ArrayList<ArrayList<Byte>>(numberOfColumns);
 
356
        byte counter;
 
357
        int max_e = 0;
 
358
        
 
359
        for (int i = 0; i < numberOfColumns; i++){
 
360
            ArrayList<Byte> temp = new ArrayList<Byte>(5);
 
361
            counter = 0;
 
362
            
 
363
            for (int j = 0; j < numberOfRows; j++){
 
364
                if (board[j][i]){
 
365
                    counter++;
 
366
                } else if (counter != 0){
 
367
                    temp.add(counter);
 
368
                    counter = 0;
 
369
                }
 
370
            }
 
371
            
 
372
            if (counter != 0){
 
373
                temp.add(counter);
 
374
            }
 
375
            
 
376
            if (max_e < temp.size()){
 
377
                max_e = temp.size();
 
378
            }
 
379
            
 
380
            cols.add(temp);
 
381
        }
 
382
        
 
383
        counter = 0;
 
384
        columnHeaders = new byte[numberOfColumns][max_e];
 
385
        
 
386
        for (ArrayList<Byte> foo : cols){
 
387
            for (int j = foo.size()-1, pos = max_e; j >= 0; j--){
 
388
                columnHeaders[counter][--pos] = foo.get(j);
 
389
            }
 
390
            counter++;
 
391
        }
 
392
    }
 
393
 
 
394
    /**
 
395
     * Returns the sum of all the marked cells
 
396
     *
 
397
     * @return number of marked cells
 
398
     */
 
399
    public int getNumberOfMarkedCells() {
 
400
        return numberOfMarkedCells;
 
401
    }
 
402
 
 
403
    /**
 
404
     * This method calculates the vertical data, is used by the solver
 
405
     */
 
406
    private void calculateColumns(){
 
407
        if (board == null){
 
408
            regenerate();
 
409
        }
 
410
        
 
411
        columns = new long[numberOfRows];
 
412
        int i, j;
 
413
        
 
414
        for (i = 0; i < numberOfColumns; i++){
 
415
            for (j = 0; j < numberOfRows; j++){
 
416
                columns[i] <<= 1;
 
417
                if (board[j][i]){
 
418
                    columns[i] |= 1;
 
419
                }
 
420
            }
 
421
        }
 
422
    }
 
423
 
 
424
    /**
 
425
     * Loads the data from the binary board, this method is called
 
426
     * when you save a newly created pattern
 
427
     */
 
428
    private void loadData(){
 
429
        numberOfMarkedCells = 0;
 
430
        data = new long[numberOfRows];
 
431
        int i, j;
 
432
        
 
433
        for (i = 0; i < numberOfRows; i++){
 
434
            for (j = 0; j < numberOfColumns; j++){
 
435
                data[i] <<= 1;
 
436
                if(board[i][j]){
 
437
                    data[i] |= 1;
 
438
                    numberOfMarkedCells++;
 
439
                }
 
440
            }
 
441
        }
 
442
    }
 
443
 
 
444
    @Override 
 
445
    public boolean equals(Object obj) {
 
446
        if (obj == null || getClass() != obj.getClass()) {
 
447
            return false;
 
448
        }
 
449
        
 
450
        for (int i = 0; i < numberOfRows; i++){
 
451
            if (data[i] != ((Picto)obj).getRow(i)){
 
452
                return false;
 
453
            }
 
454
        }
 
455
        
 
456
        return true;
 
457
    }
 
458
 
 
459
    @Override 
 
460
    public int hashCode() {
 
461
        return 41 * 7 + Arrays.hashCode(data);
 
462
    }
 
463
 
 
464
    /**
 
465
     * Generates the representation of a board to be saved as a playing board
 
466
     * @return board to save
 
467
     */
 
468
    public String toSavingString(){
 
469
        StringBuilder tmpStr = new StringBuilder(512);
 
470
        
 
471
        tmpStr.append("# Title: ")
 
472
              .append(title)
 
473
              .append("\n# Author: ")
 
474
              .append(author)
 
475
              .append("\n# Date: ")
 
476
              .append(date)
 
477
              .append("\n# X size: ")
 
478
              .append(numberOfColumns)
 
479
              .append("\n# Y size: ")
 
480
              .append(numberOfRows)
 
481
              .append( "\n\n");
 
482
        
 
483
        if (data == null){
 
484
            loadData();
 
485
        }
 
486
        
 
487
        for (int i = 0; i < numberOfRows; i++) {
 
488
            tmpStr.append(Long.toHexString(data[i]))
 
489
                  .append('\n');
 
490
        }
 
491
        
 
492
        return tmpStr.toString();
 
493
    }
 
494
 
 
495
    @Override 
 
496
    public String toString(){
 
497
        StringBuilder str = new StringBuilder(numberOfRows * (numberOfColumns + 1));
 
498
        
 
499
        for (boolean[] row : board){
 
500
            for (boolean marked_cell : row){
 
501
                str.append(marked_cell ? '*' : '_');
 
502
            }
 
503
            str.append('\n');
 
504
        }
 
505
        
 
506
        return str.toString();
 
507
    }
 
508
 
 
509
}