// Copyright (c) 2025-2026, RTE (https://www.rte-france.com) // This Source Code Form is subject to the terms of the Mozilla Public License, version 2.0. // If a copy of the Mozilla Public License, version 2.0 was not distributed with this file, // you can obtain one at http://mozilla.org/MPL/2.0/. // SPDX-License-Identifier: MPL-2.0 // This file is part of Co-Study4Grid a Power Grid Study tool Assistant Interface to help solve contigencies for a grid state under study. import type { GameSessionConfig, GameSessionLog, GameStudyResult } from './types'; export const GAME_LOG_SCHEMA_VERSION = '1.0'; /** Assemble the final, serializable session log from recorded study results. */ export function buildSessionLog( config: GameSessionConfig, studies: GameStudyResult[], startedAt: string, endedAt: string, ): GameSessionLog { return { schemaVersion: GAME_LOG_SCHEMA_VERSION, sessionName: config.sessionName, player: config.player, startedAt, endedAt, config: { timerSeconds: config.timerSeconds, maxActions: config.maxActions, nStudies: config.studies.length, }, studies, }; } /** * Flatten the session log to CSV — one row per study. This is the * human-friendly companion to the canonical JSON; the Codabench scorer * consumes the JSON, but operators can eyeball the CSV in a spreadsheet. */ export function buildSessionCsv(log: GameSessionLog): string { const header = [ 'session', 'player', 'study_index', 'study_id', 'label', 'contingency', 'duration_ms', 'time_limit_s', 'timed_out', 'num_actions', 'max_actions', 'baseline_max_rho', 'final_max_rho', 'solved', 'chosen_action_ids', ]; const rows = log.studies.map((s, i) => [ log.sessionName, log.player ?? '', i + 1, s.studyId, s.label, s.contingencyElementId, s.durationMs, s.timeLimitSeconds, s.timedOut, s.numActions, s.maxActions, fmt(s.baselineMaxRho), fmt(s.finalMaxRho), s.solved, s.actionsChosen.map((a) => a.actionId).join(' | '), ] .map(csvCell) .join(','), ); return [header.join(','), ...rows].join('\n'); } function fmt(v: number | null): string { return v == null ? '' : String(v); } function csvCell(v: unknown): string { const s = String(v); return /[",\n]/.test(s) ? `"${s.replace(/"/g, '""')}"` : s; } /** Trigger a browser download of `content` as `filename`. */ export function downloadFile(filename: string, content: string, mime: string): void { const blob = new Blob([content], { type: mime }); const url = URL.createObjectURL(blob); const a = document.createElement('a'); a.href = url; a.download = filename; document.body.appendChild(a); a.click(); document.body.removeChild(a); URL.revokeObjectURL(url); } /** Filesystem-safe slug for a session name. */ export function slugifySession(name: string): string { return name.trim().toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '') || 'session'; }