home *** CD-ROM | disk | FTP | other *** search
- #define ARNOLD_C
- #include "Arnold.h"
-
- // This is my favorite BoardJudgeProc so far.
-
- // Not static to share with assembly
- #pragma segment More
-
- short ballVal[kFields+1][1 << kDirections]; // Yep, it's big: 62*64*2 bytes
- Boolean firstTime = true;
-
-
- // Init stuff split off; body will be assembly for 68K Macs
- void
- InitArnold (void)
- {
- register unsigned char d, index, f;
-
- #define kBallValue 1100
- // A bit more than distVal[0] computed below, to account for connectivity bonus points
-
- if (firstTime) // initialise values for the value lookup tables. A lot of work once.
- {
- unsigned char bestNeighbour[kFields+1][2];
- short value[kFields+1];
- short distVal[10];
- short dist;
-
- firstTime = false;
-
- // Compute a even-slightly-faster-than-exponential relation between distance and danger.
-
- for (dist = 9 + 1; --dist >= 0;)
- {
- if (dist == 9)
- distVal[dist] = 0;
- else if (dist == 8)
- distVal[dist] = 1;
- else
- distVal[dist] = 2 * distVal[dist+1] + distVal[dist+2];
- }
- // Use the average of the dangers in all directions as the field value
-
- value[0] = distVal[0];
-
- for (f = 1; f <= kFields; f++)
- {
- value[f] = 0;
-
- for (d = right; d < down; d++)
- value[f] += distVal[Distance (f, d)];
-
- value[f] /= 6;
- }
-
- // Find out what the good neighbours are.
- // A good neighbour is a neighbour in the most profitable direction to go.
-
- for (f = 1; f <= kFields; f++)
- {
- short best = 1 << 10;
-
- for (d = right; d < down; d++)
- {
- if (value[Neighbour (f, d)] < best)
- {
- best = value[Neighbour (f, d)];
- bestNeighbour[f][0] = Neighbour (f, d);
- }
- }
- }
-
- // find good neighbour 2 (may be the same as 1)
-
- for (f = kFields + 1; --f > 0; )
- {
- short best = 1 << 10;
-
- for (d = right; d < down; d++)
- {
- if (value[Neighbour (f, d)] < best)
- {
- best = value[Neighbour (f, d)];
- bestNeighbour[f][1] = Neighbour (f, d);
- }
- }
- }
- // Based on the positional value, compute a value depending on connectivity too.
-
- for (index = 0; index < (1 << kDirections); index++)
- ballVal[0][index] = kBallValue;
-
- for (f = 1; f <= kFields; f++) // for all fields
- {
- for (index = 0; index < (1 << kDirections); index++) // for all combinations of neighbours
- {
- // What we must define now, is the value representing the resulting
- // number of malus points for the ball on field f.
- // For this, we can start with the generic distribution value[f],
- // and add bonus points (= substract malus points) for good connections.
-
- ballVal[f][index] = value[f]; // start with positional value
-
- // Now some ad-hoc abracadabra with connections
-
- for (d = right; d < down; d++) // for all directions
- {
- // index is a bitmap;
- // the bits represent the neighbour fields in each of the six directions.
- // For example, an index of 6 (000110) means the directions 1 and 2 only.
- // For each field, all possible combinations of present/absent friendly
- // neighbours can thus be represented in 64 combinations.
- // The ballVal table has one enrty (with 64 combinations) for each field.
- // The values for the ballVal table are computed below.
-
- if ((index >> d) & 1) // bit for this direction set in index
- {
- if ( Neighbour (f, d) == bestNeighbour[f][0]
- && bestNeighbour[f][0] == bestNeighbour[f][1])
- {
- // It is a very good connection, but it will be counted twice,
- // so we must compensate for this.
-
- if ((index >> ((d + 3) % kDirections)) & 1) // opposite direction too!
-
- // This is extra profitable.
- // The ball is connected in a 'good' direction, so it gets some
- // protection, but is connected in a 'bad' direction too,
- // meaning it gives protection to a less fortunate neighbour.
- // The protection is actually larger than this neighbour knows,
- // since it is backed up up by the neighbour in the good direction
- // as well, which the unfortunate neighbour cannot 'see':
- // it can only look at direct neighbours.
- // There's nothing wrong in correcting its value at this ball;
- // after all, only the grand total over all balls counts.
-
- ballVal[f][index] -= 40; // counted twice
-
- else
-
- ballVal[f][index] -= 20; // counted twice
- }
- else if ( Neighbour (f, d) == bestNeighbour[f][0]
- || Neighbour (f, d) == bestNeighbour[f][1])
- {
- if ((index >> ((d + 3) % kDirections)) & 1) // opposite direction too!
-
- ballVal[f][index] -= 50;
-
- else // Not a row of three, but it is in a good direction.
-
- ballVal[f][index] -= 30;
- }
- else
- {
- ballVal[f][index] -= 5; // A frienly neighbour is always worth something
- }
- }
- }
- }
- }
- // The ballVal entries now contain the malus points.
- // From this, compute the value for a ball:
- // This way, each entry represents the value for a ball on this field,
- // including the fact the player does have a ball.
- // (it also saves an substraction in the time-critical part of this function)
-
-
- for (f = 0; f <= kFields; f++)
-
- for (index = 0; index < (1 << kDirections); index++)
-
- ballVal[f][index] = kBallValue - ballVal[f][index];
-
- }
-
- }
-
-
- #if defined(powerc)
- short
- Arnold (BoardPtr board, short player)
- {
- short boardValue;
-
-
- InitArnold();
- // The initialisation, which is the worst part of this function, is done.
-
-
- boardValue = 0;
- {register unsigned char d, index, f, owner;
-
- for (f = 1; f <= kFields; f++)
- {
- if (! (owner = board->field[f])) // The most likely case: an empty field.
- continue;
-
- // Field f is owned by someone; let's find out how much this ball is worth.
- // To do this, we need to fetch the right entry from the ballVal table.
- // The first index in the table, f, is the field number for the ball,
- // which determines the greatest risk factor: the distance from the edge.
- // The second index deals with connectivity, and must be computed here first.
- // The index is a 6-bit number with the bits set for the directions where
- // the current ball has friendly neighbours, reducing the risk.
- // All initialisations have been done above, the first time this function was called.
-
- for (d = right, index = 0; d < down; d++)
- if (board->field[Neighbour (f, d)] == owner) // frienly neighbour in direction d...
- index |= (1 << d); // ...so the corresponding bit is set
-
- // We now have both indices in the ballVal table,
- // so the complete value of the ball is known (it's just there in the table)
-
- boardValue += (owner == player) ? ballVal[f][index] : - ballVal[f][index];
- }}
- return boardValue;
- }
- #endif
-
- #if defined(__MWERKS__)
- // The assemly stuff is included as a separate converted MPW library.
- // I have not figured out how to compile assembly in MetroWerks.
- // This works just as well, except the MetrowWerks version now depends
- // upon MPW to generate the library, which should therefore be considered
- // a source file.
- #endif
-
- #if defined(THINK_C)
- short
- Arnold (BoardPtr board, short player)
- {
- short boardValue;
-
-
- InitArnold();
-
- // The initialisation, which is the worst part of this function, is done.
-
- asm {
- ;; register usage:
- ;;
- ;; a0 = board->field[0] fixed
- ;; a1 = ballVal[f][0] outer loop
- ;; a2 = _neighbour[f][0] outer loop
- ;;
- ;; d0 = boardValue function result
- ;; d1 = index bitmask, array index
- ;; d2 = _neighbour[f][d]
- ;; d3 = d loop counter
- ;; d4 = board->field[f]
- ;; d5 = f loop counter
- ;; d6 = player fixed
-
-
- movem.l d3-d6/a2, -(a7) ;; save non-temp registers
-
- moveq #0, d0 ;; initialisation
- moveq #0, d2
- moveq #0, d4
- move player, d6
- movea.l board, a0
- lea.l ballVal, a1
- lea.l _neighbour, a2
-
- moveq #0, d5 ;; f = 1
- @1 addq #7, a2 ;; a2 = _neighbour[f][0]
- adda.l #128, a1 ;; a1 = ballVal[f][0]
-
- move.b 1(a0, d5.w), d4 ;; d4 = board->field[f]
- beq @6 ;; if (! (owner = board->field[f])) continue;
-
- moveq #0, d1
- moveq #5, d3 ;; d = topleft
-
- @3 move.b 0(a2, d3.w), d2 ;;* d2 = Neighbour (f, d)
- cmp.b 0(a0, d2.w), d4 ;; if (board->field[Neighbour (f, d)] == owner)
- bne @4
- bset d3, d1 ;; index |= (1 << d)
- @4 dbra d3, @3
- lsl #1, d1 ;; shorts (not bytes).
-
- cmp d6, d4 ;; if (owner == player)
- bne @5
- add 0(a1, d1.w), d0 ;; boardValue += ballVal[f][index]
- bra @6 ;; else
- @5 sub 0(a1, d1.w), d0 ;; boardValue -= ballVal[f][index]
- @6 addq #1, d5
- cmp #kFields, d5
- blt @1
-
- movem.l (a7)+, d3-d6/a2 ;; restore non-temp registers
-
- }
- }
- #endif
-
- #ifdef apple_c
-
- // In separate assembly file
-
- #endif
-