001 /**
002 * Jetrix TetriNET Server
003 * Copyright (C) 2005 Emmanuel Bourg
004 *
005 * This program is free software; you can redistribute it and/or
006 * modify it under the terms of the GNU General Public License
007 * as published by the Free Software Foundation; either version 2
008 * of the License, or (at your option) any later version.
009 *
010 * This program is distributed in the hope that it will be useful,
011 * but WITHOUT ANY WARRANTY; without even the implied warranty of
012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
013 * GNU General Public License for more details.
014 *
015 * You should have received a copy of the GNU General Public License
016 * along with this program; if not, write to the Free Software
017 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
018 */
019
020 package net.jetrix.filter;
021
022 import static net.jetrix.config.Block.*;
023
024 import java.io.*;
025 import java.util.logging.*;
026 import java.util.*;
027
028 import net.jetrix.config.*;
029 import net.jetrix.*;
030 import org.apache.commons.lang.*;
031
032 /**
033 * Puzzle generator supporting the puzzle format used by the 2ice server,
034 * aka downstack.com. Each puzzle consists in two files, a <tt>.field</tt> file
035 * containing the description of the field, and a <tt>.settings<tt> file
036 * containing the game settings and the puzzle description. By default the
037 * puzzle files are looked for in the <tt>data/puzzle</tt> sub directory, this
038 * can be overridden by setting the <tt>path</tt> parameter.
039 *
040 * @since 0.3
041 *
042 * @author Emmanuel Bourg
043 * @version $Revision: 836 $, $Date: 2010-04-11 23:25:35 +0200 (dim., 11 avr. 2010) $
044 */
045 public class DownstackPuzzleGenerator implements PuzzleGenerator
046 {
047 private Logger log = Logger.getLogger("net.jetrix");
048
049 private static final String DEFAULT_PATH = "data/puzzle";
050
051 /** The path where the puzzle fields and settings are located. */
052 private String path = DEFAULT_PATH;
053
054 /** The current level played. */
055 private int level;
056
057 /** The order or the specials used in the settings file. */
058 public static final Block[] BLOCKS = { LEFTL, LEFTZ, SQUARE, RIGHTL, RIGHTZ, HALFCROSS, LINE };
059
060 public void init(Configuration config)
061 {
062 path = config.getString("path", DEFAULT_PATH);
063 }
064
065 public Puzzle getNextPuzzle()
066 {
067 try
068 {
069 // load the field
070 File[] levels = getLevels();
071 File file = levels[level % levels.length];
072 level = level + 1;
073
074 Puzzle puzzle = loadPuzzle(new File(path), file.getName().substring(0, file.getName().lastIndexOf(".")));
075 puzzle.setKey(String.valueOf(level));
076
077 return puzzle;
078 }
079 catch (IOException e)
080 {
081 log.log(Level.WARNING, e.getMessage(), e);
082 }
083
084 return null;
085 }
086
087 /**
088 * Load a puzzle from the filesystem.
089 *
090 * @param directory the directory containing the puzzle file
091 * @param name the name of the puzzle
092 */
093 protected Puzzle loadPuzzle(File directory, String name) throws IOException
094 {
095 Puzzle puzzle = new Puzzle();
096
097 // load the field
098 Field field = new Field();
099 field.load(new File(directory, name + ".field").getAbsolutePath());
100 puzzle.setField(field);
101
102 // load the settings
103 readSettings(puzzle, new File(directory, name + ".settings").getAbsolutePath());
104
105 return puzzle;
106 }
107
108 /**
109 * Find all levels in the puzzle directory.
110 */
111 protected File[] getLevels()
112 {
113 File directory = new File(path);
114 File[] files = directory.listFiles(new FilenameFilter()
115 {
116 public boolean accept(File dir, String name)
117 {
118 return name.endsWith(".field");
119 }
120 });
121
122 // sort the list of files
123
124 Arrays.sort(files, new FilenameComparator());
125
126 return files;
127 }
128
129 /**
130 * Parse the specified .settings file and update the puzzle.
131 *
132 * @param puzzle the puzzle to update
133 * @param filename the settings file to parse
134 */
135 private void readSettings(Puzzle puzzle, String filename) throws IOException
136 {
137 Settings settings = new Settings();
138 puzzle.setSettings(settings);
139
140 File file = new File(filename);
141 if (file.exists())
142 {
143 BufferedReader in = null;
144
145 try
146 {
147 in = new BufferedReader(new FileReader(file));
148
149 String line;
150 while ((line = in.readLine()) != null)
151 {
152 if (line.startsWith("NAME"))
153 {
154 // parse the name and remove the leading and trailing quotes
155 String name = line.substring(4).trim();
156 if (name.startsWith("\"") && name.endsWith("\""))
157 {
158 name = name.substring(1, name.length() - 1);
159 }
160
161 puzzle.setName(name);
162 }
163 else if (line.startsWith("COMMENT"))
164 {
165 puzzle.setComment(line.substring(7).trim());
166 }
167 else if (line.startsWith("DESIGNER"))
168 {
169 puzzle.setAuthor(line.substring(8).trim());
170 }
171 else if (line.startsWith("SPECIAL"))
172 {
173 // parse the special occurancies
174 String[] occurancy = StringUtils.split(line.substring(7).trim(), ' ');
175
176 for (Special special : Special.values())
177 {
178 settings.setOccurancy(special, Integer.parseInt(occurancy[special.ordinal()]));
179 }
180 }
181 else if (line.startsWith("BLOCK"))
182 {
183 // parse the block occurancies
184 String[] occurancy = StringUtils.split(line.substring(5).trim(), ' ');
185
186 // careful, it doesn't follow the standard order
187 for (int i = 0; i < BLOCKS.length; i++)
188 {
189 settings.setOccurancy(BLOCKS[i], Integer.parseInt(occurancy[i]));
190 }
191 }
192 else if (line.startsWith("SUDDENDEATHMSG"))
193 {
194 settings.setSuddenDeathMessage(line.substring(14).trim());
195 }
196 else if (line.startsWith("SUDDENDEATH"))
197 {
198 String[] params = StringUtils.split(line.substring(11).trim(), ' ');
199
200 settings.setSuddenDeathTime(Integer.parseInt(params[0]));
201 settings.setSuddenDeathLinesAdded(Integer.parseInt(params[1]));
202 settings.setSuddenDeathDelay(Integer.parseInt(params[2]));
203 }
204 else if (line.startsWith("RULES"))
205 {
206 String[] params = StringUtils.split(line.substring(5).trim(), ' ');
207
208 settings.setStartingLevel(Integer.parseInt(params[0]));
209 settings.setLinesPerLevel(Integer.parseInt(params[1]));
210 settings.setLevelIncrease(Integer.parseInt(params[2]));
211 settings.setLinesPerSpecial(Integer.parseInt(params[3]));
212 settings.setSpecialAdded(Integer.parseInt(params[4]));
213 settings.setSpecialCapacity(Integer.parseInt(params[5]));
214 settings.setClassicRules(!"0".equals(params[6]));
215 settings.setAverageLevels(!"0".equals(params[7]));
216 }
217 }
218 }
219 finally
220 {
221 // close the file
222 if (in != null)
223 {
224 in.close();
225 }
226 }
227 }
228 }
229 }