A Deno library for easily building CHaser servers used in the U-16 programming contest. It adopts immutable state management and has an extensible design.
- Pure function state management
- Immutable data structures
- Type-safe design (TypeScript)
- Modularized action processing
To add to your Deno project, include the following in your dependencies:
// deps.ts
export * from "https://deno.land/x/chaser_engine@VERSION/mod.ts";
Replace VERSION
with your desired version number.
import {
createBlockCell,
createFloorCell,
createGame,
createItemCell,
createMap,
createPlayer,
} from "./deps.ts";
// マップの作成
const cells = [
[
createBlockCell(),
createBlockCell(),
createBlockCell(),
createBlockCell(),
createBlockCell(),
],
[
createBlockCell(),
createFloorCell(),
createFloorCell(),
createItemCell(),
createBlockCell(),
],
[
createBlockCell(),
createFloorCell(),
createBlockCell(),
createFloorCell(),
createBlockCell(),
],
[
createBlockCell(),
createItemCell(),
createFloorCell(),
createFloorCell(),
createBlockCell(),
],
[
createBlockCell(),
createBlockCell(),
createBlockCell(),
createBlockCell(),
createBlockCell(),
],
];
const map = createMap({ name: "sample-map", cells });
// プレイヤーの作成
const player1 = createPlayer({ name: "Player 1" });
const player2 = createPlayer({ name: "Player 2" });
// プレイヤーの初期位置
const playerPositions = {
[player1.id]: { x: 1, y: 1 },
[player2.id]: { x: 3, y: 3 },
};
// ゲームの作成
const game = createGame({
map,
players: [player1, player2],
playerPositions,
});
import { Commands, createAction } from "./deps.ts";
// Player 1 moves toward the item
const action1 = createAction({
actor: player1,
command: Commands.WalkRight,
});
// Execute the action
const nextGameState = game.dispatch(action1);
// Check current state
console.log(nextGameState.currentState.score); // Check scores
console.log(nextGameState.currentState.playerPositions); // Check player positions
There is a server implementation example in examples/legacy_server.ts
that supports the traditional CHaser protocol. You can use this as a reference to build your own server.
// Simple server implementation example
import { createBlockCell, createFloorCell, createItemCell, createMap, createPlayer, Game } from "../mod.ts";
import { LegacyServer } from "./legacy_server.ts";
// Create a sample map
const map = createSampleMap();
// Create players
const player1 = createPlayer({ name: "Player1" });
const player2 = createPlayer({ name: "Player2" });
// Set initial positions
const playerPositions = {
[player1.id]: { x: 1, y: 1 },
[player2.id]: { x: 8, y: 8 },
};
// Create game
const game = new Game({
map,
players: [player1, player2],
playerPositions,
maxTurn: 1000,
});
// Create and start the server
const server = new LegacyServer({
game,
coolPort: 2009,
hotPort: 2010,
});
await server.listen();
For more detailed examples, check the examples
directory.
The following actions are available in CHaser:
walk
- Move in the specified directionlook
- Look in the specified directionsearch
- Search in the specified directionput
- Place a block in the specified direction
Each action has directions (up, down, left, right), specified like Commands.WalkUp
.
To add your own action processing, implement a new processor and register it with the processorSelector:
// Custom command definition
const CustomCommands = {
TeleportUp: "tu",
TeleportRight: "tr",
TeleportDown: "td",
TeleportLeft: "tl",
} as const;
// Implement your processor
function teleportProcessor(state: State, action: Action): State {
// Custom processing logic here
// ...
return nextState(state, { /* updated state properties */ });
}
// Register in processor_selector.ts:
// 1. Add to command processors
const commandProcessors: Record<Command | CustomCommand, Processor> = {
// existing processors
[CustomCommands.TeleportUp]: teleportProcessor,
[CustomCommands.TeleportRight]: teleportProcessor,
[CustomCommands.TeleportDown]: teleportProcessor,
[CustomCommands.TeleportLeft]: teleportProcessor,
};
// 2. Add to action type detection
export function actionType(action: Action): ActionType | "teleport" {
if (isCustomCommand(action.command)) {
return "teleport";
}
// existing logic
}
// 3. Register in processors by type
const processorsByType: Record<string, Processor> = {
"walk": walkProcessor,
"look": lookProcessor,
"search": searchProcessor,
"put": putProcessor,
"teleport": teleportProcessor,
};
For a complete example, see examples/custom_processor.ts
.
Game state changes can be monitored in two ways:
- When using the Game class directly:
// Subscribe to the game's state iterator
for await (const players of game) {
console.log("New turn started with active players:", players);
console.log("Current state:", game.currentState);
}
- When using the LegacyServer:
// Subscribe to server state events
server.stateEvent.on((state) => {
console.log("Game state updated:", state);
// Check scores
for (const player of state.players) {
console.log(`${player.name}: ${state.score[player.id]} points`);
}
// Check if game is finished
if (state.isFinish) {
console.log("Game finished!");
}
});
See examples/server_example.ts
for a complete implementation.
- Fork this repository
- Create a feature branch (
git checkout -b feature/amazing-feature
) - Commit your changes (
git commit -m 'Add some amazing feature'
) - Push to the branch (
git push origin feature/amazing-feature
) - Create a Pull Request
This project is released under the Apache License 2.0. See the LICENSE file for details.