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