Chuyển đổi kiểu vuốt


27

Cuộc cách mạng tiếp theo trong việc gõ trên máy tính xách tay đã được SwiftKey phát hành vào ngày đầu tiên của tháng 4 năm 2014 . Tuy nhiên, tôi muốn trở thành người đầu tiên viết bản sao nano vuốt, nhưng, vì tôi không thể tìm thấy một văn bản vuốt tốt đến thư viện văn bản thực và tôi không thể chờ họ, tôi đang hỏi ở đây.

Bài tập

Viết chương trình lấy văn bản vuốt và xuất văn bản tương đương. Thí dụ:

Input: hgrerhjklo
Output: hello

Khi người dùng thực hiện:

nhập mô tả hình ảnh ở đây

Những ví dụ khác:

Input: wertyuioiuytrtjklkjhgfd
Output: world

Input: poiuytrtyuioiugrewsasdfgbhnmkijnbg
Output: programming

Input: poiuygfdzxcvhjklkjhgres
Output: puzzles

Input: cvhjioiugfde
Output: code

Input: ghiolkjhgf
Output: golf

Quy tắc

  • Chương trình sẽ lấy một từ 'swiped' trên stdin hoặc argv
  • Chữ cái đầu tiên và cuối cùng của đầu vào được vuốt sẽ tương đương với chữ cái đầu tiên và cuối cùng của từ thực sự
  • Bạn có thể giả sử người dùng sẽ tạo các đường thẳng hợp lý, nhưng bạn có thể sử dụng dữ liệu mẫu để xác minh điều này (Tôi đã tạo dữ liệu mẫu và tôi sẽ tạo dữ liệu thử nghiệm cuối cùng)
  • Đối với đầu vào không rõ ràng, bạn có thể chọn đầu ra, nhưng tôi sẽ cố gắng xóa tất cả sự mơ hồ khỏi dữ liệu thử nghiệm
  • Từ này sẽ nằm trong danh sách từ này (nhưng đã vuốt). Danh sách từ sẽ nằm trong thư mục hiện tại và có thể được đọc (dòng mới được phân tách, sẽ được đặt tên wordlist, không có phần mở rộng).
  • Việc vuốt sẽ chỉ chứa các ký tự chữ cái viết thường
  • Việc vuốt có thể chứa các ký tự trùng lặp, nếu người dùng tạm dừng trên một phím
  • Chương trình phải xuất ra thiết bị xuất chuẩn (trường hợp không quan trọng)
  • Chương trình phải trả về 0làm mã trả về
  • Bạn phải cung cấp lệnh chạy, lệnh biên dịch (nếu cần), tên và đường dẫn đầu vào nào sẽ sử dụng
  • Áp dụng sơ hở tiêu chuẩn (mặc dù chúng có thể không giúp đỡ)
  • Không cho phép thư viện không dựng sẵn
  • Các giải pháp xác định, không chơi gôn / che giấu được ưa thích
  • Không có tập tin viết, mạng, vv
  • Mã của bạn phải chạy trong một giây hoặc ít hơn (mã của bạn được chạy một lần cho mỗi từ)
  • Việc chạy tính điểm được chạy trên bộ xử lý Intel i7 Haswell, với 4 mã ảo (2 mã thực), vì vậy bạn có thể sử dụng các luồng nếu bạn phải
  • Độ dài mã tối đa 5000 byte
  • Ngôn ngữ bạn sử dụng phải có phiên bản miễn phí (không dùng thử) dành cho Linux (Arch Linux, nếu có vấn đề)

Tiêu chí chiến thắng

  • Người chiến thắng là giải pháp chính xác nhất (được ghi bởi chương trình kiểm soát , sử dụng danh sách kiểm tra được cung cấp)
  • Phổ biến là máy cắt cà vạt
  • Bảng điểm sẽ được cập nhật vài ngày một lần
  • Thời gian chờ và sự cố được tính là thất bại
  • Thử thách này sẽ diễn ra trong hai tuần trở lên, tùy thuộc vào mức độ phổ biến
  • Điểm cuối cùng sẽ sử dụng một danh sách các từ được chọn ngẫu nhiên khác nhau (cùng độ dài, từ cùng một danh sách từ)

