虚拟键盘文字输入


22

在现代游戏机和其他没有传统键盘的设备上,尝试输入文本是一场噩梦。不得不在虚拟键盘上用几个按钮和操纵杆来打字是很烦人的,而且我希望尽可能少地进行动作/按钮按下。

您将使用的键盘如下所示:

+---+---+---+---+---+---+---+---+---+---+
| 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 0 |
+---+---+---+---+---+---+---+---+---+---+
| 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 | _ | @ | . |
+---+---+---+---+---+---+---+---+---+---+

可以使用以下操作:

  • L:在键盘上向左移动一个方块(自动换行)
  • R:在键盘上向右移动一个方块(自动换行)
  • U:在键盘上向上移动一格(自动换行)
  • D:在键盘上向下移动一格(自动换行)
  • Y:插入一个空格
  • B:将插入指针向左移动一个空格(如果指针位于开头,则不执行任何操作)
  • F:将插入指针向右移动一个空格(如果指针位于末尾,则不执行任何操作)
  • C:切换大写锁定
  • A:将所选字符插入到插入指针的位置

给定仅包含可以使用上述键盘和命令(matches [a-zA-Z0-9 _@.-]*)键入的ASCII字符的输入字符串,输出一系列命令,这些命令将产生输出字符串。光标的初始位置在1按键上(左上角),并且大写锁定最初处于关闭状态。

计分

对于任何给定的字符串,对于该字符串中的每个字符而言,幼稚的方法都是通过最短路径导航至键盘上的字符,并在必要时切换大写锁定,然后选择该字符。这种幼稚的方法会产生长短(length of input string) + (sum of Manhattan distances on keyboard between consecutive non-space characters) + (number of times the string alternates between lowercase and uppercase characters) + (1 if string starts with an uppercase letter else 0)。例如,天真的方法101将导致ALARA长度为5的命令,并且Noob 5将导致DDDRRRRRCAUURRRCAADDLLLLAYUUUA长度为30的命令。

但是,您提交的内容要比幼稚的方法做得更好。对于每个输入字符串,您的提交将获得的点数等于幼稚方法使用的命令数减去提交输出所输出的命令数。您的总体分数将是各个分数的总和。

规则

  • 提交将在Cloud9免费虚拟工作区上运行。该工作区具有512 MB的RAM,2 GB的磁盘空间,8个2.50 GHz的Intel®Xeon®CPU(完整的CPU信息,可以通过运行找到cat /proc/cpuinfo,可以在此处找到),并且正在运行64位Ubuntu 14.04。可靠。您可以请求访问测试工作区以运行提交并为其评分,或者我可以为您评分。
  • 提交将针对每个测试用例运行一次。禁止在两次运行之间存储状态。除了源文件(运行之间可能无法修改)以外,提交内容不得写入其他文件或从中读取(可能在读取输入文件时除外)(如果需要)。
  • 对于每个测试用例,提交的时间不得超过1分钟。提交的内容可能会输出多个解决方案,但是只有分配时间内的最后一个有效解决方案才会用于评分。在指定的时间内未能输出任何有效的解决方案,将导致该测试用例的得分为0。
  • 请提供有关如何调用提交的说明,以及标准Ubuntu 14.04安装未包含的任何需要安装的工具/库。
  • 获胜者将是得分最高的作品。如果出现平局,则具有更好算法复杂性的提交将获胜。如果平局仍未解决,则达到分数和算法复杂性的第一个提交将获胜。
  • 提交的内容可能无法针对测试案例进行优化。如果我认为有必要,我保留更改测试用例的权利。

测试用例

格式: input string => naive score

(如果您发现这些错误,请在更正中留下评论)

101 => 5
quip => 12
PPCG => 15
Mego => 25
Noob 5 => 26
penguin => 27
867-5309 => 32
2_sPoOkY_4_mE => 60
The Nineteenth Byte => 76
penguins@SouthPole.org => 95
8xM3R__5ltZgrkJ.-W b => 98
correcthorsebatterystaple => 104
verylongRUNSOFCAPSandnocaps => 118
This is an English sentence. => 122
WNtza.akjzSP2GI0V9X .0epmUQ-mo => 131
Programming Puzzles and Code Golf => 140

5
我得到-27 ...没关系
Leaky Nun

1
@Mego:我觉得他说,他的解决方案做了糟糕的不是幼稚。:P
El'endia Starman

2
我同意,游戏机键盘很烂
Rɪᴋᴇʀ

如果程序在一分钟内未终止,但在此时间内打印出一个或多个解决方案,会发生什么情况?不终止会受到任何惩罚吗?
mIllIbyte '16

@mIllIbyte使用在时限内打印的最后一个有效(即,产生正确的文本)命令。如果没有打印出有效命令,则分数为
0。– Mego

Answers:


2

C

分数是193。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#define LEFT 'L'
#define RIGHT 'R'
#define CAPS 'C'
#define UP 'U'
#define DOWN 'D'
#define SPACE 'Y'
#define CURSORLEFT 'B'
#define CURSORRIGHT 'F'
#define APPLY 'A'
#define TABLEX 10
#define TABLEY 4
#define casediff(A,B) (isupper(A)*islower(B)+islower(A)*isupper(B))
typedef struct {int x; int y;} coord;
coord letters[300];
#define initLetter(letter, X, Y) \
letters[letter].x=X, letters[letter].y=Y
#define initAllLetters(); \
initLetter('1',0,0);\
initLetter('1',0,0);\
initLetter('2',1,0);\
initLetter('2',1,0);\
initLetter('3',2,0);\
initLetter('3',2,0);\
initLetter('4',3,0);\
initLetter('4',3,0);\
initLetter('5',4,0);\
initLetter('5',4,0);\
initLetter('6',5,0);\
initLetter('6',5,0);\
initLetter('7',6,0);\
initLetter('7',6,0);\
initLetter('8',7,0);\
initLetter('8',7,0);\
initLetter('9',8,0);\
initLetter('9',8,0);\
initLetter('0',9,0);\
initLetter('0',9,0);\
initLetter('Q',0,1);\
initLetter('q',0,1);\
initLetter('W',1,1);\
initLetter('w',1,1);\
initLetter('E',2,1);\
initLetter('e',2,1);\
initLetter('R',3,1);\
initLetter('r',3,1);\
initLetter('T',4,1);\
initLetter('t',4,1);\
initLetter('Y',5,1);\
initLetter('y',5,1);\
initLetter('U',6,1);\
initLetter('u',6,1);\
initLetter('I',7,1);\
initLetter('i',7,1);\
initLetter('O',8,1);\
initLetter('o',8,1);\
initLetter('P',9,1);\
initLetter('p',9,1);\
initLetter('A',0,2);\
initLetter('a',0,2);\
initLetter('S',1,2);\
initLetter('s',1,2);\
initLetter('D',2,2);\
initLetter('d',2,2);\
initLetter('F',3,2);\
initLetter('f',3,2);\
initLetter('G',4,2);\
initLetter('g',4,2);\
initLetter('H',5,2);\
initLetter('h',5,2);\
initLetter('J',6,2);\
initLetter('j',6,2);\
initLetter('K',7,2);\
initLetter('k',7,2);\
initLetter('L',8,2);\
initLetter('l',8,2);\
initLetter('-',9,2);\
initLetter('-',9,2);\
initLetter('Z',0,3);\
initLetter('z',0,3);\
initLetter('X',1,3);\
initLetter('x',1,3);\
initLetter('C',2,3);\
initLetter('c',2,3);\
initLetter('V',3,3);\
initLetter('v',3,3);\
initLetter('B',4,3);\
initLetter('b',4,3);\
initLetter('N',5,3);\
initLetter('n',5,3);\
initLetter('M',6,3);\
initLetter('m',6,3);\
initLetter('_',7,3);\
initLetter('_',7,3);\
initLetter('@',8,3);\
initLetter('@',8,3);\
initLetter('.',9,3);\
initLetter('.',9,3);
typedef struct {int length; char instr[300];} movement;
movecasefold(char*instr,coord A, coord B){
    register int i=0;int j;
    if(A.x<B.x)
     if(B.x-A.x<=TABLEX/2)
      for(;B.x-A.x!=i;)instr[i++]=RIGHT;
     else
      for(;TABLEX-B.x+A.x!=i;)instr[i++]=LEFT;
    else if(A.x>B.x)
     if(A.x-B.x<=TABLEX/2)
      for(;A.x-B.x!=i;)instr[i++]=LEFT;
     else
      for(;TABLEX-A.x+B.x!=i;)instr[i++]=RIGHT;
    j=i;
    if(A.y<B.y)
     if(B.y-A.y<=TABLEY/2)
      for(;B.y-A.y!=i-j;)instr[i++]=DOWN;
     else
      for(;TABLEY-B.y+A.y!=i-j;)instr[i++]=UP;
    else if(A.y>B.y)
     if(A.y-B.y<=TABLEY/2)
      for(;A.y-B.y!=i-j;)instr[i++]=UP;
     else
      for(;TABLEY-A.y+B.y!=i-j;)instr[i++]=DOWN;
    instr[i]='\0';
    return i;
}
char sentence[50], oldcase, oldletter;
int sentencelength;
typedef struct {
int sentencetoorder[50];
int ordertosentence[50];
int length;
} order;
ordercopy(order*a, order b){
register int i;
for(i=0;++i<sentencelength;) a->sentencetoorder[i]=b.sentencetoorder[i], a->ordertosentence[i]=b.ordertosentence[i];
a->length=b.length;
}
order currentOrder;
movetwo(char*instr,int A, int B){
    register int j; int i=0;
    if(A<B)
    { for(j=A+1;j<B;j++)
      if(currentOrder.sentencetoorder[j]<currentOrder.sentencetoorder[B])
       instr[i++]=CURSORRIGHT;
    }
    else
    { for(j=A;j>B;j--)
      if(currentOrder.sentencetoorder[j]<currentOrder.sentencetoorder[B])
       instr[i++]=CURSORLEFT;
    }
    if(sentence[B]==' '){
        instr[i++]=SPACE;
        instr[i]='\0';
        return i;
    }
    i+=movecasefold(instr+i,letters[oldletter],letters[sentence[B]]);
    oldletter=sentence[B];
    if(casediff(oldcase,sentence[B]))oldcase=sentence[B],instr[i++]=CAPS;
    instr[i++]=APPLY;
    instr[i]='\0';
    return i;
}
moveall(char*instr){
    int j;int i = 0;
    oldcase='a';
    oldletter='1';
    for(j=0;++j<sentencelength;)
        i+=movetwo(instr+i,currentOrder.ordertosentence[j-1],currentOrder.ordertosentence[j]);
    return i;
}
iteration();
main(){
initAllLetters();
gets(sentence+1);*sentence='1';sentencelength=strlen(sentence);
int i;
for(i=0;++i<sentencelength;)currentOrder.sentencetoorder[i]=currentOrder.ordertosentence[i]=i;  
char instr[300];
currentOrder.length=moveall(instr);
puts(instr);
while(iteration());
}
#define inside(item, start, stop) (((start)<=(item))&((item)<(stop)))
#define min(a,b) ((a)<(b)?(a):(b))
#define max(a,b) ((a)>(b)?(a):(b))
iteration(){
char instr[300];
register int i;
int newstart, start, l;
static order oldorder, neworder;
ordercopy(&oldorder,currentOrder);
ordercopy(&neworder,currentOrder);
for(l=0;++l<sentencelength-1;)
for(start=0;++start<sentencelength-l;)
for(newstart=0;++newstart<sentencelength-l;)
if(start!=newstart){
 for(i=0;++i<sentencelength;)
  if(inside(i,start,start+l))currentOrder.ordertosentence[i-start+newstart]=oldorder.ordertosentence[i];
  else if(inside(i,min(start,newstart),max(start+l,newstart+l)))currentOrder.ordertosentence[newstart<start?i+l:(i-l)]=oldorder.ordertosentence[i];
  else currentOrder.ordertosentence[i]=oldorder.ordertosentence[i];
 for(i=0;++i<sentencelength;) currentOrder.sentencetoorder[currentOrder.ordertosentence[i]]=i;
  currentOrder.length=moveall(instr);
 if(currentOrder.length<neworder.length){
  puts(instr);
  ordercopy(&neworder, currentOrder);
 }
}
ordercopy(&currentOrder, neworder);
return neworder.length<oldorder.length;
}

编译为“ gcc virtualKeyboard.c”。在不带参数“ ./a.out”的情况下运行它。它从stdin读取输入,并将输出写入stdout。


抱歉,延迟-您的分数是
193。– Mego

1

C99

这是我尝试解决的方法。取得62分。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>

#define COORDINATES_ARRAY_SIZE 256
#define MOVEMENT_ARRAY_LEN 1024

#define KEYBOARD_WIDTH 10
#define KEYBOARD_HEIGHT 4

#define DIRECTION_BIT 0x40

#define INITIAL_CHAR '1'

#define SPACE_WEIGHT_INCREMENT 2
#define SPACE_WEIGHT_MAX 10
#define CURSOR_WEIGHT_INCREMENT 2
#define CURSOR_WEIGHT_MAX 10

const char keyboard[KEYBOARD_HEIGHT][KEYBOARD_WIDTH] = {
    {'1','2','3','4','5','6','7','8','9','0'},
    {'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','_','@','.'}
};

typedef enum {
    right,
    left,
    up,
    down
} dir;

//iirc it is an omptimization to use int over byte or short despite only using values <10
typedef struct {
    int x;
    int y;
} coordinates;

typedef struct {
    int xmove;
    int ymove;
    dir xdir;
    dir ydir;
} movedata;

void generateCoords(coordinates*);
unsigned int nearestNeighbor(coordinates*, char*, char*, char*, char*, int, int, int, int, int, int, int);
int calcDistance(coordinates*, char, char, movedata*);

int main(int argc, char **argv){
    coordinates coords[COORDINATES_ARRAY_SIZE];
    char moves[MOVEMENT_ARRAY_LEN];
    unsigned int temp, min = 0;
    min--; //largest possible int

    if(argc != 2){
        printf("Invalid Arguments. Usage: virtualKeyboard <string>\n");

    }else{
        int len = strlen(argv[1]);
        int moveCursor, capsLock;
        char *workingString =  malloc(len * sizeof(char));
        generateCoords(coords);
        movedata data;
        for(int spaceWeight = 1; spaceWeight < SPACE_WEIGHT_MAX; spaceWeight += SPACE_WEIGHT_INCREMENT){
            for(int cursorWeight = 1; cursorWeight < CURSOR_WEIGHT_MAX; cursorWeight+= CURSOR_WEIGHT_INCREMENT){
                for(int i = 0; i<len; i++){
                    //zero out arrays
                    moveCursor = 0;
                    capsLock = 0;
                    memset(moves, 0, MOVEMENT_ARRAY_LEN);
                    memset(workingString, 0, len);
                    workingString[0] = argv[1][i];
                    //add initial moves
                    temp = calcDistance(coords, INITIAL_CHAR, argv[1][i], &data);

                    for(int j = 0; j < data.xmove; j++){
                        moves[moveCursor] = data.xdir == right ? 'R' : 'L';
                        moveCursor++;
                    }
                    for(int j = 0; j < data.ymove; j++){
                        moves[moveCursor] = data.ydir == up ? 'U' : 'D';
                        moveCursor++;
                    }
                    if((capsLock==0) != (isupper(argv[1][i])==0)){
                        moves[moveCursor] = 'C';
                        capsLock = capsLock == 0 ? 1 : 0;
                        moveCursor++;
                        temp++;
                    }
                    moves[moveCursor] = 'A';
                    temp++;
                    temp += nearestNeighbor(coords, moves, argv[1], argv[1], workingString, i ,len, len, 1, capsLock, cursorWeight, spaceWeight);
                    if(temp < min){
                        printf("%s\t%s\t%i\n", moves, workingString, temp);
                        min = temp;
                    }

                }
            }
        }
        free(workingString);
    }
    return 0;
}

//optimization so it only searches through the matrix once
void generateCoords(coordinates* coords){
    for(int i = 0; i < KEYBOARD_HEIGHT; i++){
        for(int j = 0; j < KEYBOARD_WIDTH; j++){
            coords[(int)keyboard[i][j]].y = i;
            coords[(int)keyboard[i][j]].x = j;
        }
    }
}

int calcCursorMoves(char* og, char* workingString, char added, int cursor, int len){
    char* inUseWS = calloc(len, sizeof(char));
    char* inUseOG = calloc(len, sizeof(char));
    int indexToAdd = 0, newCursor = cursor;
    for(int i = 0; i < len; i++){
        for(int j = 0; j < len; j++){
            if(workingString[j] != -1 && og[i] == workingString[j] 
                    && inUseOG[i] == 0 && inUseWS[j] == 0){
                inUseOG[i] = 1;
                inUseWS[j] = i+1; // set in use to corresponding working string
                break;
            }
            if(workingString[j] == 0){
                inUseWS[j] = -1; //append -1 to end
                break; //nothing more to see here   
            }
        }
    }
    for(int i = 0; i < len; i++){
        if(og[i] == added && inUseOG[i] == 0){
            indexToAdd = i;
            break;
        }
    }
    for(int i = 0; i < len; i++){
        if(i==0 && indexToAdd < inUseWS[i]-1){
            newCursor = i;
            break;

        }
        if(inUseWS[i]-1 < indexToAdd && (indexToAdd < inUseWS[i+1]-1 || inUseWS[i+1] == -1)){
            newCursor = i+1;
            break;
        }
        if(workingString[i+1] == 0){
            newCursor = i+1;
            break;
        }

    }
    free(inUseWS);
    free(inUseOG);
    return newCursor - cursor;  
}

