Swipe Type Converter

27

Die nächste Revolution beim Tippen auf Laptops wurde am 1. April 2014 von SwiftKey veröffentlicht . Ich möchte jedoch die erste Person sein, die einen Swiping-Nano-Klon schreibt, aber da ich keinen guten Swipe-Text für eine echte Textbibliothek finde und nicht auf sie warten kann, frage ich hier.

Aufgabe

Schreiben Sie ein Programm, das Swipe-Text aufnimmt und das echte Textäquivalent ausgibt. Beispiel:

Input: hgrerhjklo
Output: hello

Wenn der Benutzer Folgendes tut:

Bildbeschreibung hier eingeben

Andere Beispiele:

Input: wertyuioiuytrtjklkjhgfd
Output: world

Input: poiuytrtyuioiugrewsasdfgbhnmkijnbg
Output: programming

Input: poiuygfdzxcvhjklkjhgres
Output: puzzles

Input: cvhjioiugfde
Output: code

Input: ghiolkjhgf
Output: golf

Regeln

  • Das Programm nimmt ein 'Wort' auf stdin oder argv
  • Der erste und letzte Buchstabe der durchgestrichenen Eingabe entspricht dem ersten und letzten Buchstaben des tatsächlichen Wortes
  • Sie können davon ausgehen, dass der Benutzer einigermaßen gerade Linien macht, aber Sie können die Beispieldaten verwenden, um dies zu überprüfen (ich habe die Beispieldaten gemacht und ich werde die endgültigen Testdaten machen).
  • Bei mehrdeutigen Eingaben können Sie eine der beiden Ausgaben auswählen, aber ich werde versuchen, alle Mehrdeutigkeiten in den Testdaten zu beseitigen
  • Dieses Wort wird in dieser Wortliste angezeigt (aber durchgestrichen). Die Wortliste befindet sich im aktuellen Verzeichnis und kann gelesen werden (durch Zeilenumbrüche getrennt, wird benannt wordlist, keine Erweiterung).
  • Das Wischen enthält nur Kleinbuchstaben
  • Das Wischen kann doppelte Zeichen enthalten, wenn der Benutzer bei einem Schlüssel pausiert
  • Das Programm muss auf stdout ausgegeben werden (Groß- / Kleinschreibung spielt keine Rolle)
  • Das Programm muss 0als Rückkehrcode zurückgeben
  • Sie müssen den Befehl run, den Befehl compile (falls erforderlich), den Namen und den zu verwendenden Eingabepfad angeben
  • Es gelten Standardlücken (sie können jedoch nicht helfen)
  • Keine nicht eingebauten Bibliotheken erlaubt
  • Deterministische, nicht golfende / verschleierte Lösungen bevorzugt
  • Kein Schreiben von Dateien, Vernetzung usw.
  • Ihr Code muss in einer Sekunde oder weniger ausgeführt werden (Ihr Code wird einmal pro Wort ausgeführt)
  • Die Scoring-Läufe werden auf einem Intel i7 Haswell-Prozessor mit 4 virtuellen Codes (2 realen) ausgeführt, sodass Sie bei Bedarf Threads verwenden können
  • Maximale Codelänge von 5000 Bytes
  • Für die von Ihnen verwendete Sprache muss eine kostenlose (keine Test-) Version für Linux verfügbar sein (Arch Linux, falls dies von Bedeutung ist)

Gewinnkriterium

  • Der Gewinner ist die genaueste Lösung (vom Kontrollprogramm anhand der bereitgestellten Testliste bewertet)
  • Popularität ist der Krawattenbrecher
  • Die Wertungstabelle wird alle paar Tage aktualisiert
  • Auszeiten und Abstürze gelten als fehlgeschlagen
  • Diese Herausforderung dauert je nach Beliebtheit zwei Wochen oder länger
  • Die endgültige Wertung verwendet eine andere, zufällig ausgewählte Liste von Wörtern (gleiche Länge, aus derselben Wortliste).

Andere

Aktuelle Anzeigetafeln

Testliste ( Protokolle ):

Three Pass Optimizer:Errors: 0/250       Fails: 7/250        Passes: 243/250     Timeouts: 0/250     
Corner Sim:         Errors: 0/250       Fails: 9/250        Passes: 241/250     Timeouts: 0/250     
Discrete Fréchet Distance:Errors: 0/250       Fails: 17/250       Passes: 233/250     Timeouts: 0/250     
Turnaround:         Errors: 0/250       Fails: 18/250       Passes: 232/250     Timeouts: 0/250     
Direction Checker:  Errors: 0/250       Fails: 19/250       Passes: 231/250     Timeouts: 0/250     
Regex Solver:       Errors: 0/250       Fails: 63/250       Passes: 187/250     Timeouts: 0/250

Testliste2 ( Protokolle ):

