Browse Source

Initial commit

Febbweiss 8 years ago
commit
3159a4e46a

+ 3 - 0
.gitignore

@@ -0,0 +1,3 @@
+.classpath
+.project
+.settings

+ 22 - 0
pom.xml

@@ -0,0 +1,22 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>fr.pavnay</groupId>
+	<artifactId>scrabble-resolver</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<dependencies>
+		<!-- Tools -->
+		<dependency>
+			<groupId>commons-cli</groupId>
+			<artifactId>commons-cli</artifactId>
+			<version>1.4</version>
+		</dependency>
+		<!-- Unit testing -->
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.12</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>

+ 116 - 0
src/main/java/fr/pavnay/scrabble/DictionaryBuilder.java

@@ -0,0 +1,116 @@
+package fr.pavnay.scrabble;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.ObjectInputStream;
+import java.io.ObjectOutputStream;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+public class DictionaryBuilder {
+	private static Map<String, Resolver> resolvers = new HashMap<String, Resolver>();
+
+	public static Resolver generateResolver(Resolver resolver, File dictionary, int minimalSize, int maximalSize)
+			throws IOException {
+		BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(dictionary), "UTF-8"));
+		String line = null;
+
+		int total = 0;
+		int valid = 0;
+		while ((line = br.readLine()) != null) {
+			int size = line.length();
+			total++;
+			if ((size >= minimalSize) && (size <= maximalSize) && (!line.contains("-")) && (!line.contains("."))
+					&& (!line.contains("'"))) {
+				String cleaned = StringUtils.toLowerCaseASCII(line);
+				if (cleaned != null) {
+					valid++;
+
+					char[] sortedLetters = StringUtils.sortLetters(cleaned);
+
+					char currentLetter = sortedLetters[0];
+
+					Node currentNode = resolver.getNode(currentLetter);
+					for (int i = 1; i < size; i++) {
+						currentLetter = sortedLetters[i];
+						resolver.updateStatistics(currentLetter);
+						if (i == size - 1) {
+							currentNode.getNode(currentLetter).addWord(cleaned);
+						} else {
+							currentNode = currentNode.getNode(currentLetter);
+						}
+					}
+				}
+			}
+		}
+		br.close();
+
+		System.out.println("Total words in file : " + total + " - Valid words : " + valid);
+		return resolver;
+	}
+
+	public static Enigma generateEnigma(String language, int minLength, int maxLength) {
+		Resolver resolver;
+		try {
+			resolver = loadResolver(language);
+		} catch (Exception e) {
+			e.printStackTrace();
+			return null;
+		}
+		return resolver.generateEnigma(minLength, maxLength);
+	}
+
+	public static void generateResolver(String language) throws IOException {
+		File file = new File(
+				"C:/Users/viaduc105.smallbusiness/Documents/workspace-sts/SocialArena/resources/" + language);
+		if (!file.exists()) {
+			System.out.println("No dictionary for " + language);
+			return;
+		}
+		Resolver resolver = generateResolver(new Resolver(), file, 3, 7);
+		resolver.computeStatistics();
+
+		ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(
+				new File("C:/Users/viaduc105.smallbusiness/Documents/workspace-sts/SocialArena/resources/resolver/"
+						+ language)));
+		oos.writeObject(resolver);
+		oos.flush();
+		oos.close();
+	}
+
+	public static void loadResolvers() throws IOException, ClassNotFoundException {
+		List<String> languages = ScrabbleUtils.loadLanguages();
+		for (String language : languages) {
+			long t1 = System.currentTimeMillis();
+			loadResolver(language);
+			System.out.println("Resolver in " + language + " loaded in " + (System.currentTimeMillis() - t1) + "ms.");
+		}
+	}
+
+	private static Resolver loadResolver(String language)
+			throws FileNotFoundException, IOException, ClassNotFoundException {
+		Resolver resolver = (Resolver) resolvers.get(language);
+		if (resolver == null) {
+			long t1 = System.currentTimeMillis();
+			String path = DictionaryBuilder.class.getResource("/resolvers/" + language).getPath();
+			File file = new File(path);
+			if (!file.exists()) {
+				System.out.println("No resolver for " + language);
+				return null;
+			}
+			ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
+			resolver = (Resolver) ois.readObject();
+			ois.close();
+
+			resolvers.put(language, resolver);
+			System.out.println("Resolver in " + language + " loaded in " + (System.currentTimeMillis() - t1) + "ms.");
+		}
+		return resolver;
+	}
+}

+ 61 - 0
src/main/java/fr/pavnay/scrabble/Enigma.java

