[DJ playlist] Shuffle 기능 추가

jiseong·2022년 4월 12일
0

T I Learned

목록 보기
219/291
post-custom-banner

DJ playlist에서 Shuffle기능이 추가되었으면 좋겠다는 피드백을 받아 무작위 재생 기능을 어떻게 구현하면 좋을지 알아보게되면서 Fisher–Yates shuffle 라는 것을 알게되었다.

The Fisher–Yates shuffle is an algorithm for generating a random permutation of a finite sequence—in plain terms, the algorithm shuffles the sequence. The algorithm effectively puts all the elements into a hat; it continually determines the next element by randomly drawing an element from the hat until no elements remain. The algorithm produces an unbiased permutation: every permutation is equally likely.

위키피디아에 의하면 Fisher–Yates shuffle은 유한한 시퀀스의 랜덤 순열을 생성하기 위한 알고리즘으로 편향되지 않는 순열을 생성한다고 한다.

-- To shuffle an array a of n elements (indices 0..n-1):
for i from n−1 downto 1 do
     j ← random integer such that 0 ≤ j ≤ i
     exchange a[j] and a[i]

pseudo code는 위와 같으며 이제 이걸 코드로 구현하면 다음과 같이 나타낼 수 있다.

const shufflePlaylist = (oriPlaylist: IMusic[]) => {
  const copiedPlaylist = [...oriPlaylist];

  for (let i = copiedPlaylist.length - 1; i > 0; i -= 1) {
    const j = Math.floor(Math.random() * (i + 1));

    [copiedPlaylist[i], copiedPlaylist[j]] = [
      copiedPlaylist[j],
      copiedPlaylist[i],
    ];
  }
  
  return copiedPlaylist;
};

그리고 적절하게 섞여지는지 확인하기 위해 약 10만번 테스트해보았을때 현재 재생중인 노래를 제외하고 편향되지 않게 노래가 선택되는 것을 확인할 수 있었다.

그렇다면 이제 구현한 셔플기능을 기존의 코드에 적용해주면 되는데 shuffle 기능이 켜져있는지에 따라서 기존의 플레이리스트를 사용할지 셔플된 플레이리스트를 사용하면 될지만 결정해주면 되었다.

const handleChangeMusic = ({ music, isNext }: IMusicChange) => {
  const playlistLen = playlist.length - 1;
  const newPlaylist = shuffle ? shufflePlaylist(playlist) : playlist;

  const curPlayMusicIdx = getCurrentMusicIdx({
    newPlaylist,
    curMusic: music,
  });

  if (curPlayMusicIdx === NOT_INCLUDE_DJPLAYLIST) return;

  const nextMusic = isNext
  ? newPlaylist[curPlayMusicIdx === playlistLen ? 0 : curPlayMusicIdx + 1]
  : newPlaylist[curPlayMusicIdx === 0 ? playlistLen : curPlayMusicIdx - 1];

  setPlayer((prev) => ({
    ...prev,
    selectedMusic: nextMusic,
  }));
};

최종 결과


Reference

post-custom-banner

0개의 댓글