﻿use array,graph,math,ui

.lang=0;    //0 -english, 1 - cesky

.t=[
'game': ['KyblGo-Moku','KýblPiškvorky'],
'youwon': ['You won, congratulation!','Vyhrál(a) jsi, gratuluji!'],
'youlost': ['You lost, I\'m sorry','Prohrál jsi, bohužel.'],
'tie': ['Tie','Remíza']
];

size=graph.full(true);
graph.size(size[0],size[1]);
.s=[];
.s['size']=20;
.s['sizes']=graph.size();
.countx=math.floor((.s['sizes'][0]-1)/(.s['size']+1));
.county=math.floor((.s['sizes'][1]-1)/(.s['size']+1));
.s['paddingx']=math.floor(((.s['sizes'][0]-1-.countx*(.s['size']+1))+1)/2);
.s['paddingy']=math.floor(((.s['sizes'][1]-1-.county*(.s['size']+1))+1)/2);

function DrawBoard()
  graph.pen(0xF0E0A0);
  graph.brush(0xF0E0A0);
  graph.rect(0,0,.s['sizes'][0],.s['sizes'][1]);
  graph.pen(0xE0D0A0);
  maxlen=.s['sizes'][0]/5;
  for i=0 to 1000 do
    posx=math.random()*.s['sizes'][0];
    posy=math.random()*.s['sizes'][1];
    graph.line(posx,posy,posx+(math.random()-0.5)*maxlen,posy+(math.random()-0.5)*maxlen);
  end;

  graph.pen(0x908030);
  for posx=.s['paddingx']-1 to .s['sizes'][0] by .s['size']+1 do
    graph.line(posx,.s['paddingy']-1,posx,.s['paddingy']+.county*(.s['size']+1));
  end;
  for posy=.s['paddingy']-1 to .s['sizes'][1] by .s['size']+1 do
    graph.line(.s['paddingx']-1,posy,.s['paddingx']+.countx*(.s['size']+1),posy);
  end;
  graph.show();

  graph.pen(0x000000);
  .s['board']=[];
  for i=0 to .county-1 do
    append(.s['board'],[]);
    for i2=0 to .countx-1 do
      append(.s['board'][i],null);
    end;
  end;
end;

function DrawField(x,y,pict,color,bgcolor=null)
  posx=x*(.s['size']+1);
  posy=y*(.s['size']+1);

  if bgcolor # null then
    graph.pen(bgcolor);
    graph.brush(bgcolor);
    graph.rect(.s['paddingx']+posx,.s['paddingy']+posy,.s['size'],.s['size']);
  else
    graph.brush(false);
  end;

  graph.pen(color);
  case pict
    in 'O':
      graph.circle(.s['paddingx']+posx+.s['size']/6,.s['paddingy']+posy+.s['size']/6,.s['size']*2/3); 
      graph.circle(.s['paddingx']+posx+.s['size']/6+1,.s['paddingy']+posy+.s['size']/6+1,.s['size']*2/3-2);

    in 'X':
      graph.line(math.floor(.s['paddingx']+posx+.s['size']/6),math.floor(.s['paddingy']+posy+.s['size']/6),math.floor(.s['paddingx']+posx+.s['size']*5/6),math.floor(.s['paddingy']+posy+.s['size']*5/6));
      graph.line(math.floor(.s['paddingx']+posx+.s['size']/6)+1,math.floor(.s['paddingy']+posy+.s['size']/6),math.floor(.s['paddingx']+posx+.s['size']*5/6)+1,math.floor(.s['paddingy']+posy+.s['size']*5/6));
      graph.line(math.round(.s['paddingx']+posx+.s['size']*5/6) /* ? */ -1,math.round(.s['paddingy']+posy+.s['size']/6),math.round(.s['paddingx']+posx+.s['size']/6) /* ? */ -1,math.round(.s['paddingy']+posy+.s['size']*5/6));
      graph.line(math.round(.s['paddingx']+posx+.s['size']*5/6),math.round(.s['paddingy']+posy+.s['size']/6),math.round(.s['paddingx']+posx+.s['size']/6),math.round(.s['paddingy']+posy+.s['size']*5/6));
    else throw null;
  end;
end;

