Минимальные нажатия клавиш, необходимые для ввода текста


45

Все мы знаем, что программисты, как правило, ленивы. Чтобы максимизировать свое свободное время, вы решили написать программу, которая выводит минимальное количество нажатий клавиш для текста, вводимого в нее.

Ввод : текст, который необходимо преобразовать в нажатия клавиш. Вы можете решить, как вводить текст (STDIN / чтение из файла, представленного в аргументах)

Вывод : необходимые действия в следующем формате:

  • Они должны быть пронумерованы
  • Hэто: нажатие клавиши и немедленное ее отпускание
  • PRess: Нажатие клавиши и не отпускание ее (это никогда не будет оптимальным, когда клавиша Rотпущена при следующем нажатии клавиши)
  • Release: Pвыпуск освобожденного ключа

Пример :

Входные данные:

Hello!

Выход:

Наивным решением будет:

1 P Shift
2 H h
3 R Shift
4 H e
5 H l
6 H l
7 H o
8 P Shift
9 H 1
10 R Shift

Это было бы более эффективно:

1 P Shift
2 H h
3 H 1
4 R Shift
5 H Left
6 H e
7 H l
8 H l
9 H o

Среда:

  • Редактор использует моноширинный шрифт
  • Текст мягко упакован в 80 символов
  • Стрелка вверх и Стрелка вниз сохраняют столбец, даже если между ними есть более короткие линии
  • Предполагается, что буфер обмена пуст
  • Предполагается, что Num Lock включен
  • Caps Lock предполагается отключенным
  • Caps Lock работает только для букв (т.е. без Shift Lock)

Горячие клавиши / ярлыки :

  • Home: Перейти к началу текущей строки
  • End: Перейти к концу текущей строки
  • Ctrl+ A: Отметить все
  • Ctrl+ C: Копировать
  • Ctrl+ X: Вырезать
  • Ctrl+ V: Вставить
  • Shift+ Перемещение курсора: маркировка
  • Ctrl+ F: Открывает диалог поиска.
    • Глупое совпадение текста, без регулярных выражений
    • Чувствительный к регистру
    • Поиски оборачиваются
    • Однострочный ввод текста для поиска
    • Ввод заполнен текущим выбором, если между строкой нет новой строки, выбирается полный ввод
    • Копирование / вставка работает как обычно
    • Нажатие Enterвыполняет поиск, выбирая первое совпадение после текущей позиции курсора.
  • F3: Повторить последний поиск
  • Ctrl+ H: Открывает диалог замены
    • Глупое совпадение текста, без регулярных выражений
    • Чувствительный к регистру
    • Заменить все, с обтеканием
    • Однострочный ввод текста
    • Входные данные поиска заполняются текущим выбором, если между ними нет новой строки, выбирается полный ввод
    • Ввод замены пуст
    • Копирование / вставка работает как обычно
    • Tab переходит на замену ввода
    • Нажатие Enterвыполняет замену всех. Курсор ставится после последней замены

Правила :

  • Решения должны представлять собой законченную программу, которая компилирует / анализирует и выполняет без каких-либо изменений
  • Клавиатура, показанная выше, является клавиатурой для использования
    • Не требуется обрабатывать символы, которые не могут быть напечатаны с ним
  • Каждый ключ должен быть выпущен в конце
  • Курсор не должен быть в конце файла в конце

Подсчет очков :

Ваша оценка представляет собой сумму действий, необходимых для ввода следующих текстов. Победителем является решение с самым низким баллом. Используя мое наивное решение, я получаю 1371 + 833 + 2006 = 4210. Отвали! Я выберу победителя через две недели.

1 Мое наивное решение

number = 1

H = (char) -> console.log "#{number++} H #{char}"
P = (char) -> console.log "#{number++} P #{char}"
R = (char) -> console.log "#{number++} R #{char}"

strokes = (text) ->
    shiftActive = no

    for char in text
        if /^[a-z]$/.test char
            if shiftActive
                R "Shift"
                shiftActive = no

            H char
        else if /^[A-Z]$/.test char
            unless shiftActive
                P "Shift"
                shiftActive = yes

            H char.toLowerCase()
        else
            table =
                '~': '`'
                '!': 1
                '@': 2
                '#': 3
                '$': 4
                '%': 5
                '^': 6
                '&': 7
                '*': 8
                '(': 9
                ')': 0
                '_': '-'
                '+': '='
                '|': '\\'
                '<': ','
                '>': '.'
                '?': '/'
                ':': ';'
                '"': "'"
                '{': '['
                '}': ']'

            if table[char]?
                unless shiftActive
                    P "Shift"
                    shiftActive = yes

                H table[char]
            else
                H switch char
                    when " " then "Space"
                    when "\n" then "Enter"
                    when "\t" then "Tab"
                    else
                        if shiftActive
                            R "Shift"
                            shiftActive = no

                        char
    R "Shift" if shiftActive

