tictactoe script - commented - may have pedagogical value

namenobodywants at gmail.com namenobodywants at gmail.com
Mon Sep 4 23:26:14 EDT 2017


"""
this program makes an optimal tictactoe move by answering the following questions 
in their given order until it is told where to put its mark:

1) can you win the game? 
    if so then do it
2) could your opponent win the game if it was his turn? 
    if so then put your own mark where a mark of his would win him the game
3) could you win the game if you had two turns in a row?
    if so then make a move that leaves you with the largest number of second moves 
    that would win you the game if you could really make them
4) is the center square open?
    if so then put your mark there
5) are any of the corners open?
    if so then put your mark on one of them
6) put your mark on any open square
"""

#) tictactoe; python 3.6.1

count = lambda dictionary, value: list(dictionary.values()).count(value)
subdictionary = lambda dictionary, keys: {key: dictionary[key] for key in keys}

ex, oh, blank = 'X0 '

indices = list(range(3))
vectors = [(i,j) for i in indices for j in indices]
startgrid = {vector: blank for vector in vectors}

display = lambda grid: '\n-+-+-\n'.join('|'.join(grid[i,j] for j in indices) for i in indices) 

iscenter = lambda vector: vector == (1,1)
iscorner = lambda vector: vector[0]%2 == vector[1]%2 == 0

horizontal = [[(i,j) for j in indices] for i in indices]
vertical   = [[(i,j) for i in indices] for j in indices]
diagonal   = [[(i,i) for i in indices], [(i,2-i) for i in indices]]
linear     = horizontal + vertical + diagonal
getrows    = lambda grid: [subdictionary(grid,coordlist) for coordlist in linear]

movermark = lambda grid: ex if count(grid,ex) == count(grid,oh) else oh
opponent  = lambda mark: ex if mark == oh else oh if mark == ex else blank

strike   = lambda vector, rows, ownmark: int(any(row for row in rows if vector in row and count(row,blank)==1 and count(row,ownmark)==2))
parry    = lambda vector, rows, ownmark: int(any(row for row in rows if vector in row and count(row,blank)==1 and count(row,opponent(ownmark))==2))
threaten = lambda vector, rows, ownmark: len([row for row in rows if vector in row and count(row,blank)==2 and count(row,ownmark)==1])
position = lambda vector, rows, ownmark: 2 * int(iscenter(vector)) + int(iscorner(vector))
evaluate = lambda vector, rows, ownmark: [f(vector,rows,ownmark) for f in (strike,parry,threaten,position)]

def movevector(grid,ownmark):
    from random import choice
    rows = getrows(grid)
    value = lambda vector: evaluate(vector,rows,ownmark)
    candidates = [(i,j) for i in indices for j in indices if grid[i,j] == blank]
    maximum = value(max(candidates,key=value)) if candidates else None
    coords = [vector for vector in candidates if value(vector) == maximum]
    return choice(coords) if coords and blank in grid.values() else None

def getwinner(grid):
    rows = getrows(grid)
    rows = [list(row.values()) for row in rows]
    return ex if [ex]*3 in rows else oh if [oh]*3 in rows else None

def makemove(grid):
    grid = grid.copy()
    ownmark = movermark(grid)
    vector = movevector(grid,ownmark)
    if vector: grid[vector] = ownmark
    return grid

def getinteger(lower,upper,prompt):
    integer = None
    while integer == None:
        try:
            integer = int(input(prompt))
            assert lower <= integer <= upper
        except KeyboardInterrupt: raise
        except: pass
    return integer

def play(mark):
    grid = startgrid.copy()
    if mark == ex: grid = makemove(grid)
    print('\n\n' + display(grid) + '\n\n')
    winner = None
    while not winner and blank in grid.values():        
        cellnumber = getinteger(1,9,'cell number: ')
        grid[divmod(cellnumber-1,3)] = movermark(grid)
        winner = getwinner(grid)
        if not winner: grid = makemove(grid) 
        winner = getwinner(grid)
        print('\n\n' + display(grid) + '\n\n')
    message = winner + ' wins' if winner else 'tied game'
    print(message)













    

    



More information about the Python-list mailing list