[jetrix-cvs] jetrix/src/java/net/jetrix/filter DownstackPuzzleGenerator.java,NONE,1.1 FilenameCompar
Brought to you by:
smanux
From: Emmanuel B. <sm...@us...> - 2005-05-10 02:14:01
|
Update of /cvsroot/jetrix/jetrix/src/java/net/jetrix/filter In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv31804 Added Files: DownstackPuzzleGenerator.java FilenameComparator.java PuzzleFilter.java PuzzleGenerator.java Log Message: no message --- NEW FILE: FilenameComparator.java --- /** * Jetrix TetriNET Server * Copyright (C) 2005 Emmanuel Bourg * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package net.jetrix.filter; import java.io.*; import java.util.*; /** * An improved lexicographic comparator handling a number in a name as a single * character. Unlike the lexicographic order where "foo1.txt" < "foo10.txt" < "foo2.txt" * here we have "foo1.txt" < "foo2.txt" < "foo10.txt". * * todo: move this code to commons-io or commons-lang * * @since 0.3 * * @author Emmanuel Bourg * @version $Revision: 1.1 $, $Date: 2005/05/10 02:13:41 $ */ public class FilenameComparator implements Comparator<File> { public int compare(File file1, File file2) { String name1 = file1.getAbsolutePath(); String name2 = file2.getAbsolutePath(); int index1 = 0; int index2 = 0; while (true) { String token1 = getToken(name1, index1); String token2 = getToken(name2, index2); if (token1 == null && token2 == null) { // no more tokens for each name, they are equal return 0; } if (token1 == null) { // the first name is shorter, it goes first return -1; } if (token2 == null) { // the second name is shorter, it goes first return 1; } int comp = compareToken(token1, token2); if (comp == 0) { // the tokens are equal, move to the next tokens index1 = index1 + token1.length(); index2 = index2 + token2.length(); } else { return comp; } } } /** * Extract from the string the next token starting at the specified index. * * @param string the string to parse * @param index the beginning of the token */ String getToken(String string, int index) { if (string == null || string.length() == 0 || index == string.length()) { return null; } else { // are we parsing a string or a number ? boolean type = Character.isDigit(string.charAt(index)); // move forward until a different character type is detected int end = index + 1; while (end < string.length() && Character.isDigit(string.charAt(end)) == type) { end++; } return string.substring(index, end); } } /** * Tells if the specified string is a number. */ boolean isNumber(String string) { if (string == null || string.length() == 0) { return false; } else { return Character.isDigit(string.charAt(0)); } } /** * Compare two tokens according to their types (string or number). */ int compareToken(String token1, String token2) { if (isNumber(token1) && isNumber(token2)) { return Integer.parseInt(token1) - Integer.parseInt(token2); } else { return token1.compareTo(token2); } } } --- NEW FILE: PuzzleFilter.java --- /** * Jetrix TetriNET Server * Copyright (C) 2004-2005 Emmanuel Bourg * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package net.jetrix.filter; import java.util.*; import net.jetrix.*; import net.jetrix.config.*; import net.jetrix.messages.*; /** * Set the game field and settings when the game starts. A new puzzle is * displayed every time. The sequence of puzzles is generated by a * {@link PuzzleGenerator}, by default the {@link DownstackPuzzleGenerator} * is used. An alternative generator can be used by specifying the <tt>generator</tt> * parameter in the filter configuration. The filter configuration is passed * to the generator for its initialization. * * @since 0.3 * * @author Emmanuel Bourg * @version $Revision: 1.1 $, $Date: 2005/05/10 02:13:41 $ */ public class PuzzleFilter extends GenericFilter { private static final String DEFAULT_GENERATOR = DownstackPuzzleGenerator.class.getName(); private PuzzleGenerator generator; public void init() { try { generator = (PuzzleGenerator) Class.forName(getConfig().getString("generator", DEFAULT_GENERATOR)).newInstance(); } catch (Exception e) { generator = new DownstackPuzzleGenerator(); } // initialize the generator generator.init(getConfig()); } public void onMessage(NewGameMessage m, List<Message> out) { // get the next puzzle Puzzle puzzle = generator.getNextPuzzle(); // send the puzzle description PlineMessage description = new PlineMessage(); description.setKey("filter.puzzle.announce", puzzle.getKey(), puzzle.getName(), puzzle.getAuthor()); out.add(description); // update the game settings and the channel settings if (puzzle.getSettings() != null) { m.setSettings(puzzle.getSettings()); getChannel().getConfig().setSettings(puzzle.getSettings()); } // forward the new game message out.add(m); // update the field of the first slot FieldMessage fieldMessage = new FieldMessage(); fieldMessage.setSlot(1); fieldMessage.setField(puzzle.getField().getFieldString()); out.add(fieldMessage); } public void onMessage(FieldMessage m, List<Message> out) { // check the height of the new field Field field = getChannel().getField(0); if (field.getHighest() <= 2) { // stop the game getChannel().send(new EndGameMessage()); // send the congratulation message PlineMessage grats = new PlineMessage(); grats.setKey("filter.puzzle.cleared"); getChannel().send(grats); } // forward the field update out.add(m); } } --- NEW FILE: PuzzleGenerator.java --- /** * Jetrix TetriNET Server * Copyright (C) 2005 Emmanuel Bourg * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package net.jetrix.filter; import net.jetrix.config.*; /** * A {@link net.jetrix.config.Puzzle} generator. * * @since 0.3 * * @author Emmanuel Bourg * @version $Revision: 1.1 $, $Date: 2005/05/10 02:13:41 $ */ public interface PuzzleGenerator { /** * Initializes the generator. * * @param config the configuration containing the configuration parameters. */ void init(Configuration config); /** * Generates the next puzzle. */ Puzzle getNextPuzzle(); } --- NEW FILE: DownstackPuzzleGenerator.java --- /** * Jetrix TetriNET Server * Copyright (C) 2005 Emmanuel Bourg * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ package net.jetrix.filter; import static net.jetrix.config.Block.*; import java.io.*; import java.util.logging.*; import java.util.*; import net.jetrix.config.*; import net.jetrix.*; import org.apache.commons.lang.*; /** * Puzzle generator supporting the puzzle format used by the 2ice server, * aka downstack.com. Each puzzle consists in two files, a <tt>.field</tt> file * containing the description of the field, and a <tt>.settings<tt> file * containing the game settings and the puzzle description. By default the * puzzle files are looked for in the <tt>data/puzzle</tt> sub directory, this * can be overridden by setting the <tt>path</tt> parameter. * * @since 0.3 * * @author Emmanuel Bourg * @version $Revision: 1.1 $, $Date: 2005/05/10 02:13:41 $ */ public class DownstackPuzzleGenerator implements PuzzleGenerator { private Logger log = Logger.getLogger("net.jetrix"); private static final String DEFAULT_PATH = "data/puzzle"; /** The path where the puzzle fields and settings are located. */ private String path = DEFAULT_PATH; /** The current level played. */ private int level; /** The order or the specials used in the settings file. */ public static final Block[] BLOCKS = { LEFTL, LEFTZ, SQUARE, RIGHTL, RIGHTZ, HALFCROSS, LINE }; public void init(Configuration config) { path = config.getString("path", DEFAULT_PATH); } public Puzzle getNextPuzzle() { Puzzle puzzle = new Puzzle(); try { // load the field File[] levels = getLevels(); File file = levels[level % levels.length]; level = level + 1; Field field = new Field(); field.load(file.getAbsolutePath()); puzzle.setField(field); puzzle.setKey(String.valueOf(level)); // load the settings String name = file.getAbsolutePath().replace(".field", ".settings"); readSettings(puzzle, name); } catch (IOException e) { log.log(Level.WARNING, e.getMessage(), e); } return puzzle; } /** * Find all levels in the puzzle directory. */ private File[] getLevels() { File directory = new File(path); File[] files = directory.listFiles(new FilenameFilter() { public boolean accept(File dir, String name) { return name.endsWith(".field"); } }); // sort the list of files Arrays.sort(files, new FilenameComparator()); return files; } /** * Parse the specified .settings file and update the puzzle. * * @param puzzle the puzzle to update * @param filename the settings file to parse */ private void readSettings(Puzzle puzzle, String filename) throws IOException { Settings settings = new Settings(); puzzle.setSettings(settings); File file = new File(filename); if (file.exists()) { BufferedReader in = null; try { in = new BufferedReader(new FileReader(file)); String line; while ((line = in.readLine()) != null) { if (line.startsWith("NAME")) { // parse the name and remove the leading and trailing quotes String name = line.substring(4).trim(); if (name.startsWith("\"") && name.endsWith("\"")) { name = name.substring(1, name.length() - 1); } puzzle.setName(name); } else if (line.startsWith("COMMENT")) { puzzle.setComment(line.substring(7).trim()); } else if (line.startsWith("DESIGNER")) { puzzle.setAuthor(line.substring(8).trim()); } else if (line.startsWith("SPECIAL")) { // parse the special occurancies String[] occurancy = StringUtils.split(line.substring(7).trim(), ' '); for (Special special : Special.values()) { settings.setOccurancy(special, Integer.parseInt(occurancy[special.getValue()])); } } else if (line.startsWith("BLOCK")) { // parse the block occurancies String[] occurancy = StringUtils.split(line.substring(5).trim(), ' '); // careful, it doesn't follow the standard order for (int i = 0; i < BLOCKS.length; i++) { settings.setOccurancy(BLOCKS[i], Integer.parseInt(occurancy[i])); } } else if (line.startsWith("SUDDENDEATHMSG")) { settings.setSuddenDeathMessage(line.substring(14).trim()); } else if (line.startsWith("SUDDENDEATH")) { String[] params = StringUtils.split(line.substring(11).trim(), ' '); settings.setSuddenDeathTime(Integer.parseInt(params[0])); settings.setSuddenDeathLinesAdded(Integer.parseInt(params[1])); settings.setSuddenDeathDelay(Integer.parseInt(params[2])); } else if (line.startsWith("RULES")) { String[] params = StringUtils.split(line.substring(5).trim(), ' '); settings.setStartingLevel(Integer.parseInt(params[0])); settings.setLinesPerLevel(Integer.parseInt(params[1])); settings.setLevelIncrease(Integer.parseInt(params[2])); settings.setLinesPerSpecial(Integer.parseInt(params[3])); settings.setSpecialAdded(Integer.parseInt(params[4])); settings.setSpecialCapacity(Integer.parseInt(params[5])); settings.setClassicRules(!"0".equals(params[6])); settings.setAverageLevels(!"0".equals(params[7])); } } } finally { // close the file if (in != null) { in.close(); } } } } } |