Survive Your Finances
by MagicLion672538 lines61.0 KB
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Survive Your Finances</title>
<style>
* {
box-sizing: border-box;
margin: 0;
padding: 0;
}
body {
font-family: Arial, Helvetica, sans-serif;
background:
radial-gradient(circle at top left, #183b67 0%, transparent 35%),
radial-gradient(circle at bottom right, #102f4d 0%, transparent 35%),
#07111f;
color: #f5f7fa;
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
padding: 20px;
}
.game {
width: 100%;
max-width: 1050px;
min-height: 700px;
background: rgba(10, 24, 40, 0.97);
border: 1px solid rgba(255,255,255,0.08);
border-radius: 24px;
overflow: hidden;
box-shadow: 0 25px 80px rgba(0,0,0,0.45);
}
.topbar {
padding: 18px 25px;
background: rgba(255,255,255,0.035);
border-bottom: 1px solid rgba(255,255,255,0.07);
display: flex;
justify-content: space-between;
align-items: center;
gap: 15px;
flex-wrap: wrap;
}
.brand {
font-weight: 900;
letter-spacing: 1px;
}
.stats {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.stat {
padding: 9px 14px;
border-radius: 12px;
background: rgba(255,255,255,0.07);
font-size: 14px;
}
.stat strong {
color: #67e8f9;
}
.progress-wrap {
width: 100%;
height: 6px;
background: rgba(255,255,255,0.08);
}
.progress {
height: 100%;
width: 0%;
background: linear-gradient(90deg, #22d3ee, #60a5fa);
transition: width .4s ease;
}
.screen {
padding: 55px;
min-height: 630px;
display: none;
animation: fade .35s ease;
}
.screen.active {
display: block;
}
@keyframes fade {
from { opacity: 0; transform: translateY(10px); }
to { opacity: 1; transform: translateY(0); }
}
.center {
text-align: center;
}
.emoji-big {
font-size: 70px;
margin-bottom: 15px;
}
h1 {
font-size: clamp(38px, 7vw, 72px);
line-height: .95;
margin-bottom: 20px;
font-weight: 950;
}
h2 {
font-size: 34px;
margin-bottom: 12px;
}
h3 {
margin-bottom: 8px;
}
.subtitle {
color: #aebdce;
font-size: 17px;
line-height: 1.6;
max-width: 720px;
margin: 0 auto 30px;
}
.highlight {
color: #67e8f9;
}
.income-card {
margin: 30px auto;
max-width: 430px;
padding: 30px;
border-radius: 22px;
background: linear-gradient(135deg, rgba(34,211,238,.12), rgba(96,165,250,.08));
border: 1px solid rgba(103,232,249,.2);
}
.income {
font-size: 52px;
font-weight: 900;
color: #67e8f9;
}
.btn {
border: none;
border-radius: 13px;
padding: 14px 22px;
font-size: 15px;
font-weight: 800;
cursor: pointer;
transition: .2s;
color: white;
background: linear-gradient(135deg, #0891b2, #2563eb);
box-shadow: 0 8px 20px rgba(37,99,235,.22);
}
.btn:hover {
transform: translateY(-2px);
filter: brightness(1.1);
}
.btn:disabled {
opacity: .4;
cursor: not-allowed;
transform: none;
}
.btn.secondary {
background: rgba(255,255,255,.08);
box-shadow: none;
}
.btn.danger {
background: linear-gradient(135deg, #dc2626, #b91c1c);
}
.btn.success {
background: linear-gradient(135deg, #059669, #047857);
}
.round-label {
display: inline-block;
padding: 8px 13px;
border-radius: 999px;
background: rgba(34,211,238,.1);
color: #67e8f9;
font-size: 13px;
font-weight: 800;
margin-bottom: 15px;
letter-spacing: .5px;
}
.grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 16px;
margin: 25px 0;
}
.card {
background: rgba(255,255,255,.045);
border: 1px solid rgba(255,255,255,.08);
border-radius: 18px;
padding: 20px;
}
.card:hover {
border-color: rgba(103,232,249,.25);
}
.card-title {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 13px;
}
.amount {
color: #67e8f9;
font-weight: 900;
}
input[type="number"] {
width: 100%;
padding: 13px;
border-radius: 10px;
border: 1px solid rgba(255,255,255,.1);
background: #081827;
color: white;
font-size: 16px;
outline: none;
}
input[type="number"]:focus {
border-color: #22d3ee;
}
.budget-total {
position: sticky;
bottom: 10px;
padding: 18px;
border-radius: 16px;
background: rgba(7,17,31,.95);
border: 1px solid rgba(255,255,255,.1);
margin-top: 10px;
backdrop-filter: blur(10px);
}
.budget-line {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
}
.remaining.good {
color: #34d399;
}
.remaining.bad {
color: #fb7185;
}
.choices {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 15px;
margin: 20px 0;
}
.choice {
border: 1px solid rgba(255,255,255,.1);
background: rgba(255,255,255,.04);
color: white;
border-radius: 17px;
padding: 20px;
text-align: left;
cursor: pointer;
transition: .2s;
}
.choice:hover {
transform: translateY(-3px);
border-color: #22d3ee;
}
.choice.selected {
border-color: #22d3ee;
background: rgba(34,211,238,.12);
box-shadow: 0 0 0 2px rgba(34,211,238,.08);
}
.choice .icon {
font-size: 32px;
margin-bottom: 10px;
}
.choice .price {
color: #67e8f9;
font-weight: 900;
margin-top: 10px;
}
.choice small {
display: block;
color: #91a2b6;
margin-top: 7px;
line-height: 1.4;
}
.category {
margin-top: 28px;
}
.category h3 {
color: #dbeafe;
}
.feedback {
margin-top: 20px;
padding: 18px;
border-radius: 15px;
background: rgba(255,255,255,.05);
border-left: 4px solid #22d3ee;
line-height: 1.5;
}
.feedback.good {
border-left-color: #34d399;
}
.feedback.bad {
border-left-color: #fb7185;
}
.feedback.warning {
border-left-color: #fbbf24;
}
.emergency-box {
margin: 25px 0;
padding: 25px;
border-radius: 20px;
background: linear-gradient(135deg, rgba(220,38,38,.12), rgba(251,191,36,.05));
border: 1px solid rgba(248,113,113,.2);
}
.emergency-amount {
font-size: 48px;
font-weight: 950;
color: #fb7185;
margin: 10px 0;
}
.balance-board {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 12px;
margin: 25px 0;
}
.balance-item {
padding: 18px;
border-radius: 15px;
background: rgba(255,255,255,.05);
}
.balance-item span {
display: block;
color: #92a3b7;
font-size: 12px;
margin-bottom: 6px;
}
.balance-item strong {
font-size: 20px;
}
.sorting {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 20px;
margin-top: 25px;
}
.sort-zone {
min-height: 280px;
padding: 18px;
border-radius: 18px;
border: 2px dashed rgba(255,255,255,.13);
background: rgba(255,255,255,.025);
}
.sort-zone h3 {
text-align: center;
margin-bottom: 15px;
}
.sort-items {
display: flex;
flex-direction: column;
gap: 10px;
}
.sort-btn {
padding: 13px;
border: 1px solid rgba(255,255,255,.08);
border-radius: 11px;
background: rgba(255,255,255,.05);
color: white;
cursor: pointer;
text-align: left;
}
.sort-btn:hover {
border-color: #22d3ee;
}
.sort-btn.used {
opacity: .35;
pointer-events: none;
}
.final-score {
font-size: 78px;
font-weight: 950;
color: #67e8f9;
margin: 10px 0;
}
.rank {
font-size: 28px;
font-weight: 900;
margin: 10px 0 25px;
}
.score-breakdown {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 10px;
margin: 25px 0;
}
.score-box {
background: rgba(255,255,255,.05);
padding: 15px 8px;
border-radius: 14px;
}
.score-box span {
display: block;
font-size: 11px;
color: #94a3b8;
margin-bottom: 7px;
}
.score-box strong {
font-size: 22px;
}
.lesson-list {
text-align: left;
max-width: 650px;
margin: 25px auto;
display: grid;
gap: 10px;
}
.lesson {
padding: 15px;
border-radius: 13px;
background: rgba(255,255,255,.05);
}
.nav {
display: flex;
justify-content: flex-end;
gap: 10px;
margin-top: 25px;
}
.nav.center {
justify-content: center;
}
.mini {
color: #91a2b6;
font-size: 13px;
}
.hidden {
display: none !important;
}
@media (max-width: 800px) {
.screen {
padding: 30px 20px;
}
.grid,
.choices,
.sorting {
grid-template-columns: 1fr;
}
.balance-board {
grid-template-columns: repeat(2, 1fr);
}
.score-breakdown {
grid-template-columns: repeat(2, 1fr);
}
h2 {
font-size: 28px;
}
}
</style>
</head>
<body>
<div class="game">
<div class="topbar">
<div class="brand">๐ธ SURVIVE YOUR FINANCES</div>
<div class="stats">
<div class="stat">๐ฐ <strong id="moneyTop">RM2,500</strong></div>
<div class="stat">โญ <strong id="pointsTop">0</strong></div>
<div class="stat">๐ <strong id="roundTop">START</strong></div>
</div>
</div>
<div class="progress-wrap">
<div class="progress" id="progress"></div>
</div>
<!-- INTRO -->
<section class="screen active" id="intro">
<div class="center">
<div class="emoji-big">๐ธ</div>
<div class="round-label">FINANCE SIMULATION GAME</div>
<h1>
SURVIVE YOUR<br>
<span class="highlight">FINANCES</span>
</h1>
<p class="subtitle">
Can you survive a month with
<strong>RM2,500</strong>?
Make smart choices, manage your money,
and survive the month.
</p>
<div class="income-card">
<div class="mini">MONTHLY INCOME</div>
<div class="income">RM2,500</div>
<p class="mini">Your financial survival challenge begins now.</p>
</div>
<button class="btn" onclick="startGame()">
โถ START GAME
</button>
</div>
</section>
<!-- HOW TO PLAY -->
<section class="screen" id="how">
<div class="center">
<div class="round-label">HOW TO PLAY ๐ฎ</div>
<h2>Six Moves. One Month.</h2>
<p class="subtitle">
Every decision changes your financial outcome.
</p>
<div class="grid">
<div class="card">
<h3>01 ๐ฐ</h3>
<p>Receive your RM2,500 monthly income.</p>
</div>
<div class="card">
<h3>02 ๐งพ</h3>
<p>Build a monthly budget.</p>
</div>
<div class="card">
<h3>03 ๐๏ธ</h3>
<p>Make lifestyle and spending choices.</p>
</div>
<div class="card">
<h3>04 ๐จ</h3>
<p>Face unexpected financial emergencies.</p>
</div>
<div class="card">
<h3>05 ๐ง </h3>
<p>Manage your remaining money wisely.</p>
</div>
<div class="card">
<h3>06 ๐</h3>
<p>Collect points and become the Financial Master.</p>
</div>
</div>
<div class="feedback">
๐ก <strong>Remember:</strong> Every choice has a consequence.
</div>
<div class="nav center">
<button class="btn" onclick="showScreen('month')">
CONTINUE โ
</button>
</div>
</div>
</section>
<!-- MONTH -->
<section class="screen" id="month">
<div class="center">
<div class="round-label">YOUR NEW MONTH BEGINS ๐ฐ</div>
<h2>You've Got RM2,500.</h2>
<p class="subtitle">
Your goal is simple:
<strong>survive the month without unnecessary debt.</strong>
</p>
<div class="balance-board">
<div class="balance-item">
<span>MONTHLY INCOME</span>
<strong>RM2,500</strong>
</div>
<div class="balance-item">
<span>STARTING BALANCE</span>
<strong>RM2,500</strong>
</div>
<div class="balance-item">
<span>GOAL</span>
<strong>Survive</strong>
</div>
<div class="balance-item">
<span>ROUNDS</span>
<strong>4</strong>
</div>
</div>
<div class="feedback">
๐ Prioritise <strong>needs</strong> โ ๐ต Save consistently โ
๐ก๏ธ Protect yourself with an <strong>emergency fund</strong>.
</div>
<div class="nav center">
<button class="btn" onclick="showScreen('round1')">
START ROUND 1 โ
</button>
</div>
</div>
</section>
<!-- ROUND 1 -->
<section class="screen" id="round1">
<div class="round-label">ROUND 1 / 4 โ BUILD YOUR BUDGET ๐งพ</div>
<h2>Build Your Monthly Budget</h2>
<p class="subtitle" style="margin-left:0">
You have <strong>RM2,500</strong>.
Allocate every ringgit carefully.
Your total must equal exactly RM2,500.
</p>
<div class="grid">
<div class="card">
<div class="card-title">
<span>๐ Rent</span>
<span class="amount">RM</span>
</div>
<input type="number" id="rent" min="0" value="700" oninput="updateBudget()">
</div>
<div class="card">
<div class="card-title">
<span>๐ Food</span>
<span class="amount">RM</span>
</div>
<input type="number" id="food" min="0" value="300" oninput="updateBudget()">
</div>
<div class="card">
<div class="card-title">
<span>๐ Transportation</span>
<span class="amount">RM</span>
</div>
<input type="number" id="transport" min="0" value="150" oninput="updateBudget()">
</div>
<div class="card">
<div class="card-title">
<span>๐ก Bills</span>
<span class="amount">RM</span>
</div>
<input type="number" id="bills" min="0" value="150" oninput="updateBudget()">
</div>
<div class="card">
<div class="card-title">
<span>๐๏ธ Wants</span>
<span class="amount">RM</span>
</div>
<input type="number" id="wants" min="0" value="200" oninput="updateBudget()">
</div>
<div class="card">
<div class="card-title">
<span>๐ต Savings</span>
<span class="amount">RM</span>
</div>
<input type="number" id="savings" min="0" value="500" oninput="updateBudget()">
</div>
<div class="card">
<div class="card-title">
<span>๐ก๏ธ Emergency Fund</span>
<span class="amount">RM</span>
</div>
<input type="number" id="emergency" min="0" value="500" oninput="updateBudget()">
</div>
<div class="card">
<div class="card-title">
<span>๐ฑ Phone / Data</span>
<span class="amount">RM</span>
</div>
<input type="number" id="phone" min="0" value="0" oninput="updateBudget()">
</div>
</div>
<div class="budget-total">
<div class="budget-line">
<span>Total Allocated</span>
<strong id="budgetTotal">RM2,500</strong>
</div>
<div class="budget-line">
<span>Remaining</span>
<strong id="budgetRemaining" class="remaining good">RM0</strong>
</div>
</div>
<div id="budgetFeedback" class="feedback hidden"></div>
<div class="nav">
<button class="btn" id="budgetBtn" onclick="submitBudget()">
SUBMIT BUDGET
</button>
</div>
</section>
<!-- ROUND 2 -->
<section class="screen" id="round2">
<div class="round-label">ROUND 2 / 4 โ CHOOSE YOUR LIFESTYLE ๐๏ธ</div>
<h2>Your Budget Affects Your Lifestyle</h2>
<p class="subtitle" style="margin-left:0">
Choose ONE option from each category.
Expensive choices may feel good now...
but they leave less room for savings and emergencies.
</p>
<!-- FOOD -->
<div class="category">
<h3>๐ FOOD</h3>
<div class="choices">
<button class="choice" onclick="chooseLifestyle('food', 300, this)">
<div class="icon">๐ณ</div>
<strong>Cook at Home</strong>
<small>Affordable and controlled spending.</small>
<div class="price">RM300</div>
</button>
<button class="choice" onclick="chooseLifestyle('food', 500, this)">
<div class="icon">๐</div>
<strong>Eat at Cafรฉs</strong>
<small>Convenient but more expensive.</small>
<div class="price">RM500</div>
</button>
<button class="choice" onclick="chooseLifestyle('food', 800, this)">
<div class="icon">๐ต</div>
<strong>Food Delivery</strong>
<small>Maximum convenience, maximum cost.</small>
<div class="price">RM800</div>
</button>
</div>
</div>
<!-- TRANSPORT -->
<div class="category">
<h3>๐ TRANSPORT</h3>
<div class="choices">
<button class="choice" onclick="chooseLifestyle('transport', 100, this)">
<div class="icon">๐</div>
<strong>Public Transport</strong>
<small>Cheap and budget-friendly.</small>
<div class="price">RM100</div>
</button>
<button class="choice" onclick="chooseLifestyle('transport', 250, this)">
<div class="icon">๐๏ธ</div>
<strong>Motorcycle</strong>
<small>More flexible, moderate cost.</small>
<div class="price">RM250</div>
</button>
<button class="choice" onclick="chooseLifestyle('transport', 500, this)">
<div class="icon">๐</div>
<strong>Car</strong>
<small>Convenient but costly.</small>
<div class="price">RM500</div>
</button>
</div>
</div>
<!-- ENTERTAINMENT -->
<div class="category">
<h3>๐ฌ ENTERTAINMENT</h3>
<div class="choices">
<button class="choice" onclick="chooseLifestyle('entertainment', 0, this)">
<div class="icon">๐ณ</div>
<strong>Free Activities</strong>
<small>Park, games, friends and chill.</small>
<div class="price">RM0</div>
</button>
<button class="choice" onclick="chooseLifestyle('entertainment', 150, this)">
<div class="icon">๐ฌ</div>
<strong>Movie + Cafรฉ</strong>
<small>A little fun without going crazy.</small>
<div class="price">RM150</div>
</button>
<button class="choice" onclick="chooseLifestyle('entertainment', 400, this)">
<div class="icon">๐๏ธ</div>
<strong>Shopping</strong>
<small>Fun now, painful later.</small>
<div class="price">RM400</div>
</button>
</div>
</div>
<!-- PHONE -->
<div class="category">
<h3>๐ฑ PHONE / DATA</h3>
<div class="choices">
<button class="choice" onclick="chooseLifestyle('phone', 30, this)">
<div class="icon">๐ถ</div>
<strong>Basic Plan</strong>
<small>Enough for calls, texts and normal data.</small>
<div class="price">RM30</div>
</button>
<button class="choice" onclick="chooseLifestyle('phone', 60, this)">
<div class="icon">๐ฑ</div>
<strong>Unlimited Plan</strong>
<small>More data and more convenience.</small>
<div class="price">RM60</div>
</button>
<button class="choice" onclick="chooseLifestyle('phone', 100, this)">
<div class="icon">๐ฅ</div>
<strong>Premium Plan</strong>
<small>Extra services you may not actually need.</small>
<div class="price">RM100</div>
</button>
</div>
</div>
<!-- SHOPPING -->
<div class="category">
<h3>๐ SHOPPING</h3>
<div class="choices">
<button class="choice" onclick="chooseLifestyle('shopping', 0, this)">
<div class="icon">๐ซ</div>
<strong>No Shopping</strong>
<small>Protect your money this month.</small>
<div class="price">RM0</div>
</button>
<button class="choice" onclick="chooseLifestyle('shopping', 100, this)">
<div class="icon">๐</div>
<strong>Small Treat</strong>
<small>One small purchase.</small>
<div class="price">RM100</div>
</button>
<button class="choice" onclick="chooseLifestyle('shopping', 300, this)">
<div class="icon">๐</div>
<strong>Shopping Spree</strong>
<small>Enjoy now... worry later.</small>
<div class="price">RM300</div>
</button>
</div>
</div>
<div id="lifestyleFeedback" class="feedback hidden"></div>
<div class="nav">
<button class="btn" onclick="finishRound2()">
LOCK IN MY LIFESTYLE โ
</button>
</div>
</section>
<!-- ROUND 3 -->
<section class="screen" id="round3">
<div class="round-label">ROUND 3 / 4 โ LIFE HAPPENS ๐จ</div>
<h2>Unexpected Expenses!</h2>
<p class="subtitle" style="margin-left:0">
Your financial plan is about to be tested.
Choose carefully because emergencies affect your final score.
</p>
<div class="balance-board">
<div class="balance-item">
<span>SAVINGS</span>
<strong id="r3Savings">RM0</strong>
</div>
<div class="balance-item">
<span>EMERGENCY FUND</span>
<strong id="r3Emergency">RM0</strong>
</div>
<div class="balance-item">
<span>DEBT</span>
<strong id="r3Debt">RM0</strong>
</div>
<div class="balance-item">
<span>POINTS</span>
<strong id="r3Points">0</strong>
</div>
</div>
<div id="emergencyArea"></div>
</section>
<!-- ROUND 4 -->
<section class="screen" id="round4">
<div class="round-label">ROUND 4 / 4 โ NEED OR WANT? ๐ค</div>
<h2>Test Your Financial Brain</h2>
<p class="subtitle" style="margin-left:0">
For each item, decide whether it is a
<strong>NEED</strong> or a <strong>WANT</strong>.
</p>
<div id="sortingArea" class="sorting"></div>
<div class="feedback">
๐ก <strong>Tip:</strong> Needs are essential for living.
Wants improve your lifestyle but can usually be delayed.
</div>
<div class="nav">
<button class="btn" id="finishSortBtn" onclick="finishRound4()" disabled>
FINISH ROUND โ
</button>
</div>
</section>
<!-- FINAL -->
<section class="screen" id="final">
<div class="center">
<div class="round-label">FINAL CHECKPOINT ๐</div>
<h2 id="finalTitle">Did You Survive?</h2>
<p class="subtitle">
Let's see how well you managed your RM2,500.
</p>
<div class="final-score">
<span id="finalScore">0</span>
<span style="font-size:28px">/100</span>
</div>
<div class="rank" id="finalRank"></div>
<div class="balance-board">
<div class="balance-item">
<span>TOTAL SPENDING</span>
<strong id="finalSpending">RM0</strong>
</div>
<div class="balance-item">
<span>SAVINGS</span>
<strong id="finalSavings">RM0</strong>
</div>
<div class="balance-item">
<span>EMERGENCY FUND</span>
<strong id="finalEmergency">RM0</strong>
</div>
<div class="balance-item">
<span>DEBT</span>
<strong id="finalDebt">RM0</strong>
</div>
</div>
<h3>๐ SCORE BREAKDOWN</h3>
<div class="score-breakdown">
<div class="score-box">
<span>BUDGET</span>
<strong id="scoreBudget">0</strong>/20
</div>
<div class="score-box">
<span>NEEDS/WANTS</span>
<strong id="scoreNeeds">0</strong>/20
</div>
<div class="score-box">
<span>SAVINGS</span>
<strong id="scoreSavings">0</strong>/20
</div>
<div class="score-box">
<span>EMERGENCY</span>
<strong id="scoreEmergency">0</strong>/20
</div>
<div class="score-box">
<span>DECISIONS</span>
<strong id="scoreDecision">0</strong>/20
</div>
</div>
<div id="finalMessage" class="feedback"></div>
<div class="lesson-list">
<div class="lesson">
๐ฐ <strong>Budget before spending.</strong>
</div>
<div class="lesson">
๐ <strong>Prioritise needs over wants.</strong>
</div>
<div class="lesson">
๐ต <strong>Save consistently.</strong>
</div>
<div class="lesson">
๐ก๏ธ <strong>Build an emergency fund.</strong>
</div>
<div class="lesson">
๐ง <strong>Think through the consequences.</strong>
</div>
</div>
<button class="btn" onclick="location.reload()">
โป PLAY AGAIN
</button>
</div>
</section>
</div>
<script>
/* =========================================================
GAME STATE
========================================================= */
const game = {
income: 2500,
budget: {
rent: 0,
food: 0,
transport: 0,
bills: 0,
wants: 0,
savings: 0,
emergency: 0,
phone: 0
},
lifestyle: {
food: 0,
transport: 0,
entertainment: 0,
phone: 0,
shopping: 0
},
savings: 0,
emergency: 0,
debt: 0,
points: 0,
budgetScore: 0,
savingsScore: 0,
emergencyScore: 0,
decisionScore: 0,
needsScore: 0,
emergenciesCompleted: 0,
emergencyIndex: 0,
sortCorrect: 0,
sorted: 0
};
/* =========================================================
SCREEN CONTROL
========================================================= */
const screens = [
"intro",
"how",
"month",
"round1",
"round2",
"round3",
"round4",
"final"
];
function showScreen(id) {
document.querySelectorAll(".screen").forEach(screen => {
screen.classList.remove("active");
});
document.getElementById(id).classList.add("active");
updateTopBar(id);
window.scrollTo({
top: 0,
behavior: "smooth"
});
}
function updateTopBar(id) {
document.getElementById("moneyTop").textContent =
"RM" + Math.max(0, calculateRemainingMoney()).toLocaleString();
document.getElementById("pointsTop").textContent =
game.points;
let roundText = "START";
if (id === "round1") roundText = "ROUND 1";
if (id === "round2") roundText = "ROUND 2";
if (id === "round3") roundText = "ROUND 3";
if (id === "round4") roundText = "ROUND 4";
if (id === "final") roundText = "FINAL";
document.getElementById("roundTop").textContent = roundText;
let percentage = 0;
if (id === "how") percentage = 8;
if (id === "month") percentage = 15;
if (id === "round1") percentage = 25;
if (id === "round2") percentage = 50;
if (id === "round3") percentage = 72;
if (id === "round4") percentage = 88;
if (id === "final") percentage = 100;
document.getElementById("progress").style.width = percentage + "%";
}
/* =========================================================
START
========================================================= */
function startGame() {
showScreen("how");
}
/* =========================================================
ROUND 1 โ BUDGET
========================================================= */
function updateBudget() {
const fields = [
"rent",
"food",
"transport",
"bills",
"wants",
"savings",
"emergency",
"phone"
];
let total = 0;
fields.forEach(id => {
let value = Number(document.getElementById(id).value);
if (value < 0 || isNaN(value)) {
value = 0;
}
total += value;
});
const remaining = 2500 - total;
document.getElementById("budgetTotal").textContent =
"RM" + total.toLocaleString();
document.getElementById("budgetRemaining").textContent =
"RM" + Math.abs(remaining).toLocaleString();
document.getElementById("budgetRemaining").className =
"remaining " + (remaining >= 0 ? "good" : "bad");
const btn = document.getElementById("budgetBtn");
btn.disabled = total !== 2500;
if (total === 2500) {
btn.textContent = "SUBMIT BUDGET โ";
} else if (total > 2500) {
btn.textContent = "OVER BUDGET";
} else {
btn.textContent = "RM" + remaining.toLocaleString() + " LEFT";
}
}
function submitBudget() {
const fields = [
"rent",
"food",
"transport",
"bills",
"wants",
"savings",
"emergency",
"phone"
];
let total = 0;
fields.forEach(id => {
game.budget[id] =
Math.max(0, Number(document.getElementById(id).value) || 0);
total += game.budget[id];
});
if (total !== 2500) {
return;
}
game.savings = game.budget.savings;
game.emergency = game.budget.emergency;
/* Budget scoring */
let score = 0;
if (game.budget.savings >= 400) score += 8;
else if (game.budget.savings >= 250) score += 5;
else if (game.budget.savings >= 100) score += 3;
if (game.budget.emergency >= 400) score += 7;
else if (game.budget.emergency >= 200) score += 5;
else if (game.budget.emergency > 0) score += 2;
if (game.budget.wants <= 250) score += 5;
else if (game.budget.wants <= 400) score += 3;
game.budgetScore = Math.min(20, score);
game.points += game.budgetScore;
const feedback = document.getElementById("budgetFeedback");
feedback.classList.remove("hidden");
if (game.budgetScore >= 16) {
feedback.className = "feedback good";
feedback.innerHTML =
"๐ฅ <strong>Excellent budget!</strong><br>" +
"You prioritised savings, emergency protection and controlled your wants." +
"<br><strong>+" + game.budgetScore + " points</strong>";
} else if (game.budgetScore >= 10) {
feedback.className = "feedback warning";
feedback.innerHTML =
"๐ <strong>Not bad!</strong><br>" +
"Your budget is workable, but there is still room to improve your financial protection." +
"<br><strong>+" + game.budgetScore + " points</strong>";
} else {
feedback.className = "feedback bad";
feedback.innerHTML =
"โ ๏ธ <strong>Risky budget.</strong><br>" +
"You may struggle when an unexpected expense appears." +
"<br><strong>+" + game.budgetScore + " points</strong>";
}
const btn = document.getElementById("budgetBtn");
btn.textContent = "CONTINUE โ";
btn.onclick = function() {
showScreen("round2");
updateTopBar("round2");
};
}
/* =========================================================
ROUND 2 โ LIFESTYLE
========================================================= */
const lifestyleChoices = {
food: null,
transport: null,
entertainment: null,
phone: null,
shopping: null
};
function chooseLifestyle(category, amount, element) {
lifestyleChoices[category] = amount;
const parent = element.parentElement;
parent.querySelectorAll(".choice").forEach(btn => {
btn.classList.remove("selected");
});
element.classList.add("selected");
calculateLifestyleFeedback();
}
function calculateLifestyleTotal() {
return Object.values(lifestyleChoices)
.reduce((sum, value) => sum + (value || 0), 0);
}
function calculateLifestyleFeedback() {
const selected = Object.values(lifestyleChoices)
.filter(value => value !== null);
if (selected.length === 0) return;
const total = calculateLifestyleTotal();
const feedback = document.getElementById("lifestyleFeedback");
feedback.classList.remove("hidden");
const baseSpending =
game.budget.rent +
game.budget.bills;
const remaining =
2500 -
baseSpending -
total;
if (remaining >= 700) {
feedback.className = "feedback good";
feedback.innerHTML =
"๐ข <strong>Comfortable lifestyle.</strong><br>" +
"Estimated money remaining after your current choices: " +
"<strong>RM" + remaining.toLocaleString() + "</strong>.";
} else if (remaining >= 400) {
feedback.className = "feedback warning";
feedback.innerHTML =
"๐ก <strong>Be careful.</strong><br>" +
"Your lifestyle is manageable, but unexpected expenses could reduce your savings.";
} else {
feedback.className = "feedback bad";
feedback.innerHTML =
"๐ด <strong>Danger zone!</strong><br>" +
"Your lifestyle choices are eating up a large part of your monthly money.";
}
}
function finishRound2() {
const categories = Object.keys(lifestyleChoices);
const missing = categories.filter(
category => lifestyleChoices[category] === null
);
if (missing.length > 0) {
alert(
"You haven't chosen everything yet!\n\n" +
"Complete all 5 lifestyle categories."
);
return;
}
game.lifestyle = {
...lifestyleChoices
};
const lifestyleTotal = calculateLifestyleTotal();
/*
Decision score:
Lower lifestyle cost = better protection.
*/
let score = 0;
if (lifestyleTotal <= 500) score = 20;
else if (lifestyleTotal <= 750) score = 16;
else if (lifestyleTotal <= 1000) score = 12;
else if (lifestyleTotal <= 1250) score = 8;
else score = 5;
game.decisionScore += score;
game.points += score;
showScreen("round3");
prepareRound3();
}
/* =========================================================
ROUND 3 โ EMERGENCIES
========================================================= */
const emergencies = [
{
icon: "๐๏ธ",
title: "Your motorcycle suddenly breaks down.",
amount: 350,
options: [
{
title: "USE EMERGENCY FUND",
type: "emergency",
points: 10,
lesson: "Excellent! This is exactly why an emergency fund exists."
},
{
title: "USE SAVINGS",
type: "savings",
points: 5,
lesson: "You solved the problem, but your long-term savings are reduced."
},
{
title: "BORROW MONEY",
type: "borrow",
points: -5,
lesson: "The problem is solved now, but debt creates a future obligation."
}
]
},
{
icon: "๐ฅ",
title: "You receive an unexpected medical bill.",
amount: 500,
options: [
{
title: "USE EMERGENCY FUND",
type: "emergency",
points: 10,
lesson: "Good decision. Your emergency fund protects you from unexpected medical costs."
},
{
title: "CUT WANTS + USE CASH",
type: "cash",
points: 7,
lesson: "Smart adjustment. You sacrificed wants instead of creating debt."
},
{
title: "USE CREDIT / BORROW",
type: "borrow",
points: -5,
lesson: "Borrowing can help immediately, but debt may follow you into the next month."
}
]
},
{
icon: "๐ฑ",
title: "Your phone screen breaks and you need it for university.",
amount: 300,
options: [
{
title: "USE EMERGENCY FUND",
type: "emergency",
points: 9,
lesson: "Your emergency fund protects you from unexpected essential repairs."
},
{
title: "USE SAVINGS",
type: "savings",
points: 5,
lesson: "You avoided debt, but your savings took a hit."
},
{
title: "BUY A NEW PHONE ON CREDIT",
type: "borrow",
points: -6,
lesson: "Buying on credit creates debt for something that could potentially be repaired."
}
]
},
{
icon: "๐ ",
title: "Your landlord requires an unexpected RM300 repair contribution.",
amount: 300,
options: [
{
title: "USE EMERGENCY FUND",
type: "emergency",
points: 9,
lesson: "Good use of emergency protection for an unexpected essential expense."
},
{
title: "CUT ENTERTAINMENT + PAY",
type: "cash",
points: 7,
lesson: "You adjusted your lifestyle instead of borrowing."
},
{
title: "BORROW MONEY",
type: "borrow",
points: -5,
lesson: "Debt solves the immediate issue but reduces your future financial flexibility."
}
]
}
];
function prepareRound3() {
game.emergencyIndex = 0;
renderEmergency();
}
function renderEmergency() {
const area = document.getElementById("emergencyArea");
if (game.emergencyIndex >= emergencies.length) {
finishRound3();
return;
}
const e = emergencies[game.emergencyIndex];
document.getElementById("r3Savings").textContent =
"RM" + Math.max(0, game.savings).toLocaleString();
document.getElementById("r3Emergency").textContent =
"RM" + Math.max(0, game.emergency).toLocaleString();
document.getElementById("r3Debt").textContent =
"RM" + game.debt.toLocaleString();
document.getElementById("r3Points").textContent =
game.points;
area.innerHTML = `
<div class="emergency-box">
<div style="font-size:48px">${e.icon}</div>
<h3>EMERGENCY ${game.emergencyIndex + 1} / ${emergencies.length}</h3>
<p style="font-size:20px; margin-top:10px;">
${e.title}
</p>
<div class="emergency-amount">
RM${e.amount}
</div>
<p class="mini">
How will you pay?
</p>
</div>
<div class="choices">
${e.options.map((option, index) => `
<button
class="choice"
onclick="handleEmergency(${index})"
>
<div class="icon">
${index === 0 ? "๐ก๏ธ" : index === 1 ? "๐ต" : "๐ณ"}
</div>
<strong>${option.title}</strong>
<small>
${index === 0
? "Protect your finances."
: index === 1
? "Avoid unnecessary debt."
: "Get money now, pay later."
}
</small>
</button>
`).join("")}
</div>
<div id="emergencyFeedback"></div>
`;
}
function handleEmergency(optionIndex) {
const e = emergencies[game.emergencyIndex];
const option = e.options[optionIndex];
let message = "";
let className = "feedback";
if (option.type === "emergency") {
if (game.emergency >= e.amount) {
game.emergency -= e.amount;
message = `
๐ข <strong>GOOD DECISION!</strong><br>
You used your emergency fund to handle the unexpected expense.<br><br>
Emergency Fund: <strong>-RM${e.amount}</strong><br>
Debt: <strong>RM${game.debt}</strong><br>
<strong>+${option.points} POINTS</strong><br><br>
๐ก ${option.lesson}
`;
className += " good";
} else {
const available = game.emergency;
game.emergency = 0;
const remaining = e.amount - available;
game.debt += remaining;
message = `
โ ๏ธ <strong>YOUR EMERGENCY FUND WAS TOO SMALL.</strong><br>
You had RM${available} available, so you still needed RM${remaining}.<br><br>
Remaining amount became debt.<br>
Debt: <strong>RM${game.debt}</strong><br>
<strong>+${Math.max(-5, option.points - 4)} POINTS</strong><br><br>
๐ก Try building a larger emergency fund.
`;
game.points += Math.max(-5, option.points - 4);
className += " warning";
updateTopBar("round3");
showEmergencyContinue(message, className);
return;
}
} else if (option.type === "savings") {
if (game.savings >= e.amount) {
game.savings -= e.amount;
message = `
๐ก <strong>ACCEPTABLE DECISION</strong><br>
You used your savings to cover the emergency.<br><br>
Savings: <strong>-RM${e.amount}</strong><br>
Debt: <strong>RM${game.debt}</strong><br>
<strong>+${option.points} POINTS</strong><br><br>
๐ก ${option.lesson}
`;
className += " warning";
} else {
const available = game.savings;
game.savings = 0;
const remaining = e.amount - available;
game.debt += remaining;
message = `
โ ๏ธ <strong>NOT ENOUGH SAVINGS.</strong><br>
You used your remaining RM${available}, then borrowed RM${remaining}.<br><br>
Debt: <strong>RM${game.debt}</strong><br>
<strong>+1 POINT</strong>
`;
game.points += 1;
className += " warning";
updateTopBar("round3");
showEmergencyContinue(message, className);
return;
}
} else if (option.type === "cash") {
/*
Cutting wants means we sacrifice lifestyle money
instead of touching savings.
*/
const lifestyleTotal = calculateLifestyleTotal();
if (lifestyleTotal >= e.amount) {
game.points += option.points;
message = `
๐ข <strong>SMART ADJUSTMENT!</strong><br>
You cut unnecessary spending to handle the emergency.<br><br>
No new debt.<br>
Savings protected.<br>
<strong>+${option.points} POINTS</strong><br><br>
๐ก ${option.lesson}
`;
className += " good";
} else {
const shortfall = e.amount - lifestyleTotal;
game.debt += shortfall;
game.points += 2;
message = `
๐ก <strong>PARTIAL SOLUTION.</strong><br>
Your lifestyle spending wasn't enough to fully cover the expense.<br><br>
Remaining debt: <strong>RM${shortfall}</strong><br>
<strong>+2 POINTS</strong>
`;
className += " warning";
updateTopBar("round3");
showEmergencyContinue(message, className);
return;
}
} else if (option.type === "borrow") {
game.debt += e.amount;
message = `
๐ด <strong>RISKY DECISION</strong><br>
You borrowed RM${e.amount} to pay for the emergency.<br><br>
Immediate payment: <strong>RM0</strong><br>
Debt: <strong>RM${game.debt}</strong><br>
<strong>${option.points} POINTS</strong><br><br>
๐ก ${option.lesson}
`;
className += " bad";
}
game.points += option.points;
showEmergencyContinue(message, className);
}
function showEmergencyContinue(message, className) {
const feedback = document.getElementById("emergencyFeedback");
feedback.className = className;
feedback.innerHTML = `
${message}
<br><br>
<button
class="btn"
onclick="nextEmergency()"
>
${game.emergencyIndex < emergencies.length - 1
? "NEXT EMERGENCY โ"
: "CONTINUE TO ROUND 4 โ"
}
</button>
`;
document.querySelectorAll("#emergencyArea .choice")
.forEach(button => {
button.disabled = true;
button.style.opacity = ".45";
button.style.pointerEvents = "none";
});
updateTopBar("round3");
}
function nextEmergency() {
game.emergencyIndex++;
renderEmergency();
}
function finishRound3() {
/*
Emergency score is based on:
- having emergency fund
- avoiding debt
- making good decisions
*/
let score = 0;
if (game.emergency >= 300) score += 8;
else if (game.emergency > 0) score += 5;
if (game.debt === 0) score += 7;
else if (game.debt <= 300) score += 4;
if (game.savings > 0) score += 5;
game.emergencyScore = Math.min(20, score);
game.points += game.emergencyScore;
// Prepare Round 4
setupRound4();
// FIX: actually move the player to Round 4
showScreen("round4");
}
/* =========================================================
ROUND 4 โ NEEDS VS WANTS
========================================================= */
const sortingItems = [
{
name: "๐ Groceries",
price: 200,
correct: "need"
},
{
name: "๐ฑ New Phone",
price: 1200,
correct: "want"
},
{
name: "๐ Rent",
price: 700,
correct: "need"
},
{
name: "๐ง New Headphones",
price: 300,
correct: "want"
},
{
name: "๐ก Electricity Bill",
price: 100,
correct: "need"
},
{
name: "๐ Food Delivery",
price: 80,
correct: "want"
},
{
name: "๐ Medicine",
price: 50,
correct: "need"
},
{
name: "๐ฎ New Video Game",
price: 250,
correct: "want"
}
];
function setupRound4() {
const area = document.getElementById("sortingArea");
area.innerHTML = `
<div class="sort-zone">
<h3>๐ NEEDS</h3>
<div class="sort-items" id="needItems">
${sortingItems.map((item, index) => `
<button
class="sort-btn"
id="item${index}"
onclick="sortItem(${index}, 'need')"
>
${item.name}
<span style="float:right;color:#67e8f9">
RM${item.price}
</span>
</button>
`).join("")}
</div>
</div>
<div class="sort-zone">
<h3>๐๏ธ WANTS</h3>
<div class="sort-items" id="wantItems">
${sortingItems.map((item, index) => `
<button
class="sort-btn"
id="itemWant${index}"
onclick="sortItem(${index}, 'want')"
>
${item.name}
<span style="float:right;color:#67e8f9">
RM${item.price}
</span>
</button>
`).join("")}
</div>
</div>
`;
/*
We hide duplicate buttons initially.
A separate selector will appear below.
*/
const zones = area.querySelectorAll(".sort-zone");
zones.forEach(zone => {
const buttons = zone.querySelectorAll(".sort-btn");
buttons.forEach(button => {
button.style.display = "none";
});
});
createSortingSelector();
}
function createSortingSelector() {
const area = document.getElementById("sortingArea");
const selector = document.createElement("div");
selector.style.gridColumn = "1 / -1";
selector.innerHTML = `
<div class="card">
<h3>Choose an item to classify:</h3>
<div
id="sortingButtons"
style="
display:grid;
grid-template-columns:repeat(2,1fr);
gap:10px;
margin-top:15px;
"
>
${sortingItems.map((item, index) => `
<button
class="choice"
id="sortChoice${index}"
onclick="selectSortItem(${index})"
style="padding:15px;"
>
${item.name}
<br>
<span class="mini">
RM${item.price}
</span>
</button>
`).join("")}
</div>
<p
id="sortInstruction"
class="mini"
style="margin-top:15px;"
>
Pick an item above, then choose NEED or WANT.
</p>
</div>
`;
area.appendChild(selector);
}
let selectedSortItem = null;
function selectSortItem(index) {
selectedSortItem = index;
document.querySelectorAll("#sortingButtons .choice")
.forEach(button => {
button.classList.remove("selected");
});
document
.getElementById("sortChoice" + index)
.classList.add("selected");
document.getElementById("sortInstruction").innerHTML = `
<strong>${sortingItems[index].name}</strong>
โ Is this a NEED or a WANT?
<br><br>
<button
class="btn"
onclick="sortItem(${index}, 'need')"
>
๐ NEED
</button>
<button
class="btn secondary"
onclick="sortItem(${index}, 'want')"
>
๐๏ธ WANT
</button>
`;
}
function sortItem(index, answer) {
if (selectedSortItem !== index) return;
const item = sortingItems[index];
const correct = item.correct === answer;
if (correct) {
game.sortCorrect++;
game.points += 2;
document.getElementById("sortChoice" + index).style.opacity = ".35";
document.getElementById("sortChoice" + index).style.pointerEvents = "none";
document.getElementById("sortInstruction").innerHTML = `
๐ข <strong>CORRECT!</strong>
${item.name} is a
<strong>${item.correct.toUpperCase()}</strong>.
+2 points!
`;
} else {
game.points -= 1;
document.getElementById("sortInstruction").innerHTML = `
๐ด <strong>Not quite!</strong>
${item.name} is better classified as a
<strong>${item.correct.toUpperCase()}</strong>.
-1 point.
`;
}
game.sorted++;
selectedSortItem = null;
document.querySelectorAll("#sortingButtons .choice")
.forEach(button => {
button.classList.remove("selected");
});
if (game.sorted >= sortingItems.length) {
document.getElementById("sortInstruction").innerHTML +=
"<br><br>๐ You've classified all the items!";
document.getElementById("finishSortBtn").disabled = false;
}
}
function finishRound4() {
/*
Needs/Wants = 20 points maximum.
8 items x 2.5 points.
*/
game.needsScore =
Math.round(
(game.sortCorrect / sortingItems.length) * 20
);
game.points += game.needsScore;
showFinal();
}
/* =========================================================
FINAL CALCULATIONS
========================================================= */
function calculateRemainingMoney() {
const budgetSpent =
Object.values(game.budget)
.reduce((sum, value) => sum + value, 0);
return Math.max(0, 2500 - budgetSpent);
}
function showFinal() {
const lifestyleTotal =
calculateLifestyleTotal();
const totalSpending =
game.budget.rent +
game.budget.food +
game.budget.transport +
game.budget.bills +
game.budget.wants +
game.budget.phone +
lifestyleTotal;
/*
Savings + emergency are technically allocated from
the original RM2,500. Lifestyle spending represents
additional pressure on the lifestyle side.
*/
let finalScore =
game.budgetScore +
game.needsScore +
game.emergencyScore +
game.decisionScore +
game.savingsScore;
/*
Savings score
*/
let savingsScore = 0;
if (game.savings >= 700) savingsScore = 20;
else if (game.savings >= 500) savingsScore = 17;
else if (game.savings >= 300) savingsScore = 14;
else if (game.savings >= 150) savingsScore = 9;
else if (game.savings > 0) savingsScore = 5;
/*
Debt penalty
*/
if (game.debt >= 1000) {
savingsScore = Math.max(0, savingsScore - 6);
} else if (game.debt >= 500) {
savingsScore = Math.max(0, savingsScore - 4);
} else if (game.debt > 0) {
savingsScore = Math.max(0, savingsScore - 2);
}
game.savingsScore = Math.min(20, savingsScore);
finalScore =
game.budgetScore +
game.needsScore +
game.savingsScore +
game.emergencyScore +
game.decisionScore;
finalScore = Math.max(0, Math.min(100, finalScore));
game.points = finalScore;
/* Rank */
let rank = "";
let title = "";
let message = "";
if (finalScore >= 80) {
rank = "๐ FINANCIAL MASTER";
title = "YOU SURVIVED LIKE A PRO!";
message = `
๐ฅ Amazing work!
You showed strong budgeting, saving and decision-making skills.
You were prepared for unexpected expenses and avoided unnecessary debt.
`;
} else if (finalScore >= 60) {
rank = "๐ฐ MONEY SMART";
title = "YOU SURVIVED!";
message = `
๐ Good job!
Your financial decisions were generally strong,
although there are still some areas you can improve.
`;
} else if (finalScore >= 40) {
rank = "๐ FINANCIAL LEARNER";
title = "YOU BARELY SURVIVED";
message = `
๐ You made it through the month,
but some financial decisions could put you at risk in real life.
Keep working on your budgeting and emergency savings.
`;
} else {
rank = "๐จ FINANCIAL SURVIVOR";
title = "FINANCIAL TROUBLE!";
message = `
๐ Your money management needs attention.
Too much spending or debt can make emergencies much harder to handle.
`;
}
/* Update UI */
document.getElementById("finalTitle").textContent = title;
document.getElementById("finalScore").textContent =
finalScore;
document.getElementById("finalRank").textContent =
rank;
document.getElementById("finalSpending").textContent =
"RM" + Math.round(totalSpending).toLocaleString();
document.getElementById("finalSavings").textContent =
"RM" + Math.max(0, Math.round(game.savings)).toLocaleString();
document.getElementById("finalEmergency").textContent =
"RM" + Math.max(0, Math.round(game.emergency)).toLocaleString();
document.getElementById("finalDebt").textContent =
"RM" + Math.round(game.debt).toLocaleString();
document.getElementById("scoreBudget").textContent =
game.budgetScore;
document.getElementById("scoreNeeds").textContent =
game.needsScore;
document.getElementById("scoreSavings").textContent =
game.savingsScore;
document.getElementById("scoreEmergency").textContent =
game.emergencyScore;
document.getElementById("scoreDecision").textContent =
game.decisionScore;
document.getElementById("finalMessage").innerHTML =
message;
document.getElementById("finalMessage").className =
finalScore >= 60
? "feedback good"
: finalScore >= 40
? "feedback warning"
: "feedback bad";
showScreen("final");
}
/* =========================================================
INITIALISE
========================================================= */
updateBudget();
</script>
</body>
</html>Game Source: Survive Your Finances
Creator: MagicLion67
Libraries: none
Complexity: complex (2538 lines, 61.0 KB)
The full source code is displayed above on this page.
Remix Instructions
To remix this game, copy the source code above and modify it. Add a ARCADELAB header at the top with "remix_of: survive-your-finances-magiclion67" to link back to the original. Then publish at arcadelab.ai/publish.