@@ -0,0 +1,61 @@
+package fr.pavnay.scrabble;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+
+public class Enigma implements Serializable {
+	private static final long serialVersionUID = 7542854436817730656L;
+	private int min;
+	private int max;
+	private char[] letters;
+	private Map<Integer, List<String>> words;
+
+	public Enigma(char[] letters, Map<Integer, List<String>> words, int min, int max) {
+		this.letters = letters;
+		this.words = words;
+		this.min = min;
+		this.max = max;
+	}
+
+	public List<String> getLetters() {
+		List<String> lLetters = new ArrayList<String>();
+		if (this.letters == null) {
+			return lLetters;
+		}
+		for (int i = 0; i < this.letters.length; i++) {
+			lLetters.add(Character.toString(this.letters[i]));
+		}
+		return lLetters;
+	}
+
+	public Map<Integer, List<String>> getWords() {
+		return this.words;
+	}
+
+	public boolean isValid(String word) {
+		if (StringUtils.isBlank(word)) {
+			return false;
+		}
+		List<String> lWords = this.words.get(Integer.valueOf(word.length()));
+		return lWords.contains(word);
+	}
+
+	public String toString() {
+		StringBuilder sb = new StringBuilder("Letters : ");
+		if (this.letters != null) {
+			sb.append(new String(this.letters));
+		} else {
+			sb.append("-");
+		}
+		sb.append("\nWords between ").append(this.min).append(" and ").append(this.max).append(" letters : \n");
+		for (int i = this.max; i > this.min - 1; i--) {
+			List<String> lWords = this.words.get(Integer.valueOf(i));
+			if (lWords != null) {
+				sb.append("\twith ").append(i).append(" letters (" + lWords.size() + "): ").append(lWords).append('\n');
+			}
+		}
+		return sb.toString();
+	}
+}

+ 167 - 0
src/main/java/fr/pavnay/scrabble/Main.java

@@ -0,0 +1,167 @@
+package fr.pavnay.scrabble;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.ObjectOutputStream;
+import java.util.List;
+
+import org.apache.commons.cli.CommandLine;
+import org.apache.commons.cli.CommandLineParser;
+import org.apache.commons.cli.DefaultParser;
+import org.apache.commons.cli.HelpFormatter;
+import org.apache.commons.cli.Option;
+import org.apache.commons.cli.Options;
+import org.apache.commons.cli.ParseException;
+
+public class Main {
+
+	private final static List<String> availableLanguages = ScrabbleUtils.loadLanguages();
+	private final static Options helpOptions = configHelpParameter();
+	private final static Options options = configParameters();
+	
+	public static void main(String[] args) {
+		
+		final CommandLineParser parser = new DefaultParser();
+		CommandLine firstLine = null;
+		try {
+			firstLine = parser.parse(helpOptions, args, true);
+		} catch (ParseException e1) {
+			e1.printStackTrace();
+			System.exit(1);
+		}
+		
+		boolean helpMode = firstLine.hasOption("help");
+		if (helpMode) {
+		    final HelpFormatter formatter = new HelpFormatter();
+		    formatter.printHelp("Main", options, true);
+		    System.exit(0);
+		}
+		
+		try {
+			CommandLine line = parser.parse(options, args);
+			if( line.hasOption( "build" ) ) {
+				generate(line);
+			} else {
+				getEnigma(line);
+			}
+		} catch (ParseException e) {
+			System.err.println(e.getMessage());
+			System.exit(1);
+		}
+
+	}
+	
+	private static void generate(CommandLine line) {
+		final String source = line.getOptionValue("build", "");
+		final String language = line.getOptionValue("lang", "");
+		final int min = Integer.parseInt(line.getOptionValue("min", "3"));
+		final int max = Integer.parseInt(line.getOptionValue("max", "7"));
+
+		System.out.println(String.format("Generating %s resolver from %s.\nWords size: [%d, %d]", language, source, min, max ));
+		
+		File file = new File(source);
+	    if (!file.exists())
+	    {
+	      System.err.println("Source file not found " + source);
+	      return;
+	    }
+	    
+	    try {
+		    Resolver resolver = DictionaryBuilder.generateResolver(new Resolver(), file, min, max);
+		    resolver.computeStatistics();
+		    
+		    ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream(new File("src/main/resources/resolvers/" + language)));
+		    oos.writeObject(resolver);
+		    oos.flush();
+		    oos.close();
+	    } catch( IOException e) {
+	    	System.err.println(e.getMessage());
+	    }
+	}
+	
+	private static void getEnigma(CommandLine line) {
+		final String language = line.getOptionValue("lang", "");
+		final int min = Integer.parseInt(line.getOptionValue("min", "3"));
+		final int max = Integer.parseInt(line.getOptionValue("max", "7"));
+		
+		try {
+			manageLanguage(language);
+			Enigma enigma = DictionaryBuilder.generateEnigma(language, min, max);
+			System.out.println(enigma);
+		} catch(IllegalArgumentException e) {
+			System.err.println(e.getMessage());
+		}
+	}
+	
+	private static Options configHelpParameter() {
+		 final Option helpFileOption = Option.builder("h") 
+				.longOpt("help")
+				.desc("Display help message").build();
+
+	    final Options firstOptions = new Options();
+
+	    firstOptions.addOption(helpFileOption);
+
+	    return firstOptions;
+	}
+	
+	private static Options configParameters() {
+		
+		final Option buildOption = Option.builder("b")
+				.longOpt("build") //
+				.desc("Build a new dictionary from the source file (a Linux words file)")
+				.hasArg(true)
+				.argName("source")
+				.required(false)
+				.build();
+
+		final Option languageOption = Option.builder("l")
+				.longOpt("lang")
+				.desc("Language (in " + availableLanguages + ")")
+				.hasArg(true)
+				.argName("language")
+				.required(true)
+				.build();
+		
+		final Option helpFileOption = Option.builder("h") 
+				.longOpt("help")
+				.desc("Display help message").build();
+
+		final Option minOption = Option.builder("min")
+				.longOpt("min")
+				.desc("Minimum word length (default : 3)")
+				.hasArg(true)
+				.argName("min")
+				.required(false)
+				.build();
+		
+		final Option maxOption = Option.builder("max")
+				.longOpt("max")
+				.desc("Maximum word length (default : 7)")
+				.hasArg(true)
+				.argName("max")
+				.required(false)
+				.build();
+		
+		final Options options = new Options();
+
+		options.addOption(buildOption);
+		options.addOption(helpFileOption);
+		options.addOption(languageOption);
+		options.addOption(minOption);
+		options.addOption(maxOption);
+
+		return options;
+	}
+	
+	private static void manageLanguage(String language) {
+		if( StringUtils.isBlank(language) ) {
+			throw new IllegalArgumentException("No language provided");
+		}
+		
+		if( !availableLanguages.contains(language.toLowerCase()) ) {
+			throw new IllegalArgumentException(String.format("Unknown %s language. Available languages : %s", language, availableLanguages.toString()));
+		}
+	}
+}

