Tokatlama Tipi Dönüştürücü


27

Dizüstü bilgisayarlarda bir sonraki devrim , 2014 yılının ilk Nisan ayında SwiftKey tarafından yayımlandı . Ancak, bir kaydırma nano klonu yazan ilk kişi olmak istiyorum, ancak gerçek metin kitaplığına iyi bir kaydırma metni bulamadığım için ve ben onları bekleyemiyorum, burada soruyorum.

Görev

Kaydırma metni alan ve gerçek metin karşılığını veren bir program yazın. Örnek:

Input: hgrerhjklo
Output: hello

Kullanıcı yaptığı zaman:

görüntü tanımını buraya girin

Diğer örnekler:

Input: wertyuioiuytrtjklkjhgfd
Output: world

Input: poiuytrtyuioiugrewsasdfgbhnmkijnbg
Output: programming

Input: poiuygfdzxcvhjklkjhgres
Output: puzzles

Input: cvhjioiugfde
Output: code

Input: ghiolkjhgf
Output: golf

kurallar

  • Program, stdin veya argv üzerine bir tane 'kelime' yazacaktır.
  • Kaydırılan girişin ilk ve son harfi, gerçek kelimenin ilk ve son harfine eşit olacaktır.
  • Kullanıcının makul derecede düz çizgiler yapacağını varsayabilir, ancak bunu doğrulamak için örnek verileri kullanabilirsiniz (Örnek verileri yaptım ve son test verilerini yapacağım)
  • Belirsiz giriş için herhangi bir çıktının seçimini yapabilirsiniz, ancak tüm belirsizliği test verilerinden kaldırmaya çalışacağım
  • Bu kelime bu kelime listesinde olacaktır (ancak kaydırılmıştır). Kelime listesi geçerli dizinde olacak ve okunabilir (yeni satır ayrılmış, adlandırılmış wordlist, uzantı yok).
  • Kaydırma yalnızca küçük harfli alfabetik karakterler içerecek
  • Kullanıcı bir tuşa basarsa, kaydırma işlemi kopyalanmış karakterler içerebilir.
  • Program stdout'ta çıktı vermelidir (durum önemli değil)
  • Program 0dönüş kodu olarak geri dönmelidir.
  • Run komutunu, compile komutunu (gerekirse), adı ve kullanılacak giriş yolunu sağlamalısınız
  • Standart boşluklar uygulanır (yine de yardım etmeyebilirler)
  • Yerleşik olmayan kütüphanelere izin verilmez
  • Deterministik, golf oynamayan / anlaşılmayan çözümler tercih edildi
  • Dosya yazma, ağ oluşturma vb.
  • Kodunuz bir saniye veya daha kısa sürede çalıştırılmalıdır (kodunuz kelime başına bir kere çalıştırılır)
  • Puanlama işlemleri 4 sanal kodlu (2 gerçek) bir Intel i7 Haswell işlemcide çalıştırılıyor, böylece gerekirse bunları kullanabilirsiniz.
  • 5000 baytlık maksimum kod uzunluğu
  • Kullandığınız dilin Linux için kullanılabilen ücretsiz bir deneme sürümü olması gerekir (eğer önemliyse Arch Linux)

Kazanan Kriter

  • Kazanan en doğru çözümdür ( verilen test listesini kullanarak kontrol programı tarafından puanlandırılmıştır )
  • Popülerlik kravat kırıcıdır
  • Puanlama tablosu birkaç günde bir güncellenecek
  • Zaman aşımları ve çökmeler başarısız sayılır
  • Bu zorluk popülerliğe bağlı olarak iki hafta veya daha fazla sürecek.
  • Son puanlama, farklı, rastgele seçilmiş bir kelime listesi kullanacaktır (aynı kelime listesinden aynı uzunluk)

Diğer

Mevcut Puan Kurulları

test listesi ( günlükler ):

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

testlist2 ( günlükler ):

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 Koşusu

test listesi ( günlükler ):

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

Aferin herkese ve hgfdsasdertyuiopoiuy swertyuiopoijnhg!


"Bir Çözüm" nedir? Kodu nerede?
Doorknob



