গেম অব লাইফ বা সংক্ষেপে লাইফ হচ্ছে একটি সেলুলার অটোমাটন। এটি একটি জিরো-প্লেয়ার গেম, অর্থাৎ এটি কারো হস্তক্ষেপ ছাড়াই প্রাথমিক একটি অবস্থা থেকে নিজে নিজে চলতে থাকে।

১৯৭০ সালে গেমটি উদ্ভাবন করেন ব্রিটিশ গণিতবিদ জন হর্টন কনওয়ে (২৬ ডিসেম্বর ১৯৩৭ – ১১ এপ্রিল ২০২০)। ফাইনাইট গ্রুপ (finite groups), নট থিউরি (knot theory), সংখ্যাতত্ত্ব, কম্বিনেটরিয়াল গেম থিউরি, কোডিং থিউরিসহ অনেক বিষয়ে তিনি কাজ করেছেন। তবে গেম অব লাইফ উদ্ভাবনের জন্য তিনি বিশেষভাবে পরিচিত।

১৯৪০-এর দশকের শেষ দিকে জন ভন নিউম্যান জীবনকে (Life) এমন একটি ‘সৃষ্টি’ হিসেবে আখ্যায়িত করেন, যা নিজেই নিজের প্রতিরূপ সৃষ্টি করতে পারে এবং একটি টুরিং মেশিনকে সিমুলেট করতে পারে। ভন নিউম্যান এর একটি বাস্তব মডেল তৈরি করতে চেয়েছিলেন, কিন্তু তৎকালীন প্রযুক্তির সীমাবদ্ধতার কারণে করতে পারেননি। তাঁর ধারণাকে সিমুলেট করতে স্তানিসোয়াফ উলাম (Stanisław Ulam) আবিষ্কার করেন সেলুলার অটোমাটা। নিউম্যান পরে সেলুলার অটোমাটন ব্যবহার করে তাঁর ডিজাইনটি তৈরি করেন।

নিউম্যানের ডিজাইনটি ছিল অনেক জটিল। অন্যান্য গবেষকগণ আরো সরল প্রকৃতির লাইফ তৈরি করেছেন। কনওয়ে তাদের মধ্যে অন্যতম। কনওয়ে উলামের ডিজাইনের চেয়ে একটু ভিন্ন নিয়ম-কানুন নিয়ে গবেষণা করে গেম অব লাইফ তৈরি করেন। কনওয়ে গেমটির নিয়ম বাছাই করেছেন খুব চিন্তাভাবনা করে, যাতে জনসংখ্যার হঠাৎ বিস্ফোরণ (অতিবৃদ্ধি) না হয়, ফলাফল এলোমেলো ও অনিশ্চিত হয় ইত্যাদি।

গেম অব লাইফ ১৯৭০ সালে সায়েন্টিফিক আমেরিকানে প্রথম প্রকাশিত হয়।

নিয়ম

গেমটি একটি দ্বিমাত্রিক গ্রিড নিয়ে গঠিত। এই গ্রিড অসীম পর্যন্ত বিস্তৃত। গ্রিডের প্রতিটি ঘর সম্ভাব্য দুটি অবস্থার যেকোনো একটি অবস্থায় থাকে : জীবিত অথবা মৃত (অন/অফ)। প্রতিটি ঘরের সাথে তার আশেপাশের আটটি ঘরের মিথস্ক্রিয়া (interaction) হয়। আটটি ঘর হচ্ছে চারপাশের চারটি এবং কোনাকুনি চারটি।

গেমটি ধাপে ধাপে সম্পন্ন হয়। যেকোনো ধাপে নির্দিষ্ট ঘটনা একই সময়ে গ্রিডের প্রতিটি ঘরে সম্পন্ন হয়। প্রতি ধাপে নিচের চারটি ঘটনা ঘটে :

  • যেকোনো জীবিত ঘরের যদি দুটির কম জীবিত প্রতিবেশী থাকে, তাহলে ঘরটি মারা যায়।
  • যেকোনো জীবিত ঘরের যদি দুই বা তিনটি জীবিত প্রতিবেশী থাকে, তাহলে ঘরটি পরের ধাপে বেঁচে থাকে।
  • যেকোনো জীবিত ঘরের যদি তিনটির বেশি জীবিত প্রতিবেশী থাকে, তাহলে ঘরটি মারা যায় (সংখ্যাধিক্যের কারণে)।
  • যেকোনো মৃত ঘরের যদি ঠিক তিনটি জীবিত প্রতিবেশী থাকে, তাহলে ঘরটি জীবিত হয়ে ওঠে (নতুন করে জন্মায়)।

এই চারটি ধাপকে তিনটি ধাপে সংকুচিত করা যায় :

  • যেকোনো জীবিত ঘরের যদি দুই বা তিনটি জীবিত প্রতিবেশী থাকে, তাহলে ঘরটি পরের ধাপে জীবিত থাকবে।
  • যেকোনো মৃত ঘরের যদি ঠিক তিনটি জীবিত প্রতিবেশী থাকে, তাহলে ঘরটি পরের ধাপে জীবিত হয়ে উঠবে।
  • বাকি সব জীবিত ঘর পরের ধাপে মারা যাবে। মৃত ঘরগুলো মৃতই থাকবে।

এভাবে ধাপে ধাপে গেমটির একেকটি প্রজন্ম তৈরি হয়। প্রতিটি প্রজন্ম তার পূর্বের প্রজন্মের একটি ফাংশন। অর্থাৎ, পূর্বের প্রজন্মের নির্দিষ্ট বিন্যাসের জন্য পরের প্রজন্মে সব সময় একই বিন্যাস তৈরি হবে।

লাইফের নমুনা