Corner Sim:         Errors: 0/250       Fails: 10/250       Passes: 240/250     Timeouts: 0/250     
Three Pass Optimizer:Errors: 2/250       Fails: 14/250       Passes: 234/250     Timeouts: 0/250     
Turnaround:         Errors: 0/250       Fails: 16/250       Passes: 234/250     Timeouts: 0/250     
Direction Checker:  Errors: 0/250       Fails: 17/250       Passes: 233/250     Timeouts: 0/250     
Discrete Fréchet Distance:Errors: 0/250       Fails: 18/250       Passes: 232/250     Timeouts: 0/250     
Regex Solver:       Errors: 0/250       Fails: 67/250       Passes: 183/250     Timeouts: 0/250

Final Run

Testliste ( Protokolle ):

Corner Sim:         Errors: 0/250       Fails: 14/250       Passes: 236/250     Timeouts: 0/250     
Three Pass Optimizer:Errors: 0/250       Fails: 18/250       Passes: 232/250     Timeouts: 0/250     
Direction Checker:  Errors: 0/250       Fails: 20/250       Passes: 230/250     Timeouts: 0/250     
Turnaround:         Errors: 0/250       Fails: 23/250       Passes: 227/250     Timeouts: 0/250     
Discrete Fréchet Distance:Errors: 0/250       Fails: 30/250       Passes: 220/250     Timeouts: 0/250     
Regex Solver:       Errors: 0/250       Fails: 55/250       Passes: 195/250     Timeouts: 0/250

Gut gemacht an alle und hgfdsasdertyuiopoiuy swertyuiopoijnhg!

Matsjoyce
quelle
Was ist "eine Lösung"? Wo ist der Code?
Türklinke
Lassen Sie uns diese Diskussion im Chat fortsetzen .
Optimierer
8
Etwas verwandt.
wchargin
@Optimizer Nicht sicher über die anderen Fälle, aber " p oiuytres a se r n a n d fghui o iugfd x cgu i ug c xs ein sdfghjk l ku y " enthält alle Buchstaben von "paradox", um, mit Ausnahme der l, das ist nicht verdoppelt.
Es1024
1
@Optimiser Nun, ich dachte, Ihr Beitrag würde es unterbieten, aber es war knapp darunter (ein bisschen Tweaking hätte das sicher geändert). Es scheint, dass ich es akzeptieren kann, also ... sollte ich (ich scheine keine Wiederholung zu bekommen, wenn ich es akzeptiere)? Ich würde gerne die eines anderen akzeptieren, aber das entspricht nicht den Regeln (es sei denn, Sie haben eine gute Idee).
Matsjoyce

Antworten:

12

JavaScript, ES6, Three Pass Optimizer, 112 187 235 240 241 243 und 231 234 Durchgänge

Ein Drei-Pass-Filter, der zuerst kritische Tasten in der Tastenanschlagssequenz herausfindet und die Sequenz dann durch die drei Filter leitet:

  1. Ein locker geformter RegEx aus den kritischen Schlüsseln und den helfenden Schlüsseln. Dies ergibt ein korrektes Ergebnis für die Mehrheit der Tasten (ungefähr 150).
  2. Ein strenger RegEx, der nur aus kritischen Schlüsseln besteht. Dies ergibt ein korrektes Ergebnis für zusätzliche 85 Sequenzen
  3. Ein dritter Filter, um Mehrdeutigkeiten zwischen engen Antworten herauszufinden. Dies funktioniert in 40% mehrdeutigen Fällen.

Code

keyboard = {
  x: {},
  y: ['  q      w      e      r      t      y      u      i      o      p',
      '    a      s      d      f      g      h      j      k      l',
      '        z      x      c      v      b      n      m'],
};
for (var i in keyboard.y)
  for (var y of keyboard.y[i])
    keyboard.x[y] = +i*7;