input = ""

process.stdin.on 'data', (chunk) -> input += chunk
process.stdin.on 'end', -> strokes input

2 Легкое повторение

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
BBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBBB
CCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCCC
DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE
FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF
GGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGGG
HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH
IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII
JJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJJ
KKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKKK
LLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLLL
MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM

3 Более сложное повторение

We're no strangers to love
You know the rules and so do I
A full commitment's what I'm thinking of
You wouldn't get this from any other guy
I just wanna tell you how I'm feeling
Gotta make you understand

Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you

We've known each other for so long
Your heart's been aching but
You're too shy to say it
Inside we both know what's been going on
We know the game and we're gonna play it
And if you ask me how I'm feeling
Don't tell me you're too blind to see

Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you

Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you

(Ooh, give you up)
(Ooh, give you up)
(Ooh)
Never gonna give, never gonna give
(Give you up)
(Ooh)
Never gonna give, never gonna give
(Give you up)

We've know each other for so long
Your heart's been aching but
You're too shy to say it
Inside we both know what's been going on
We know the game and we're gonna play it

I just wanna tell you how I'm feeling
Gotta make you understand

Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you

Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you

Never gonna give you up
Never gonna let you down
Never gonna run around and desert you
Never gonna make you cry
Never gonna say goodbye
Never gonna tell a lie and hurt you

Вы можете использовать программу воспроизведения, написанную мной, для тестирования ваших решений (Примечание: пока она не поддерживает поиск / замену, все остальное должно работать).


6
Я хотел бы увидеть такую ​​программу для vim.
Брэден Бест

4
Обычно я использую мышь для некоторых из этих вещей.
Виктор Стафуса

1
Очень интересно. Я пойду утром; 3
cjfaure

2
Тебе не нужно было Рика Ролла с нами? :)
Филип Хаглунд

1
Я вроде как с @ B1KMusic. Мне было бы интереснее генерировать решения для vimgolf. (Что является эквивалентом того, что вы пытаетесь сделать здесь, просто используя команды vim.) Это, тем не менее, звучит как забавная идея уменьшить нажатия клавиш очень сложно (или, по крайней мере, я так думаю), так как точное движение для выбора затруднительно. Это делает копирование и вставку действительно трудной задачей и требует почти столько же нажатий клавиш, сколько и то, что вы пытались скопировать. (Или, по крайней мере, так я читаю, как работает копирование и вставка). И я не вижу много других способов уменьшить количество нажатий клавиш.
FDinoff

Ответы:


11

Haskell 1309 + 457 + 1618 = 3384

Наконец, ответ (оценка значительно улучшилась, как только я понял, что в вашем первом тесте есть закладки - пришлось редактировать вопрос, чтобы увидеть их). Скомпилируйте с ghc, поставьте вход на стандартный ввод. Пример:

$ ghc keyboard.hs && echo hello|./keyboard
1 H h
2 H e
3 H l
4 H l
5 H o
6 H Enter

Я пробовал очевидные вещи, такие как Dijkstra, но это было слишком медленно, даже после уменьшения разветвления до единственных полезных ходов, а именно: вывести следующую клавишу или скопировать с начала строки (Shift + Home, Ctrl + C, Конец) или вставьте.

Таким образом, этот подход использует буфер обмена фиксированной длины, копирует, когда префикс строки собирается стать «полезным», и продолжает использовать этот префикс до тех пор, пока он будет вставлен в большее количество строк, чем префиксы строк, которых он достигнет в следующем. Когда он не может использовать буфер обмена, он прибегает к наивному решению, поэтому он гарантированно превзойдет его, как только выбранная длина превысит стоимость копии.

Минимальная оценка достигается, когда длина префикса выбрана так, чтобы соответствовать «Никогда не будет». Есть способы улучшить это, но с меня хватит читать Рика Эстли.

import Data.List (isPrefixOf,isInfixOf)
import Control.Monad (foldM)
plen=12
softlines text=sl 0 [] text
  where
    sl n [] [] = []
    sl n acc [] = [(n,reverse acc)]
    sl n acc (x:xs)
      |x=='\n'||length acc==79=(n,reverse (x:acc)):(sl (n+1) [] xs)
      |otherwise=sl n (x:acc) xs
