# Make a Deckbuilding Battle: Energy, Block and Discard Piles

Build Paper Duel: a visible enemy intent, two card types and a small turn-based combat loop.

Canonical: https://binxforge.com/guides/make-a-deckbuilding-battle
Published and checked: 2026-10-11
Topics: Deckbuilder tutorial, Roguelike deckbuilder, Card game, Turn based combat

## Run the reference
Download https://binxforge.com/examples/guide-workshops/guide-workshops.zip, extract into a new folder and run node check.mjs. For browser games, serve with python3 -m http.server 8080 and open http://localhost:8080/index.html. Browser ES modules; Node 24 checks.

## 1. Make one deterministic encounter
Start Deck battler. The player has eighteen health and three energy; the enemy has eighteen health and announces seven damage. Strike deals six; Guard adds five block. These small numbers let you calculate a turn before playing it.

Expected: A visible hand, energy count and enemy intent.
Check: Confirm the initial hand is Strike, Guard, Strike and every card costs one energy.

## 2. Own card play in one function
deck.play validates a current hand index, energy and active encounter before removing the card. Apply its effect, spend energy and put it in discard. Render from the resulting hand so removed-card indices do not refer to yesterday’s UI.

Expected: A card leaves the hand exactly once and spends one energy.
Check: Click an invalid index or play with zero energy; health, hand and piles stay unchanged.

## 3. Resolve the enemy turn explicitly
End turn applies max(0,7-block) damage, clears block, discards the unused hand and refills energy. Draw up to three cards, refilling the draw pile from discard when empty. This reference uses a fixed order to support repeatable tests; it is not a random shuffle.

Expected: No negative damage, persistent old block or unlimited hand.
Check: Play Guard, then end turn: take two damage and start the next turn with zero block.

## 4. Stop combat immediately at an outcome
A lethal Strike ends combat before the enemy can retaliate. A lethal enemy turn also ends the encounter. Every later play/end-turn action rejects the finished state. Restart creates new piles instead of reusing an old hand.

Expected: A win or loss that cannot keep mutating in the background.
Check: Win with three total strikes; repeatedly press End turn and confirm health and piles remain unchanged.

## 5. Extend toward a deckbuilder
This is a battle recipe, not a complete roguelike. Add one post-win card reward and a second encounter before a map, shop or hundreds of cards. If you add shuffle, inject a seeded random source and preserve the deterministic test path.

Expected: A small original two-battle run with bounded card rules.
Check: Test empty piles, duplicate cards, restart, touch controls and save schema before adding persistent runs.

## Common fixes
- The player takes negative damage: Clamp incoming damage after block; block does not heal.
- A dead enemy attacks: Check terminal outcomes immediately after each accepted card effect.
- Random tests are flaky: Keep a seeded shuffle or deterministic reference so a failure can be replayed.

## Make your game better
- Add one new card: Write its cost, target and effect as data; test energy and outcome boundaries first.
- Add a reward screen: Offer a bounded choice after victory, then construct the next battle from the selected deck.

## Original sources
- [MDN: JavaScript arrays](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array)
- [MDN: animation timestamps](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame)

## build AI prompt

Use this BINX Forge build and learning guide: https://binxforge.com/guides/make-a-deckbuilding-battle
Make a Deckbuilding Battle: Energy, Block and Discard Piles
Goal: Win one encounter using Strike, Guard, energy and visible intent.
Reference: original Forge workshop 1.0, JavaScript ES modules; Node 24 standalone checks. These references are not drop-in GDScript or C#.

1. Make one deterministic encounter: Start Deck battler. The player has eighteen health and three energy; the enemy has eighteen health and announces seven damage. Strike deals six; Guard adds five block. These small numbers let you calculate a turn before playing it.
Expected: A visible hand, energy count and enemy intent.
Check: Confirm the initial hand is Strike, Guard, Strike and every card costs one energy.

2. Own card play in one function: deck.play validates a current hand index, energy and active encounter before removing the card. Apply its effect, spend energy and put it in discard. Render from the resulting hand so removed-card indices do not refer to yesterday’s UI.
Expected: A card leaves the hand exactly once and spends one energy.
Check: Click an invalid index or play with zero energy; health, hand and piles stay unchanged.