Khác

Bảng điểm hiện tại

danh sách kiểm tra ( nhật ký ):

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 ( nhật ký ):

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

Chạy cuối cùng

danh sách kiểm tra ( nhật ký ):

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

Làm tốt cho tất cả mọi người và hgfdsasdertyuiopoiuy swertyuiopoijnhg!


"Giải pháp" là gì? Mã của nó ở đâu?
Doorknob



@Optimizer Không chắc chắn về các trường hợp khác, nhưng " p oiuytres a se r es a s d fghui o iugfd x cgu i ug c xs a sdfghjk l ku y " chứa mọi chữ cái "nghịch lý", ngoại trừ l, mà không được nhân đôi.
es1024

1
@Optimiser Vâng, tôi nghĩ rằng trình của bạn sẽ đánh bại nó, nhưng nó ở ngay bên dưới (một chút điều chỉnh sẽ thay đổi điều đó, tôi chắc chắn). Có vẻ như tôi có thể chấp nhận nó, vì vậy ... tôi có nên (tôi không xuất hiện để nhận đại diện từ việc chấp nhận nó)? Tôi muốn chấp nhận người khác, nhưng điều đó không tuân theo các quy tắc (trừ khi bạn có ý tưởng hay).
matjoyce

Câu trả lời:


12

JavaScript, ES6, Trình tối ưu hóa ba lượt, 112 187 235 240 241 243 và 231 234 lượt

Một bộ lọc ba lần đầu tiên tìm ra các phím quan trọng trong chuỗi phím tắt và sau đó chuyển chuỗi qua ba bộ lọc:

  1. Một RegEx lỏng lẻo hình thành từ các phím quan trọng và các phím trợ giúp. Điều này cho kết quả chính xác cho phần lớn các khóa (khoảng 150)
  2. Một RegEx nghiêm ngặt được tạo ra từ các khóa quan trọng. Điều này cho kết quả chính xác cho thêm 85 chuỗi
  3. Một bộ lọc thứ ba để tìm ra sự mơ hồ giữa các câu trả lời gần gũi. Điều này làm việc cho 40% trường hợp mơ hồ.

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

Mã giả định một biến được gọi wordslà hiện diện, đó là một mảng của tất cả các từ trong trang này

Xem mã hoạt động tại đây

Xem các trường hợp thử nghiệm trong hành động ở đây

Cả hai liên kết trên chỉ hoạt động trên một Firefox mới nhất (33 trở lên) (do ES6).


Vâng Tôi đang bắn ra với vỏ. Bạn cũng có thể sử dụng các keypos.csvtập tin thích hợp bây giờ. Các chức năng IO có sẵn được liệt kê tại developer.mozilla.org/en-US/docs/Mozilla/Projects/SpiderMonkey/
Kẻ

Không sao, nhưng các thao tác vuốt được thực hiện với các góc bàn phím của tôi, vì vậy đó là lựa chọn của bạn (mặc dù dường như không ảnh hưởng đến điểm số của bạn!)
matjoyce


240 đường chuyền là xuất sắc! Tôi đã có thể nghĩ rằng sự mơ hồ sẽ ngăn chặn kết quả tốt như vậy. Tôi sẽ tò mò làm thế nào điều này sẽ thực hiện trên bộ thử nghiệm cuối cùng.
Emil

@Emil - Vâng, tôi cũng đang chờ để thấy điều đó.
Trình tối ưu hóa

9

Ruby, Bộ giải Regex - 30 140 176 180 182 187 và 179 183 lượt

