{xDEFINE PERTURBADO}
{xDEFINE CHEQUEOMEM}
{xDEFINE ESTABILIZAR_FRAMEINICIAL}
{$DEFINE SPXMEJORCAMINO}
{xDEFINE chequeo_DCF}// Si está definido ajusteDCF, calcula el ajuste por
// dos métodos y compara resultados para ver que esten OK.
{x$DEFINE DUMP_TEXT_OPTRES}
unit usalasdejuegodistr;

{$IFDEF FPC}
{$MODE Delphi}
{$ENDIF}
interface

uses
{$IFDEF CHEQUEOMEM}
  udbgutil,
{$ENDIF}
  Classes, SysUtils,
  Math,
  uglobs,
  uconstantesSimSEE,
  xmatdefs,
 {$IFDEF SPXMEJORCAMINO}
  umipsimplex_mejorcamino,
 {$ELSE}
  umipsimplex,
 {$ENDIF}
  uActores, ucosa, uCosaConNombre,
  ufechas,
  uDemandas,
  uGeneradores, uGTer,
  uArcos,
  uNodos,
  uMercadoSpot,
  uEstados,
  uFuentesAleatorias,
  uodt_types,
  fddp,
{$IFNDEF FPC}
  shellapi,
{$ENDIF}
  upreprocesador,
  uEsclavizador,
  uEsclavizadorSobreMuestreado,
{$IFDEF WINDOWS}
  ipcthrd,
  {$IFDEF FPCLCL}
  LCLIntf, lcltype,
  {$ELSE}
  Windows,
  {$ENDIF}
{$ELSE}
  uEmuladorWinIPC,
{$ENDIF}
  uEsclavizadorSubMuestreado,
  winLinuxUtils,
  uAuxiliares;

type
  TDAOfTextFile = array of TextFile;

  TSalaDeJuego = class(TCosaConNombre)
  private
    //Variables para cálculo multi core
    oldEstrellaIni, oldEstrellaFin: integer;
    estrellaIniCalc, estrellaFinCalc: integer;  //rango de estrellas a calcular por el
    //hilo principal
    listaOptimizadores: TList;
    //Lista con los hilos TRobotOptimizadorMultiCore
    listaEventos: TList;                //Lista con los eventos por los que esperan
    //los hilos para continuar la ejecución
    //del próximo paso
    semCntCalculando: TMutex;           //semáforo para acceder al contador de
    //hilos calculando
{$IFDEF WINDOWS}
    evPasoCompleto: TEvent;
{$ELSE}
    evPasoCompleto: TEventRX;             //evento para notificar al hilo principal
{$ENDIF}
    //que se termino de calcular el frame del paso
    pasosAutomaticos: boolean;
    //Fin variables para cálculo multi core

    //Variables publicadas en la simulación
    costoDelPaso, CF_AlInicioDelPaso, CFaux_AlInicioDelPaso: NReal;
  public
    globs: TGlobs;

    // listas de todos los actores en el escenario
    listaActores: TListaDeCosasConNombre; {of TActor}
    // lista de todas las fuentes en el escenario
    listaFuentes: TListaDeCosasConNombre; {of IFuenteAleatoria}
    //Contiene las fuentes que hayan sido reemplazadas por algún esclavizador
    listaFuentesReemplazadas: TListaDeCosasConNombre;

    // Listas para soporte clasificado de los actores
    Nods: TListaDeCosasConNombre {of INodo};
    Arcs: TListaDeCosasConNombre {of IArco};
    Gens: TListaDeCosasConNombre {of TGenerador};
    Dems: TListaDeCosasConNombre {of IDemanda};
    ComercioInternacional: TListaDeCosasConNombre {of TComercioInternacional};

    // listas auxiliares para acelerar los barridos
    lst_barridoFijarEstadoDeActoresToEstrella: TList;
    lst_barridoFijarEstadoDeFuentesToEstrella: TList;
    lst_SorteosDelPaso_Actores: array of TActor;
    lst_opt_PrintResultados: TList;
    lst_Sim_cronicaIdInicio: TList;

    lst_actores_evolucionarEstado: TList;
    lst_fuentes_evolucionarEstado: TList;

    lst_Sim_Paso_Fin: TList;

    lst_NecesitoIterar: TList;

    termicos: array of TGter;
    actores: array of TActor;
    fuentes: array of TFuenteAleatoria;
    hidraulicos: array of TGeneradorHidraulico;

    // contadores de variables para el registro de variables por los actores
    // conjunto de variables asociadas con el planteo del Simplex.
    ivar: integer; // Contador de variables de optimización
    ires: integer; // Contador de restricciones
    ivae: integer; // Contador de variables Enteras en el problema de optimización

    // contadores de variables para el registro de variables por los actores
    // conjunto de variables asociadas con el espacio de estados.
    ivar_xr: integer; // Contador de variables de estado continuas
    ivar_xd: integer; // Contador de varialbes de estado discretas
    ivar_auxNReal: integer; // Contador de variables auxiliares de estado contínuas
    ivar_auxInt: integer; // Contador de variables auxiliares de estado discretas

    spx: TMIPSimplex;

    descripcion: string;
    archiSala: string; //El archivo de la sala de juego
    dirSala: string; // directorio del archiSala
    dirResultadosCorrida: string;
    //El directorio donde se van a salvar los resultados de
    //la corrida. Por defecto es uconstantes.getDir_bin

    estabilizarInicio: boolean;
    usarArchivoParaInicializarFrameInicial: boolean;
    //El archivo desde el que se va cargar los datos para inicializar el frame inicial
    archivoCF: string;

    escribirOptActores: boolean;

    //Los valores en los que se quiere inicializar las variables del CF archivoCF
    //que no esten en el CF de esta sala
    enganchesContinuos, enganchesDiscretos: TListaDeCosas;

    HayQueIterar_: boolean; // Variable auxiliar que determina si es necesario iterar
    // para una mejor resolución del paso de tiempo.

    //      modoImprimirPotenciasFirmes: boolean;
    RandSeedInicial: integer;

    nCores: integer;                    //cantidad de Nucleos en el equipo.
    //Por defecto es 1
    forzarNumeroDeHilos: boolean;
    nHilos: integer;
    //Número de hilos realizando los cálculos

    constructor Create(nombre: string;
      fechaIniSim, fechaFinSim, fechaIniOpt, fechaFinOpt: TFecha; DurPos: TDAOfNReal);
    constructor Create_ReadFromText(f: TArchiTexto); override;
    constructor cargarSala(archiSala: string; abortarEnError: boolean);
    procedure WriteToText(f: TArchiTexto); override;
    procedure Free; override;

    procedure PubliVars; override;
    //      function varsPSimRes3PorDefecto: TDAofString; override;

    procedure setDirCorrida(archivoSala: string);

    //Crea los arreglos de actores
    procedure Prepararse(TiempoHaciaAdelante: boolean);
    procedure PrepararseYPubliVars(lista: TListaDeCosasConNombre); overload;
    procedure PrepararseYPubliVars; overload;

    // Determina la dimensión del espacio de estado y los frames
    // de variables auxiliares que sea necesario crear.
    procedure optx_nvxs;

    // retorna la suma de variables de estado primero llama a optx_nvxs
    function ContarVariablesDeEstado: integer;
    function generarResumenTermicoPrimerasFichas: string;
    procedure Simular(archiCFAux: string);
    procedure Sim_Cronica_Inicio;

    procedure Actores_AcumularAuxs1;
    procedure Actores_SetAuxs1;

    procedure prepararSalaParaPaso;
    procedure SorteosDelPasoSim;
    procedure SorteosDelPasoOpt(sortear: boolean);

    // este método sellama luego de los sorteos del  paso
    // y antes de preparar los actores.
    procedure PrepararPaso_PostSorteo_Fuentes;

    // este método sellama luego de los sorteos del  paso y luego
    // de que la fuentes se prepararon.
    // Si hay iteraciones puede ocurrir que ese método se llame reiteradas veces.
    procedure PrepararPaso_PostSorteo_Actores;

    procedure PrepararPaso_PreSorteo;

    function ResolverPaso: NReal; // retorna el costo en USD

    procedure EvolucionarEstado;
    procedure Sim_Paso_Fin;
    procedure CapturarResultadosDelPaso;
    procedure Sim_Cronica_Fin;
    procedure FinSimulacion;

    //Inicializa la sala para optimizar.
    //Si costoFuturo es nil crea una matriz nueva para usar como costo futuro,
    //sino lo usa como propio
    procedure inicializarOptimizacion(const costoFuturo: TMatOfNReal);

    procedure asignarSemillasIniciales(semillaDelPaso: integer);
    procedure asignarEstadosFuentesMadreUniforme(glma: TDAofNInt;
      glinext, glinextp: integer);
    procedure calcularRangoEstrellas(estrellaIni, estrellaFin: integer);
    procedure calcularRangoEstrellasMultiCore(estrellaIni, estrellaFin: integer);
    procedure llenarRangoEstrellas(estrellaIni: integer;
      costosFuturos: TDAOfNReal; kpaso: integer);
    //si la sala esta en un paso mayor o igual que kpaso, copia los valores en
    //el costo futuro de kpaso, queda en el paso kpaso -1 y retorna 1
    //si la sala esta en un paso menor que kpaso no copia los valores,
    //queda en el paso que estaba y retorna 0
    function llenarRangosEstrellasYDarPaso(estrellasIni: TDAofNInt;
      costosFuturos: TDAOfDAOfNReal; kpaso: integer): integer;
    function getRangoEstrellas(estrellaIni, estrellaFin, paso: integer): TDAOfNReal;
    function irAPaso(nuevoPaso: integer): integer;
    procedure darPaso;
    procedure Optimizar;

    procedure inicializarArchisOptRes(var fsal: TextFile;
      var fsal_opt: TDAOfTextFile; var fsalopen: boolean);
    procedure escribirPasoOptRes(var fsal: TextFile; var fsal_opt: TDAOfTextFile);
    procedure cerrarArchisOptRes(var fsal: TextFile; var fsal_opt: TDAOfTextFile;
      var fsalopen: boolean);

    procedure printEncabezadoSimRes(var f: TextFile);
    procedure printFinalSimRes(var f: TextFile);

    procedure inicializarVariablesMultiCore(darPasoAuto: boolean);
    procedure liberarVariablesMultiCore;
    procedure asignarEstrellas(estrellaIni, estrellaFin: integer);
    procedure OptimizarMultiCore;
    procedure guardarResultadosOpt(dir: string);

{$IFDEF ESTABILIZAR_FRAMEINICIAL}
    procedure EstabilizarFrameInicial;
{$ENDIF}
    procedure FinOptimizacion;

    //Reordena la lista de fuentes para que si una fuente usa un valor de otra
    //entonces el valor de la otra se calcule antes
    procedure ordenarFuentes;

    procedure Armar_lst_BarridoFijarEstadoDeActoresYFuentesToEstrella;
    procedure Armar_lst_SorteosDelPaso_Actores;

    procedure Armar_lst_opt_PrintResultados;
    procedure Armar_lst_Sim_cronicaIdInicio;
    procedure armar_lst_Sim_Paso_Fin;

    procedure Armar_lst_EvolucionarEstado;

    procedure Armar_lst_necesitoIterar;
    function NecesitoIterar: boolean;

    //      procedure Armar_lst_Encadenamientos;
    procedure barridoFijarEstadoDeActoresYFuentesToEstrellaOpt;
    procedure barridoFijarEstadoGlobal;
    function getNombreVar(ivar: integer): string;
    function getNombreRes(ires: integer): string;
    procedure dump_Variables;
    procedure ImprimirPotenciasFirmes;
  end;

procedure AlInicio;
procedure AlFinal;

implementation

uses
  uRobotOptimizadorMultiCore;

constructor TSalaDeJuego.Create(nombre: string;
  fechaIniSim, fechaFinSim, fechaIniOpt, fechaFinOpt: TFecha; DurPos: TDAOfNReal);
begin
  inherited Create(nombre);
  Globs := TGlobs.Create('Globs', fechaIniSim, fechaFinSim, fechaIniOpt,
    fechaFinOpt, DurPos);
  Gens := TListaDeCosasConNombre.Create('Generadores');
  Dems := TListaDeCosasConNombre.Create('Demandas');
  Nods := TListaDeCosasConNombre.Create('Nodos');
  Arcs := TListaDeCosasConNombre.Create('Arcos');
  ComercioInternacional := TListaDeCosasConNombre.Create('Comercio Internacional');
  listaActores := TListaDeCosasConNombre.Create('Lista de Actores');
  listaFuentes := TListaDeCosasConNombre.Create('Fuentes Aleatorias');
  enganchesContinuos := TListaDeCosas.Create('Enganches continuos');
  enganchesDiscretos := TListaDeCosas.Create('Enganches discretos');
  spx := nil;
  dirResultadosCorrida := getDir_Run;
  estabilizarInicio := False;
  usarArchivoParaInicializarFrameInicial := False;
  archivoCF := '';
  listaFuentesReemplazadas := TListaDeCosasConNombre.Create('FuentesReemplazadas');
  self.nCores := winLinuxUtils.GetSystemCoreCount;
  self.nHilos := nCores;
  forzarNumeroDeHilos := False;
  self.pasosAutomaticos := True;
  self.escribirOptActores := False;
end;

