Files
ai.interactive.fiction/dist/cli/game-runner.js
2025-04-01 08:37:41 +02:00

262 lines
10 KiB
JavaScript

"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