Open In App

Minimax Algorithm in Game Theory | Set 5 (Zobrist Hashing)

Last Updated : 26 Apr, 2023
Improve
Improve
Like Article
Like
Save
Share
Report

Previous posts on this topic : Minimax Algorithm in Game Theory, Evaluation Function in Game Theory, Tic-Tac-Toe AI – Finding optimal move, Alpha-Beta Pruning.
Zobrist Hashing is a hashing function that is widely used in 2 player board games. It is the most common hashing function used in transposition table. Transposition tables basically store the evaluated values of previous board states, so that if they are encountered again we simply retrieve the stored value from the transposition table. We will be covering transposition tables in a later article. In this article we shall take the example of chess board and implement a hashing function for that.

Pseudocode :

// A matrix with random numbers initialized once
Table[#ofBoardCells][#ofPieces] 

// Returns Zobrist hash function for current conf-
// iguration of board.
function findhash(board):
    hash = 0
    for each cell on the board :
        if cell is not empty :
            piece = board[cell]
            hash ^= table[cell][piece]
    return hash

Explanation :

The idea behind Zobrist Hashing is that for a given board state, if there is a piece on a given cell, we use the random number of that piece from the corresponding cell in the table. 
If more bits are there in the random number the lesser chance of a hash collision. Therefore 64 bit numbers are commonly used as the standard and it is highly unlikely for a hash collision to occur with such large numbers. The table has to be initialized only once during the programs execution. 
Also the reason why Zobrist Hashing is widely used in board games is because when a player makes a move, it is not necessary to recalculate the hash value from scratch. Due to the nature of XOR operation we can simply use few XOR operations to recalculate the hash value.

Implementation :

We shall try to find a hash value for the given board configuration.

CPP




// A program to illustrate Zobrist Hashing Algorithm
#include <bits/stdc++.h>
using namespace std;
 
unsigned long long int ZobristTable[8][8][12];
mt19937 mt(01234567);
 
// Generates a Random number from 0 to 2^64-1
unsigned long long int randomInt()
{
    uniform_int_distribution<unsigned long long int>
                                 dist(0, UINT64_MAX);
    return dist(mt);
}
 
// This function associates each piece with
// a number
int indexOf(char piece)
{
    if (piece=='P')
        return 0;
    if (piece=='N')
        return 1;
    if (piece=='B')
        return 2;
    if (piece=='R')
        return 3;
    if (piece=='Q')
        return 4;
    if (piece=='K')
        return 5;
    if (piece=='p')
        return 6;
    if (piece=='n')
        return 7;
    if (piece=='b')
        return 8;
    if (piece=='r')
        return 9;
    if (piece=='q')
        return 10;
    if (piece=='k')
        return 11;
    else
        return -1;
}
 
// Initializes the table
void initTable()
{
    for (int i = 0; i<8; i++)
      for (int j = 0; j<8; j++)
        for (int k = 0; k<12; k++)
          ZobristTable[i][j][k] = randomInt();
}
 
// Computes the hash value of a given board
unsigned long long int computeHash(char board[8][9])
{
    unsigned long long int h = 0;
    for (int i = 0; i<8; i++)
    {
        for (int j = 0; j<8; j++)
        {
            if (board[i][j]!='-')
            {
                int piece = indexOf(board[i][j]);
                h ^= ZobristTable[i][j][piece];
            }
        }
    }
    return h;
}
 
// Main Function
int main()
{
    // Uppercase letters are white pieces
    // Lowercase letters are black pieces
    char board[8][9] =
    {
        "---K----",
        "-R----Q-",
        "--------",
        "-P----p-",
        "-----p--",
        "--------",
        "p---b--q",
        "----n--k"
    };
 
    initTable();
 
    unsigned long long int hashValue = computeHash(board);
    printf("The hash value is     : %llu\n", hashValue);
 
    //Move the white king to the left
    char piece = board[0][3];
 
    board[0][3] = '-';
    hashValue ^= ZobristTable[0][3][indexOf(piece)];
 
    board[0][2] = piece;
    hashValue ^= ZobristTable[0][2][indexOf(piece)];
 
 
    printf("The new hash value is : %llu\n", hashValue);
 
    // Undo the white king move
    piece = board[0][2];
 
    board[0][2] = '-';
    hashValue ^= ZobristTable[0][2][indexOf(piece)];
 
    board[0][3] = piece;
    hashValue ^= ZobristTable[0][3][indexOf(piece)];
 
    printf("The old hash value is : %llu\n", hashValue);
 
    return 0;
}


Java




// A Java program to illustrate Zobrist Hashing Algorithm
 
import java.util.*;
 
public class GFG {
 
  public static List<List<List<Integer>>> ZobristTable = new ArrayList<>();
 
  // mt19937 mt(01234567);
  public static void initialise() {
    for (int i = 0; i < 8; i++) {
      ZobristTable.add(new ArrayList<>());
      for (int j = 0; j < 8; j++) {
        ZobristTable.get(i).add(new ArrayList<>());
        for (int k = 0; k < 12; k++) {
          ZobristTable.get(i).get(j).add(0);
        }
      }
    }
  }
  // Generates a Random number from 0 to 2^64-1
  public static long randomLong() {
    long min = 0L;
    long max = (long) Math.pow(2, 64);
    Random rnd = new Random();
    return rnd.nextLong() * (max - min) + min;
  }
 
  // This function associates each piece with a number
  public static int indexOf(char piece)
  {
    if (piece=='P')
      return 0;
    if (piece=='N')
      return 1;
    if (piece=='B')
      return 2;
    if (piece=='R')
      return 3;
    if (piece=='Q')
      return 4;
    if (piece=='K')
      return 5;
    if (piece=='p')
      return 6;
    if (piece=='n')
      return 7;
    if (piece=='b')
      return 8;
    if (piece=='r')
      return 9;
    if (piece=='q')
      return 10;
    if (piece=='k')
      return 11;
    else
      return -1;
  }
 
  // Initializes the table
  public static void initTable() {
    for (int i = 0; i < 8; i++) {
      for (int j = 0; j < 8; j++) {
        for (int k = 0; k < 12; k++) {
          Random rnd = new Random();
          ZobristTable.get(i).get(j).set(k, (int) randomLong());
        }
      }
    }
  }
 
  // Computes the hash value of a given board
  public static int computeHash(List<List<Character>> board) {
    int h = 0;
    for (int i = 0; i < 8; i++) {
      for (int j = 0; j < 8; j++) {
        if (board.get(i).get(j) != '-') {
          int piece = indexOf(board.get(i).get(j));
          h ^= ZobristTable.get(i).get(j).get(piece);
        }
      }
    }
    return h;
  }
 
  public static void main(String[] args) {
    // Main Function
    // Uppercase letters are white pieces
    // Lowercase letters are black pieces
    List<List<Character>> board = new ArrayList<>();
    board.add(new ArrayList<>(Arrays.asList('-', '-', '-', 'K', '-', '-', '-', '-')));
    board.add(new ArrayList<>(Arrays.asList('-', 'R', '-', '-', '-', '-', 'Q', '-')));
    board.add(new ArrayList<>(Arrays.asList('-', '-', '-', 'K', '-', '-', '-', '-')));
    board.add(new ArrayList<>(Arrays.asList('-', '-', '-', '-', '-', '-', '-', '-')));
    board.add(new ArrayList<>(Arrays.asList('-', 'P', '-', '-', '-', '-', 'p', '-')));
    board.add(new ArrayList<>(Arrays.asList('-', '-', '-', '-', '-', 'p', '-', '-')));
    board.add(new ArrayList<>(Arrays.asList('-', '-', '-', '-', '-', '-', '-', '-')));
    board.add(new ArrayList<>(Arrays.asList('p', '-', '-', '-', 'b', '-', '-', 'q')));
    board.add(new ArrayList<>(Arrays.asList('-', '-', '-', '-', 'n', '-', '-', 'k')));
 
 
    initialise();
    initTable();
 
    int hashValue = computeHash(board);
    System.out.println("The hash value is     : " + hashValue);
 
    // Move the white king to the left
    char piece = board.get(0).get(3);
 
    board.get(0).set(3, '-');
 
    hashValue ^= ZobristTable.get(0).get(3).get(indexOf(piece));
 
    board.get(0).set(2, piece);
    hashValue ^= ZobristTable.get(0).get(2).get(indexOf(piece));
 
    System.out.println("The new hash value is     : " + hashValue);
 
    // Undo the white king move
    piece = board.get(0).get(2);
 
    board.get(0).set(2, '-');
 
    hashValue ^= ZobristTable.get(0).get(2).get(indexOf(piece));
 
    board.get(0).set(3, piece);
    hashValue ^= ZobristTable.get(0).get(3).get(indexOf(piece));
 
    System.out.println("The new hash value is     : " + hashValue);
  }
 
}
 
// This code is contributed by Vaibhav


Python3




# A program to illustrate Zobrist Hashing Algorithm
# python code implementation
import random
 
# mt19937 mt(01234567);
  
# Generates a Random number from 0 to 2^64-1
def randomInt():
    min = 0
    max = pow(2, 64)
    return random.randint(min, max)
  
# This function associates each piece with
# a number
def indexOf(piece):
    if (piece=='P'):
        return 0
    elif (piece=='N'):
        return 1
    elif (piece=='B'):
        return 2
    elif (piece=='R'):
        return 3
    elif (piece=='Q'):
        return 4
    elif (piece=='K'):
        return 5
    elif (piece=='p'):
        return 6
    elif (piece=='n'):
        return 7
    elif (piece=='b'):
        return 8
    elif (piece=='r'):
        return 9
    elif (piece=='q'):
        return 10
    elif (piece=='k'):
        return 11
    else:
        return -1
  
# Initializes the table
def initTable():
    # 8x8x12 array
    ZobristTable = [[[randomInt() for k in range(12)] for j in range(8)] for i in range(8)]
    return ZobristTable
  
# Computes the hash value of a given board
def computeHash(board, ZobristTable):
    h = 0
    for i in range(8):
        for j in range(8):
            if (board[i][j] != '-'):
                piece = indexOf(board[i][j])
                h ^= ZobristTable[i][j][piece]
    return h
  
# Main Function
# Uppercase letters are white pieces
# Lowercase letters are black pieces
board = [
    "---K----",
    "-R----Q-",
    "--------",
    "-P----p-",
    "-----p--",
    "--------",
    "p---b--q",
    "----n--k"
]
 
ZobristTable = initTable()
 
hashValue = computeHash(board, ZobristTable)
print("The hash value is     : " + str(hashValue))
 
#Move the white king to the left
piece = board[0][3]
 
board[0] = list(board[0])
board[0][3] = '-'
board[0] = ''.join(board[0])
 
hashValue ^= ZobristTable[0][3][indexOf(piece)]
 
board[0] = list(board[0])
board[0][2] = piece
board[0] = ''.join(board[0])
hashValue ^= ZobristTable[0][2][indexOf(piece)]
 
 
print("The new hash value is : " + str(hashValue))
 
# Undo the white king move
piece = board[0][2]
 
board[0] = list(board[0])
board[0][2] = '-'
board[0] = ''.join(board[0])
 
hashValue ^= ZobristTable[0][2][indexOf(piece)]
 
board[0] = list(board[0])
board[0][3] = piece
board[0] = ''.join(board[0])
hashValue ^= ZobristTable[0][3][indexOf(piece)]
 
print("The old hash value is : " + str(hashValue))


C#




using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
 
// C# program for the above approach
// A program to illustrate Zobrist Hashing Algorithm
// javascript code implementation
 
 
class HelloWorld {
 
    public static List<List<List<int>>> ZobristTable = new List<List<List<int>>>();
 
    // mt19937 mt(01234567);
    public static void initialise(){
             
        for(int i = 0; i < 8; i++){
            ZobristTable.Add(new List<List<int>>());
            for(int j = 0; j < 8; j++){
                ZobristTable[i].Add(new List<int>());
                for(int k = 0; k < 12; k++){
                    ZobristTable[i][j].Add(0);
                }
            }           
        }
 
    }
    // Generates a Random number from 0 to 2^64-1
    public static int randomInt() {
        int min = 0;
        int max = (int)Math.Pow(2, 64);
        Random rnd = new Random();
        return rnd.Next() * (max - min) + min;
    }
 
    // This function associates each piece with
    // a number
    public static int indexOf(char piece)
    {
        if (piece=='P')
            return 0;
        if (piece=='N')
            return 1;
        if (piece=='B')
            return 2;
        if (piece=='R')
            return 3;
        if (piece=='Q')
            return 4;
        if (piece=='K')
            return 5;
        if (piece=='p')
            return 6;
        if (piece=='n')
            return 7;
        if (piece=='b')
            return 8;
        if (piece=='r')
            return 9;
        if (piece=='q')
            return 10;
        if (piece=='k')
            return 11;
        else
            return -1;
    }
 
    // Initializes the table
    public static void initTable()
    {
        for (int i = 0; i<8; i++){
            for(int j = 0; j < 8; j++){
                for(int k = 0; k < 12; k++){
                    Random rnd = new Random();
                    ZobristTable[i][j][k] = rnd.Next();
                }
            }
        }
 
    }
 
    // Computes the hash value of a given board
    public static int computeHash(List<List<char>> board)
    {
        int h = 0;
        for (int i = 0; i<8; i++)
        {
            for (int j = 0; j<8; j++)
            {
                if (board[i][j]!='-')
                {
                    int piece = indexOf(board[i][j]);
                    h ^= ZobristTable[i][j][piece];
                }
            }
        }
        return h;
    }
  
    static void Main() {
        // Main Function
        // Uppercase letters are white pieces
        // Lowercase letters are black pieces
        List<List<char>> board = new List<List<char>>();
        board.Add(new List<char>(){'-', '-', '-', 'K', '-', '-', '-', '-'});
        board.Add(new List<char>(){'-', 'R', '-', '-', '-', '-', 'Q', '-'});
        board.Add(new List<char>(){'-', '-', '-', 'K', '-', '-', '-', '-'});
        board.Add(new List<char>(){'-', '-', '-', '-', '-', '-', '-', '-'});
        board.Add(new List<char>(){'-', 'P', '-', '-', '-', '-', 'p', '-'});
        board.Add(new List<char>(){'-', '-', '-', '-', '-', 'p', '-', '-', });
        board.Add(new List<char>(){'-', '-', '-', '-', '-', '-', '-', '-'});
        board.Add(new List<char>(){'p', '-', '-', '-', 'b', '-', '-', 'q'});
        board.Add(new List<char>(){'-', '-', '-', '-', 'n', '-', '-', 'k'});
 
        initialise();
        initTable();
         
         
        int hashValue = computeHash(board);
        Console.WriteLine("The hash value is     : " + hashValue);
 
        //Move the white king to the left
        char piece = board[0][3];
 
        board[0][3] = '-';
 
        hashValue ^= ZobristTable[0][3][indexOf(piece)];
 
        board[0][2] = piece;
        hashValue ^= ZobristTable[0][2][indexOf(piece)];
 
 
        Console.WriteLine("The new hash value is : " + hashValue);
 
        // Undo the white king move
        piece = board[0][2];
 
        board[0][2] = '-';
        hashValue ^= ZobristTable[0][2][indexOf(piece)];
 
        board[0][3] = piece;
        hashValue ^= ZobristTable[0][3][indexOf(piece)];
 
        Console.WriteLine("The old hash value is : " + hashValue);
    }
}
 
// The code is contributed by Nidhi goel.


Javascript




// A program to illustrate Zobrist Hashing Algorithm
// javascript code implementation
 
let ZobristTable = new Array(8);
for(let i = 0; i < 8; i++){
    ZobristTable[i] = new Array(8);
}
for(let i = 0; i < 8; i++){
    for(let j = 0; j < 8; j++){
        ZobristTable[i][j] = new Array(12);
    }
}
 
// mt19937 mt(01234567);
  
// Generates a Random number from 0 to 2^64-1
function randomInt() {
    let min = 0;
    let max = Math.pow(2, 64);
    return Math.floor(Math.random() * (max - min) + min);
}
  
// This function associates each piece with
// a number
function indexOf(piece)
{
    if (piece=='P')
        return 0;
    if (piece=='N')
        return 1;
    if (piece=='B')
        return 2;
    if (piece=='R')
        return 3;
    if (piece=='Q')
        return 4;
    if (piece=='K')
        return 5;
    if (piece=='p')
        return 6;
    if (piece=='n')
        return 7;
    if (piece=='b')
        return 8;
    if (piece=='r')
        return 9;
    if (piece=='q')
        return 10;
    if (piece=='k')
        return 11;
    else
        return -1;
}
  
// Initializes the table
function initTable()
{
    for (let i = 0; i<8; i++)
      for (let j = 0; j<8; j++)
        for (let k = 0; k<12; k++){
            ZobristTable[i][j][k] = randomInt();
        }
           
}
  
// Computes the hash value of a given board
function computeHash(board)
{
    let h = 0;
    for (let i = 0; i<8; i++)
    {
        for (let j = 0; j<8; j++)
        {
            if (board[i][j]!='-')
            {
                let piece = indexOf(board[i][j]);
                h ^= ZobristTable[i][j][piece];
            }
        }
    }
    return h;
}
  
// Main Function
// Uppercase letters are white pieces
// Lowercase letters are black pieces
let board = [
    "---K----",
    "-R----Q-",
    "--------",
    "-P----p-",
    "-----p--",
    "--------",
    "p---b--q",
    "----n--k"
];
 
initTable();
 
let hashValue = computeHash(board);
console.log("The hash value is     : " + hashValue);
 
//Move the white king to the left
let piece = board[0][3];
 
board[0] = board[0].split('');
board[0][3] = '-';
board[0] = board[0].join("");
 
hashValue ^= ZobristTable[0][3][indexOf(piece)];
 
board[0] = board[0].split('');
board[0][2] = piece;
board[0] = board[0].join("");
hashValue ^= ZobristTable[0][2][indexOf(piece)];
 
 
console.log("The new hash value is : " + hashValue);
 
// Undo the white king move
piece = board[0][2];
 
board[0] = board[0].split('');
board[0][2] = '-';
board[0] = board[0].join("");
 
hashValue ^= ZobristTable[0][2][indexOf(piece)];
 
board[0][3] = piece;
hashValue ^= ZobristTable[0][3][indexOf(piece)];
 
console.log("The old hash value is : " + hashValue);
 
// The code is contributed by Nidhi goel.


Output :

The hash value is     : 14226429382419125366
The new hash value is : 15124945578233295113
The old hash value is : 14226429382419125366

 



Similar Reads

Minimax Algorithm in Game Theory | Set 1 (Introduction)
Minimax is a kind of backtracking algorithm that is used in decision making and game theory to find the optimal move for a player, assuming that your opponent also plays optimally. It is widely used in two player turn-based games such as Tic-Tac-Toe, Backgammon, Mancala, Chess, etc.In Minimax the two players are called maximizer and minimizer. The
9 min read
Minimax Algorithm in Game Theory | Set 4 (Alpha-Beta Pruning)
Prerequisites: Minimax Algorithm in Game Theory, Evaluation Function in Game TheoryAlpha-Beta pruning is not actually a new algorithm, but rather an optimization technique for the minimax algorithm. It reduces the computation time by a huge factor. This allows us to search much faster and even go into deeper levels in the game tree. It cuts off bra
11 min read
Introduction to Evaluation Function of Minimax Algorithm in Game Theory
Prerequisite: Minimax Algorithm in Game TheoryAs seen in the above article, each leaf node had a value associated with it. We had stored this value in an array. But in the real world when we are creating a program to play Tic-Tac-Toe, Chess, Backgammon, etc. we need to implement a function that calculates the value of the board depending on the pla
8 min read
Finding optimal move in Tic-Tac-Toe using Minimax Algorithm in Game Theory
Prerequisites: Minimax Algorithm in Game Theory, Evaluation Function in Game TheoryLet us combine what we have learnt so far about minimax and evaluation function to write a proper Tic-Tac-Toe AI (Artificial Intelligence) that plays a perfect game. This AI will consider all possible scenarios and makes the most optimal move. Finding the Best Move :
15+ min read
Game Theory (Normal form game) | Set 2 (Game with Pure Strategy)
Game Theory (Normal – form game) | Set 1 (Introduction) Please go through the above article before proceeding. Given a payoff matrix. The task is to find the optimum strategies of the players. Solution: Player A is having 3 strategies - 1, 2 and 3, and player B is also having 3 strategies - 1, 2 and 3. Step 1: Find row minimum values for each row a
2 min read
Game Theory (Normal-form game) | Set 3 (Game with Mixed Strategy)
Consider the following payoff matrix with respect to player A and solve it optimally. Solution: If a game has no saddle point then the game is said to have mixed strategy. Step 1: Find out the row minimum and column maximum. Step 2: Find out the minimax and maximin values. Since minimax and maximin value of this game are not equal, this game has no
4 min read
Game Theory (Normal-form Game) | Set 7 (Graphical Method [M X 2] Game)
The payoff matrix of an M * 2 game consists of M rows and two columns. This article will discuss how to solve an M * 2 game by graphical method. Also, this article will discuss if more than two lines intersect the same point in the graph then how can a 2 * 2 payoff matrix be formed. Consider the below problem: Solution: First check whether the prob
3 min read
Game Theory (Normal-form Game) | Set 6 (Graphical Method [2 X N] Game)
The payoff matrix of a 2 * N game consists of 2 rows and N columns . This article will discuss how to solve a 2 * N game by graphical method. Consider the below 2 * 5 game: Solution: First check the saddle point of the game. This game has no saddle point. Step 1: Reduce the size of the payoff matrix by applying dominance property, if it exists. Thi
3 min read
Game Theory (Normal - form game) | Set 1 (Introduction)
Game theory is a mathematical model used for decision making. It has applications in all fields of social science, as well as in logic and computer science. Game theory has come to play an increasingly important role in logic and in computer science. To be fully defined, a game must specify the following elements: the players of the game, the infor
4 min read
Game Theory (Normal-form Game) | Set 5 (Dominance Property-Mixed Strategy)
This article discusses how to solve a game by the dominance property with mixed strategy. Consider the below game: Solution: Find out the row minimum and column maximum values. Here Minimax value is not equal to Maximin so this game has no saddle point. Now proceed with dominance property to reduce the rows and the columns. Reducing the row and the
2 min read
Article Tags :
Practice Tags :