constructor TSalaDeJuego.Create_ReadFromText(f: TArchiTexto);
var
  i: integer;
  durPosAux: TDAofNReal;
  funcsBasura: TListaDeCosasConNombre;
begin
  if f.Version < 4 then
  begin
    inherited Create_ReadFromText(f);
    f.IniciarLecturaRetrasada;
    f.rd('descripcion', descripcion);
    f.rd('globs', TCosa(globs));
    f.rd('gens', TCosa(gens));
    f.rd('dems', TCosa(dems));
    f.rd('nods', TCosa(nods));
    f.rd('arcs', TCosa(arcs));
    f.rd('spots', TCosa(ComercioInternacional));
    f.rd('listaFuentes', TCosa(listaFuentes));
    f.rd('funcs', TCosa(funcsBasura));
    f.rd('estabilizarInicio', estabilizarInicio);
    f.rd('usarArchivoParaInicializarFrameInicial',
      usarArchivoParaInicializarFrameInicial);
    //    f.rdArch('archivoCF', archivoCF);
    f.rd('enganchesContinuos', TCosa(enganchesContinuos));
    f.rd('enganchesDiscretos', TCosa(enganchesDiscretos));

    f.EjecutarLectura;
    spx := nil;

    if globs = nil then
    begin
      SetLength(durPosAux, 1);
      globs := TGlobs.Create('Globs', TFecha.Create_Dt(0), TFecha.Create_Dt(0),
        TFecha.Create_Dt(0), TFecha.Create_Dt(0), durPosAux);
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudieron leer las variables globales. Asignando valores por defecto.');
    end;
    if gens = nil then
    begin
      gens := TListaDeCosasConNombre.Create('gens');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de generadores. Asignando la lista vacía.');
    end;
    if nods = nil then
    begin
      nods := TListaDeCosasConNombre.Create('nods');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de nodos. Asignando la lista vacía.');
    end;
    if dems = nil then
    begin
      dems := TListaDeCosasConNombre.Create('dems');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de demandas. Asignando la lista vacía.');
    end;
    if Arcs = nil then
    begin
      Arcs := TListaDeCosasConNombre.Create('arcs');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de arcos. Asignando la lista vacía.');
    end;
    if ComercioInternacional = nil then
    begin
      ComercioInternacional := TListaDeCosasConNombre.Create('Comercio Internacional');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de spots de mercado. Asignando la lista vacía.');
    end;
    if listaFuentes = nil then
    begin
      listaFuentes := TListaDeCosasConNombre.Create('fuentes');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de fuentes. Asignando la lista vacía.');
    end;

    if enganchesContinuos = nil then
    begin
      enganchesContinuos := TListaDeCosas.Create('Enganches continuos');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de Enganches Continuos. Asignando la lista vacía.');
    end;
    if enganchesDiscretos = nil then
    begin
      enganchesDiscretos := TListaDeCosas.Create('Enganches discretos');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de Enganches Discretos. Asignando la lista vacía.');
    end;

    listaActores := TListaDeCosasConNombre.Create('Lista de Actores');
    listaActores.Capacity := gens.Count + dems.Count + nods.Count + arcs.Count;
    for i := 0 to nods.Count - 1 do
      listaActores.Add(TCosaConNombre(nods[i]));
    for i := 0 to dems.Count - 1 do
      listaActores.Add(TCosaConNombre(dems[i]));
    for i := 0 to gens.Count - 1 do
      listaActores.Add(TCosaConNombre(gens[i]));
    for i := 0 to arcs.Count - 1 do
      listaActores.Add(TCosaConNombre(arcs[i]));
    for i := 0 to ComercioInternacional.Count - 1 do
      listaActores.Add(TCosaConNombre(ComercioInternacional[i]));
    dirResultadosCorrida := getDir_Run;
    if funcsBasura <> nil then
      funcsBasura.Free;
    listaFuentesReemplazadas := TListaDeCosasConNombre.Create('FuentesReemplazadas');
  end
  else if f.Version < 9 then
  begin
    inherited Create_ReadFromText(f);
    f.IniciarLecturaRetrasada;
    f.rd('descripcion', descripcion);
    f.rd('globs', TCosa(globs));
    f.rd('gens', TCosa(gens));
    f.rd('dems', TCosa(dems));
    f.rd('nods', TCosa(nods));
    f.rd('arcs', TCosa(arcs));
    f.rd('ComercioInternacional', TCosa(ComercioInternacional));
    f.rd('listaFuentes', TCosa(listaFuentes));
    f.rd('funcs', TCosa(funcsBasura));
    f.rd('estabilizarInicio', estabilizarInicio);
    f.rd('usarArchivoParaInicializarFrameInicial',
      usarArchivoParaInicializarFrameInicial);
    //    f.rdArch('archivoCF', archivoCF);
    f.rd('enganchesContinuos', TCosa(enganchesContinuos));
    f.rd('enganchesDiscretos', TCosa(enganchesDiscretos));

    f.EjecutarLectura;
    spx := nil;

    if globs = nil then
    begin
      SetLength(durPosAux, 1);
      globs := TGlobs.Create('Globs', TFecha.Create_Dt(0), TFecha.Create_Dt(0),
        TFecha.Create_Dt(0), TFecha.Create_Dt(0), durPosAux);
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudieron leer las variables globales. Asignando valores por defecto.');
    end;
    if gens = nil then
    begin
      gens := TListaDeCosasConNombre.Create('gens');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de generadores. Asignando la lista vacía.');
    end;
    if nods = nil then
    begin
      nods := TListaDeCosasConNombre.Create('nods');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de nodos. Asignando la lista vacía.');
    end;
    if dems = nil then
    begin
      dems := TListaDeCosasConNombre.Create('dems');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de demandas. Asignando la lista vacía.');
    end;
    if Arcs = nil then
    begin
      Arcs := TListaDeCosasConNombre.Create('arcs');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de arcos. Asignando la lista vacía.');
    end;
    if ComercioInternacional = nil then
    begin
      ComercioInternacional := TListaDeCosasConNombre.Create('Comercio Internacional');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de spots de mercado. Asignando la lista vacía.');
    end;
    if listaFuentes = nil then
    begin
      listaFuentes := TListaDeCosasConNombre.Create('fuentes');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de fuentes. Asignando la lista vacía.');
    end;

    if enganchesContinuos = nil then
    begin
      enganchesContinuos := TListaDeCosas.Create('Enganches continuos');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de Enganches Continuos. Asignando la lista vacía.');
    end;
    if enganchesDiscretos = nil then
    begin
      enganchesDiscretos := TListaDeCosas.Create('Enganches discretos');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de Enganches Discretos. Asignando la lista vacía.');
    end;

    listaActores := TListaDeCosasConNombre.Create('Lista de Actores');
    listaActores.Capacity := gens.Count + dems.Count + nods.Count + arcs.Count;
    for i := 0 to nods.Count - 1 do
      listaActores.Add(TCosaConNombre(nods[i]));
    for i := 0 to dems.Count - 1 do
      listaActores.Add(TCosaConNombre(dems[i]));
    for i := 0 to gens.Count - 1 do
      listaActores.Add(TCosaConNombre(gens[i]));
    for i := 0 to arcs.Count - 1 do
      listaActores.Add(TCosaConNombre(arcs[i]));
    for i := 0 to ComercioInternacional.Count - 1 do
      listaActores.Add(TCosaConNombre(ComercioInternacional[i]));
    dirResultadosCorrida := getDir_Run;
    if funcsBasura <> nil then
      funcsBasura.Free;
    listaFuentesReemplazadas := TListaDeCosasConNombre.Create('FuentesReemplazadas');
  end
  else
  begin
    inherited Create_ReadFromText(f);
    f.IniciarLecturaRetrasada;
    f.rd('descripcion', descripcion);
    f.rd('globs', TCosa(globs));
    f.rd('gens', TCosa(gens));
    f.rd('dems', TCosa(dems));
    f.rd('nods', TCosa(nods));
    f.rd('arcs', TCosa(arcs));
    f.rd('ComercioInternacional', TCosa(ComercioInternacional));
    f.rd('listaFuentes', TCosa(listaFuentes));
    f.rd('estabilizarInicio', estabilizarInicio);
    f.rd('usarArchivoParaInicializarFrameInicial',
      usarArchivoParaInicializarFrameInicial);
    //    f.rdArch('archivoCF', archivoCF);
    f.rd('enganchesContinuos', TCosa(enganchesContinuos));
    f.rd('enganchesDiscretos', TCosa(enganchesDiscretos));

    f.EjecutarLectura;
    spx := nil;

    if globs = nil then
    begin
      SetLength(durPosAux, 1);
      globs := TGlobs.Create('Globs', TFecha.Create_Dt(0), TFecha.Create_Dt(0),
        TFecha.Create_Dt(0), TFecha.Create_Dt(0), durPosAux);
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudieron leer las variables globales. Asignando valores por defecto.');
    end;
    if gens = nil then
    begin
      gens := TListaDeCosasConNombre.Create('gens');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de generadores. Asignando la lista vacía.');
    end;
    if nods = nil then
    begin
      nods := TListaDeCosasConNombre.Create('nods');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de nodos. Asignando la lista vacía.');
    end;
    if dems = nil then
    begin
      dems := TListaDeCosasConNombre.Create('dems');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de demandas. Asignando la lista vacía.');
    end;
    if Arcs = nil then
    begin
      Arcs := TListaDeCosasConNombre.Create('arcs');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de arcos. Asignando la lista vacía.');
    end;
    if ComercioInternacional = nil then
    begin
      ComercioInternacional := TListaDeCosasConNombre.Create('Comercio Internacional');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de spots de mercado. Asignando la lista vacía.');
    end;
    if listaFuentes = nil then
    begin
      listaFuentes := TListaDeCosasConNombre.Create('fuentes');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de fuentes. Asignando la lista vacía.');
    end;

    if enganchesContinuos = nil then
    begin
      enganchesContinuos := TListaDeCosas.Create('Enganches continuos');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de Enganches Continuos. Asignando la lista vacía.');
    end;
    if enganchesDiscretos = nil then
    begin
      enganchesDiscretos := TListaDeCosas.Create('Enganches discretos');
      ucosa.procMsgErrorLectura(
        'Advertencia: no se pudo leer la lista de Enganches Discretos. Asignando la lista vacía.');
    end;

    listaActores := TListaDeCosasConNombre.Create('Lista de Actores');
    listaActores.Capacity := gens.Count + dems.Count + nods.Count + arcs.Count;
    for i := 0 to nods.Count - 1 do
      listaActores.Add(TCosaConNombre(nods[i]));
    for i := 0 to dems.Count - 1 do
      listaActores.Add(TCosaConNombre(dems[i]));
    for i := 0 to gens.Count - 1 do
      listaActores.Add(TCosaConNombre(gens[i]));
    for i := 0 to arcs.Count - 1 do
      listaActores.Add(TCosaConNombre(arcs[i]));
    for i := 0 to ComercioInternacional.Count - 1 do
      listaActores.Add(TCosaConNombre(ComercioInternacional[i]));
    dirResultadosCorrida := getDir_Run;
    listaFuentesReemplazadas := TListaDeCosasConNombre.Create('FuentesReemplazadas');
  end;
  self.archiSala := f.nombreArchivo;
  self.nCores := winLinuxUtils.GetSystemCoreCount;
  nHilos := nCores;
  forzarNumeroDeHilos := False;
  self.pasosAutomaticos := True;
  self.escribirOptActores := False;
end;

constructor TSalaDeJuego.cargarSala(archiSala: string; abortarEnError: boolean);
var
  f: TArchiTexto;
  aux: TListaDeCosasConNombre;
begin
  if FileExists(archiSala) then
  begin
    f := TArchiTexto.CreateForRead(archiSala, abortarEnError);
    aux := TListaDeCosasConNombre.Create('aux');
    try
      f.rd('sala', TCosa(self));
      f.Free;
      f := nil;

      //Resolver referencias
      if uCosaConNombre.referenciasSinResolver > 0 then
      begin
        aux.Add(globs);
        uCosaConNombre.resolver_referencias(aux);
        uCosaConNombre.resolver_referencias(listaActores);
        uCosaConNombre.resolver_referencias(listaFuentes);
        aux.FreeSinElemenentos;
        aux := nil;
      end;
      if uCosaConNombre.referenciasSinResolver > 0 then
      begin
        uCosaConNombre.DumpReferencias('errRefs.txt');
        raise Exception.Create(
          'TSalaDeJuego.cargarSala: Quedaron Referencias Sin Resolver Cargando la Sala. Puede Ver Que Referencias No Se Resolvieron En: '
          + 'errRefs.txt');
      end;
    except
      if aux <> nil then
        aux.Free;
      if f <> nil then
        f.Free;
      raise;
    end;
  end
  else
    raise Exception.Create('TSalaDeJuego.cargarSala: no se encuentra el archivo ' +
      archiSala);
end;

procedure TSalaDeJuego.WriteToText(f: TArchiTexto);
begin
  inherited WriteToText(f);
  f.wr('descripcion', descripcion);
  f.writeline('');
  f.wr('globs', globs);
  f.writeline('');
  f.wr('gens', gens);
  f.writeline('');
  f.wr('dems', dems);
  f.writeline('');
  f.wr('nods', nods);
  f.writeline('');
  f.wr('arcs', arcs);
  f.writeline('');
  f.wr('ComercioInternacional', ComercioInternacional);
  f.writeline('');
  f.wr('listaFuentes', listaFuentes);

  f.wr('estabilizarInicio', estabilizarInicio);
  f.wr('usarArchivoParaInicializarFrameInicial',
    usarArchivoParaInicializarFrameInicial);
  //  f.wrArch('archivoCF', quitarRaiz(archivoCF));
  f.wr('enganchesContinuos', TCosa(enganchesContinuos));
  f.wr('enganchesDiscretos', TCosa(enganchesDiscretos));
  self.archiSala := f.nombreArchivo;
