AI_Diplomacy/diplomacy/daide/responses.ts

184 lines
10 KiB
TypeScript

// diplomacy/daide/responses.ts
import { Map as DiplomacyMap } from '../engine/map'; // Placeholder
import {
StringTs, PowerTs, ProvinceTs, TurnTs, UnitTs,
add_parentheses_ts, strip_parentheses_ts, parse_string_ts, AbstractClauseTs
} from './clauses';
import * as daideTokens from './tokens'; // Import all for token instances
import { Token } from './tokens';
import { daideBytesToString } from './utils';
// import { OrderSplitter } from '../utils/splitter'; // Placeholder
// Logger
const logger = {
debug: (message: string) => console.debug(message),
info: (message: string) => console.info(message),
warn: (message: string) => console.warn(message),
error: (message: string, error?: any) => console.error(message, error),
};
// Base DaideResponse class
// Note: This is for server-generated DAIDE responses. It's different from the client-side parsing of generic responses.
export abstract class DaideResponse {
protected _bytes: Uint8Array = new Uint8Array(0);
// If these responses need to link back to a request_id for client-side matching,
// it would typically be part of the wrapper message (e.g. DaideDiplomacyMessage content),
// not this low-level DAIDE command string.
// For now, keeping it simple as per Python structure which focuses on byte generation.
constructor() {}
toBytes(): Uint8Array {
return this._bytes;
}
toString(): string {
return daideBytesToString(this._bytes);
}
protected concatBytes(byteArrays: Uint8Array[]): Uint8Array {
let totalLength = 0;
for (const arr of byteArrays) {
totalLength += arr.length;
}
const result = new Uint8Array(totalLength);
let offset = 0;
for (const arr of byteArrays) {
result.set(arr, offset);
offset += arr.length;
}
return result;
}
}
// Specific DAIDE Response Classes
export class MapNameResponseTs extends DaideResponse {
constructor(map_name: string) {
super();
const mapNameClause = parse_string_ts(StringTs, map_name, 'raise');
if (!mapNameClause) throw new Error("Failed to parse map name for MapNameResponse");
this._bytes = this.concatBytes([
daideTokens.MAP.toBytes(),
mapNameClause.toBytes() // StringTs toBytes already includes parentheses
]);
}
}
export class MapDefinitionResponseTs extends DaideResponse {
constructor(map_name: string, gameMapInstance?: DiplomacyMap) { // gameMapInstance for actual map data
super();
// const game_map = gameMapInstance || new DiplomacyMap(map_name); // Use instance or load
// For placeholder, we can't fully implement _build_..._clause methods without map data.
// This will be a simplified version.
const powersClause = this._build_powers_clause_stub();
const provincesClause = this._build_provinces_clause_stub();
const adjacenciesClause = this._build_adjacencies_clause_stub();
this._bytes = this.concatBytes([
daideTokens.MDF.toBytes(),
powersClause,
provincesClause,
adjacenciesClause
]);
}
private _build_powers_clause_stub(): Uint8Array {
// Simplified: ( (AUS) (ENG) ... )
const powerTokens = [daideTokens.AUS, daideTokens.ENG, daideTokens.FRA, daideTokens.GER, daideTokens.ITA, daideTokens.RUS, daideTokens.TUR];
const inner = this.concatBytes(powerTokens.map(pt => add_parentheses_ts(pt.toBytes())));
return add_parentheses_ts(inner);
}
private _build_provinces_clause_stub(): Uint8Array {
logger.warn("MapDefinitionResponseTs._build_provinces_clause_stub is a placeholder.");
return add_parentheses_ts(new Uint8Array(0)); // ( () () )
}
private _build_adjacencies_clause_stub(): Uint8Array {
logger.warn("MapDefinitionResponseTs._build_adjacencies_clause_stub is a placeholder.");
return add_parentheses_ts(new Uint8Array(0)); // ( () )
}
// Full implementation of _build_powers_clause, _build_provinces_clause, _build_adjacencies_clause
// would require a detailed DiplomacyMap placeholder and its data.
}
export class HelloResponseTs extends DaideResponse {
constructor(power_name: string, passcode: number, level: number, deadline: number, rules: string[]) {
super();
const powerClause = parse_string_ts(PowerTs, power_name, 'raise');
const passcodeToken = new Token({ from_int: passcode });
if (!powerClause) throw new Error("Failed to parse power name for HelloResponse");
let variantsBytes: Uint8Array[] = [];
variantsBytes.push(add_parentheses_ts(this.concatBytes([daideTokens.LVL.toBytes(), new Token({ from_int: level }).toBytes()])));
if (deadline > 0) {
const deadlineTokenBytes = new Token({ from_int: deadline }).toBytes();
variantsBytes.push(add_parentheses_ts(this.concatBytes([daideTokens.MTL.toBytes(), deadlineTokenBytes])));
variantsBytes.push(add_parentheses_ts(this.concatBytes([daideTokens.RTL.toBytes(), deadlineTokenBytes])));
variantsBytes.push(add_parentheses_ts(this.concatBytes([daideTokens.BTL.toBytes(), deadlineTokenBytes])));
}
if (rules.includes('NO_CHECK')) { // Example rule check
variantsBytes.push(add_parentheses_ts(daideTokens.AOA.toBytes()));
}
// Add other rules like PDA, NPR, NPB, PTL based on 'rules' array and DAIDE spec for LVL 10
if (rules.includes('PARTIAL_DRAWS_ALLOWED')) { // Made up rule name for example
variantsBytes.push(add_parentheses_ts(daideTokens.PDA.toBytes()));
}
// ... etc. for NPR, NPB, PTL if applicable based on level and rules
const allVariantsBytes = add_parentheses_ts(this.concatBytes(variantsBytes));
this._bytes = this.concatBytes([
daideTokens.HLO.toBytes(),
add_parentheses_ts(powerClause.toBytes()),
add_parentheses_ts(passcodeToken.toBytes()),
allVariantsBytes // This is already parenthesized in python: add_parentheses(bytes(variants))
// The `variants` string in python is already (LVL...)(MTL...). So one more pair of parens.
]);
}
}
// ... Other response classes will follow a similar pattern ...
// SupplyCenterResponse, CurrentPositionResponse, ThanksResponse, MissingOrdersResponse,
// OrderResultResponse, TimeToDeadlineResponse, AcceptResponse, RejectResponse,
// NotResponse, PowerInCivilDisorderResponse, PowerIsEliminatedResponse,
// TurnOffResponse, ParenthesisErrorResponse, SyntaxErrorResponse
// Stubs for remaining for brevity
export class SupplyCenterResponseTs extends DaideResponse { constructor(powers_centers: Record<string,string[]>, map_name: string){super(); logger.warn("SupplyCenterResponseTs not fully implemented")} }
export class CurrentPositionResponseTs extends DaideResponse { constructor(phase_name: string, powers_units: Record<string,string[]>, powers_retreats: Record<string,Record<string,string[]>>){super(); logger.warn("CurrentPositionResponseTs not fully implemented")} }
export class ThanksResponseTs extends DaideResponse { constructor(order_bytes: Uint8Array, results: number[]){super(); logger.warn("ThanksResponseTs not fully implemented")} }
export class MissingOrdersResponseTs extends DaideResponse { constructor(phase_name: string, power: any /*Power placeholder*/){super(); logger.warn("MissingOrdersResponseTs not fully implemented")} }
export class OrderResultResponseTs extends DaideResponse { constructor(phase_name: string, order_bytes: Uint8Array, results: number[]){super(); logger.warn("OrderResultResponseTs not fully implemented")} }
export class TimeToDeadlineResponseTs extends DaideResponse { constructor(seconds: number){super(); this._bytes = this.concatBytes([daideTokens.TME.toBytes(), add_parentheses_ts(new Token({from_int: seconds}).toBytes())]); } }
export class AcceptResponseTs extends DaideResponse { constructor(request_bytes: Uint8Array){super(); this._bytes = this.concatBytes([daideTokens.YES.toBytes(), add_parentheses_ts(request_bytes)]);} }
export class RejectResponseTs extends DaideResponse { constructor(request_bytes: Uint8Array){super(); this._bytes = this.concatBytes([daideTokens.REJ.toBytes(), add_parentheses_ts(request_bytes)]);} }
export class NotResponseTs extends DaideResponse { constructor(response_bytes: Uint8Array){super(); this._bytes = this.concatBytes([daideTokens.NOT.toBytes(), add_parentheses_ts(response_bytes)]);} }
export class PowerInCivilDisorderResponseTs extends DaideResponse { constructor(power_name: string){super(); const p = parse_string_ts(PowerTs, power_name, 'raise'); if(!p) throw new Error("Invalid power"); this._bytes = this.concatBytes([daideTokens.CCD.toBytes(), add_parentheses_ts(p.toBytes())]);} }
export class PowerIsEliminatedResponseTs extends DaideResponse { constructor(power_name: string){super(); const p = parse_string_ts(PowerTs, power_name, 'raise'); if(!p) throw new Error("Invalid power"); this._bytes = this.concatBytes([daideTokens.OUT.toBytes(), add_parentheses_ts(p.toBytes())]);} }
export class TurnOffResponseTs extends DaideResponse { constructor(){super(); this._bytes = daideTokens.OFF.toBytes();} }
export class ParenthesisErrorResponseTs extends DaideResponse { constructor(request_bytes: Uint8Array){super(); this._bytes = this.concatBytes([daideTokens.PRN.toBytes(), add_parentheses_ts(request_bytes)]);} }
export class SyntaxErrorResponseTs extends DaideResponse { constructor(request_bytes: Uint8Array, error_index: number){super(); const errTokenBytes = daideTokens.ERR.toBytes(); const msgWithErr = new Uint8Array(request_bytes.length + errTokenBytes.length); msgWithErr.set(request_bytes.slice(0, error_index)); msgWithErr.set(errTokenBytes, error_index); msgWithErr.set(request_bytes.slice(error_index), error_index + errTokenBytes.length); this._bytes = this.concatBytes([daideTokens.HUH.toBytes(), add_parentheses_ts(msgWithErr)]);} }
// Aliases
export const MAP = MapNameResponseTs;
export const MDF = MapDefinitionResponseTs;
export const HLO = HelloResponseTs;
export const SCO = SupplyCenterResponseTs;
export const NOW = CurrentPositionResponseTs;
export const THX = ThanksResponseTs;
export const MIS = MissingOrdersResponseTs;
export const ORD = OrderResultResponseTs;
export const TME = TimeToDeadlineResponseTs;
export const YES = AcceptResponseTs;
export const REJ = RejectResponseTs;
export const NOT = NotResponseTs;
export const CCD = PowerInCivilDisorderResponseTs;
export const OUT = PowerIsEliminatedResponseTs;
export const OFF = TurnOffResponseTs;
export const PRN = ParenthesisErrorResponseTs;
export const HUH = SyntaxErrorResponseTs;