Skip to content
Draft
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
43 changes: 43 additions & 0 deletions .github/workflows/flying_ninja.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
name: flying_ninja

on:
push:
branches: [master]
pull_request:
paths:
- motoko/flying_ninja/**
- rust/flying_ninja/**
- .github/workflows/flying_ninja.yml

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
motoko-flying_ninja:
runs-on: ubuntu-24.04
container: ghcr.io/dfinity/icp-dev-env-motoko:0.3.2
env:
ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Deploy and test
working-directory: motoko/flying_ninja
run: |
icp network start -d
icp deploy
make test

rust-flying_ninja:
runs-on: ubuntu-24.04
container: ghcr.io/dfinity/icp-dev-env-rust:0.3.2
env:
ICP_CLI_GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
steps:
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
- name: Deploy and test
working-directory: rust/flying_ninja
run: |
icp network start -d
icp deploy
make test
20 changes: 0 additions & 20 deletions motoko/flying_ninja/.devcontainer/devcontainer.json

This file was deleted.

26 changes: 0 additions & 26 deletions motoko/flying_ninja/BUILD.md

This file was deleted.

28 changes: 28 additions & 0 deletions motoko/flying_ninja/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
.PHONY: test

test:
@echo "=== Test 1: isHighScore returns true when leaderboard is empty ==="
@result=$$(icp canister call backend isHighScore '(42)') && \
echo "$$result" && \
echo "$$result" | grep -q 'true' && \
echo "PASS" || (echo "FAIL" && exit 1)

@echo "=== Test 2: addLeaderboardEntry returns entry in leaderboard ==="
@result=$$(icp canister call backend addLeaderboardEntry '("Alice", 100)') && \
echo "$$result" && \
echo "$$result" | grep -q '"Alice"' && \
echo "$$result" | grep -q '100' && \
echo "PASS" || (echo "FAIL" && exit 1)

@echo "=== Test 3: getLeaderboard returns persisted entry ==="
@result=$$(icp canister call backend getLeaderboard '()') && \
echo "$$result" && \
echo "$$result" | grep -q '"Alice"' && \
echo "$$result" | grep -q '100' && \
echo "PASS" || (echo "FAIL" && exit 1)

@echo "=== Test 4: getRandomness returns a blob ==="
@result=$$(icp canister call backend getRandomness '()') && \
echo "$$result" && \
echo "$$result" | grep -q 'blob' && \
echo "PASS" || (echo "FAIL" && exit 1)
39 changes: 10 additions & 29 deletions motoko/flying_ninja/README.md
Original file line number Diff line number Diff line change
@@ -1,53 +1,34 @@
# Flying Ninja

[View this sample's code on GitHub](https://github.com/dfinity/examples/tree/master/motoko/flying_ninja)

## Overview

Flying Ninja is a 2D side-scroller game where players interact with the flying ninja character using their keyboard's space bar to move up and down. The goal is to avoid the obstacles and obtain points for each obstacle you dodge. When the game ends, the user can add their score to the leaderboard.

## Deploying from ICP Ninja

This example can be deployed directly from [ICP Ninja](https://icp.ninja), a browser-based IDE for ICP. To continue developing locally after deploying from ICP Ninja, see [BUILD.md](BUILD.md).

[![Open in ICP Ninja](https://icp.ninja/assets/open.svg)](https://icp.ninja/i?g=https://github.com/dfinity/examples/motoko/flying_ninja)

> **Note:** ICP Ninja currently uses `dfx` under the hood, which is why this example includes a `dfx.json` configuration file. `dfx` is the legacy CLI, being superseded by [icp-cli](https://cli.internetcomputer.org), which is what developers should use for local development.
Flying Ninja is a 2D side-scroller game where players control a ninja character using the space bar to move up and down, dodging obstacles to earn points. When the game ends, players can submit their score to an on-chain leaderboard backed by a Motoko canister on ICP.

## Build and deploy from the command line

### Prerequisites

- [x] Install [Node.js](https://nodejs.org/en/download/)
- [x] Install [icp-cli](https://cli.internetcomputer.org): `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm`
- Node.js
- icp-cli: `npm install -g @icp-sdk/icp-cli @icp-sdk/ic-wasm`

### Install

Clone the example project:

```bash
git clone https://github.com/dfinity/examples
cd examples/motoko/flying_ninja
```

### Deployment

Start the local network:
### Deploy and test

```bash
icp network start -d
```

Deploy the canisters:

```bash
icp deploy
make test
icp network stop
```

Stop the local network when done:
For frontend development with hot reload:

```bash
icp network stop
npm run dev
```

## Updating the Candid interface
Expand All @@ -57,9 +38,9 @@ The `backend/backend.did` file defines the backend canister's public interface.
If you modify the backend's public API, regenerate the `.did` file:

```bash
$(mops toolchain bin moc) --idl $(mops sources) -o backend/backend.did backend/app.mo
$(mops toolchain bin moc) --idl -o backend/backend.did backend/app.mo
```

## Security considerations and best practices

If you base your application on this example, it is recommended that you familiarize yourself with and adhere to the [security best practices](https://internetcomputer.org/docs/building-apps/security/overview) for developing on ICP. This example may not implement all the best practices.
Refer to the [security best practices](https://docs.internetcomputer.org/guides/security/overview) for information on security and best practices for your ICP app.
2 changes: 1 addition & 1 deletion motoko/flying_ninja/backend/app.mo
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Array "mo:core/Array";
import Nat "mo:core/Nat";
import Random "mo:core/Random";

persistent actor FlyingNinja {
actor FlyingNinja {
type Order = { #less; #equal; #greater };
type LeaderboardEntry = {
name : Text;
Expand Down
23 changes: 0 additions & 23 deletions motoko/flying_ninja/dfx.json

This file was deleted.

8 changes: 3 additions & 5 deletions motoko/flying_ninja/frontend/src/actor.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,12 @@ import { createActor } from "./bindings/backend";
// via Set-Cookie header (see vite.config.js).
const canisterEnv = safeGetCanisterEnv();

// Resolve canister ID: cookie (icp-cli + dev server) → env var (dfx build-time)
const canisterId =
canisterEnv?.["PUBLIC_CANISTER_ID:backend"] ??
process.env.CANISTER_ID_BACKEND;
// Resolve canister ID from cookie (icp-cli + dev server)
const canisterId = canisterEnv?.["PUBLIC_CANISTER_ID:backend"];

if (!canisterId) {
throw new Error(
"Canister ID for 'backend' not found. Run 'icp deploy' or 'dfx deploy' first."
"Canister ID for 'backend' not found. Run 'icp deploy' first."
);
}

Expand Down
43 changes: 3 additions & 40 deletions motoko/flying_ninja/frontend/vite.config.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { defineConfig, loadEnv } from "vite";
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import { execSync } from "child_process";
import { icpBindgen } from "@icp-sdk/bindgen/plugins/vite";
Expand Down Expand Up @@ -28,44 +28,12 @@ function getDevServerConfig() {
};
} catch {}

// Try dfx
try {
const pingResult = JSON.parse(
execSync("dfx ping", { encoding: "utf-8", stdio: "pipe" })
);
const rootKeyHex = Buffer.from(pingResult.root_key).toString("hex");
const canisterId = execSync("dfx canister id backend", {
encoding: "utf-8",
stdio: "pipe",
}).trim();
return {
headers: {
"Set-Cookie": `ic_env=${encodeURIComponent(
`ic_root_key=${rootKeyHex}&PUBLIC_CANISTER_ID:backend=${canisterId}`
)}; SameSite=Lax;`,
},
proxy: {
"/api": {
target: "http://127.0.0.1:4943",
changeOrigin: true,
},
},
host: "127.0.0.1",
};
} catch {}

throw new Error(
"No local network running. Start with:\n icp network start -d && icp deploy\nor:\n dfx start --background && dfx deploy"
"No local network running. Start with:\n icp network start -d && icp deploy"
);
}

export default defineConfig(({ command, mode }) => {
// dfx generates ../.env with CANISTER_ID_* vars on deploy. Bake them into the
// bundle so actor.js can fall back to them when the ic_env cookie does not
// contain canister IDs (dfx does not inject PUBLIC_CANISTER_ID:* env vars
// into the asset canister, unlike icp-cli).
const env = loadEnv(mode, "..", ["CANISTER_"]);

export default defineConfig(({ command }) => {
return {
base: "./",
plugins: [
Expand All @@ -75,11 +43,6 @@ export default defineConfig(({ command, mode }) => {
outDir: "./src/bindings",
}),
],
define: {
"process.env.CANISTER_ID_BACKEND": JSON.stringify(
env.CANISTER_ID_BACKEND
),
},
optimizeDeps: {
esbuildOptions: { define: { global: "globalThis" } },
},
Expand Down
7 changes: 2 additions & 5 deletions motoko/flying_ninja/icp.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
canisters:
- name: backend
recipe:
type: "@dfinity/motoko@v4.1.0"
configuration:
main: backend/app.mo
candid: backend/backend.did
type: "@dfinity/motoko@v5.0.0"

- name: frontend
recipe:
type: "@dfinity/asset-canister@v2.1.0"
type: "@dfinity/asset-canister@v2.2.1"
configuration:
dir: frontend/dist
build:
Expand Down
10 changes: 7 additions & 3 deletions motoko/flying_ninja/mops.toml
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
# Motoko dependencies (https://mops.one/)

[toolchain]
moc = "1.5.1"
moc = "1.9.0"

[dependencies]
core = "2.4.0"
core = "2.5.0"

[moc]
# M0236: use context dot notation (e.g. map.get(k) instead of Map.get(map, compare, k))
# M0237: redundant explicit implicit arguments (e.g. Nat.compare is inferred automatically)
# M0223: redundant type instantiation (e.g. Array.tabulate instead of Array.tabulate<T>)
args = ["-W", "M0236", "-W", "M0237", "-W", "M0223"]
args = ["--default-persistent-actors", "-W=M0236,M0237,M0223"]

[canisters.backend]
main = "backend/app.mo"
candid = "backend/backend.did"
20 changes: 0 additions & 20 deletions rust/flying_ninja/.devcontainer/devcontainer.json

This file was deleted.

26 changes: 0 additions & 26 deletions rust/flying_ninja/BUILD.md

This file was deleted.

Loading
Loading