end;

procedure TSalaDeJuego.Free;
begin
  Gens.Free;
  Dems.Free;
  Nods.Free;
  Arcs.Free;
  ComercioInternacional.Free;
  listaActores.FreeSinElemenentos;
  listaFuentes.Free;
  listaFuentesReemplazadas.FreeSinElemenentos;
  if lst_barridoFijarEstadoDeActoresToEstrella <> nil then
    lst_barridoFijarEstadoDeActoresToEstrella.Free;
  if lst_barridoFijarEstadoDeFuentesToEstrella <> nil then
    lst_barridoFijarEstadoDeFuentesToEstrella.Free;
  if lst_opt_PrintResultados <> nil then
    lst_opt_PrintResultados.Free;
  if lst_Sim_cronicaIdInicio <> nil then
    lst_Sim_cronicaIdInicio.Free;


  if lst_Sim_Paso_Fin <> nil then
    lst_Sim_Paso_Fin.Free;
  if spx <> nil then
    spx.Free;
  Globs.Free;
  Globs := nil;
  SetLength(actores, 0);
  SetLength(fuentes, 0);
  SetLength(hidraulicos, 0);
  SetLength(termicos, 0);

  if enganchesContinuos <> nil then
    enganchesContinuos.Free;
  if enganchesDiscretos <> nil then
    enganchesDiscretos.Free;
  inherited Free;
end;

procedure TSalaDeJuego.PubliVars;
begin
  inherited PubliVars;

  PublicarVariableNR('CF_AlInicioDelPaso', '[USD]', 12, 1, CF_AlInicioDelPaso);
  PublicarVariableNR('CFaux', '[USD]', 12, 1, CFaux_AlInicioDelPaso);
  PublicarVariableNR('CPSimplex', '[USD]', 12, 1, costoDelPaso);
end;

//function TSalaDeJuego.varsPSimRes3PorDefecto: TDAofString;
//var
//  res: TDAofString;
//begin
//  SetLength(res, 3);

//  res[0]:= 'CF_AlInicioDelPaso';
//  res[1]:= 'CFaux';
//  res[2]:= 'CPSimplex';

//  result:= res;
//end;

procedure TSalaDeJuego.setDirCorrida(archivoSala: string);
var
  i, j: integer;
  nombreArchivo: string;
  buscando: boolean;

begin
  j := length(archivoSala);
  i := j + 1;

  buscando := True;
  while buscando and (j > 0) do
  begin
    if archivoSala[j] = DirectorySeparator then
      buscando := False
    else
    begin
      // si encontramos un punto antes de la primer barra de directorio
      // anotamos su posición en i
      if (archivoSala[j] = '.') then
        i := j;
      Dec(j);
    end;
  end;

  if buscando then
  begin
    // archivoSala está en la raiz del disco.
    dirSala := '';
    j := 0;
  end
  else
  begin
    dirSala := copy(archivoSala, 1, j - 1) + DirectorySeparator;
  end;
  nombreArchivo := copy(archiSala, j + 1, i - j - 1);
  dirResultadosCorrida := getDir_Run + nombreArchivo + DirectorySeparator;
end;

procedure TSalaDeJuego.Prepararse(TiempoHaciaAdelante: boolean);
var
  acumr: NReal;
  k, j: integer;
  cntHidraulicos, cntTermicos: integer;
  fuente: TFuenteAleatoria;
begin
  //Preparar valores de las variables globales de la sala
  acumr := xmatdefs.vsum(globs.DurPos);
  globs.HorasDelPaso := round(acumr);
  if globs.EstadoDeLaSala = CES_OPTIMIZANDO then
    globs.nPasos := globs.calcNPasosOpt
  else
    globs.nPasos := globs.calcNPasosSim;
  globs.fActPaso := 1.0 + globs.TasaDeActualizacion;
  globs.fActPaso := Math.power(1 / globs.fActPaso, globs.HorasDelPaso / (365 * 24));

  //Armar el listado de actores
  j := 0;
  SetLength(actores, listaActores.Count);
  for k := 0 to nods.Count - 1 do
  begin
    actores[j] := TActor(nods[k]);
    Inc(j);
  end;
  for k := 0 to dems.Count - 1 do
  begin
    actores[j] := TActor(dems[k]);
    Inc(j);
  end;
  for k := 0 to arcs.Count - 1 do
  begin
    actores[j] := TActor(arcs[k]);
    Inc(j);
  end;
  cntHidraulicos := 0;
  cntTermicos := 0;
  for k := 0 to gens.Count - 1 do
  begin
    actores[j] := TActor(gens[k]);
    if actores[j] is TGeneradorHidraulico then
      Inc(cntHidraulicos)
    else if actores[j] is TGTer then
      Inc(cntTermicos);
    Inc(j);
  end;
  for k := 0 to ComercioInternacional.Count - 1 do
  begin
    actores[j] := TActor(ComercioInternacional[k]);
    Inc(j);
  end;

  //Armar el listado de generadores hidráulicos
  setlength(Hidraulicos, cntHidraulicos);
  cntHidraulicos := 0;
  for k := 0 to gens.Count - 1 do
  begin
    if TActor(gens[k]) is TGeneradorHidraulico then
    begin
      hidraulicos[cntHidraulicos] := TGeneradorHidraulico(gens[k]);
      Inc(cntHidraulicos);
    end;
  end;
  //Armar el listado de generadores térmicos
  setlength(termicos, cntTermicos);
  cntTermicos := 0;
  for k := 0 to gens.Count - 1 do
  begin
    if TActor(gens[k]) is TGTer then
    begin
      termicos[cntTermicos] := TGTer(gens[k]);
      Inc(cntTermicos);
    end;
  end;

  //Armar el listado de fuentes
  ordenarFuentes;
  SetLength(fuentes, listaFuentes.Count);

  // ATENCION aquí controlamos si es necesario esclavizar fuentes.
  for k := 0 to listaFuentes.Count - 1 do
  begin
    fuente := TFuenteAleatoria(listaFuentes[k]);
    if (fuente.durPasoDeSorteoEnHoras = 0) or
      (fuente.durPasoDeSorteoEnHoras = globs.HorasDelPaso) then
      fuentes[k] := fuente
    else
    begin
      if globs.HorasDelPaso < fuente.durPasoDeSorteoEnHoras then
      begin
        listaFuentesReemplazadas.Add(fuente);
        fuentes[k] := TEsclavizadorSobreMuestreado.Create(
          'SobreMuestreada_' + fuente.nombre, fuente);
        listaFuentes[k] := fuentes[k];
      end
      else
      begin
        listaFuentesReemplazadas.Add(fuente);
        fuentes[k] := TEsclavizadorSubMuestreado.Create(
          'SubMuestreada_' + fuente.nombre, fuente, fuente.ResumirPromediando);
        listaFuentes[k] := fuentes[k];
      end;
    end;
  end;

  Armar_lst_SorteosDelPaso_Actores;

  globs.ActualizadorLPD.limpiar;

(**** ATENCION ****************************************************
Primero preparan memoria los ACTORES y luego las FUENTES
Esto es así pues los actores pueden registrar funciones en las fuentes
y las fuentes precisan esa información para prepararse
***********************************************************)
  // Que los actores preparen su memoria
  for k := 0 to high(actores) do
    actores[k].PrepararMemoria(globs);

  //Preparar las fuentes
  for k := 0 to high(fuentes) do
  begin
    fuentes[k].prepararMemoria(globs);
    fuentes[k].RegistrarParametrosDinamicos;
  end;

  // Que los actores publiquen las variables
  for k := 0 to high(actores) do
    actores[k].PubliVars;

  // Que las fuentes publiquen las variables
  for k := 0 to high(fuentes) do
    fuentes[k].PubliVars;
  for k := 0 to listaFuentesReemplazadas.Count - 1 do
    TCosaConNombre(listaFuentesReemplazadas[k]).PubliVars;

  // Que los actores registren sus parámetros dinámicos
  for k := 0 to high(actores) do
    actores[k].RegistrarParametrosDinamicos;

  uCosaConNombre.resolver_referencias(self.listaActores);
  uCosaConNombre.resolver_referencias(self.listaFuentes);
  uCosaConNombre.resolver_referencias(listaFuentesReemplazadas);

  // Preparamos el actualizador de fichas.
  if globs.EstadoDeLaSala = CES_OPTIMIZANDO then
  begin
    globs.ActualizadorLPD.ChequeoFechas(globs.fechaIniOpt, globs.fechaFinOpt);
    globs.ActualizadorLPD.Preparse(TiempoHaciaAdelante, globs.fechaIniOpt,
      globs.fechaFinOpt);
    globs.ActualizadorLPD.PrepararOptSim(globs.fechaIniOpt, globs.fechaFinOpt);
  end
  else
  begin
    globs.ActualizadorLPD.ChequeoFechas(globs.fechaIniSim, globs.fechaFinSim);
    globs.ActualizadorLPD.Preparse(TiempoHaciaAdelante, globs.fechaIniSim,
      globs.fechaFinSim);
    globs.ActualizadorLPD.PrepararOptSim(globs.fechaIniSim, globs.fechaFinSim);
  end;
end;

procedure TSalaDeJuego.PrepararseYPubliVars(lista: TListaDeCosasConNombre);
var
  i: integer;
begin
  globs.PubliVars;
  for i := 0 to lista.Count - 1 do
  begin
    if TObject(lista[i]) is TActor then
      TActor(lista[i]).PrepararMemoria(globs);
    TCosaConNombre(lista[i]).PubliVars;
  end;
end;

procedure TSalaDeJuego.PrepararseYPubliVars;
var
  i: integer;
  cosasEnLaSala: TListaDeCosasConNombre;
begin
  cosasEnLaSala := TListaDeCosasConNombre.Create('aux_PrepararseYPubliVars');

  cosasEnLaSala.Capacity := 2 + listaActores.Count + listaFuentes.Count;
  cosasEnLaSala.Add(self);
  cosasEnLaSala.Add(globs);
  for i := 0 to listaActores.Count - 1 do
    cosasEnLaSala.Add(TCosaConNombre(listaActores[i]));
  for i := 0 to listaFuentes.Count - 1 do
    cosasEnLaSala.Add(TCosaConNombre(listaFuentes[i]));

  PrepararseYPubliVars(cosasEnLaSala);

  cosasEnLaSala.FreeSinElemenentos;
end;

procedure TSalaDeJuego.inicializarOptimizacion(const costoFuturo: TMatOfNReal);
var
  k: integer;
begin
  if not DirectoryExists(dirResultadosCorrida) then
    MkDir(dirResultadosCorrida);
  globs.invNCronicasOpt := 1 / globs.NCronicasOpt;
  globs.EstadoDeLaSala := CES_OPTIMIZANDO;
  spx := TMIPSimplex.Create_init(1, 1, 1, self.getNombreVar, self.getNombreRes);
  // solo para que esté definido.
  Prepararse(False);
  optx_nvxs;
  if (ivar_xr = 0) and (ivar_xd = 0) then
  begin
    globs.EstadoDeLaSala := CES_OPTIMIZACION_TERMINADA;
    exit; // si no hay variables de estado no optimizo nada y me voy
  end;

  Armar_lst_BarridoFijarEstadoDeActoresYFuentesToEstrella;


  if globs.CF <> nil then
    globs.CF.Free;
  globs.CF := TAdminEstados.Create(ivar_xr, ivar_xd, globs.NPasos);
  //    globs.ActualizadorLPD.ActualizarFichasHasta( globs.fechaFin  );

  for k := 0 to high(actores) do
    actores[k].optx_RegistrarVariablesDeEstado(globs.CF);
  for k := 0 to high(fuentes) do
    fuentes[k].optx_RegistrarVariablesDeEstado(globs.CF);
  globs.CF.CrearElEspacioTiempo(globs.fechaIniOpt, globs.fechaFinOpt,
    globs.HorasDelPaso, costoFuturo);

  // Ahora creamos frames para las variables auxiliares
  setlength(globs.Auxs_r0, ivar_auxNReal);
  setlength(globs.Auxs_r1, ivar_auxNReal);
  for k := 0 to ivar_auxNReal - 1 do
  begin
    setlength(globs.Auxs_r0[k], globs.CF.constelacion.nEstrellas);
    setlength(globs.Auxs_r1[k], globs.CF.constelacion.nEstrellas);
    vclear(globs.Auxs_r0[k]);
    vclear(globs.Auxs_r1[k]);
  end;
  setlength(globs.Auxs_i0, ivar_auxInt);
  setlength(globs.Auxs_i1, ivar_auxInt);
  for k := 0 to ivar_auxInt - 1 do
  begin
    setlength(globs.Auxs_i0[k], globs.CF.constelacion.nEstrellas);
    setlength(globs.Auxs_i1[k], globs.CF.constelacion.nEstrellas);
    vclear(globs.Auxs_i0[k]);
    vclear(globs.Auxs_i1[k]);
  end;

  // fijamos la fecha del paso igual a la de inicio del último paso
  globs.Fijar_kPaso(globs.nPasos);
  prepararSalaParaPaso;

  //globs.ActualizadorLPD.DumpListaToArchi('fichaslpd.txt');
  Armar_lst_EvolucionarEstado;

  Armar_lst_necesitoIterar;

  {$IFDEF PERTURBADO}
  //-- introducimos una perturbación manualmente --
  globs.CF.Free;
  globs.CF := TAdminEstados.CreateLoadFromArchi(dirResultadosCorrida + 'CF.bin');
  vswap(globs.CF.constelacion.fcosto[1], globs.CF.constelacion.fcosto[globs.kPaso_]);

  globs.CF.setEstrellaCERO;
  globs.CF.estrella_kr[0] := 5;
  //    globs.CF.SetCostoEstrella(  globs.kPaso_, 1E8 );
{$ELSE}
  //  if usarArchivoParaInicializarFrameInicial then
  //    globs.CF.InicializarFrameFinal(archivoCF, enganchesContinuos, enganchesDiscretos)
  //  else
  //    globs.CF.constelacion.ClearFrame(globs.kPaso_ + 1);
{$ENDIF}