+ 73 - 0
src/main/java/fr/pavnay/scrabble/Node.java

@@ -0,0 +1,73 @@
+package fr.pavnay.scrabble;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class Node implements Serializable {
+	private static final long serialVersionUID = 3539215265338287172L;
+	private Node[] nodes = new Node[26];
+	private List<String> words;
+	private String parent;
+
+	public Node(String parent) {
+		this.parent = parent;
+	}
+
+	public void addWord(String word) {
+		if (this.words == null) {
+			this.words = new ArrayList<String>();
+		}
+		if (!this.words.contains(word)) {
+			this.words.add(word);
+		}
+	}
+
+	public List<String> getWords() {
+		if (this.words == null) {
+			return null;
+		}
+		Collections.sort(this.words);
+		return this.words;
+	}
+
+	public Node getNode(char character) {
+		Node node = this.nodes[(character - 'a')];
+		if (node == null) {
+			node = new Node(this.parent + character);
+			this.nodes[(character - 'a')] = node;
+		}
+		return node;
+	}
+
+	public int getWordsCount() {
+		int total = 0;
+		for (int i = 0; i < 26; i++) {
+			Node current = this.nodes[i];
+			if (current != null) {
+				total += current.getWordsCount();
+			}
+			if (this.words != null) {
+				total += this.words.size();
+			}
+		}
+		return total;
+	}
+
+	public String toString() {
+		StringBuilder sb = new StringBuilder("Parent " + this.parent);
+		if (this.words != null) {
+			sb.append(this.words.toString());
+		} else {
+			sb.append('-');
+		}
+		for (int i = 0; i < 26; i++) {
+			Node current = this.nodes[i];
+			if (current != null) {
+				sb.append(current);
+			}
+		}
+		return sb.toString();
+	}
+}

+ 185 - 0
src/main/java/fr/pavnay/scrabble/Resolver.java

