From e8bf00ab61c8be8f1fdd8c5c44e8f0e9c42abf15 Mon Sep 17 00:00:00 2001 From: WakeTrainDev <175499942+waketraindev@users.noreply.github.com> Date: Thu, 12 Dec 2024 19:33:07 +0200 Subject: [PATCH 01/11] Rename schema references for consistency Updated schema references and definitions in SpinApi.yaml to ensure consistent naming. Changed `MachineStateMessage` to `StateMessage` and `MachineStatsMessage` to `SpinStatsMessage`. These changes improve clarity and align with updated naming conventions. --- openapi-docs/SpinApi.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/openapi-docs/SpinApi.yaml b/openapi-docs/SpinApi.yaml index 5abe5ba..65ae94d 100644 --- a/openapi-docs/SpinApi.yaml +++ b/openapi-docs/SpinApi.yaml @@ -27,7 +27,7 @@ paths: content: '*/*': schema: - $ref: "#/components/schemas/MachineStateMessage" + $ref: "#/components/schemas/StateMessage" /api/machine-stats: get: summary: "GET api/machine-stats" @@ -38,7 +38,7 @@ paths: content: '*/*': schema: - $ref: "#/components/schemas/MachineStatsMessage" + $ref: "#/components/schemas/SpinStatsMessage" /api/spin/{amount}: post: summary: "POST api/spin/{amount}" @@ -100,7 +100,7 @@ components: properties: version: type: "string" - MachineStateMessage: + StateMessage: type: "object" properties: version: @@ -138,7 +138,7 @@ components: max: type: "integer" format: "int64" - MachineStatsMessage: + SpinStatsMessage: type: "object" properties: timestampMs: From 26ba9768db54cfa7bd914af3af43517361f594b9 Mon Sep 17 00:00:00 2001 From: WakeTrainDev <175499942+waketraindev@users.noreply.github.com> Date: Thu, 12 Dec 2024 20:09:21 +0200 Subject: [PATCH 02/11] Add SpringDoc dependencies and configure Maven compiler plugin Integrated SpringDoc dependencies for OpenAPI documentation support and added the Maven compiler plugin with annotation processor configuration for runtime Javadoc processing. This enhances the project's documentation capabilities and ensures smooth integration with Javadoc annotations. --- pom.xml | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 0376eda..cd4a25d 100644 --- a/pom.xml +++ b/pom.xml @@ -34,7 +34,6 @@ org.springframework.boot spring-boot-starter - org.springframework.boot spring-boot-starter-test @@ -57,6 +56,16 @@ org.springframework.boot spring-boot-starter-actuator + + org.springdoc + springdoc-openapi-starter-webmvc-ui + 2.7.0 + + + org.springdoc + springdoc-openapi-javadoc + 1.8.0 + @@ -65,6 +74,19 @@ org.springframework.boot spring-boot-maven-plugin + + org.apache.maven.plugins + maven-compiler-plugin + + + + com.github.therapi + therapi-runtime-javadoc-scribe + 0.15.0 + + + + From 3efeae5837b6461a061eb016d944946c0a97450f Mon Sep 17 00:00:00 2001 From: WakeTrainDev <175499942+waketraindev@users.noreply.github.com> Date: Thu, 12 Dec 2024 20:12:06 +0200 Subject: [PATCH 03/11] Remove unnecessary blank line in pom.xml --- pom.xml | 1 - 1 file changed, 1 deletion(-) diff --git a/pom.xml b/pom.xml index cd4a25d..d78ec8a 100644 --- a/pom.xml +++ b/pom.xml @@ -67,7 +67,6 @@ 1.8.0 - From 0f54dcf3e6048391becff9a7602c6a16a4ceb582 Mon Sep 17 00:00:00 2001 From: WakeTrainDev <175499942+waketraindev@users.noreply.github.com> Date: Thu, 12 Dec 2024 20:15:29 +0200 Subject: [PATCH 04/11] Update SpinApi.yaml with improved API documentation and schema Refactored OpenAPI spec to improve clarity and standardization. Updated API paths and added detailed descriptions for endpoints, including new parameters and response structures. Changed schema definitions for better consistency and enhanced documentation annotations. --- openapi-docs/SpinApi.yaml | 312 +++++++++++++++++++++++--------------- 1 file changed, 192 insertions(+), 120 deletions(-) diff --git a/openapi-docs/SpinApi.yaml b/openapi-docs/SpinApi.yaml index 65ae94d..fcb478a 100644 --- a/openapi-docs/SpinApi.yaml +++ b/openapi-docs/SpinApi.yaml @@ -1,177 +1,249 @@ -openapi: "3.1.0" +openapi: 3.0.1 info: - title: "SlotsEngine API" - description: "SlotsEngine API" - version: "1.0.0" + title: OpenAPI definition + version: v0 servers: - - url: "https://SlotsEngine" + - url: http://localhost:8080 + description: Generated server url +tags: + - name: rest-api-controller + description: |- + The ApiController class is responsible for handling API requests related to a slot machine application. + It provides endpoints for retrieving server version information, loading the slot machine state, + performing spin operations, and managing the user's balance through deposit and withdrawal actions. + Each method is mapped to a specific HTTP request type and URL path, enabling interaction with the slot machine. +