{$IFDEF ESTABILIZAR_FRAMEINICIAL}
  if (costoFuturo <> nil) and EstabilizarInicio then
    EstabilizarFrameInicial;
{$ENDIF}
  //  RandSeedInicial := globs.sorteadorUniforme.RandSeed;
end;

procedure TSalaDeJuego.Armar_lst_BarridoFijarEstadoDeActoresYFuentesToEstrella;
var
  k: integer;
  p1, p2: procedure of object;
  actor1, actor: TActor;
  fuente1, fuente: TFuenteAleatoria;
begin
  // FijarEstadoEstrellita
  if lst_barridoFijarEstadoDeActoresToEstrella <> nil then
    lst_barridoFijarEstadoDeActoresToEstrella.Clear
  else
    lst_barridoFijarEstadoDeActoresToEstrella := TList.Create;

  actor1 := TActor.Create('', TFecha.Create_DT(now()), TFecha.Create_DT(now()),
    TActor.CreateDefaultLPDUnidades(1));
  p1 := Actor1.PosicionarseEnEstrellita;
  for k := 0 to high(actores) do
  begin
    actor := actores[k];
    p2 := actor.PosicionarseEnEstrellita;
    if @p1 <> @p2 then
      lst_barridoFijarEstadoDeActoresToEstrella.Add(actor);
  end;
  actor1.Free;

  if lst_barridoFijarEstadoDeFuentesToEstrella <> nil then
    lst_barridoFijarEstadoDeFuentesToEstrella.Clear
  else
    lst_barridoFijarEstadoDeFuentesToEstrella := TList.Create;

  fuente1 := TFuenteAleatoria.Create('', -1, False);
  p1 := fuente1.PosicionarseEnEstrellita;
  for k := 0 to listaFuentes.Count - 1 do
  begin
    fuente := fuentes[k];

    if fuente is TEsclavizador then
      p2 := TEsclavizador(fuente).esclava.PosicionarseEnEstrellita
    else
      p2 := fuente.PosicionarseEnEstrellita;
    if @p1 <> @p2 then
      lst_barridoFijarEstadoDeFuentesToEstrella.Add(fuente);
  end;
  fuente1.Free;
end;

procedure TSalaDeJuego.Armar_lst_SorteosDelPaso_Actores;
var
  k, cantRedefiniciones: integer;
  p1, p2: procedure(sortear: boolean) of object;
  actor1, actor: TActor;
begin
  SetLength(lst_SorteosDelPaso_Actores, length(actores));

  actor1 := TActor.Create('', TFecha.Create_DT(now()), TFecha.Create_DT(now()),
    TActor.CreateDefaultLPDUnidades(1));
  p1 := Actor1.SorteosDelPaso;
  cantRedefiniciones := 0;
  for k := 0 to high(actores) do
  begin
    actor := actores[k];
    p2 := actor.SorteosDelPaso;
    if @p1 <> @p2 then
    begin
      lst_SorteosDelPaso_Actores[cantRedefiniciones] := actor;
      cantRedefiniciones := cantRedefiniciones + 1;
    end;
  end;
  actor1.Free;
  if cantRedefiniciones <> Length(lst_SorteosDelPaso_Actores) then
    lst_SorteosDelPaso_Actores :=
      copy(lst_SorteosDelPaso_Actores, 0, cantRedefiniciones);
end;

procedure TSalaDeJuego.Armar_lst_opt_PrintResultados;
var
  k: integer;
  p1, p2: procedure(var fsal: textfile) of object;
  actor1: TActor;
begin
  actor1 := TActor.Create('', TFecha.Create_DT(now()), TFecha.Create_DT(now()),
    TActor.CreateDefaultLPDUnidades(1));

  // lst_opt_PrintResultados
  if lst_opt_PrintResultados <> nil then
    lst_opt_PrintResultados.Clear
  else
    lst_opt_PrintResultados := TList.Create;
  p1 := Actor1.opt_PrintResultados;
  for k := 0 to high(actores) do
  begin
    p2 := actores[k].opt_PrintResultados;
    if @p1 <> @p2 then
      lst_opt_PrintResultados.add(actores[k]);
  end;

  actor1.Free;
end;

procedure TSalaDeJuego.Armar_lst_Sim_cronicaIdInicio;
var
  k: integer;
  p1, p2: function: string of object;
  fuente1, fuente: TFuenteAleatoria;
begin
  // FijarEstadoEstrellita
  if lst_Sim_cronicaIdInicio <> nil then
    lst_Sim_cronicaIdInicio.Clear
  else
    lst_Sim_cronicaIdInicio := TList.Create;

  fuente1 := TFuenteAleatoria.Create('', 0, False);
  p1 := fuente1.cronicaIdInicio;
  for k := 0 to high(fuentes) do
  begin
    fuente := fuentes[k];
    if fuente is TEsclavizador then
      p2 := TEsclavizador(fuente).esclava.cronicaIdInicio
    else
      p2 := fuente.cronicaIdInicio;

    if @p1 <> @p2 then
      lst_Sim_cronicaIdInicio.Add(fuente);
  end;
  fuente1.Free;
end;

procedure TSalaDeJuego.armar_lst_Sim_Paso_Fin;
var
  k: integer;
  p1, p2: procedure of object;
  actor1: TActor;
begin
  actor1 := TActor.Create('', TFecha.Create_DT(now()), TFecha.Create_DT(now()),
    TActor.CreateDefaultLPDUnidades(1));

  // lst_opt_PrintResultados
  if lst_Sim_Paso_Fin <> nil then
    lst_Sim_Paso_Fin.Clear
  else
    lst_Sim_Paso_Fin := TList.Create;
  p1 := Actor1.Sim_Paso_Fin;
  for k := 0 to high(actores) do
  begin
    p2 := actores[k].Sim_Paso_Fin;
    if @p1 <> @p2 then
      lst_Sim_Paso_Fin.add(actores[k]);
  end;

  actor1.Free;
end;

procedure TSalaDeJuego.Armar_lst_EvolucionarEstado;
var
  k: integer;
  p1, p2: procedure of object;
  actor1: TActor;
  fuente1: TFuenteAleatoria;
begin
  actor1 := TActor.Create('', TFecha.Create_DT(now()), TFecha.Create_DT(now()),
    TActor.CreateDefaultLPDUnidades(1));

  // lst_opt_PrintResultados
  if lst_actores_EvolucionarEstado <> nil then
    lst_actores_evolucionarEstado.Clear
  else
    lst_actores_evolucionarEstado := TList.Create;
  p1 := Actor1.EvolucionarEstado;

  for k := 0 to high(actores) do
  begin
    p2 := actores[k].EvolucionarEstado;
    if @p1 <> @p2 then
      lst_actores_evolucionarEstado.add(actores[k]);
  end;
  actor1.Free;

  fuente1 := TFuenteAleatoria.Create('', 0, False);
  if lst_fuentes_EvolucionarEstado <> nil then
    lst_fuentes_EvolucionarEstado.Clear
  else
    lst_fuentes_EvolucionarEstado := TList.Create;

  p1 := fuente1.evolucionarEstadoActual;
  for k := 0 to high(fuentes) do
  begin
    p2 := fuentes[k].evolucionarEstadoActual;
    if @p1 <> @p2 then
      lst_fuentes_EvolucionarEstado.add(fuentes[k]);
  end;
  fuente1.Free;
end;


procedure TSalaDeJuego.Armar_lst_necesitoIterar;
var
  k: integer;
  p1, p2: function(kIteracion: integer; var errRelativo: NReal): boolean of object;
  actor1: TActor;
begin
  actor1 := TActor.Create('', TFecha.Create_DT(now()), TFecha.Create_DT(now()),
    TActor.CreateDefaultLPDUnidades(1));

  // lst_opt_PrintResultados
  if lst_necesitoIterar <> nil then
    lst_necesitoIterar.Clear
  else
    lst_necesitoIterar := TList.Create;
  p1 := Actor1.opt_NecesitoIterar;
  for k := 0 to high(actores) do
  begin
    p2 := actores[k].opt_NecesitoIterar;
    if @p1 <> @p2 then
      lst_NecesitoIterar.add(actores[k]);
  end;

  actor1.Free;
end;



(****** por ahora no va esto. Puede ser que vuelva a ponerse para
aumentar la eficiencia

procedure TSalaDeJuego.Armar_lst_Encadenamientos;
var
  iHidraulicos, iAux, nHidraulicos: Integer;
  temp: TGeneradorHidraulico;
function todasResueltas(centralesAguasArriba : TListaCentralesAguasArriba ; var noResuelta : TGeneradorHidraulico) : boolean;
var
  iCentralesEncadenadas, iter: Integer;
  res, buscando: boolean;
begin
  res:= true;
  iCentralesEncadenadas:= 0;
  //Recorro las centrales aguas arriba de la mía, si las encuentro entre las
  //que ya estan ordenadas entonces estan todas resueltas y puedo dejar la central
  //en ese lugar en la lista
  while iCentralesEncadenadas < Length(centralesAguasArriba.lst) do
  begin
    noResuelta:= centralesAguasArriba.lst[iCentralesEncadenadas];
    buscando:= true;
    for iter:= 0 to iHidraulicos -1 do
    begin
      if hidraulicos[iter] = noResuelta then
      begin
        buscando:= false;
        break;
      end;
    end;
    if not buscando then
      inc(iCentralesEncadenadas)
    else
    begin
      res:= false;
      break;
    end;
  end;
  result:= res;
end;

begin
  if length(hidraulicos) = 0 then
  begin
    nHidraulicos:= 0;
    for iAux:= 0 to Gens.lst.Count -1 do
    begin
      if (TGenerador(gens.lst[iAux]) is TGeneradorHidraulico) then
        inc(nHidraulicos);
    end;
    SetLength(hidraulicos, nHidraulicos);
    iHidraulicos:= 0;
    for iAux:= 0 to Gens.lst.Count -1 do
    begin
      if (TGenerador(gens.lst[iAux]) is TGeneradorHidraulico) then
      begin
        hidraulicos[iHidraulicos]:= gens.lst[iAux];
        inc(iHidraulicos);
        if iHidraulicos >= nHidraulicos then
          break;
      end;
    end;
  end
  else
    nHidraulicos:= Length(hidraulicos);

  iHidraulicos:= 0;
  while iHidraulicos < nHidraulicos do
  begin
    if todasResueltas(hidraulicos[iHidraulicos].centralesAguasArriba, temp) then
      inc(iHidraulicos)
    else
    begin
      for iAux:= iHidraulicos +1 to nHidraulicos -1 do
        if hidraulicos[iAux] = temp then
          break;
      hidraulicos[iAux]:= hidraulicos[iHidraulicos];
      hidraulicos[iHidraulicos]:= temp;
    end;
  end;
end;
**********)


function TSalaDeJuego.NecesitoIterar: boolean;
var
  k: integer;
  xerr: NReal;
  cnt: integer;
begin
  cnt := 0;

  // Atención: Es a propósito que le preguntamos a todos los actores.
  // No paramos en el primero que precise iterar dado que ya que vamos a iterar
  // le permitimos a los demás mejorar sus valores para lo que hay que llamar
  // a su correspondiente NecesitoIterar.
  for k := 0 to lst_NecesitoIterar.Count - 1 do
    if TActor(lst_NecesitoIterar.Items[k]).opt_NecesitoIterar(
      globs.cntIteracionesDelPaso, xerr) then
      Inc(cnt);

  Result := cnt > 0;
end;



function TSalaDeJuego.getNombreVar(ivar: integer): string;
var
  i: integer;
  nombre: string;
begin
  nombre := '';
  for i := 0 to high(actores) do
    if actores[i].getNombreVar(ivar, nombre) then
      break;
  if nombre = '' then
    nombre := '??_x' + IntToStr(ivar);
  Result := nombre;
end;

function TSalaDeJuego.getNombreRes(ires: integer): string;
var
  i: integer;
  nombre: string;
begin
  nombre := '';
  for i := 0 to high(actores) do
    if actores[i].getNombreRes(ires, nombre) then
      break;
  if nombre = '' then
    nombre := '??_y' + IntToStr(ires);
  Result := nombre;
end;

procedure TSalaDeJuego.dump_Variables;
const
  indentador = #9;
