特定のテキストを入力するために必要な最小限のキーストローク


45

プログラマーは怠け者になる傾向があることは誰もが知っています。空き時間を最大化するために、そこに入力されるテキストに対して最小限のキーストロークを出力するプログラムを作成することにします。

入力:キーストロークに変換する必要があるテキスト。テキストの入力方法(STDIN /引数で提供されたファイルからの読み取り)を決定できます。

出力:次の形式の必要なアクション:

  • 番号を付ける必要があります
  • Hit:キーを押してすぐに離す
  • 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。失せろ!2週間後に勝者を選びます。

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のためにこのようなプログラムを見たいです。
ブレーデンベスト14年

4
通常、これらのことの一部にマウスを使用します。
ビクターStafusa 14年

1
とても興味深い。私は朝に行く; 3
cjfaure 14年

2
あなたは本当にリック・ロールをする必要はありませんでしたね?:)
フィリップハグランド14年

1
私は@ B1KMusicとちょっと違います。私にとって、これはvimgolfのソリューションを生成するのにもっと面白いでしょう。(これはvimコマンドを使用してここでやろうとしていることと同等です。)ただし、これはキーストロークを減らす楽しいアイデアのように聞こえますが、選択のための正確な移動が難しいためです。これにより、コピーと貼り付けは非常に困難な作業になり、コピーしようとしていたものとほぼ同じキーストロークが必要になります。(または、少なくともこれがコピーアンドペーストの仕組みを読んでいる方法です)。そして、キーストロークを減らす他の多くの方法は見当たりません。
FDinoff 14年

回答:


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

ダイクストラなどの明白なものを試しましたが、次のキーを出力するか、行の先頭からコピーするか(Shift + Home、Ctrl + C、終了)、または貼り付けます。

そのため、このアプローチでは固定長のクリップボードを使用し、行プレフィックスが「有用」になろうとするときにコピーし、次に到達する行のプレフィックスよりも多くの行に貼り付け可能な限り、そのプレフィックスを使用し続けます。クリップボードを使用できない場合は、単純なソリューションにフォールバックするため、選択された長さがコピーのコストを超えた場合にクリップボードを使用することが保証されます。

最小スコアは、プレフィックス長が「Never gonna」に合うように選択されたときに達成されます。これを改善する方法はありますが、Rick Astleyを十分に読んでいます。

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)

非常に良い解決策:)ところで:ペーストを組み合わせることで、可能であればいくつかの文字を削ることができます。
ティムウォラ14年

これは例2にのみ実際に影響します。これを検出するダイクストラアルゴリズムバージョンがありましたが、最初の3行に対してのみ使用可能です。さまざまなプレフィックスサイズを試すことで、すべてのテストのソリューションを改善できます。このソリューションは十分に高速であるため、総当たりでこれを行うことができ、必要な実行は約10回だけです。ただし、haskellのリファクタリングは厄介です。
バザーグ14年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.