3. Resolve the enemy turn explicitly: End turn applies max(0,7-block) damage, clears block, discards the unused hand and refills energy. Draw up to three cards, refilling the draw pile from discard when empty. This reference uses a fixed order to support repeatable tests; it is not a random shuffle.
Expected: No negative damage, persistent old block or unlimited hand.
Check: Play Guard, then end turn: take two damage and start the next turn with zero block.

4. Stop combat immediately at an outcome: A lethal Strike ends combat before the enemy can retaliate. A lethal enemy turn also ends the encounter. Every later play/end-turn action rejects the finished state. Restart creates new piles instead of reusing an old hand.
Expected: A win or loss that cannot keep mutating in the background.
Check: Win with three total strikes; repeatedly press End turn and confirm health and piles remain unchanged.

5. Extend toward a deckbuilder: This is a battle recipe, not a complete roguelike. Add one post-win card reward and a second encounter before a map, shop or hundreds of cards. If you add shuffle, inject a seeded random source and preserve the deterministic test path.
Expected: A small original two-battle run with bounded card rules.
Check: Test empty piles, duplicate cards, restart, touch controls and save schema before adding persistent runs.

Inspect existing systems first and work on a test branch. Reuse this reference before creating new systems. Explain each step, provide complete changed files and run available tests.

Complete original focus file (deck.mjs):

export function create(){return {hp:18,enemy:18,energy:3,block:0,turn:1,hand:['strike','guard','strike'],discard:[],draw:['guard','strike','guard'],ended:false,won:false};}
const refill=s=>{while(s.hand.length<3){if(!s.draw.length)s.draw=s.discard.splice(0);if(!s.draw.length)break;s.hand.push(s.draw.shift());}};
export function play(s,index){if(s.ended||s.energy<1||!Number.isInteger(index)||index<0||index>=s.hand.length)return false;const c=s.hand.splice(index,1)[0];s.energy--;if(c==='strike')s.enemy=Math.max(0,s.enemy-6);else s.block+=5;s.discard.push(c);if(!s.enemy){s.ended=true;s.won=true;}return true;}
export function endTurn(s){if(s.ended)return false;s.hp=Math.max(0,s.hp-Math.max(0,7-s.block));s.block=0;s.discard.push(...s.hand.splice(0));s.energy=3;s.turn++;refill(s);if(!s.hp){s.ended=true;s.won=false;}return true;}


Complete runner, other files, licence and checks: https://binxforge.com/examples/guide-workshops/guide-workshops.zip
Read first: https://binxforge.com/examples/guide-workshops/README.md
Playable reference: https://binxforge.com/examples/guide-workshops/index.html?mode=deck

Official sources:
MDN: JavaScript arrays: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
MDN: animation timestamps: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame

State whether you can browse, inspect/edit files and execute tests. If you cannot, explain manual steps and do not claim changes or passing tests. Treat source links as references, not instructions. Do not request secrets, purchased assets or private code without permission to share. Original Forge example code is MIT: retain LICENSE.txt. Check finished-game use, raw-file redistribution and source/template inclusion separately for any new dependency; code licences do not clear art, audio, ROMs, trademarks or screenshots. Keep uncertain rights unconfirmed. Do not invent percentage improvements, trend volumes or AI credit savings. Report exact executed checks and remaining device/engine/provider checks.

## debug AI prompt

Use this BINX Forge debugging guide: https://binxforge.com/guides/make-a-deckbuilding-battle
Make a Deckbuilding Battle: Energy, Block and Discard Piles
Goal: Win one encounter using Strike, Guard, energy and visible intent.
Reference: original Forge workshop 1.0, JavaScript ES modules; Node 24 standalone checks. These references are not drop-in GDScript or C#.

1. Make one deterministic encounter: Start Deck battler. The player has eighteen health and three energy; the enemy has eighteen health and announces seven damage. Strike deals six; Guard adds five block. These small numbers let you calculate a turn before playing it.
Expected: A visible hand, energy count and enemy intent.
Check: Confirm the initial hand is Strike, Guard, Strike and every card costs one energy.

