¿Vamos a jugar al ahorcado?


8

Según esta página , la mejor estrategia para adivinar las palabras del verdugo en inglés es calcular las probabilidades de cada letra en una lista de palabras que cumplan con nuestras condiciones. Pero, como soy realmente vago, no quiero calcular cada palabra en el diccionario por mí mismo. Pero, como sé que siempre estás aquí para ayudarme, estoy seguro de que podrás hacerme un rey de un código que lo haga por mí. Y, debido a que mi disco duro está casi lleno, me gustaría tener el código más pequeño posible. Significa que este es el código de golf, y la presentación con la menor cantidad de bytes ganará, ¡pero también la más precisa! .

De entrada y salida

Se tomará una palabra aleatoria de esta lista de palabras .

Su programa debe aceptar, en argumentos o por entrada del usuario (ventanas emergentes, stdin, lo que sea),

  • Longitud de la palabra
  • Letra incorrecta ya encontrada, o 0 si acabamos de comenzar el juego, y no proporcionaste ninguna letra incorrecta.
  • Cartas ya encontradas Y su posición en la palabra

Ejemplo: ./hangsolver 6 XBZ 1P 4P 2E 6EAquí, elegí la palabra "personas". Para mayor claridad: PE _ P _ E (las letras incorrectas son XB y Z)

¡Eso significa que, en un juego, tendré que lanzar tu script muchas veces!

La salida será una sola letra, su próximo intento.

Reglas

  • El que adivine 10 palabras en menos intentos que los demás ganará.
  • En caso de empate, gana el código más corto en bytes.
  • Si todavía hay un empate, el programa más rápido ganará.
  • Puede suponer que solo hay estas palabras en inglés.
  • Solo intentaré palabras válidas de la lista de palabras.
  • Tengo una buena computadora, la potencia de la CPU no será un problema (¡pero trata de responder lo más rápido que puedas!)
  • No puede resolver con un solucionador en línea, pero puede descargar la lista de palabras o pasarla como argumento. Puede suponer que se llamará "wordlist.txt" y en el mismo directorio que su script.
  • Su código debe poder ejecutarse en un sistema operativo común. Podría ser Windows, Mac o Ubuntu / Debian / CentOS o Redhat.
  • No puede usar un solucionador externo.
  • Sin embargo, puede acortar la URL a la lista de palabras.
  • Este código de golf finalizará el primero de septiembre.
  • DEBE utilizar el método descrito anteriormente.

Buena suerte !

Lista de palabras encontrada aquí en SE.


2
¿Ganará el código más corto en bytes, y la mejor estimación será el desempate? Eso significa que mi programa que simplemente adivina cualquier letra aleatoria que no se haya utilizado antes vencerá a alguien que realmente intente resolver una buena suposición. Es posible que desee repensar su puntuación, o obtendrá respuestas triviales.
Level River St

"DEBE utilizar el método descrito anteriormente", cito las reglas. Pero lo
editaré

1
Para ser claros, "usar el método descrito anteriormente" significa ¿adivinar la letra que aparece en la mayor cantidad de palabras posibles que aún no se ha adivinado?
isaacg

Sí exactamente. Gracias por la edición de error!
WayToDoor

1
¿No deberían ser los datos de entrada en su ejemplo "6 XBZ 1P 4P 2E 6E"?
Razvan

Respuestas:


5

PowerShell, 248 246 241 bytes

$a=$args;$c=$a[0];$b=$a[2..$c];((gc wordlist.txt)-match"^$((1..$c|%{$e=,"[^$($a[1])]"*$c}{$e[$_-1]=(((@($b)-match"^$_\D")[0]-split$_)[-1],$e[$_-1]-ne'')[0]}{$e-join''}))$"-join''-split'\B'|?{!(@($b)-match$_).Count}|group|sort count)[-1].Name

Sin golf

Bueno, tanto como pude sin cambiar la forma en que funciona:

$a=$args
$c=$a[0]
$b=$a[2..$c]
(
    (gc wordlist.txt) -match "^$(
        (
            1..$c | ForEach-Object -Begin {
                $e = ,"[^$($a[1])]" * $c
            } -Process {
                $e[$_-1] = (
                    ( ( @($b) -match "^$_\D" )[0] -split $_ )[-1] , $e[$_-1] -ne ''
                )[0]
            } -End {
                $e-join''
            }
        )
    )$" -join '' -split'\B' |
    Where-Object {
        -not (@($b) -match $_).Count
    } | 
    Group-Object |
    Sort-Object count
)[-1].Name

