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

3
.gitignore vendored Normal file
View File

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

22
pom.xml Normal file
View File

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

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

Binary file not shown.

Binary file not shown.

View File

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

View File

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

View File

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

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.