Initial commit

This commit is contained in:
2017-09-22 09:57:04 +02:00
commit 3159a4e46a
23 changed files with 715 additions and 0 deletions

View File

@@ -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;
}
}

View File

@@ -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();
}
}

View File

@@ -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()));
}
}
}

View File

@@ -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();
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View File

@@ -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;
}
}