p = C => (x=keyboard.x[C],{x, y: keyboard.y[x/7].indexOf(C)})
angle = (L, C, R) => (
  p0 = p(L), p1 = p(C), p2 = p(R),
  a = Math.pow(p1.x-p0.x,2) + Math.pow(p1.y-p0.y,2),
  b = Math.pow(p1.x-p2.x,2) + Math.pow(p1.y-p2.y,2),
  c = Math.pow(p2.x-p0.x,2) + Math.pow(p2.y-p0.y,2),
  Math.acos((a+b-c) / Math.sqrt(4*a*b))/Math.PI*180
)
corner = (L, C, R, N, W) => {
  if (skip) {
    skip = false;
    return [];
  } 
  ngl = angle(L, C, R);
  if (ngl < 80) return [C + "{1,3}"]
  if (ngl < 115 && p(L).x != p(R).x && p(L).x != p(C) && p(R).x != p(C).x && Math.abs(p(L).y - p(R).y) < 5) return [C + "{0,3}"]
  if (ngl < 138) {
    if (N && Math.abs(ngl - angle(C, R, N)) < 6) {
      skip = true;
      return [L + "{0,3}", "([" + C + "]{0,3}|[" + R + "]{0,3})?", N + "{0,3}"]
    }
    return [C + "{0,3}"]
  }
  return ["([" + L + "]{0,3}|[" + C + "]{0,3}|[" + R + "]{0,3})?"]
}
f = S => {
  for (W = [S[0] + "{1,2}"],i = 1; i < S.length - 1; i++)
    W.push(...corner(S[i - 1], S[i], S[i + 1], S[i + 2], W))
  return [
    new RegExp("^" + W.join("") + S[S.length - 1] + "{1,3}$"),
    new RegExp("^" + W.filter(C=>!~C.indexOf("[")).join("") + S[S.length - 1] + "{1,3}$")
  ]
}
thirdPass = (F, C) => {
  if (!F[0]) return null
  F = F.filter((s,i)=>!F[i - 1] || F[i - 1] != s)
  FF = F.map(T=>[...T].filter((c,i)=>!T[i - 1] || T[i - 1] != c).join(""))
  if (FF.length == 1) return F[0];
  if (FF.length < 6 && FF[0][2] && FF[1][2] && FF[0][0] == FF[1][0] && FF[0][1] == FF[1][1])
    if (Math.abs(F[0].length - F[1].length) < 1)
      for (i=0;i<Math.min(F[0].length, FF[1].length);i++) {
        if (C.indexOf(FF[0][i]) < C.indexOf(FF[1][i])) return F[0]
        else if (C.indexOf(FF[0][i]) > C.indexOf(FF[1][i])) return F[1]
      }
  return F[0]
}
var skip = false;
SwiftKey = C => (
  C = [...C].filter((c,i)=>!C[i - 1] || C[i - 1] != c).join(""),
  skip = false, matched = [], secondPass = [], L = C.length, reg = f(C),
  words.forEach(W=>W.match(reg[0])&&matched.push(W)),
  words.forEach(W=>W.match(reg[1])&&secondPass.push(W)),
  matched = matched.sort((a,b)=>Math.abs(L-a.length)>Math.abs(L-b.length)),
  secondPass = secondPass.sort((a,b)=>Math.abs(L-a.length)>Math.abs(L-b.length)),
  first = matched[0], second = secondPass[0], third = thirdPass(secondPass.length? secondPass: matched, C),
  second && second.length >= first.length - 1? first != third ? third: second: third.length >= first.length ? third: first
)

// For use by js shell of latest firefox
print(SwiftKey(readline()));

Der Code geht davon aus, dass eine aufgerufene Variable wordsvorhanden ist, die alle Wörter dieser Seite enthält

Sehen Sie hier den Code in Aktion

Sehen Sie hier die Testfälle in Aktion

Beide oben genannten Links funktionieren nur auf einem aktuellen Firefox (33 und höher) (aufgrund von ES6).

