Проведіть конвертер типів


27

Наступна революція у наборі текстів на ноутбуках була випущена першого квітня 2014 року SwiftKey . Однак я хочу бути першою людиною, яка написала розмахуючий нано-клон, але, оскільки я не можу знайти хорошого перенесення тексту до бібліотеки реального тексту, і я не можу їх чекати, я прошу тут.

Завдання

Напишіть програму, яка бере шрифт тексту та виводить еквівалент реального тексту. Приклад:

Input: hgrerhjklo
Output: hello

Коли користувач робить:

введіть тут опис зображення

Інші приклади:

Input: wertyuioiuytrtjklkjhgfd
Output: world

Input: poiuytrtyuioiugrewsasdfgbhnmkijnbg
Output: programming

Input: poiuygfdzxcvhjklkjhgres
Output: puzzles

Input: cvhjioiugfde
Output: code

Input: ghiolkjhgf
Output: golf

Правила

  • Програма займе одне промальоване слово на stdin чи argv
  • Перша і остання літера прокладеного введення буде дорівнювати першій і останній літери справжнього слова
  • Ви можете припустити, що користувач зробить досить прямі лінії, але ви можете використовувати зразки даних для підтвердження цього (я зробив вибіркові дані, і я зроблю остаточні дані тесту)
  • Для неоднозначного введення можна зробити вибір будь-якого виводу, але я спробую усунути всю неоднозначність з даних тесту
  • Це слово буде в цьому списку слів (але проведіть пальцем). Список слів буде у поточному каталозі і може бути прочитаний (розділений новий рядок, буде названо wordlist, без розширення).
  • Проведення пальцем буде містити лише рядкові букви
  • Проведення пальцем може містити дублюються символи, якщо користувач робить паузу на клавіші
  • Програма повинна виводити на stdout (випадок не має значення)
  • Програма повинна повернутися 0як код повернення
  • Ви повинні надати команду run, команду компіляції (за потреби), назву та шлях введення для використання
  • Застосовуються стандартні лазівки (хоча вони можуть не допомогти)
  • Не дозволяється використовувати вбудовані бібліотеки
  • Переважними є детерміновані рішення, що не мають гольфу та затуманені
  • Немає написання файлів, створення мереж тощо.
  • Ваш код повинен працювати протягом однієї секунди або менше (ваш код запускається один раз на слово)
  • Виконання балів проводиться на процесорі Intel i7 Haswell з 4 віртуальними кодами (2 реальних), тож ви можете використовувати теми, якщо вам доведеться
  • Максимальна довжина коду 5000 байт
  • Мова, якою ви користуєтесь, повинна мати безкоштовну (не пробну) версію, доступну для Linux (Arch Linux, якщо це має значення)

Критерій виграшу

  • Переможець - це найточніше рішення (набране контрольною програмою , використовуючи наданий тестовий список)
  • Популярність - краватка краватки
  • Таблиця балів оновлюватиметься кожні кілька днів
  • Часи очікування та збої вважаються помилками
  • Це завдання триватиме два тижні або більше, залежно від популярності
  • Підсумкове підрахунок використовує інший, випадковим чином обраний список слів (однакова довжина, з одного списку слів)

Інший

Поточні таблиці оцінок

тестовий список ( журнали ):

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

test2 ( журнали ):

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

Фінальний запуск

тестовий список ( журнали ):

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

Молодці всім і hgfdsasdertyuiopoiuy swertyuiopoijnhg!


Що таке "Рішення"? Де його код?
Дверна ручка



@Optimizer Не впевнений в інших випадках, але " p oiuytres a se r es a s d fghui o iugfd x cgu i ug c xs a sdfghjk l ku y " містить кожну букву "парадоксально", щоб, крім l, яка не подвоюється.
es1024

1
@Optimiser Добре, я думав, що ваше повідомлення переможе його, але воно було трохи нижче (трохи налаштування змінило б це, я впевнений). Здається, я можу це прийняти, так що ... чи повинен я (я, здається, не отримую реп. Від його прийняття)? Я хотів би прийняти чужі, але це не дотримання правил (якщо ви не маєте гарної ідеї).
matsjoyce

Відповіді:


12

JavaScript, ES6, трипропускний оптимізатор, 112 187 235 240 241 243 та 231 234 проходи

Трипропускний фільтр, який спочатку визначає критичні клавіші в послідовності натискань клавіш, а потім передає послідовність через три фільтри:

  1. Слабо сформований RegEx з критичних клавіш та допоміжних клавіш. Це дає правильний результат для більшості клавіш (близько 150)
  2. Суворий RegEx зроблений із лише критичних клавіш. Це дає правильний результат для додаткових 85 послідовностей
  3. Третій фільтр для виявлення неоднозначності серед близьких відповідей. Це працює на 40% неоднозначних випадків.

Код

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

Код передбачає наявність змінної, яка називається wordsприсутня, яка є масивом усіх слів з цієї сторінки