@Optimizer emin değil, diğer durumlarda, ama yaklaşık " s oiuytres bir se r es bir ler d fghui o iugfd x CGU i ug c xs bir sdfghjk l ku y " hariç üzere "paradoksal", her harf içeren l, ki iki katına çıkmadı.
es1024

1
@Optimiser Peki, gönderiminizin onu geçeceğini düşünmüştüm, ancak hemen altındaydı (biraz tweaking bunu değiştirirdi, eminim). Görünüşe göre kabul edebilirim, öyleyse ... yapmalı mıyım? Başkasının kabul etmesini isterim, ama bu kurallara uymuyor (iyi bir fikrin yoksa).
matsjoyce

Yanıtlar:


12

JavaScript, ES6, Üç Geçiş Doktoru, 112 187 235 240 241 243 ve 231 234 geçişleri

Önce tuş vuruşu sırasındaki kritik tuşları belirleyen ve ardından sırayı üç filtreden geçiren üç geçişli bir filtre:

  1. Kritik anahtarlardan ve yardım anahtarlarından gevşekçe oluşan bir RegEx. Bu, tuşların çoğu için doğru sonuç verir (yaklaşık 150)
  2. Sadece kritik anahtarlardan oluşan katı bir RegEx. Bu, fazladan 85 sekans için doğru sonuç verir
  3. Yakın cevaplar arasındaki belirsizliği bulmak için üçüncü bir filtre. Bu% 40 belirsiz durumlarda işe yarar.

kod

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()));

Kod words, bu sayfadaki tüm kelimelerin bir dizisi olan bir değişken bulunduğunu varsayar.

Buradaki işlem kodunu görün

Burada test vakalarını görün

Yukarıdaki bağlantıların her ikisi de yalnızca en son Firefox'ta (33 ve üzeri) çalışır (ES6 nedeniyle).


Evet! Mermilerle bombardıman yapıyorum. Artık doğru keypos.csvdosyayı da kullanabilirsiniz .
G /

Sorun değil, ama
tokatlamak klavye açılarımla


240 paso olağanüstü! Belirsizliklerin böyle iyi sonuçları önleyeceğini düşünmüştüm. Son test setinde bunun nasıl bir performans göstereceğini merak edeceğim.
Emil,

@Emil - Evet, ben de onu görmeyi bekliyorum.
Doktor

9

Ruby, Regex Çözücü - 30 140 176 180 182 187 ve 179 183 geçer

Daha sonra skoru çözeceğim. Klavye düzenini hesaba katmayan çok saf bir çözüm:

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]

ARGV'den girdi alır ve sonucu yazdırır. Kelime listesini yalnızca ilk ve son harfe göre filtreliyorum ve kalan tüm sözcükleri girişe karşı deniyorum (yinelenen harfleri silerek ^g.*u.*e.*s$ve "tahmin" için bir regex kullanarak ) ve varsa bunların ilkini çoklu çözümlerdir.

Gibi koş

ruby regex-solver.rb cvhjioiugfde

Bir başkası, bu adımı ilk filtre olarak tekrar kullanmaktan çekinmeyin - doğru kelimeler atmayacağına inanıyorum, bu nedenle bu ön kontrol daha iyi algoritmalar için arama alanını büyük ölçüde azaltabilir.

Düzenleme: OP'nin önerisini takiben, şimdi makul bir sezgisel gibi görünen en uzun adayları seçiyorum.

Ayrıca, yinelenen harfleri hatırlattığı için es1024'e teşekkür ederim.


Bitti. Günlük, github.com/matsjoyce/codegolf-swipe-type/blob/master/logs/… adresinde yer alıyor . Sorunun, en uzun veya başka bir şeyi seçerek geliştirilebilecek rastgele çözümlerden rastgele seçmesi olduğunu düşünüyorum.
matsjoyce

Bunun doğru ifadeyi, regex'in talep ettiğinin iki katından ziyade girişte yalnızca bir kez göründüğü gibi paradoxically, yan yana iki özdeş harfle birlikte atabileceğini düşünüyorum l.
es1024

@ es1024, ah, teşekkürler, bu algoritmayı ilk kez sandbox'a önerdiğimde, aslında bunun farkındaydım, ama dün unutmuştum. Daha sonra tamir edecek.
Martin Ender

