Lass uns Hangman spielen?


8

Laut dieser Seite besteht die beste Strategie, um englische Henkerwörter zu erraten, darin, die Wahrscheinlichkeit jedes Buchstabens in einer Wortliste zu berechnen, die unseren Bedingungen entspricht. Aber da ich wirklich faul bin, möchte ich nicht jedes Wort im Wörterbuch selbst berechnen. Aber da ich weiß, dass Sie immer hier sind, um mir zu helfen, bin ich sicher, dass Sie mich zum König eines Codes machen können, der das für mich erledigt. Und weil meine Festplatte fast voll ist, möchte ich den kleinstmöglichen Code. Dies bedeutet, dass dies Code-Golf ist und die Übermittlung mit der geringsten Anzahl von Bytes gewinnt, aber auch mit der genauesten! .

Input-Output

Ein zufälliges Wort aus dieser Wortliste wird genommen.

Ihr Programm sollte in Argumenten oder durch Benutzereingaben (Popups, stdin, was auch immer) Folgendes akzeptieren:

  • Länge des Wortes
  • Es wurde bereits ein falscher Buchstabe gefunden oder 0, wenn wir das Spiel gerade gestartet haben und Sie keinen falschen Buchstaben angegeben haben.
  • Buchstaben bereits gefunden UND ihre Position im Wort

Beispiel: ./hangsolver 6 XBZ 1P 4P 2E 6EHier habe ich das Wort "Menschen" gewählt. Zur Verdeutlichung: PE _ P _ E (Falsche Buchstaben sind XB und Z)

Das bedeutet, dass ich in einem Spiel Ihr Skript viele Male starten muss!

Die Ausgabe wird ein einzelner Buchstabe sein, Ihr nächster Versuch.

Regeln

  • Derjenige, der 10 Wörter in weniger Versuchen errät als die anderen, wird gewinnen.
  • Bei einem Gleichstand gewinnt der kürzeste Code in Bytes.
  • Wenn es immer noch ein Unentschieden gibt, gewinnt das schnellste Programm.
  • Sie können davon ausgehen, dass es nur diese Wörter in der englischen Sprache gibt
  • Ich werde nur gültige Wörter aus der Wortliste versuchen.
  • Ich habe einen guten Computer, die CPU-Leistung ist kein Problem (aber versuchen Sie, so schnell wie möglich zu antworten!)
  • Sie können nicht mit einem Online-Löser lösen, aber Sie können die Wortliste herunterladen oder als Argument übergeben lassen. Sie können davon ausgehen, dass es "wordlist.txt" heißt und sich im selben Verzeichnis wie Ihr Skript befindet.
  • Ihr Code muss auf einem gemeinsamen Betriebssystem ausgeführt werden können. Es kann Windows, Mac oder Ubuntu / Debian / CentOS oder Redhat sein.
  • Sie können keinen externen Solver verwenden.
  • Sie können jedoch die URL zur Wortliste kürzen.
  • Dieser Code-Golf endet am 1. September.
  • Sie MÜSSEN die oben beschriebene Methode verwenden.

Viel Glück !

Wordlist hier auf SE gefunden.


2
Der kürzeste Code in Bytes gewinnt, wobei die beste Vermutung der Tiebreak ist? Das bedeutet, dass mein Programm, das einfach einen zufälligen Buchstaben errät, der zuvor noch nicht verwendet wurde, jemanden schlägt, der tatsächlich versucht, eine gute Vermutung zu treffen. Vielleicht möchten Sie Ihre Wertung überdenken, oder Sie erhalten triviale Antworten.
Level River St

"Sie MÜSSEN die oben beschriebene Methode verwenden.", Zitiere ich die Regeln. Aber ich werde bearbeiten, um das zu den primären
Gewinnkriterien

1
Um klar zu sein, bedeutet "mit der oben beschriebenen Methode", den Buchstaben zu erraten, der in der größten Anzahl der möglichen Wörter erscheint, die noch nicht erraten wurden?
isaacg

Ja genau. Danke für die Tippfehlerbearbeitung!
WayToDoor

1
Sollten die Eingabedaten in Ihrem Beispiel nicht "6 XBZ 1P 4P 2E 6E" sein?
Razvan

Antworten:


5

PowerShell, 248 246 241 Byte

$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

Ungolfed

So viel ich konnte, ohne die Funktionsweise zu ändern:

