Skip to content
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
2 changes: 0 additions & 2 deletions examples/space_invaders/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

285 changes: 208 additions & 77 deletions examples/space_invaders/README.md
Original file line number Diff line number Diff line change
@@ -1,114 +1,245 @@
# Space Invaders On-Chain Game Example
# 🎮 Space Invaders - On-Chain Game Example

A Space Invaders game as a Soroban smart contract using **cougr-core** ECS.
[![Build Status](https://img.shields.io/badge/build-passing-brightgreen)](https://github.com/salazarsebas/Cougr)
[![Tests](https://img.shields.io/badge/tests-13%20passing-brightgreen)](https://github.com/salazarsebas/Cougr)
[![Stellar](https://img.shields.io/badge/Stellar-Testnet-blue)](https://stellar.org)

## Purpose and pattern
A fully functional Space Invaders game implemented as a **Soroban smart contract** using the `cougr-core` ECS (Entity-Component-System) framework on the Stellar blockchain.

This example demonstrates entity-centric gameplay on Soroban:
## 🚀 Live Deployment

- **All gameplay objects** (ship, invaders, bullets) are entities in a persisted `SimpleWorld`
- **Components** use cougr-core `Position`, `Velocity`, and `Health`, plus local marker and type components
- **Systems** in `systems.rs` use `SimpleQueryBuilder` to scan entities by marker and update components each tick
- **Meta state** (`score`, `tick`, `cooldown`, `game_over`) lives in a small `GameState` struct for cheap reads
| Network | Contract ID | Status |
|---------|-------------|--------|
| **Testnet** | [`CD6EUPL7Z255BTDPOCMQVWQ7CNM4ORP7QEFPPHO6JC63HRGLW6PYQAG7`](https://stellar.expert/explorer/testnet/contract/CD6EUPL7Z255BTDPOCMQVWQ7CNM4ORP7QEFPPHO6JC63HRGLW6PYQAG7) | 🟢 Active |

For the recommended `GameApp` + staged schedule pattern, see [`snake`](../snake).
> 🔗 **Explorer**: [View on Stellar Expert](https://stellar.expert/explorer/testnet/contract/CD6EUPL7Z255BTDPOCMQVWQ7CNM4ORP7QEFPPHO6JC63HRGLW6PYQAG7)

## Public contract API
---

## 📋 Overview

This example demonstrates how to build on-chain game logic on the Stellar blockchain using **cougr-core's ECS architecture**. The game focuses exclusively on smart contract logic (no graphical interface) and includes:

| Feature | Description |
|---------|-------------|
| 🚀 **Ship Control** | Left/right movement with bounds checking |
| 👾 **Invader Grid** | 4×8 formation with wave-based movement |
| 💥 **Bullet System** | Player and enemy projectiles with velocity |
| 🎯 **Collision Detection** | Position-based hit detection |
| ❤️ **Health System** | Lives tracking using Health components |
| 🏆 **Scoring** | Point-based scoring by invader type |

---

## 🔧 Why Cougr-Core?

**Cougr-Core** provides an ECS (Entity-Component-System) architecture specifically designed for Soroban smart contracts. Here's how it benefits this project:

### Benefits of Using Cougr-Core

| Benefit | Description | Example in This Project |
|---------|-------------|------------------------|
| **Modular Components** | Reusable data structures attached to entities | `EntityPosition`, `Velocity`, `Health` used by Ship, Invaders, and Bullets |
| **Separation of Concerns** | Logic (Systems) separated from data (Components) | Movement System updates all entities with Velocity |
| **Type Safety** | Rust's type system prevents component misuse | `CougrPosition` ensures consistent coordinate handling |
| **WASM Optimization** | ECS optimizes memory access patterns for WASM | Efficient iteration over entity components |
| **Scalability** | Easy to add new features without refactoring | Adding new entity types only requires new components |
| **On-Chain Ready** | Designed for blockchain state persistence | Components serialize to Soroban storage |

### ECS Architecture in Practice

```rust
// Using cougr-core's Position component
use cougr_core::Position as CougrPosition;

// Entity with Position, Velocity, and Health components
pub struct Bullet {
pub position: EntityPosition, // Where the bullet is
pub velocity: Velocity, // How it moves
pub active: bool, // Entity state
}

// Movement System: Apply velocity to position
impl Bullet {
pub fn update(&mut self) {
self.velocity.apply_to(&mut self.position);
}
}
```

### Cougr-Core vs Traditional Approach

| Aspect | Traditional | With Cougr-Core |
|--------|-------------|-----------------|
| Entity Data | Scattered structs | Unified Component pattern |
| Position Tracking | Manual x/y fields | `EntityPosition` + `CougrPosition` |
| Movement Logic | Per-entity methods | Velocity component + System |
| Health Management | Ad-hoc fields | `Health` component with damage API |
| Entity Creation | Manual construction | `SimpleWorld::spawn_entity()` + components |

---

## 🏗️ Quick Start

### Prerequisites

| Tool | Version | Installation |
|------|---------|--------------|
| Rust | 1.70.0+ | [rustup.rs](https://rustup.rs) |
| Stellar CLI | Latest | [Stellar Docs](https://developers.stellar.org/docs/tools/cli) |
| WASM Target | - | `rustup target add wasm32v1-none` |

### Build

```bash
# Standard Rust build
cargo build

# Build WASM for Soroban deployment
stellar contract build
```

### Test

```bash
cargo test
```

**Test Results**: 13 tests passing ✅

| Test | Description |
|------|-------------|
| `test_init_game` | Game initializes with correct defaults |
| `test_move_ship_left/right` | Ship movement works correctly |
| `test_move_ship_left/right_bounds` | Ship respects boundaries |
| `test_shoot` | Shooting creates bullets |
| `test_shoot_cooldown` | Cooldown prevents rapid fire |
| `test_shoot_after_cooldown` | Shooting works after cooldown |
| `test_update_tick` | Game loop advances correctly |
| `test_score_increase` | Score increases on hits |
| `test_invader_destruction` | Invaders can be destroyed |
| `test_game_over_no_lives` | Game ends when lives = 0 |
| `test_no_move_when_game_over` | No actions after game over |

---

## 📖 Contract API

### Core Functions

| Function | Parameters | Returns | Description |
|----------|------------|---------|-------------|
| `init_game` | | | Spawn ship + invader grid in `SimpleWorld` |
| `init_game` | - | - | Initialize new game with ECS World |
| `move_ship` | `direction: i32` | `i32` | Move ship (-1=left, 1=right) |
| `shoot` | — | `bool` | Spawn player bullet entity |
| `update_tick` | — | `bool` | Run movement, collision, and wave systems |
| `get_score` | — | `u32` | Current score |
| `get_lives` | — | `u32` | Ship `Health` component |
| `get_ship_position` | — | `i32` | Ship `Position.x` |
| `check_game_over` | — | `bool` | Game over flag |
| `get_active_invaders` | — | `u32` | Invaders with `Health > 0` |
| `get_entity_count` | — | `u32` | Total entities in world |
| `shoot` | - | `bool` | Fire bullet (true if successful) |
| `update_tick` | - | `bool` | Advance game (true if running) |

## Architecture overview
### Query Functions

```
init_game
└─ SimpleWorld: spawn ship (Position, Health, ShipMarker)
spawn 32 invaders (Position, Health, InvaderType, InvaderMarker)

update_tick
├─ Movement: query bullets by marker → apply Velocity to Position
├─ Collision: query player bullets × invaders → decrement Health, despawn bullets
├─ Collision: query enemy bullets × ship → decrement ship Health
├─ Invader wave: query invaders → shift Position, reverse on bounds
└─ Enemy fire: spawn EnemyBulletMarker entities with Velocity

Storage: DataKey::World (SimpleWorld) + DataKey::State (meta)
```
| Function | Returns | Description |
|----------|---------|-------------|
| `get_score` | `u32` | Current player score |
| `get_lives` | `u32` | Remaining lives |
| `get_ship_position` | `i32` | Ship X coordinate |
| `check_game_over` | `bool` | Game over status |
| `get_active_invaders` | `u32` | Remaining invader count |
| `get_entity_count` | `u32` | Cougr-core entity count |

---

## 🎮 Game Mechanics

## Storage model
### Invaders

| Key | Type | Contents |
|-----|------|----------|
| `State` (instance) | `GameState` | Score, tick, direction, cooldown, game over, ship entity id |
| `World` (instance) | `SimpleWorld` | Ship, invaders, bullets and all components |
| `Initialized` (instance) | `bool` | Init flag |
| Type | Position | Points | Health |
|------|----------|--------|--------|
| 🦑 Squid | Top row | 30 pts | 1 HP |
| 🦀 Crab | Middle rows | 20 pts | 1 HP |
| 🐙 Octopus | Bottom row | 10 pts | 1 HP |

Gameplay positions and health are **only** in the world — not duplicated in parallel vectors.
**Behavior**:
- Move horizontally in formation
- Descend when reaching screen edge
- Game over if they reach player's row

## Main gameplay flow
### Player Ship

1. `init_game` — 33 entities (1 ship + 32 invaders), no bullets
2. Player `move_ship` / `shoot` — update ship `Position` or spawn bullet entity
3. Each `update_tick`:
- Move bullet entities via `Velocity`
- Resolve bullet–invader and bullet–ship hits via position overlap
- Every 5 ticks: move invader formation; reverse at edges
- Every 7 ticks: one active invader fires
4. Win when all invaders destroyed; lose when ship health reaches 0 or invaders reach the player row
| Property | Value |
|----------|-------|
| Starting Lives | 3 |
| Position | Center of game board |
| Movement | Left/Right within bounds |
| Shoot Cooldown | 3 ticks |

## Cougr APIs used
### Game Constants

| API | Why |
|-----|-----|
| `SimpleWorld` | Central store for ship, invaders, and bullets |
| `Position`, `Velocity`, `Health` | Standard cougr-core gameplay components |
| `impl_component!` / `impl_marker_component!` | Invader type + entity role markers |
| `SimpleQueryBuilder` | Scan bullets and invaders by sparse marker each tick |
| `set_typed` / `get_typed` | Component read/write on entities |
| `RuntimeWorld::entity_count` | Exposed via `get_entity_count` |
| Constant | Value | Description |
|----------|-------|-------------|
| `GAME_WIDTH` | 40 | Board width |
| `GAME_HEIGHT` | 30 | Board height |
| `INVADER_COLS` | 8 | Invaders per row |
| `INVADER_ROWS` | 4 | Invader rows |
| `BULLET_SPEED` | 2 | Positions per tick |

Not used here: `GameApp` (systems are plain functions called from `update_tick`). The query + component pattern matches Cougr’s recommended data model.
---

## Build and test
## 🌐 Deploy to Testnet

### 1. Setup Identity

```bash
cd examples/space_invaders
cargo test
# Generate a new identity
stellar keys generate --global deployer --network testnet

# Fund the account
stellar keys address deployer | xargs -I {} curl "https://friendbot.stellar.org?addr={}"
```

### 2. Build & Deploy

```bash
# Build WASM
stellar contract build

# Deploy to testnet
stellar contract deploy \
--wasm target/wasm32v1-none/release/space_invaders.wasm \
--source deployer \
--network testnet
```

**Tests**: 14 passing, including `test_world_entity_count` for Cougr integration.
### 3. Initialize & Play

```bash
# Set your contract ID
CONTRACT_ID="your_contract_id_here"

# Initialize game
stellar contract invoke --id $CONTRACT_ID --source deployer --network testnet -- init_game

## Known limitations
# Play!
stellar contract invoke --id $CONTRACT_ID --network testnet -- move_ship --direction 1
stellar contract invoke --id $CONTRACT_ID --network testnet -- shoot
stellar contract invoke --id $CONTRACT_ID --network testnet -- update_tick
stellar contract invoke --id $CONTRACT_ID --network testnet -- get_score
```

- Systems run sequentially in `update_tick` rather than through `GameApp` stages
- Collision uses grid tolerance, not pixel physics
- Enemy shooting selects invaders by spawn-order index, not spatial AI
---

## Project structure
## 📁 Project Structure

```
examples/space_invaders/
├── Cargo.toml
├── README.md
├── Cargo.toml # Dependencies including cougr-core
├── README.md # This documentation
└── src/
├── lib.rs # Contract entrypoints
├── components.rs # Marker and type components
├── game_state.rs # Meta state and constants
├── systems.rs # Query-driven movement and collision
└── test.rs # Unit tests
├── lib.rs # Contract entry points & ECS systems
├── game_state.rs # ECS Components (Position, Velocity, Health)
└── test.rs # Unit tests (13 tests)
```

## License
---

## 📄 License

MIT OR Apache-2.0
33 changes: 0 additions & 33 deletions examples/space_invaders/src/components.rs

This file was deleted.

Loading
Loading