Optimierer
quelle
Jep! Ich schieße mit Muscheln. Sie können keypos.csvjetzt auch die richtige Datei verwenden. Die verfügbaren E / A-Funktionen sind unter developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/…
matsjoyce
Es ist in Ordnung, aber die Wischbewegungen werden mit meinen Tastaturwinkeln vorgenommen, daher ist es Ihre Wahl (scheint jedoch keinen
Einfluss
240 Pässe ist hervorragend! Ich hätte gedacht, dass Mehrdeutigkeiten solch gute Ergebnisse verhindern würden. Ich bin gespannt, wie sich das beim letzten Testset verhält.
Emil
@Emil - Ja, ich warte auch darauf, das zu sehen.
Optimierer
9

Ruby, Regex Solver - 30 140 176 180 182 187 und 179 183 Pässe

Ich werde die Partitur später herausfinden. Hier ist eine sehr naive Lösung, die das Tastaturlayout nicht berücksichtigt:

words = File.readlines('wordlist').map(&:chomp)

swipe = ARGV.shift
puts words.select {|word| word[0] == swipe[0] &&
                          word[-1] == swipe[-1]}
          .select {|word|
              chars = [word[0]]
              (1..word.size-1).each {|i| chars << word[i] if word[i] != word[i-1]}
              swipe[Regexp.new('^'+chars.join('.*')+'$')]
          }.sort_by {|word| word.size}[-1]

Es nimmt Eingaben von ARGV entgegen und druckt das Ergebnis aus. Ich filtere nur die Wortliste nach dem ersten und dem letzten Buchstaben und versuche alle verbleibenden Wörter gegen die Eingabe (doppelte Buchstaben werden entfernt und ein regulärer Ausdruck wie ^g.*u.*e.*s$"rate" wird verwendet) und gebe den ersten davon zurück, falls vorhanden sind mehrere Lösungen.

Führen Sie es wie

ruby regex-solver.rb cvhjioiugfde

Jeder andere kann diesen Schritt als ersten Filter verwenden. Ich glaube, er wirft keine korrekten Wörter aus, sodass diese vorläufige Überprüfung den Suchraum für bessere Algorithmen erheblich verringern kann.

Edit: Dem Vorschlag des OP folgend, wähle ich jetzt den längsten der Kandidaten aus, was eine anständige Heuristik zu sein scheint.

Vielen Dank auch an es1024 für die Erinnerung an doppelte Briefe.

Martin Ender
quelle
Getan. Dein Log ist unter github.com/matsjoyce/codegolf-swipe-type/blob/master/logs/…. Ich denke, das Problem ist, dass es zufällig aus den möglichen Lösungen auswählt, was durch Auswahl der längsten oder etwas anderem verbessert werden könnte.
Matsjoyce
Ich denke, dies könnte alle korrekten Wörter mit zwei identischen Buchstaben nebeneinander werfen, wie z. B. paradoxically, wenn lsie nur einmal in der Eingabe erscheinen würden, anstatt doppelt so, wie es der reguläre Ausdruck verlangt.
Es1024
@ es1024, ah danke, als ich diesen Algorithmus zum ersten Mal in der Sandbox vorschlug, war mir das eigentlich bewusst, aber ich habe es gestern vergessen. Wird später behoben.
Martin Ender
7

C ++, Discrete Fréchet Distance - 201 220 222 232 und 232 Pässe

Für mich war das Problem die Fréchet-Distanz, die leider sehr schwer zu berechnen ist.

Aus Spaß habe ich versucht, das Problem zu lösen, indem ich eine diskrete Approximation implementiert habe, die von Thomas Eiter und Heikki Mannila in Computing Discrete Fréchet Distance (1994) beschrieben wurde.

Zuerst benutze ich den gleichen Ansatz wie die anderen, um alle Wörter in der Liste zu filtern, die Teilfolgen der Eingabe sind (wobei auch mehrere sequenzielle Vorkommen desselben Zeichens berücksichtigt werden). Dann fülle ich die Polygonkurve von Buchstabe zu Buchstabe jedes Wortes mit Zwischenpunkten und vergleiche sie mit der Eingabekurve. Schließlich dividiere ich jede Distanz durch die Länge des Wortes und nehme die Mindestpunktzahl.

Bisher funktioniert die Methode nicht so gut, wie ich es mir erhofft hatte (das Codebeispiel wird als "chide" erkannt), aber dies könnte nur das Ergebnis von Fehlern sein, die ich noch nicht gefunden habe. Andernfalls wäre eine andere Idee, andere Variationen der Fréchet-Distanz zu verwenden ("Durchschnitt" anstelle von "maximale Hundeleinenlänge").

Edit: Jetzt benutze ich eine Annäherung an die "durchschnittliche Hundeleinenlänge". Das bedeutet, dass ich eine geordnete Zuordnung zwischen beiden Pfaden finde, die die Summe aller Entfernungen minimiert und später durch die Anzahl der Entfernungen dividiert.

Wenn dasselbe Zeichen zweimal oder öfter im Wörterbuchwort vorkommt, füge ich nur einen Knoten in den Pfad ein.

#include<iostream>
#include<fstream>
#include<vector>
#include<map>
#include<algorithm>
#include<utility>
#include<cmath>

using namespace std;

const double RESOLUTION = 3.2;

double dist(const pair<double, double>& a, const pair<double, double>& b) {
    return sqrt((a.first - b.first) * (a.first - b.first) + (a.second - b.second) * (a.second - b.second));
}

double helper(const vector<pair<double, double> >& a,
        const vector<pair<double, double> >& b,
        vector<vector<double> >& dp,
        int i,
        int j) {
    if (dp[i][j] > -1)
        return dp[i][j];
    else if (i == 0 && j == 0)
        dp[i][j] = dist(a[0], b[0]);
    else if (i > 0 && j == 0)
        dp[i][j] = helper(a, b, dp, i - 1, 0) +
                   dist(a[i], b[0]);
    else if (i == 0 && j > 0)
        dp[i][j] = helper(a, b, dp, 0, j - 1) +
                   dist(a[0], b[j]);
    else if (i > 0 && j > 0)
        dp[i][j] = min(min(helper(a, b, dp, i - 1, j),
                           helper(a, b, dp, i - 1, j - 1)),
                       helper(a, b, dp, i, j - 1)) +
                   dist(a[i], b[j]);
    return dp[i][j];
}

double discretefrechet(const vector<pair<double, double> >& a, const vector<pair<double, double> >& b) {
    vector<vector<double> > dp = vector<vector<double> >(a.size(), vector<double>(b.size(), -1.));
    return helper(a, b, dp, a.size() - 1, b.size() - 1);
}

bool issubsequence(string& a, string& b) {
    // Accounts for repetitions of the same character: hallo subsequence of halo
    int i = 0, j = 0;
    while (i != a.size() && j != b.size()) {
        while (a[i] == b[j])
            ++i;
        ++j;
    }
    return (i == a.size());
}

int main() {
    string swipedword;
    cin >> swipedword;

    ifstream fin;
    fin.open("wordlist");
    map<string, double> candidatedistance = map<string, double>();
    string readword;
    while (fin >> readword) {
        if (issubsequence(readword, swipedword) && readword[0] == swipedword[0] && readword[readword.size() - 1] == swipedword[swipedword.size() - 1]) {
            candidatedistance[readword] = -1.;
        }
    }
    fin.close();

    ifstream fin2;
    fin2.open("keypos.csv");
    map<char, pair<double, double> > keypositions = map<char, pair<double, double> >();
    char rc, comma;
    double rx, ry;
    while (fin2 >> rc >> comma >> rx >> comma >> ry) {
        keypositions[rc] = pair<double, double>(rx, ry);
    }
    fin2.close();

    vector<pair<double, double> > swipedpath = vector<pair<double, double> >();
    for (int i = 0; i != swipedword.size(); ++i) {
        swipedpath.push_back(keypositions[swipedword[i]]);
    }

    for (map<string, double>::iterator thispair = candidatedistance.begin(); thispair != candidatedistance.end(); ++thispair) {
        string thisword = thispair->first;
        vector<pair<double, double> > thispath = vector<pair<double, double> >();
        for (int i = 0; i != thisword.size() - 1; ++i) {
            pair<double, double> linestart = keypositions[thisword[i]];
            pair<double, double> lineend = keypositions[thisword[i + 1]];
            double linelength = dist(linestart, lineend);
            if (thispath.empty() || linestart.first != thispath[thispath.size() - 1].first || linestart.second != thispath[thispath.size() - 1].second)
                thispath.push_back(linestart);
            int segmentnumber = linelength / RESOLUTION;
            for (int j = 1; j < segmentnumber; ++j) {
                thispath.push_back(pair<double, double>(linestart.first + (lineend.first - linestart.first) * ((double)j) / ((double)segmentnumber),
                                    linestart.second + (lineend.second - lineend.second) * ((double)j) / ((double)segmentnumber)));
            }
        }
        pair<double, double> lastpoint = keypositions[thisword[thisword.size() - 1]];
        if (thispath.empty() || lastpoint.first != thispath[thispath.size() - 1].first || lastpoint.second != thispath[thispath.size() - 1].second)
        thispath.push_back(lastpoint);

        thispair->second = discretefrechet(thispath, swipedpath) / (double)(thispath.size());
    }

    double bestscore = 1e9;
    string bestword = "";
    for (map<string, double>::iterator thispair = candidatedistance.begin(); thispair != candidatedistance.end(); ++thispair) {
        double score = thispair->second;
        if (score < bestscore) {
            bestscore = score;
            bestword = thispair->first;
        }
        // cout << thispair->first << ": " << score << endl;
    }
    cout << bestword << endl;

    return 0;
}

Ich habe den Code mit Clang kompiliert, g++ ./thiscode.cpp -o ./thiscodesollte aber auch gut funktionieren.

camelNeck
quelle
1
Ja! Jemand hat endlich eine Lösung mit einem großen fetten Algorithmusnamen hinzugefügt! Dein Log ist bei github.com/matsjoyce/codegolf-swipe-type/blob/master/logs/…
matsjoyce 13.10.14
1
Nennen wir es lieber einen einfachen dynamischen Programmieralgorithmus für ein großes Problem der Fettinformatik.
camelNeck
Aus irgendeinem Grund scheint dies für die Eingabe fehlzuschlagen programmijng- es gibt mir pang.
Riking
Das ist merkwürdig. Ich werde so, programmingwie es sein sollte. Könnten Sie bitte die Zeile auskommentieren // cout << thispair->first << ": " << score << endl;und die resultierenden Punkte einfügen?
camelNeck
6

Python 2, Turnarounds, 224, 226, 230, 232 und 230, 234 Pässe

Dies ist ein recht einfacher Ansatz:

  • Finden Sie die Punkte, an denen der Buchstabenfluss die Richtung ändert (plus Anfang und Ende).
  • Das längste Wort ausgeben, das alle diese Wendepunkte enthält.
import sys, csv, re

wordlistfile = open('wordlist', 'r')
wordlist = wordlistfile.read().split('\n')

layout = 'qwertyuiop asdfghjkl  zxcvbnm'
keypos = dict((l, (2*(i%11)+i/11, i/11)) for i,l in enumerate(layout))

#read input from command line argument
input = sys.argv[1]

#remove repeated letters
input = ''.join(x for i,x in enumerate(input) if i==0 or x!=input[i-1])

#find positions of letters on keyboard
xpos = map(lambda l: keypos[l][0], input)
ypos = map(lambda l: keypos[l][1], input)

#check where the direction changes (neglect slight changes in x, e.g. 'edx')
xpivot = [(t-p)*(n-t)<-1.1 for p,t,n in zip(xpos[:-2], xpos[1:-1], xpos[2:])]
ypivot = [(t-p)*(n-t)<0 for p,t,n in zip(ypos[:-2], ypos[1:-1], ypos[2:])]
pivot = [xp or yp for xp,yp in zip(xpivot, ypivot)]

#the first and last letters are always pivots
pivot = [True] + pivot + [True]

#build regex
regex = ''.join(x + ('+' if p else '*') for x,p in zip(input, pivot))
regexobj = re.compile(regex + '$')

#find all words that match the regex and output the longest one
words = [w for w in wordlist if regexobj.match(w)]
output = max(words, key=len) if words else input
print output

Rennen wie

python turnarounds.py tyuytrghn

Die Lösung ist nicht sehr robust. Ein einziger falscher Tastendruck würde zum Fehlschlagen führen. Da die Testfälle jedoch kaum Rechtschreibfehler aufweisen, funktioniert sie recht gut und ist nur in einigen Fällen verwirrt, wenn sie zu lange Wörter enthalten.

Bearbeiten: Ich habe es ein wenig geändert, um nicht die bereitgestellte Schlüsselpositionsdatei, sondern ein vereinfachtes Layout zu verwenden. Dies macht es für meinen Algorithmus einfacher, da die Schlüssel in der Datei nicht gleichmäßig verteilt sind. Ich kann sogar etwas bessere Ergebnisse erzielen, indem ich einige Ad-hoc-Tiebreaker für mehrdeutige Fälle einbeziehe, aber das wäre für diesen speziellen Testsatz zu optimierend. Einige Fehler bleiben bestehen, die eigentlich nicht mehrdeutig sind, die ich aber mit meinem einfachen Algorithmus nicht einfange, da nur Richtungsänderungen von mehr als 90 Grad berücksichtigt werden.

Emil
quelle
Gut gemacht! Aktueller Leiter! Wenn du das Log sehen willst,
gehe zu
@matsjoyce: Ich habe der Frage einen Kommentar hinzugefügt, der auf die beiden Rechtschreibfehler hinweist, von denen ich glaube, dass ich sie gefunden habe. :)
Emil
Ja, danke, ich gebe es nur noch einen Scheck. Ich bin mir jedoch nicht ganz sicher, was ich mit zweideutigen Fällen anfangen soll.
Matsjoyce
@matsjoyce: Einige Unklarheiten können behoben werden, indem Sie einen anderen der möglichen Pfade über die Tastatur auswählen. ZB bratskönnte 'bgrdsasdrtrds'das nicht zu verwechseln sein breasts. Es würde mir aber auch nichts ausmachen, wenn Sie es so lassen, wie es ist.
Emil
Richtig, das würde funktionieren. Ich bin nur besorgt , dass , wenn dieser Satz auch gemacht wird ‚optimale‘ und der Endwertung Satz setzen nicht zu viel Kontrolle auf, einige Lösungen möglicherweise nicht so gut durchführen
matsjoyce
6

