github.com/gorgonia/agogo@v0.1.1/game/komi/zobrist.go (about)

     1  package komi
     2  
     3  import (
     4  	"math/rand"
     5  	"time"
     6  
     7  	"github.com/gorgonia/agogo/game"
     8  	"github.com/pkg/errors"
     9  )
    10  
    11  // zobrist is a data structure for calculating Zobrist hashes.
    12  // https://en.wikipedia.org/wiki/Zobrist_hashing
    13  //
    14  // The original implementation uses gorgonia's tensor
    15  // Fundamentally it is a (BOARDSIZE, BOARDSIZE 2) 3-Tensor, which stores the hash state.
    16  // The hash is then calculated from that
    17  // But in light of optimizing all the things for memory, it's been stripped down to the absolute fundamentals:
    18  //	- a backing storage
    19  //	- an iterator for quick access
    20  //
    21  // The semantics of the iterator has also been updated.  Given that the board will be updated
    22  // with a game.Single, instead of a game.Coord, another way to think of the table is as a matrix of
    23  // (BOARDSIZE * BOARDSIZE, 2). The design of the iterator is geared around that.
    24  type zobrist struct {
    25  	table  [361 * 2]int32 // backing storage
    26  	it     [][]int32      // iterator for the normal hash
    27  	hash   int32
    28  	koHash int32
    29  	size   int
    30  }
    31  
    32  func makeZobrist(m, n int) zobrist {
    33  	r := rand.New(rand.NewSource(time.Now().UnixNano()))
    34  	size := m * n
    35  	retVal := zobrist{
    36  		size: size,
    37  	}
    38  	for i := range retVal.table[:size+1] {
    39  		retVal.table[i] = r.Int31()
    40  	}
    41  	retVal.makeIterator()
    42  	return retVal
    43  }
    44  
    45  // update calculates the hash and returns it. As per the namesake, the calculated hash is updaated as a side effect.
    46  func (z *zobrist) update(m game.PlayerMove) (int32, error) {
    47  	switch game.Colour(m.Player) {
    48  	case game.Black:
    49  		z.hash ^= z.it[m.Single][0]
    50  		return z.hash, nil
    51  	case game.White:
    52  		z.hash ^= z.it[m.Single][1]
    53  		return z.hash, nil
    54  	default:
    55  		return 0, errors.Errorf("Cannot update hash for %v", m)
    56  	}
    57  }
    58  
    59  func (z *zobrist) clone() zobrist {
    60  	retVal := zobrist{
    61  		hash:   z.hash,
    62  		koHash: z.koHash,
    63  		size:   z.size,
    64  	}
    65  	copy(retVal.table[:], z.table[:])
    66  	retVal.makeIterator()
    67  	return retVal
    68  }