Descompostura

El enfoque que tomé aquí fue generar primero una expresión regular para sacar posibles palabras de la lista de palabras. Como sé la longitud de la palabra y las letras que no funcionaron, puedo hacer una expresión regular de eso con bastante facilidad.

Entonces, en el ejemplo de PEOPLE, 6 letras con XBZ como parte de la palabra, buscaría generar ^PE[^XBZ]P[^XBZ]E$.

Estoy explotando el hecho de que Get-Content(gc ) devuelve una matriz de líneas, y el -matchoperador cuando se usa con una matriz en el lado izquierdo, devuelve una matriz de coincidencias en lugar de un bool, por lo que puedo obtener rápidamente una lista de solo palabras que son candidatos, una vez que tenga la expresión regular.

Para generar la expresión regular, comienzo con una matriz ( $e) de la clase de caracteres negativos coincidentes con $celementos (que $ces el número de letras en la palabra). Iterando los números del 1 al 1 $c, verifico si hay una letra coincidente en esa posición y, si existe, reemplazo el elemento en$e con esa letra.

Una vez que he iterado a través de todas las posiciones, la matriz final es -join edita (con una cadena vacía) y tenemos nuestra expresión regular.

Así que ahora tengo una serie de todas las palabras posibles que podría ser. Un rápido -joincon una cadena vacía en eso, me da una gran cadena concatenada de todas las palabras, la dividí en\B (no un límite de palabra, si me divido en una cadena vacía obtendré 2 elementos en blanco adicionales), así que ahora tengo una matriz de cada letra en cada palabra posible.

Tipear eso en Where-Objectme permite filtrar las letras que ya se han emparejado. Esta parte fue un verdadero dolor. Tuvo que lidiar con la lista de letras coincidentes (que incluyen la posición) que es 1 elemento, más de 1 elemento o 0 elementos, forzando $bprimero a una matriz para que -matchpueda operar en todas ellas, pero eso (desafortunadamente en este caso ) devuelve una matriz, por lo que tenemos que verificar .Count. Usar !(thing).Countes un poco más pequeño que usar(thing).Count-gt0 .

Continuando, ahora tenemos una matriz de todos los caracteres individuales (como strings no como chars) de todas las palabras que podría ser, menos las letras que ya se adivinaron correctamente.

Conectar eso en Group-Objectme da un objeto con los recuentos de cada letra, por lo que un tubo rápido en Sort-Object counthace que sea fácil obtener el recuento más alto. En lugar de lo (thing|sort count -des)[0]que podemos usar (thing|sort count)[-1]. En PowerShell [-1]obtiene el último elemento. En este punto, todavía estamos tratando con los objetos que provienen, por Group-Objectlo que obtenemos la .Namepropiedad que es la letra que más aparece.

Notas

  • Debería funcionar con PowerShell v3 +; casi seguramente se ahogará con 2.
  • Recuerde que cuando llama a un script de PowerShell, pase argumentos con espacios, no comas.
  • Aunque no lo vi en las reglas, parece que todos están usando el nombre de archivo, de lo wordlist.txtcontrario, eso podría reducir algunos bytes.
  • La velocidad no debería ser un problema. Esto parece ejecutarse instantáneamente para mí. La ejecución más lenta que pude hacer ( .\hangman.ps1 7 0) se ejecuta en unos 350 ms.

1
Bienvenido a Programming Puzzles & Code Golf Stack Exchange, ¡excelente primera respuesta! :)
Pomo de la puerta

@Doorknob muchas gracias!
briantist

6

Python3, 299 bytes

import sys,collections as c;x,X,N,*Z=sys.argv;print([x for x in c.Counter(''.join([''.join(x)for x in map(set,filter(lambda l:len(l)==int(X)+1and all(x not in X.lower()for x in l)and all(l[int(x[0])-1]==x[1].lower()for x in Z),open('wordlist.txt')))]))if x not in ''.join(Z).lower()and x!='\n'][0])

Estoy bastante seguro de que esto se puede jugar más.