var
  f: TextFile;
  k: integer;
  archi: string;
begin
  try
    archi := DateTimeToStr(now()) + '_dump_Variables.txt';
    while pos('/', archi) > 0 do
      archi[pos('/', archi)] := '-';
    while pos(':', archi) > 0 do
      archi[pos(':', archi)] := '-';
    archi := getDir_Dbg + archi;
    AssignFile(f, archi);
    rewrite(f);
    writeln(f, 'Estado de la Sala= ', estadoSalaToString(globs.EstadoDeLaSala));
    writeln(f, 'globs.kpaso_= ', globs.kpaso_);
    writeln(f, 'globs.kcronica= ', globs.kcronica);

    writeln(f, 'globs.cntIteracionesDelPaso= ', globs.cntIteracionesDelPaso);

    writeln(f);
    for k := 0 to Nods.Count - 1 do
      TNodo(Nods[k]).dump_Variables(f, indentador);
    for k := 0 to Dems.Count - 1 do
      TDemanda(Dems[k]).dump_Variables(f, indentador);
    for k := 0 to Gens.Count - 1 do
      TGenerador(Gens[k]).dump_Variables(f, indentador);
    for k := 0 to ComercioInternacional.Count - 1 do
      TMercadoSpot(ComercioInternacional[k]).dump_Variables(f, indentador);
    for k := 0 to Arcs.Count - 1 do
      TArco(Arcs[k]).dump_Variables(f, indentador);
    for k := 0 to high(fuentes) do
      fuentes[k].dump_Variables(f, indentador);
  finally
    CloseFile(f);
  end;
end;

procedure TSalaDeJuego.Sim_Cronica_Inicio;
var
  k: integer;
begin
  globs.Fijar_kPaso(1);
  globs.ActualizadorLPD.PrepararOptSim(globs.fechaIniSim, globs.fechaFinSim);
  for k := 0 to high(actores) do
    actores[k].Sim_Cronica_Inicio;
  for k := 0 to high(fuentes) do
    fuentes[k].Sim_Cronica_Inicio;
  globs.procNot(globs.procNot_InicioCronica);
end;

procedure TSalaDeJuego.prepararSalaParaPaso;
begin
  globs.ActualizadorLPD.ActualizarFichasHasta(globs.FechaInicioDelpaso);
end;

procedure TSalaDeJuego.barridoFijarEstadoDeActoresYFuentesToEstrellaOpt;
var
  k: integer;
begin
  for k := 0 to high(fuentes) do
    fuentes[k].PosicionarseEnEstrellita;

  for k := 0 to lst_barridoFijarEstadoDeActoresToEstrella.Count - 1 do
    TActor(lst_barridoFijarEstadoDeActoresToEstrella.items[k]).PosicionarseEnEstrellita;
end;

procedure TSalaDeJuego.barridoFijarEstadoGlobal;
var
  k: integer;
begin
  for k := 0 to lst_barridoFijarEstadoDeActoresToEstrella.Count - 1 do
    TActor(lst_barridoFijarEstadoDeActoresToEstrella.items[k]).ActualizarEstadoGlobal;
  for k := 0 to lst_barridoFijarEstadoDeFuentesToEstrella.Count - 1 do
    TFuenteAleatoria(lst_barridoFijarEstadoDeFuentesToEstrella[k]).
      ActualizarEstadoGlobal;
end;

procedure TSalaDeJuego.Actores_AcumularAuxs1;
var
  k: integer;
begin
  for k := 0 to high(actores) do
    actores[k].AcumAux1(globs.invNCronicasOpt);
end;

procedure TSalaDeJuego.Actores_SetAuxs1;
var
  k: integer;
begin
  for k := 0 to high(actores) do
    actores[k].SetAux1;
end;

procedure TSalaDeJuego.SorteosDelPasoSim;
var
  k: integer;
begin
  for k := 0 to high(fuentes) do
  begin
    //Calcula (RB) y (BC)
    fuentes[k].sorteosDelPaso(True);
  end;
  for k := 0 to high(lst_SorteosDelPaso_Actores) do
    lst_SorteosDelPaso_Actores[k].SorteosDelPaso(True);
end;

procedure TSalaDeJuego.SorteosDelPasoOpt(sortear: boolean);
var
  k: integer;
begin
  for k := 0 to high(fuentes) do
    fuentes[k].sorteosDelPaso(sortear);
  for k := 0 to high(lst_SorteosDelPaso_Actores) do
    lst_SorteosDelPaso_Actores[k].SorteosDelPaso(sortear);
end;

procedure TSalaDeJuego.PrepararPaso_PreSorteo;
var
  k: integer;
begin
  for k := 0 to high(actores) do
  begin
    actores[k].PrepararPaso_as;
  end;
end;

procedure TSalaDeJuego.PrepararPaso_PostSorteo_Fuentes;
var
  k: integer;
begin
  for k := 0 to high(Fuentes) do
    Fuentes[k].PrepararPaso_ps;
end;

procedure TSalaDeJuego.PrepararPaso_PostSorteo_Actores;
var
  k: integer;
{$IFDEF CHEQUEOMEM}
  tam: cardinal;
{$ENDIF}
begin
{$IFDEF CHEQUEOMEM}
  tam := udbgutil.tam;
{$ENDIF}
  for k := 0 to high(actores) do
  begin
    actores[k].PrepararPaso_ps;
{$IFDEF CHEQUEOMEM}
    if tam <> udbgutil.tam then
      raise Exception.Create('OJO; el Actor: ' + actores[k].nombre +
        ' pierde memoria en PrepararPaso_ps');
{$ENDIF}
  end;
end;

function TSalaDeJuego.ResolverPaso: NReal;
var
  k: integer;
  ispxres: integer;
begin
  ivar := 1;
  ires := 1;
  ivae := 1;

  // Amar_lst_Encadenamientos;

  // barrido
  for k := 0 to high(actores) do
    actores[k].opt_nvers(ivar, ivae, ires);

  if ((ires = spx.nf) and (ivar = spx.nc)) and (ivae - 1 = spx.nvents) then
    spx.limpiar
  else
  begin
    spx.Free;
    spx := TMIPSimplex.Create_init(ires, ivar, ivae - 1, self.getNombreVar,
      self.getNombreRes);
  end;

  for k := 0 to high(actores) do
    actores[k].opt_cargue(spx);

  for k := 0 to high(actores) do
    actores[k].opt_fijarRestriccionesDeCaja(spx);

  for k := 0 to high(hidraulicos) do
    hidraulicos[k].ResolverEncadenamientos(spx);

  // le damos la oportunidad a las fuentes con estado de cargar
  for k := 0 to high(fuentes) do
    fuentes[k].opt_cargue(spx);

  try
    ispxres := spx.resolver;
    if ispxres < 0 then
      raise Exception.Create('ERROR DE DATOS, NO ENCONTRE DESPACHO FACTIBLE!!! ispxres:'
        +
        IntToStr(ispxres) + ' kpaso:' + IntToStr(globs.kPaso_));
  except
    On E: Exception do
    begin
      //      spx.DumpSistemaToXLT('DESPACHOINFACTIBLE.XLT', e.Message);
      dump_Variables;
      raise;
    end;
  end;

  globs.costodelpaso := -spx.fval;
  // spx.DumpSistemaToXLT('spx-despuesderesolver.xlt', 'debug');

  for k := 0 to high(actores) do
    actores[k].opt_leerSolucion(spx);

  Result := -spx.fval;
end;

procedure TSalaDeJuego.Sim_Paso_Fin;
var
  k: integer;
begin
  for k := 0 to lst_Sim_Paso_Fin.Count - 1 do
    TActor(lst_Sim_Paso_Fin[k]).Sim_Paso_Fin;
  globs.procNot(globs.procNot_FinPaso);
end;

procedure TSalaDeJuego.CapturarResultadosDelPaso;
begin
end;

procedure TSalaDeJuego.Sim_Cronica_Fin;
var
  k: integer;
begin
  for k := 0 to high(actores) do
    actores[k].Sim_Cronica_Fin;
  globs.procNot(globs.procNot_FinCronica);
  Inc(globs.kCronica);
end;

procedure TSalaDeJuego.FinSimulacion;
begin
  globs.procNot(globs.procNot_FinSimulacion);
end;

procedure TSalaDeJuego.FinOptimizacion;
begin
  globs.procNot(globs.procNot_opt_FinOptimizacion);
end;

procedure TSalaDeJuego.optx_nvxs;
var
  k: integer;
begin
  // ahora determinamos la dimensión del espacio de estado y los frames
  // de variables auxiliares que sea necesario crear.
  ivar_xr := 0;
  ivar_xd := 0;
  ivar_auxNReal := 0;
  ivar_auxInt := 0;
  for k := 0 to high(actores) do
    actores[k].optx_nvxs(ivar_xr, ivar_xd, ivar_auxNReal, ivar_auxInt);
  for k := 0 to high(fuentes) do
    fuentes[k].optx_nvxs(ivar_xr, ivar_xd, ivar_auxNReal, ivar_auxInt);
end;

function TSalaDeJuego.ContarVariablesDeEstado: integer;
begin
  optx_nvxs;
  Result := ivar_xr + ivar_xd;
end;

function TSalaDeJuego.generarResumenTermicoPrimerasFichas: string;
var
  archi: TextFile;
  i: integer;
  res: string;
begin
  if not DirectoryExists(dirResultadosCorrida) then
    MkDir(dirResultadosCorrida);
  res := dirResultadosCorrida + 'resumen_termico_' +
    nombreArchSinExtension(archiSala) + '.xlt';
  AssignFile(archi, res);
  try
    Rewrite(archi);
    TGTer.generarLineaEncabezadosResumen(archi);
    for i := 0 to Gens.Count - 1 do
      if Gens[i] is TGTer then
        TGter(Gens[i]).generarLineaResumenPrimerFicha(archi);
    Result := res;
  finally
    CloseFile(archi);
  end;
end;


procedure TSalaDeJuego.EvolucionarEstado;
var
  k: integer;
begin
  for k := 0 to lst_actores_evolucionarEstado.Count - 1 do
    TActor(lst_actores_evolucionarEstado[k]).EvolucionarEstado;
  for k := 0 to lst_fuentes_evolucionarEstado.Count - 1 do
    TFuenteAleatoria(lst_fuentes_evolucionarEstado[k]).EvolucionarEstadoActual;
end;

procedure TSalaDeJuego.Simular(ArchiCFAux: string);
var
  costoAcum, costoAcumCronica: NReal;
  qAct: NReal;
begin
  globs.EstadoDeLaSala := CES_SIMULANDO;
  //  RandSeedInicial := globs.sorteadorUniforme.RandSeed;

  //Inicialización de las variables locales al procedimiento
  costoAcum := 0; // costo acumulado actualizado
  try
    spx := TMipSimplex.Create_init(1, 1, 1, self.getNombreVar, self.getNombreRes);
    // solo para que esté definido.

    // Preparamos la sala y los actores.
    Prepararse(True);

    // Determinamos la dimesión del espacio de estados y si es necesario
    // crear frames auxiliares del estado para cálculos iterativos.
    optx_nvxs;
    Armar_lst_BarridoFijarEstadoDeActoresYFuentesToEstrella;
    armar_lst_Sim_Paso_Fin;

    Armar_lst_EvolucionarEstado;

    Armar_lst_necesitoIterar;

    globs.procNot(globs.procNot_InicioSimulacion);
    globs.kCronica := 1;

    while (globs.kCronica <= globs.NCronicasSim) and (not globs.abortarSim) do
    begin
      Sim_Cronica_Inicio;
      // inicializamos acumulador de costos de la crónica
      costoAcumCronica := 0;
      qAct := 1;

      // rch
      //      globs.ActualizadorLPD.DumpListaToArchi( 'fichas_LPD.xlt' );

      while (globs.kPaso_ <= globs.nPasos) and (not globs.abortarSim) do
      begin
        globs.procNot(globs.procNot_InicioPaso);
        prepararSalaParaPaso;
        PrepararPaso_PreSorteo;
        SorteosDelPasoSim;
        barridoFijarEstadoGlobal;
        PrepararPaso_PostSorteo_Fuentes;

        globs.cntIteracionesDelPaso := 0;
        repeat
          Inc(globs.cntIteracionesDelPaso);
          PrepararPaso_PostSorteo_Actores;
          CostoDelPaso := ResolverPaso;
        until (globs.cntIteracionesDelPaso >= globs.NMAX_ITERACIONESDELPASO_OPT) or
          (not NecesitoIterar);




        EvolucionarEstado;
        Sim_Paso_Fin;

        globs.Fijar_kPaso(globs.kPaso_ + 1);
        costoAcumCronica := costoAcumCronica + costoDelPaso * qAct;
        qAct := qAct * globs.fActPaso;
      end;
      Sim_Cronica_Fin;
      costoAcum := costoAcum + costoAcumCronica / globs.NCronicasSim;
    end;
    FinSimulacion;
    globs.EstadoDeLaSala := CES_SIMULACION_TERMINADA;
  finally
    if globs.EstadoDeLaSala <> CES_SIMULACION_TERMINADA then
      globs.EstadoDeLaSala := CES_SIMULACION_ABORTADA;
  end;
end;

procedure TSalaDeJuego.ImprimirPotenciasFirmes;
var
  iposteres, kactorres: integer;
  fPotsFirmes: TextFile;
