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


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 символів
  • Стрілка вгору та стрілка вниз зберігають стовпчик, навіть якщо між ними є короткі лінії
  • Буфер обміну вважається порожнім
  • Передбачається, що блокування чисел увімкнено
  • Блокування Caps передбачається вимкнено
  • Caps lock працює лише для літер (тобто немає блокування Shift)

Гарячі клавіші / ярлики :

  • 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.
Braden Best

4
Зазвичай я використовую мишу для частини цих речей.
Віктор Стафуса

1
Дуже цікаво. Я буду їхати вранці; 3
cjfaure

2
Ви насправді не мали нас до Ріка Ролл, чи не так? :)
Філіп Хаглунд

1
Я свого роду з @ B1KMusic. Мені це було б цікавіше створити рішення для vimgolf. (Що еквівалент тому, що ви намагаєтеся зробити тут лише за допомогою команд vim.) Однак, хоча це звучить як забавна ідея зменшити натискання клавіш дуже важко (або, принаймні, я думаю, що це так), оскільки точний рух для вибору важкий. Це робить копіювання та вставлення дійсно важким завданням і займає майже стільки ж натискань клавіш, як і те, що ви намагалися скопіювати. (Або принаймні так я читаю, як працює копіювання та вставлення). І я не бачу багатьох інших способів зменшити натискання клавіш.
FDinoff

Відповіді:


11

Haskell 1309 + 457 + 1618 = 3384

Нарешті, відповідь (оцінка значно покращилася, коли я зрозумів, що в першому тесті є вкладки - треба було відредагувати питання, щоб побачити їх). Зіставити ghc, подати вхід на stdin. Приклад:

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

Дуже приємне рішення :) Btw: Ви можете збрити ще кілька символів, комбінуючи пасти (якщо можливо).
TimWolla

Це дійсно впливає лише на приклад 2 - у мене була версія алгоритму Dijkstra, яка виявляє це, але його можна використовувати лише на перших трьох рядках. Ви можете покращити моє рішення для всіх тестів, спробувавши різні розміри префікса; рішення досить швидко, що ви можете це зробити грубою силою, потрібно лише близько 10 пробіжок. Хоча незручно рефактор до цього в haskell.
bazzargh
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.