+ The controller ensures that each endpoint is appropriately logged and handles exceptions that may occur + during operations, such as insufficient funds for a spin or withdrawal. +

+ The class leverages a SlotMachine instance to perform the core operations and depends on configuration + values for versioning information. paths: - /api: - get: - summary: "GET api" - operationId: "indexAction" - responses: - "200": - description: "OK" - content: - '*/*': - schema: - $ref: "#/components/schemas/ServerVersionMessage" - /api/load: - get: - summary: "GET api/load" - operationId: "load" - responses: - "200": - description: "OK" - content: - '*/*': - schema: - $ref: "#/components/schemas/StateMessage" - /api/machine-stats: - get: - summary: "GET api/machine-stats" - operationId: "getMachineStats" + /api/withdraw/{amount}: + post: + tags: + - rest-api-controller + summary: Handles the HTTP request to withdraw a specified amount from the slot + machine's balance. + description: Handles the HTTP request to withdraw a specified amount from the + slot machine's balance. + operationId: withdraw + parameters: + - name: amount + in: path + description: the amount to withdraw from the balance. Must be a positive Long + value. + required: true + schema: + type: integer + format: int64 responses: "200": - description: "OK" + description: "a {@link BalanceMessage BalanceMessage} object containing\ + \ the updated balance after the withdrawal." content: '*/*': schema: - $ref: "#/components/schemas/SpinStatsMessage" + $ref: "#/components/schemas/BalanceMessage" /api/spin/{amount}: post: - summary: "POST api/spin/{amount}" - operationId: "spin" + tags: + - rest-api-controller + summary: Initiates a spin operation on the slot machine for the specified bet + amount. + description: Initiates a spin operation on the slot machine for the specified + bet amount. + operationId: spin parameters: - - name: "amount" - in: "path" + - name: amount + in: path + description: the amount to bet on the spin. required: true schema: - type: "integer" - format: "int64" + type: integer + format: int64 responses: "200": - description: "OK" + description: |- + a {@link BetResultMessage BetResultMessage} object containing the timestamp, bet amount, win amount, + balance after the spin, and the resulting symbol from the spin. content: '*/*': schema: $ref: "#/components/schemas/BetResultMessage" /api/deposit/{amount}: post: - summary: "POST api/deposit/{amount}" - operationId: "deposit" + tags: + - rest-api-controller + summary: Handles the HTTP request to deposit a specified amount into the slot + machine. + description: Handles the HTTP request to deposit a specified amount into the + slot machine. + operationId: deposit parameters: - - name: "amount" - in: "path" + - name: amount + in: path + description: a positive Long value representing the amount to deposit. required: true schema: - type: "integer" - format: "int64" + type: integer + format: int64 responses: "200": - description: "OK" + description: "a {@link BalanceMessage BalanceMessage} object containing\ + \ the updated balance after the deposit." content: '*/*': schema: $ref: "#/components/schemas/BalanceMessage" - /api/withdraw/{amount}: - post: - summary: "POST api/withdraw/{amount}" - operationId: "withdraw" - parameters: - - name: "amount" - in: "path" - required: true - schema: - type: "integer" - format: "int64" + /api: + get: + tags: + - rest-api-controller + summary: Handles the HTTP GET request for the root API endpoint and provides + the server version information. + description: Handles the HTTP GET request for the root API endpoint and provides + the server version information. + operationId: indexAction responses: "200": - description: "OK" + description: "a {@link ServerVersionMessage ServerVersionMessage} object\ + \ containing the current version of the application." content: '*/*': schema: - $ref: "#/components/schemas/BalanceMessage" + $ref: "#/components/schemas/ServerVersionMessage" + /api/machine-stats: + get: + tags: + - rest-api-controller + summary: "Retrieves the current machine statistics, including the timestamp,\ + \ bet statistics, and win statistics." + description: "Retrieves the current machine statistics, including the timestamp,\ + \ bet statistics, and win statistics." + operationId: getMachineStats + responses: + "200": + description: "a MachineStatsMessage object containing the current timestamp,\ + \ bet statistics, and win statistics." + content: + '*/*': + schema: + $ref: "#/components/schemas/SpinStatsMessage" + /api/load: + get: + tags: + - rest-api-controller + summary: Handles the HTTP GET request for loading the current state of the slot + machine. + description: Handles the HTTP GET request for loading the current state of the + slot machine. + operationId: load + responses: + "200": + description: |- + a {@link StateMessage StateMessage} object containing the current timestamp, + machine's return to player (RTP), bet amount, win amount, balance, and result. + content: + '*/*': + schema: + $ref: "#/components/schemas/StateMessage" components: schemas: - ServerVersionMessage: - type: "object" + BalanceMessage: + type: object properties: - version: - type: "string" - StateMessage: - type: "object" + balance: + type: integer + format: int64 + BetResultMessage: + type: object properties: - version: - type: "string" timestampMs: - type: "integer" - format: "int64" - rtp: - type: "number" - format: "double" + type: integer + format: int64 betAmount: - type: "integer" - format: "int64" + type: integer + format: int64 winAmount: - type: "integer" - format: "int64" + type: integer + format: int64 balance: - type: "integer" - format: "int64" + type: integer + format: int64 result: - type: "integer" - format: "int32" - LongSummaryStatistics: - type: "object" + type: integer + format: int32 + ServerVersionMessage: + type: object properties: - count: - type: "integer" - format: "int64" - sum: - type: "integer" - format: "int64" - min: - type: "integer" - format: "int64" - max: - type: "integer" - format: "int64" + version: + type: string SpinStatsMessage: - type: "object" + type: object properties: timestampMs: - type: "integer" - format: "int64" + type: integer + format: int64 rtp: - type: "number" - format: "double" + type: number + format: double betStats: - $ref: "#/components/schemas/LongSummaryStatistics" + type: object + properties: + count: + type: integer + format: int64 + sum: + type: integer + format: int64 + min: + type: integer + format: int64 + max: + type: integer + format: int64 + average: + type: number + format: double winStats: - $ref: "#/components/schemas/LongSummaryStatistics" - BetResultMessage: - type: "object" + type: object + properties: + count: + type: integer + format: int64 + sum: + type: integer + format: int64 + min: + type: integer + format: int64 + max: + type: integer + format: int64 + average: + type: number + format: double + StateMessage: + type: object properties: + version: + type: string timestampMs: - type: "integer" - format: "int64" + type: integer + format: int64 + rtp: + type: number + format: double betAmount: - type: "integer" - format: "int64" + type: integer + format: int64 winAmount: - type: "integer" - format: "int64" + type: integer + format: int64 balance: - type: "integer" - format: "int64" + type: integer + format: int64 result: - type: "integer" - format: "int32" - BalanceMessage: - type: "object" - properties: - balance: - type: "integer" - format: "int64" \ No newline at end of file + type: integer + format: int32 \ No newline at end of file From 33a23a92536d56b8c6c0afe1cc229b6655a7bf14 Mon Sep 17 00:00:00 2001 From: WakeTrainDev <175499942+waketraindev@users.noreply.github.com> Date: Thu, 12 Dec 2024 20:39:48 +0200 Subject: [PATCH 05/11] Refactor GeneratedReel to ReelBufferedGenerator for clarity Renamed the GeneratedReel class to ReelBufferedGenerator to better reflect its functionality. Updated all references to ensure consistent usage across the codebase. This improves code readability and maintainability. --- .../{GeneratedReel.java => ReelBufferedGenerator.java} | 4 ++-- .../java/wtd/slotsengine/utils/generator/ReelOptimizer.java | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) rename src/main/java/wtd/slotsengine/utils/generator/{GeneratedReel.java => ReelBufferedGenerator.java} (97%) diff --git a/src/main/java/wtd/slotsengine/utils/generator/GeneratedReel.java b/src/main/java/wtd/slotsengine/utils/generator/ReelBufferedGenerator.java similarity index 97% rename from src/main/java/wtd/slotsengine/utils/generator/GeneratedReel.java rename to src/main/java/wtd/slotsengine/utils/generator/ReelBufferedGenerator.java index 4ac536b..877d723 100644 --- a/src/main/java/wtd/slotsengine/utils/generator/GeneratedReel.java +++ b/src/main/java/wtd/slotsengine/utils/generator/ReelBufferedGenerator.java @@ -3,7 +3,7 @@ import java.util.Random; import java.util.stream.IntStream; -public class GeneratedReel { +public class ReelBufferedGenerator { private static final int MAX_SYMBOLS = 256; private static final int BATCH_SIZE = 8; private static final Random random = new Random(); @@ -11,7 +11,7 @@ public class GeneratedReel { private final double maxRtp; private int index = 0; - public GeneratedReel(final double targetRtp) { + public ReelBufferedGenerator(final double targetRtp) { this.maxRtp = targetRtp; } diff --git a/src/main/java/wtd/slotsengine/utils/generator/ReelOptimizer.java b/src/main/java/wtd/slotsengine/utils/generator/ReelOptimizer.java index 83f8545..b96b1bc 100644 --- a/src/main/java/wtd/slotsengine/utils/generator/ReelOptimizer.java +++ b/src/main/java/wtd/slotsengine/utils/generator/ReelOptimizer.java @@ -21,10 +21,11 @@ public ReelOptimizer(int historySize, double targetRtp) { this.targetRtp = targetRtp; } + @SuppressWarnings("unused") public void runSingle(final GenStopCondition stopCondition) { int runCount = 0; while (stopCondition.apply(runCount)) { - GeneratedReel gen = new GeneratedReel(targetRtp); + ReelBufferedGenerator gen = new ReelBufferedGenerator(targetRtp); processGeneratedResult(runCount, gen.generateReel()); runCount++; } @@ -64,7 +65,7 @@ private Thread startGeneratingThread(GenStopCondition stopCondition, AtomicInteg Runnable generatingTask = () -> { while (stopCondition.apply(runCount.get())) { try { - GeneratedReel gen = new GeneratedReel(targetRtp); + ReelBufferedGenerator gen = new ReelBufferedGenerator(targetRtp); blockQueue.put(workPool.submit(gen::generateReel)); } catch (InterruptedException | RejectedExecutionException e) { Thread.currentThread().interrupt(); From de41f7a99d5a5637bf07ff46182cbfacfed925af Mon Sep 17 00:00:00 2001 From: WakeTrainDev <175499942+waketraindev@users.noreply.github.com> Date: Thu, 12 Dec 2024 21:18:22 +0200 Subject: [PATCH 06/11] Refactor JavaScript and enhance UI animations. Simplified JavaScript code by replacing redundant `let` declarations with `const` for improved readability and consistency. Added CSS border-radius and spin animations for better visuals and clarified spin logic handling. --- src/main/resources/static/css/main.css | 1 + src/main/resources/static/index.html | 2 +- src/main/resources/static/js/main.js | 45 ++++++++++++-------------- 3 files changed, 22 insertions(+), 26 deletions(-) diff --git a/src/main/resources/static/css/main.css b/src/main/resources/static/css/main.css index 5aff53d..ebda8f1 100644 --- a/src/main/resources/static/css/main.css +++ b/src/main/resources/static/css/main.css @@ -7,6 +7,7 @@ #blkDisplay { font-size: 150px; border: 1px solid gray; + border-radius: 10px; } #lblDisplay { diff --git a/src/main/resources/static/index.html b/src/main/resources/static/index.html index 51bd71b..ea9a2dc 100644 --- a/src/main/resources/static/index.html +++ b/src/main/resources/static/index.html @@ -30,7 +30,7 @@

Balance: 100 credits

-

7

+

7

diff --git a/src/main/resources/static/js/main.js b/src/main/resources/static/js/main.js index 1826879..dacc8fb 100644 --- a/src/main/resources/static/js/main.js +++ b/src/main/resources/static/js/main.js @@ -1,31 +1,25 @@ /* jshint esversion: 6 */ -(() => { +(function () { "use strict"; - let appWindow = document.getElementById('appWindow'); - let btnSpin = document.getElementById('btnSpin'); - let btnIncBet = document.getElementById('btnIncrementBet'); - let btnDecBet = document.getElementById('btnDecrementBet'); - - let btnDeposit = document.getElementById('btnDeposit'); - let btnWithdraw = document.getElementById('btnWithdraw'); - - let lblBalanceAmount = document.getElementById('lblBalanceAmount'); - let lblBetAmount = document.getElementById('lblBetAmount'); - - let lblDisplay = document.getElementById('lblDisplay'); - - let lblRollResult = document.getElementById('lblRollResultText'); - let lblRollAmount = document.getElementById('lblRollResultAmount'); - - let lblVersion = document.getElementById('lblVersion'); + const appWindow = document.getElementById('appWindow'); + const btnSpin = document.getElementById('btnSpin'); + const btnIncBet = document.getElementById('btnIncrementBet'); + const btnDecBet = document.getElementById('btnDecrementBet'); + const btnDeposit = document.getElementById('btnDeposit'); + const btnWithdraw = document.getElementById('btnWithdraw'); + const lblBalanceAmount = document.getElementById('lblBalanceAmount'); + const lblBetAmount = document.getElementById('lblBetAmount'); + const lblDisplay = document.getElementById('lblDisplay'); + const lblRollResult = document.getElementById('lblRollResultText'); + const lblRollAmount = document.getElementById('lblRollResultAmount'); + const lblVersion = document.getElementById('lblVersion'); + const blkDisplay = document.getElementById('blkDisplay'); + const betRange = [1, 10, 15, 25, 50, 100, 200, 500, 1000, 2000, 5000, 10000]; let lastSpin = {winAmount: 0}; - let machineState = { balance: 1, betAmount: 1 }; - - let betRange = [1, 10, 15, 25, 50, 100, 200, 500, 1000, 2000, 5000, 10000]; let betPos = 0; let numFormat = new Intl.NumberFormat('en-US', {}); @@ -102,9 +96,6 @@ lblBalanceAmount.innerText = prettyNumber(state.balance); lblDisplay.innerText = prettyNumber(state.result); machineState.balance = state.balance; - setButtonsState(false); - btnSpin.disabled = machineState.betAmount > machineState.balance; - let tabBody = document.querySelector("#historyTable tbody"); let rows = tabBody.getElementsByTagName("tr"); if (rows.length > 10) { @@ -118,15 +109,20 @@ if (isWin() > 0) { setStatusLabel('WIN', prettyNumber(state.winAmount), 'text-bg-success'); + blkDisplay.className = "animate-spin-win"; } else { setStatusLabel('LOSS', prettyNumber(state.betAmount), 'text-bg-danger'); + blkDisplay.className = "animate-spin-loss"; } + setButtonsState(false); + btnSpin.disabled = machineState.betAmount > machineState.balance; setTimeout(refreshStats, 0); } function spin() { setButtonsState(true); lblDisplay.style.color = 'orange'; + blkDisplay.className = "animate-spin"; let betAmount = machineState.betAmount; setStatusLabel('Spin', prettyNumber(machineState.betAmount), 'text-bg-warning'); @@ -154,7 +150,6 @@ let value = cells[1]; value.innerText = prettyNumber(((i >= 10) ? 100 : i) * machineState.betAmount); } - } function bindListeners() { From 718a6abcc5dc4b0dbe42b22c0ca7d1f3df437654 Mon Sep 17 00:00:00 2001 From: WakeTrainDev <175499942+waketraindev@users.noreply.github.com> Date: Thu, 12 Dec 2024 21:25:14 +0200 Subject: [PATCH 07/11] Refactor variable declarations and optimize DOM element reuse Replaced `let` with `const` where applicable for immutability. Reused DOM element references instead of querying repeatedly, improving code readability and performance. Adjustments were also made to streamline the code structure. --- src/main/resources/static/js/main.js | 43 ++++++++++++++-------------- 1 file changed, 21 insertions(+), 22 deletions(-) diff --git a/src/main/resources/static/js/main.js b/src/main/resources/static/js/main.js index dacc8fb..b0c9ce0 100644 --- a/src/main/resources/static/js/main.js +++ b/src/main/resources/static/js/main.js @@ -14,7 +14,12 @@ const lblRollAmount = document.getElementById('lblRollResultAmount'); const lblVersion = document.getElementById('lblVersion'); const blkDisplay = document.getElementById('blkDisplay'); + const lblBetStats = document.getElementById("lblBetStats"); + const lblWinStats = document.getElementById("lblWinStats"); + const lblRtpStats = document.getElementById("lblRtpStats"); + const betRange = [1, 10, 15, 25, 50, 100, 200, 500, 1000, 2000, 5000, 10000]; + const numFormat = new Intl.NumberFormat('en-US', {}); let lastSpin = {winAmount: 0}; let machineState = { @@ -22,8 +27,6 @@ }; let betPos = 0; - let numFormat = new Intl.NumberFormat('en-US', {}); - function prettyNumber(num) { return numFormat.format(num); } @@ -51,7 +54,10 @@ } return rsp; }).then((response) => response.json()) - .then(data => callback(data)).catch(ignored => window.alert(`Error running API call`)); + .then(data => callback(data)).catch(ignored => { + window.alert(`Error running API call`); + console.log(ignored); + }); } function setStatusLabel(label, text, classes) { @@ -64,10 +70,6 @@ } function refreshStats() { - let lblBetStats = document.getElementById("lblBetStats"); - let lblWinStats = document.getElementById("lblWinStats"); - let lblRtpStats = document.getElementById("lblRtpStats"); - sendCall((data) => { let newText = `Bets: ${prettyNumber(data["betStats"]["count"])} `; newText += `Max: ${prettyNumber(data["betStats"]["max"])} `; @@ -96,17 +98,15 @@ lblBalanceAmount.innerText = prettyNumber(state.balance); lblDisplay.innerText = prettyNumber(state.result); machineState.balance = state.balance; - let tabBody = document.querySelector("#historyTable tbody"); - let rows = tabBody.getElementsByTagName("tr"); + const tabBody = document.querySelector("#historyTable tbody"); + const rows = tabBody.getElementsByTagName("tr"); if (rows.length > 10) { tabBody.querySelector("tr:last-child").remove(); } - let newRow = document.createElement('tr'); + const newRow = document.createElement('tr'); newRow.innerHTML = `${prettyNumber(state.betAmount)}${prettyNumber(state.winAmount)}${state.result}` + `${isWin() ? 'Win' : 'Loss'}`; tabBody.prepend(newRow); - lblDisplay.style.color = isWin() ? 'green' : 'red'; - if (isWin() > 0) { setStatusLabel('WIN', prettyNumber(state.winAmount), 'text-bg-success'); blkDisplay.className = "animate-spin-win"; @@ -121,13 +121,12 @@ function spin() { setButtonsState(true); - lblDisplay.style.color = 'orange'; blkDisplay.className = "animate-spin"; - let betAmount = machineState.betAmount; + const betAmount = machineState.betAmount; setStatusLabel('Spin', prettyNumber(machineState.betAmount), 'text-bg-warning'); let count = 0; - let animateDisplay = setInterval(() => { + const animateDisplay = setInterval(() => { if (count++ < 7) { lblDisplay.innerText = Math.floor(Math.random() * 10).toFixed(0); } else { @@ -142,19 +141,19 @@ } function calcBetValues() { - let tb = document.getElementById('payoutTable'); - let body = tb.getElementsByTagName("tbody")[0]; - let rows = body.getElementsByTagName("tr"); + const tb = document.getElementById('payoutTable'); + const body = tb.getElementsByTagName("tbody")[0]; + const rows = body.getElementsByTagName("tr"); for (let i = 0; i < rows.length; i++) { - let cells = rows[i].getElementsByTagName("td"); - let value = cells[1]; + const cells = rows[i].getElementsByTagName("td"); + const value = cells[1]; value.innerText = prettyNumber(((i >= 10) ? 100 : i) * machineState.betAmount); } } function bindListeners() { btnDeposit.addEventListener('click', () => { - let value = window.prompt("Enter deposit amount", "1000"); + const value = window.prompt("Enter deposit amount", "1000"); sendCall(data => { lblBalanceAmount.innerText = data.balance; machineState.balance = data.balance; @@ -165,7 +164,7 @@ }); btnWithdraw.addEventListener('click', () => { - let value = window.prompt("Enter withdrawal amount", "1000"); + const value = window.prompt("Enter withdrawal amount", "1000"); sendCall(data => { lblBalanceAmount.innerText = data.balance; machineState.balance = data.balance; From 3921e78edb36c69b3919b173f4b09ea56c98ea8d Mon Sep 17 00:00:00 2001 From: WakeTrainDev <175499942+waketraindev@users.noreply.github.com> Date: Thu, 12 Dec 2024 21:39:26 +0200 Subject: [PATCH 08/11] Update CSS with animations and improve styling Added classes for spin animations with color changes for win, loss, and default cases. Refactored and improved button and label styling for better UI consistency. --- src/main/resources/static/css/main.css | 28 ++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/src/main/resources/static/css/main.css b/src/main/resources/static/css/main.css index ebda8f1..567b844 100644 --- a/src/main/resources/static/css/main.css +++ b/src/main/resources/static/css/main.css @@ -4,6 +4,17 @@ padding: .75em } + +#lblBalanceText { + font-size: 2em; +} + +#btnIncrementBet, #btnDecrementBet { + font-size: 2em; + font-weight: bolder; + padding: .75em; +} + #blkDisplay { font-size: 150px; border: 1px solid gray; @@ -15,12 +26,17 @@ display: inline-block; } -#lblBalanceText { - font-size: 2em; +.animate-spin { } -#btnIncrementBet, #btnDecrementBet { - font-size: 2em; - font-weight: bolder; - padding: .75em; +.animate-spin #lblDisplay { + color: orange; +} + +.animate-spin-loss #lblDisplay { + color: red; +} + +.animate-spin-win #lblDisplay { + color: green; } \ No newline at end of file From 1a7d81ff3ae83336a5a2d1f19396f24419213cbe Mon Sep 17 00:00:00 2001 From: WakeTrainDev <175499942+waketraindev@users.noreply.github.com> Date: Thu, 12 Dec 2024 21:45:24 +0200 Subject: [PATCH 09/11] Refactor row handling in payout table initialization. Moved DOM element retrieval for payout table rows to a higher scope to improve reusability and reduce redundancy. Adjusted row creation logic for better code readability and maintainability. --- src/main/resources/static/js/main.js | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/src/main/resources/static/js/main.js b/src/main/resources/static/js/main.js index b0c9ce0..fcb2a0c 100644 --- a/src/main/resources/static/js/main.js +++ b/src/main/resources/static/js/main.js @@ -18,7 +18,11 @@ const lblWinStats = document.getElementById("lblWinStats"); const lblRtpStats = document.getElementById("lblRtpStats"); - const betRange = [1, 10, 15, 25, 50, 100, 200, 500, 1000, 2000, 5000, 10000]; + const tb = document.getElementById('payoutTable'); + const body = tb.getElementsByTagName("tbody")[0]; + const rows = body.getElementsByTagName("tr"); + + const betRange = [1, 10, 15, 25, 50, 100, 150, 200, 250, 500, 1000, 1500, 2000, 2500, 5000, 10000]; const numFormat = new Intl.NumberFormat('en-US', {}); let lastSpin = {winAmount: 0}; @@ -104,7 +108,11 @@ tabBody.querySelector("tr:last-child").remove(); } const newRow = document.createElement('tr'); - newRow.innerHTML = `${prettyNumber(state.betAmount)}${prettyNumber(state.winAmount)}${state.result}` + `${isWin() ? 'Win' : 'Loss'}`; + newRow.innerHTML = + `${prettyNumber(state.betAmount)}${prettyNumber(state.winAmount)}${state.result}`; + newRow.innerHTML += + `${isWin() ? 'Win' : + 'Loss'}`; tabBody.prepend(newRow); if (isWin() > 0) { @@ -141,9 +149,6 @@ } function calcBetValues() { - const tb = document.getElementById('payoutTable'); - const body = tb.getElementsByTagName("tbody")[0]; - const rows = body.getElementsByTagName("tr"); for (let i = 0; i < rows.length; i++) { const cells = rows[i].getElementsByTagName("td"); const value = cells[1]; From 484a907fd1e46039cafc20ed7278893d823d99aa Mon Sep 17 00:00:00 2001 From: WakeTrainDev <175499942+waketraindev@users.noreply.github.com> Date: Thu, 12 Dec 2024 22:02:00 +0200 Subject: [PATCH 10/11] Use consistent double quotes for strings in JavaScript. Refactored the code to replace single quotes with double quotes for string consistency throughout the file. Minor updates include optional comma corrections and improved readability by adding proper spacing and addressing small logical clarifications. --- src/main/resources/static/js/main.js | 125 ++++++++++++++------------- 1 file changed, 66 insertions(+), 59 deletions(-) diff --git a/src/main/resources/static/js/main.js b/src/main/resources/static/js/main.js index fcb2a0c..33a368f 100644 --- a/src/main/resources/static/js/main.js +++ b/src/main/resources/static/js/main.js @@ -1,33 +1,33 @@ /* jshint esversion: 6 */ (function () { "use strict"; - const appWindow = document.getElementById('appWindow'); - const btnSpin = document.getElementById('btnSpin'); - const btnIncBet = document.getElementById('btnIncrementBet'); - const btnDecBet = document.getElementById('btnDecrementBet'); - const btnDeposit = document.getElementById('btnDeposit'); - const btnWithdraw = document.getElementById('btnWithdraw'); - const lblBalanceAmount = document.getElementById('lblBalanceAmount'); - const lblBetAmount = document.getElementById('lblBetAmount'); - const lblDisplay = document.getElementById('lblDisplay'); - const lblRollResult = document.getElementById('lblRollResultText'); - const lblRollAmount = document.getElementById('lblRollResultAmount'); - const lblVersion = document.getElementById('lblVersion'); - const blkDisplay = document.getElementById('blkDisplay'); + const appWindow = document.getElementById("appWindow"); + const btnSpin = document.getElementById("btnSpin"); + const btnIncBet = document.getElementById("btnIncrementBet"); + const btnDecBet = document.getElementById("btnDecrementBet"); + const btnDeposit = document.getElementById("btnDeposit"); + const btnWithdraw = document.getElementById("btnWithdraw"); + const lblBalanceAmount = document.getElementById("lblBalanceAmount"); + const lblBetAmount = document.getElementById("lblBetAmount"); + const lblDisplay = document.getElementById("lblDisplay"); + const lblRollResult = document.getElementById("lblRollResultText"); + const lblRollAmount = document.getElementById("lblRollResultAmount"); + const lblVersion = document.getElementById("lblVersion"); + const blkDisplay = document.getElementById("blkDisplay"); const lblBetStats = document.getElementById("lblBetStats"); const lblWinStats = document.getElementById("lblWinStats"); const lblRtpStats = document.getElementById("lblRtpStats"); - const tb = document.getElementById('payoutTable'); + const tb = document.getElementById("payoutTable"); const body = tb.getElementsByTagName("tbody")[0]; const rows = body.getElementsByTagName("tr"); - const betRange = [1, 10, 15, 25, 50, 100, 150, 200, 250, 500, 1000, 1500, 2000, 2500, 5000, 10000]; - const numFormat = new Intl.NumberFormat('en-US', {}); + const betRange = [1, 10, 15, 25, 50, 100, 150, 200, 250, 500, 1000, 1500, 2000, 2500, 5000, 10000,]; + const numFormat = new Intl.NumberFormat("en-US", {}); - let lastSpin = {winAmount: 0}; + let lastSpin = {winAmount: 0,}; let machineState = { - balance: 1, betAmount: 1 + balance: 1, betAmount: 1, }; let betPos = 0; @@ -45,10 +45,10 @@ lblDisplay.innerText = prettyNumber(data.result); lblVersion.innerText = data.version; btnSpin.disabled = machineState.betAmount > machineState.balance; - setStatusLabel('Balance', prettyNumber(machineState.balance)); + setStatusLabel("Balance", prettyNumber(machineState.balance)); refreshStats(); - }, '/api/load').then(() => appWindow.classList.remove('d-none')); + }, "/api/load").then(() => appWindow.classList.remove("d-none")); } function sendCall(callback, path, options) { @@ -58,15 +58,15 @@ } return rsp; }).then((response) => response.json()) - .then(data => callback(data)).catch(ignored => { + .then((data) => callback(data)).catch((ignored) => { window.alert(`Error running API call`); - console.log(ignored); + //console.log(ignored); }); } function setStatusLabel(label, text, classes) { if (classes === undefined) { - classes = 'text-bg-info'; + classes = "text-bg-info"; } lblRollResult.className = `badge ${classes}`; lblRollResult.innerText = label; @@ -75,21 +75,24 @@ function refreshStats() { sendCall((data) => { - let newText = `Bets: ${prettyNumber(data["betStats"]["count"])} `; - newText += `Max: ${prettyNumber(data["betStats"]["max"])} `; - newText += `Sum: ${prettyNumber(data["betStats"]["sum"])} `; + if (data === undefined) { + data = {betStats: {count: 0, max: 0, sum: 0,}, winStats: {count: 0, max: 0, sum: 0,}, rtp: 0,}; + } + let newText = `Bets: ${prettyNumber(data.betStats.count)} `; + newText += `Max: ${prettyNumber(data.betStats.max)} `; + newText += `Sum: ${prettyNumber(data.betStats.sum)} `; lblBetStats.innerText = newText; - newText = `Wins: ${prettyNumber(data["winStats"]["count"])} `; - newText += `Max: ${prettyNumber(data["winStats"]["max"])} `; - newText += `Sum: ${prettyNumber(data["winStats"]["sum"])} `; + newText = `Wins: ${prettyNumber(data.winStats.count)} `; + newText += `Max: ${prettyNumber(data.winStats.max)} `; + newText += `Sum: ${prettyNumber(data.winStats.sum)} `; lblWinStats.innerText = newText; - lblRtpStats.innerText = `RTP: ${(data["rtp"] * 100.0).toFixed(2)}%`; + lblRtpStats.innerText = `RTP: ${(data.rtp * 100.0).toFixed(2)}%`; }, "/api/machine-stats", {}).then(); } function setButtonsState(state) { - [btnSpin, btnIncBet, btnDecBet, btnDeposit, btnWithdraw].forEach((i) => i.disabled = state); + [btnSpin, btnIncBet, btnDecBet, btnDeposit, btnWithdraw,].forEach((i) => i.disabled = state); } function isWin() { @@ -107,19 +110,20 @@ if (rows.length > 10) { tabBody.querySelector("tr:last-child").remove(); } - const newRow = document.createElement('tr'); - newRow.innerHTML = - `${prettyNumber(state.betAmount)}${prettyNumber(state.winAmount)}${state.result}`; - newRow.innerHTML += - `${isWin() ? 'Win' : - 'Loss'}`; + const newRow = document.createElement("tr"); + newRow.innerHTML = `${prettyNumber(state.betAmount)} +${prettyNumber(state.winAmount)} +${state.result}`; + newRow.innerHTML += ` +${isWin() ? "Win" : "Loss"}`; tabBody.prepend(newRow); if (isWin() > 0) { - setStatusLabel('WIN', prettyNumber(state.winAmount), 'text-bg-success'); + setStatusLabel("WIN", prettyNumber(state.winAmount), "text-bg-success"); blkDisplay.className = "animate-spin-win"; } else { - setStatusLabel('LOSS', prettyNumber(state.betAmount), 'text-bg-danger'); + setStatusLabel("LOSS", prettyNumber(state.betAmount), "text-bg-danger"); blkDisplay.className = "animate-spin-loss"; } setButtonsState(false); @@ -131,25 +135,26 @@ setButtonsState(true); blkDisplay.className = "animate-spin"; const betAmount = machineState.betAmount; - setStatusLabel('Spin', prettyNumber(machineState.betAmount), 'text-bg-warning'); + setStatusLabel("Spin", prettyNumber(machineState.betAmount), "text-bg-warning"); let count = 0; const animateDisplay = setInterval(() => { if (count++ < 7) { lblDisplay.innerText = Math.floor(Math.random() * 10).toFixed(0); } else { - sendCall(data => { + sendCall((data) => { clearInterval(animateDisplay); updateMachineState(data); }, `/api/spin/${betAmount}`, { - method: 'POST' + method: "POST", }).then(); } }, 47); } function calcBetValues() { - for (let i = 0; i < rows.length; i++) { + let i; + for (i = 0; i < rows.length; i++) { const cells = rows[i].getElementsByTagName("td"); const value = cells[1]; value.innerText = prettyNumber(((i >= 10) ? 100 : i) * machineState.betAmount); @@ -157,31 +162,31 @@ } function bindListeners() { - btnDeposit.addEventListener('click', () => { + btnDeposit.addEventListener("click", () => { const value = window.prompt("Enter deposit amount", "1000"); - sendCall(data => { + sendCall((data) => { lblBalanceAmount.innerText = data.balance; machineState.balance = data.balance; btnSpin.disabled = machineState.betAmount > machineState.balance; - }, '/api/deposit/' + value, { - method: 'POST' + }, "/api/deposit/" + value, { + method: "POST", }).then(); }); - btnWithdraw.addEventListener('click', () => { + btnWithdraw.addEventListener("click", () => { const value = window.prompt("Enter withdrawal amount", "1000"); - sendCall(data => { + sendCall((data) => { lblBalanceAmount.innerText = data.balance; machineState.balance = data.balance; btnSpin.disabled = machineState.betAmount > machineState.balance; - }, '/api/withdraw/' + value, { - method: 'POST' + }, "/api/withdraw/" + value, { + method: "POST", }).then(); }); - btnSpin.addEventListener('click', () => { + btnSpin.addEventListener("click", () => { spin(); }); - btnIncBet.addEventListener('click', () => { + btnIncBet.addEventListener("click", () => { betPos = Math.min((betPos + 1), betRange.length - 1); machineState.betAmount = betRange[betPos]; lblBetAmount.innerText = prettyNumber(machineState.betAmount); @@ -189,23 +194,25 @@ btnSpin.disabled = machineState.betAmount > machineState.balance; }); - btnDecBet.addEventListener('click', () => { + btnDecBet.addEventListener("click", () => { betPos = Math.max((betPos - 1), 0); machineState.betAmount = betRange[betPos]; lblBetAmount.innerText = prettyNumber(machineState.betAmount); calcBetValues(); - btnSpin.disabled = !(machineState.balance > machineState.betAmount); + const isEnabled = (machineState.balance > machineState.betAmount); + /* This needs negation for the correct behaviour */ + btnSpin.disabled = !isEnabled; }); - document.addEventListener('keydown', (e) => { + document.addEventListener("keydown", (e) => { switch (e.key) { - case 's': + case "s": btnSpin.click(); break; - case 'a': + case "a": btnIncBet.click(); break; - case 'd': + case "d": btnDecBet.click(); break; } From 7f0a29218b29ff0a96fb57da5ead315ceb594b15 Mon Sep 17 00:00:00 2001 From: WakeTrainDev <175499942+waketraindev@users.noreply.github.com> Date: Thu, 12 Dec 2024 22:05:07 +0200 Subject: [PATCH 11/11] Update project version to 0.2.7 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index d78ec8a..4d7a3eb 100644 --- a/pom.xml +++ b/pom.xml @@ -10,7 +10,7 @@ wtd SlotsEngine - 0.2.6 + 0.2.7 SlotsEngine SlotsEngine