Дивіться код у дії тут

Дивіться тестові приклади, що діють тут

Обидві вищезгадані посилання працюють лише на останньому Firefox (33 і вище) (завдяки ES6).


Так! Я обстрілюю снаряди. Ви також можете скористатися належним keypos.csvфайлом зараз. Функції IO, доступні, перераховані на developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/…
matsjoyce

Це добре, але крок зроблений з моїми кутами клавіатури, тому це ваш вибір (однак, здається, це не вплинуло на ваш рахунок!)
matsjoyce


240 пропусків видатні! Я б подумав, що двозначності завадять таким хорошим результатам. Мені буде цікаво, як це буде працювати на фінальному тестовому наборі.
Еміль

@Emil - Так, я чекаю, що теж побачу це.
Оптимізатор

9

Ruby, Regex Solver - 30 140 176 180 182 187 та 179 183 пас

Я буду рахувати рахунок пізніше. Ось дуже наївне рішення, яке не враховує розкладку клавіатури:

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 і друкує результат. Я просто фільтрую список слів за першою та останньою літерою, і з них я намагаюсь усі решта слів проти введення (усуваючи дублікати букв і використовуючи регулярний вираз, як ^g.*u.*e.*s$для "здогадуюсь") і повертаю перше з тих, якщо є є декількома рішеннями.

Виконайте це як

ruby regex-solver.rb cvhjioiugfde

Хто-небудь інший, не соромтесь використовувати цей крок як перший фільтр - я вважаю, що він не викине жодних правильних слів, тому ця попередня перевірка може значно зменшити простір пошуку для кращих алгоритмів.

Редагувати: Після пропозиції ОП, я зараз вибираю найдовшого з кандидатів, що здається гідним евристичним.

Також дякую es1024, що нагадав мені про дублюючі літери.


Зроблено. Ваш журнал знаходиться за адресою github.com/matsjoyce/codegolf-swipe-type/blob/master/logs/…. Я думаю, що проблема полягає в тому, що він вибирає з можливих рішень випадковим чином, які можна вдосконалити, вибравши найдовший або щось інше.
matsjoyce

Я думаю, що це може викинути всі правильні слова з двома однаковими літерами поруч, такі як, наприклад paradoxically, lвони з'являться лише один раз у вводі, а не вдвічі, ніж того вимагає регулярний вираз.
es1024

@ es1024, а, дякую, коли я вперше запропонував цей алгоритм ще в пісочниці, я насправді про це знав, але вчора забув про це. Виправимо пізніше.
Мартін Ендер

7

C ++, дискретна відстань фреше - 201 220 222 232 та 232 проходи

Мені ця проблема дуже викликала відстань Фреше, яку, на жаль, важко обчислити.

Для задоволення я спробував підійти до проблеми, здійснивши дискретний наближення, описаний Томасом Ейтером та Хейкі Манілою в обчислювальній дискретній відстані Фреше (1994).

Спочатку я використовую той самий підхід, що й інші, для фільтрації всіх слів у списку, які є подальшими вхідними даними (також враховуючи кілька послідовних випадків одного і того ж символу). Потім я заповнюю криву багатокутника від літери до літери кожного слова проміжними точками і порівнюю його з вхідною кривою. Нарешті, я ділю кожну відстань на довжину слова і беру мінімальний бал.

Поки що метод працює не так добре, як я сподівався (він визнає приклад коду як "chide"), але це може бути просто результатом помилок, яких я ще не знайшов. Інше, ще одна ідея полягала б у використанні інших варіацій відстані фреш ("середній" замість "максимальної довжини собачого повідка").

Редагувати: Тепер я використовую наближення до "середньої довжини повідка собаки". Це означає, що я знаходжу впорядковане відображення між обома шляхами, що мінімізує суму всіх відстаней і пізніше поділяю його на кількість відстаней.

Якщо один і той же символ з’являється в словниковому слові двічі або частіше, я кладу лише один вузол на шляху.

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

Я склав код з clang, але g++ ./thiscode.cpp -o ./thiscodeтакож повинен добре працювати.


1
Так! Хтось нарешті додав рішення з великою назвою алгоритму жиру! Ваш журнал знаходиться на веб-
matsjoyce

1
Давайте назвемо це простим алгоритмом динамічного програмування для великої жирової проблеми інформатики.
camelNeck

Чомусь це здається невдалим для введення даних programmijng- це дає мені pang.
Рікінг

Це дивно. Я стаю так, programmingяк слід. Не могли б Ви прокоментувати рядок // cout << thispair->first << ": " << score << endl;і вставити отримані партитури?
camelNeck

6

Python 2, Turnarounds, 224 226 230 232 та 230 234 проходи

Це досить прямий підхід:

  • Знайдіть точки, де потік літер змінює напрямок (плюс початок і кінець).
  • Виведіть найдовше слово, яке включає всі ці переломні моменти.
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

Виконати як