Tôi sẽ tìm ra điểm số sau. Đây là giải pháp rất ngây thơ không tính đến bố cục bàn phí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]

Nó nhận đầu vào từ ARGV và in kết quả. Tôi chỉ lọc danh sách từ bằng chữ cái đầu tiên và chữ cái cuối cùng và chúng tôi đang thử tất cả các từ còn lại so với đầu vào (loại bỏ các chữ cái trùng lặp và sử dụng một biểu ^g.*u.*e.*s$thức chính như "đoán") và trả lại từ đầu tiên nếu có là nhiều giải pháp.

Chạy nó như

ruby regex-solver.rb cvhjioiugfde

Bất cứ ai khác, vui lòng sử dụng lại bước này như một bộ lọc đầu tiên - tôi tin rằng nó sẽ không đưa ra bất kỳ từ nào chính xác, vì vậy kiểm tra sơ bộ này có thể giảm đáng kể không gian tìm kiếm cho các thuật toán tốt hơn.

Chỉnh sửa: Theo đề xuất của OP, hiện tôi đang chọn ứng viên dài nhất, có vẻ là một heuristic đàng hoàng.

Cũng cảm ơn es1024 đã nhắc nhở tôi về các chữ cái trùng lặp.


Làm xong. Nhật ký của bạn tại github.com/matsjoyce/codegolf-swipe-type/blob/master/logs/ nam Tôi nghĩ rằng vấn đề là nó chọn ngẫu nhiên từ các giải pháp có thể, có thể được cải thiện bằng cách chọn dài nhất hoặc thứ gì khác.
matjoyce

Tôi nghĩ rằng điều này có thể loại bỏ tất cả các từ chính xác với hai chữ cái giống nhau cạnh nhau, chẳng hạn như paradoxically, lchỉ xuất hiện một lần trong đầu vào, thay vì hai lần theo yêu cầu của biểu thức chính quy.
es1024

@ es1024, ah cảm ơn, khi lần đầu tiên tôi đề xuất thuật toán này trong hộp cát, tôi thực sự đã nhận ra điều đó, nhưng đã quên nó ngày hôm qua. Sẽ sửa sau.
Martin Ender

7

C ++, Khoảng cách Fréchet rời rạc - 201 220 222 232 và 232 đi qua

Đối với tôi, vấn đề rất được gọi là Khoảng cách Fréchet , thật không may là rất khó tính toán.

Để giải trí, tôi đã cố gắng tiếp cận vấn đề bằng cách thực hiện một xấp xỉ rời rạc được mô tả bởi Thomas Eiter và Heikki Mannila trong Tính toán khoảng cách Fréchet rời rạc (1994).

Lúc đầu, tôi đang sử dụng cùng một cách tiếp cận như các cách tiếp cận khác để lọc tất cả các từ trong danh sách là các phần tiếp theo của đầu vào (cũng chiếm nhiều lần xuất hiện liên tiếp của cùng một ký tự). Sau đó, tôi điền đường cong đa giác từ chữ cái này sang chữ cái khác của mỗi từ với các điểm trung gian và khớp nó với đường cong đầu vào. Cuối cùng, tôi chia mọi khoảng cách cho độ dài của từ và lấy điểm tối thiểu.

Cho đến nay, phương thức này không hoạt động tốt như tôi mong đợi (Nó nhận ra ví dụ mã là "chide"), nhưng đây có thể chỉ là kết quả của các lỗi mà tôi chưa tìm thấy. Khác, một ý tưởng khác sẽ là sử dụng các biến thể khác của khoảng cách fréchet ("trung bình" thay vì "chiều dài dây xích chó tối đa").

Chỉnh sửa: Bây giờ, tôi đang sử dụng một xấp xỉ với "chiều dài dây xích chó trung bình". Điều này có nghĩa là tôi đang tìm một ánh xạ có thứ tự giữa cả hai đường dẫn để giảm thiểu tổng của tất cả các khoảng cách và sau đó chia nó cho số khoảng cách.