PHP, Direction Checker, 203 213 216 229 231 und 229 233 besteht

Dies beginnt mit einem einfachen Durchlauf durch das Wörterbuch, um eine Liste von Wörtern zu erhalten, deren Buchstaben alle in der Eingabe vorhanden sind (was Martin hier getan hat ), und auch nur zu überprüfen, ob l.*statt l.*l.*wann ldupliziert wurde.

Das längste Wort hier wird dann in einer Variablen gespeichert.

Eine weitere Überprüfung wird dann basierend auf den Tasten an den Punkten durchgeführt, an denen das Wischen die Richtung ändert (+ / 0 nach - oder - / 0 nach +, entweder in x oder y). Die Liste der möglichen Wörter wird mit dieser Liste von Zeichen verglichen, und Wörter, die nicht übereinstimmen, werden entfernt. Dieser Ansatz beruht auf scharfen Drehungen beim Wischen, um genau zu sein.

Das längste verbleibende Wort wird ausgegeben, oder wenn keine Wörter mehr übrig sind, wird stattdessen das längste Wort von früher ausgegeben.

Bearbeiten: Es wurden alle Zeichen in einer horizontalen Linie hinzugefügt, die zu einer Richtungsänderung führten, als mögliche zu überprüfende Werte.

PHP 5.4 ist erforderlich (oder ersetzen Sie alle [..]durch array(..)).

