키패드 누르기
 
제출한 풀이
function solution(numbers, hand) {
    
    const numToPos = number => {
        if(number === 0){
            return [1,3]
        } 
        
        const x = (number -1) % 3
        const y = Math.floor((number-1) / 3)
        
        return [x,y]
    }
    
    const cost = (pos1, pos2) => Math.abs(pos1[0] - pos2[0]) + Math.abs(pos1[1] - pos2[1])

    const current = {
        left: [0,3],
        right: [2,3]
    }
    
    const acronyms = {
        left: "L",
        right: "R",
    }
    
    return numbers.map(number => {
        const newPos = numToPos(number)
        
        if(newPos[0] === 2){
            current.right = newPos
            return acronyms.right
        }
        
        if(newPos[0] === 0){
            current.left = newPos
            return acronyms.left
        }
        
        const leftCost = cost(newPos, current.left)
        const rightCost = cost(newPos, current.right)    
        
        const movedHand = leftCost > rightCost ? 'right' : leftCost < rightCost ? 'left' : hand
        current[movedHand] = newPos
        
        return acronyms[movedHand]
    }).join('')
}
 
예전에 풀어본 문제를 다시 풀어봤습니다.
최종 풀이는 위의 제출한 풀이 토글 탭을 클릭해주세요.
 

1. 잘못된 첫 번째 접근 방법

 
문제를 읽고 아래와 같이 생각했고
 
키패드의 2개의 특수문자 *, #은 초기 위치로만 사용되니
주어진 숫자 배열을 순회하면서 L 또는 R를 리턴해주고
마지막으로 이를 string으로 바꿔주면 되겠구만?
 
의사 코드를 작성하고 시작했습니다.

numbers.map(number => {
	if(나머지 1) left
	
	if(나머지 0) right
	
	if(같은 거리){
		hand
	}
	
	nearest
}).join('')
 
처음에는 방향을 결정할 때 좌표보다 나머지 계산이 빠른 로직이라 생각해 나머지로 풀어보겠다고 애를 썼었는데요.
생각한 논리에서 여러 예외가 생겼습니다.
항상 쉽고, 빠르게 작성 가능하고, 이해하기 쉬운 코드롤 작성도록 노력해야겠습니다.
 

2. 단순하게 생각한 올바른 접근 방법

 
결국 나머지 계산이 아닌 좌표로 바꿔 생각했습니다.
 
아래와 같이 의사코드를 수정해보겠습니다.
function solution(numbers, hand){
	return numbers.map(number => {
		if(number.x == 0) return left // 규칙 2
		
		if(number.x == 2) return right // 규칙 3
		
		if(same distance){ // 규칙 4-1
			return hand
		}
		
		return nearest // 규칙 4
	}).join('')

}
 
2.1 규칙 2,3 처리
 
위 의사코드를 토대로 규칙 2,3 (3/6/9 , 1/4/7)를 우선적으로 해결해보겠습니다.
 
숫자 0~9를 좌표로 바꿔주는 numToPos 함수를 작성하고
numbers.map 으로 numbers 배열을 순회하며 각 숫자의 좌표를 newPos 변수에 저장합니다.
만약 x 좌표가 2라면 R를, x 좌표가 0이라면 L를 리턴해줍니다.
 
// solution 함수 내의 helper 함수

const numToPos = number => {
    if(number === 0){
        return [1,3]
    } 
    
    const x = (number -1) % 3
    const y = Math.floor((number-1) / 3)
    
    return [x,y]
}

// 
 
2.2 규칙 4 처리
 
숫자 2/5/8/0에 대해 규칙 4를 적용해보겠습니다.
 
  1. 각 숫자를 좌표로 변환하고
  1. 변환된 좌표를 이용해, 현재 두 손가락이 위치한 곳까지 소요되는 비용을 계산하고
  1. 두 손가락에 대한 비용을 비교해 움직일 손가락을 결정하고
  1. 움직일 손가락의 현재 위치를 갱신하고
  1. 움직인 손가락의 방향 리턴
 
거리 대신 비용이라는 단어를 사용했는데요.
일반적으로 사용되는 두 좌표의 대각선 거리가 아니기 떄문에 distance를 cost로 바꿔 생각했습니다.
 
규칙 4를 적용하기 위해
현재 위치를 저장하는 current 변수와 cost helper 함수를 작성했습니다.
const current = {
    left: [0,3],
    right: [2,3]
}

const cost = (pos1, pos2) => Math.abs(pos1[0] - pos2[0]) + Math.abs(pos1[1] - pos2[1])
 
규칙 2,3의 if문에서 방향 정보를 리턴하기 전에 현재 위치를 갱신해줬고
 
규칙 2,3이 적용되지 않을 경우 (규칙 4가 적용될 경우)
비용을 비교해 움직일 손가락을 결정하고 방향을 리턴했습니다.
acronyms 는 최상단의 풀이 코드를 참고해주시면 감사하겠습니다.
  const leftCost = cost(newPos, current.left)
  const rightCost = cost(newPos, current.right)    
  
  const movedHand = leftCost > rightCost ? 'right' : leftCost < rightCost ? 'left' : hand
  current[movedHand] = newPos
  
  return acronyms[movedHand]
 
처음 생각한 의사코드에서
현재 위치를 갱신하는 코드가 추가됐고
규칙 4 , 4-1를 구분해 작성했던 if문이 사라졌습니다.