Nếu cùng một ký tự xuất hiện hai lần hoặc nhiều hơn trong từ điển, tôi chỉ đặt một nút trong đường dẫn.

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

Tôi đã biên dịch mã bằng tiếng kêu, nhưng g++ ./thiscode.cpp -o ./thiscodecũng sẽ hoạt động tốt.


1
Vâng! Ai đó cuối cùng đã thêm một giải pháp với một tên thuật toán lớn chất béo! Nhật ký của bạn có tại github.com/matsjoyce/codegolf-swipe-type/blob/master/logs/
trộm

1
Chúng ta hãy gọi nó là một thuật toán lập trình động đơn giản cho một vấn đề khoa học máy tính lớn.
camelNeck

Đối với một số lý do, điều này dường như thất bại cho đầu vào của programmijng- nó mang lại cho tôi pang.
Đạp xe

Điều đó thật lạ. Tôi đang nhận được programmingnhư nó nên. Bạn có thể vui lòng bỏ dòng // cout << thispair->first << ": " << score << endl;và dán điểm kết quả?
camelNeck

6

Python 2, Turnaround, 224 226 230 232 và 230 234 vượt qua

Đây là một cách tiếp cận khá thẳng:

  • Tìm các điểm mà dòng chữ thay đổi hướng (cộng với bắt đầu và kết thúc).
  • Xuất ra từ dài nhất bao gồm tất cả các bước ngoặt này.
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

Chạy như

python turnarounds.py tyuytrghn

Giải pháp không mạnh lắm. Một lần nhấn phím sai sẽ làm cho nó thất bại. Tuy nhiên, vì tập hợp các trường hợp kiểm thử có rất ít lỗi chính tả, nó hoạt động khá tốt, chỉ bị nhầm lẫn trong một số trường hợp nó chọn các từ quá dài.

Chỉnh sửa: Tôi đã thay đổi một chút để không sử dụng tệp vị trí khóa được cung cấp mà bố trí đơn giản hóa. Điều này làm cho thuật toán của tôi dễ dàng hơn vì trong tệp, các khóa không cách đều nhau. Tôi có thể đạt được kết quả thậm chí tốt hơn một chút bằng cách bao gồm một số công cụ bẻ khóa đặc biệt cho các trường hợp mơ hồ, nhưng điều đó sẽ tối ưu hóa quá mức cho bộ thử nghiệm cụ thể này. Một số thất bại vẫn không thực sự mơ hồ nhưng tôi không nắm bắt được bằng thuật toán đơn giản của mình vì nó chỉ xem xét sự thay đổi hướng hơn 90 độ.


Làm tốt! Lãnh đạo hiện tại! Nếu bạn muốn xem nhật ký, hãy goto github.com/matsjoyce/codegolf-swipe-type/blob/master/logs/
Lỗi

@matsjoyce: Tôi đã thêm một nhận xét cho câu hỏi chỉ ra hai lỗi chính tả mà tôi nghĩ tôi đã tìm thấy. :)
Emil

Vâng, cảm ơn, tôi chỉ cho nó một tấm séc khác. Tôi không chắc chắn phải làm gì với các trường hợp mơ hồ, mặc dù.
matjoyce

@matsjoyce: Một số sự mơ hồ có thể được giải quyết bằng cách chọn một đường dẫn khác có thể có trên bàn phím. Ví dụ bratscó thể là 'bgrdsasdrtrds'không thể nhầm lẫn với breasts. Tuy nhiên, tôi cũng không phiền nếu bạn để nó như vậy.
Emil

Đúng, điều đó sẽ làm việc. Tôi chỉ lo lắng rằng nếu bộ này được thực hiện quá 'tối ưu' và bộ ghi điểm cuối cùng sẽ không được xem xét kỹ lưỡng, một số giải pháp có thể không thực hiện tốt
matjoyce

6

