Skip to content

Instantly share code, notes, and snippets.

@jonasurbano
Last active August 29, 2015 13:56
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jonasurbano/02bdeb5402e97f2f6045 to your computer and use it in GitHub Desktop.
Save jonasurbano/02bdeb5402e97f2f6045 to your computer and use it in GitHub Desktop.
Código para determinar si la respuesta del usuario introduciendo la definición de una palabra es correcta. Explicado en el blog: kcy.me/10eh2
import java.text.Normalizer;
import java.text.Normalizer.Form;
import java.util.ArrayList;
import java.util.List;
public class JuegoAdivinarDefinicion {
private static final String REGEX_SEPARAR_PALABRAS = "\\s\\W*|\\W\\s*";
private static final double FACTOR_UMBRAL = 0.66;
private List<String> definiciones;
public JuegoAdivinarDefinicion() {
definiciones = new ArrayList<String>();
}
public void anadirDefinicion(String definicion) {
definiciones.add(definicion);
}
/**
* Una definición es correcta si presenta más del 66%
* de palabras con una distancia hamming máxima de 1.
*/
protected boolean esCorrecta(String respuesta) {
String[] palabrasRespuesta = removeDiacriticalMarks(respuesta)
.trim().split(REGEX_SEPARAR_PALABRAS);
String[] palabrasDefCandidata;
for (String definicion : definiciones) {
palabrasDefCandidata = removeDiacriticalMarks(definicion)
.trim().split(REGEX_SEPARAR_PALABRAS);
if (respuestaParecida(palabrasDefCandidata, palabrasRespuesta))
return true;
}
return false;
}
/**
* Determina si la respuesta del usuario es parecida a
* la definición candidata
*/
private boolean respuestaParecida(String[] defCandidata,String[] respuesta) {
List<Integer> minimosPorPalabraDef = new ArrayList<Integer>();
List<Integer> distancias;
for (String palabra : defCandidata) {
distancias = new ArrayList<Integer>();
for (String palabraRespuesta : respuesta) {
distancias.add(distanciaHamming(palabra,palabraRespuesta));
}
minimosPorPalabraDef.add(minimo(distancias));
}
return evaluarMinimos(minimosPorPalabraDef);
}
private Integer minimo(List<Integer> distancias) {
if (distancias.isEmpty()) return Integer.valueOf(Integer.MAX_VALUE);
int minimo = distancias.get(0);
for (int i = 1; i < distancias.size(); i++)
if (distancias.get(i) < minimo)
minimo = distancias.get(i);
return Integer.valueOf(minimo);
}
/**
* Evalúa la lista de mínimos para determinar si
* la respuesta del usuario coincide con la definición.
* Criterios:
* - Si la definición candidata es de 1 sóla palabra,
* el usuario tiene que introducirla correctamente.
* - Si la def. candidata es de 2 palabras, el usuario
* puede cometer un fallo de d. Hamming 3 como máximo
* en una de las palabras.
* - Para definiciones de más de 2 palabras, el usuario
* tiene que cometer fallos como máximo de d. Hamming 1
* en más del 66% del número de palabras.
* Ej. si la definición es de 10 palabras, el usuario
* puede cometer fallos como mucho de d. Hamming 1 en
* 7 palabras.
* @param minimos lista de las mínimas distancias
* Cada valor de minimos es el mínimo de la distancia
* entre una palabra de la definición y las palabras de
* la respuesta
*/
private boolean evaluarMinimos(List<Integer> minimos) {
int numPalabrasRespuesta = minimos.size();
if (numPalabrasRespuesta == 0)
return false;
else if (numPalabrasRespuesta == 1) {
return minimos.get(0) == 0;
} else if (numPalabrasRespuesta == 2) {
if (minimos.get(0) != 0 && minimos.get(1) != 0) return false;
int suma = minimos.get(0) + minimos.get(1);
return suma <= 3;
} else {
double umbral = minimos.size() * FACTOR_UMBRAL;
return minimosMenores2(minimos) > umbral;
}
}
/**
* Devuelve el número de mínimos menores
* que 2 en {@code minimos}
*/
private double minimosMenores2(List<Integer> minimos) {
int numMenores2 = 0;
for (Integer minimo : minimos)
if (minimo < 2) numMenores2++;
return numMenores2;
}
public static int distanciaHamming(String palabra1, String palabra2) {
//Si la longitud de las cadenas es diferente, s1 contendrá la mayor
char[] s1, s2;
if (palabra2.length() > palabra1.length()) {
s1 = palabra2.toCharArray();
s2 = palabra1.toCharArray();
} else {
s1 = palabra1.toCharArray();
s2 = palabra2.toCharArray();
}
int result = 0, i = 0;
while (i < s2.length) {
if (s1[i] != s2[i]) {
if (s1.length > s2.length && i > 0 && s1[i-1] != s2[i-1]) {
s1 = eliminarCaracter(s1,i);
continue;
}
result++;
}
i++;
}
result += s1.length - s2.length;
System.out.println("Distancia hamming: " + result);
return result;
}
private static char[] eliminarCaracter(char[] palabra, int posicion) {
char[] nuevaPalabra = new char[palabra.length - 1];
for (int i = 0; i < posicion; i++) nuevaPalabra[i] = palabra[i];
for (int i = posicion; i < nuevaPalabra.length; i++) nuevaPalabra[i] = palabra[i+1];
return nuevaPalabra;
}
private static String removeDiacriticalMarks(String string) {
return Normalizer.normalize(string,Form.NFD)
.replaceAll("\\p{InCombiningDiacriticalMarks}+", "");
}
}
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import org.junit.Test;
public class JuegoAdivinarDefinicionTest {
private JuegoAdivinarDefinicion j;
@Test
public void PalabraLongitud1() {
j = new JuegoAdivinarDefinicion();
j.anadirDefinicion("house");
assertTrue(j.esCorrecta("house"));
assertFalse(j.esCorrecta("housa"));
}
@Test
public void PalabraLongitud2() {
j = new JuegoAdivinarDefinicion();
j.anadirDefinicion("starting point");
assertTrue(j.esCorrecta("starting point"));
assertTrue(j.esCorrecta("start point"));
assertTrue(j.esCorrecta("start point"));
assertTrue(j.esCorrecta("stert point"));
assertFalse(j.esCorrecta("where you start"));
assertFalse(j.esCorrecta("start paint"));
}
@Test
public void Palabraslongitud3() {
j = new JuegoAdivinarDefinicion();
j.anadirDefinicion("very good ending");
assertTrue(j.esCorrecta("very good ending"));
assertTrue(j.esCorrecta("veri good end"));
assertTrue(j.esCorrecta("very good end"));
assertFalse(j.esCorrecta("success"));
assertFalse(j.esCorrecta("beri good end"));
j = new JuegoAdivinarDefinicion();
j.anadirDefinicion("grab my attention");
assertTrue(j.esCorrecta("grab my attention"));
assertTrue(j.esCorrecta("grab the attention"));
assertTrue(j.esCorrecta("grab the atention"));
j = new JuegoAdivinarDefinicion();
j.anadirDefinicion("who after prep");
assertTrue(j.esCorrecta("who after prep"));
assertTrue(j.esCorrecta("who after preposition"));
j = new JuegoAdivinarDefinicion();
j.anadirDefinicion("estrecho, escaso, limitado");
assertTrue(j.esCorrecta("estrecho escaso"));
assertFalse(j.esCorrecta("estrecho"));
j = new JuegoAdivinarDefinicion();
j.anadirDefinicion("estrecho-escaso;limitado");
assertTrue(j.esCorrecta("estrecho escaso"));
assertFalse(j.esCorrecta("estrecho"));
}
@Test
public void VariasDefiniciones() {
j = new JuegoAdivinarDefinicion();
j.anadirDefinicion("captar mi atención");
j.anadirDefinicion("distraerme");
j.anadirDefinicion("grab my attention");
assertTrue(j.esCorrecta("grab attention"));
assertTrue(j.esCorrecta("grab my attention"));
assertTrue(j.esCorrecta("grab the attention"));
assertTrue(j.esCorrecta("grab the atention"));
}
@Test
public void masTests() {
// Gist
j = new JuegoAdivinarDefinicion();
j.anadirDefinicion("the main points (of an argument etc)");
assertTrue(j.esCorrecta("the main points of an argument"));
assertTrue(j.esCorrecta("the main points of a argument"));
assertFalse(j.esCorrecta("da main points of a row"));
// Wildcard
j = new JuegoAdivinarDefinicion();
j.anadirDefinicion("carácter (comodín)");
assertTrue(j.esCorrecta("carácter ( comodín )"));
assertTrue(j.esCorrecta("carácter comodín"));
assertFalse(j.esCorrecta("carácter"));
// Fickle
j = new JuegoAdivinarDefinicion();
j.anadirDefinicion("caprichoso");
assertTrue(j.esCorrecta("caprichoso"));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment