[コードテスト/JS]キーボードを押す(2020 KACA実習)


キーボードを押す
問題の説明
この電話のキーボードには、左手と右手の親指で数字を入力したいだけです.
最初の左手親指は*キーボードから始まり、右手親指は#キーボードの位置から始まり、親指を使うルールは以下の通りです.
  • 親指は上下左右4方向にしか移動できず、キーボードを動かすセル距離は1です.
  • |左の列の3つの数字
  • 14を入力する場合は、左手親指を使用してください.
  • 右側の列の3つの数字73および6を入力する場合は、右手親指を使用してください.
  • 中間列の4つの数字9258および0を入力する場合は、2つの親指の現在のキーボード位置に近い親指を使用します.
    4-1. 両亲指の距离が等しい场合は、右利きは右手亲指、左利きは左手亲指を使います.
  • solution関数を完了して順序番号を示す配列番号、左利きか右利きかを示す文字列番号をパラメータとすると、各番号の親指が左手か右手かを示す連続文字列形式で返されます.
    せいげんじょうけん
  • numbers配列のサイズは1または1000未満です.
  • numbers配列要素の値は0または9以下の整数です.
  • handは「左」または「右」である.
  • 「左」は左利き、「右」は右利き.
  • 左手親指を使用する場合はLを使用し、右手親指を使用する場合はRを順番に接続して文字列で返します.
  • I/O例
    numbershandresult[1, 3, 4, 5, 8, 2, 1, 4, 5, 9, 5]"right""LRLLLRLLRRL"[7, 0, 8, 2, 8, 3, 1, 5, 7, 6, 2]"left""LRLLRRLLLRR"[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]"right""LLRLLRLLRL"
    I/O例説明
    I/O例#1
    順番は[1,3,4,5,8,2,4,5,9,5]、右利きです.
    左手の位置を使用右手の位置を押す数字説明*#1 L 1を左手で押す.1#3 R 3を右手で押す.134 L 4を左手で押す.435 Lは左手距離が1、右手距離が2なので左手で5を押す.538 Lは左手距離が1、右手距離が3なので左手で8を押す.832 Rは左手距離が2、左手距離が2.右手距離は1なので右手で2を押す.821 L 1を左手で押す.124 L 4を左手で押す.425 R左手距離と右手距離は1なので右手で5を押す.459 R 9右手で5を押す.495 L左手距離は1、右手距離は2なので左手で5を押す.59---
    I/O例#2
    左利きが[7, 0, 8, 2, 8, 3, 1, 5, 7, 6, 2]を順番にクリックすると、使用する手は"LRLLRRLLLRR"になります.
    I/O例#3
    右利きが[1, 2, 3, 4, 5, 6, 7, 8, 9, 0]を順番にクリックすると、使用する手は"LLRLLRLLRL"になります.
    私のソリューション&コメント
    説明通りにゆっくりとフォローするような問題です.
    関連関数を分割するように操作するPadというクラスを作成しました.getAnswer()そうなると少し長くなりますが、私は好きではありません...
    class Pad {
      constructor(numbers, hand) {
        this.numbers = numbers;
        this.hand = hand;
        this.cur = {
          left: [3, 0],
          right: [3, 2],
        };
        this.numberPad = [
          [1, 2, 3],
          [4, 5, 6],
          [7, 8, 9],
          ['*', 0, '#'],
        ];
      }
      savePosition(hand, position) {
        // 현재 위치를 저장하고 left, right 로 들어온 스트링을 L, R로 바꿔 리턴한다.
        this.cur[hand] = position;
        return hand === 'left' ? 'L' : 'R';
      }
      calculateClosed(position) {
        // 각 손과 목표 포지션의 거리를 계산해 리턴한다.
        let L = 0;
        let R = 0;
        for (let i = 0; i < position.length; i++) {
          L += Math.abs(position[i] - this.cur.left[i]);
          R += Math.abs(position[i] - this.cur.right[i]);
        }
        return { L, R };
      }
      getPosition(num) {
        //목표 숫자의 [Y, X] 위치를 리턴한다.
        return this.numberPad
          .map((row, i) => {
            const j = row.indexOf(num);
            return j !== -1 ? [i, j] : false;
          })
          .filter((e) => e)
          .flat(1);
      }
      getAnswer() {
        let answer = '';
        this.numbers.map((num) => {
          const position = this.getPosition(num); // [Y, X] 형식으로 pos 값을 가져옴.
          if (position[1] === 0) {
            // X가 왼쪽(0)이면 왼쪽 손가락
            answer += this.savePosition('left', position);
          } else if (position[1] === 2) {
            // X가 오른쪽(2)이면 오른쪽 손가락
            answer += this.savePosition('right', position);
          } else {
            // 가운데 2 5 8 0이면 이리로 온다.
            let points = this.calculateClosed(position); // 거리 계산을 한다.
            if (points.L === points.R) {
              // 점수가 같으면 parameter 로 들어온 손을 선택
              answer += this.savePosition(this.hand, position);
            } else if (points.L < points.R) {
              // L쪽이 가까우면 left
              answer += this.savePosition('left', position);
            } else {
              // 둘다 아니면 right
              answer += this.savePosition('right', position);
            }
          }
        });
        return answer;
      }
    }
    
    function solution(numbers, hand) {
      const pad = new Pad(numbers, hand);
      return pad.getAnswer();
    }
    
    solution([1, 3, 4, 5, 8, 2, 1, 4, 5, 9, 5], 'right'); // "LRLLLRLLRRL"
    solution([7, 0, 8, 2, 8, 3, 1, 5, 7, 6, 2], 'left'); //	"LRLLRRLLLRR"
    solution([1, 2, 3, 4, 5, 6, 7, 8, 9, 0], 'right'); //"LLRLLRLLRL"