PHP, Trình kiểm tra hướng, 203 213 216 229 231 và 229 233 lượt

Điều này bắt đầu bằng một cách đơn giản thông qua từ điển để có được danh sách các từ có chữ cái hiện diện trong đầu vào (những gì Martin đã làm ở đây ) và cũng chỉ kiểm tra l.*thay vì l.*l.*khi nàol được sao chép.

Từ dài nhất ở đây sau đó được lưu trong một biến.

Sau đó, một kiểm tra khác được thực hiện, dựa trên các phím tại các điểm mà thao tác vuốt thay đổi hướng (+ / 0 thành - hoặc - / 0 thành +, theo x hoặc y). Danh sách các từ có thể được kiểm tra đối với danh sách các ký tự này và các từ không khớp sẽ bị loại bỏ. Cách tiếp cận này dựa trên các bước ngoặt sắc nét để chính xác.

Từ còn lại dài nhất được xuất ra, hoặc nếu không còn từ nào, từ dài nhất từ ​​trước đó được xuất ra thay thế.

Chỉnh sửa: Đã thêm tất cả các ký tự trong một hàng ngang dẫn đến thay đổi hướng càng tốt các giá trị có thể để kiểm tra.

PHP 5.4 là bắt buộc (hoặc thay thế tất cả [..]bằng 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 Bỏ lỡ điểm đạn trong khi đọc qua câu hỏi. Mã hiện sử dụng các vị trí từkeypos.csv
es1024

@ es1024 Mặc dù không phải là một phần của 250 trường hợp kiểm tra, danh sách từ có chứa 238 từ có ba chữ cái liên tiếp (cho đến nay, tất cả những gì tôi đã kiểm tra là những từ kết thúc sss) - Tôi không nghĩ rằng việc loại bỏ trùng lặp của bạn sẽ bắt được những từ đó.
Martin Ender

Nếu bạn cần nó, nhật ký của bạn tại github.com/matsjoyce/codegolf-swipe-type/blob/master/logs/
Kẻ

3

Python 3, Corner Simulator, 241 và 240 lượt

Thuật toán:

  • Không trùng lặp (loại bỏ các lần chạy liên tiếp của cùng một ký tự) đầu vào và tất cả các từ trong danh sách từ (sử dụng từ điển để lấy lại các từ gốc)
  • Xóa tất cả các từ không bắt đầu và kết thúc bằng bắt đầu và kết thúc vuốt (vượt qua đầu tiên)
  • Tạo một regex trong tất cả các góc lớn hơn 80 độ, sau đó xóa tất cả các từ không khớp với điều này (vượt qua lần thứ hai)
  • Regex từng từ (như Regex Solver) chống lại thao tác vuốt, sau đó chia phần vuốt thành một chuỗi các đường thẳng theo lý thuyết và kiểm tra xem chúng có thẳng không và có thể được tạo ra bởi một ngón tay di chuyển dọc theo dòng đó ( significant_letterchức năng) (vượt qua lần thứ ba)
  • Sắp xếp các từ theo sự gần gũi với các đường thẳng
  • Sau đó sử dụng chiều dài như một bộ ngắt kết nối (lâu hơn là tốt hơn)
  • Sau đó sử dụng khoảng cách Levenshtein làm bộ ngắt kết nối cuối cùng
  • Từ đầu ra!

Mã số:

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

Chạy với python3 entries/corner_sim.py


Đây là một câu trả lời hợp lệ. Không cần phải làm cho tôi câu trả lời.
Tối ưu hóa

@Optimizer Vâng, các cuộc thảo luận meta dường như ủng hộ việc chấp nhận câu trả lời của bạn, vì vậy nó ổn đối với tôi.
matjoyce

Sau khi đọc chỉ thảo luận về meta đó, tôi thấy ổn với quyết định của bạn, nhưng điều này cũng tốt (tốt hơn) :)
Trình tối ưu hóa
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.