前回、基本的なところをお試ししてみましたが、

もう少しなんかやらせてみたいので、数あてゲームをやらせてみます。

ランダムな数字を、ヒント(High or Low)を基に当てる超楽しいゲームね。。。

実用性は皆無ですが、条件分岐、ループ、演算と、一通りのことを試すのにはいいかなと。

HTMLはこんな感じにしました。

<!doctype html>
<html ng-app="app">
<head>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.5/angular.min.js"></script>
</head>
<body ng-controller="ctrl">
<div>
Random=<span id='val'>{{val}}</span><br>
Hint:<span id='hint'>{{hint}}</span><br>
<input id="expect" type="text" ng-model="expectNum"/><input type="button" ng-click="doExpect()" value="expect!" id="doExpect"/><br>
you guessed <span id='times'>{{challenge}}</span> times.
</div>
</body>
<script>
var app = angular.module("app", []);
app.controller("ctrl", function ($scope, $interval) {
var min = 1 ,max = 100 ;
$scope.val = Math.floor( Math.random() * (max + 1 - min) ) + min ;
$scope.challenge=0;
$scope.doExpect=function(){
$scope.hint="";
var num=parseInt($scope.expectNum, 10);
if(!isNaN(num)){
$scope.challenge++;
if(num==$scope.val){
$scope.hint="correct!";
} else if(num<$scope.val){
$scope.hint="low";
} else {
$scope.hint="high";
}
}
};
});
</script>
</html>

まぁ、素直なコードかと。。。

問題のNightmare部ですが、ループや条件分岐を使うので、Promise/thenではきついと思われます。

ので、Generatorとvoを使用します。

1
> npm install vo --save

こちらを激しく参考にさせてもらいました

んで、できたコードがこちら。

const Nightmare = require('nightmare'); // import から requreに変更
const cheerio = require('cheerio');
const vo = require('vo');
function gotoHighLow(nm){
return nm
.goto('http://localhost:8000/angularsample2.html')
.wait('#expect');
}
function doExpect(nm, num){
return nm
.type('#expect')
.type('#expect', num)
.click('#doExpect')
.evaluate(() => {
return document.getElementsByTagName('body')[0].innerHTML;
});
}
//メインロジック
//yield を使のでfunction*
function* searchNumber(nm){
let min=1,max=100; //予想範囲
let hint='';
let times='';
while(hint!='correct!'){ ///正解するまでループ
let expectedNum=Math.floor((min + max)/2); //番号を予想(予想範囲の真ん中を使う)して
let doc=yield doExpect(nm,expectedNum); //お伺いを立てる
const $ = cheerio.load(doc); //結果をパース
hint = $("#hint").text();
times= $("#times").text();
console.log(min + '<' + expectedNum + '<' + max + ':' + hint);
if( hint=='low'){ //結果をもとに、番号予想範囲を狭める
min=expectedNum;
} else if(hint=='high'){
max=expectedNum;
}
}
return parseInt(times); //成功までに要した試行回数を返す。
}
//全体処理
vo(function* () {
let nightmare = Nightmare({ show: true });
yield gotoHighLow(nightmare); //ゲームページに移動
let times=yield searchNumber(nightmare); // ゲームします
yield nightmare.end(); // 終わります
return times;
})(function (err, result) {
if (err) return console.log(err);
console.log(result + "Times");
});

Generatorの使い方がミソですな。

今回function/yieldの概念を初めて使いましたが、<a href=“https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Statements/function” rel=“noopener” target=”_blank”>この辺読んでも意味が分かりません。

要はyield使うときは「function*」にしとけってことでしょうか?

今までコールバックとかPromiseとかで独特な記述になってた所が、素直に書けるようになったようです。

印象としてはC#のasync/awaitみたいな感じでしょうかね。

JSエンジンによってかなり実装が違うようです。ブラウザ向けのスクリプトじゃまだ使えなさそう。

実行するとこんな感じ

Nightmare使えそうです。

MoneyForwardが非対応なサービスのデータを、自動で取り込めるようにしたいなぁ。

さしあたってAEONのネットスーパー。対応してくんないかな。