使用技術
Java(練習のために使用)
SpringBoot (練習のために使用)
Javascript
HTML
CSS
スロットの説明
差枚数と設定
スロットマシンは、メダルを入れてレバーを叩く(もしくはボタンを押す)と、リールが回転し、絵柄が揃うことでメダルを獲得できるゲームです。
差枚数とは
その名の通り、投入したメダルの枚数から払い出されたメダルの枚数を引いた値のことです。スロット台の上にあるディスプレイには、この差枚数の増減がグラフで表示されます。
設定とは
スロットマシンの当選確率を調整する数値のことです。設定は1から6まであり、数字が大きくなるほど、大当たりであるBIGボーナスやREGボーナスの当選確率が高くなります。
(架空のスロット設定表。)
BIGボーナスとREGボーナス
スロットには、主に2種類のボーナスがあります。
BIGボーナス
メダルを多く獲得できるボーナス。獲得できる枚数は機種によって異なりますが、一般的には300枚程度です。
REGボーナス
BIGボーナスよりも獲得できる枚数は少ないボーナス。獲得できる枚数は機種によって異なりますが、一般的には100枚程度です。
これらのボーナスの当選確率は、設定によって異なり、設定が高いほど当選しやすくなります。スロット台のディスプレイには、その日のBIGボーナスとREGボーナスの当選確率が表示されます。
役とぶどう
スロットには、ボーナス以外にも様々な役が存在します。役とは、特定の絵柄の組み合わせのことです。例えば、同じ絵柄が3つ揃うとメダルが払い出されます。
役の中でも、特に重要なのがぶどうと呼ばれる役です。ぶどうは、他の役よりも出現率が低く、設定によって出現率が大きく異なります。そのため、ぶどうの出現率を調べることで、設定を推測することができます。
しかし、ぶどうの出現率は台のディスプレイには表示されません。そこで、ぶどう確率計算ツールを使って、過去のデータからぶどうの出現率を計算することができます。このツールを使うには、差枚数を含めた4つの情報を入力する必要があります。
なぜ、差枚数計算サイトを作成しようと思ったのか?
グラフの正確な差枚数が設定を推測する上で重要だからです。
大学の確率・統計学の講義において、スロットの確率の話がありました。スロットの台がどのぐらいBIG,REGなど様々な要素を用いて設定をどれだけ予想できるかというものでした。
ぶどう確率については、先ほど説明していたように台の上のディスプレイに表示されておらず、ぶどうシュミレーター(ぶどう確率を算出するサイト)で(回転数、BIG,REG,差枚数)の4つの情報を入力することで武道確率を計算します。この中で、(BIG,REG,差枚数)についてはディスプレイに表示されていますが、
差枚数についてはグラフ(下記画像)から、目視で読み取る必要があります。しかし、グラフの多くが1000刻み,2000刻みなど目盛りの幅がとても大きいため、目視で正確な差枚数を計算するのは難しい。また、ぶどう確率については、設定1〜6で大きな差があるわけではないので、100枚でも読み間違えるだけで大きく推定設定が外れてしまう問題があり正確に差枚数を計算することのできるサイトを作成しようと考えました。
差枚数計算システムの概要
(0の目盛りの位置,最終位置,グラフの目盛りの位置,グラフの目盛りの値)を指定、入力してもらう。
0の目盛りのY座標 : zero_y
グラフの目盛りY座標 : graph_y
最終位置のY座標 : final_y
グラフ目盛りの値 : graph_num
(名前をつけるのがとても難しかったので以下の画像を参考にしてください。)
まず、最初に
(0の目盛りとグラフの目盛り位置の距離) = |zero_y - graph_y|
次に
(1px当たりの枚数を計算)=\frac{(0の目盛りとグラフの目盛り位置の距離)}{グラフ目盛りの値}
を計算する。
これによって、差枚数 = (1px当たりの枚数を計算) ✖️ |(グラフの最終位置) - (0の目盛り位置)|で求めることができる。
画像表示部分の作成
画像アップロード機能は、JavaScriptのFileReaderとHTMLの要素を用いて実装しました。アップロードされた画像は、2つの要素に表示されます。1つは画像を表示するためのもので、もう1つはスライダーで操作する線を重ねて描画するためのものです。
// 画像読み込みと表示
function loadImage(obj) {
if (obj.files.length > 0) { // ファイルが選択されているか確認
var fileReader = new FileReader();
fileReader.onload = function (e) {
var previewCanvas = document.getElementById("imgPreview");
var drawingCanvas = document.getElementById("drawing-canvas");
// 以前の画像と削除ボタンをクリア
previewCanvas.innerHTML = '';
var ctx = previewCanvas.getContext("2d");
var img = new Image();
img.src = e.target.result;
img.onload = function() {
var graph = document.getElementById('graph');
// canvasのサイズを親要素(graph)に合わせて設定
// padding分(左右10pxずつ)を引いて調整
previewCanvas.width = graph.clientWidth - 20;
// ウィンドウ高さの60%をグラフ表示領域にする
previewCanvas.height = window.innerHeight * 0.6;
// drawingCanvasも同様にサイズ調整
drawingCanvas.width = graph.clientWidth - 20;
drawingCanvas.height = window.innerHeight * 0.6;
// graphの高さもcanvasと操作領域に合わせて調整
graph.style.height = (previewCanvas.height + 90) + 'px';
// アスペクト比を無視して、canvas全体に画像をフィットさせる
ctx.drawImage(img, 0, 0, previewCanvas.width, previewCanvas.height);
// スライダーの初期化
sliderInit(previewCanvas.height, previewCanvas.width);
};
// 画像の最大幅を100%に設定(レスポンシブ対応)
img.style.maxWidth = "100%";
fileReader.readAsDataURL(obj.files[0]); // 選択された最初のファイルを読み込む
}
}
数値の設定部分
0の目盛り,グラフ目盛りの位置,最終的なグラフの位置
inputタグのtypeをrangeにすることでスライダーを用いて設定するようにしました。
// スライダーの初期化関数
function sliderInit(imgSizeHeight, imgSizeWidth) {
console.log(imgSizeHeight, imgSizeWidth); // デバッグ用:画像の高さ、幅を出力
// 各スライダー要素を取得
var minSlider = document.getElementById('min-slider'); // 0の目盛りの位置スライダー
var maxSlider = document.getElementById('max-slider'); // グラフ目盛りの位置スライダー
var finalSlider = document.getElementById('final-slider'); // 最終位置スライダー
//スライダーの初期化
minSlider.value = 0;
maxSlider.value = 0;
finalSlider.value = 0;
// スライダーの最小値と最大値を画像の高さに設定
minSlider.min = 0;
minSlider.max = imgSizeHeight;
maxSlider.min = 0;
maxSlider.max = imgSizeHeight;
finalSlider.min = 0;
finalSlider.max = imgSizeHeight;
// スライダーの値が変更された時のイベントリスナーを追加
minSlider.addEventListener('input', function() {
// スライダーの値(上から何px目か)を画像の下からの位置に変換
minMemoryY = imgSizeHeight - minSlider.value;
drawLine(); // 別途定義されたdrawLine関数を呼び出し、線を再描画
});
maxSlider.addEventListener('input', function() {
// スライダーの値(上から何px目か)を画像の下からの位置に変換
maxMemoryY = imgSizeHeight - maxSlider.value;
drawLine(); // 別途定義されたdrawLine関数を呼び出し、線を再描画
});
finalSlider.addEventListener('input', function() {
// スライダーの値(上から何px目か)を画像の下からの位置に変換
finalY = imgSizeHeight - finalSlider.value;
drawLine(); // 別途定義されたdrawLine関数を呼び出し、線を再描画
});
}
グラフ目盛りの値
inputタグのtype = numberを用いて数値を入力できるようにしました。
ここで、注意するべきポイントが、入力フォームを利用した、XSSの対策です。
入力されたテキストに対して、サニタイズ関数を呼びだすようにしました。これによって、もし悪意のあるスクリプトが入力されたとしても、スクリプトとして解釈されるのではなく、ただのテキストとして解釈されます。
//このようなテキストが入力された時
sanitize("<script>alert('XSS攻撃');</script>");
↓
//このように変換されます
<script>alert('XSS攻撃');</script>
// サニタイズ
function sanitize(input) {
const map = {
'&': '&',
'<': '<',
'>': '>',
'"': '"',
"'": '''
};
return input.replace(/[&<>"']/g, function(m) { return map[m]; });
}
差枚数の計算を実装
スライダー、入力フォームの数値を取得後に、システム概要で示した計算式を用いて、差枚数を計算します。
スライダーの初期値は、sliderInit()によって0で初期化されています。そのため、スライダーの値が0で等しい場合にはアラートでスライダーを動かすように促します。
また、絶対値を使用している都合上、マイナスの時にも正の値が計算されてしまうため、
(グラフの最終的な位置) < (0の目盛り位置)の時には、差枚数がマイナスであるので、三項演算子を用いて処理を分岐しています。
// 差枚数計算
function diffNumCal() {
// スライダーが動かされていない時のアラート
if (minMemoryY === maxMemoryY ||minMemoryY === finalY ) {
alert('スライダーを動かしてください。');
return;
}
var MemoryNum;
MemoryNum = sanitize(document.getElementById('memory-num').value); // ボタンクリック時に値を取得するように修正
// 入力検証
if (!/^-?\d*\.?\d+$/.test(MemoryNum)) { // 正または負の数値であるかを検証する正規表現
alert('数値を入力してください。');
MemoryNum = ''; // 不正な入力をクリア
}
if (MemoryNum === '') {
alert('上部目盛りの値を入力してください。');
return;
}
//グラフ目盛りと0の目盛りの距離を計算
var distanceMinYtoMaxY = Math.abs(minMemoryY - maxMemoryY);
//1pxあたりの枚数を計算
var sheetsPerCoordinate = MemoryNum / distanceMinYtoMaxY;
//最終位置と0の目盛りの距離を計算
var distanceFinalYtoBase = Math.abs(finalY - minMemoryY);
var diffNumSheets = Math.round(sheetsPerCoordinate * distanceFinalYtoBase);
// 差枚数がマイナスの時の判定
diffNumSheets = finalY < minMemoryY ? diffNumSheets : -diffNumSheets;
document.getElementById('diff-num').innerText = diffNumSheets;
}
初めてのデプロイ
調べてみると有名なherokuが無料プランが終了してしまっていました。。。
他のqiita記事があるので、デプロイの流れは割愛。
代替案として https://render.com/ を使用しました。
ただし、無料プランでは15分間アクセスがないとダウンしてしまうそうなので、以下を参考にスリープ対策をさせていただきました。
最後に
精度を測ってみると、数十枚ほどはずれが出てしまうが、概ね想定通りに制作できたと思います。また、誤差の原因として、アップロードしている画像自体が傾いている問題があります。そのため、pythonを用いて、画像の傾きを検出して、水平に補正するようなプログラムを作成したいと考えています。また、今回のサイトは数値を自分で指定する必要があります。今後、画像認識を活用して、数値等を自動取得できるようにすればUXが高いサイトになるのではないかと考えています。
更新情報
これまでは、あくまでグラフから差枚数を計算するサイトであり、ぶどう確率の算出には他のサイトにお願いしていました。自作ぶどうシミュレーターを作成し、ぶどう確率の算出を当サイトのみで完結するようにしました。 最新版のサイトイメージは下記画像です。