<?php
function get_dir($a, $b){
    $c = [0, 0];
    if($a[0] - $b[0] < -1.4) $c[0] = 1;
    else if($a[0] - $b[0] > 1.4) $c[0] = -1;
    if($a[1] < $b[1]) $c[1] = 1;
    else if($a[1] > $b[1]) $c[1] = -1;
    return $c;
}
function load_dict(){
    return explode(PHP_EOL, file_get_contents('wordlist'));
}

$coord = [];
$f = fopen('keypos.csv', 'r');
while(fscanf($f, "%c, %f, %f", $c, $x, $y)){
    $coord[$c] = [$x, $y];  
}
fclose($f);

$dict = load_dict();
$in = $argv[1];
$possib = [];

foreach($dict as $c){
    if($c[0] == $in[0]){
        $q = strlen($c);
        $r = strlen($in);
        $last = '';
        $fail = false;
        $i = $j = 0;
        for($i = 0; $i < $q; ++$i){
            if($last == $c[$i]) continue;
            if($j >= $r){
                $fail = true;
                break;
            }
            while($c[$i] != $in[$j++])
                if($j >= $r){
                    $fail = true; 
                    break;
                }
            if($fail) break;
            $last = $c[$i];
        }
        if(!$fail) 
            $possib[] = $c;
    }
}

$longest = '';
foreach($possib as $p){
    if(strlen($p) > strlen($longest))
        $longest = $p;
}

$dir = [[0, 0]];
$cdir = [0, 0];
$check = '/^' . $in[0] . '.*?';
$olst = []; $p = 1;