7

C ++, Ayrık Çatal Mesafesi - 201 220 222 232 ve 232 geçer

Bana göre sorun, maalesef hesaplaması çok zor olan Fréchet Distance için çağrıda bulundu .

Sadece eğlence için, Thomas Eiter ve Heikki Mannila'nın Compcrete Fréchet Distance (1994) 'te tarif ettiği ayrık bir yaklaşımı uygulayarak soruna yaklaşmaya çalıştım .

İlk başta, listedeki girişin bir sonucu olan tüm sözcükleri filtrelemek için diğerleri ile aynı yaklaşımı kullanıyorum (aynı karakterin birden fazla sıralı oluşumunu da hesaba katar). Sonra, poligon eğrisini, harflerden harflere, her kelimenin ara noktalarına kadar doldurup giriş eğrisine karşı eşleştiriyorum. Sonunda, her mesafeyi kelimenin uzunluğuna bölerek asgari puanı alıyorum.

Şimdiye kadar, yöntem umduğum kadar iyi çalışmadı (Kod örneğini "chide" olarak tanıyor), ancak bu henüz bulamadığım hataların sonucu olabilir. Aksi takdirde, başka bir fikir fréchet mesafesinin diğer çeşitlerini kullanmaktır ("maksimum köpek tasma uzunluğu" yerine "ortalama").

Düzenleme: Şimdi, "ortalama köpek tasma uzunluğu" için bir yaklaşım kullanıyorum. Bu, her iki yol arasında, tüm mesafelerin toplamını en aza indiren ve daha sonra mesafelerin sayısına bölen sıralı bir harita bulduğum anlamına gelir.

Aynı karakter sözlük kelimesinde iki kez veya daha sık görünüyorsa, yola sadece bir düğüm koyarım.

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

Kodu clang ile derledim, ancak g++ ./thiscode.cpp -o ./thiscodeiyi çalışması gerekiyor.


1
Evet! Birisi sonunda büyük bir yağ algoritması adı olan bir çözüm ekledi! Günlüğünüz github.com/matsjoyce/codegolf-swipe-type/blob/master/logs/… adresinde
matsjoyce

1
Büyük şişman bir bilgisayar bilimi sorunu için basit bir dinamik programlama algoritması diyelim.
camelNeck

Nedense, bu giriş için başarısız görünüyor programmijng- bana veriyor pang.
Riking

Bu garip. programmingOlması gerektiği gibi alıyorum . Lütfen çizgiyi yorumlayıp // cout << thispair->first << ": " << score << endl;sonuçtaki puanları yapıştırabilir misiniz?
camelNeck

6

Python 2, Dönüşler, 224 226 230 232 ve 230 234 geçer

Bu oldukça yalındır bir yaklaşımdır:

  • Harf akışının yön değiştirdiği noktaları bulun (artı başlangıç ​​ve bitiş).
  • Tüm bu dönüm noktalarını içeren en uzun kelimeyi çıktılayın.
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

Olarak çalıştırmak

python turnarounds.py tyuytrghn

Çözüm çok sağlam değil. Tek bir yanlış tuş vuruşu başarısızlığa neden olur. Bununla birlikte, test senaryoları setinde çok az yazım hatası olmadığından, oldukça iyi performans gösterir, sadece çok uzun kelimeler seçtiği bazı durumlarda şaşırır.

Düzenleme: Verilen anahtar konum dosyasını değil, basitleştirilmiş bir düzeni kullanmak için onu biraz değiştirdim. Bu, algoritmam için kolaylaştırır, çünkü dosyada anahtarlar eşit aralıklarla yerleştirilmez. Belirsiz durumlar için bazı geçici tiebreaker'ler ekleyerek biraz daha iyi sonuçlar elde edebilirim, ancak bu, bu özel test seti için aşırı optimize edici olacaktır. Bazı başarısızlıklar, aslında belirsiz olmayan ancak basit algoritmamla yetişemediğim için kalıyor çünkü sadece 90 dereceden fazla yön değişimlerini göz önüne alıyor.


Aferin! Mevcut lider! Kaydı
matsjoyce 13:14

