서론

동적 웹에 대해 막 배우기 시작했을 때 재미삼아 mvc구조로 뽑기 시뮬레이터를 만든 적이 있다.
모델링 개념도 없던 시절이라 DB 구조부터 모든게 엉성하기 짝이 없었지만 나름 만족할만한 결과가 나왔다. 하지만 웹 호스팅을 할 여력이 없어 배포하지 못했던게 많이 아쉬웠다.
github Pages를 이용하면 간편하게 배포가 가능하단 말에 알아봤지만 jsp 파일을 지원하지 않을 뿐더러 애초에 mvc 구조에 db까지 돌린다는게 말이 안되더라.
jekyll 블로그를 이용하게 되어 html 파일의 배포가 쉬워진 지금, 이 가챠 시뮬레이터를 배포할 방법이 없을까 이리저리 고민해본 결과 Collection을 DB로 활용하자는 아이디어가 떠올랐고 곧바로 개발을 시작했다.

구조

Collection 구조는 다음과 같다.

구조_image

전체 DB 역할을 하는 Characters Map에 등급별 Array를 삽입한 뒤 거기에 각각의 캐릭터 데이터를 넣는 간단한 방식이다.
페이지를 불러올때마다 일일이 데이터를 입력해야 하기 때문에 썩 마음에 들진 않는 방식이지만 대안을 떠올릴 수 없어 이대로 진행했다.
아래는 실제 코드부분이다.

    //Collection으로 DB 대체
    var characters = new Map();
    var normal = new Array();
    var rare = new Array();
    var superRare = new Array();
    //캐릭터 정보 column Insert
    function dbInit(){
        characters.set('normal',normal);
        characters.set('rare',rare);
        characters.set('superrare',superRare);
        normal.push('N-스레기');
        normal.push('N-수레기');
        normal.push('N-슈레기');
        normal.push('N-시레기');
        normal.push('N-개스레기');
        normal.push('N-쑤레기');
        rare.push('R-평타');
        rare.push('R-평범');
        rare.push('R-괜찮');
        rare.push('R-오히려좋아');
        superRare.push('SR-대바아아아악');
        superRare.push('SR-운다씀');
    }

뽑기

다음은 뽑기 로직 부분이다.

뽑기_image
먼저 0에서 1 사이의 난수를 생성한다.
이후 해당 숫자가 어느 구간에 속하느냐에 따라 등급을 결정한다.
예를 들어 SR등급 확률이 10%(0.1), R등급 확률이 30%(0.3)인 혜자 뽑기라고 가정했을 때 0.9~1.0 구간은 SR등급, 0.6~0.9 구간은 R등급, 그 이외에는 N등급이 된다.
이번에도 코드와 함께 살펴보겠다.

우선 확률을 상수로 설정해준다.

    //확률정보
    const srRate = 0.025;
    const rRate = 0.1;

간단한 통계자료를 제공하기 위해 뽑은 횟수도 전역변수로 선언했다.

    //시행정보 전역변수
    var pickN = 0;
    var pickR = 0;
    var pickSR = 0;

등급을 결정한 뒤 전역변수를 1 올려주고 등급명(Characters Map의 Key값)을 return 한다.

    //뽑기결과 등급 결정
    function gatcha(){
        let random = Math.random();
        let result = '';
        if(random > (1-srRate)){
            pickSR++;
            result = 'superrare';
        }else if(random > (1-srRate-rRate)){
            pickR++;
            result = 'rare';
        }else{
            pickN++;
            result = 'normal';
        }
        console.log(random);
        console.log(result);
        return result;
    }

등급이 정해졌으면 다음으로 어떤 캐릭터가 뽑혔는지를 결정해야 한다.
해당 등급의 배열 사이즈를 측정한 뒤 0 이상, 사이즈 이하의 무작위 난수를 생성해 해당 인덱스의 요소가 뽑힌 캐릭터가 되도록 설계했다.

    //등급 내 뽑은 캐릭터 결정
    function selectCharacter(grade){
        let range = characters.get(grade).length;
        console.log(range);
        let pick = rand(0,(range-1));
        console.log(pick);
        return characters.get(grade)[pick];
    }
    //범위 내 자연수 난수 생성
    function rand(min, max) {
        return Math.floor(Math.random() * (max - min + 1)) + min;
    }

출력

마지막으로 jQuery를 통해 사용자에게 결과를 전달해주면 모든 프로세스가 마무리된다.

    //가챠버튼 눌렀을 때 param만큼 뽑기 프로세스 진행
    function process(param){
        let htmlText = '';
        let grade;
        let picked;
        for(let i=0; i<param; i++){
            grade = gatcha();
            picked = selectCharacter(grade);
            htmlText += '<p>'+grade+'등급의 '+picked+' 캐릭터를 뽑았다!!</p>';
        }
        htmlText += '<p>뽑은횟수 : '+(pickN+pickR+pickSR)+' N등급 : '+pickN+'('+((pickN/(pickN+pickR+pickSR))*100).toFixed(2)+'%)'+
            ' R등급 : '+pickR+'('+((pickR/(pickN+pickR+pickSR))*100).toFixed(2)+'%)'+' SR등급 : '+pickSR+'('+((pickSR/(pickN+pickR+pickSR))*100).toFixed(2)+'%)'+'</p>';
        $('div#resultWindow').html(htmlText);
    }

결과


결과_image

테스트페이지

아직 눈으로 보기엔 조잡하지만 기능 개발이 끝난 후 css작업에 착수하고 결과를 이미지로 표시하게 되면 꽤나 그럴듯해질것이라 기대한다.
추가하고 싶은 기능이 아직 많이 남아서 당분간 즐겁게 갖고 놀 수 있을 것 같다.
다음 포스팅때는 픽업뽑기 기능 추가에 대해 다뤄보려 한다.