$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

Nervenzusammenbruch

Der Ansatz, den ich hier gewählt habe, bestand darin, zuerst einen regulären Ausdruck zu generieren, um mögliche Wörter aus der Wortliste zu entfernen. Da ich die Länge des Wortes und die Buchstaben kenne, die nicht funktioniert haben, kann ich ziemlich leicht einen regulären Ausdruck daraus machen.

Im PEOPLE-Beispiel würde ich also versuchen, 6 Buchstaben zu generieren, wobei XBZ nicht Teil des Wortes ist ^PE[^XBZ]P[^XBZ]E$.

Ich nutze die Tatsache aus, dass Get-Content( gc) ein Array von Zeilen zurückgibt und der -matchOperator, wenn er mit einem Array auf der linken Seite verwendet wird, ein Array von Übereinstimmungen anstelle eines Bools zurückgibt, sodass ich schnell eine Liste nur der Wörter erhalten kann, die es sind Kandidaten, sobald ich die Regex habe.

Um den regulären Ausdruck zu erzeugen, beginne ich mit einem Array ( $e) der negativ übereinstimmenden Zeichenklasse mit $cElementen ( $cdh der Anzahl der Buchstaben im Wort). Beim Durchlaufen der Zahlen 1 bis suche $cich an dieser Position nach einem passenden Buchstaben. Wenn er vorhanden ist, ersetze ich das Element $edurch diesen Buchstaben.

Sobald ich alle Positionen durchlaufen habe, wird das endgültige Array -join(mit leerer Zeichenfolge) bearbeitet und wir haben unseren regulären Ausdruck.

Jetzt habe ich eine Reihe aller möglichen Wörter, die es sein könnte. Eine schnelle -joinmit leerer Zeichenfolge gibt mir eine große verkettete Zeichenfolge aller Wörter, auf die ich aufgeteilt habe \B(keine Wortgrenze, wenn ich auf leere Zeichenfolge teile, erhalte ich 2 zusätzliche leere Elemente), sodass ich jetzt ein Array habe von jedem Buchstaben in jedem möglichen Wort.

Wenn Where-Objectich das einführe, kann ich die Buchstaben herausfiltern, die bereits übereinstimmen. Dieser Teil war ein echter Schmerz. Es musste sich damit befassen, dass die Liste der übereinstimmenden Buchstaben (einschließlich der Position) 1 Element, mehr als 1 Element oder 0 Elemente war, und daher zuerst $bin ein Array gezwungen wurde, damit -matchalle bearbeitet werden können, aber das (leider in diesem Fall) ) gibt ein Array zurück, daher müssen wir dies überprüfen .Count. Die Verwendung !(thing).Countist etwas kleiner als die Verwendung (thing).Count-gt0.

Im weiteren Verlauf haben wir jetzt eine Reihe aller einzelnen Zeichen (als strings nicht als chars) aus allen möglichen Wörtern, abzüglich der Buchstaben, die bereits richtig erraten wurden.

Wenn Group-Objectich Sort-Object countdas hineinpfeife, bekomme ich ein Objekt mit den Zählungen der einzelnen Buchstaben. Ein schnelles Einpipsen macht es also einfach, die höchste Zählung zu erhalten. Anstatt zu tun (thing|sort count -des)[0], können wir verwenden (thing|sort count)[-1]. In PowerShell [-1]wird das letzte Element abgerufen. Zu diesem Zeitpunkt beschäftigen wir uns noch mit den Objekten, von denen Group-Objectwir stammen, sodass wir die .NameEigenschaft erhalten, die der Buchstabe ist, der am häufigsten erscheint.

Anmerkungen

  • Sollte mit PowerShell v3 + funktionieren; wird mit ziemlicher Sicherheit an 2 ersticken.
  • Denken Sie daran, dass Sie beim Aufrufen eines PowerShell-Skripts Argumente mit Leerzeichen und nicht mit Kommas übergeben.
  • Obwohl ich es in den Regeln nicht gesehen habe, sieht es so aus, als würde jeder den Dateinamen verwenden, wordlist.txtsonst könnten sich ein paar Bytes sparen.
  • Geschwindigkeit sollte kein Problem sein. Dies scheint für mich sofort zu laufen. Der langsamste Lauf, den ich machen konnte ( .\hangman.ps1 7 0), läuft in ungefähr 350 ms.