Filtra la lista de palabras para posibles coincidencias, crea un mapa de frecuencia de caracteres y selecciona el carácter más frecuente que aún no se ha seleccionado.


Tienes mucho ''.join(..)s. Si todos los elementos en el interior son cadenas de longitud 1, puede cambiarlo a '..'[2::5], donde los apóstrofos son puntos de retroceso.
Kade

3

Java, 646 640 631 607 606 (corto) 790 789 779 (rápido) bytes

CORTO

import java.util.*;class I{public static void main(String[]a)throws Exception{char[]w=a[1].toCharArray(),p,q;int l=Integer.parseInt(a[0]),i,z=w.length,j;q=new char[l];for(i=2;i<a.length;i++)q[Character.getNumericValue(a[i].charAt(0))-1]=(char)(a[i].charAt(1)+32);java.io.File u=new java.io.File("wordlist.txt");Scanner s=new Scanner(u);while(s.hasNextLine()){p=s.nextLine().toCharArray();if(p.length==l)for(i=0;i<l;i++)if(p[i]==q[i]||q[i]=='\0'){if(i==l-1)y:for(i=0;i<l;i++)for(j=0;j<z;j++)if(!(p[i]==w[j])){if(j==z-1){System.out.print(p[new String(q).indexOf('\0')]);return;}}else break y;}else{break;}}}}

RÁPIDO

import java.util.*;class I{public static void main(String[]a)throws Exception{char[]w=a[1].toCharArray(),p,q;int l=Integer.parseInt(a[0]),i,z=w.length,j,k,o,n[]=new int[255],r[];q=new char[l];for(i=2;i<a.length;i++)q[Character.getNumericValue(a[i].charAt(0))-1]=(char)(a[i].charAt(1)+32);String m=new String(q);java.io.File u=new java.io.File("wordlist.txt");Scanner s=new Scanner(u);while(s.hasNextLine()){p=s.nextLine().toCharArray();h:if(p.length==l)for(i=0;i<l;i++)if(p[i]==q[i]||q[i]=='\0'){if(i==l-1)y:for(i=0;i<l;i++)for(j=0;j<z;j++)if(p[i]!=w[j]){if(j==z-1){for(k=0;k<l-m.replace("\0","").length();k++)n[(int)p[new String(q).indexOf('\0',k)]]++;break h;}}else break y;}else{break;}}r=n.clone();Arrays.sort(n);for(o=0;o<255;o++)System.out.print(r[o]==n[254]?(char)o:"");}}

Ponga el archivo de la lista de palabras en la carpeta.

Algoritmo de versión corta

  1. Args de carga
  2. Construya la palabra que estamos tratando de adivinar {'p', 'e', ​​'\ 0', 'p', '\ 0', 'e'}
  3. Cargar WordList
  4. Ir a través de cada línea de WordList
  5. Pare cuando encuentre que la palabra completa coincide con esta condición p[i] == q[i] || q[i] == '\0' donde p es una palabra de la lista de palabras (matriz de caracteres), y q es la palabra que estamos tratando de adivinar
  6. Recorre los caracteres incorrectos y compáralo con la palabra
  7. Imprime el primer personaje perdido

Algoritmo de versión larga

  1. Pasos cortos 1-7
  2. Incremente el recuento de caracteres en la matriz n para los caracteres faltantes
  3. Bucle hasta que todas las palabras salgan
  4. Imprime el personaje que tuvo el mayor conteo

¿Puedo eliminar las importaciones?
Roberto Anić Banić

¿Está bien eliminar las importaciones de Java en Code Golf?
Roberto Anić Banić

está bien, siempre y cuando especifique que debería importarlos (si no es obvio);)
WayToDoor

Kk. Lo actualizaré cuando regrese a casa. Voy a comprar un nuevo enrutador :) y 90 pies de Cat5e
Roberto Anić Banić

1
No creo que funcione para palabras de más de 9 letras. Intente usar "prospectivo" de la lista de palabras con la entrada "6 XBZ 1P 5P 6E 11E" Quizás cambie el primer ciclo a q [Integer.parseInt (a [i] .substring (0, a [i] .length () - 1) ) -1] = (char) (a [i] .charAt (a [i] .length () - 1) +32); También, un consejo de golf: intente usar Scanner s = new Scanner (new java.io.File ("wordlist.txt"));
comentarios

