八百長モード搭載のルーレットつくった

## はじめに
こんにちは。デザイナーの@nagatです。
「Aucfan Advent Calendar 2020」も23日目。もうすぐクリスマスですねー＞＜

突然ですが、イベントの実行委員や定例会議の司会を、手っ取り早く決めるときルーレットやクジを使うケースって多いですよね？
完全ランダムで担当者を決めてくれるので、ネット上で公開されている無料のルーレットツールはプライベートでもとても重宝しています＾＾

ただ、実行時に相手にバレず担当者を狙い撃ちできる八百長モード搭載のルーレットツールって探しても無いんですよねー。

ネタに困っていたので作りました！
注）ただの一発ネタの記事です。


## 要求される仕様
1. webブラウザで実行できる
2. ルーレット候補を自由にtextareaに入力できる
3. 完全ランダムの公平なルーレットができる
4. 特定の相手を指定できるモードが搭載されている
5. 八百長モード実行時に相手にバレない


## 使用したもの
* gulp
* webpack
* html
* css(scss)
* JavaScript
  * [jQuery](https://jquery.com/)
  * [Swiper.js](https://swiperjs.com/)
  * [Velocity.js](http://velocityjs.org/)


## まずはランダム性のある公平なルーレット機能を作る
骨組みはhtmlとcssで作り、ルーレット機構はJavaScriptで作成します。
こういう自作ツールは、裏で何してるか分からないので、
公平性を出すためにツール名は「__絶対に八百長しないRoulette__」と名付けました＾＾
タイトルはcssアニメーションを使用して、虹色に怪しく輝きます。

![タイトル.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75700/239abc31-c3bf-0e64-7c6a-1f1c6e21aaa1.gif)

登場する名前は [苗字のトレンドランキング](https://myoji-yurai.net/halfYearAccessRanking.htm) から適当に入力しています。

ルーレット自体の機構はスライダーで有名なSwiper.jsを無理やりルーレットぽい挙動に動かしてそれっぽく作ります。
画面のtextareaに改行を入れながら候補を入力し、「はじめる」ボタンを押下することでルーレットが開始されます。

また、本来カルーセル等に使うSwiper.jsでルーレットぽく無理やり作成しているので、ルーレット実行時に乱数を使用して__候補の順番を入れ替えることで__ランダム性を出しています。

```javascript
/**
 * エントリーリストの取得
 * @return {Array.<String>} エントリーネームと画像ナンバーの配列
 */
getEntryList() {
    let t = this;
    let entryTxtareaElem;
    let entryListVal;
    let entryNameList;
    let entryListData;

    entryListData = {
        all: [],
    };

    entryTxtareaElem = $(t.config.entryTxtarea);
    entryListVal = t.trimText(entryTxtareaElem.val());
    entryNameList = entryListVal.split(/\n/);

    entryNameList.forEach((elem, i) => {
        let num;

        if ((i + 1) < 10) {
            num = '0' + (i + 1);
        } else {
            num = i + 1;
        }

        entryListData.all[i] = {
            name: elem,
            imgNum: num,
        };
    });

    entryListData.all = t.shuffle(entryListData.all);

    return entryListData;
}

/**
 * 配列内の要素をシャッフル
 * @param  {Array} array シャッフルする対象の配列
 * @return {Array} シャッフルされた配列
 */
shuffle([...array]) {
    for (let i = array.length - 1; i >= 0; i--) {
        const j = Math.floor(Math.random() * (i + 1));
        [array[i], array[j]] = [array[j], array[i]];
    }

    return array;
}

```

テキストだけだと寂しいので、イケメンや美女の写真を適当に入れてみました。
![ルーレット（序盤）.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75700/f30c5d73-b0d4-d405-8e22-4aae7d5c27e8.gif)
![ルーレット（確定）.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75700/5b002207-c784-3f86-77e9-811f0df99842.gif)
確定する寸前に遅延をかけてちょっと焦らし演出を付けました。
まずは、公平性のあるルーレットが完成しました！


## 八百長するぞ
### 八百長モードの流れ
1. textarea内でキーボード入力実行で八百長モード発動
2. textarea内の候補リストから八百長対象となる候補を除外する
3. 八百長対象以外の候補のリストでルーレットを回す
4. ルーレットの途中で確定演出（茶番）を入れる
5. すべてのスライダー要素を八百長対象の写真とテキストに入れ替える（大いなる力）
6. フィニッシュ（無慈悲）

### キーボード入力実行で八百長モード発動
八百長モードを有効にしてルーレットを回すにしても、開始時点で相手に八百長行為がバレてはいけません。
なので、八百長モード有効のトリガーをボタンクリックではなくキーボード入力にします。

textareaにキャレットを乗せている状態で、
Macは `⌘ + Enter` で発火。
Windowsは `Ctrl + Enter` で発火。
キャレットが表示されている行の名前の人が八百長の~~餌食~~ 対象になる人です。

```javascript
let t = this;
let entryData;

$('textarea').on('keydown', (event) => {
    if ((event.ctrlKey || event.metaKey) && event.keyCode === 13) {
        let self = event.currentTarget;
        let selin = self.selectionStart;
        let val1 = self.value;
        let val2 = val1.substr(0, selin);
        let matchLine = val2.match(/\n/g);
        let row = 0;

        if (matchLine) {
            row = matchLine.length;
        } else {
            row = 0;
        }

        // 候補リストの取得
        entryData = t.getEntryList(row);

        // ルーレットの実行
        t.playing();
    }
});
```

### 八百長対象となる候補を除外する
先述したとおり、ランダム性はスライダーの並び替えで出しているので、八百長の対象者をルーレット実行時に制御できません。。。
なので、一旦八百長対象者をリストから除外します。

```javascript
/**
 * エントリーリストの取得
 * @return {Array.<String>} エントリーネームと画像ナンバーの配列
 */
getEntryList(yaochoNum) {
    let t = this;
    let entryTxtareaElem;
    let entryListVal;
    let entryNameList;
    let entryListData;

    entryListData = {
        pickup: [],
        all: [],
    };

    entryTxtareaElem = $(t.config.entryTxtarea);
    entryListVal = t.trimText(entryTxtareaElem.val());
    entryNameList = entryListVal.split(/\n/);

    entryNameList.forEach((elem, i) => {
        let num;

        if ((i + 1) < 10) {
            num = '0' + (i + 1);
        } else {
            num = i + 1;
        }

        entryListData.all[i] = {
            name: elem,
            imgNum: num,
        };
    });

    if (yaochoNum != null && typeof yaochoNum != 'undefined') {
        t.yaochoMode = true;
        entryListData.pickup[0] = entryListData.all[yaochoNum];

        entryListData.all = entryListData.all.filter((value) => {
            return value.name != entryListData.all[yaochoNum].name;
        });
    } else {
        t.yaochoMode = false;
        entryListData.pickup[0] = null;
    }

    entryListData.all = t.shuffle(entryListData.all);

    return entryListData;
}
```


### 確定演出を入れる
八百長対象者が居ないリストを、八百長対象者にすべて差し替える動作を、ゲームのガチャみたいな確定演出を入れて誤魔化します。

![ルーレット.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75700/1d161ec7-6f88-869e-83bc-6309a8e0e643.gif)

カットインは写真だけじゃ物足りなかったので、テキストも入れてみる。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75700/6489a60b-44dd-3e06-6250-601f4b0d1695.png)

今回のルーレット一番の見どころ。
大いなる力ですべての要素が渡辺さん（仮名）に入れ変わる瞬間。

![ルーレット.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75700/236121fc-ab10-2af0-3851-2255d38f01c5.gif)

うおおおおおおおおおおおおおおおおおおおおお

![渡辺.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75700/450dff93-1609-3281-78bf-dbf8e00921f0.gif)

フィニッシュ

![フィニッシュ.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75700/c0708092-87c6-d9c7-e8b9-f2f0c3ef9d02.gif)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75700/4be3e3b1-eccb-58ba-7427-bcd4f6dace98.png)


## 「絶対に八百長しないRoulette」はこちら
https://nagatsan.github.io/yaocho-roulette/

9名までなら画像が表示されます。
あ、画像のプリロード入れるの忘れてた... (；´Д｀)


## おまけ
候補リストに「肉」って入れたら、フィニッシュの画面でオデコに丁度収まった＾＾；
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/75700/b2a468bc-5ccd-a15d-8790-efb6045975eb.png)


## さいごに
当社ではコーディングが好きなデザイナーを募集中です！
当社に興味を持っていただける方がいらっしゃれば、お気軽にお話だけでも聞きに来てください！