@@ -0,0 +1,185 @@
+package fr.pavnay.scrabble;
+
+import java.io.Serializable;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Random;
+
+public class Resolver implements Serializable {
+	private static final long serialVersionUID = 8267126995323709570L;
+	private Node[] nodes = new Node[26];
+	private float[] statistics = new float[26];
+	private float totalCharacter = 0.0F;
+
+	public Node getNode(char character) {
+		Node node = this.nodes[(character - 'a')];
+		this.statistics[(character - 'a')] += 1.0F;
+		this.totalCharacter += 1.0F;
+		if (node == null) {
+			node = new Node(Character.toString(character));
+			this.nodes[(character - 'a')] = node;
+		}
+		return node;
+	}
+
+	public void updateStatistics(char character) {
+		this.statistics[(character - 'a')] += 1.0F;
+		this.totalCharacter += 1.0F;
+	}
+
+	public void computeStatistics() {
+		for (int i = 0; i < 26; i++) {
+			this.statistics[i] = (this.statistics[i] * 100.0F / this.totalCharacter);
+			System.out.println((char) (i + 97) + "=" + this.statistics[i] + "%");
+		}
+	}
+
+	public void displayStatistics() {
+		for (int i = 0; i < 26; i++) {
+			System.out.println((char) (i + 97) + "=" + this.statistics[i] + "%");
+		}
+	}
+
+	public float[] getStatistics() {
+		return this.statistics;
+	}
+
+	public Enigma generateEnigma(int minimalLettersCount, int maximalLettersCount) {
+		boolean mustRun = false;
+		char[] letters;
+		Map<Integer, List<String>> allWords;
+		do {
+			letters = getRandomSet(maximalLettersCount);
+			allWords = getWords(letters, minimalLettersCount);
+
+			int wordsCount = 0;
+			for (int i = minimalLettersCount; i < maximalLettersCount + 1; i++) {
+				List<String> words = allWords.get(Integer.valueOf(i));
+				if (words != null) {
+					wordsCount += words.size();
+				}
+			}
+			mustRun = wordsCount < (maximalLettersCount + minimalLettersCount) * 0.7D;
+		} while (mustRun);
+		return new Enigma(letters, allWords, minimalLettersCount, maximalLettersCount);
+	}
+
+	private char[] getRandomSet(int maxLetter) {
+		float[] statistics = getStatistics();
+		float max = 0.0F;
+		for (int i = 0; i < 26; i++) {
+			max += statistics[i];
+		}
+		Random randomizer = new Random();
+		char[] letters = new char[maxLetter];
+		for (int i = 0; i < maxLetter; i++) {
+			letters[i] = getRandomLetter(randomizer, max, statistics);
+		}
+		return letters;
+	}
+
+	private char getRandomLetter(Random randomizer, float max, float[] statistics) {
+		float r = randomizer.nextFloat() * (int) max;
+		max = 0.0F;
+		for (int i = 0; i < 26; i++) {
+			max += statistics[i];
+			if (r < max) {
+				return (char) (i + 97);
+			}
+		}
+		return 'z';
+	}
+
+	private Map<Integer, List<String>> getWords(char[] letters, int minimum) {
+		List<String> words = new ArrayList<String>();
+		for (int i = letters.length; i > 2; i--) {
+			List<String> newWords = getWords(letters, minimum, i);
+			if (newWords != null) {
+				for (String word : newWords) {
+					if (!words.contains(word)) {
+						words.add(word);
+					}
+				}
+			}
+		}
+		Map<Integer, List<String>> lists = new HashMap<Integer, List<String>>();
+		for (String word : words) {
+			List<String> list = lists.get(Integer.valueOf(word.length()));
+			if (list == null) {
+				list = new ArrayList<String>();
+				lists.put(Integer.valueOf(word.length()), list);
+			}
+			list.add(word);
+		}
+		for (int i = letters.length; i > 2; i--) {
+			List<String> list = lists.get(Integer.valueOf(i));
+			if (list != null) {
+				Collections.sort(list);
+			}
+		}
+		return lists;
+	}
+
+	public List<String> getWords(char[] letters, int minimum, int size) {
+		if (size < minimum) {
+			return null;
+		}
+		String set = new String(letters);
+		char[] sortLetters = StringUtils.sortLetters(set);
+
+		int setSize = letters.length;
+		if (size == setSize) {
+			Node currentNode = this.nodes[(sortLetters[0] - 'a')];
+			if (currentNode == null) {
+				return null;
+			}
+			for (int i = 1; i < setSize; i++) {
+				currentNode = currentNode.getNode(sortLetters[i]);
+				if (currentNode == null) {
+					return null;
+				}
+				if (i == setSize - 1) {
+					return currentNode.getWords();
+				}
+			}
+			return null;
+		}
+		String nString = new String(sortLetters);
+		List<String> results = null;
+		for (int i = 0; i < setSize - size; i++) {
+			for (int j = 0; j < setSize; j++) {
+				List<String> words = getWords(
+						(nString.substring(0, j) + nString.subSequence(j + 1, setSize)).toCharArray(), minimum,
+						size - i);
+				if (words != null) {
+					if (results == null) {
+						results = new ArrayList<String>();
+					}
+					for (String word : words) {
+						if (!results.contains(word)) {
+							results.add(word);
+						}
+					}
+				}
+			}
+		}
+		if (results != null) {
+			Collections.sort(results);
+		}
+		return results;
+	}
+
+	public int countWords() {
+		int total = 0;
+		for (int i = 0; i < 26; i++) {
+			Node current = this.nodes[i];
+			if (current != null) {
+				total += current.getWordsCount();
+			}
+		}
+		return total;
+	}
+}

