Skip to content

Gold Rush Game

Using <wcp-template> and <wcp-ui-state> to build a game of Gold Rush.

Gold Rush

/ ๐Ÿช™
Cost ๐Ÿช™ — next reward ๐Ÿช™

Code

The following example assumes that you have imported the component and set up the theme.

Gold Rush
<!-- Cell template -->
<wcp-template id="game-grid-cell">
<template>
<!-- Per-cell state -->
<wcp-ui-state>
20 collapsed lines
<wcp-store type="boolean" name="$opened" value="false"></wcp-store>
<button
class="cell-btn"
bind-attr="aria-label:($opened ? '[[ type ]]' : 'hidden') + ' cell'"
bind-attr-toggle="disabled:$opened || $lost || $won"
bind-class="$opened ? '[[ type ]]' : ''"
bind-event="
click:
$opened(true),
(
'[[ type ]]' === 'trap'
? $health($health - 1)
: '[[ type ]]' === 'gold'
? $gold($gold + $reward)
: null
)
"
bind-text="$opened ? ('[[ type ]]' === 'trap' ? '๐Ÿ’ฃ' : '[[ type ]]' === 'gold' ? '๐Ÿช™' : 'ยท') : '?'"
></button>
</wcp-ui-state>
</template>
<!-- Scoped style -->
<style>
46 collapsed lines
.cell-btn {
flex: 1;
aspect-ratio: 1;
font-family: inherit;
font-size: var(--sb-text-base);
color: var(--sb-color-text);
cursor: pointer;
background: var(--sb-color-gray-6);
border: var(--wcp-bdr-sz-sm) solid var(--sb-color-gray-5);
border-radius: var(--wcp-bdr-rad-sm);
transition:
background 0.1s,
border-color 0.1s,
transform 0.1s;
&:disabled {
cursor: default;
}
&:hover:not(:disabled) {
background: var(--sb-color-gray-5);
border-color: var(--sb-color-accent);
transform: scale(1.08);
}
&:active:not(:disabled) {
transform: scale(0.92);
}
&.gold {
background: var(--sb-color-accent-low);
border-color: var(--sb-color-accent);
}
&.trap {
background: var(--sb-caution-background);
border-color: var(--sb-caution-border);
}
&.empty {
font-size: var(--sb-text-sm);
color: var(--sb-color-gray-4);
background: var(--sb-color-gray-7);
border-color: var(--sb-color-gray-6);
}
}
</style>
</wcp-template>
<!-- Game board template -->
<wcp-template id="game-grid">
11 collapsed lines
<template>
<div class="cell" template-for=",random of randoms">
<wcp-template prop-type="[[ random < 55 ? 'trap' : random < 175 ? 'gold' : 'empty' ]]" use="game-grid-cell"></wcp-template>
</div>
</template>
<style>
.cell {
display: flex;
}
</style>
</wcp-template>
<wcp-ui-state>
<!-- Game state -->
11 collapsed lines
<wcp-store type="number" name="$gold" value="0"></wcp-store>
<wcp-store type="number" name="$health" value="5"></wcp-store>
<wcp-store type="number" name="$target" value="25"></wcp-store>
<wcp-store type="number" name="$shovelCost" value="5"></wcp-store>
<wcp-store type="number" name="$reward" value="2"></wcp-store>
<wcp-store type="number" name="$resetCount" value="0"></wcp-store>
<wcp-store derive="$gold >= $target" name="$won"></wcp-store>
<wcp-store derive="$health <= 0" name="$lost"></wcp-store>
<wcp-store derive="Math.min($gold / $target * 100, 100)" name="$goldPct"></wcp-store>
<wcp-store derive="$health / 5 * 100" name="$healthPct"></wcp-store>
<div class="rush">
<!-- Game header -->
21 collapsed lines
<div class="rush-header">
<h3 class="rush-title">Gold Rush</h3>
<div class="rush-health">
<div class="rush-health-track">
<div class="rush-health-fill" bind-class-toggle="is-critical:$health <= 2" bind-style="width:$healthPct + '%'"></div>
</div>
<span class="rush-health-label" bind-text="$health + ' โค๏ธ'"></span>
</div>
</div>
<div class="rush-progress">
<div class="rush-progress-track">
<div class="rush-progress-fill" bind-style="width:$goldPct + '%'"></div>
</div>
<span class="rush-progress-label">
<span bind-text="$gold"></span>
/
<span bind-text="$target"></span>
๐Ÿช™
</span>
</div>
<!-- Game board -->
32 collapsed lines
<div class="rush-board-wrap">
<div class="rush-board">
<!-- prop-reset is used just to force re-render the template -->
<wcp-template
bind-attr="prop-reset:$resetCount"
prop-randoms:js="crypto.getRandomValues(new Uint8Array(6 * 6))"
prop-reset="0"
use="game-grid"
></wcp-template>
</div>
<!-- Overlay appears over the board when the game ends -->
<div class="rush-overlay" style="display: none" bind-show="$won || $lost">
<div class="rush-result" bind-class-toggle="rush-result-win:$won; rush-result-lose:$lost">
<p class="rush-result-msg" bind-text="$won ? '๐Ÿ’ฐ You struck gold!' : '๐Ÿ’€ Cave collapsed!"></p>
<button
class="rush-restart"
bind-event="
click:
$resetCount($resetCount + 1),
$gold(0),
$health(5),
$target(25),
$shovelCost(5),
$reward(2)
"
>
Play again
</button>
</div>
</div>
</div>
<!-- Shop -->
16 collapsed lines
<div class="rush-shop">
<button
class="rush-upgrade"
bind-attr-toggle="disabled:$gold < $shovelCost || $lost || $won"
bind-event="click:$gold($gold - $shovelCost),$reward($reward + 1),$shovelCost($shovelCost + 5)"
>
โ›๏ธ Upgrade shovel
</button>
<span class="rush-shop-info">
Cost
<strong bind-text="$shovelCost"></strong>
๐Ÿช™ &mdash; next reward
<strong bind-text="$reward + 1"></strong>
๐Ÿช™
</span>
</div>
</div>
</wcp-ui-state>
<style>
193 collapsed lines
.rush {
display: flex;
flex-direction: column;
gap: 0.875rem;
max-inline-size: 24rem;
margin: auto;
font-family: var(--sb-md-font);
color: var(--sb-color-text);
& .rush-header {
display: flex;
gap: 1rem;
align-items: center;
justify-content: space-between;
}
& .rush-title {
margin: 0;
font-size: var(--sb-text-xl);
line-height: var(--sb-line-height-headings);
color: var(--sb-heading-color);
}
& .rush-health {
display: flex;
flex-shrink: 0;
gap: 0.5rem;
align-items: center;
}
& .rush-health-track {
inline-size: 4.5rem;
block-size: 6px;
overflow: hidden;
background: var(--sb-color-gray-6);
border: var(--wcp-bdr-sz-sm) solid var(--sb-color-gray-5);
border-radius: 999px;
}
& .rush-health-fill {
block-size: 100%;
background: var(--sb-tip-border);
border-radius: 999px;
transition:
width 0.25s ease,
background 0.25s;
&.is-critical {
background: var(--sb-caution-border);
}
}
& .rush-health-label {
font-size: var(--sb-text-sm);
color: var(--sb-color-gray-3);
white-space: nowrap;
}
& .rush-progress {
display: flex;
gap: 0.75rem;
align-items: center;
}
& .rush-progress-track {
flex: 1;
block-size: 6px;
overflow: hidden;
background: var(--sb-color-gray-6);
border: var(--wcp-bdr-sz-sm) solid var(--sb-color-gray-5);
border-radius: 999px;
}
& .rush-progress-fill {
block-size: 100%;
background: var(--sb-color-accent);
border-radius: 999px;
transition: width 0.25s ease;
}
& .rush-progress-label {
flex-shrink: 0;
font-size: var(--sb-text-sm);
color: var(--sb-color-gray-3);
white-space: nowrap;
}
& .rush-board-wrap {
position: relative;
}
& .rush-board {
display: grid;
grid-template-columns: repeat(6, 1fr);
gap: 0.375rem;
padding: 0.625rem;
background: var(--sb-color-gray-7);
border: var(--wcp-bdr-sz-sm) solid var(--sb-color-gray-5);
border-radius: var(--wcp-bdr-rad-md);
}
& .rush-overlay {
position: absolute;
inset: 0;
display: flex;
align-items: center;
justify-content: center;
background: var(--wcp-overlay);
border-radius: var(--wcp-bdr-rad-md);
}
& .rush-result {
display: flex;
flex-direction: column;
gap: 0.875rem;
align-items: center;
padding: 1.375rem 2rem;
border: var(--wcp-bdr-sz-sm) solid;
border-radius: var(--wcp-bdr-rad-md);
}
& .rush-result-win {
color: var(--sb-tip-text);
background: var(--sb-tip-background);
border-color: var(--sb-tip-border);
}
& .rush-result-lose {
color: var(--sb-caution-text);
background: var(--sb-caution-background);
border-color: var(--sb-caution-border);
}
& .rush-result-msg {
margin: 0;
font-size: var(--sb-text-lg);
font-weight: 700;
}
& .rush-restart {
display: inline-block;
padding: 0.4rem 1.125rem;
font-size: var(--sb-text-sm);
font-weight: 500;
color: var(--wcp-primary-content);
text-decoration: none;
background: var(--sb-color-accent);
border: none;
border-radius: var(--wcp-bdr-rad-sm);
&:hover {
filter: brightness(1.1);
}
}
& .rush-shop {
display: flex;
flex-wrap: wrap;
gap: 1rem;
align-items: center;
padding: 0.75rem 0.875rem;
background: var(--sb-color-gray-7);
border: var(--wcp-bdr-sz-sm) solid var(--sb-color-gray-5);
border-radius: var(--wcp-bdr-rad-sm);
}
& .rush-upgrade {
padding: 0.4rem 0.875rem;
font-family: inherit;
font-size: var(--sb-text-sm);
color: var(--wcp-primary-content);
white-space: nowrap;
cursor: pointer;
background: var(--sb-color-accent);
border: none;
border-radius: var(--wcp-bdr-rad-sm);
&:hover:not(:disabled) {
filter: brightness(1.1);
}
&:disabled {
cursor: not-allowed;
opacity: 0.55;
filter: grayscale(1);
}
}
& .rush-shop-info {
font-size: var(--sb-text-sm);
color: var(--sb-color-gray-3);
}
}
</style>