begin
  globs.EstadoDeLaSala := CES_SIMULANDO;
  try
    // Preparamos la sala y los actores.
    Prepararse(True);
    Sim_Cronica_Inicio;
    //Creo el archivo de simres
    if not DirectoryExists(dirResultadosCorrida) then
      MkDir(dirResultadosCorrida);
    assignfile(fPotsFirmes, dirResultadosCorrida + 'potencias_Termicas_Firmes.xlt');
    rewrite(fPotsFirmes);
    system.writeln(fPotsFirmes, 'Inicio simulación: ', #9,
      DateTimeToIsoStr(now()));
    system.Writeln(fPotsFirmes, 'FechaIniSim: ', #9, globs.fechaIniSim.AsISOStr,
      #9, 'FechaFinSim: ', #9, globs.fechaFinSim.AsISOStr);
    system.writeln(fPotsFirmes, 'NCronicas:', #9, 1);
    system.writeln(fPotsFirmes, 'NPasos:', #9, globs.nPasos);
    system.writeln(fPotsFirmes, 'NPostes:', #9, length(globs.Durpos));
    system.Write(fPotsFirmes, 'DurPos[h]:');
    for iposteres := 0 to high(globs.DurPos) do
      Write(fPotsFirmes, #9, globs.DurPos[iposteres]: 6: 0);
    system.writeln(fPotsFirmes);
    system.writeln(fPotsFirmes, 'NActores:', #9, length(actores));
    TActor.printEncabezadoResumenSim(fPotsFirmes);
    for kactorres := 0 to high(actores) do
      actores[kactorres].printResumenSim(fPotsFirmes);

    writeln(fPotsFirmes);
    //    writeln(fPotsFirmes, 'CRONICA:', #9, globs.kCronica, #9,
    //      'SemillaAleatoria:', #9, globs.sorteadorUniforme.RandSeed);
    // PRINT kencab= 0
    Write(fPotsFirmes, '-', #9, '-');
    for kactorres := 0 to high(termicos) do
      termicos[kactorres].sim_PrintResultados_Encab_PotFirme(fPotsFirmes, 0);
    writeln(fPotsFirmes);

    // PRINT kencab= 1
    Write(fPotsFirmes, '-', #9, '-');
    for kactorres := 0 to high(termicos) do
      termicos[kactorres].sim_PrintResultados_Encab_PotFirme(fPotsFirmes, 1);
    writeln(fPotsFirmes);

    // PRINT kencab= 2
    Write(fPotsFirmes, 'Paso', #9, 'FechaInicioDelPaso');
    for kactorres := 0 to high(termicos) do
      termicos[kactorres].sim_PrintResultados_Encab_PotFirme(fPotsFirmes, 2);
    writeln(fPotsFirmes);


    while (globs.kPaso_ <= globs.nPasos) do
    begin
      globs.procNot(globs.procNot_InicioPaso);
      // Actualizar   fichas lpd
      prepararSalaParaPaso;
      Write(fPotsFirmes, IntToStr(globs.kPaso_), #9, globs.FechaInicioDelPaso.AsISOStr);
      for kactorres := 0 to high(termicos) do
        termicos[kactorres].sim_PrintResultados_PotFirme(fPotsFirmes);
      writeln(fPotsFirmes);
      globs.Fijar_kPaso(globs.kPaso_ + 1);
    end;
    globs.EstadoDeLaSala := CES_SIMULACION_TERMINADA;
  finally
    system.writeln(fPotsFirmes, 'Fin simulación: ', #9, DateTimeToStr(now()));
    Close(fPotsFirmes);
  end;
end;

procedure TSalaDeJuego.ordenarFuentes;
var
  auxSwap, fuenteI: TFuenteAleatoria;
  huboReferencia: boolean;
  i, j: integer;
begin
  i := 0;
  //El -1 esta bien porque la ultima fuente no tiene despues de ella nadie,
  //particularmente nadie que la referencie
  while i < listaFuentes.Count - 1 do
  begin
    fuenteI := TFuenteAleatoria(listaFuentes[i]);
    huboReferencia := False;
    for j := i + 1 to listaFuentes.Count - 1 do
    begin
      if fuenteI.referenciaFuente(TFuenteAleatoria(listaFuentes[j])) then
      begin
        assert(not TFuenteAleatoria(listaFuentes[j]).referenciaFuente(fuenteI),
          'TSalaDeJuego.ordenarFuentes: hay un ciclo entre las fuentes.' +
          'Las fuentes ' + fuenteI.nombre + ' y ' +
          TFuenteAleatoria(listaFuentes[j]).nombre + ' se referencian mutuamente.');
        auxSwap := TFuenteAleatoria(listaFuentes[j]);
        listaFuentes[j] := fuenteI;
        listaFuentes[i] := auxSwap;
        huboReferencia := True;
        break;
      end;
    end;
    if not huboReferencia then
      Inc(i);
  end;
end;


procedure TSalaDeJuego.asignarSemillasIniciales(semillaDelPaso: integer);
var
  i: integer;
begin
  //  self.globs.fijarSemillaAleatoria(semillaDelPaso);
  if listaOptimizadores <> nil then
  begin
    //    for i := 0 to listaOptimizadores.Count - 1 do
    //      TRobotOptimizadorMultiCore(listaOptimizadores[i]).sala.globs.fijarSemillaAleatoria(
    //        semillaDelPaso);
  end;
end;

procedure TSalaDeJuego.asignarEstadosFuentesMadreUniforme(glma: TDAofNInt;
  glinext, glinextp: integer);
var
  i: integer;
  // estado: TEstado_Tf_ddpMadreUniformeRand3;
begin

  //Estado agregado 18/10/2011
  //  estado.glma := glma;
  //  estado.glinext := glinext;
  //  estado.glinextp := glinextp;

  //  self.globs.sorteadorUniforme.setEstado(estado);
  if listaOptimizadores <> nil then
  begin
    for i := 0 to listaOptimizadores.Count - 1 do
    begin

      //      TRobotOptimizadorMultiCore(
      //        listaOptimizadores[i]).sala.globs.sorteadorUniforme.setEstado(estado);
    end;
  end;
end;

procedure TSalaDeJuego.calcularRangoEstrellas(estrellaIni, estrellaFin: integer);
var
  kcron, kestr: integer;
  CostoDelPaso_simplex,

  CostoDelPaso_Esperado, CostoFuturo_Cronica, CostoFuturo_Esperado: NReal;
  CF_Base: NReal;
begin
  prepararSalaParaPaso;
  PrepararPaso_PreSorteo;
  //globs.procNot(globs.procNot_opt_InicioCalculosDeEtapa);

  if globs.SortearOpt then // Cálculo de los costos de la etapa con sorteos
  begin
  (* optimización CRONIZADA
    Para cada crónica realizamos sorteos y realizamos un barrido de los
    estados con el sorteo fijo. Así vamos acumulando los costos del paso
    con cada sorteo para cada uno de los estados. Después de haber realizado
    todos los sorteos-barridos_de_estado que queremos, realizamos un nuevo
    barrido_de_estado para promediar los valores acumulados.
    El orden en que se realiza esto es importante, pues es importante calcular con
    cada sorteo el costo para los diferentes estados. Los sorteos determinan
    las máquinas que estan disponibles, los aportes a los embalses y los
    recursos disponibles en general. Es importante que para un mismo conjunto
    de recursos disponibles se realice el barrido de los estados y luego con
    otro conjunto para salvaguardar la monotonía esperable de los costos de
    una etapa respecto de las variables de estado.
    *)
    // borro el frame pues lo voy a usar para acumular
    globs.CF.constelacion.ClearFrame(globs.kPaso_, estrellaIni, estrellaFin);
    //    globs.ClearAuxs1;
    for kCron := 1 to globs.NCronicasOpt do
    begin
      globs.kCronica := kCron;
      //      globs.procNot( globs.procNot_opt_InicioCronicaSorteos );
      SorteosDelPasoOpt(True);
      // Aqui hacemos el barrido de los estados
      // nos posicionamos en la primer estrella
      globs.CF.posicionarseEnEstrella(estrellaIni);
      for kestr := estrellaIni to estrellaFin do
      begin
        globs.CF.SetEstadoToEstrella; // Fijamos la estrella
        barridoFijarEstadoDeActoresYFuentesToEstrellaOpt;// Fijamos el estado en los actores
        CF_Base := globs.CF.costoEstrella(globs.kPaso_ + 1);

        PrepararPaso_PostSorteo_Fuentes;

        globs.cntIteracionesDelPaso := 0;
        repeat
          Inc(globs.cntIteracionesDelPaso);

          PrepararPaso_PostSorteo_Actores;
          CostoDelPaso_Simplex := ResolverPaso;

        until (globs.cntIteracionesDelPaso >= globs.NMAX_ITERACIONESDELPASO_OPT) or
          (not NecesitoIterar);

        EvolucionarEstado;

        globs.CF.AcumCostoEstrella(globs.kPaso_, CostoFuturo_Cronica *
          globs.invNCronicasOpt);
        Actores_AcumularAuxs1;
{$IFDEF SPXMEJORCAMINO}
        vswap(spx.mejorCaminoEsperado, spx.mejorCaminoEncontrado);
{$ENDIF}
        globs.CF.incEstrella; // pasamos a la siguiente estrella
      end; // for de las estrellas
    end; // for de las crónicas

    globs.MultAuxs1(1 / globs.NCronicasOpt);
    globs.SwapAuxs;
  end // optimización Cronizada
  else
  begin // optimización con valores esperados
    globs.kCronica := 0; // ponemos a cero para que sea en valor esperado

    SorteosDelPasoOpt(False);
    // Aqui hacemos el barrido de los estados
    // nos posicionamos en la primer estrella
    globs.CF.posicionarseEnEstrella(estrellaIni);
    for kEstr := estrellaIni to estrellaFin do
    begin
      // fijamos el estado
      globs.CF.SetEstadoToEstrella;
      barridoFijarEstadoDeActoresYFuentesToEstrellaOpt;// Fijamos el estado en los actores

      CF_Base := globs.CF.costoEstrella(globs.kPaso_ + 1);

      PrepararPaso_PostSorteo_Fuentes;

      globs.cntIteracionesDelPaso := 0;
      repeat
        Inc(globs.cntIteracionesDelPaso);

        PrepararPaso_PostSorteo_Actores;
        CostoDelPaso_Simplex := ResolverPaso;

      until (globs.cntIteracionesDelPaso >= globs.NMAX_ITERACIONESDELPASO_OPT) or
        (not NecesitoIterar);

      EvolucionarEstado;



      CostoFuturo_Esperado := CostoDelPaso_Esperado + globs.fActPaso * CF_Base;

      globs.CF.SetCostoEstrella(globs.kPaso_, CostoFuturo_Esperado);
      Actores_SetAuxs1;
      globs.SwapAuxs;

{$IFDEF SPXMEJORCAMINO}
      vswap(spx.mejorCaminoEsperado, spx.mejorCaminoEncontrado);
{$ENDIF}
      globs.CF.incEstrella;
    end;// barrido estrellas
  end;
end;

procedure TSalaDeJuego.calcularRangoEstrellasMultiCore(estrellaIni,
  estrellaFin: integer);
var
  i: integer;
  fuiElUltimo: boolean;
begin
  globs.procNot(globs.procNot_opt_InicioCalculosDeEtapa);
  if (oldEstrellaIni <> estrellaIni) or (oldEstrellaFin <> estrellaFin) then
  begin
    oldEstrellaIni := estrellaIni;
    oldEstrellaFin := estrellaFin;

    asignarEstrellas(estrellaIni, estrellaFin);
  end;
  uRobotOptimizadorMultiCore.cntCalculando := nHilos;
  for i := 0 to listaEventos.Count - 1 do
  begin
{$IFDEF WINDOWS}
    TEvent(listaEventos[i]).Signal;
{$ELSE}
    TEventTX(listaEventos[i]).Signal;
{$ENDIF}
  end;

  calcularRangoEstrellas(self.estrellaIniCalc, self.estrellaFinCalc);

  if not semCntCalculando.Get(6000) then
  begin
    writeln('TSalaDeJuego.calcularRangoEstrellasMultiCore: No obtuve semCntCalculando');
    globs.abortarSim := True;
  end;

  cntCalculando := cntCalculando - 1;
  //    writeln('hiloPrincipal:', cntCalculando);
  fuiElUltimo := cntCalculando = 0;
  semCntCalculando.Release;
  if not fuiElUltimo and not evPasoCompleto.Wait(60000) then
    //1 minutos para completar un paso. Tendría que dar
  begin
    writeln('TSalaDeJuego.calcularRangoEstrellasMultiCore: No me despertaron de evPasoCompleto');
    globs.abortarSim := True;
    for i := 0 to listaOptimizadores.Count - 1 do
      TRobotOptimizadorMultiCore(listaOptimizadores[i]).Terminate;
  end;
end;

procedure TSalaDeJuego.llenarRangoEstrellas(estrellaIni: integer;
  costosFuturos: TDAOfNReal; kpaso: integer);
begin
  vcopyTramoDesplazando(globs.CF.constelacion.fCosto[kpaso],
    estrellaIni, costosFuturos, 0, Length(costosFuturos));
end;

function TSalaDeJuego.llenarRangosEstrellasYDarPaso(estrellasIni: TDAofNInt;
  costosFuturos: TDAOfDAOfNReal; kpaso: integer): integer;
var
  i: integer;
begin
  if globs.kPaso_ >= kpaso then
  begin
    for i := 0 to high(estrellasIni) do
      vcopyTramoDesplazando(globs.CF.constelacion.fCosto[kPaso],
        estrellasIni[i], costosFuturos[i], 0, Length(costosFuturos[i]));
  end;
  Result := irAPaso(kpaso - 1);
end;

function TSalaDeJuego.getRangoEstrellas(estrellaIni, estrellaFin, paso: integer):
TDAOfNReal;
var
  res: TDAofNReal;
begin
  SetLength(res, estrellaFin - estrellaIni + 1);
  vcopyTramoDesplazando(res, 0, globs.CF.constelacion.fCosto[paso],
    estrellaIni, estrellaFin - estrellaIni + 1);
  Result := res;
end;

procedure TSalaDeJuego.darPaso;
var
  i: integer;
begin
  if not pasosAutomaticos then
    for i := 0 to listaOptimizadores.Count - 1 do
      TRobotOptimizadorMultiCore(listaOptimizadores[i]).sala.darPaso;
  globs.Fijar_kPaso(globs.kPaso_ - 1);
end;

function TSalaDeJuego.irAPaso(nuevoPaso: integer): integer;
var
  i: integer;
begin
  if not pasosAutomaticos then
    for i := 0 to listaOptimizadores.Count - 1 do
      TRobotOptimizadorMultiCore(listaOptimizadores[i]).sala.irAPaso(nuevoPaso);
  if globs.EstadoDeLaSala = CES_OPTIMIZANDO then
  begin
    if globs.kPaso_ > nuevoPaso then
    begin
      globs.Fijar_kPaso(nuevoPaso);
      globs.procNot(globs.procNot_opt_FinCalculosDeEtapa);
      Result := 1;
    end
    else if globs.kpaso_ = nuevoPaso then
      Result := 2
    else
      Result := 0;
  end
  else //if globs.EstadoDeLaSala = CES_SIMULANDO
  begin
    if globs.kPaso_ < nuevoPaso then
    begin
      globs.Fijar_kPaso(nuevoPaso);
      //      globs.procNot( globs.procNot_opt_FinCalculosDeEtapa);
      Result := 1;
    end
    else if globs.kPaso_ = nuevoPaso then
      Result := 2
    else
      Result := 0;
  end;
end;

procedure TSalaDeJuego.Optimizar;
var
  k: integer;
begin
  //  RandSeedInicial := globs.sorteadorUniforme.RandSeed;
  globs.invNCronicasOpt := 1 / globs.NCronicasOpt;
  try
    globs.EstadoDeLaSala := CES_OPTIMIZANDO;
    spx := TMIPSimplex.Create_init(1, 1, 1, self.getNombreVar, self.getNombreRes);
    // solo para que esté definido.
    Prepararse(False);
    optx_nvxs;
    if (ivar_xr = 0) and (ivar_xd = 0) then
    begin
      globs.EstadoDeLaSala := CES_OPTIMIZACION_TERMINADA;
      exit; // si no hay variables de estado no optimizo nada y me voy
    end;

    Armar_lst_BarridoFijarEstadoDeActoresYFuentesToEstrella;

    if globs.CF <> nil then
      globs.CF.Free;
    globs.CF := TAdminEstados.Create(ivar_xr, ivar_xd, globs.NPasos);
    //    globs.ActualizadorLPD.ActualizarFichasHasta( globs.fechaFin  );

    for k := 0 to high(actores) do
      actores[k].optx_RegistrarVariablesDeEstado(globs.CF);
    for k := 0 to high(fuentes) do
      fuentes[k].optx_RegistrarVariablesDeEstado(globs.CF);

    globs.CF.CrearElEspacioTiempo(globs.fechaIniOpt, globs.fechaFinOpt,
      globs.HorasDelPaso, nil);

    // Ahora creamos frames para las variables auxiliares
    setlength(globs.Auxs_r0, ivar_auxNReal);
    setlength(globs.Auxs_r1, ivar_auxNReal);
    for k := 0 to ivar_auxNReal - 1 do
    begin
      setlength(globs.Auxs_r0[k], globs.CF.constelacion.nEstrellas);
      setlength(globs.Auxs_r1[k], globs.CF.constelacion.nEstrellas);
      vclear(globs.Auxs_r0[k]);
      vclear(globs.Auxs_r1[k]);

    end;
    setlength(globs.Auxs_i0, ivar_auxInt);
    setlength(globs.Auxs_i1, ivar_auxInt);
    for k := 0 to ivar_auxInt - 1 do
    begin
      setlength(globs.Auxs_i0[k], globs.CF.constelacion.nEstrellas);
      setlength(globs.Auxs_i1[k], globs.CF.constelacion.nEstrellas);
      vclear(globs.Auxs_i0[k]);
      vclear(globs.Auxs_i1[k]);
    end;

    // fijamos la fecha del paso igual a la de inicio del último paso
    globs.Fijar_kPaso(globs.nPasos);
    prepararSalaParaPaso;
    //globs.ActualizadorLPD.DumpListaToArchi('fichaslpd.txt');

    Armar_lst_necesitoIterar;

  {$IFDEF PERTURBADO}
    // Frame CERO EN CERO y un pendorcho
    globs.CF.constelacion.ClearFrame(globs.kPaso_ + 1);
    globs.CF.posicionarseEnEstrella(2);
    //    globs.CF.setEstrellaCERO;
    //    globs.CF.estrella_kr[0]:= 2;
    globs.CF.SetCostoEstrella(globs.kPaso_ + 1, 1e7);
{$ELSE}
    if usarArchivoParaInicializarFrameInicial then
    //      globs.CF.InicializarFrameFinal(
    //        archivoCF,
    //        enganchesContinuos,
    //        enganchesDiscretos)
    else
      globs.CF.constelacion.ClearFrame(globs.kPaso_ + 1);
{$ENDIF}

{$IFDEF ESTABILIZAR_FRAMEINICIAL}
    if EstabilizarInicio then
      EstabilizarFrameInicial;
{$ENDIF}

    if not DirectoryExists(dirResultadosCorrida) then
      MkDir(dirResultadosCorrida);
    //Hasta aca viene el inicializarOptimizacion

    // nos vamos un paso
    //    globs.Fijar_kPaso( globs.kPaso_ -1 );
    globs.procNot(globs.procNot_opt_InicioOptimizacion);

    // Barrido de los pasos en reversa
    while (globs.kPaso_ > 0) and (not globs.abortarSim) do
    begin
      calcularRangoEstrellas(0, globs.CF.nEstrellasPorPuntoT - 1);
      darPaso;
    end; // while del paso

    FinOptimizacion;
    globs.EstadoDeLaSala := CES_OPTIMIZACION_TERMINADA;
  except
    globs.EstadoDeLaSala := CES_OPTIMIZACION_ABORTADA;
    raise;
  end;
end;

procedure TsalaDeJuego.inicializarArchisOptRes(var fsal: TextFile;
  var fsal_opt: TDAOfTextFile; var fsalopen: boolean);
var
  archi: string;
  k: integer;
  NoFinBarridoEstrellas: boolean;
begin
  if globs.SortearOpt then
    archi := dirResultadosCorrida + 'optres_' + IntToStr(RandSeedInicial) +
      'x' + IntToStr(globs.NCronicasOpt) + '.xlt'
  else
    archi := dirResultadosCorrida + 'optres_VE.xlt';
  assignfile(fsal, archi);
  {$I-}
  rewrite(fsal);
  {$I+}
  if ioresult <> 0 then
    raise Exception.Create('No es posible crear el archivo: ' + archi);
  fsalopen := True;

  if escribirOptActores then
  begin
    Armar_lst_opt_PrintResultados;
    // aquí ya podemos detectar cuantos hay en lst_PrintResultadosOpt
    setlength(fsal_opt, lst_opt_PrintResultados.Count);
    for k := 0 to high(fsal_opt) do
    begin
      if globs.SortearOpt then
        archi := dirResultadosCorrida + 'opt' +
          TActor(lst_opt_PrintResultados.items[k]).nombre + '_' +
          IntToStr(RandSeedInicial) + 'x' + IntToStr(globs.NCronicasOpt) + '.xlt'
      else
        archi := dirResultadosCorrida + 'opt' +
          TActor(lst_opt_PrintResultados.items[k]).nombre + '_VE.xlt';
      assignfile(fsal_opt[k], archi);
      rewrite(fsal_opt[k]);
    end;
  end
  else
    SetLength(fsal_opt, 0);

  writeln(fsal, 'fActPaso: ', #9, FloatToStrF(globs.fActPaso, ffGeneral, 12, 10));
  for k := 0 to high(fsal_opt) do
    writeln(fsal_opt[k], 'fActPaso: ', #9, FloatToStrF(globs.fActPaso,
      ffGeneral, 12, 10));
  globs.CF.constelacion.PrintDefsToText(fsal, True);
  for k := 0 to high(fsal_opt) do
    globs.CF.constelacion.PrintDefsToText(fsal_opt[k], False);

  Write(fsal, 'paso\estado' + #9 + 'Fecha');
  for k := 1 to globs.CF.nEstrellasPorPuntoT do
    Write(fsal, #9, k);
  writeln(fsal);

  // salvamos el frame del último paso de tiempo
  globs.Fijar_kPaso(globs.kPaso_ + 1);
  Write(fsal, IntToStr(globs.kpaso_) + #9 + globs.FechaInicioDelpaso.AsISOStr);
  globs.Fijar_kPaso(globs.kPaso_ - 1);
  for k := 0 to high(fsal_opt) do
    Write(fsal_opt[k], 'paso\estado' + #9 + 'Fecha');

  globs.CF.setEstrellaCERO;
  NoFinBarridoEstrellas := True;
  while NoFinBarridoEstrellas do
  begin
    globs.CF.SetEstadoToEstrella; // Fijamos la estrella
    Write(fsal, #9, FloatToStrF(globs.CF.costoEstrella(globs.kPaso_ + 1)
      , ffGeneral, 6, 2));
    for k := 0 to high(fsal_opt) do
    begin
      TActor(Self.lst_opt_PrintResultados.items[k]).PosicionarseEnEstrellita;
      TActor(Self.lst_opt_PrintResultados.items[k]).prepararPaso_ps;
      TActor(Self.lst_opt_PrintResultados.items[k]).opt_PrintResultados_Encab(
        fsal_opt[k]);
    end;
    NoFinBarridoEstrellas := globs.CF.incEstrella;
  end;// barrido estrellas
  writeln(fsal);
  for k := 0 to high(fsal_opt) do
    writeln(fsal_opt[k]);
end;

procedure TsalaDeJuego.escribirPasoOptRes(var fsal: TextFile;
  var fsal_opt: TDAOfTextFile);
var
  k: integer;
  NoFinBarridoEstrellas: boolean;
begin
  writeln(fsal, IntToStr(globs.kPaso_) + #9 + globs.FechaInicioDelpaso.AsISOStr,
    #9, TDAOfNRealToTabbedString(globs.cf.constelacion.fcosto[globs.kpaso_], 6, 2));

  if escribirOptActores then
  begin
    for k := 0 to high(fsal_opt) do
      Write(fsal_opt[k], IntToStr(globs.kPaso_) + #9 +
        globs.FechaInicioDelpaso.AsISOStr);
    vclear(globs.CF.estrella_kr);
    vclear(globs.CF.estrella_kd);
    NoFinBarridoEstrellas := True;
    while NoFinBarridoEstrellas do
    begin
      globs.CF.SetEstadoToEstrella;
      barridoFijarEstadoDeActoresYFuentesToEstrellaOpt;
      for k := 0 to high(fsal_opt) do
      begin
        TActor(Self.lst_opt_PrintResultados.items[k]).PosicionarseEnEstrellita;
        TActor(Self.lst_opt_PrintResultados.items[k]).prepararPaso_ps;
        TActor(Self.lst_opt_PrintResultados.items[k]).opt_PrintResultados(fsal_opt[k]);
      end;
      NoFinBarridoEstrellas := globs.CF.incEstrella;
    end;
    for k := 0 to high(fsal_opt) do
      writeln(fsal_opt[k]);
  end;
end;

procedure TsalaDeJuego.cerrarArchisOptRes(var fsal: TextFile;
  var fsal_opt: TDAOfTextFile; var fsalopen: boolean);
var
  k: integer;
begin
  if fsalopen then
  begin
    for k := 0 to high(fsal_opt) do
      closeFile(fsal_opt[k]);
    if fsalopen then
      closeFile(fsal);
  end;
end;

procedure TSalaDeJuego.printEncabezadoSimRes(var f: TextFile);
var
  iposteres, kactorres: integer;
begin
  system.writeln(f, 'Inicio simulación: ', #9, DateTimeToIsoStr(now()));
  system.Writeln(f, 'FechaIniSim: ', #9, globs.fechaIniSim.AsISOStr,
    #9, 'FechaFinSim: ', #9, globs.fechaFinSim.AsISOStr);
  system.writeln(f, 'NCronicas:', #9, globs.NCronicasSim);
  system.writeln(f, 'NPasos:', #9, globs.nPasos);
  system.writeln(f, 'NPostes:', #9, length(globs.Durpos));
  system.Write(f, 'DurPos[h]:');
  for iposteres := 0 to high(globs.DurPos) do
    Write(f, #9, globs.DurPos[iposteres]: 6: 0);
  system.writeln(f);
  system.writeln(f, 'NActores:', #9, length(actores));
  TActor.printEncabezadoResumenSim(f);
  for kactorres := 0 to high(actores) do
    actores[kactorres].printResumenSim(f);
end;

procedure TSalaDeJuego.printFinalSimRes(var f: TextFile);
begin
  system.writeln(f, 'Fin simulación: ', #9, DateTimeToStr(now()));
end;

procedure TSalaDeJuego.inicializarVariablesMultiCore(darPasoAuto: boolean);
var
  i, nIters: integer;
  robot: TRobotOptimizadorMultiCore;
begin

  nIters := globs.NMAX_ITERACIONESDELPASO_OPT;



  if not forzarNumeroDeHilos then
    nHilos := uRobotOptimizadorMultiCore.getNHilos(nCores,
      globs.CF.nEstrellasPorPuntoT, globs.NCronicasOpt, nIters);

  globs.Alerta('Optimización multihilo, Nucleos: ' + IntToStr(nCores) +
    ', Hilos: ' + IntToStr(nHilos));
  globs.deshabilitarAlertas;
  //Para que se asignen las estrellas en el primer paso
  oldEstrellaIni := -1;
  oldEstrellaFin := -1;
  pasosAutomaticos := darPasoAuto;

  if nHilos > 1 then
  begin
    //Inicializo variables de sincronizacion
    //Usaremos ev paso completo para que los robots avisen que se terminaron de
    //construir. En particular nos interesa saber que los TEventRX de los robots
    //estan creados para poder crear los TEventTX para despertarlos
    cntCalculando := nHilos - 1;
  {$IFDEF WINDOWS}
    semCntCalculando := TMutex.Create(nomSemCntCalculando);
    evPasoCompleto := TEvent.Create(nomEvPasoCompleto, False);
  {$ELSE}
    if not FileExists(nomSemCntCalculando) then
      CrearArchiKey(nomSemCntCalculando);
    if not FileExists(nomEvPasoCompleto) then
      CrearArchiKey(nomEvPasoCompleto);
    if not FileExists(nombreEvHiloProximoPaso) then
      CrearArchiKey(nombreEvHiloProximoPaso);

    semCntCalculando := TMutex.Create(nomSemCntCalculando, 1);
    evPasoCompleto := TEventRX.Create(nomEvPasoCompleto, 1);
  {$ENDIF}

    listaOptimizadores := TList.Create;
    listaOptimizadores.Capacity := nHilos - 1;
    listaEventos := TList.Create;
    listaEventos.Capacity := listaOptimizadores.Capacity;
    //el -2 es porque un hilo va a ser el principal.
    //En la lista van a quedar nHilos -1 robots y va a estar tambien el hilo principal
    for i := 0 to nHilos - 2 do
    begin
      robot := TRobotOptimizadorMultiCore.Create(self, globs.CF.constelacion.fCosto,
        darPasoAuto, i);
      listaOptimizadores.Add(robot);
    end;

    if evPasoCompleto.Wait(60000) then  // espero a que se creen los robots
    begin
      //Se hace en dos pasadas porque los eventos RX deben estar creados antes que los
      //eventos TX
      for i := 0 to nHilos - 2 do
      begin
        robot := listaOptimizadores[i];
    {$IFDEF WINDOWS}
        listaEventos.Add(TEvent.Create(nombreEvHiloProximoPaso +
          IntToStr(robot.iRobotEnLaLista), False));
    {$ELSE}
        listaEventos.Add(TEventTX.Create(nombreEvHiloProximoPaso,
          robot.iRobotEnLaLista));
    {$ENDIF}
      end;
    end
    else
      globs.abortarSim := True;
  end
  else
  begin
    listaOptimizadores := nil;
    listaEventos := nil;
    semCntCalculando := nil;
    evPasoCompleto := nil;
  end;
  globs.habilitarAlertas;
end;

procedure TSalaDeJuego.liberarVariablesMultiCore;
var
  i: integer;
begin
  if listaOptimizadores <> nil then
  begin
    for i := 0 to listaOptimizadores.Count - 1 do
      TRobotOptimizadorMultiCore(listaOptimizadores[i]).Terminate;

    if listaEventos <> nil then
    begin
      for i := 0 to listaEventos.Count - 1 do
      begin
    {$IFDEF WINDOWS}
        TEvent(listaEventos[i]).Signal;
        TEvent(listaEventos[i]).Free;
    {$ELSE}
        TEventTX(listaEventos[i]).Signal;
        TEventTX(listaEventos[i]).Free;
    {$ENDIF}
        TRobotOptimizadorMultiCore(listaOptimizadores[i]).WaitFor;
        TRobotOptimizadorMultiCore(listaOptimizadores[i]).Free;
      end;
      listaEventos.Free;
    end;
    listaOptimizadores.Free;
  end;
  if semCntCalculando <> nil then
    semCntCalculando.Free;
  if evPasoCompleto <> nil then
    evPasoCompleto.Free;
(*
{$IFNDEF WINDOWS}
  if FileExists(nomSemCntCalculando) then
    DeleteFile(nomSemCntCalculando);
  if FileExists(nomEvPasoCompleto) then
    DeleteFile(nomEvPasoCompleto);
  if FileExists(nombreEvHiloProximoPaso) then
    DeleteFile(nombreEvHiloProximoPaso);
{$ENDIF}
*)
end;

procedure TSalaDeJuego.asignarEstrellas(estrellaIni, estrellaFin: integer);
var
  i, iEstrIni, iEstrFin: integer;
  robot: TRobotOptimizadorMultiCore;
  nEstrellas, nEstrellasPorRobot, nRobotsConUnaEstrellaMas: integer;
begin
  nEstrellas := estrellaFin - estrellaIni + 1;
  nEstrellasPorRobot := nEstrellas div nHilos;
  nRobotsConUnaEstrellaMas := nEstrellas mod nHilos;

  self.estrellaIniCalc := estrellaIni;
  self.estrellaFinCalc := self.estrellaIniCalc + nEstrellasPorRobot - 1;

  iEstrFin := self.estrellaFinCalc;
  //el -2 es porque un hilo va a ser el principal.
  //En la lista van a quedar nHilos -1 robots y va a estar tambien el hilo principal
  for i := 0 to nRobotsConUnaEstrellaMas - 1 do
  begin
    iEstrIni := iEstrFin + 1;
    iEstrFin := iEstrIni + nEstrellasPorRobot;

    robot := listaOptimizadores[i];
    robot.estrellaIni := iEstrIni;
    robot.estrellaFin := iEstrFin;
  end;

  for i := nRobotsConUnaEstrellaMas to nHilos - 2 do
  begin
    iEstrIni := iEstrFin + 1;
    iEstrFin := iEstrIni + nEstrellasPorRobot - 1;

    robot := listaOptimizadores[i];
    robot.estrellaIni := iEstrIni;
    robot.estrellaFin := iEstrFin;
  end;
end;

procedure TSalaDeJuego.OptimizarMultiCore;
begin
  try
    inicializarOptimizacion(nil);
    if globs.EstadoDeLaSala = CES_OPTIMIZANDO then
    begin
      inicializarVariablesMultiCore(True);
      //      asignarSemillasIniciales(globs.sorteadorUniforme.RandSeed);

      globs.procNot(globs.procNot_opt_InicioOptimizacion);
      while (globs.kPaso_ > 0) and (not globs.abortarSim) do
      begin
        calcularRangoEstrellasMultiCore(0, globs.CF.nEstrellasPorPuntoT - 1);
        darPaso;
        globs.procNot(globs.procNot_opt_FinCalculosDeEtapa);
      end;
      FinOptimizacion;
    end;
    if not globs.abortarSim then
    begin
      globs.EstadoDeLaSala := CES_OPTIMIZACION_TERMINADA;
    end
    else
      globs.EstadoDeLaSala := CES_OPTIMIZACION_ABORTADA;
    liberarVariablesMultiCore;
  except
    globs.EstadoDeLaSala := CES_OPTIMIZACION_ABORTADA;
    globs.abortarSim := True;
    liberarVariablesMultiCore;
    raise;
  end;
end;

procedure TSalaDeJuego.guardarResultadosOpt(dir: string);
var
  archi: string;
  fsal: TextFile;
  costosFuturosDelPaso: TDAofNReal;
  i, k: integer;
  nPasos: integer;
  linea: string;
begin
  if dir[Length(dir)] = DirectorySeparator then
  begin
    if globs.SortearOpt then
      archi := dir + 'optres_' + IntToStr(RandSeedInicial) + 'x' +
        IntToStr(globs.NCronicasOpt) + '.xlt'
    else
      archi := dir + 'optres_VE.xlt';
  end
  else
  begin
    if globs.SortearOpt then
      archi := dir + DirectorySeparator + 'optres_' + IntToStr(RandSeedInicial) +
        'x' + IntToStr(globs.NCronicasOpt) + 'Sorteos.xlt'
    else
      archi := dir + DirectorySeparator + 'optres_VE.xlt';
  end;

  globs.Alerta('Escribiendo resultados en: ' + archi);
  assignfile(fsal, archi);
  rewrite(fsal);
  try
    writeln(fsal, 'fActPaso: ', #9, FloatToStrF(globs.fActPaso, ffGeneral, 12, 10));
    globs.CF.constelacion.PrintDefsToText(fsal, True);
    nPasos := globs.calcNPasosOpt;

    Write(fsal, 'paso\estado' + #9 + 'Fecha');
    for k := 1 to globs.CF.nEstrellasPorPuntoT do
      Write(fsal, #9, k);
    writeln(fsal);

    for i := 0 to nPasos do
    begin
      globs.Fijar_kPaso(nPasos - (i - 1));
      costosFuturosDelPaso := globs.CF.constelacion.fCosto[nPasos - (i - 1)];
      linea := IntToStr(globs.kpaso_) + #9 + globs.FechaInicioDelpaso.AsISOStr +
        #9 + FloatToStrF(costosFuturosDelPaso[0], ffGeneral, 6, 2);
      for k := 1 to high(costosFuturosDelPaso) do
        linea := linea + #9 + FloatToStrF(costosFuturosDelPaso[k], ffGeneral, 6, 2);
      writeln(fsal, linea);
    end;
    globs.CF.StoreInArchi(dir + DirectorySeparator + 'CF.bin');
  finally
    CloseFile(fsal);
  end;
end;

{$IFDEF ESTABILIZAR_FRAMEINICIAL}
procedure TSalaDeJuego.EstabilizarFrameInicial;
const
  MAX_CANT_ITERS = 100;
var
  NoFinBarridoEstrellas: boolean;

  //cociente sera costok+1/costok
  cociente: NReal;
  maxCociente: NReal;
  CostoDelPaso_Esperado, CostoFuturo_Esperado, CostoDelPaso_Simplex, CF_Base: NReal;
  cntIters: integer;
  stop: boolean;
begin
  cntIters := 0;
  stop := False;
  globs.kCronica := 0;
  while (not stop) and (cntIters < MAX_CANT_ITERS) do
  begin
    maxCociente := -MaxNReal;
    //PosicionarseEnPrimerEstrella
    vclear(globs.CF.estrella_kr);
    vclear(globs.CF.estrella_kd);
    NoFinBarridoEstrellas := True;

    PrepararPaso_PreSorteo;
    SorteosDelPasoOpt(False);
    while NoFinBarridoEstrellas do
    begin
      // fijamos el estado
      globs.CF.SetEstadoToEstrella;
      barridoFijarEstadoDeActoresYFuentesToEstrellaOpt;// Fijamos el estado en los actores

      CF_Base := globs.CF.costoEstrella(globs.kPaso_ + 1);


      globs.cntIteracionesDelPaso := 0;
      repeat
        Inc(globs.cntIteracionesDelPaso);

        PrepararPaso_PostSorteo;
        CostoDelPaso_Simplex := ResolverPaso;

      until (globs.cntIteracionesDelPaso >= NMAX_ITERACIONESDELPASO) or
        (not NecesitoIterar);

      EvolucionarEstado;


      CostoFuturo_Esperado := CostoDelPaso_Esperado + globs.fActPaso * CF_Base;

      globs.CF.SetCostoEstrella(globs.kPaso_, CostoFuturo_Esperado);
      Actores_SetAuxs1;
      globs.SwapAuxs;
{$IFDEF SPXMEJORCAMINO}
      vswap(spx.mejorCaminoEsperado, spx.mejorCaminoEncontrado);
{$ENDIF}
      NoFinBarridoEstrellas := globs.CF.incEstrella;

      if CF_Base <> 0 then
        cociente := CostoFuturo_Esperado / CF_Base
      else
        cociente := MaxNReal;

      if cociente > maxCociente then
        maxCociente := cociente;
    end;// barrido estrellas

    vswap(globs.CF.constelacion.fCosto[globs.kPaso_],
      globs.CF.constelacion.fCosto[globs.kPaso_ + 1]);
    if (maxCociente < 1.05) and (maxCociente > 0.95) then
      stop := True;
    Inc(cntIters);
  end;
  if not stop then
    globs.Alerta(Self.Nombre + ': Estabilizar frame inicial no convergio');
end;

{$ENDIF}

procedure AlInicio;
begin
  registrarClaseDeCosa(TSalaDeJuego.ClassName, TSalaDeJuego);
end;

procedure AlFinal;
begin
end;

end.