+ 17 - 0
src/main/java/fr/pavnay/scrabble/ScrabbleUtils.java

@@ -0,0 +1,17 @@
+package fr.pavnay.scrabble;
+
+import java.io.File;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.List;
+
+public class ScrabbleUtils {
+	
+	public static List<String> loadLanguages() {
+		URL url = Main.class.getClassLoader().getResource("resolvers");
+		File resolver = new File(url.getPath());
+		String[] files = resolver.list();
+		return Arrays.asList(files);
+	}
+	
+}

+ 37 - 0
src/main/java/fr/pavnay/scrabble/StringUtils.java

@@ -0,0 +1,37 @@
+package fr.pavnay.scrabble;
+
+import java.text.Normalizer;
+import java.util.Arrays;
+
+public final class StringUtils {
+	
+	public static String toLowerCaseASCII(String s) {
+		if (isBlank(s)) {
+			return "";
+		}
+		return stripAccents(s).trim().toLowerCase();
+	}
+
+	public static boolean isBlank(String s) {
+		return (s == null) || (s.trim().length() == 0);
+	}
+
+	public static char[] sortLetters(String s) {
+		if (s == null) {
+			return null;
+		}
+		if (isBlank(s)) {
+			return s.toCharArray();
+		}
+		char[] sortedLetters = s.toCharArray();
+		Arrays.sort(sortedLetters);
+
+		return sortedLetters;
+	}
+
+	public static String stripAccents(String s) {
+	    s = Normalizer.normalize(s, Normalizer.Form.NFD);
+	    s = s.replaceAll("[\\p{InCombiningDiacriticalMarks}]", "").replaceAll("[^\\p{ASCII}]", "");
+	    return s;
+	}
+}

BIN
src/main/resources/resolvers/english


BIN
src/main/resources/resolvers/french


+ 5 - 0
target/classes/META-INF/MANIFEST.MF

@@ -0,0 +1,5 @@
+Manifest-Version: 1.0
+Built-By: FECAILLE
+Build-Jdk: 1.8.0_77
+Created-By: Maven Integration for Eclipse
+

+ 7 - 0
target/classes/META-INF/maven/fr.pavnay/scrabble-resolver/pom.properties

@@ -0,0 +1,7 @@
+#Generated by Maven Integration for Eclipse
+#Fri Sep 22 09:55:44 CEST 2017
+version=0.0.1-SNAPSHOT
+groupId=fr.pavnay
+m2e.projectName=scrabble-resolver
+m2e.projectLocation=D\:\\Workspace_JADE\\scrabble-resolver
+artifactId=scrabble-resolver

+ 22 - 0
target/classes/META-INF/maven/fr.pavnay/scrabble-resolver/pom.xml

@@ -0,0 +1,22 @@
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
+	<modelVersion>4.0.0</modelVersion>
+	<groupId>fr.pavnay</groupId>
+	<artifactId>scrabble-resolver</artifactId>
+	<version>0.0.1-SNAPSHOT</version>
+	<dependencies>
+		<!-- Tools -->
+		<dependency>
+			<groupId>commons-cli</groupId>
+			<artifactId>commons-cli</artifactId>
+			<version>1.4</version>
+		</dependency>
+		<!-- Unit testing -->
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.12</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>

BIN
target/classes/fr/pavnay/scrabble/DictionaryBuilder.class


BIN
target/classes/fr/pavnay/scrabble/Enigma.class


BIN
target/classes/fr/pavnay/scrabble/Main.class


BIN
target/classes/fr/pavnay/scrabble/Node.class


BIN
target/classes/fr/pavnay/scrabble/Resolver.class


BIN
target/classes/fr/pavnay/scrabble/ScrabbleUtils.class


BIN
target/classes/fr/pavnay/scrabble/StringUtils.class


BIN
target/classes/resolvers/english


BIN
target/classes/resolvers/french