//  THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF 
//  ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO 
//  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A 
//  PARTICULAR PURPOSE.
//
//  Copyright © 1993, 1994  Microsoft Corporation.  All Rights Reserved.
//
//  PROGRAM: Rev.c
//
//  PURPOSE: Helper functions for Reversi Sample Game
//
//  FUNCTIONS:
//  finalscore() - Calculate final score
//  legalcheck() - Return 1 if move legal, else return 0
//  makemove() - Capture enemy pieces
//  score() - Calculate the value of board
//  minmax() - Play human move then recursively play computer move
//
//  SPECIAL INSTRUCTIONS: N/A
//
#include <windows.h>
#include <windowsx.h>
#include <process.h>
#include <stdlib.h>
#include "reversi.h"

// declarations of function in reversi.c
VOID NEAR PASCAL paintmove(BYTE b[BoardSize], INT move, INT friendly,
    INT enemy);

// variable declarations
extern INT     moves[61];
extern INT     BestMove[max_depth+2];
extern HWND    hWin;
extern HDC     hDisp;
extern INT     depth;
extern INT     direc[];


//  Indexes for computing scores and whether or not a player has
//  any pieces on the board.  Very order dependant.

BYTE PieceFlags[] = {   0x00 ,      // Ignore sides
            0x00 ,      // Ignore blanks
            0x01 ,      // Human has a piece
            0x02 ,      // Computer has a piece
            };

            
//
//  The scoring tables are used to evaluate the board
//  position.  The corners of the board change value
//  according to whether a given square is occupied or
//  not.  This can be done dynamically, saving ~ 1K
//  worth of data space but costing an as of yet
//  undetermined performance hit.
//

#define B11     11    // Offsets to particular squares
#define B18     18 
#define B81     81 
#define B88     88 

#define maskb11     0x08    // Masks used for indexing into Scoring tables.
#define maskb18     0x04
#define maskb81     0x02
#define maskb88     0x01


//
//  FUNCTION: finalscore()
//
//  PURPOSE:  Calculate final score
//

INT NEAR PASCAL finalscore(
BYTE b[],
BYTE friendly,
BYTE enemy)
{
    INT i;
    INT count=0;

    for (i=11 ; i<=88 ; i++) {
        if (b[i] == friendly)
            count++;
        else
            if (b[i] == enemy)
                count--;
    }
    if (count > 0)
        return(win + count);
    else
        if (count < 0)
            return(loss + count);
        else
            return(0);
}


//
//  FUNCTION: legalcheck()
//
//  PURPOSE:  Return 1 if move legal, else return 0
//

INT NEAR PASCAL legalcheck(
BYTE b[],
INT move,
BYTE friendly,
BYTE enemy)
{
   INT sq,d;
   INT *p;

   if (b[move] == empty) {
      p=direc;
      while ((d = *p++) != 0) {
          sq=move;
          if (b[sq += d] == enemy) {
             while(b[sq += d] == enemy)
            ;
             if (b[sq] == friendly) return(1);
          }
      }
   }
   return(0);
}


//
//  FUNCTION: makemove()
//
//  PURPOSE:  Capture enemy pieces
//

VOID NEAR PASCAL makemove(
BYTE b[],
INT move,
BYTE friendly,
BYTE enemy)
{
   INT sq,d;
   INT *p;

   if (move != PASS) {
      p=direc;
      while ((d = *p++) != 0) {
          sq=move;
          if (b[sq += d] == enemy) {
            while(b[sq += d] == enemy)
            ;
            if (b[sq] == friendly)
                while(b[sq -= d] == enemy)
                   b[sq]=friendly;
          }
      }
      b[move]=friendly;
   }
}


//
//  FUNCTION: score()
//
//  PURPOSE:  Calculate the value of board
//