1
Willkommen bei Programming Puzzles & Code Golf Stack Exchange, tolle erste Antwort! :)
Türknauf

@ Doorknob vielen Dank!
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])

ziemlich sicher, dass dies weiter golfen kann.

Filtert die Wortliste nach möglichen Übereinstimmungen, erstellt eine Char-Frequenzkarte und wählt das am häufigsten vorkommende Zeichen aus, das noch nicht ausgewählt wurde.


Du hast viele ''.join(..)s. Wenn alle darin enthaltenen Elemente Zeichenfolgen der Länge 1 sind, können Sie sie in ändern '..'[2::5], wobei die Apostrophe Backticks sind.
Kade

3

Java, 646 640 631 607 606 (kurz) 790 789 779 (schnell) Bytes

KURZ

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

SCHNELL

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:"");}}

Legen Sie die Wortlistendatei in den Ordner.

Kurzversionsalgorithmus

  1. Args laden
  2. Konstruieren Sie das Wort, das wir erraten möchten {'p', 'e', ​​'\ 0', 'p', '\ 0', 'e'}
  3. Laden Sie die WordList
  4. Gehen Sie jede Zeile der WordList durch
  5. Stoppen Sie, wenn Sie feststellen, dass das gesamte Wort dieser Bedingung entspricht, p[i] == q[i] || q[i] == '\0'wobei p ein Wort aus der Wortliste (char-Array) ist und q das Wort ist, das wir zu erraten versuchen
  6. Durchlaufen Sie falsche Zeichen und vergleichen Sie sie mit dem Wort
  7. Drucken Sie das erste fehlende Zeichen

Langversionsalgorithmus

  1. Kurze Schritte 1-7
  2. Erhöhen Sie die Zeichenanzahl im n-Array für die fehlenden Zeichen
  3. Schleife, bis alle Wörter durchkommen
  4. Drucken Sie das Zeichen mit der höchsten Anzahl

kann ich die Importe entfernen?
Roberto Anić Banić

Ist es in Ordnung, Java-Importe in Code Golf zu entfernen?
Roberto Anić Banić

Es ist in Ordnung, solange Sie angeben, dass ich sie importieren soll (wenn nicht offensichtlich);)
WayToDoor

Kk. Ich werde es aktualisieren, wenn ich nach Hause komme. Ich werde mir einen neuen Router kaufen :) und 90 Fuß Cat5e
Roberto Anić Banić

1
Ich denke nicht, dass es für Wörter funktioniert, die länger als 9 Buchstaben sind. Versuchen Sie, "prospektiv" aus der Wortliste mit der Eingabe "6 XBZ 1P 5P 6E 11E" zu verwenden. Ändern Sie möglicherweise die erste Schleife in q [Integer.parseInt (a [i] .substring (0, a [i] .length () - 1). ) -1] = (char) (a [i] .charAt (a [i] .length () - 1) +32); Auch ein Golftipp: Versuchen Sie es mit Scanner s = new Scanner (new java.io.File ("wordlist.txt"));
Bmarks

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;

Es funktioniert wie folgt:

  1. Erstellt ein Regex-Muster für die Übereinstimmung mit den bisher erratenen Buchstaben
  2. Iteriert über jedes Wort aus der Datei
  3. Wenn das Wort mit dem regulären Ausdruck übereinstimmt, wird sichergestellt, dass es keinen der falschen Buchstaben enthält
  4. Erhöht einen Zähler für jeden der möglichen Buchstaben dieses Wortes (basierend auf ihrer Position).
  5. Gibt den Buchstaben mit dem höchsten Zähler aus

Annahmen:

  • PHP >=5.4
  • wordlist.txtIm aktuellen Ordner befindet sich eine Datei

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 1Versucht, ihn dazu zu bringen, Leute zu erraten
WayToDoor

1
Vielen Dank für den Hinweis. Es gab einen kleinen Fehler im Code. Ich habe es aktualisiert (immer noch 346 Bytes).
Razvan

1

Powershell, 153 Bytes

Inspiriert von der Antwort des Briantisten .

Als andere Autoren habe ich den Dateinamen verwendet wordlist.txt. Obwohl es möglich war, einen kürzeren Namen zu wählen.

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]

Weniger Golf-Testskript:

$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

Ausgabe:

c
r
l

Variable Werte für &$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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.