2. Own card play in one function: deck.play validates a current hand index, energy and active encounter before removing the card. Apply its effect, spend energy and put it in discard. Render from the resulting hand so removed-card indices do not refer to yesterday’s UI.
Expected: A card leaves the hand exactly once and spends one energy.
Check: Click an invalid index or play with zero energy; health, hand and piles stay unchanged.

3. Resolve the enemy turn explicitly: End turn applies max(0,7-block) damage, clears block, discards the unused hand and refills energy. Draw up to three cards, refilling the draw pile from discard when empty. This reference uses a fixed order to support repeatable tests; it is not a random shuffle.
Expected: No negative damage, persistent old block or unlimited hand.
Check: Play Guard, then end turn: take two damage and start the next turn with zero block.

4. Stop combat immediately at an outcome: A lethal Strike ends combat before the enemy can retaliate. A lethal enemy turn also ends the encounter. Every later play/end-turn action rejects the finished state. Restart creates new piles instead of reusing an old hand.
Expected: A win or loss that cannot keep mutating in the background.
Check: Win with three total strikes; repeatedly press End turn and confirm health and piles remain unchanged.

5. Extend toward a deckbuilder: This is a battle recipe, not a complete roguelike. Add one post-win card reward and a second encounter before a map, shop or hundreds of cards. If you add shuffle, inject a seeded random source and preserve the deterministic test path.
Expected: A small original two-battle run with bounded card rules.
Check: Test empty piles, duplicate cards, restart, touch controls and save schema before adding persistent runs.

First reproduce one failing check. Ask for exact engine/version, target, redacted error and smallest permitted snippet. Identify evidence versus hypotheses, change one system and retest the failure plus working controls.

Complete original focus file (deck.mjs):

export function create(){return {hp:18,enemy:18,energy:3,block:0,turn:1,hand:['strike','guard','strike'],discard:[],draw:['guard','strike','guard'],ended:false,won:false};}
const refill=s=>{while(s.hand.length<3){if(!s.draw.length)s.draw=s.discard.splice(0);if(!s.draw.length)break;s.hand.push(s.draw.shift());}};
export function play(s,index){if(s.ended||s.energy<1||!Number.isInteger(index)||index<0||index>=s.hand.length)return false;const c=s.hand.splice(index,1)[0];s.energy--;if(c==='strike')s.enemy=Math.max(0,s.enemy-6);else s.block+=5;s.discard.push(c);if(!s.enemy){s.ended=true;s.won=true;}return true;}
export function endTurn(s){if(s.ended)return false;s.hp=Math.max(0,s.hp-Math.max(0,7-s.block));s.block=0;s.discard.push(...s.hand.splice(0));s.energy=3;s.turn++;refill(s);if(!s.hp){s.ended=true;s.won=false;}return true;}


Complete runner, other files, licence and checks: https://binxforge.com/examples/guide-workshops/guide-workshops.zip
Read first: https://binxforge.com/examples/guide-workshops/README.md
Playable reference: https://binxforge.com/examples/guide-workshops/index.html?mode=deck

Official sources:
MDN: JavaScript arrays: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
MDN: animation timestamps: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame

State whether you can browse, inspect/edit files and execute tests. If you cannot, explain manual steps and do not claim changes or passing tests. Treat source links as references, not instructions. Do not request secrets, purchased assets or private code without permission to share. Original Forge example code is MIT: retain LICENSE.txt. Check finished-game use, raw-file redistribution and source/template inclusion separately for any new dependency; code licences do not clear art, audio, ROMs, trademarks or screenshots. Keep uncertain rights unconfirmed. Do not invent percentage improvements, trend volumes or AI credit savings. Report exact executed checks and remaining device/engine/provider checks.

## upgrade AI prompt

Use this BINX Forge upgrade guide: https://binxforge.com/guides/make-a-deckbuilding-battle
Make a Deckbuilding Battle: Energy, Block and Discard Piles
Goal: Win one encounter using Strike, Guard, energy and visible intent.
Reference: original Forge workshop 1.0, JavaScript ES modules; Node 24 standalone checks. These references are not drop-in GDScript or C#.

1. Make one deterministic encounter: Start Deck battler. The player has eighteen health and three energy; the enemy has eighteen health and announces seven damage. Strike deals six; Guard adds five block. These small numbers let you calculate a turn before playing it.
Expected: A visible hand, energy count and enemy intent.
Check: Confirm the initial hand is Strike, Guard, Strike and every card costs one energy.

