program circusim;





uses crt;

Const
  SizeX=27;
  SizeY=20;
  PosX=40-(SizeX div 2);
  PosY=25-(SizeY div 2);
  PosCells: array [0..8] of char = ' Û-+ÏÁ*' ;
  CellColor: array[-2..1] of byte = (Red,Blue,LightGray,Yellow);
type
  PosDevs=(Nothing,Wire,Ground,Source,
    PGateOpen,PGateClose,NGateOpen,NGateClose,Shit);

  SetOfDevs=set of posdevs;
  MapedCellRec = record
    Dev:posDevs;
    State:integer;
    end;
  ArrOfNeighbourgs=array[0..3] of integer;
  CompiledCellRec=Record
    x,y,color,state,NofNeighbours:integer;
    done:boolean;
    Neighbour: ArrOfNeighbourgs;
    dev:posdevs;
    end;
  carray=array[0..SizeY*SizeX-1] of integer;


var
  MapedCell:array[0..SizeY-1,0..SizeX-1] of MapedCellRec;
  CompiledCell:array[0..SizeY*SizeX-1] of CompiledCellRec;
  NofCompiledCells:integer;
  Conexion,temperature,Gate,Master:carray;
  NofConexions,NOfGates:integer;
  beeps:boolean;
  SeeTemperature:boolean;

procedure DrawCell(y,x:integer);
Const
  Devs: array [0..8] of char = ' ±-+ÏÁ*';
begin
  Textcolor(CellColor[MapedCell[y,x].state]);
  gotoxy(x+PosX,y+PosY);
  write(Devs[ord(MapedCell[y,x].Dev)]);
  end;


procedure DrawColoredCell(p:integer);
Const
  Devs: array [0..8] of char = ' Û-+ÏÁ*' ;
begin
  TextColor(cellcolor[CompiledCell[p].state]);
  gotoxy(CompiledCell[p].x+PosX,CompiledCell[p].y+PosY);
  if SeeTemperature then
    if temperature[p]<1000 then
      write(temperature[p] div 100)
    else
      write('!')
  else write(Devs[ord(CompiledCell[p].Dev)]);
  end;

procedure DrawMap;
var x,y:integer;
begin
  textbackground(black);
  textcolor(white);
  for y:=0 to SizeY-1 do
    for x:=0 to SizeX-1 do drawcell(y,x);
  end;

procedure initrandom;
var x,y:integer;
begin
  for x:=0 to SizeX-1 do
    for y:=0 to SizeY-1 do MapedCell[y,x].Dev:=PosDevs(Random(6));
  end;

procedure drawtitles(mode:integer);
const
  Title: array[1..3] of string =
  ('1-Skip','2-Nothing','3-Wire');
var i:integer;
begin
  gotoxy(1,49);
  textcolor(white);
  textbackground(black);
  write('S-Simulate  ');
  for i:=1 to 3 do begin
    if i=mode then textbackground(cyan) else textbackground(black);
    write(Title[i]);
    textbackground(black);
    write('  ');
    end;
  writeln;
  write('4-Ground  5-Source  6-P_Gate  7-N_Gate           N-New ESC-Quit');
  end;

function modifymap:integer;
var x2,y2,x,y,k,mode:integer;
begin
  for y:=0 to SizeY-1 do
    for x:=0 to SizeX-1 do MapedCell[y,x].State:=0;
  textcolor(lightgray);
  drawmap;
  x:=0;y:=0;
  mode:=1;
  drawtitles(mode);
  repeat
    drawcell(y,x);
    gotoxy(x+PosX,y+PosY);
    k:=ord(readkey);
    case k of
      0:  ;
      27: ;
      72: if y>0 then Dec(y);
      80: if y<SizeY-1 then Inc(y);
      77: if x<SizeX-1 then Inc(x);
      75: if x>0 then Dec(x);
      49..51: begin
            mode:=k-48;
            drawtitles(mode);
            end;
      52: MapedCell[y,x].Dev:=Ground;
      53: MapedCell[y,x].Dev:=Source;
      54: MapedCell[y,x].dev:=PGateOpen;
      55: MapedCell[y,x].Dev:=NGateOpen;
      78,110: begin
            for y2:=0 to SizeY-1 do for x2:=0 to SizeX-1 do
              MapedCell[y2,x2].dev:=nothing;
            drawmap;
            x:=0;y:=0;mode:=1;
            drawtitles(mode);
            end
      {else writeln(k);}
      end;
    if k in [72,80,77,75,50,51] then
      if mode<>1 then MapedCell[y,x].Dev:=PosDevs(mode-2);
    until k in [27,83,115];
    modifymap:=k;
  end;

procedure addNeighbour(y,x:integer;barriers:SetOfDevs);
begin
  if [MapedCell[y,x].dev,CompiledCell[NofCompiledCells].dev]*barriers=[] then begin
    CompiledCell[NofCompiledCells].Neighbour[CompiledCell[NofCompiledCells].NofNeighbours]:=y*SizeX+x;
    Inc(CompiledCell[NofCompiledCells].NofNeighbours);
    end;
  end;


procedure compile;
const
  Gates:SetOfDevs=[PGateOpen,PGateClose,NGateOpen,NGateClose];
  HoriBarriers:SetOfDevs=[nothing,shit];
  VertBarriers:SetOfDevs=[nothing,shit,
    PGateOpen,PGateClose,NGateOpen,NGateClose];
  Conductors:SetOfDevs=[wire,source,ground,
    PGateOpen,PGateClose,NGateOpen,NGateClose];
var i,i2,x,y:integer;
begin
  for i:=0 to SizeY*SizeX-1 do Conexion[i]:=-1;
  NofCompiledCells:=0;
  for y:=0 to SizeY-1 do
    for x:=0 to SizeX-1 do
      if MapedCell[y,x].dev in Conductors then begin
        CompiledCell[NofCompiledCells].x:=x;
        CompiledCell[NofCompiledCells].y:=y;
        CompiledCell[NofCompiledCells].state:=0;
        CompiledCell[NofCompiledCells].color:=-5;
        CompiledCell[NofCompiledCells].dev:=MapedCell[y,x].dev;
        CompiledCell[NofCompiledCells].done:=false;
        CompiledCell[NofCompiledCells].NofNeighbours:=0;
        if x<SizeX-1 then addNeighbour(y,x+1,horibarriers);
        if x>0 then addNeighbour(y,x-1,horibarriers);
        if y<SizeY-1 then addNeighbour(y+1,x,vertbarriers);
        if y>0 then addNeighbour(y-1,x,vertbarriers);
        Conexion[y*SizeX+x]:=NofCompiledCells;
        {if CompiledCell[NofCompiledCells].NofNeighbours>0 then}Inc(NofCompiledCells);
        end;
  for i:=0 to NofCompiledCells-1 do
    for i2:=0 to CompiledCell[i].NofNeighbours-1 do
      CompiledCell[i].Neighbour[i2]:=Conexion[ CompiledCell[i].Neighbour[i2] ];
  NofConexions:=0;
  NOfGates:=0;
  for i:=0 to NofCompiledCells-1 do
    if CompiledCell[i].dev in Gates then begin
      Gate[NOfGates]:=i;
      Master[NOfGates]:=-1;
      if CompiledCell[i].y>0 then
        if MapedCell[CompiledCell[i].y-1,CompiledCell[i].x].dev in [source,ground,wire] then
          Master[NOfGates]:=Conexion[(CompiledCell[i].y-1)*SizeX+CompiledCell[i].x]; {the one who is just above}
      inc(NOfGates);
      end;
  for i:=0 to NofCompiledCells-1 do
    if CompiledCell[i].dev in [source,ground] then begin
      Conexion[NofConexions]:=i;
      Inc(NofConexions);
      end;

  end;

function TestCell(curr,state:integer):Integer;
var
  Neighbour:ArrOfNeighbourgs;
  NofNeighbours,r,tmp:integer;
begin
  if (CompiledCell[curr].dev in [PGateClose,NGateClose])
  or CompiledCell[curr].done then begin
    TestCell:=-1;
    exit;
    end;
  CompiledCell[curr].done:=true;
  if (CompiledCell[curr].state=0) or (CompiledCell[curr].state=state) then begin
    if state=1 then temperature[curr]:=temperature[curr]+2;
    { WOW! done here to avoid high temperature in border}
    TestCell:=curr;
    exit;
    end;
  Neighbour:=CompiledCell[curr].Neighbour;
  NofNeighbours:=CompiledCell[curr].NofNeighbours;
  while NofNeighbours>0 do begin
    r:=random(NofNeighbours);
    tmp:=TestCell(Neighbour[r],State);
    if tmp>-1 then begin
      temperature[curr]:=temperature[curr]+2;
      TestCell:=tmp;
      exit;
      end;
    Neighbour[r]:=Neighbour[NofNeighbours-1];
    Dec(NofNeighbours);
    end;
  TestCell:=-1;
  end;

procedure clean(curr:integer);
var
  i:integer;
begin
  if not CompiledCell[curr].done then exit;
  CompiledCell[curr].done:=false;
  for i:=0 to CompiledCell[curr].NofNeighbours-1 do clean(CompiledCell[curr].Neighbour[i]);
  end;


procedure removecell(p:integer);
var
  i,i2:integer;
begin
  for i:=0 to NofConexions-1 do
    if Conexion[i]=p then begin
      Conexion[i]:=Conexion[NofConexions-1];
      dec(NofConexions);
      i:=NofConexions;
      end;
  for i:=0 to NOfGates-1 do
    if Gate[i]=p then begin
      Gate[i]:=Gate[NOfGates-1];
      dec(NOfGates);
      i:=NOfGates;
      end;
  for i:=0 to NofCompiledCells-1 do begin
    i2:=0;
    while i2<CompiledCell[i].NofNeighbours do begin
      if CompiledCell[i].Neighbour[i2]=p then begin
        CompiledCell[i].Neighbour[i2]:=CompiledCell[i].Neighbour[CompiledCell[i].NofNeighbours-1];
        dec(CompiledCell[i].NofNeighbours);
        end;
      inc(i2);
      end;
    end;
  for i:=0 to NOfGates-1 do if Master[i]=p then Master[p]:=-1;
  CompiledCell[p]:=CompiledCell[NofCompiledCells-1];
  temperature[p]:=temperature[NofCompiledCells-1];
  dec(NofCompiledCells);
  for i:=0 to NofConexions-1 do
    if Conexion[i]=NofCompiledCells then Conexion[i]:=p;
  for i:=0 to NOfGates-1 do if Gate[i]=NofCompiledCells then Gate[i]:=p;
  for i:=0 to NofCompiledCells-1 do
    for i2:=0 to CompiledCell[i].NofNeighbours-1 do
      if CompiledCell[i].Neighbour[i2]=NofCompiledCells then
        CompiledCell[i].Neighbour[i2]:=p;
  end;



function simulate:integer;
var
  i,i2,target,newcolor,power:integer;
  newdev:posdevs;

begin
  drawmap;
  SeeTemperature:=False;
  for i:=0 to NofCompiledCells-1 do temperature[i]:=0;
  repeat
    gotoxy(1,49);
    textcolor(White);
    textbackground(black);
    write('M-Modify SPACE-Sound_');
    if beeps then write('Off') else write('On ');
    writeln('                                                  ');
    if SeeTemperature then write('C-See_Circuit')
    else write('T-See_Temperature');
    write('                                      ');
    repeat

      for i:=0 to NofGates-1 do
        if Master[i]>-1 then begin
          i2:=CompiledCell[Master[i]].state;
          if i2=0 then
            if Random(2)=0 then i2:=-1 else i2:=1;
          if CompiledCell[Gate[i]].Dev in [PGateOpen,PGateClose] then
            if i2=-1 then newdev:=PGateOpen else newdev:=PGateClose
          else
            if i2=-1 then newdev:=NGateClose else newdev:=NGateOpen;
          if newdev<>CompiledCell[Gate[i]].dev then begin
            CompiledCell[Gate[i]].dev:=newdev;
            DrawColoredCell(Gate[i]);
            end;
          end;

      for i2:=0 to NofConexions-1 do begin
        i:=random(NofConexions);
        if CompiledCell[Conexion[i]].dev=source then power:=1
        else power:=-1;
        target:=TestCell(Conexion[i],-power);
        clean(Conexion[i]);
        if target>-1 then begin
          CompiledCell[Target].state:=CompiledCell[Target].state+power;
          DrawColoredCell(target);
          end;
        end;

      for i:=0 to NofCompiledCells-1 do
        if random(50)=0 then
          if temperature[i]>1000 then begin
            CompiledCell[i].dev:=Shit;
            CompiledCell[i].State:=-2;

            textcolor(cellcolor[CompiledCell[i].state]);
            gotoxy(CompiledCell[i].x+PosX,CompiledCell[i].y+PosY);
            write('*');

            MapedCell[CompiledCell[i].y,CompiledCell[i].x].dev:=shit;
            if beeps then write(chr(7));
            removecell(i);
            i2:=NofCompiledCells;
            end;
      for i:=0 to NofCompiledCells-1 do
        if temperature[i]>0 then
          dec(temperature[i]);
      for i:=0 to NofCompiledCells-1 do
        if SeeTemperature then begin
          gotoxy(CompiledCell[i].x+PosX,CompiLedCell[i].y+PosY);
          textcolor(cellcolor[CompiledCell[i].state]);
          if temperature[i]<1000 then write(temperature[i] div 100)
          else write('!');
          end;
        {else inc(temperature[i]);}

      until keypressed;
    i:=ord(readkey);
    if i=32 then beeps:=not beeps;
    if i in [67,99] then begin
      SeeTemperature:=False;
      for i2:=0 to NofCompiledCells-1 do DrawColoredCell(i2)
      end;
    if i in [84,116] then SeeTemperature:=True;
    until i in [27,77,109];
  simulate:=i;
end;

var
  k,i,OrigMode:integer;
label
  shittypascal;

begin
beeps:=true;
initrandom;
OrigMode:=LastMode;
TextMode(C80 + Font8x8);
textcolor(darkgray);
for i:=1 to 50 do write('ùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùù',
                          'ùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùùù');
repeat
  if ModifyMap=27 then goto shittypascal;
  compile;
  if simulate=27 then goto shittypascal;
  until false;
shittypascal:
TextMode(OrigMode);
end.