pasteable (a,b) (c,d)=(c>a && b`isInfixOf`d)
                      || (c==a && b`isInfixOf`(drop (length b) d))
findprefixes l=filter (\(a,b,c)->c/=[])
               $ map (\(a,b)->(a, b, map fst $ filter (pasteable (a,b)) l))
               $ filter (\(a,b)->length b==plen && last b/='\n')
               $ map (\(a,b)->(a, take plen b)) l
mergePrefixes [] = []
mergePrefixes (p:ps) = mergePrefixes' p ps
 where mergePrefixes' p [] = [p]
       mergePrefixes' (a,x,b) ((c,y,d):qs) =
         if length (filter (>=c) b) >= length d then
           mergePrefixes' (a,x,b) qs
         else
           (a, x, (filter (<c) b)):(mergePrefixes' (c,y,d) qs)
uc = ("~!@#$%^&*()_+<>?:{}|\""++['A'..'Z'])
lc = ("`1234567890-=,./;[]\\'"++['a'..'z'])
down c = case [[lo]|(lo,hi)<-zip lc uc,c==hi] of []->error [c];p->head p
applyPrefixToLine prefix [] s=return s
applyPrefixToLine [] line s=emit line s
applyPrefixToLine prefix line@(ch:rest) s=
 if prefix`isPrefixOf`line then
   do { s<-emitPaste s; applyPrefixToLine prefix (drop (length prefix) line) s}
 else
   do { s<-emitch s ch; applyPrefixToLine prefix rest s}
type Keystroke = (Char, [Char])
key action k (n, shift) = do
  putStrLn ((show n)++" "++[action]++" "++k)
  if k=="Shift" then return (n+1, (not shift))
  else return (n+1, shift)
emitch (m, shift) ch=
  case ch of
    '\t'->key 'H' "Tab" (m,shift)
    '\n'->key 'H' "Enter" (m,shift)
    ' '->key 'H' "Space" (m,shift)
    _->
      if shift && ch`elem`lc then
        do { key 'R' "Shift" (m, True); key 'H' [ch] (m+1, False) }
      else if not shift && ch`elem`uc then
             do { key 'P' "Shift" (m, False); key 'H' (down ch) (m+1, True) }
           else if ch`elem`lc
                then key 'H' [ch] (m, shift)
                else key 'H' (down ch) (m, shift)
emit line s = foldM emitch s line
emitPaste s = do
  s<-key 'P'"Ctrl" s
  s<-key 'H' "v" s
  key 'R' "Ctrl" s
emitCopy s = do
  s<-key 'H' "Home" s
  s<-key 'P'"Ctrl" s
  s<-key 'H' "c" s
  s<-key 'R' "Ctrl" s
  s<-key 'R' "Shift" s
  key 'H' "End" s
applyPrefix pf ((a,b):xs) p@((c,y,d):ps) s=
  if (c==a) then
    do
      s@(n, shift) <- emit y s
      s <- if shift then return s else key 'P' "Shift" s
      s <- emitCopy s
      s <- applyPrefixToLine y (drop (length y) b) s
      applyPrefix y xs ps s
  else
    do
      s<-applyPrefixToLine pf b s
      applyPrefix pf xs p s
applyPrefix "" ((a,b):xs) [] s=
  do
    s <- emit b s
    applyPrefix "" xs [] s
applyPrefix pf ((a,b):xs) [] s=
  do
    s<-applyPrefixToLine pf b s
    applyPrefix pf xs [] s
applyPrefix _ [] _ s=return s

main=do
  input <- getContents
  let lines = softlines input
  let prefixes = mergePrefixes (findprefixes lines)
  (n,shift) <- applyPrefix "" lines prefixes (1, False)
  if shift then
    key 'R' "Shift" (n, shift)
  else
    return(n,shift)

Очень хорошее решение :) Кстати: вы можете сбрить еще несколько символов, комбинируя вставки (если это возможно).
TimWolla

Это действительно влияет только на пример 2 - у меня была версия алгоритма Дейкстры, которая это находит, но она применима только к первым 3 строкам. Вы можете улучшить мое решение для всех тестов, попробовав префиксы разных размеров; решение достаточно быстрое, чтобы вы могли сделать это грубой силой, требуется всего около 10 прогонов. Хотя неудобно рефакторинг этого в haskell.
Bazzargh
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.