int calcDistance(coordinates* coords, char from, char to, movedata* data){
    if(to == ' ')
        return 0;
    from = tolower(from);
    to = tolower(to);
    data->xmove = coords[(int)from].x - coords[(int)to].x;
    data->ymove = coords[(int)from].y - coords[(int)to].y;  
    data->xdir = data->xmove >= 0 ? left : right;
    data->ydir = data->ymove >= 0 ? up : down;
    data->xmove = abs(data->xmove);
    data->ymove = abs(data->ymove);
    //wraparound
    if(data->xmove > 5){
        data->xmove = 10 - data->xmove;
        data->xdir = data->xdir == right ? left : right;
    }   
    if(data->ymove > 2){
        data->ymove = 4 - data->ymove;
        data->ydir = data->ydir == up ? down : up;
    }
    return data->xmove + data->ymove;
}

//assumes array is longer than current string
void insertAt(char* str, char toIns, int index){
    char* temp = malloc(strlen(str) + 1);
    strncpy(temp, str, index);
    temp[index] = toIns;
    strcpy(temp+index+1, str+index);
    strcpy(str, temp);  
    free(temp);
}

unsigned int nearestNeighbor(coordinates* coords, char* moves, char* og, 
        char* input, char* workingString, int index, int lenOG, int len,  
        int cursor, int capsLock, int cursorWeight, int spaceWeight){
    if(len  == 1){
        return 0;
    }
    movedata data, bestData;
    char bestChar = '\0';
    unsigned int bestCursor = -1, bestIndex = -1, bestDist = -1;
    unsigned int dist = -1, cursorDist = -1, best = -1;
    char newString[len-1] ;
    memset(newString, 0, len-1);
    strncpy(newString, input, index);
    strcpy(newString+index, input+index+1);
//  printf("%s\t", newString);
    for(int i = 0; i < len-1; i++){
        dist = calcDistance(coords, input[index], newString[i], &data);
        cursorDist = calcCursorMoves(og, workingString, newString[i], cursor, lenOG);
        if(dist + cursorWeight * abs(cursorDist) + spaceWeight * (newString[i] == ' ') + ((capsLock==0) != (isupper(newString[i])==0)) < best){
    //      printf("%c %c %i %i\n", workingString[cursor], newString[i], cursorDist, cursor);
            bestDist = cursorDist;
            bestCursor = cursor + cursorDist;
            best = dist + cursorWeight * abs(cursorDist) + spaceWeight * (newString[i] == ' ') + ((capsLock==0) != (isupper(newString[i])==0));
            bestChar = newString[i];
            bestIndex = i;
            memcpy(&bestData, &data, sizeof(data));
        }
    }
    //append moves
    int i;
    for(i = 0; i < MOVEMENT_ARRAY_LEN && moves[i]!=0; i++);
    if(bestChar != ' '){
        for(int j = 0; j < bestData.xmove; j++){
            moves[i] = bestData.xdir == right ? 'R' : 'L';
            i++;
        }
        for(int j = 0; j < bestData.ymove; j++){
            moves[i] = bestData.ydir == up ? 'U' : 'D';
            i++;
        }
        for(int j = 0; j < abs(bestDist); j++){
            moves[i] = bestDist > 0 ? 'B' : 'F'; 
            i++;
        }
        if((capsLock==0) != (isupper(bestChar)==0)){
            moves[i] = 'C';
            capsLock = capsLock == 0 ? 1 : 0;
            i++;
        }
        moves[i] = 'A';
    } else {
        moves[i] = 'Y';
    }
    //remove weight
    best -= cursorWeight * abs(bestDist);
    best -= spaceWeight * (bestChar==' ');
    insertAt(workingString, bestChar, bestCursor);
    int max = nearestNeighbor(coords, moves, og, newString, workingString, 
             bestIndex, lenOG, len-1, bestCursor+1, capsLock, cursorWeight, spaceWeight);
    //the 1 is for the actual click
    return max + best + 1 + abs(bestDist);
}

对于较小的单词来说似乎可以正常工作,而我只是认为较大的单词也可以正常工作,因为它们太大了,无法手动测试。

使用“ gcc -std = gnu99”进行编译。
用法是“ virtualKeyboard “字符串””


1
这无法在许多输入上产生有效的命令序列。例如,第一个输出,用于Noob 5IS RRRRRUCALCAYRRRRARRRRDBBBAA,其产生N33b @,和第二个输出是LLDAAYRRRRAUBBARBBBCA,其产生4oo3 e。就目前而言,您的当前分数是5,因为您的程序仅输出前三个测试用例的有效命令序列。
Mego 2016年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.