INT NEAR PASCAL score(
BYTE b[],
BYTE friendly,
BYTE enemy)
{
    INT *pvalue;
    BYTE *pb;
    INT fpoints=0;
    INT epoints=0;
    INT ecount=0;
    BYTE bv;
    INT v,b11,b18,b81,b88;

    static INT value[79] = {     99, -8,  8,  6,  6,  8, -8, 99,000,
                000, -8,-24, -4, -3, -3, -4,-24, -8,000,
                000,  8, -4,  7,  4,  4,  7, -4,  8,000,
                000,  6, -3,  4,  0,  0,  4, -3,  6,000,
                000,  6, -3,  4,  0,  0,  4, -3,  6,000,
                000,  8, -4,  7,  4,  4,  7, -4,  8,000,
                000, -8,-24, -4, -3, -3, -4,-24, -8,000,
                000, 99, -8,  8,  6,  6,  8, -8, 99,infin};

    static INT value2[79]= {     99, -8,  8,  6,  6,  8, -8, 99,000,
                000, -8,-24,  0,  1,  1,  0,-24, -8,000,
                000,  8,  0,  7,  4,  4,  7,  0,  8,000,
                000,  6,  1,  4,  1,  1,  4,  1,  6,000,
                000,  6,  1,  4,  1,  1,  4,  1,  6,000,
                000,  8,  0,  7,  4,  4,  7,  0,  8,000,
                000, -8,-24,  0,  1,  1,  1,-24, -8,000,
                000, 99, -8,  8,  6,  6,  8, -8, 99,infin};

    pb = &b[11];
    b11 = *pb;
    b18 = b[18];
    b81 = b[81];
    b88 = b[88];

    if ((b11 != empty) || (b18 != empty) || (b81 != empty) || (b88 != empty)) {
    pvalue = value2;

    if (b11 == empty) {
        value2[12-11] = -8;  value2[21-11] = -8;  value2[22-11] = -24;
    } else {
        value2[12-11] = 12;  value2[21-11] = 12;  value2[22-11] = 8;
    }

    if (b18 == empty) {
        value2[17-11] = -8;  value2[28-11] = -8;  value2[27-11] = -24;
    } else {
        value2[17-11] = 12;  value2[28-11] = 12;  value2[27-11] = 8;
    }

    if (b81 == empty) {
        value2[82-11] = -8;  value2[71-11] = -8;  value2[72-11] = -24;
    } else {
        value2[82-11] = 12;  value2[71-11] = 12;  value2[72-11] = 8;
    }

    if (b88 == empty) {
        value2[87-11] = -8;  value2[78-11] = -8;  value2[77-11] = -24;
    } else {
        value2[87-11] = 12;  value2[78-11] = 12;  value2[77-11] = 8;
    }
    } else {
    pvalue = value;
    }

    while((v=*pvalue++) != infin) {
       bv = *pb++;
       if (bv == friendly)
       fpoints += v;
       else if (bv == enemy) {
           epoints += v;
       ecount++;
       }

    }
    if (!ecount)          // any enemy pieces on the board?
       return(win);       // if not, we just won!
    else
       return(fpoints-epoints);
}


//
//  FUNCTION: minmax()
//
//  PURPOSE:  Play human move then recursively play computer move 
//

INT NEAR PASCAL minmax(
BYTE b[max_depth + 2][100],
INT move,
BYTE friendly,
BYTE enemy,
INT ply,
INT vmin,
INT vmax)
{
    BYTE *pCurrent, *pPrevious, *pSource, *pDest;
    INT *pMoves;
    INT *pBestMove;
    INT i;
    INT sq, value, cur_move;
    INT count=0;    // not used !

    pPrevious = &b[ply][0];
    pCurrent =  &b[ply+1][0];

    pSource = &b[ply][11];
    pDest =   &b[ply+1][11];
    for (i=11 ; i<=88 ; i++) *pDest++=*pSource++;

    pBestMove = &BestMove[ply];
    if (move == PASS) {
        if (ply == depth) {
            pMoves = moves;
            while((sq = *pMoves++) != 0) {
                if (legalcheck(pCurrent,sq,enemy,friendly))
                    return(score(pCurrent,friendly,enemy));
            }
            return(finalscore(pCurrent,friendly,enemy));
        }
    }
    else {
        if (ply == 0) {
            hDisp = GetDC(hWin);
            paintmove(pCurrent,move,friendly,enemy);
            ReleaseDC(hWin, hDisp);
        }
        else {
            makemove(pCurrent,move,friendly,enemy);
            if (ply == depth) return(score(pCurrent,friendly,enemy));
        }
    }
    pMoves = moves;
    cur_move = PASS;
    *pBestMove = PASS;
    while((sq = *pMoves++) != 0) {
        if (legalcheck(pCurrent,sq,enemy,friendly)) {
           cur_move = sq;
           value = minmax(b,cur_move,enemy,friendly,ply+1,-vmax,-vmin);
           if (value > vmin) {
              vmin = value;
              *pBestMove = cur_move;
              if (value >= vmax) goto cutoff;   // alpha-beta cutoff
           }
        }
    }
    if (cur_move == PASS) {
        if (move == PASS)        // two passes in a row mean game is over
            return(finalscore(pCurrent,friendly,enemy));
        else {
            value = minmax(b,PASS,enemy,friendly,ply+1,-vmax,-vmin);
            if (value > vmin) vmin = value;
        }
    }
cutoff:
    return(-vmin);
}