function GetPosition(posx,posy)
  posx-=.s['paddingx'];
  posy-=.s['paddingy'];

  if (posx%(.s['size']+1)) = .s['size'] or (posy%(.s['size']+1)) = .s['size'] then
    return null;  //click to line at intersection, not valid
  end;

  x=math.floor(posx/(.s['size']+1));
  y=math.floor(posy/(.s['size']+1));

  if x < 0 or y < 0 or x >= .countx  or y >= .county then
    return null;
  end;

  return [x,y];
end;

function Max(value1,value2)
  if not isarray(value1) then
    if value1 > value2 then
      return value1;
    else
      return value2;
    end;
  else    //array
    for i=0 to len(value1)-1 do
      if value1[i] > value2[i] then
        return value1;
      elsif value1[i] < value2[i] then
        return value2;
      end;
    end;
    return value1;//the same values
  end;
end;

function GetValue(x,y)
  if x >= 0 and y >= 0 and x < .countx and y < .county then
    return .s['board'][y][x];
  else
    return false;
  end;
end;

function GetRatingOneDirection(player,x,y,dirx,diry)
  newx=x+dirx;
  newy=y+diry;

  realplayer=null;
  startholes=0;
  playerfields=0;
  totalfields=0;

  do
    while true do
      value=GetValue(newx,newy);
      newx+=dirx;
      newy+=diry;
      if value # null then
        break;
      else
        startholes++;
        totalfields++;
      end;
    end;

    if value = false then
      break;
    end;
    realplayer=value;
    if realplayer = player then
      playerfields=1.1;
    else
      playerfields=0.9;
    end;
    case startholes
      in 0:
      in 1: playerfields-=0.6;
    end;
    totalfields++;

    while true do
      value=GetValue(newx,newy);
      if value # realplayer then
        if value # null then
          playerfields-=0.5;
        end;
        break;
      else
        newx+=dirx;
        newy+=diry;
        playerfields++;
        totalfields++;
      end;
    end;

    for i=totalfields to 5 do
      value=GetValue(newx,newy);
      if value # null and value # realplayer then
        break;
      end;
      newx+=dirx;
      newy+=diry;
      totalfields++;
    end;
  until true;

  if startholes > 1 then
    playerfields=0;
  end;

  return [realplayer,startholes,playerfields,totalfields];
end;

function GetRatingBothDirections(player,x,y,dirx,diry,verbose=false)
  left=GetRatingOneDirection(player,x,y,-dirx,-diry);
  right=GetRatingOneDirection(player,x,y,dirx,diry);
  if verbose then
    ui.msg(str(dirx)+','+str(diry)+'\n'+str(left)+'\n'+str(right));
  end;

  if left[0] # right[0] then
    if left[0] = null then
      left[0]=right[0];
    elsif right[0] = null then
      right[0]=left[0];
    end;
  end;

  if left[0] # right[0] then
    if left[3]+right[1] < 5-1 then
      left[2]=-1;  //no place to 5
    end;
    if right[3]+left[1] < 5-1 then
      right[2]=-1;  //no place to 5
    end;
    ret=Max([left[2],left[3],left[0]],[right[2],right[3],right[0]]);
    if ret[2] = player then
      ret[0]-=0.2;
    end;
    return [ret[0],ret[1]];
  else
    if left[3]+right[3] < 5-1 then
      return [-1,0];  //no place to 5
    end;
    if left[1] > 0 and right[1] > 0 then
      return Max([left[2],left[3]],[right[2],right[3]]);
    end;
    return [left[2]+right[2],left[3]+right[3]];
  end;
end;

function GetDirections()
  return [[-1,1],[0,1],[1,1],[1,0]];
end;

function GetRating(player,x,y)
  directions=GetDirections();
  rating=[-10,0];
  for dir in directions do
    rating=Max(rating,GetRatingBothDirections(player,x,y,dir[0],dir[1]));
//    rating+=math.pow(10,GetRatingBothDirections(player,x,y,dir[0],dir[1]));
  end;

  return rating;
end;

function GetBestMove(player)
  rating=-10*100;
  bestmoves=[];
  for y=0 to .county-1 do
    for x=0 to .countx-1 do
      if .s['board'][y][x] = null then
        newrating=GetRating(player,x,y);
        newrating=newrating[0]*100+newrating[1];
        if newrating >= rating then
          if newrating > rating then
            rating=newrating;
            bestmoves=[];
          end;
          append(bestmoves,[x,y]);
        end;
      end;
    end;
  end;

  move=math.floor(math.random()*len(bestmoves));

  return [bestmoves[move][0],bestmoves[move][1]];
end;

function CheckEndGame(x,y,pict)
  directions=GetDirections();
  player=GetValue(x,y);
  winfields=[];
  for dir in directions do
    tmpfields=[[x,y]];
    newx=x-dir[0]; newy=y-dir[1];
    while GetValue(newx,newy) = player do
      append(tmpfields,[newx,newy]);
      newx-=dir[0];
      newy-=dir[1];
    end;
    newx=x+dir[0]; newy=y+dir[1];
    while GetValue(newx,newy) = player do
      append(tmpfields,[newx,newy]);
      newx+=dir[0];
      newy+=dir[1];
    end;
    if len(tmpfields) >= 5 then
      winfields=array.concat(winfields,tmpfields);
    end;
  end;

  if len(winfields) # 0 then
    for field in winfields do
      DrawField(field[0],field[1],pict,0x000000,0x90E8FF);
    end;
    graph.show();
    sleep(1000);

    return true;
  end;

  return false;
end;

endgame=false;

while not endgame do
  DrawBoard();
  comppos=null;
  fieldsleft=.countx*.county;
  tie=false;

  while true do
    if fieldsleft = 0 then
      tie=true;
      break;
    end;

    ui.ptr(false);
    ui.keys(false);
    input=ui.cmd();
    if not isarray(input) then
      if input = 98 then
        if comppos # null then
          .s['board'][comppos[1]][comppos[0]]=null;
        end;
        graph.pen(graph.black);
        graph.bg(graph.white);
        graph.font(['SwissA',10,false,false]);
        for y=0 to .county-1 do
          for x=0 to .countx-1 do
            if .s['board'][y][x] = null then
              rating='';
  for dir in GetDirections() do
    rating=rating+GetRatingBothDirections(2,x,y,dir[0],dir[1])+':';
  end;
              graph.pen(graph.white);
              graph.rect(x*(.s['size']+1)+.s['paddingx'],(y+1)*(.s['size']+1)+.s['paddingy']-13,.s['size'],12);
              graph.pen(0x606060);
              graph.text(x*(.s['size']+1)+.s['paddingx']+1,(y+1)*(.s['size']+1)+.s['paddingy']-1,str(rating));
            else
              picts=[null,'X','O'];
              DrawField(x,y,picts[.s['board'][y][x]],0x000000,0xF0E0A0);
            end;
          end;
        end;
        graph.show();


        ui.ptr(false);
        ui.keys(false);
        input=ui.cmd();
        if isarray(input) then
          pos=GetPosition(input[0],input[1]);
          if pos # null and .s['board'][pos[1]][pos[0]] = null then //valid posit.
            for dir in GetDirections() do  
              GetRatingBothDirections(2,pos[0],pos[1],dir[0],dir[1],true);
            end;
          end;
        end;

        if comppos # null then
          .s['board'][comppos[1]][comppos[0]]=2;
        end;
      else
        endgame=true;
        break;
      end;
    elsif input[2] = 1 then
      pos=GetPosition(input[0],input[1]);
      if pos # null and .s['board'][pos[1]][pos[0]] = null then //valid posit.
        DrawField(pos[0],pos[1],'X',0x000000);
        .s['board'][pos[1]][pos[0]]=1;
        fieldsleft--;
        if comppos # null then
          DrawField(comppos[0],comppos[1],'O',0x000000);
        end;

        if CheckEndGame(pos[0],pos[1],'X') then
          ui.msg(.t['youwon'][.lang],.t['game'][.lang]);
          break;
        end;

        graph.show();
        if fieldsleft = 0 then
          tie=true;
          break;
        end;

        comppos=GetBestMove(2);
        DrawField(comppos[0],comppos[1],'O',0xFF0000);
        .s['board'][comppos[1]][comppos[0]]=2;
        fieldsleft--;

        if CheckEndGame(comppos[0],comppos[1],'O') then
          ui.msg(.t['youlost'][.lang],.t['game'][.lang]);
          break;
        end;

        graph.show();
      end;
    end;
  end;

  if tie then
    sleep(1000);
    ui.msg(.t['tie'][.lang],.t['game'][.lang]);
  end;
end;

graph.hide();