$len = strlen($in);
for($i = 1; $i < $len; ++$i){
    $dir[$i] = get_dir($coord[$in[$i - 1]], $coord[$in[$i]]);
    $olddir = $cdir;
    $newdir = $dir[$i];
    $xc = $olddir[0] && $newdir[0] && $newdir[0] != $olddir[0];
    $yc = $olddir[1] && $newdir[1] && $newdir[1] != $olddir[1];
    if($xc || $yc){ // separate dir from current dir
        if($yc && !$xc && $dir[$i - 1][1] == 0){
            $tmp = '';
            for($j = $i - 1; $j >= 0 && $dir[$j][1] == 0; --$j){
                $tmp = '(' . $in[$j-1] . '.*?)?' . $tmp;
            }
            $olst[] = range($p, $p + (($i - 1) - $j));
            $p += ($i - 1) - $j + 1;
            $check .= $tmp . '(' . $in[$i - 1] . '.*?)?';
        }else{
            $check .= $in[$i - 1] . '.*?';
        }
        $cdir = $dir[$i];
    }else{
        if(!$cdir[0]) $cdir[0] = $dir[$i][0]; 
        if(!$cdir[1]) $cdir[1] = $dir[$i][1]; 
    }
}
$check .= substr($in, -1) . '$/';
$olstc = count($olst);
$res = [];
foreach($possib as $p){
    if(preg_match($check, $p, $match)){
        if($olstc){
            $chk = array_fill(0, $olstc, 0);
            for($i = 0; $i < $olstc; ++$i){
                foreach($olst[$i] as $j){
                    if(isset($match[$j]) && $match[$j]){
                        ++$chk[$i];
                    }
                }
                // give extra weight to the last element of a horizontal run
                if(@$match[$olst[$i][count($olst[$i])-1]])
                    $chk[$i] += 0.5;
            }
            if(!in_array(0, $chk)){
                $res[$p] = array_sum($chk);
            }
        }else{
            $res[$p] = 1;
        }
    }
}
$possib = array_keys($res, @max($res));
$newlong = '';
foreach($possib as $p){
    if(strlen($p) > strlen($newlong))
        $newlong = $p;
}
if(strlen($newlong) == 0) echo $longest;
else echo $newlong;
es1024
quelle
@matsjoyce Verpasste diesen Aufzählungspunkt beim Lesen der Frage. Der Code verwendet jetzt die Positionen vonkeypos.csv
es1024 13.10.14
@ es1024 Obwohl die Wortliste nicht Teil der 250 Testfälle ist, enthält sie 238 Wörter mit drei aufeinander folgenden Buchstaben (bisher habe ich nur Wörter überprüft, die auf enden sss).
Martin Ender
Wenn Sie es brauchen, Ihre Protokolle bei github.com/matsjoyce/codegolf-swipe-type/blob/master/logs/…
matsjoyce
3

Python 3, Corner Simulator, 241 und 240 Pässe

Algorithmus:

  • Deduplizieren Sie die Eingabe und alle Wörter in der Wortliste (entfernen Sie aufeinanderfolgende Folgen desselben Zeichens) (verwenden Sie ein Wörterbuch, um die ursprünglichen Wörter wiederherzustellen).
  • Entfernen Sie alle Wörter, die nicht mit dem Anfang und dem Ende des Wischens beginnen und enden (erster Durchgang).
  • Machen Sie eine Regex aus allen Ecken, die größer als 80 Grad sind, und entfernen Sie dann alle Wörter, die nicht dazu passen (zweiter Durchgang).
  • Regexen Sie jedes Wort (wie Regex Solver) gegen das Wischen, teilen Sie das Wischen dann in eine Reihe von theoretisch geraden Linien auf und prüfen Sie, ob sie gerade sind und von einem Finger erzeugt wurden, der sich entlang dieser Linie bewegt ( significant_letterFunktion) (dritter Durchgang)
  • Sortieren Sie die Wörter nach der Nähe zu den geraden Linien
  • Verwenden Sie dann die Länge als Kabelbinder (länger ist besser)
  • Verwenden Sie dann den Levenshtein-Abstand als letzten Binder
  • Ausgabewort!

Code:

# Corner Sim

from math import atan, degrees, pi, factorial, cos, radians
import csv
import re
import sys

keys = {}
key_size = 1.5
for line in open("keypos.csv"):
    k, x, y = map(str.strip, line.split(","))
    keys[k] = float(x), float(y)


def deduplicate(s):
    return s[0] + "".join(s[i + 1] for i in range(len(s) - 1) if s[i + 1] != s[i])


def angle(coord1, coord2):
    x1, y1, x2, y2 = coord1 + coord2
    dx, dy = x2 - x1, y1 - y2
    t = abs(atan(dx/dy)) if dy else pi / 2
    if dx >= 0 and dy >= 0: a = t
    elif dx >= 0 and dy < 0: a = pi - t
    elif dx < 0 and dy >= 0: a = 2 * pi - t
    else: a = t + pi
    return degrees(a)


