ServiceNowでnumer0nを作ってみた


# はじめに
「Numer0n」というゲームを知っていますか？
詳細なルールは後述しますが、簡単に言うと相手が選んだ数字を当てるゲームです。
非常にシンプルなルールですが、それ故に高度な思考力と運が必要なため、とても難しいです。

今回はServiceNowを用いてNumer0nを作ってみようと思います。

## Numer0nのルール
・それぞれのプレイヤーが、0-9までの数字が書かれた10枚のカードのうち3枚を使って、3桁の番号を作成する。カードに重複は無いので「550」「377」「999」といった同じ数字を2つ以上使用した番号は作れない。

・先攻のプレイヤーは相手の番号を推理してコールする。相手はコールされた番号と自分の番号を見比べ、コールされた番号がどの程度合っているかを発表する。数字と桁が合っていた場合は「EAT」（イート）、数字は合っているが桁は合っていない場合は「BITE」（バイト）となる。
　・例として相手の番号が「765」・コールされた番号が「746」であった場合は、3桁のうち「7」は桁の位置が合致しているためEAT、「6」は数字自体は合っているが桁の位置が違うためBITE。EATが1つ・BITEが1つなので、「1EAT-1BITE」となる。

・これを先攻・後攻が繰り返して行い、先に相手の番号を完全に当てきった（3桁なら3EATを相手に発表させた）プレイヤーの勝利となる。

(引用：https://ja.wikipedia.org/wiki/Numer0n#%E3%83%AB%E3%83%BC%E3%83%AB)

## バージョン
サンディエゴ

## 要件定義
Numer0nと言っても一人で遊ぶ用の数当てゲームです。
１つのレコード上で遊べるように作成していきます。
#### 設定できる数
0～9 の 3桁
※同じ数字は不可

#### フィールド
- 「Call1～3」（Integer）：コールする数字
- 「Answer1～3」（Integer）：相手の数字（読み取り専用）
- 「Look」（True/False）：相手の数字を表示/非表示
- 「Result」（String）：コールの結果

#### ビジネスルール
レコード作成時にAnswer1～3をランダムで設定する。

#### UIアクション「Call」
ボタンを押すと「Call1～3」フィールドと「Answer1～3」フィールドを見比べて、結果を「Result」フィールドに追記する。
【例】
Call1：4　　Answer1：4
Call2：1　　Answer2：7
Call3：7　　Answer3：9
⇒4 1 7 --- 1EAT-1BITE

#### UIポリシー
「Look」フィールドがFalseの場合は「Answer1～3」を非表示する。（デフォルト）
「Look」フィールドがTrueの場合は「Answer1～3」を表示する。


## 画面の作成
１．System Definition > Tables　から「Numer0n」というテーブルを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073316/dd376e5c-b662-6144-387d-3d4c051d225c.png)


２．「Numer0n」テーブルに必要なフィールドを追加する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073316/c49fe3c2-2830-7da3-74f5-1aff84747402.png)


:::note warn
「Call1～3」フィールドと「Answer1～3」フィールドは選択肢にすると選択しやすいです。
:::
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073316/01d15408-b5c5-91b0-8de0-fbd392bb51b2.png)

## 機能の作成

#### ビジネスルールで「Answer1～3」の整数をランダム生成する
ビジネスルールを作成し、レコード作成時の「Answer1～3」フィールドに異なる整数をランダムで設定する。
Name：RandomAnswer
Table：Numer0n[u_numer0n]
Advanced：True
Insert：True
Script：以下に記載
```js
(function executeRule(current, previous /*null when async*/ ) {

    var ans = [];

    //重複しない３つの数字を取得
    while (ans.length < 3) {
        var rand = Math.floor(Math.random() * 10);
        if (ans.indexOf(rand) == -1) ans.push(rand);
    }

    current.u_answer1 = ans[0];
    current.u_answer2 = ans[1];
    current.u_answer3 = ans[2];

})(current, previous);
```
---------------------------

#### UIアクション「Call」を作成する
Name：Call
Table：Numer0n[u_numer0n]
Form button：True
Script：以下に記載
```js
var call = new Array(current.u_call1, current.u_call2, current.u_call3); //Call1～3
var answer = new Array(current.u_answer1, current.u_answer2, current.u_answer3); //Answer1～3

//Call1～3の重複確認
if((call[0]==call[1] || call[0]==call[2])||call[1]==call[2]){
	gs.addErrorMessage("Call1～3に重複している値があります。");
	current.setAbortAction(true);
}

var result = current.u_result; //結果出力用
var resultTmp; //今回の結果
var eat = 0; //EAT
var bite = 0; //BITE

//EATとBITEの数
for (i = 0; i < 3; i++) {
    if (call[i] == answer[i]) eat++;

    switch (i) {
        case 0:
            if (call[i] == answer[i + 1]) bite++;
            if (call[i] == answer[i + 2]) bite++;
            break;

        case 1:
            if (call[i] == answer[i - 1]) bite++;
            if (call[i] == answer[i + 1]) bite++;
            break;

        case 2:
            if (call[i] == answer[i - 2]) bite++;
            if (call[i] == answer[i - 1]) bite++;
    }
}

//結果出力
resultTmp = call[0] + " " + call[1] + " " + call[2] + "  ---  " + eat + "EAT - " + bite + "BITE\n";
result = result + resultTmp;
current.u_result = result;

if(eat == 3){
	gs.addInfoMessage("Numer0n！！！おめでとう👍");
}

//レコードにとどまる
current.update();
action.setRedirectURL(current);
```
---------------------------
#### UIポリシーで表示制御をする
「Look」フィールドがFalseの場合は「Answer1～3」を非表示する。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073316/339991c7-e2dc-9587-2aa0-05e0c94a6ec1.png)
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073316/d085bd5e-2fd3-b3aa-e368-89256c6a3697.png)


---------------------------

### 実際に遊んでみる
では実際に遊んでみましょう！

まずは「Numer0n」テーブルに新規でレコードを作成します。
「Look」がFalseなので「Answer1～3」フィールドが非表示となっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073316/e4221e48-da20-4695-625b-025c72c200d9.png)

まずは１回目のコールです。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073316/5dbdafae-de9a-ffb6-23c9-a88fdd87aa36.png)
012で0EAT-2BITE。1回目でこれは上場ですね！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073316/bd6878ec-6f1a-edb7-9095-8f521f9ee149.png)
続けて2回目のコールをしていきます。
345で0EAT-0BITE。どんどん絞れてきましたね。
コール結果は追記させているので過去の結果も見れるようになっています。
（「Result」フィールドは読み取り専用にしても良さそうですね）
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073316/c838dfda-7681-122d-1551-8f3c0a64d0b4.png)
続けてコールして数字を当てることが出来ました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073316/c5379937-d861-a015-4dbf-cc0518c4de9a.png)
「Look」フィールドをTrueにして「Answer1～3」フィールドを見てみましょう。
3EATしていることも確認できました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073316/e9dad652-19db-5cda-ccd2-d2119cb93511.png)
補足：「Call1～3」で同じ値をコールしようとしたら、警告を出して更新出来ないようにしています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3073316/b0b063d8-ab58-bb1a-638e-ec09df8616a1.png)

### さいごに
今回ServiceNowを用いてNumer0nを作成しました。
追加オプションとして、他ユーザーと対戦できるようにしたり、High&Low機能（「Answer」の数字がHigh:5～9,Low:0～4を教えてくれる）を搭載したりと、まだまだ拡張できると思います。

ServiceNowのデータベース機能だけでNumer0nの様にシンプルなゲームは作れると思います。
是非色んなゲームを作ってみて下さい。