home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / C / Games / Abalone 1.4.2 / src / arnold.c < prev    next >
Encoding:
C/C++ Source or Header  |  1995-09-21  |  8.5 KB  |  303 lines  |  [TEXT/MPS ]

  1. #define ARNOLD_C
  2. #include "Arnold.h"
  3.  
  4. //    This is my favorite BoardJudgeProc so far.
  5.  
  6. //    Not static to share with assembly
  7. #pragma segment More
  8.  
  9. short            ballVal[kFields+1][1 << kDirections];    //    Yep, it's big: 62*64*2 bytes
  10. Boolean            firstTime = true;
  11.  
  12.  
  13. //    Init stuff split off; body will be assembly for 68K Macs
  14. void
  15. InitArnold (void)
  16. {
  17.     register unsigned char    d, index, f;
  18.     
  19.     #define kBallValue        1100
  20. //    A bit more than distVal[0] computed below, to account for connectivity bonus points
  21.     
  22.     if (firstTime)    //    initialise values for the value lookup tables. A lot of work once.
  23.     {    
  24.         unsigned char    bestNeighbour[kFields+1][2];
  25.         short            value[kFields+1];
  26.         short            distVal[10];
  27.         short            dist;
  28.         
  29.         firstTime = false;
  30.         
  31.     //    Compute a even-slightly-faster-than-exponential relation between distance and danger.
  32.  
  33.         for (dist = 9 + 1; --dist >= 0;)
  34.         {
  35.             if (dist == 9)
  36.                 distVal[dist] = 0;
  37.             else if (dist == 8)
  38.                 distVal[dist] = 1;
  39.             else
  40.                 distVal[dist] = 2 * distVal[dist+1] + distVal[dist+2];
  41.         }
  42.     //    Use the average of the dangers in all directions as the field value
  43.         
  44.         value[0] = distVal[0];
  45.         
  46.         for (f = 1; f <= kFields; f++)
  47.         {
  48.             value[f] = 0;
  49.             
  50.             for (d = right; d < down; d++)
  51.                 value[f] += distVal[Distance (f, d)];
  52.             
  53.             value[f] /= 6;
  54.         }
  55.         
  56.     //    Find out what the good neighbours are.
  57.     //    A good neighbour is a neighbour in the most profitable direction to go.
  58.         
  59.         for (f = 1; f <= kFields; f++)
  60.         {
  61.             short    best = 1 << 10;
  62.                         
  63.             for (d = right; d < down; d++)
  64.             {
  65.                 if (value[Neighbour (f, d)] < best)
  66.                 {
  67.                     best = value[Neighbour (f, d)];
  68.                     bestNeighbour[f][0] = Neighbour (f, d);
  69.                 }
  70.             }
  71.         }
  72.         
  73.     //    find good neighbour 2 (may be the same as 1)
  74.         
  75.         for (f = kFields + 1; --f > 0; )
  76.         {
  77.             short    best = 1 << 10;
  78.                         
  79.             for (d = right; d < down; d++)
  80.             {
  81.                 if (value[Neighbour (f, d)] < best)
  82.                 {
  83.                     best = value[Neighbour (f, d)];
  84.                     bestNeighbour[f][1] = Neighbour (f, d);
  85.                 }
  86.             }
  87.         }
  88.     //    Based on the positional value, compute a value depending on connectivity too.
  89.             
  90.         for (index = 0; index < (1 << kDirections); index++)
  91.             ballVal[0][index] = kBallValue;
  92.             
  93.         for (f = 1; f <= kFields; f++)                                //    for all fields
  94.         {
  95.             for (index = 0; index < (1 << kDirections); index++)    //    for all combinations of neighbours
  96.             {
  97.             //    What we must define now, is the value representing the resulting
  98.             //    number of malus points for the ball on field f.
  99.             //    For this, we can start with the generic distribution value[f],
  100.             //    and add bonus points (= substract malus points) for good connections.
  101.  
  102.                 ballVal[f][index] = value[f];        //    start with positional value
  103.                 
  104.             //    Now some ad-hoc abracadabra with connections
  105.                 
  106.                 for (d = right; d < down; d++)        //    for all directions
  107.                 {
  108.                 //    index is a bitmap;
  109.                 //    the bits represent the neighbour fields in each of the six directions.
  110.                 //    For example, an index of 6 (000110) means the directions 1 and 2 only.
  111.                 //    For each field, all possible combinations of present/absent friendly
  112.                 //    neighbours can thus be represented in 64 combinations.
  113.                 //    The ballVal table has one enrty (with 64 combinations) for each field.
  114.                 //    The values for the ballVal table are computed below.
  115.  
  116.                     if ((index >> d) & 1)            //    bit for this direction set in index
  117.                     {
  118.                         if (    Neighbour (f, d) == bestNeighbour[f][0]
  119.                             &&    bestNeighbour[f][0] == bestNeighbour[f][1])
  120.                         {
  121.                         //    It is a very good connection, but it will be counted twice,
  122.                         //    so we must compensate for this.
  123.  
  124.                             if ((index >> ((d + 3) % kDirections)) & 1)    //    opposite direction too!
  125.                             
  126.                             //    This is extra profitable.
  127.                             //    The ball is connected in a 'good' direction, so it gets some
  128.                             //    protection, but is connected in a 'bad' direction too,
  129.                             //    meaning it gives protection to a less fortunate neighbour.
  130.                             //    The protection is actually larger than this neighbour knows,
  131.                             //    since it is backed up up by the neighbour in the good direction
  132.                             //    as well, which the unfortunate neighbour cannot 'see':
  133.                             //    it can only look at direct neighbours.
  134.                             //    There's nothing wrong in correcting its value at this ball;
  135.                             //    after all, only the grand total over all balls counts.
  136.  
  137.                                 ballVal[f][index] -= 40;    //    counted twice
  138.                                 
  139.                             else
  140.                             
  141.                                 ballVal[f][index] -= 20;    //    counted twice
  142.                         }
  143.                         else if (    Neighbour (f, d) == bestNeighbour[f][0]
  144.                                 ||    Neighbour (f, d) == bestNeighbour[f][1])
  145.                         {
  146.                             if ((index >> ((d + 3) % kDirections)) & 1)    //    opposite direction too!
  147.                             
  148.                                 ballVal[f][index] -= 50;
  149.                             
  150.                             else    //    Not a row of three, but it is in a good direction.
  151.                                 
  152.                                 ballVal[f][index] -= 30;
  153.                         }
  154.                         else
  155.                         {                
  156.                             ballVal[f][index] -= 5;    //    A frienly neighbour is always worth something
  157.                         }
  158.                     }
  159.                 }
  160.             }
  161.         }
  162.     //    The ballVal entries now contain the malus points.
  163.     //    From this, compute the value for a ball:
  164.     //    This way, each entry represents the value for a ball on this field,
  165.     //    including the fact the player does have a ball.
  166.     //    (it also saves an substraction in the time-critical part of this function)
  167.  
  168.  
  169.         for (f = 0; f <= kFields; f++)
  170.         
  171.             for (index = 0; index < (1 << kDirections); index++)
  172.             
  173.                 ballVal[f][index] = kBallValue - ballVal[f][index];
  174.         
  175.     }
  176.     
  177. }
  178.  
  179.  
  180. #if defined(powerc)
  181. short
  182. Arnold (BoardPtr board, short player)
  183. {
  184.     short            boardValue;
  185.  
  186.  
  187.     InitArnold();    
  188. //    The initialisation, which is the worst part of this function, is done.
  189.  
  190.  
  191.     boardValue = 0;
  192.     {register unsigned char    d, index, f, owner;
  193.  
  194.     for (f = 1; f <= kFields; f++)
  195.     {
  196.         if (! (owner = board->field[f]))    //    The most likely case: an empty field.
  197.             continue;
  198.             
  199.     //    Field f is owned by someone; let's find out how much this ball is worth.
  200.     //    To do this, we need to fetch the right entry from the ballVal table.
  201.     //    The first index in the table, f, is the field number for the ball,
  202.     //    which determines the greatest risk factor: the distance from the edge.
  203.     //    The second index deals with connectivity, and must be computed here first.
  204.     //    The index is a 6-bit number with the bits set for the directions where
  205.     //    the current ball has friendly neighbours, reducing the risk.
  206.     //    All initialisations have been done above, the first time this function was called.
  207.             
  208.         for (d = right, index = 0; d < down; d++)
  209.             if (board->field[Neighbour (f, d)] == owner)    //    frienly neighbour in direction d...
  210.                 index |= (1 << d);                            //    ...so the corresponding bit is set
  211.  
  212.     //    We now have both indices in the ballVal table,
  213.     //    so the complete value of the ball is known (it's just there in the table)
  214.         
  215.         boardValue += (owner == player) ? ballVal[f][index] : - ballVal[f][index];
  216.     }}
  217.     return boardValue;
  218. }
  219. #endif
  220.  
  221. #if defined(__MWERKS__)
  222. //    The assemly stuff is included as a separate converted MPW library.
  223. //    I have not figured out how to compile assembly in MetroWerks.
  224. //    This works just as well, except the MetrowWerks version now depends
  225. //    upon MPW to generate the library, which should therefore be considered
  226. //    a source file.
  227. #endif
  228.  
  229. #if defined(THINK_C)
  230. short
  231. Arnold (BoardPtr board, short player)
  232. {
  233.     short            boardValue;
  234.  
  235.  
  236.     InitArnold();    
  237.     
  238. //    The initialisation, which is the worst part of this function, is done.
  239.  
  240.     asm    {
  241.     ;;            register usage:
  242.     ;;
  243.     ;;            a0 = board->field[0]    fixed
  244.     ;;            a1 = ballVal[f][0]        outer loop
  245.     ;;            a2 = _neighbour[f][0]    outer loop
  246.     ;;
  247.     ;;            d0 = boardValue         function result
  248.     ;;            d1 = index                bitmask, array index
  249.     ;;            d2 = _neighbour[f][d]
  250.     ;;            d3 = d                    loop counter
  251.     ;;            d4 = board->field[f]
  252.     ;;            d5 = f                    loop counter
  253.     ;;            d6 = player                fixed
  254.     
  255.     
  256.         movem.l    d3-d6/a2, -(a7)            ;;    save non-temp registers
  257.         
  258.         moveq    #0, d0                    ;;    initialisation
  259.         moveq    #0, d2
  260.         moveq    #0, d4
  261.         move    player, d6
  262.         movea.l    board, a0
  263.         lea.l    ballVal, a1
  264.         lea.l    _neighbour, a2
  265.         
  266.         moveq    #0, d5                    ;;    f = 1
  267.     @1    addq    #7, a2                    ;;    a2 = _neighbour[f][0]
  268.         adda.l    #128, a1                ;;    a1 = ballVal[f][0]
  269.         
  270.         move.b    1(a0, d5.w), d4            ;;    d4 = board->field[f]
  271.         beq        @6                        ;;    if (! (owner = board->field[f])) continue;
  272.  
  273.         moveq    #0, d1
  274.         moveq    #5, d3                    ;;    d = topleft
  275.         
  276.     @3    move.b    0(a2, d3.w), d2            ;;*    d2 = Neighbour (f, d)
  277.         cmp.b    0(a0, d2.w), d4            ;;    if (board->field[Neighbour (f, d)] == owner)
  278.         bne        @4
  279.         bset    d3, d1                    ;;    index |= (1 << d)
  280.     @4    dbra    d3, @3
  281.         lsl        #1, d1                    ;;    shorts (not bytes).
  282.     
  283.         cmp        d6, d4                    ;;    if (owner == player)
  284.         bne        @5
  285.         add        0(a1, d1.w), d0            ;;        boardValue += ballVal[f][index]
  286.         bra        @6                        ;;    else
  287.     @5    sub        0(a1, d1.w), d0            ;;        boardValue -= ballVal[f][index]
  288.     @6    addq    #1, d5
  289.         cmp        #kFields, d5
  290.         blt        @1
  291.         
  292.         movem.l    (a7)+, d3-d6/a2            ;;    restore non-temp registers
  293.         
  294.     }
  295. }
  296. #endif
  297.  
  298. #ifdef apple_c
  299.  
  300. //    In separate assembly file
  301.  
  302. #endif
  303.