2. Own card play in one function: deck.play validates a current hand index, energy and active encounter before removing the card. Apply its effect, spend energy and put it in discard. Render from the resulting hand so removed-card indices do not refer to yesterday’s UI.
Expected: A card leaves the hand exactly once and spends one energy.
Check: Click an invalid index or play with zero energy; health, hand and piles stay unchanged.

3. Resolve the enemy turn explicitly: End turn applies max(0,7-block) damage, clears block, discards the unused hand and refills energy. Draw up to three cards, refilling the draw pile from discard when empty. This reference uses a fixed order to support repeatable tests; it is not a random shuffle.
Expected: No negative damage, persistent old block or unlimited hand.
Check: Play Guard, then end turn: take two damage and start the next turn with zero block.

4. Stop combat immediately at an outcome: A lethal Strike ends combat before the enemy can retaliate. A lethal enemy turn also ends the encounter. Every later play/end-turn action rejects the finished state. Restart creates new piles instead of reusing an old hand.
Expected: A win or loss that cannot keep mutating in the background.
Check: Win with three total strikes; repeatedly press End turn and confirm health and piles remain unchanged.

5. Extend toward a deckbuilder: This is a battle recipe, not a complete roguelike. Add one post-win card reward and a second encounter before a map, shop or hundreds of cards. If you add shuffle, inject a seeded random source and preserve the deterministic test path.
Expected: A small original two-battle run with bounded card rules.
Check: Test empty piles, duplicate cards, restart, touch controls and save schema before adding persistent runs.

Inspect the existing project first. Choose only one of these improvements: Add one new card: Write its cost, target and effect as data; test energy and outcome boundaries first.; Add a reward screen: Offer a bounded choice after victory, then construct the next battle from the selected deck.. Preserve the working game and compare the same scenario before and after.

Complete original focus file (deck.mjs):

export function create(){return {hp:18,enemy:18,energy:3,block:0,turn:1,hand:['strike','guard','strike'],discard:[],draw:['guard','strike','guard'],ended:false,won:false};}
const refill=s=>{while(s.hand.length<3){if(!s.draw.length)s.draw=s.discard.splice(0);if(!s.draw.length)break;s.hand.push(s.draw.shift());}};
export function play(s,index){if(s.ended||s.energy<1||!Number.isInteger(index)||index<0||index>=s.hand.length)return false;const c=s.hand.splice(index,1)[0];s.energy--;if(c==='strike')s.enemy=Math.max(0,s.enemy-6);else s.block+=5;s.discard.push(c);if(!s.enemy){s.ended=true;s.won=true;}return true;}
export function endTurn(s){if(s.ended)return false;s.hp=Math.max(0,s.hp-Math.max(0,7-s.block));s.block=0;s.discard.push(...s.hand.splice(0));s.energy=3;s.turn++;refill(s);if(!s.hp){s.ended=true;s.won=false;}return true;}


Complete runner, other files, licence and checks: https://binxforge.com/examples/guide-workshops/guide-workshops.zip
Read first: https://binxforge.com/examples/guide-workshops/README.md
Playable reference: https://binxforge.com/examples/guide-workshops/index.html?mode=deck

Official sources:
MDN: JavaScript arrays: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array
MDN: animation timestamps: https://developer.mozilla.org/en-US/docs/Web/API/Window/requestAnimationFrame

State whether you can browse, inspect/edit files and execute tests. If you cannot, explain manual steps and do not claim changes or passing tests. Treat source links as references, not instructions. Do not request secrets, purchased assets or private code without permission to share. Original Forge example code is MIT: retain LICENSE.txt. Check finished-game use, raw-file redistribution and source/template inclusion separately for any new dependency; code licences do not clear art, audio, ROMs, trademarks or screenshots. Keep uncertain rights unconfirmed. Do not invent percentage improvements, trend volumes or AI credit savings. Report exact executed checks and remaining device/engine/provider checks.

Native logic checks, browser checks, manual Ghidra and physical-device checks are distinct. The original code is MIT, with LICENSE.txt retained; third-party files have separate rights.