def significant_letter(swipe):
    if len(swipe) <= 2: return 0
    x1, y1, x2, y2 = keys[swipe[0]] + keys[swipe[-1]]
    m = 0 if x2 == x1 else (y2 - y1) / (x2 - x1)
    y = lambda x: m * (x - x1) + y1
    def sim_fun(x2, y2):
        try: return (x2 / m + y2 - y1 + x1 * m) / (m + 1 / m)
        except ZeroDivisionError: return x2
    dists = []
    for i, key in enumerate(swipe[1:-1]):
        sx, bx = min(x1, x2), max(x1, x2)
        pos_x = max(min(sim_fun(*keys[key]), bx), sx)
        sy, by = min(y1, y2), max(y1, y2)
        pos_y = max(min(y(pos_x), by), sy)
        keyx, keyy = keys[key]
        dist = ((keyx - pos_x) ** 2 + (keyy - pos_y) ** 2) ** 0.5
        a = angle((keyx, keyy), (pos_x, pos_y))
        if 90 <= a <= 180: a = 180 - a
        elif 180 <= a <= 270: a = a - 180
        elif 270 <= a <= 360: a = 360 - a
        if a > 45: a = 90 - a
        h = key_size / 2 / cos(radians(a))
        dists.append((max(dist - h, 0), i + 1, key))
    return sorted(dists, reverse=True)[0][0]


def closeness2(s, t):
    # https://en.wikipedia.org/wiki/Levenshtein_distance
    if s == t: return 0
    if not len(s): return len(t)
    if not len(t): return len(s)
    v0 = list(range(len(t) + 1))
    v1 = list(range(len(t) + 1))
    for i in range(len(s)):
        v1[0] = i + 1
        for j in range(len(t)):
            cost = 0 if s[i] == t[j] else 1
            v1[j + 1] = min(v1[j] + 1, v0[j + 1] + 1, v0[j] + cost)
        for j in range(len(v0)):
            v0[j] = v1[j]
    return v1[len(t)] / len(t)


def possible(swipe, w, s=False):
    m = re.match("^" + "(.*)".join("({})".format(i) for i in w) + "$", swipe)
    if not m or s:
        return bool(m)
    return closeness1(swipe, w) < 0.8


def closeness1(swipe, w):
    m = re.match("^" + "(.*)".join("({})".format(i) for i in w) + "$", swipe)
    unsigpatches = []
    groups = m.groups()
    for i in range(1, len(groups), 2):
        unsigpatches.append(groups[i - 1] + groups[i] + groups[i + 1])
    if debug: print(unsigpatches)
    sig = max(map(significant_letter, unsigpatches))
    if debug: print(sig)
    return sig


def find_closest(swipes):
    level1, level2, level3, level4 = swipes
    if debug: print("Loading words...")
    words = {deduplicate(i.lower()): i.lower() for i in open("wordlist").read().split("\n") if i[0] == level1[0] and i[-1] == level1[-1]}
    if debug: print("Done first filter (start and end):", words)
    r = re.compile("^" + ".*".join(level4) + "$")
    pos_words2 = list(filter(lambda x: bool(r.match(x)), words))
    if debug: print("Done second filter (sharpest points):", pos_words2)
    pos_words = pos_words2 or words
    pos_words = list(filter(lambda x: possible(level1, x), pos_words)) or list(filter(lambda x: possible(level1, x, s=True), pos_words))
    if debug: print("Done third filter (word regex):", pos_words)
    sorted_pos_words = sorted((closeness1(level1, x), -len(x), closeness2(level1, x), x)
                              for x in pos_words)
    if debug: print("Closeness matching (to", level2 + "):", sorted_pos_words)
    return words[sorted_pos_words[0][3]]


def get_corners(swipe):
    corners = [[swipe[0]] for i in range(4)]
    last_dir = last_char = None
    for i in range(len(swipe) - 1):
        dir = angle(keys[swipe[i]], keys[swipe[i + 1]])
        if last_dir is not None:
            d = abs(last_dir - dir)
            a_diff = min(360 - d, d)
            corners[0].append(last_char)
            if debug: print(swipe[i], dir, last_dir, a_diff, end=" ")
            if a_diff >= 55:
                if debug: print("C", end=" ")
                corners[1].append(last_char)
            if a_diff >= 65:
                if debug: print("M", end=" ")
                corners[2].append(last_char)
            if a_diff >= 80:
                if debug: print("S", end=" ")
                corners[3].append(last_char)
            if debug: print()
        last_dir, last_char = dir, swipe[i + 1]
    tostr = lambda x: deduplicate("".join(x + [swipe[-1]]).lower())
    return list(map(tostr, corners))


if __name__ == "__main__":
    debug = "-d" in sys.argv
    if debug: sys.argv.remove("-d")
    swipe = deduplicate(sys.argv[1] if len(sys.argv) > 1 else input()).lower()
    corners = get_corners(swipe)
    if debug: print(corners)
    print(find_closest(corners))

Laufen Sie mit python3 entries/corner_sim.py

Matsjoyce
quelle
Dies war eine gültige Antwort. Keine Notwendigkeit, mir die Antwort zu geben.
Optimierer
@Optimizer Nun, die Meta scheint Diskussion um Ihre Antwort zu favorisieren akzeptieren, von mir so seine Ordnung.
Matsjoyce
Nachdem ich nur diese Metadiskussion gelesen hatte, war ich mit Ihrer Entscheidung einverstanden, aber das ist auch gut (besser) :)
Optimizer