/*----------------------------------------
* flash-anzan.js
* Copyright (C) 2022 StraightApps.com, All Rights Reserved
* April 22, 2022 初期版
*-----------------------------------------*/
//----------------------------------------
// グローバル変数/オブジェクト
var gameLevel = 1; // ゲームレベル
var gameCount = 5; // 1ゲームで表示する数字数
var gameSpeed = 2; // 表示速度
var num = 0; // 0 はゲーム開始前、1 以上は表示済みの数字数
var numNow = 0; // 現在表示中の数字
var gameAnswer = 0; // 正解
var userAnswer = 0; // 入力した回答
var numHistory = []; // 要素数未定の配列
var gameRetry = 0; // リトライ中?
var startTime = 0; // 現在の数字の表示開始カウント
var endTime = 0;
var sepTime = 0; // セパレート終了カウント
var drawNumber = 0; // 数字を描画するタイミングかどうか
var sepDuration = 200; // 数字が消えているのは 200ms
//----------------------------------------
// canvas の設定
// この部分は、id "canvas-number" の定義後でないと、失敗します。
var canvas = document.getElementById('canvas-number');
var w = document.documentElement.clientWidth;
var h = 48;
if (w > 240){ // 十分広い場合は
w = 240; // 240 ピクセルまでとします
}
canvas.width = w; // canvas の幅設定
canvas.height = w; // canvas の高さ設定
//----------------------------------------
// コンテキストを取得
var ctx = canvas.getContext('2d');
//----------------------------------------
// 数値を右詰め表示するときの x 位置を決めます。
ctx.font = "36px 'ヒラギノ角ゴ Pro', 'Hiragino Kaku Gothic Pro', 'MS ゴシック', 'MS Gothic', sans-serif";
//----------------------------------------
// 新しい問題を作成します。
//makeNewNumber();
//----------------------------------------
// レベル・コンボボックスを作成します。
setLevelCombo();
//----------------------------------------
// 表示個数コンボボックスを作成します。
setCountCombo();
//----------------------------------------
// 表示速度コンボボックスを作成します。
setSpeedCombo();
//----------------------------------------
// 初期状態で利用できないボタンをグレーにします。
setButtonState(0);
/*----------------------------------------
* メインループ
*-----------------------------------------*/
function main()
{
//----------------------------------------
// canvas を指定の色で塗りつぶします。
ctx.fillStyle = "rgb( 230, 230, 230 )";
ctx.fillRect(0, 0, canvas.width, canvas.height);
//----------------------------------------
// デバッグ用文字列を出力します。
//var elem = document.getElementById('debug');
//elem.innerHTML= data;
//----------------------------------------
// ゲーム中は、数字を canvas に描画します。
if (num > 0){
// 現在の時刻(ミリ秒単位)を取得します。
var now = new Date();
var curTime = (now.getTime() % 10000); // 9秒 999までの値
var data = curTime + ' 秒とミリ秒';
// canvas の背景色を、数字表示ごとに切り替えます。
// これに該当しない場合は、rgb(230,230,230) でクリアされています。
if ((num % 2) == 0){
ctx.fillStyle = "rgb( 200, 200, 200 )";
ctx.fillRect(0, 0, canvas.width, canvas.height);
}
// 新しい数字を設定後、最初の 100ms は描画しないこととします。
if (startTime < sepTime){ // 例えば 1000 開始で 1100 終わりのとき
//elem.innerHTML= curTime + ' 時点 ' + startTime + ' から ' + sepTime + ' と ' + endTime + ' まで ';
if (curTime > sepTime){ // 経過した場合
drawNumber = 1;
}
}
else{ // 例えば 9950 開始で 50 終わりのとき
//elem.innerHTML= curTime + ' 時点 ' + startTime + ' から ' + sepTime + ' と ' + endTime + ' まで ';
if (curTime > sepTime && curTime < startTime){
drawNumber = 1;
}
}
// 数字を描画します。
if (drawNumber > 0){
// フォントを決定します。
var fontSize = Math.floor(canvas.width * 4 / 5);
ctx.font = fontSize + "px 'ヒラギノ角ゴ Pro', 'Hiragino Kaku Gothic Pro', 'MS ゴシック', 'MS Gothic', sans-serif";
//ctx.font = "36px 'ヒラギノ角ゴ Pro', 'Hiragino Kaku Gothic Pro', 'MS ゴシック', 'MS Gothic', sans-serif";
// 加算は黒で、減算は赤+枠付きで描画します。
if (numNow >= 0){
var x = (canvas.width - ctx.measureText( numNow ).width) / 2; // 中央あわせ
var y = canvas.height - (canvas.height - fontSize ) / 2;
ctx.fillStyle = '#000'; // 黒字
ctx.fillText( numNow, x, y ); // canvas 領域の座標(y座標は'下'を指している)
}
else{
var x = (canvas.width - ctx.measureText( -numNow ).width) / 2; // 中央あわせ
var y = canvas.height - (canvas.height - fontSize ) / 2;
ctx.fillStyle = '#000'; // 黒
ctx.fillRect(0, 0, canvas.width, canvas.height * 0.1);
//ctx.fillRect(0, canvas.height - canvas.height * 0.15, canvas.width, canvas.height * 0.15);
ctx.fillStyle = '#d00'; // 赤字
ctx.fillText( -numNow, x, y ); // 符号は表示しません
}
// 時間になったら、新しい数字を関数で作成するか、
// あるいは最後まで表示したら終わり。
// 数字と数字の間は一瞬消すべき? 背景色をトグルさせるべき?
// 連続で同じ数字を否定はしないが、わからなくならないようにすること。
var nextNumber = 0;
if (startTime < endTime){ // 例えば 1000 開始で 1800 終わりのとき
//elem.innerHTML= '順接 ' + curTime + ' 時点 ' + startTime + ' から ' + sepTime + ' と ' + endTime + ' まで ';
if (curTime > endTime){ // 経過した場合
nextNumber = 1;
}
}
else{ // 例えば 9500 開始で 300 終わりのとき
//elem.innerHTML= '逆接 ' + curTime + ' 時点 ' + startTime + ' から ' + sepTime + ' と ' + endTime + ' まで ';
if (curTime > endTime && curTime < startTime){
nextNumber = 1;
}
}
if (nextNumber > 0){
// 必要な数だけ表示済み?
if (num >= gameCount){
// ゲームを止めます。
num = 0;
/*
var data = '正解は ' + gameAnswer + '
';
// 経過を
for ( var i = 0; i < numHistory.length; i++ ){
if (i > 0 ){
data += ' + ';
}
data += numHistory[i];
}
var elem = document.getElementById('debug');
elem.innerHTML= data;
*/
// 回答をクリアして、ボタンを利用可能にします。
numReset();
setButtonState(1);
}
// 続きがある場合
else{
num ++;
// リトライ中の場合
if (gameRetry == 1){
numNow = numHistory[num - 1];
}
// リトライ中ではない場合
else{
// 新しい数字を設定
// レベルによって、プラスマイナスなど
// 最終的な正解が 0 になることは避ける!!
//numNow = Math.floor(Math.random() * 9) + 1; // 関数で!!
numNow = getNextNumber();
gameAnswer += numNow;
// 入力履歴配列を記録します。
numHistory.length ++;
numHistory[numHistory.length - 1] = numNow;
}
// 次の数字の表示時間を設定します。
setTimers(curTime);
//elem.innerHTML= curTime + ' 時点 ' + startTime + ' から ' + sepTime + ' と ' + endTime + ' まで ';
}
}
}
}
requestAnimationFrame( main );
}
/*----------------------------------------
* ページと、依存している全てのデータが読み込まれたら、
* メインループを開始します。
*-----------------------------------------*/
addEventListener('load', main(), false);
/*----------------------------------------
* ボタンの状態を設定します。
* btnState が 0 のとき、利用不可にします。
* btnState が 1 のとき、利用可能にします。
*-----------------------------------------*/
function setButtonState(btnState)
{
var btnDisabled = true;
if (btnState > 0){
btnDisabled = false;
}
// 「やりなおす」ボタンの状態を設定します。
document.getElementById("btnRetry").disabled = btnDisabled;
// 回答入力用の数字ボタンの状態を設定します。
for (var i = 0; i < 10; i++ ){
document.getElementById("btnNum" + i).disabled = btnDisabled;
}
// リセットボタンの状態を設定します。
document.getElementById("btnNumReset").disabled = btnDisabled;
// ギブアップボタンの状態を設定します。
document.getElementById("btnGiveUp").disabled = btnDisabled;
}
/*----------------------------------------
* 「スタート」ボタンで新しいゲームを開始します。
*-----------------------------------------*/
function gameStart()
{
// num = 0 のときはゲーム開始前で、
// ゲーム開始後は、表示済みの数字数をカウントしています。
num = 1;
// ゲームの正解をクリアします。
gameAnswer = 0;
// 最初に表示する数字を決定します。
numNow = getNextNumber();
// 入力履歴配列を初期化します。
numHistory.length = 1;
numHistory[0] = numNow;
gameAnswer = numNow;
// リトライ中ではありません。
gameRetry = 0;
// ゲーム中であることを示すメッセージにします。
//var elem = document.getElementById("answer");
document.getElementById("answer").innerHTML = '数字の表示が終わったら、回答を入力できます。';
document.getElementById("history").innerHTML = '「ギブアップ」ボタンを押すか正解すると、ここに式が表示されます。';
// 数字の表示時間を設定します。
setTimers(-1);
}
/*----------------------------------------
* 「やりなおす」ボタンで前回のゲームを繰り返します。
*-----------------------------------------*/
function gameRestart()
{
// num = 0 のときはゲーム開始前で、
// ゲーム開始後は、表示済みの数字数をカウントしています。
num = 1;
// 前回の履歴をたどります。
numNow = numHistory[num - 1];
//gameAnswer = numNow;
// リトライ中です。
gameRetry = 1;
// ゲーム中であることを示すメッセージにします。
//var elem = document.getElementById("answer");
document.getElementById("answer").innerHTML = '数字の表示が終わったら、回答を入力できます。';
document.getElementById("history").innerHTML = '「ギブアップ」ボタンを押すか正解すると、ここに式が表示されます。';
// 数字の表示時間を設定します。
setTimers(-1);
}
/*----------------------------------------
* 数字の表示時間を設定します。
*-----------------------------------------*/
function setTimers(curTime)
{
if (curTime < 0){
var now = new Date();
curTime = (now.getTime() % 10000); // 9秒 999までの値
}
startTime = curTime;
endTime = ((startTime + getInterval()) % 10000);
sepTime = (startTime + sepDuration) % 10000;
// 描画タイミングではありません。
drawNumber = 0;
}
/*----------------------------------------
* 数字の表示間隔(ms)を返します。
*-----------------------------------------*/
function getInterval()
{
switch (gameSpeed){
case '0':
return 1500 + sepDuration;
case '1':
return 1000 + sepDuration;
case '2':
return 800 + sepDuration;
case '3':
return 600 + sepDuration;
case '4':
return 400 + sepDuration;
}
return 800 + sepDuration;
}
/*----------------------------------------
* 次に表示する数字を決定します。
* Level 1: 加算のみ
* Level 2: 減算あり、途中でマイナスなし(加算が多めになる)
* Level 3: 減算あり、途中でマイナスあり
* ※ 解答はマイナスなし
*-----------------------------------------*/
function getNextNumber()
{
// 1〜9 の乱数を発生させます。
var num = Math.floor(Math.random() * 9) + 1;
// Level 1: 加算のみ
if (gameLevel == 1){
return num;
}
// Level 2: 減算あり、途中でマイナスなし
if (gameLevel == 2){
// 25% の確率でマイナス値にします。
if (Math.random() < 0.25){
num = -num; // 符号反転
// これにより解答が(一時的に)マイナスになるなら、正に戻します。
if (gameAnswer + num < 0){
num = -num; // 符号反転(戻し)
}
}
return num;
}
// Level 3: 減算あり、途中でマイナスあり
if (gameLevel == 3){
// 40% の確率でマイナス値にします。
if (Math.random() < 0.4){
num = -num; // 符号反転
// これにより -9 より小さくなると、最終解答を正に戻せない可能性があるので禁止します。
if (gameAnswer + num < -9){
num = -num; // 符号反転(戻し)
}
// 最終回答がマイナスになるなら、プラスになるように調整します。
if (num == gameCount){ // 最終数字を作成中
while (gameAnswer + num < 0){ // 最悪でも 0 までは戻せます
num = Math.floor(Math.random() * 9) + 1;
}
}
}
}
return num;
}
/*----------------------------------------
* 「ギブアップ」ボタンで解答を表示します。
*-----------------------------------------*/
function gameGiveUp()
{
var data = '
正解は ' + gameAnswer + ' です。
';
// 経過を表示します。
for ( var i = 0; i < numHistory.length; i++ ){
if (i > 0 ){
if (numHistory[i] < 0){
data += ' - ';
}
else{
data += ' + ';
}
}
data += Math.abs(numHistory[i]);
}
var elem = document.getElementById('history');
elem.innerHTML= data + '
あなたの回答 ' + userAnswer + ' で、正解です!
'; // 「ギブアップ」ボタンと同様に、式も表示します。 gameGiveUp(); } // まだ正解ではない場合 else{ var elem = document.getElementById("answer"); elem.innerHTML = 'あなたの回答は ' + userAnswer + ' です。'; } } /*---------------------------------------- * 回答リセットボタンが押されたときの処理 *-----------------------------------------*/ function numReset() { userAnswer = 0; var elem = document.getElementById("answer"); elem.innerHTML = '回答を入力してください。'; }