Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/in game events #54

Merged
merged 6 commits into from
Jul 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,24 @@
# The Predictive Maintenance Game

![immagine](https://github.com/linomp/pdm-game/assets/40581019/fe7fbee0-bf31-487b-a727-f34472d94840)

![immagine](./mvp/media/13_07_2024_trim.PNG)

- **_Your mission_**: maximize profit & machine lifespan.
- **_Your tools_**: data, intuition & nerve!
- **_Your opponents_**: machine degradation & limited funds!


### **_Will you accept the challenge?_**

https://app.pdmgame.xmp.systems/

---

### Dev roadmap

- [X] Basic UI
- [X] Basic machine degradation model
- [X] Sensor & prediction model purchase
- [X] Live machine parameters visualization
- [X] Leaderboard
- [X] Basic RUL prediction model
- [ ] In-game events (e.g. production peak, score multipliers)
- [X] In-game events (e.g. production peak, score multipliers)

1 change: 1 addition & 0 deletions mvp/client/ui/src/api/generated/models/GameSessionDTO.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@ export type GameSessionDTO = {
game_over_reason?: (string | null);
final_score?: (number | null);
user_messages?: Record<string, UserMessage>;
cash_multiplier?: number;
};
1 change: 0 additions & 1 deletion mvp/client/ui/src/api/generated/models/UserMessage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
export type UserMessage = {
type: UserMessage.type;
content: string;
seen?: boolean;
};

export namespace UserMessage {
Expand Down
48 changes: 16 additions & 32 deletions mvp/client/ui/src/components/MachineData.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,12 @@
gameOver,
gameSession,
globalSettings,
isOnNarrowScreen,
predictionPurchaseButtonDisabled,
sensorPurchaseButtonDisabled,
} from "src/stores/stores";
import Sensor from "src/components/Sensor.svelte";
import UserMessages from "src/components/UserMessages.svelte";

export let updateGameSession: (newGameSessionDto: GameSessionDTO) => void;

Expand Down Expand Up @@ -73,6 +75,11 @@

{#if isNotUndefinedNorNull($gameSession)}
<div class="machine-data">
{#if $isOnNarrowScreen}
{#key 'narrow'}
<UserMessages messages={$gameSession?.user_messages ?? {}}/>
{/key}
{/if}
<div class="sensors-display">
{#each Object.entries($gameSession?.machine_state?.operational_parameters ?? {}) as [parameter, value]}
<Sensor
Expand All @@ -85,7 +92,7 @@
{/each}
</div>
<div class="rul-display">
<span> {"Remaining Useful Life"}: </span>
<span> {"Remaining Useful Life 🔮"}: </span>
<span>{$gameSession?.machine_state?.predicted_rul
? `${$gameSession.machine_state?.predicted_rul} steps`
: "???"}
Expand All @@ -103,23 +110,22 @@
</button>
</span>
</div>
<div class="user-messages">
{#each Object.entries($gameSession?.user_messages ?? {}) as [key, message]}
<div class="message-card {message.type}">
<span>{message.content}</span>
</div>
{/each}
</div>
{#if !$isOnNarrowScreen}
{#key 'wide'}
<UserMessages messages={$gameSession?.user_messages ?? {}}/>
{/key}
{/if}
</div>
{/if}

<style>
.machine-data {
display: flex;
flex-direction: column;
align-items: center;
margin-bottom: 2em;
position: relative;
gap: 2em;
align-items: center;
flex-direction: column;
}

.sensors-display {
Expand All @@ -130,31 +136,9 @@
}

.rul-display {
margin-top: 1em;
display: flex;
flex-direction: column;
gap: 0.5rem;
align-items: center;
}

.user-messages {
display: flex;
flex-direction: column;
gap: 1rem;
margin-top: 2em;
align-items: center; /* Center horizontally */
}

.message-card {
border-radius: 8px;
padding: 1em;
width: 80%;
text-align: center;
border: 1px solid #ccc;
}

.message-card.WARNING {
background-color: #ffffe0; /* Light yellow */
color: #000000; /* Black */
}
</style>
34 changes: 30 additions & 4 deletions mvp/client/ui/src/components/SessionData.svelte
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts">
import {type GameSessionDTO, PlayerActionsService, SessionsService,} from "src/api/generated";
import {formatNumber, isNotUndefinedNorNull, isUndefinedOrNull,} from "src/shared/utils";
import {type GameSessionDTO, PlayerActionsService, SessionsService} from "src/api/generated";
import {formatNumber, isNotUndefinedNorNull, isUndefinedOrNull} from "src/shared/utils";
import {
dayInProgress,
gameOver,
Expand Down Expand Up @@ -67,7 +67,14 @@
{#if isNotUndefinedNorNull($gameSession) && !$gameOver}
<div class="session-data">
<p>Current Step: {$gameSession?.current_step}</p>
<p>Available Funds: {formatNumber($gameSession?.available_funds)}</p>
<p>
Available Funds:
<span
class:rainbow-funds={($gameSession?.cash_multiplier??0) > 1}>{formatNumber($gameSession?.available_funds)}</span>
<span
hidden={($gameSession?.cash_multiplier??0) <= 1}>🔥 (3x !!)
</span>
</p>
<div class={`session-controls ${$isOnNarrowScreen ? "flex-row" : "flex-col"}`}>
<button on:mousedown={advanceToNextDay} disabled={$dayInProgress}>
Advance to next day
Expand All @@ -81,7 +88,8 @@

<style>
.session-data {
margin: 1em;
margin-top: 1em;
margin-right: 1em;
}

.session-controls {
Expand All @@ -96,4 +104,22 @@
.flex-col {
flex-direction: column;
}

.rainbow-funds {
background-image: linear-gradient(to right, red, orange, #f3bd5a, green, cadetblue, dodgerblue);
-webkit-background-clip: text;
background-clip: text;
color: transparent;
font-weight: bold;
animation: rainbow-animation 5s linear infinite;
}

@keyframes rainbow-animation {
0% {
background-position: 0% 50%;
}
100% {
background-position: 100% 50%;
}
}
</style>
41 changes: 41 additions & 0 deletions mvp/client/ui/src/components/UserMessages.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<script lang="ts">
export let messages: Record<string, { type: string; content: string }>;
</script>

<div class="user-messages">
{#each Object.entries(messages) as [key, message]}
<div class="message-card {message.type}">
<span>{message.content}</span>
</div>
{/each}
</div>

<style>
.user-messages {
display: flex;
flex-direction: column;
gap: 1rem;
align-items: center;
width: 80%;
}

.message-card {
border-radius: 8px;
padding: 1em;
width: 100%;
text-align: center;
border: 1px solid #ccc;
transition: opacity 0.3s;
font-size: smaller;
}

.message-card.WARNING {
background-color: #ffffe0;
color: #000000;
}

.message-card.INFO {
background-color: rgba(129, 248, 94, 0.86);
color: #000000;
}
</style>
1 change: 0 additions & 1 deletion mvp/client/ui/src/pages/HomePage.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,6 @@
.game-area {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
gap: 2em;
flex-wrap: wrap;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
// vite.config.mjs
import { defineConfig } from "file:///C:/Users/LM2P/Documents/000_programming/000-projects/pdm-game/mvp/client/ui/node_modules/vite/dist/node/index.js";
import { svelte } from "file:///C:/Users/LM2P/Documents/000_programming/000-projects/pdm-game/mvp/client/ui/node_modules/@sveltejs/vite-plugin-svelte/src/index.js";
import { run } from "file:///C:/Users/LM2P/Documents/000_programming/000-projects/pdm-game/mvp/client/ui/node_modules/vite-plugin-run/dist/index.mjs";
import * as path from "path";
var __vite_injected_original_dirname = "C:\\Users\\LM2P\\Documents\\000_programming\\000-projects\\pdm-game\\mvp\\client\\ui";
var isTest = process.env.NODE_ENV === "test";
var vite_config_default = defineConfig({
resolve: {
alias: {
src: path.resolve(__vite_injected_original_dirname, "./src")
},
conditions: isTest ? ["browser"] : []
},
plugins: [
svelte({ hot: !process.env.VITEST }),
run({
silent: !!process.env.VITEST,
input: [
{
name: "typecheck",
run: ["npm", "run", "check"],
pattern: ["src/**/*.ts", "src/**/*.svelte"]
}
]
})
],
server: {
port: isTest ? 8678 : 5173,
proxy: isTest ? void 0 : {
"/api": {
target: "http://localhost:8000",
changeOrigin: false
}
}
},
build: {
outDir: "build",
target: "es2020",
cssCodeSplit: false
},
optimizeDeps: {
include: isTest ? ["@testing-library/svelte", "chai"] : void 0
},
test: {
globals: true,
environment: "jsdom",
setupFiles: "src/setup-tests.ts"
}
});
export {
vite_config_default as default
};
//# sourceMappingURL=data:application/json;base64,ewogICJ2ZXJzaW9uIjogMywKICAic291cmNlcyI6IFsidml0ZS5jb25maWcubWpzIl0sCiAgInNvdXJjZXNDb250ZW50IjogWyJjb25zdCBfX3ZpdGVfaW5qZWN0ZWRfb3JpZ2luYWxfZGlybmFtZSA9IFwiQzpcXFxcVXNlcnNcXFxcTE0yUFxcXFxEb2N1bWVudHNcXFxcMDAwX3Byb2dyYW1taW5nXFxcXDAwMC1wcm9qZWN0c1xcXFxwZG0tZ2FtZVxcXFxtdnBcXFxcY2xpZW50XFxcXHVpXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ZpbGVuYW1lID0gXCJDOlxcXFxVc2Vyc1xcXFxMTTJQXFxcXERvY3VtZW50c1xcXFwwMDBfcHJvZ3JhbW1pbmdcXFxcMDAwLXByb2plY3RzXFxcXHBkbS1nYW1lXFxcXG12cFxcXFxjbGllbnRcXFxcdWlcXFxcdml0ZS5jb25maWcubWpzXCI7Y29uc3QgX192aXRlX2luamVjdGVkX29yaWdpbmFsX2ltcG9ydF9tZXRhX3VybCA9IFwiZmlsZTovLy9DOi9Vc2Vycy9MTTJQL0RvY3VtZW50cy8wMDBfcHJvZ3JhbW1pbmcvMDAwLXByb2plY3RzL3BkbS1nYW1lL212cC9jbGllbnQvdWkvdml0ZS5jb25maWcubWpzXCI7Ly8vIDxyZWZlcmVuY2UgdHlwZXM9XCJ2aXRlc3RcIiAvPlxyXG5pbXBvcnQgeyBkZWZpbmVDb25maWcgfSBmcm9tICd2aXRlJ1xyXG5pbXBvcnQgeyBzdmVsdGUgfSBmcm9tICdAc3ZlbHRlanMvdml0ZS1wbHVnaW4tc3ZlbHRlJ1xyXG5pbXBvcnQgeyBydW4gfSBmcm9tICd2aXRlLXBsdWdpbi1ydW4nO1xyXG5pbXBvcnQgKiBhcyBwYXRoIGZyb20gJ3BhdGgnXHJcblxyXG5jb25zdCBpc1Rlc3QgPSBwcm9jZXNzLmVudi5OT0RFX0VOViA9PT0gJ3Rlc3QnXHJcblxyXG4vLyBodHRwczovL3ZpdGVqcy5kZXYvY29uZmlnL1xyXG5leHBvcnQgZGVmYXVsdCBkZWZpbmVDb25maWcoe1xyXG4gIHJlc29sdmU6IHtcclxuICAgIGFsaWFzOiB7XHJcbiAgICAgIHNyYzogcGF0aC5yZXNvbHZlKF9fZGlybmFtZSwgJy4vc3JjJylcclxuICAgIH0sXHJcbiAgICBjb25kaXRpb25zOiBpc1Rlc3QgPyBbJ2Jyb3dzZXInXSA6IFtdXHJcbiAgfSxcclxuICBwbHVnaW5zOiBbXHJcbiAgICBzdmVsdGUoeyBob3Q6ICFwcm9jZXNzLmVudi5WSVRFU1QgfSksXHJcbiAgICBydW4oe1xyXG4gICAgICBzaWxlbnQ6ICEhcHJvY2Vzcy5lbnYuVklURVNULFxyXG4gICAgICBpbnB1dDogW1xyXG4gICAgICAgIHtcclxuICAgICAgICAgIG5hbWU6ICd0eXBlY2hlY2snLFxyXG4gICAgICAgICAgcnVuOiBbJ25wbScsICdydW4nLCAnY2hlY2snXSxcclxuICAgICAgICAgIHBhdHRlcm46IFsnc3JjLyoqLyoudHMnLCAnc3JjLyoqLyouc3ZlbHRlJ10sXHJcbiAgICAgICAgfVxyXG4gICAgICBdXHJcbiAgICB9KVxyXG4gIF0sXHJcbiAgc2VydmVyOiB7XHJcbiAgICBwb3J0OiBpc1Rlc3QgPyA4Njc4IDogNTE3MyxcclxuICAgIHByb3h5OiBpc1Rlc3QgPyB1bmRlZmluZWQgOiB7XHJcbiAgICAgICcvYXBpJzoge1xyXG4gICAgICAgIHRhcmdldDogJ2h0dHA6Ly9sb2NhbGhvc3Q6ODAwMCcsXHJcbiAgICAgICAgY2hhbmdlT3JpZ2luOiBmYWxzZVxyXG4gICAgICB9XHJcbiAgICB9XHJcbiAgfSxcclxuICBidWlsZDoge1xyXG4gICAgb3V0RGlyOiAnYnVpbGQnLFxyXG4gICAgdGFyZ2V0OiAnZXMyMDIwJyxcclxuICAgIGNzc0NvZGVTcGxpdDogZmFsc2VcclxuICB9LFxyXG4gIG9wdGltaXplRGVwczoge1xyXG4gICAgaW5jbHVkZTogaXNUZXN0ID8gWydAdGVzdGluZy1saWJyYXJ5L3N2ZWx0ZScsICdjaGFpJ10gOiB1bmRlZmluZWRcclxuICB9LFxyXG4gIHRlc3Q6IHtcclxuICAgIGdsb2JhbHM6IHRydWUsXHJcbiAgICBlbnZpcm9ubWVudDogJ2pzZG9tJyxcclxuICAgIHNldHVwRmlsZXM6ICdzcmMvc2V0dXAtdGVzdHMudHMnXHJcbiAgfVxyXG59KVxyXG4iXSwKICAibWFwcGluZ3MiOiAiO0FBQ0EsU0FBUyxvQkFBb0I7QUFDN0IsU0FBUyxjQUFjO0FBQ3ZCLFNBQVMsV0FBVztBQUNwQixZQUFZLFVBQVU7QUFKdEIsSUFBTSxtQ0FBbUM7QUFNekMsSUFBTSxTQUFTLFFBQVEsSUFBSSxhQUFhO0FBR3hDLElBQU8sc0JBQVEsYUFBYTtBQUFBLEVBQzFCLFNBQVM7QUFBQSxJQUNQLE9BQU87QUFBQSxNQUNMLEtBQVUsYUFBUSxrQ0FBVyxPQUFPO0FBQUEsSUFDdEM7QUFBQSxJQUNBLFlBQVksU0FBUyxDQUFDLFNBQVMsSUFBSSxDQUFDO0FBQUEsRUFDdEM7QUFBQSxFQUNBLFNBQVM7QUFBQSxJQUNQLE9BQU8sRUFBRSxLQUFLLENBQUMsUUFBUSxJQUFJLE9BQU8sQ0FBQztBQUFBLElBQ25DLElBQUk7QUFBQSxNQUNGLFFBQVEsQ0FBQyxDQUFDLFFBQVEsSUFBSTtBQUFBLE1BQ3RCLE9BQU87QUFBQSxRQUNMO0FBQUEsVUFDRSxNQUFNO0FBQUEsVUFDTixLQUFLLENBQUMsT0FBTyxPQUFPLE9BQU87QUFBQSxVQUMzQixTQUFTLENBQUMsZUFBZSxpQkFBaUI7QUFBQSxRQUM1QztBQUFBLE1BQ0Y7QUFBQSxJQUNGLENBQUM7QUFBQSxFQUNIO0FBQUEsRUFDQSxRQUFRO0FBQUEsSUFDTixNQUFNLFNBQVMsT0FBTztBQUFBLElBQ3RCLE9BQU8sU0FBUyxTQUFZO0FBQUEsTUFDMUIsUUFBUTtBQUFBLFFBQ04sUUFBUTtBQUFBLFFBQ1IsY0FBYztBQUFBLE1BQ2hCO0FBQUEsSUFDRjtBQUFBLEVBQ0Y7QUFBQSxFQUNBLE9BQU87QUFBQSxJQUNMLFFBQVE7QUFBQSxJQUNSLFFBQVE7QUFBQSxJQUNSLGNBQWM7QUFBQSxFQUNoQjtBQUFBLEVBQ0EsY0FBYztBQUFBLElBQ1osU0FBUyxTQUFTLENBQUMsMkJBQTJCLE1BQU0sSUFBSTtBQUFBLEVBQzFEO0FBQUEsRUFDQSxNQUFNO0FBQUEsSUFDSixTQUFTO0FBQUEsSUFDVCxhQUFhO0FBQUEsSUFDYixZQUFZO0FBQUEsRUFDZDtBQUNGLENBQUM7IiwKICAibmFtZXMiOiBbXQp9Cg==
Binary file added mvp/media/13_07_2024.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added mvp/media/13_07_2024_trim.PNG
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion mvp/server/core/analysis/rul_prediction.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import numpy as np
import onnxruntime as rt

from mvp.server.core.machine.OperationalParameters import OperationalParameters
from mvp.server.core.machine.MachineState import OperationalParameters

# Load the SVR pipeline from ONNX file
onnx_path = "mvp/server/core/analysis/artifacts/svr_pipeline_23_06_24.onnx"
Expand Down
8 changes: 5 additions & 3 deletions mvp/server/core/constants.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Gameplay
DEFAULT_SESSION_ID = 'test'
GAME_TICK_INTERVAL = 0.03 # 30ms
GAME_TICK_INTERVAL = 0.015 # 30ms
IDLE_SESSION_TTL_SECONDS = 60 * 30 # 15 minutes
SESSION_CLEANUP_INTERVAL_SECONDS = 60 * 60 # 60 minutes
TIMESTEPS_PER_MOVE = 24 # "hours"
Expand All @@ -22,7 +22,9 @@

# Financials
INITIAL_CASH = 0
REVENUE_PER_DAY = 20
REVENUE_PER_DAY = 15
MAINTENANCE_COST = 40
SENSOR_COST = 30
PREDICTION_MODEL_COST = 50
PREDICTION_MODEL_COST = 80
DEMAND_PEAK_EVENT_PROBABILITY = 0.20
DEMAND_PEAK_BONUS_MULTIPLIER = 3
Loading
Loading