python turnarounds.py tyuytrghn

Рішення не дуже надійне. Одне неправильне натискання клавіші призведе до невдачі. Однак, оскільки набір тестових випадків має дуже мало помилок, він виконує досить непогано, лише плутаючись у деяких випадках, коли вибирає занадто довгі слова.

Редагувати: я трохи змінив його, щоб не використовувати наданий файл ключового положення, а спрощену компоновку. Це полегшує мій алгоритм, оскільки у файлі ключі не рівномірно розташовані. Я можу досягти навіть дещо кращих результатів, включивши кілька спеціальних розривів для неоднозначних випадків, але це буде надмірно оптимізовано для цього конкретного тестового набору. Залишаються деякі помилки, які насправді не є неоднозначними, але я не догадуюсь свого простого алгоритму, оскільки він враховує лише зміни напрямку понад 90 градусів.


Молодці! Нинішній керівник! Якщо ви хочете побачити журнал, перейдіть на сторінку github.com/matsjoyce/codegolf-swipe-type/blob/master/logs/…
matsjoyce

@matsjoyce: Я додав коментар до питання, вказуючи на дві орфографічні помилки, які, я думаю, знайшов. :)
Еміль

Так, дякую, я лише даю йому ще одну перевірку. Я не зовсім впевнений, що робити з неоднозначними випадками.
matsjoyce

@matsjoyce: Деякі неясності можна було б вирішити, вибравши інший із можливих шляхів через клавіатуру. Наприклад, bratsте, з чим 'bgrdsasdrtrds'не можна переплутати breasts. Однак я також не заперечував би, якби ти залишив його таким, яким він є.
Еміль

Щоправда, це спрацювало б. Я просто переживаю, що якщо цей набір буде зроблений занадто "оптимальним", а підсумковий набір балів не буде піддаватися великій ретельній увазі, деякі рішення можуть не так добре працювати
matsjoyce

6

PHP, напрямок перевірки, 203 213 216 229 231 та 229 233 проходи

Це починається з простим проходом через словник , щоб отримати список слів, букви всі присутні на вході (то , що зробив Мартін тут ), а також перевіряти тільки l.*замість того , щоб, l.*l.*коли lдублюються.

Тут найдовше слово зберігається у змінній.

Потім робиться ще одна перевірка, ґрунтуючись на клавішах у точках, де проведіть пальцем зміну напрямку (+ / 0 до - або - / 0 до +, або x, або y). Список можливих слів перевіряється відповідно до цього списку символів, а слова, які не відповідають, видаляються. Цей підхід покладається на різкі повороти при проведенні, щоб бути точним.

Виводиться найдовше слово, що залишилось, або якщо слова не залишилося, замість цього виводиться найдовше слово з попереднього.

Редагувати: Додано всі символи в горизонтальній лінії, що призводить до зміни напрямку, як можливих значень, на які потрібно перевірити.

Потрібен PHP 5.4 (або замінити все [..]на 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 Пропустив цю крапку під час читання запитання. Код тепер використовує позиції зkeypos.csv
es1024

@ es1024 Хоча цей список не входить до 250 тестових випадків, список слів містить 238 слів із трьома послідовними літерами (поки що все, що я перевірив, - це слова, що закінчуються sss) - я не думаю, що їхнє усунення дублікатів би зачепило їх.
Мартін Ендер

Якщо вам це потрібно, ваші журнали за адресою github.com/matsjoyce/codegolf-swipe-type/blob/master/logs/…
matsjoyce

3

Python 3, Corner Simulator, 241 та 240 проходів

Алгоритм:

  • Повторне копіювання (видалення послідовних циклів одного і того ж символу) введення та всіх слів у списку слів (за допомогою словника для повернення оригінальних слів)
  • Видаліть усі слова, які не починаються і не закінчуються пальцем початку та кінця (перший пропуск)
  • Зробіть регулярний вираз з усіх кутів більше 80 градусів, потім видаліть усі слова, які не відповідають цьому (другий прохід)
  • Повторно вирівнюйте кожне слово (як Regex Solver) проти пальця, а потім розділіть пальцем на ряд теоретично прямих ліній і перевірте, чи вони прямі та чи могли вони бути отримані пальцем, що рухається по цій лінії ( significant_letterфункції) (третій прохід)
  • Сортуйте слова за близькістю до прямих
  • Потім використовуйте довжину в якості розриву краватки (довше краще)
  • Потім використовуйте відстань Левенштейна в якості остаточного вимикача
  • Вивідне слово!

Код:

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

Бігайте з python3 entries/corner_sim.py


Це була правильна відповідь. Не потрібно робити мою відповідь.
Оптимізатор

@Optimizer Добре, мета- дискусія, здається, сприяє прийняттю вашої відповіді, тому я штрафую мене.
matsjoyce

Прочитавши лише мета-дискусію, я був у порядку з вашим рішенням, але це теж добре (краще) :)
Оптимізатор
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.