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.
<!-- 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> ๐ช — 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>