@ matsjoyce: Soruya, bulduğum iki yazım hatasını gösteren bir yorum ekledim. :)
Emil

Evet, teşekkürler, sadece başka bir çek vereceğim. Yine de belirsiz vakalarla ne yapacağımdan emin değilim.
matsjoyce 13:14

@ matsjoyce: Klavyedeki olası yollardan bir başkasını seçerek bazı belirsizlikler çözülebilir. Örneğin brats, 'bgrdsasdrtrds'karıştırılamayan olabilir breasts. Ancak, onu olduğu gibi bıraksan da sakıncası olmaz.
Emil

Doğru, işe yarayacak. Ben sadece bu setin 'optimal' yapılması durumunda ve son skor setinin pek fazla incelemeye
girmediğinden endişeleniyorum

6

PHP, Yön Denetleyicisi, 203 213 216 229 231 ve 229 233 geçer

Bu harfler (Martin yaptıklarını girdi tüm mevcut kelimelerin bir listesini elde etmek için sözlükten basit pas ile başlar burada ) ve ayrıca sadece kontrol l.*yerinel.*l.* zaman lyineleniyor.

Buradaki en uzun kelime bir değişkene kaydedilir.

Kaydırma yönünü değiştirdiği noktadaki tuşlara (x ya da y'de ya da + / 0 ila - ya da - / 0 ila +) bir başka kontrol yapılır. Olası kelimelerin listesi bu karakter listesine göre kontrol edilir ve eşleşmeyen kelimeler silinir. Bu yaklaşım, kaydırmanın doğru olması için keskin dönüşlere dayanır.

Kalan en uzun sözcük çıkarılır veya hiç kelime bırakılmazsa, daha önce gelen en uzun sözcük çıkar.

Düzenleme: Tüm karakterleri, kontrol etmek için olası değerler yönünde yönde bir değişikliğe yol açan yatay bir çizgi içine eklendi.

PHP 5.4 gereklidir (veya hepsini [..]ile değiştirin 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;

@ matsjoyce Soruyu okurken bu kurşun noktasını kaçırdım. Kod şimdi pozisyonları kullanırkeypos.csv
es1024

@ es1024 250 test durumunun parçası olmasa da, kelime listesi üç ardışık harfe sahip 238 kelime içeriyor (şu ana kadar kontrol ettiğim tek şey biten kelimeler. sss) - Yinelenen elemenin bunları yakalayacağını sanmıyorum.
Martin Ender


3

Python 3, Köşe Simülatörü, 241 ve 240 geçer

Algoritma:

  • Deduplicate (aynı karakterdeki ardışık işlemleri kaldır) girişi ve kelime listesindeki tüm kelimeleri (orijinal kelimeleri geri almak için bir sözlük kullanarak)
  • Kaydırmanın başlangıcı ve bitişiyle başlamayan ve bitmeyen tüm kelimeleri kaldırın (ilk geçiş)
  • Tüm köşelerden 80 dereceden büyük bir regex yapın, sonra buna uymayan tüm sözcükleri kaldırın (ikinci geçiş)
  • Kaydırmaya karşı her kelimeyi (Regex Çözücü gibi) düzenleyin, daha sonra kaydırmayı teorik olarak düz çizgiler dizisine bölün ve düz olup olmadığını ve bu satır boyunca hareket eden bir parmak tarafından üretilmiş olup olmadıklarını kontrol edin (significant_letter işlev) (üçüncü geçiş)
  • Kelimeleri düz çizgilere yakınlığa göre sıralayın
  • O zaman uzunluğu kırıcı olarak kullanın (daha uzun daha iyidir)
  • Daha sonra son kravat kırıcı olarak Levenshtein mesafesini kullanın.
  • Çıktı kelimesi!

Kod:

# 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))

İle koş python3 entries/corner_sim.py


Bu geçerli bir cevaptı. Cevabı benim yapmanıza gerek yok.
Doktor

@Optimizer Peki, meta tartışma cevabınızı kabul etmeyi tercih ediyor gibi görünüyor, bu yüzden benim için iyi.
matsjoyce

Sadece bu meta tartışmayı okuduktan sonra kararın için iyiydim, ama bu da iyi (daha iyi) :)
Doktor
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.