গেমটিতে নির্দিষ্ট কিছু বিন্যাস থেকে মজার কিছু প্যাটার্ন তৈরি হয়। প্যাটার্নগুলোর আচরণের ওপর ভিত্তি করে এদের বিভিন্ন নাম দেওয়া হয়েছে, যেমন : স্টিল লাইফ, ওসিলেটর, স্পেসশিপ ইত্যাদি।

স্টিল লাইফ পরবর্তী প্রজন্মেও স্থির থাকে, পরিবর্তন হয় না। ওসিলেটর নির্দিষ্ট কিছু প্রজন্ম পরপর আগের অবস্থায় ফিরে আসে। চিত্র ৪-এর পালসার ও চিত্র ২-এর ব্লিংকার ওসিলেটরের উদাহরণ। স্পেসশিপও একধরনের ওসিলেটর, তবে এগুলো এক জায়গায় স্থির না থেকে নির্দিষ্ট দিকে ছুটতে থাকে। চিত্র ৩-এর গ্লাইডার একটি স্পেসশিপ।

নিচে দেওয়া প্রোগ্রামটির র‌্যানডম ভার্শন কয়েকবার চালালে প্রচুর স্টিল লাইফ ও ব্লিংকার দেখা যাবে। কপাল ভালো হলে কিছু স্পেসশিপও দেখা যাবে।

ছবি ১: ব্লিংকার

ছবি ১: ব্লিংকার

ছবি ২: গ্লাইডার

ছবি ২: গ্লাইডার

ছবি ৩: পালসার

ছবি ৩: পালসার

ছবি ৪: ক্রস

ছবি ৪: ক্রস

ছবি ৫: গসপার’স গ্লাইডার গান

ছবি ৫: গসপার’স গ্লাইডার গান

কম্পিউটার সিমুলেশন

আধুনিক কম্পিউটারে খুব সহজে এই গেমটি সিমুলেট করে দেখা যায়। কম্পিউটারের মেমোরি যেহেতু সসীম, তাই এতে অসীম গ্রিডের পরিবর্তে নির্দিষ্ট আকারের গ্রিডে গেমটি সিমুলেট করা হয়।

এখানে পাইথন দিয়ে একটি কোড দেখানো হলো। গ্রিডের আকার পরিবর্তন করতে main() ফাংশনে size ভ্যারিয়েবলটি পরিবর্তন করতে হবে (লাইন ৮৪)। গসপারের গ্লাইডার গান (ছবি ৫) সিমুলেট করতে choice ভ্যারিয়েবলের মান পরিবর্তন করে অন্য কিছু বা ফাঁকা স্ট্রিং দিলেই হবে (হাইলাইট করা লাইন)।

import numpy as np
import matplotlib.pyplot as plt


class Board(object):
    def __init__(self, size, choice = 'Random'):
        if choice == 'Random':
            self.state = np.random.randint(2, size=(size, size))
        else:
            self.state = np.zeros(size*size).reshape(size, size)
            gliderGun(self.state)

        self.engine = Engine(self)
        self.iteration = 0
   
    def animate(self):
        i = self.iteration
        img = None
        plt.figure(figsize=(8, 8))
        plt.title("Conway's Game of Life")
        while True:
            if i == 0:
                plt.ion()
                img = plt.imshow(self.state)
            else:
                img.set_data(self.state)
            
            i += 1
            self.engine.applyRules()
            plt.pause(0.01)    # animation speed
            yield self


class Engine(object):
    def __init__(self, board):
        self.state = board.state
    
    def countNeighbors(self):
        state = self.state
        n = (state[0:-2,0:-2] + state[0:-2,1:-1] + state[0:-2,2:] +
            state[1:-1,0:-2] + state[1:-1,2:] + state[2:,0:-2] +
            state[2:,1:-1] + state[2:,2:])
        return n
   
    def applyRules(self):
        n = self.countNeighbors()
        state = self.state
        birth = (n == 3) & (state[1:-1,1:-1] == 0)
        survive = ((n == 2) | (n == 3)) & (state[1:-1,1:-1] == 1)
        state[...] = 0
        state[1:-1,1:-1][birth | survive] = 1
        return state


def gliderGun(grid):  
    gun = np.zeros(11*38).reshape(11, 38) 
  
    gun[5][1] = gun[5][2] = 1
    gun[6][1] = gun[6][2] = 1
  
    gun[3][13] = gun[3][14] = 1
    gun[4][12] = gun[4][16] = 1
    gun[5][11] = gun[5][17] = 1
    gun[6][11] = gun[6][15] = gun[6][17] = gun[6][18] = 1
    gun[7][11] = gun[7][17] = 1
    gun[8][12] = gun[8][16] = 1
    gun[9][13] = gun[9][14] = 1
  
    gun[1][25] = 1
    gun[2][23] = gun[2][25] = 1
    gun[3][21] = gun[3][22] = 1
    gun[4][21] = gun[4][22] = 1
    gun[5][21] = gun[5][22] = 1
    gun[6][23] = gun[6][25] = 1
    gun[7][25] = 1
  
    gun[3][35] = gun[3][36] = 1
    gun[4][35] = gun[4][36] = 1
    
    grid[2:13, 2:40] = gun


def main():
    size = 100
    choice = 'Random'  # set any other string to show glider gun

    # size = int(input('Enter the size of the board (50+): '))
    # print('0. Gosper glider gun\n1. Random');
    # choice = int(input('Enter your choice: '))
    
    # choice = "" if choice == 0 else "Random"
    # if size < 50 or size > 400:
    #     size = 50
    
    board = Board(size, choice)
    for _ in board.animate():
        pass


if __name__ == '__main__':
    main()

(সুবিন ডট কমে পূর্বে প্রকাশিত।)