Initial commit
This commit is contained in:
Vendored
+64
@@ -0,0 +1,64 @@
|
||||
/**
|
||||
* Command-line interface for running the interactive fiction game
|
||||
*/
|
||||
export declare class GameRunner {
|
||||
private engine;
|
||||
private llmProvider;
|
||||
private rl;
|
||||
private gameContext;
|
||||
private gameHistory;
|
||||
private suggestedCommands;
|
||||
constructor();
|
||||
/**
|
||||
* Initialize the game
|
||||
*/
|
||||
initialize(worldPath: string): Promise<void>;
|
||||
/**
|
||||
* Start the game in CLI mode
|
||||
*/
|
||||
start(): Promise<void>;
|
||||
/**
|
||||
* The main game loop for CLI mode
|
||||
*/
|
||||
private gameLoop;
|
||||
/**
|
||||
* Process a player command and return the narrative response
|
||||
* Used by both CLI and web interfaces
|
||||
*/
|
||||
processCommand(input: string): Promise<string>;
|
||||
/**
|
||||
* End the game
|
||||
*/
|
||||
end(): void;
|
||||
/**
|
||||
* Update the game context with new narrative
|
||||
*/
|
||||
private updateGameContext;
|
||||
/**
|
||||
* Get the current game state
|
||||
* Used by web interface
|
||||
*/
|
||||
getGameState(): {
|
||||
world: import("../interfaces/world-model").WorldModel;
|
||||
currentRoomId: string;
|
||||
inventory: string[];
|
||||
visitedRooms: string[];
|
||||
flags: Record<string, boolean>;
|
||||
counters: Record<string, number>;
|
||||
};
|
||||
/**
|
||||
* Get the current room description
|
||||
* Used by web interface
|
||||
*/
|
||||
getCurrentRoomDescription(): string;
|
||||
/**
|
||||
* Get suggested actions for the current game state
|
||||
* Used by web interface
|
||||
*/
|
||||
getSuggestions(): string[];
|
||||
/**
|
||||
* Load a saved game state
|
||||
* Used by web interface
|
||||
*/
|
||||
loadGameState(savedState: any): void;
|
||||
}
|
||||
Vendored
+262
@@ -0,0 +1,262 @@
|
||||
"use strict";
|
||||
/**
|
||||
* Command-line interface for running the interactive fiction game
|
||||
*/
|
||||
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
var desc = Object.getOwnPropertyDescriptor(m, k);
|
||||
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
||||
desc = { enumerable: true, get: function() { return m[k]; } };
|
||||
}
|
||||
Object.defineProperty(o, k2, desc);
|
||||
}) : (function(o, m, k, k2) {
|
||||
if (k2 === undefined) k2 = k;
|
||||
o[k2] = m[k];
|
||||
}));
|
||||
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
||||
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
||||
}) : function(o, v) {
|
||||
o["default"] = v;
|
||||
});
|
||||
var __importStar = (this && this.__importStar) || (function () {
|
||||
var ownKeys = function(o) {
|
||||
ownKeys = Object.getOwnPropertyNames || function (o) {
|
||||
var ar = [];
|
||||
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
||||
return ar;
|
||||
};
|
||||
return ownKeys(o);
|
||||
};
|
||||
return function (mod) {
|
||||
if (mod && mod.__esModule) return mod;
|
||||
var result = {};
|
||||
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
||||
__setModuleDefault(result, mod);
|
||||
return result;
|
||||
};
|
||||
})();
|
||||
Object.defineProperty(exports, "__esModule", { value: true });
|
||||
exports.GameRunner = void 0;
|
||||
const readline = __importStar(require("readline"));
|
||||
const path = __importStar(require("path"));
|
||||
const dotenv = __importStar(require("dotenv"));
|
||||
const game_engine_1 = require("../engine/game-engine");
|
||||
const openrouter_provider_1 = require("../llm/openrouter-provider");
|
||||
// Load environment variables
|
||||
dotenv.config();
|
||||
class GameRunner {
|
||||
constructor() {
|
||||
this.rl = null;
|
||||
this.gameContext = '';
|
||||
this.gameHistory = [];
|
||||
this.suggestedCommands = [];
|
||||
this.engine = new game_engine_1.TextAdventureEngine();
|
||||
this.llmProvider = new openrouter_provider_1.OpenRouterProvider();
|
||||
}
|
||||
/**
|
||||
* Initialize the game
|
||||
*/
|
||||
async initialize(worldPath) {
|
||||
console.log('Initializing game...');
|
||||
// Initialize LLM provider
|
||||
const apiKey = process.env.OPENROUTER_API_KEY;
|
||||
const model = process.env.OPENROUTER_MODEL;
|
||||
if (!apiKey || !model) {
|
||||
throw new Error('Missing required environment variables: OPENROUTER_API_KEY and/or OPENROUTER_MODEL');
|
||||
}
|
||||
await this.llmProvider.initialize({
|
||||
apiKey,
|
||||
model,
|
||||
temperature: 0.7,
|
||||
maxTokens: 800
|
||||
});
|
||||
// Load the world
|
||||
const resolvedPath = path.resolve(worldPath);
|
||||
console.log(`Loading world from ${resolvedPath}...`);
|
||||
await this.engine.loadWorld(resolvedPath);
|
||||
console.log('Game initialized successfully!');
|
||||
}
|
||||
/**
|
||||
* Start the game in CLI mode
|
||||
*/
|
||||
async start() {
|
||||
// Create readline interface for CLI mode
|
||||
this.rl = readline.createInterface({
|
||||
input: process.stdin,
|
||||
output: process.stdout
|
||||
});
|
||||
try {
|
||||
// Display introduction
|
||||
const introText = await this.engine.start();
|
||||
console.log('\n' + introText + '\n');
|
||||
// Look at initial room
|
||||
const initialLook = this.engine.processAction({ action: 'look', confidence: 1 });
|
||||
// Generate narrative description
|
||||
const narrativeRequest = {
|
||||
action: 'look',
|
||||
result: initialLook.message,
|
||||
roomDescription: this.engine.getCurrentRoomDescription(),
|
||||
visibleObjects: this.engine.getVisibleObjects(),
|
||||
visibleCharacters: this.engine.getVisibleCharacters(),
|
||||
tone: 'descriptive'
|
||||
};
|
||||
const narrative = await this.llmProvider.generateNarrative(narrativeRequest);
|
||||
console.log('\n' + narrative.text + '\n');
|
||||
// Store suggestions if available
|
||||
if (narrative.suggestions && narrative.suggestions.length > 0) {
|
||||
this.suggestedCommands = narrative.suggestions;
|
||||
}
|
||||
// Update game context
|
||||
this.updateGameContext(narrative.text);
|
||||
// Start the game loop
|
||||
this.gameLoop();
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error starting game:', error);
|
||||
this.end();
|
||||
}
|
||||
}
|
||||
/**
|
||||
* The main game loop for CLI mode
|
||||
*/
|
||||
gameLoop() {
|
||||
if (!this.rl)
|
||||
return;
|
||||
this.rl.question('> ', async (input) => {
|
||||
if (input.toLowerCase() === 'quit' || input.toLowerCase() === 'exit') {
|
||||
this.end();
|
||||
return;
|
||||
}
|
||||
const response = await this.processCommand(input);
|
||||
console.log('\n' + response + '\n');
|
||||
// Continue the game loop
|
||||
this.gameLoop();
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Process a player command and return the narrative response
|
||||
* Used by both CLI and web interfaces
|
||||
*/
|
||||
async processCommand(input) {
|
||||
try {
|
||||
// Process player input
|
||||
const actionRequest = {
|
||||
playerInput: input,
|
||||
currentRoom: this.engine.getWorldModel().rooms[this.engine.getCurrentState().currentRoomId].name,
|
||||
visibleObjects: this.engine.getVisibleObjects().map(id => this.engine.getWorldModel().objects[id].name),
|
||||
visibleCharacters: this.engine.getVisibleCharacters().map(id => this.engine.getWorldModel().characters[id].name),
|
||||
possibleActions: this.engine.getAvailableActions(),
|
||||
inventory: this.engine.getCurrentState().inventory.map(id => this.engine.getWorldModel().objects[id].name),
|
||||
gameContext: this.gameContext
|
||||
};
|
||||
if (this.rl) {
|
||||
console.log('Thinking...');
|
||||
}
|
||||
// Translate player input to action
|
||||
const action = await this.llmProvider.translateAction(actionRequest);
|
||||
// Process the action in the game engine
|
||||
const actionResult = this.engine.processAction(action);
|
||||
// If state changed, update it
|
||||
if (actionResult.stateChanged && actionResult.newState) {
|
||||
this.engine.getCurrentState().currentRoomId = actionResult.newState.currentRoomId;
|
||||
this.engine.getCurrentState().inventory = actionResult.newState.inventory;
|
||||
this.engine.getCurrentState().visitedRooms = actionResult.newState.visitedRooms;
|
||||
this.engine.getCurrentState().flags = actionResult.newState.flags;
|
||||
this.engine.getCurrentState().counters = actionResult.newState.counters;
|
||||
}
|
||||
// Generate narrative description
|
||||
const narrativeRequest = {
|
||||
action: `${action.action}${action.object ? ' ' + action.object : ''}${action.target ? ' on ' + action.target : ''}`,
|
||||
result: actionResult.message,
|
||||
roomDescription: this.engine.getCurrentRoomDescription(),
|
||||
visibleObjects: this.engine.getVisibleObjects().map(id => this.engine.getWorldModel().objects[id].name),
|
||||
visibleCharacters: this.engine.getVisibleCharacters().map(id => this.engine.getWorldModel().characters[id].name),
|
||||
previousContext: this.gameHistory.slice(-3).join('\n'),
|
||||
tone: 'descriptive'
|
||||
};
|
||||
const narrative = await this.llmProvider.generateNarrative(narrativeRequest);
|
||||
// Store suggestions if available
|
||||
if (narrative.suggestions && narrative.suggestions.length > 0) {
|
||||
this.suggestedCommands = narrative.suggestions;
|
||||
}
|
||||
// Update game context with the new narrative
|
||||
this.updateGameContext(narrative.text);
|
||||
// Return the narrative text
|
||||
return narrative.text;
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Error processing input:', error);
|
||||
return 'Something went wrong. Please try again.';
|
||||
}
|
||||
}
|
||||
/**
|
||||
* End the game
|
||||
*/
|
||||
end() {
|
||||
console.log('\nThanks for playing!');
|
||||
if (this.rl) {
|
||||
this.rl.close();
|
||||
this.rl = null;
|
||||
}
|
||||
this.engine.end();
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Update the game context with new narrative
|
||||
*/
|
||||
updateGameContext(narrative) {
|
||||
// Add to history
|
||||
this.gameHistory.push(narrative);
|
||||
// Keep history limited to last 10 entries
|
||||
if (this.gameHistory.length > 10) {
|
||||
this.gameHistory.shift();
|
||||
}
|
||||
// Update current context (last 5 entries)
|
||||
this.gameContext = this.gameHistory.slice(-5).join('\n');
|
||||
}
|
||||
/**
|
||||
* Get the current game state
|
||||
* Used by web interface
|
||||
*/
|
||||
getGameState() {
|
||||
return {
|
||||
world: this.engine.getWorldModel(),
|
||||
currentRoomId: this.engine.getCurrentState().currentRoomId,
|
||||
inventory: this.engine.getCurrentState().inventory,
|
||||
visitedRooms: this.engine.getCurrentState().visitedRooms,
|
||||
flags: this.engine.getCurrentState().flags,
|
||||
counters: this.engine.getCurrentState().counters
|
||||
};
|
||||
}
|
||||
/**
|
||||
* Get the current room description
|
||||
* Used by web interface
|
||||
*/
|
||||
getCurrentRoomDescription() {
|
||||
const roomId = this.engine.getCurrentState().currentRoomId;
|
||||
return this.engine.getWorldModel().rooms[roomId].description;
|
||||
}
|
||||
/**
|
||||
* Get suggested actions for the current game state
|
||||
* Used by web interface
|
||||
*/
|
||||
getSuggestions() {
|
||||
return this.suggestedCommands;
|
||||
}
|
||||
/**
|
||||
* Load a saved game state
|
||||
* Used by web interface
|
||||
*/
|
||||
loadGameState(savedState) {
|
||||
// Set the current state to match the saved state
|
||||
this.engine.getCurrentState().currentRoomId = savedState.currentRoomId;
|
||||
this.engine.getCurrentState().inventory = savedState.inventory;
|
||||
this.engine.getCurrentState().visitedRooms = savedState.visitedRooms;
|
||||
this.engine.getCurrentState().flags = savedState.flags;
|
||||
this.engine.getCurrentState().counters = savedState.counters;
|
||||
}
|
||||
}
|
||||
exports.GameRunner = GameRunner;
|
||||
//# sourceMappingURL=game-runner.js.map
|
||||
Vendored
+1
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user