In [1]:
import sdm as sdmlib
from collections import defaultdict
import random
from IPython.display import clear_output

empty = ' '

In [2]:
class Player(object):
    def __init__(self, name):
        self.name = name
    
    def on_invalid_move(self):
        raise Exception('Ops')
    
    def on_finish(self, winner, seq):
        pass
    
    def next_move(self, step, board):
        v = []
        for i, row in enumerate(board):
            for j, x in enumerate(row):
                if x == empty:
                    v.append((i, j))
        return random.choice(v)

In [3]:
class SDMPlayer(object):
    def __init__(self, name):
        self.name = name

        self.bits = 256
        self.sample = 1000000
        self.scanner_type = sdmlib.SDM_SCANNER_THREAD
        
        self.address_space = sdmlib.AddressSpace.init_random(self.bits, self.sample)
        self.counter = sdmlib.Counter.init_zero(self.bits, self.sample)
        self.sdm = sdmlib.SDM(self.address_space, self.counter, 103, self.scanner_type)
        
        self.bs_steps = [sdmlib.Bitstring.init_random(self.bits) for _ in range(10)]
        self.code_bits = self.bits // 9
        self.bs_codes = {
            'X': sdmlib.Bitstring.init_random(self.code_bits),
            'O': sdmlib.Bitstring.init_random(self.code_bits),
            ' ': sdmlib.Bitstring.init_random(self.code_bits),
        }
        
        self.reset_stats()
    
    def reset_stats(self):
        self.stats = defaultdict(int)
    
    def on_invalid_move(self):
        raise Exception('Ops')
    
    def board_to_bitstring(self, board):
        bs = sdmlib.Bitstring.init_zeros(self.bits)
        bit = 0
        for row in board:
            for x in row:
                code = self.bs_codes[x]
                for i in range(self.code_bits):
                    bs.set_bit(bit, code.get_bit(i))
                    bit += 1
        return bs
    
    def bitstring_to_board(self, ref):
        board = [[empty]*3 for _ in range(3)]
        bit = 0
        for i in range(3):
            for j in range(3):
                bs1 = sdmlib.Bitstring.init_zeros(self.code_bits)
                for k in range(self.code_bits):
                    bs1.set_bit(k, ref.get_bit(bit))
                    bit += 1
                found = False
                for x, code in self.bs_codes.items():
                    if code == bs1:
                        board[i][j] = x
                        found = True
                if not found:
                    #print 'Not found'
                    return None
        return board
    
    def on_finish(self, winner, seq):
        debug = False
        if winner == self.name:
            prev = None
            if debug:
                print('Learning...')
            for step, board in enumerate(seq):
                if prev is not None:
                    if debug:
                        print_board(prev)
                        print('')
                        print_board(board)
                        print('--')
                    bs1 = self.board_to_bitstring(prev)
                    bs2 = self.board_to_bitstring(board)
                    self.sdm.write(bs1 ^ self.bs_steps[step], bs2)
                prev = board
    
    def next_move(self, step, board):
        bs1 = self.board_to_bitstring(board)
        bs2 = self.sdm.iter_read(bs1 ^ self.bs_steps[step], max_iter=12)
        board2 = self.bitstring_to_board(bs2)
        if board2 is None:
            return self.random_move(step, board)
        diff = []
        for i in range(3):
            for j in range(3):
                if board[i][j] != board2[i][j]:
                    if board[i][j] == empty and board2[i][j] == self.name:
                        diff.append((i, j))
        if diff:
            self.stats['sdm'] += 1
            return random.choice(diff)
        return self.random_move(step, board)
        #print diff
        #print ''
        #print_board(board)
        #print ''
        #print_board(board2)
        #print ''
        #raise Exception
    
    def random_move(self, step, board):
        self.stats['random'] += 1
        v = []
        for i, row in enumerate(board):
            for j, x in enumerate(row):
                if x == empty:
                    v.append((i, j))
        return random.choice(v)

In [4]:
def print_board(board):
    for row in board:
        print(row)

def check_all_equal(*args):
    if len(set(args)) == 1 and args[0] != empty:
        return True
    return False
        
def check_for_winner(board):
    for i in range(3):
        if check_all_equal(board[i][0], board[i][1], board[i][2]):
            return board[i][0]
        
        if check_all_equal(board[0][i], board[1][i], board[2][i]):
            return board[0][i]

    if check_all_equal(board[0][0], board[1][1], board[2][2]):
        return board[0][0]
    
    if check_all_equal(board[0][2], board[1][1], board[2][0]):
        return board[0][2]
    
    return None

def copy_board(board):
    v = []
    for row in board:
        v.append(list(row))
    return v

def play(p1, p2):
    board = [[empty]*3 for _ in range(3)]
    end = False
    players = [p1, p2]
    random.shuffle(players)
    index = 0
    step = 0
    sequence = [board]
    winner = None
    while not end:
        cur_player = players[index]
        
        i, j = cur_player.next_move(step, board)
        if board[i][j] != empty:
            cur_player.on_invalid_move()
        board = copy_board(board)
        board[i][j] = cur_player.name
        sequence.append(board)
        
        winner = check_for_winner(board)
        if winner is not None:
            end = True
            
        v = []
        for i, row in enumerate(board):
            for j, x in enumerate(row):
                if x == empty:
                    v.append((i, j))
        if len(v) == 0:
            end = True
        
        index = (index+1)%2
        step += 1
    return winner, sequence

In [5]:
p1 = SDMPlayer('X')
p2 = Player('O')

In [ ]:
n = 200
wins = defaultdict(int)
for i in range(n):
    winner, seq = play(p1, p2)
    p1.on_finish(winner, seq)
    p2.on_finish(winner, seq)
    
    wins[winner] += 1
    
    clear_output(wait=True)
    print('Game #{:5d}: {}  {}'.format(i+1, list(wins.items()), list(p1.stats.items())))
    
    #print ''
    #print_board(seq[-1])
    #print ''
    #for board in seq:
    #    print_board(board)
    #    print ''

In [6]:
bs1 = p1.board_to_bitstring([[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']])
bs2 = p1.board_to_bitstring([['X', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']])
bs3 = p1.board_to_bitstring([[' ', 'X', ' '], [' ', ' ', ' '], [' ', ' ', ' ']])
bs4 = p1.board_to_bitstring([[' ', ' ', 'X'], [' ', ' ', ' '], [' ', ' ', ' ']])
bs5 = p1.board_to_bitstring([[' ', ' ', ' '], ['X', ' ', ' '], [' ', ' ', ' ']])
bs6 = p1.board_to_bitstring([[' ', ' ', ' '], [' ', 'X', ' '], [' ', ' ', ' ']])

In [13]:
#p1.sdm.write(bs1, bs2)
#p1.sdm.write(bs1, bs3)
#p1.sdm.write(bs1, bs4)
p1.sdm.write(bs1, bs5)
#p1.sdm.write(bs1, bs6)

In [14]:
rd1 = p1.sdm.read(bs1)

In [15]:
p1.bitstring_to_board(rd1)


Out[15]:
[[' ', ' ', ' '], [' ', ' ', ' '], [' ', ' ', ' ']]

In [ ]:


In [ ]: