/*
 *  Awwari - An Awari game by ! Wizard Works !
 *  Written by Stefan Zeiger in 1993-1994
 *  (c) 1993-1994 by Stefan Zeiger
 *  Freely distributable, All other rights reserved
 *
 *  I am not very proud of this piece of source. I hacked the first
 *  first version together on one afternoon. Don't blame me if you
 *  can't make your way through this code jungle ;)
 *
 *  22.11.1993  0.1   Project started
 *  23.11.1993  0.2   Playable; dumb computer
 *  27.11.1993  0.3   AwariMove() added, PerformMove() rewritten to use AwariMove()
 *              0.4   Simple computer strategy added
 *  28.11.1993  0.5   Auto finish added
 *              0.6   Recursive computer strategy added
 *  12.11.1993  0.7   Updated with new Triton system
 *  23.12.1993  1.0   First release, using Triton 0.106
 *  20.04.1994  1.1b1 Rewritten for triton.library
 *  09.05.1994  1.1   Released Awwari 1.1
 *
 */

#define AWWARI_VERSION "1.1"
#define AWWARI_DATE    "9.5.94"


/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////// Include our stuff //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

#include <libraries/triton.h>
#include <proto/intuition.h>
#include <proto/triton.h>
#include <proto/dos.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <math.h>


/////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////// Windows //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

ProjectDefinition(about_trwintags)
{
  BeginRequester("About Awwari...",TRWP_MOUSEPOINTER),

  VertGroupA, Space,  CenteredText3("Awwari " AWWARI_VERSION),
              Space,  CenteredText_BS("An Awari game by ! Wizard Works !"),
              Space,  CenteredText("© 1993-1994 by Stefan Zeiger"),
              Space,  HorizSeparator,
              Space,  CenteredText("This program is using the"),
              SpaceS, CenteredText("Triton GUI creation system"),
              SpaceS, CenteredText("which is © by Stefan Zeiger"),
              Space,  EndGroup,

  BeginRequesterGads,
  CenteredButtonRE("_OK",1),
  EndRequester
};

STRPTR cycle_entries_player[]=
{
  "Human",
  "Computer",
  NULL
};

STRPTR cycle_entries_first[]=
{
  "Player 1",
  "Player 2",
  "Random",
  NULL
};

STRPTR cycle_entries_level[]=
{
  "1",
  "2",
  "3",
  "4",
  "5",
  NULL
};

ProjectDefinition(main_trwintags)
{
  WindowTitle("Awwari " AWWARI_VERSION " - © 1993-1994 by Stefan Zeiger"),
  WindowPosition(TRWP_CENTERDISPLAY), WindowID(1),

  BeginMenu("Project"),
    MenuItem("?_About...",1001),
    ItemBarlabel,
    MenuItem("Q_Quit",1002),

  VertGroupA,
    Space,  NamedSeparator("Options"),
    Space,  HorizGroupEAC,
              HorizGroupS, Space, TextN("Pl_ayer 1"), TRAT_ID, 102, EndGroup,
              SpaceB,
                TROB_Cycle,           (ULONG) cycle_entries_player,
                  TRAT_ID,            (ULONG) 102,
                  TRAT_Value,         (ULONG) 0,
              Space,
              HorizGroupS, Space, TextN("_First move"), TRAT_ID, 104, EndGroup,
              SpaceB,
                TROB_Cycle,           (ULONG) cycle_entries_first,
                  TRAT_ID,            (ULONG) 104,
                  TRAT_Value,         (ULONG) 2,
              Space,
            EndGroup,
    Space,  HorizGroupEAC,
              HorizGroupS, Space, TextN("Pla_yer 2"), TRAT_ID, 103, EndGroup,
              SpaceB,
                TROB_Cycle,           (ULONG) cycle_entries_player,
                  TRAT_ID,            (ULONG) 103,
                  TRAT_Value,         (ULONG) 1,
              Space,
              HorizGroupS, Space, TextN("_Level"), TRAT_ID, 105, EndGroup,
              SpaceB,
                TROB_Cycle,           (ULONG) cycle_entries_level,
                  TRAT_ID,            (ULONG) 105,
                  TRAT_Value,         (ULONG) 0,
              Space,
            EndGroup,
    Space,  HorizGroupE, Space, ButtonR("_Start game",110), Space, Button("_Quit game",112), Disabled, Space, Button("A_uto finish",111), Disabled, Space, EndGroup,
    Space,  NamedSeparator("Status"),
    Space,  HorizGroup, Space, FrameBox, ObjectBackfillB, VertGroup|TRGR_FIXVERT, SpaceS, HorizGroupSC, Space, TextN("Ready."),ID(101), TRAT_MinWidth, 40, Space, EndGroup, SpaceS, EndGroup, Space, EndGroup,
    Space,  NamedSeparator("Playfield"),
    Space,  HorizGroupEA,
              Space, VertGroupA, SpaceS, CenteredText3("1"), SpaceS, EndGroup,
              Space, IntegerBox(3,6,3),
              Space, IntegerBox(3,5,3),
              Space, IntegerBox(3,4,3),
              Space, IntegerBox(3,3,3),
              Space, IntegerBox(3,2,3),
              Space, IntegerBox(3,1,3),
              Space, HorizGroupS, Space, EndGroup,
              Space, EndGroup,
    Space,  HorizGroupEAC,
              Space, IntegerBox(0,7,3),
              Space, Button("_1",201), Disabled,
              Space, Button("_2",202), Disabled,
              Space, Button("_3",203), Disabled,
              Space, Button("_4",204), Disabled,
              Space, Button("_5",205), Disabled,
              Space, Button("_6",206), Disabled,
              Space, IntegerBox(0,17,3),
              Space, EndGroup,
    Space,  HorizGroupEA,
              Space, HorizGroupS, Space, EndGroup,
              Space, IntegerBox(3,11,3),
              Space, IntegerBox(3,12,3),
              Space, IntegerBox(3,13,3),
              Space, IntegerBox(3,14,3),
              Space, IntegerBox(3,15,3),
              Space, IntegerBox(3,16,3),
              Space, VertGroupA, SpaceS, CenteredText3("2"), SpaceS, EndGroup,
              Space, EndGroup,
    Space, EndGroup, EndProject
};


/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////// Our own names //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

VOID                  SetInfoString           (STRPTR);
VOID                  StartGame               (VOID);
VOID                  QuitGame                (VOID);
VOID                  Winner                  (UWORD);
VOID                  MakeTurn                (UWORD player, BOOL setsecondturn);
VOID                  SetPositionGadgets      (UWORD);
VOID                  DisablePositionGadgets  (VOID);
VOID                  ActualizeDisplay        (VOID);
VOID                  PerformMove             (UWORD,UWORD);
VOID                  ComputeMove             (UWORD);
UWORD                 BestMove                (UBYTE awarifield[7][2], UWORD aplayer, UWORD *bestval);
UWORD                 BestMoveNR              (UBYTE awarifield[7][2], UWORD aplayer);
UWORD                 AwariMove               (UBYTE awarifield[7][2], UWORD player, UWORD field);
struct TR_Project   * main_project;
UWORD                 player1,
                      player2,
                      first,
                      level,
                      currentplayer,
                      currentdepth;
BOOL                  gamerunning;
UBYTE                 playfield               [7][2];
UBYTE                 display                 [7][2];
UWORD                 i,
                      j;
BOOL                  posgaddisabled          [6];
BOOL                  secondturn;


/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////////// Main function //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

VOID do_main(VOID)
{
  BOOL close_me=FALSE;
  struct TR_Message *trmsg;

  for(i=0;i<=1;i++)
  {
    for(j=0;j<=5;j++) display[j][i]=3;
    display[6][i]=0;
  }

  for(j=0;j<=5;j++) posgaddisabled[j]=TRUE;

  if(main_project=TR_OpenProject(Application,main_trwintags))
  {
    while(!close_me)
    {
      TR_Wait(Application,NULL);
      while(trmsg=TR_GetMsg(Application))
      {
        switch(trmsg->trm_Class)
        {
          case TRMS_CLOSEWINDOW:
            close_me=TRUE;
            break;

          case TRMS_ERROR:
            DisplayBeep(NULL);
            break;

          case TRMS_NEWVALUE:
            break;

          case TRMS_ACTION:
            switch(trmsg->trm_ID)
            {
              case 110:
                StartGame();
                break;
              case 111:
                player1=1;
                player2=1;
                TR_SetAttribute(main_project,112,TRAT_Disabled,1L);
                TR_SetAttribute(main_project,111,TRAT_Disabled,1L);
                MakeTurn(currentplayer,FALSE);
                break;
              case 112:
                QuitGame();
                SetInfoString("Ready.");
                break;
              case 201:
                PerformMove(currentplayer,currentplayer?0:5);
                break;
              case 202:
                PerformMove(currentplayer,currentplayer?1:4);
                break;
              case 203:
                PerformMove(currentplayer,currentplayer?2:3);
                break;
              case 204:
                PerformMove(currentplayer,currentplayer?3:2);
                break;
              case 205:
                PerformMove(currentplayer,currentplayer?4:1);
                break;
              case 206:
                PerformMove(currentplayer,currentplayer?5:0);
                break;
              case 1001:
                TR_AutoRequest(Application,main_project,about_trwintags);
                break;
              case 1002:
                close_me=TRUE;
            }
            break;
        }
        TR_ReplyMsg(trmsg);
      }
    }
    TR_CloseProject(main_project);
  }
  else DisplayBeep(NULL);
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////////////////// Entry function //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

int main(void)
{
  if(TR_OpenTriton(TRITON10VERSION,
    TRCA_Name,    "Awwari",
    TRCA_LongName,"Awwari",
    TRCA_Info,    "An Awari game for the Workbench\nWritten and © 1993-1994 by Stefan Zeiger",
    TRCA_Release, "1.1",
    TRCA_Date,    AWWARI_DATE,
    TAG_END))
  {
    do_main();
    TR_CloseTriton();
    return 0;
  }

  puts("Can't open triton.library.");
  return 20;
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////// Evaluate a move //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

UWORD MyRand(VOID)
{
  unsigned int clock[2];
  WORD seed[3];

  (VOID)timer(clock);

  seed[0]=(WORD)clock[0];
  seed[1]=(WORD)clock[1]>>16;
  seed[2]=(WORD)clock[1];

  return (UWORD)(jrand48((UWORD *)seed));
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////// Evaluate a move //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

#define AM_OK         0L
#define AM_EMPTYFIELD 1L
#define AM_EXTRATURN  2L
#define AM_1WINS      3L
#define AM_2WINS      4L
#define AM_TIE        5L

UWORD AwariMove(UBYTE awarifield[7][2], UWORD aplayer, UWORD afield)
{
  UBYTE stonesleft=awarifield[afield][aplayer];
  UBYTE cfield=afield;
  UBYTE cplayer=aplayer;
  UBYTE nextplayer;

  if(stonesleft==0) return AM_EMPTYFIELD;

  awarifield[afield][aplayer]=0;

  while(stonesleft>0)
  {
    cfield++;
    if(cfield==7) { cfield=0; cplayer++; cplayer%=2; }
    awarifield[cfield][cplayer]++;
    stonesleft--;
  }
  if(cfield!=6)
  {
    if((awarifield[cfield][cplayer]==1)&&(awarifield[5-cfield][(cplayer+1)%2]!=0))
    {
      awarifield[6][aplayer]+=(awarifield[cfield][cplayer]+awarifield[5-cfield][(cplayer+1)%2]);
      awarifield[cfield][cplayer]=0;
      awarifield[5-cfield][(cplayer+1)%2]=0;
    }
  }

  if((cplayer==aplayer)&&(cfield==6)) nextplayer=aplayer; else nextplayer=(aplayer+1)%2;

  if(awarifield[6][0]>18) return AM_1WINS;
  if(awarifield[6][1]>18) return AM_2WINS;
  if( (awarifield[0][nextplayer]==0) &&
      (awarifield[1][nextplayer]==0) &&
      (awarifield[2][nextplayer]==0) &&
      (awarifield[3][nextplayer]==0) &&
      (awarifield[4][nextplayer]==0) &&
      (awarifield[5][nextplayer]==0) )
  {
    if(awarifield[6][0]==awarifield[6][1]) return AM_TIE;
    if(awarifield[6][0]>awarifield[6][1]) return AM_1WINS;
    return AM_2WINS;
  }

  if(nextplayer==aplayer) return AM_EXTRATURN; else return AM_OK;
}


/////////////////////////////////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////////// Helping functions //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////

VOID SetInfoString(STRPTR istr)
{
  TR_SetAttribute(main_project,101,TRAT_Text,(ULONG)istr);
}


VOID StartGame()
{
  for(i=0;i<=1;i++)
  {
    for(j=0;j<=5;j++) playfield[j][i]=3;
    playfield[6][i]=0;
  }

  TR_SetAttribute(main_project,102,TRAT_Disabled,1L);
  TR_SetAttribute(main_project,103,TRAT_Disabled,1L);
  TR_SetAttribute(main_project,104,TRAT_Disabled,1L);
  TR_SetAttribute(main_project,105,TRAT_Disabled,1L);
  TR_SetAttribute(main_project,110,TRAT_Disabled,1L);
  player1=TR_GetAttribute(main_project,102,TRAT_Value);
  player2=TR_GetAttribute(main_project,103,TRAT_Value);
  first=TR_GetAttribute(main_project,104,TRAT_Value);
  level=TR_GetAttribute(main_project,105,TRAT_Value)+1;
  if((player1==0)||(player2==0))
  {
    TR_SetAttribute(main_project,112,TRAT_Disabled,0L);
    TR_SetAttribute(main_project,111,TRAT_Disabled,0L);
  }
  if(first==2) currentplayer=((MyRand())&(1<<7))>>7; else currentplayer=first;
  gamerunning=TRUE;

  ActualizeDisplay();

  MakeTurn(currentplayer,TRUE);
}


VOID QuitGame()
{
  TR_SetAttribute(main_project,102,TRAT_Disabled,0L);
  TR_SetAttribute(main_project,103,TRAT_Disabled,0L);
  TR_SetAttribute(main_project,104,TRAT_Disabled,0L);
  TR_SetAttribute(main_project,105,TRAT_Disabled,0L);
  TR_SetAttribute(main_project,112,TRAT_Disabled,1L);
  TR_SetAttribute(main_project,111,TRAT_Disabled,1L);
  TR_SetAttribute(main_project,110,TRAT_Disabled,0L);
  DisablePositionGadgets();
  gamerunning=FALSE;
}


VOID Winner(UWORD player)
{
  QuitGame();
  switch(player)
  {
    case 0:
      SetInfoString("Player 1 wins.");
      break;

    case 1:
      SetInfoString("Player 2 wins.");
      break;

    default:
      SetInfoString("A tie.");
      break;
  }
}


VOID MakeTurn(UWORD player, BOOL setsecondturn)
{
  if(setsecondturn) secondturn=FALSE;

  if(player==0)
  {
    if(player1==0)
    {
      SetInfoString("Make your turn, player 1.");
      SetPositionGadgets(0);
    }
    else
    {
      SetInfoString("Player 1 is thinking...");
      DisablePositionGadgets();
      ComputeMove(0);
    }
  }
  else
  {
    if(player2==0)
    {
      SetInfoString("Make your turn, player 2.");
      SetPositionGadgets(1);
    }
    else
    {
      SetInfoString("Player 2 is thinking...");
      DisablePositionGadgets();
      ComputeMove(1);
    }
  }
}


VOID SetPositionGadgets(UWORD player)
{
  BOOL newval;
  UWORD k;

  for(j=0;j<=5;j++)
  {
    newval=playfield[j][player]?0L:1L;
    if(player==0) k=5-j; else k=j;
    if(posgaddisabled[k]!=newval?1L:0L)
    {
      TR_SetAttribute(main_project,201+k,TRAT_Disabled,(UWORD)newval);
      posgaddisabled[k]=newval;
    }
  }
}


VOID DisablePositionGadgets(VOID)
{
  for(j=0;j<=5;j++) if(!posgaddisabled[j])
  {
    TR_SetAttribute(main_project,201+j,TRAT_Disabled,1L);
    posgaddisabled[j]=1L;
  }
}


VOID ActualizeDisplay()
{
  for(i=0;i<=1;i++) for(j=0;j<=6;j++) if(display[j][i]!=playfield[j][i])
  {
    display[j][i]=playfield[j][i];
    TR_SetAttribute(main_project,1+(i*10)+j,TRAT_Value,playfield[j][i]);
  }
}


VOID PerformMove(UWORD player, UWORD field)
{
  UWORD result;

  result=AwariMove(playfield,player,field);
  ActualizeDisplay();

  switch(result)
  {
    case AM_1WINS:
    case AM_2WINS:
    case AM_TIE:
      Winner(result-3);
      return;

    case AM_EXTRATURN:
      if(!secondturn)
      {
        secondturn=TRUE;
        SetPositionGadgets(currentplayer);
        if(currentplayer==0)
        {
          if(player1==0) SetInfoString("Extra turn, player 1.");
          else
          {
            SetInfoString("Extra turn. Player 1 is thinking...");
            DisablePositionGadgets();
            ComputeMove(0);
          }
        }
        else
        {
          if(player2==0) SetInfoString("Extra turn, player 2.");
          else
          {
            SetInfoString("Extra turn. Player 2 is thinking...");
            DisablePositionGadgets();
            ComputeMove(1);
          }
        }
        return;
      }

    case AM_OK:
      currentplayer++;
      currentplayer%=2;
      MakeTurn(currentplayer,TRUE);
      return;

    case AM_EMPTYFIELD:
      DisplayBeep(NULL);
      return;
  }
}


VOID ComputeMove(UWORD player)
{
  UWORD num;

  TR_LockProject(main_project);
  //Delay(50L);

  currentdepth=0;
  num=BestMove(playfield,player,NULL);

  if(playfield[num][player]) PerformMove(player,num);
  else if(playfield[0][player]) PerformMove(player,0);
  else if(playfield[1][player]) PerformMove(player,1);
  else if(playfield[2][player]) PerformMove(player,2);
  else if(playfield[3][player]) PerformMove(player,3);
  else if(playfield[4][player]) PerformMove(player,4);
  else if(playfield[5][player]) PerformMove(player,5);
  else PerformMove(currentplayer,2);

  TR_UnlockProject(main_project);
}


UWORD BestMove(UBYTE awarifield[7][2], UWORD aplayer, UWORD *bestval)
{
  UBYTE dest[6][7][2];
  register UWORD result;
  register UWORD i,j,k;
  register UBYTE val=0, newval, retval=0;
  UWORD recurseval,otherplayer;

  currentdepth++;

  for(i=0;i<7;i++) for(j=0;j<2;j++) for(k=0;k<6;k++) dest[k][i][j]=awarifield[i][j];

  for(k=0;k<6;k++)
  {
    if(currentdepth>=level)
    {
      result=AwariMove(dest[k],aplayer,k);
      newval=dest[k][6][aplayer];
      switch(result)
      {
        case AM_EXTRATURN:
          newval++;
          break;
        case AM_1WINS:
          if(aplayer==0) newval=255; else newval=0;
          break;
        case AM_2WINS:
          if(aplayer==1) newval=255; else newval=0;
          break;
        case AM_TIE:
          if(newval>0) newval--;
          break;
        case AM_EMPTYFIELD:
          newval=0;
          break;
      }
    }
    else
    {
      if((result=AwariMove(dest[k],aplayer,k)==AM_EXTRATURN)&&((secondturn==FALSE)||(currentdepth>1)))
        result=AwariMove(dest[k],aplayer,BestMoveNR(dest[k],aplayer));
      otherplayer=(aplayer+1)%2;
      if(AwariMove(dest[k],otherplayer,BestMoveNR(dest[k],otherplayer))==AM_EXTRATURN)
        AwariMove(dest[k],otherplayer,BestMoveNR(dest[k],otherplayer));
      BestMove(dest[k],aplayer,&recurseval);
      newval=recurseval;
      if((result==AM_EXTRATURN)&&((secondturn==FALSE)||(currentdepth>1))) newval++;
    }
    if( (newval>val) || ((newval==val)&&( ((MyRand())&(1<<7))>>7 == 1)) )
    {
      val=newval;
      retval=k;
    }
  }

  currentdepth--;
  if(bestval) *bestval=val;
  return retval;
}


UWORD BestMoveNR(UBYTE awarifield[7][2], UWORD aplayer)
{
  UBYTE dest[6][7][2];
  register UWORD result;
  register UWORD i,j,k;
  register UBYTE val=0, newval, retval=0;

  for(i=0;i<7;i++) for(j=0;j<2;j++) for(k=0;k<6;k++) dest[k][i][j]=awarifield[i][j];

  for(k=0;k<6;k++)
  {
    result=AwariMove(dest[k],aplayer,k);
    newval=dest[k][6][aplayer];
    switch(result)
    {
      case AM_EXTRATURN:
        newval++;
        break;
      case AM_1WINS:
        if(aplayer==0) newval=255; else newval=0;
        break;
      case AM_2WINS:
        if(aplayer==1) newval=255; else newval=0;
        break;
      case AM_TIE:
        if(newval>0) newval--;
        break;
      case AM_EMPTYFIELD:
        newval=0;
        break;
    }
    if( (newval>val) || ((newval==val)&&( ((MyRand())&(1<<7))>>7 == 1)) )
    {
      val=newval;
      retval=k;
    }
  }

  return retval;
}
