// -----------------------------------------------------------------------
// Copyright 1999 Manuel de la Herrán Gascón
// mherran@aircenter.net Madrid (Spain).
// http://www.aircenter.net/gaia
// -----------------------------------------------------------------------
// Este código han sido creado originariamente por Manuel de la Herrán Gascón
// -----------------------------------------------------------------------
// Otras contribuciones / Contributed by
// (Tu nombre aquí / Your name here)
// -----------------------------------------------------------------------
// DO NOT REMOVE THIS COPYRIGHT LINES!
// You can copy and paste this code in your own web pages, if you keep this lines.
// You can modify, distribute and sell this code, but keep the copyright lines
// and add your name in "Contributed by". If you are interested in just some 
// special procedures, copy them, and you don't have to keep this lines.
// ¡NO QUITAR ESTAS LINEAS DE COPYRIGHT!
// Puedes copiar y pegar este código en tus propias páginas web, pero manten estas lineas.
// Puedes modificar, distribuir y vender este código, manteniendo las lineas de
// copyright e incluyendo tu nombre en "Otras contribuciones". Si sólo estás
// interesando en unas rutinas, copialas y no hace falta que mantengas estas lineas.
// -----------------------------------------------------------------------
// This program is provided "as is" without any express or implied warranties.
// The author assumes no responsibility for errors, omissions or damages
// resulting from the use of it.
// Este programa se distribuye tal cual, si ninguna garantía implícita o explícita
// El autor no asume ninguna responsabilidad por errores, omisiones o daños
// provocados por su uso
// -----------------------------------------------------------------------
  // --------------------------------------------------------------
  function updateInitialWorldStatus() {
  // Inicializo todo y actualizo el estado inicial del mundo, mostrando agentes
  var currentCell;
  var found;
  for (var row = 1; row <= totalRows; row++) {
    for (var col = 1; col <= totalCols; col++) {
      // Busco a ver si en esa celda existe algún agente
      found=false;
      ag=firstAgentInRowCol(row,col);
      // Si encuentro un agente y es pajaro o hombre
      if (ag != 0 && (aliveAgentsK[ag] == 2 || aliveAgentsK[ag] == 3))
        found=true;
      currentCell=(row-1)*totalCols+col;
      if (mapShape[currentCell] == 1 || mapShape[currentCell] == 2 || mapShape[currentCell] == 3 || mapShape[currentCell] == 4) {
        if (found)
          if (aliveAgentsK[ag] == 2) {
            document.images[currentCell].src = birdFileName[aliveAgentsD[ag]];
            document.images[totalCells+currentCell].src = birdFileName[aliveAgentsD[ag]];
            }
          else {
            document.images[currentCell].src = humanFileName[aliveAgentsD[ag]];
            document.images[totalCells+currentCell].src = humanFileName[aliveAgentsD[ag]];
            }
        else {
          document.images[currentCell].src = spaceFileName[imageOfNumberOfWorms(numberOfWorms[currentCell])];
          document.images[totalCells+currentCell].src = spaceFileName[mapShape[currentCell]];
          }
        }
      else {
        document.images[currentCell].src = spaceFileName[0]; //Error
        document.images[totalCells+currentCell].src = spaceFileName[0]; //Error
        }
      }
    }
  }
  // --------------------------------------------------------------
  function imageOfNumberOfWorms(numberOfWorms) {
    if (numberOfWorms>5) {
      if (numberOfWorms>10) 
        return 4;
      else 
        return 3;
      }
    else {
      if (numberOfWorms>2) 
        return 2;
      else 
        return 1;
      }
  }
  // --------------------------------------------------------------
  function showInitialWorldStatus() {
  // Muestro el estado inicial del mundo sin agentes
  var currentCell;
  var found;
  for (var row = 1; row <= totalRows; row++) {
    for (var col = 1; col <= totalCols; col++) {
      currentCell=(row-1)*totalCols+col;
      document.write('<img width=8 height=8 src="',spaceFileName[imageOfNumberOfWorms(numberOfWorms[currentCell])],'">');
      }
    document.write('<br>');
    }
  document.write('<p>');
  for (var row = 1; row <= totalRows; row++) {
    for (var col = 1; col <= totalCols; col++) {
      currentCell=(row-1)*totalCols+col;
      if (mapShape[currentCell] == 1 || mapShape[currentCell] == 2 || mapShape[currentCell] == 3 || mapShape[currentCell] == 4)
        document.write('<img width=8 height=8 src="',spaceFileName[mapShape[currentCell]],'">');
      else
        document.write('<img width=8 height=8 src="',spaceFileName[0],'">'); //Error
      }
    document.write('<br>');
    }
  }
  // --------------------------------------------------------------
  function closeWindows() {
    if (resultWindowIsVisible && !resultWindow.closed) {
      resultWindow.close();
      resultWindowIsVisible=false;
    }
    if (descriptionWindowIsVisible && !descriptionWindow.closed) {
      descriptionWindow.close();
      descriptionWindowIsVisible=false;
    }
  }
  // --------------------------------------------------------------
  function countAgentsInCells(neighboringIndexes,numberOfCells) {
    var ret=0;
    for (var ag = 1; ag <= totalAgents; ag++) {
      if (aliveAgentsK[ag] == 2)
        for (var cell = 1; cell <= numberOfCells; cell++) {
          if (aliveAgentsM[ag]==neighboringIndexes[cell])
            ret++;
        }
    }
    return ret;
  }
  // --------------------------------------------------------------
  function firstAgentInRowCol(row,col) {
    for (var ag = 1; ag <= totalAgents; ag++) {
      if (aliveAgentsR[ag]==row && aliveAgentsC[ag]==col) {
        return ag;
        break;
        }
      }
    return 0;
  }
  // --------------------------------------------------------------
  function updateMapStatus() {
    for (var cell = 1; cell <= totalCells; cell++) {
      // Muestro el estado del mapa en funcion del nivel de gusanos
      // Se podría probar a solo actualizar las celdas que han cambiado, guardando el estado anterior
      // para incrementar la velocidad
      document.images[cell].src = spaceFileName[imageOfNumberOfWorms(numberOfWorms[cell])];
      // Muestro el estado del mapa en funcion de los tipos de espacio
      document.images[totalCells+cell].src = spaceFileName[mapShape[cell]];
    }
  }
  // --------------------------------------------------------------
  function getNeighborsIndexes(currentCell,neighboringIndexes) {
    neighboringIndexes[1]=sumCirc2(totalCells,currentCell,-totalCols); //Norte
    neighboringIndexes[2]=sumCirc2(totalCells,currentCell,-totalCols+1); //NorEste
    neighboringIndexes[3]=sumCirc2(totalCells,currentCell,1); //Este
    neighboringIndexes[4]=sumCirc2(totalCells,currentCell,totalCols+1); //SurEste
    neighboringIndexes[5]=sumCirc2(totalCells,currentCell,totalCols); //Sur
    neighboringIndexes[6]=sumCirc2(totalCells,currentCell,totalCols-1); //SurOeste
    neighboringIndexes[7]=sumCirc2(totalCells,currentCell,-1); //Oeste
    neighboringIndexes[8]=sumCirc2(totalCells,currentCell,-totalCols-1); //NorOeste
    neighboringIndexes[9]=currentCell; //Actual
  }
  // --------------------------------------------------------------
  function eatNeighboringWorms(ag,neighboringIndexes) {
    var ret=0;
    // Por cada vecino (¿No tendria que ser solo en el actual?)?????????????????
    for (var neighbor = 1; neighbor <= numberOfNeighbors; neighbor++) {
      // Si hay algo de comida
      if (numberOfWorms[neighboringIndexes[neighbor]]>0) {
        // Cada vez intenta comer al menos 2 unidades de gusano
        if (numberOfWorms[neighboringIndexes[neighbor]]>2) {
          numberOfWorms[neighboringIndexes[neighbor]]-=2;
          ret+=2;
        } else {
          ret+=numberOfWorms[neighboringIndexes[neighbor]];
          numberOfWorms[neighboringIndexes[neighbor]]=0; }
      }  
    }
    return ret;
  }
  // --------------------------------------------------------------
  function lookNeighbors(ag,currentCell,shapesArroundMe) {
    var neighboringIndexes = new Array(numberOfNeighbors);
    getNeighborsIndexes(currentCell,neighboringIndexes);
    for (var neighbor = 1; neighbor <= numberOfNeighbors; neighbor++) {
      shapesArroundMe[neighbor]=mapShape[neighboringIndexes[neighbor]]
    }
  }
  // --------------------------------------------------------------
  function seeNeighbors(ag,currentCell,currentCellR,currentCellC,totalVH,totalVP) {
    var neighboringIndexes = new Array(numberOfNeighbors);
    var tempNumberOfNeighboringAgents = new Array(numberOfNeighbors);
    getNeighborsIndexes(currentCell,neighboringIndexes);
    for (var neighbor = 1; neighbor <= numberOfNeighbors; neighbor++) {
      tempNumberOfNeighboringAgents[neighbor]=countAgentsInCells(neighboringIndexes,numberOfNeighbors);
      totalVH+=tempNumberOfNeighboringAgents[neighbor];
      totalVP+=numberOfWorms[currentCell];
    }
    aliveAgentsE[ag]+=eatNeighboringWorms(ag,neighboringIndexes);
  }
  // --------------------------------------------------------------
  function changeHumanDirection(currentD,shapesArroundMe) {
    var ret=currentD;
    // 1-Blanco=Vacío 2-Gris Claro=Sendero 3-Gris Oscuro=Arbusto 4-Negro=Obstáculo
    order = randDistDiscUnif1Ub1(2);
    if (order == 1) {
      if ((shapesArroundMe[sumCirc2(numberOfDirections,aliveAgentsD[ag],-1)] == 2) || (shapesArroundMe[sumCirc2(numberOfDirections,aliveAgentsD[ag],-2)] == 2))
        ret=sumCirc2(numberOfDirections,aliveAgentsD[ag],-1); }
    else {
      if ((shapesArroundMe[sumCirc2(numberOfDirections,aliveAgentsD[ag],1)] == 2) || (shapesArroundMe[sumCirc2(numberOfDirections,aliveAgentsD[ag],2)] == 2))
        ret=sumCirc2(numberOfDirections,aliveAgentsD[ag],1); }
    return ret;
  }
  // --------------------------------------------------------------
  function executeWorm(ag) {
    // Los gusanos se interpretan como un único gran agente que ocupa todas las celdas
    // Los gusanos crecen siempre
    for (var cell = 1; cell <= totalCells; cell++)
      if (numberOfWorms[cell]<5)
        numberOfWorms[cell]+=0.1;
    updateMapStatus();
  }
  // --------------------------------------------------------------
  function executeBird(ag) {
    var currentCell;
    var action;
    var totalVH;
    var totalVP;
    // Por cada agente-pájaro vivo
    currentCell=(aliveAgentsR[ag]-1)*totalCols+aliveAgentsC[ag];
    aliveAgentsE[ag]-=0.1;
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    // El bicho observa sus 9 vecinos y come 1 unidad de comida de todas las plantas vecinas
    seeNeighbors(ag,currentCell,aliveAgentsR[ag],aliveAgentsC[ag],totalVH,totalVP);
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    // El bicho elige una acción
    action = randDistDiscUnif1Ub1(5);
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    // Si ha elegido cambio de dirección, se cambia
    if (action == 1)
      aliveAgentsD[ag]=randDistDiscUnif1Ub1(numberOfDirections);
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    // Borro el bicho de su posición vieja pintando el mapa
      // Muestro el estado del mapa en funcion del nivel de gusanos
      document.images[currentCell].src = spaceFileName[imageOfNumberOfWorms(numberOfWorms[currentCell])];
      // Muestro el estado del mapa en funcion de los tipos de espacio
      document.images[totalCells+currentCell].src = spaceFileName[mapShape[currentCell]];
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    // Detecto la nueva posición en el sentido actual y avanzo de frente a esa posición
    aliveAgentsR[ag]=calculateNewRowLocation8D(aliveAgentsD[ag],aliveAgentsR[ag],totalRows);
    aliveAgentsC[ag]=calculateNewColLocation8D(aliveAgentsD[ag],aliveAgentsC[ag],totalCols);
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    // Muevo el bicho a la nueva posición
    // Los bichos sí se pueden superponer
    aliveAgentsM[ag]=(aliveAgentsR[ag]-1)*totalCols+aliveAgentsC[ag];
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    // Pinto el bicho en su nueva posición
    document.images[aliveAgentsM[ag]].src = birdFileName[aliveAgentsD[ag]];
    document.images[totalCells+aliveAgentsM[ag]].src = birdFileName[aliveAgentsD[ag]];
  }
  // --------------------------------------------------------------
  function executeHuman(ag) {
    var directionType;
    var newDirection;
    // Por cada agente-humano vivo
    currentCell=(aliveAgentsR[ag]-1)*totalCols+aliveAgentsC[ag];
    var shapesArroundMe = new Array(numberOfNeighbors);
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    // El humano observa sus 9 vecinos y elige una nueva posición, teniendo en cuenta:
    // * Gran tendencia a mantener la dirección vieja
    // * Gran tendencia a seguir senderos
    // * Gran tendencia a hacer la media de estas dos
    // Elijo a priori una posible dirección alternativa
    newDirection=changeHumanDirection(aliveAgentsD[ag],shapesArroundMe)
    // El humano elige una dirección de esas tres con identica probabilidad
    directionType = randDistDiscUnif1Ub1(3);
    switch (directionType){
       case 1 : 
          aliveAgentsD[ag]=aliveAgentsD[ag];
          break;
       case 2 : 
          aliveAgentsD[ag]=newDirection;
          break;
       case 3 : 
          aliveAgentsD[ag]=newDirection;
          break;
       default :
          alert('ERRORS CONTROL: Error: ');
    }
    lookNeighbors(ag,currentCell,shapesArroundMe); //Calculo las formas del espacio a mi alrrededor
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    // Detecto la nueva posición en la nueva dirección elegida y avanzo de frente a esa posición
    aliveAgentsR[ag]=calculateNewRowLocation8D(aliveAgentsD[ag],aliveAgentsR[ag],totalRows);
    aliveAgentsC[ag]=calculateNewColLocation8D(aliveAgentsD[ag],aliveAgentsC[ag],totalCols);
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    aliveAgentsM[ag]=(aliveAgentsR[ag]-1)*totalCols+aliveAgentsC[ag];
    // Pinto el bicho en su nueva posición
    document.images[aliveAgentsM[ag]].src = humanFileName[aliveAgentsD[ag]];
    document.images[totalCells+aliveAgentsM[ag]].src = humanFileName[aliveAgentsD[ag]];
  }
  // --------------------------------------------------------------
  function executeAutomata() {
    var totalEnergy=0;
    // Carpe diem, tempus fugit
    currentTime++;
    // Actualizo la información de energía total del sistema
    for (var ag = 1; ag <= totalAgents; ag++)
      totalEnergy+=aliveAgentsE[ag];
    resultWindow.document.writeln(currentTime.toString()+';'+totalAgents.toString()+';'+totalEnergy.toString()+'<br>');
    // Ejecuto los agentes
    // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 
    // 1=Gusanos 2=Pajaros 3=Hombres
    for (var ag = 1; ag <= totalAgents; ag++) {
      switch (aliveAgentsK[ag]){
         case 1 : 
            //alert('DEBUG: ejecutando gusano '+ag.toString());
            executeWorm(ag);
            break;
         case 2 : 
            //alert('DEBUG: ejecutando pájaro '+ag.toString());
            executeBird(ag);
            break;
         case 3 : 
            //alert('DEBUG: ejecutando humano '+ag.toString());
            executeHuman(ag);
            break;
         default :
            alert('ERRORS CONTROL: Error: Agente '+ag.toString()+' es del tipo '+aliveAgentsK[ag].toString());
      }
    }
  }
  // --------------------------------------------------------------
  function loadMapFromShape(mapShapeData) {
    // Cargo el temporal mapShapeData en el mapa real mapShape
    for (var row = 1; row <= totalRows; row++)
      for (var col = 1; col <= totalCols; col++)
        mapShape[(row-1)*totalCols+col]=mapShapeData[row].substr(col,1);
  }
  // --------------------------------------------------------------
  function initWorld() {
    var distance;
    // Inicializo las celdas
    for (var cell = 1; cell <= totalCells; cell++) {
      numberOfWorms[cell]=2;
      distance=distanceCellToRowCol(cell,4,6);
      if (distance<6) {
        numberOfWorms[cell]+=10+6-distance;
      }
      distance=distanceCellToRowCol(cell,10,8);
      if (distance<6) {
        numberOfWorms[cell]+=10+6-distance;
      }
    }
    // 1-Blanco=Vacío 2-Gris Claro=Sendero 3-Gris Oscuro=Arbusto 4-Negro=Obstáculo
    var mapShapeData = new Array(totalRows);
    // El último mapa es el activo: 5
    mapShapeData[01]="Z33332";
    mapShapeData[02]="Z33332";
    mapShapeData[03]="Z33333";
    mapShapeData[04]="Z33333";
    mapShapeData[05]="Z23333";
    mapShapeData[06]="Z22233";
    mapShapeData[07]="Z32222";
    mapShapeData[08]="Z33444";
    mapShapeData[09]="Z33333";
    mapShapeData[10]="Z33333";
    mapShapeData[11]="Z33333";
    mapShapeData[12]="Z33333";
    mapShapeData[13]="Z33333";
    mapShapeData[14]="Z33333";
    mapShapeData[15]="Z33332";
    // El último mapa es el activo: 20
    mapShapeData[01]="Z33332333333334333333";
    mapShapeData[02]="Z33332233333114333333";
    mapShapeData[03]="Z33333223331111443333";
    mapShapeData[04]="Z33333332211111114433";
    mapShapeData[05]="Z23333333222222222222";
    mapShapeData[06]="Z22233222222222233433";
    mapShapeData[07]="Z32222223333333233433";
    mapShapeData[08]="Z33444333333332333433";
    mapShapeData[09]="Z33333333332223333433";
    mapShapeData[10]="Z33333333323333333433";
    mapShapeData[11]="Z33333333233333333343";
    mapShapeData[12]="Z33333333233333333333";
    mapShapeData[13]="Z33333322333333333333";
    mapShapeData[14]="Z33333223333333333333";
    mapShapeData[15]="Z33332333333333333333";
    // El último mapa es el activo: 40
    mapShapeData[01]="Z3333233333333433333333333333333333333333";
    mapShapeData[02]="Z3333223333311433333333333333333333333333";
    mapShapeData[03]="Z3333322333111144333333333333333333333333";
    mapShapeData[04]="Z3333333221111111443333333333333333333333";
    mapShapeData[05]="Z2333333322222222222233333333333333333333";
    mapShapeData[06]="Z2223322222222223343333333333333333333333";
    mapShapeData[07]="Z3222222333333323343333333333222333333333";
    mapShapeData[08]="Z3344433333333233343333333333332333333333";
    mapShapeData[09]="Z3333333333222333343333333333332333333333";
    mapShapeData[10]="Z3333333332333333343333333333332333333333";
    mapShapeData[11]="Z3333333323333333334333333333332333333333";
    mapShapeData[12]="Z3333333323333333333333333333333333333333";
    mapShapeData[13]="Z3333332233333333333333333333333333333333";
    mapShapeData[14]="Z3333322333333333333333333333333333333333";
    mapShapeData[15]="Z3333233333333333333333333333333333333333";

    // Cargo el temporal mapShapeData en el mapa real mapShape
    loadMapFromShape(mapShapeData);

    // Inicializo los agentes vivos
    // alert('DEBUG: Comienza una ejecución con '+totalWorms.toString()+' gusanos, '+totalBirds.toString()+' pájaros, y '+totalHumans.toString()+' hombres durante '+totalTimes.toString()+' ciclos. En total son '+totalAgents.toString()+' agentes.');
    // 1=Gusanos___________________________________________________
    aliveAgentsK[1]=1; 
    aliveAgentsE[1]=0;
    // 2=Pajaros___________________________________________________
    for (var ag = 2; ag <= totalBirds+1; ag++) {
      aliveAgentsK[ag]=2;
      aliveAgentsR[ag]=randDistDiscUnif1Ub1(totalRows);
      aliveAgentsC[ag]=randDistDiscUnif1Ub1(totalCols);
      aliveAgentsD[ag]=randDistDiscUnif1Ub1(numberOfDirections);
      aliveAgentsE[ag]=0;
    }
    // 3=Hombres___________________________________________________
    for (var ag = totalBirds+2; ag <= totalAgents; ag++) {
      aliveAgentsK[ag]=3;
      aliveAgentsR[ag]=randDistDiscUnif1Ub1(totalRows);
      aliveAgentsC[ag]=randDistDiscUnif1Ub1(totalCols);
      aliveAgentsD[ag]=randDistDiscUnif1Ub1(numberOfDirections);
      aliveAgentsE[ag]=0;
    }
  }
  // --------------------------------------------------------------
  function showDescWindow() {
    // Muestro una ventana informativa
    descriptionWindow=window.open("px0014b.htm",'descriptionWindow',"scrollbars=yes,dependent,width=600,height=400,menubar=no");
    descriptionWindow.moveTo(0,10);
    descriptionWindowIsVisible=true;
  }
  // --------------------------------------------------------------
  function distanceCellToRowCol(cell,row,col) {
    var acol=(((cell-1)+totalCols)%totalCols)+1;
    var arow=(cell-acol)/totalCols+1;
    return Math.ceil(dist2ptos2d(acol,arow,col,row));
  }