2

PHP, 346 bytes

<?php $a='array_shift';$p='preg_match';$a($v=&$argv);$w=array_fill(1,$a($v),'(.)');$c=$a($v);foreach($v as$q)$p('/(\d+)(\w)/',$q,$m)&&$w[$m[1]]=$m[2];foreach(file('wordlist.txt')as$y)if($p("/^".implode($w)."$/",$y=strtoupper(trim($y)),$m)&&(!$c||!$p("/[$c]/",$y)))for($i=0;$i++<count($m);)($v=@++$g[$m[$i].$i])&&$v>@$t&&$t=$v&&$l=$m[$i];echo @$l;

Funciona de la siguiente manera:

  1. Crea un patrón de expresiones regulares para unir las letras adivinadas hasta ahora
  2. Itera sobre cada palabra del archivo
  3. Si la palabra coincide con la expresión regular, se asegura de que no contenga una de las letras incorrectas
  4. Incrementa un contador para cada una de las letras posibles de esa palabra (según su posición)
  5. Da salida a la letra con el contador más alto

Suposiciones

  • PHP >=5.4
  • Hay un wordlist.txtarchivo en la carpeta actual

php hangman.php 6 YH 2E 6E 3O 1P 4P PHP Notice: Undefined offset: 2 in ./Desktop/hangman.php on line 1 Notice: Undefined offset: 2 in ./Desktop/hangman.php on line 1
Intenté

1
Gracias por mencionarlo. Hubo un pequeño error en el código. Lo actualicé (todavía 346 bytes).
Razvan

1

Powershell, 153 bytes

Inspirado por la respuesta de briantist .

Como otros escritores usé el nombre del archivo wordlist.txt . Aunque fue posible elegir un nombre más corto.

param($l,$b,$c)((sls "^$(-join(1..$l|%{$p=$c|sls "$_."|% m*
("$p"[1],"[^$b$c]")[!$p]}))$" wordlist.txt|% l*e|% t*y|group|sort c*).Name-match"[^ $c]")[-1]

Menos guión de prueba de golf:

$f = {

param($length,$bad,$chars)

$wordPattern=-join(1..$length|%{                  # join all subpatterns for $_ from 1 to $length
    $c=$chars|sls "$_."|% Matches                 # find subpattern in char array
    ("$c"[1],"[^$bad$chars]")[!$c]                # return a first char of subpattern if subpattern found, or bad characters
})

# Note 1: The word subpattern is similar to 'PE[^XBZ1P 4P 2E 6E]P[^XBZ1P 4P 2E 6E]E'
#         Spaces and digits does not affect the search for letters, only letters are important.
#
# Note 2: The same applies to 0. [^0] matches any letter.
#

$matches=sls "^$wordPattern$" wordlist.txt|% Line # find matched words in file wordlist.txt and return matched string only
$groups=$matches|% toCharArray|group|sort Count   # group all chars in matched words by count
$name=$groups.Name-match"[^ $chars]"              # select property Name from all grouped elements (chars itself) and return not matched to $chars only
$name[-1]                                         # return last element in the sorted array (most frequently found char)

# Note 3: The space is important in the regexp "[^ $chars]"
#         The space makes the regexp valid if the $chars array is empty

}

&$f 7 0 2o,5e,7t
&$f 7 nl 2o,5e,7t
&$f 6 XBZ 1P,4P,2E,6E

Salida:

c
r
l

Valores variables para &$f 7 0 2o,5e,7t:

$WordPattern: "[^02o 5e 7t]o[^02o 5e 7t][^02o 5e 7t]e[^02o 5e 7t]t"
$Matches: concept collect comment correct connect convert consent concert
$Groups:
    Count Name                      Group
    ----- ----                      -----
        1 p                         {p}
        1 v                         {v}
        1 s                         {s}
        2 m                         {m, m}
        2 l                         {l, l}
        4 r                         {r, r, r, r}
        8 n                         {n, n, n, n...}
        8 o                         {o, o, o, o...}
        8 t                         {t, t, t, t...}
        8 e                         {e, e, e, e...}
       13 c                         {c, c, c, c...}
$name: p v s m l r n c
$name[-1]: c
return: c
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.