# much of the code below borrowed from http://cwoebker.com/posts/tic-tac-toe
import random
from bibliopixel.animation.matrix import Matrix
from bibliopixel.colors import COLORS
[docs]class Tic:
winning_combos = (
[0, 1, 2], [3, 4, 5], [6, 7, 8],
[0, 3, 6], [1, 4, 7], [2, 5, 8],
[0, 4, 8], [2, 4, 6])
winners = ('X-win', 'Draw', 'O-win')
def __init__(self, squares=[]):
if len(squares) == 0:
self.clearBoard()
else:
self.squares = squares
[docs] def clearBoard(self):
self.squares = [None for i in range(9)]
[docs] def available_moves(self):
"""what spots are left empty?"""
return [k for k, v in enumerate(self.squares) if v is None]
[docs] def available_combos(self, player):
"""what combos are available?"""
return self.available_moves() + self.get_squares(player)
[docs] def complete(self):
"""is the game over?"""
if None not in [v for v in self.squares]:
return True
if self.winner() is not None:
return True
return False
[docs] def X_won(self):
return self.winner() == 'X'
[docs] def O_won(self):
return self.winner() == 'O'
[docs] def tied(self):
return self.complete() and self.winner() is None
[docs] def winner(self):
for player in ('X', 'O'):
positions = self.get_squares(player)
for combo in self.winning_combos:
win = True
for pos in combo:
if pos not in positions:
win = False
break
if win:
return player
return None
[docs] def winningCombo(self, player):
positions = self.get_squares(player)
winCombo = None
for combo in self.winning_combos:
win = True
for pos in combo:
if pos not in positions:
win = False
break
if win:
winCombo = combo
break
return winCombo
[docs] def get_squares(self, player=None):
"""squares that belong to a player"""
if player:
return [k for k, v in enumerate(self.squares) if v == player]
else:
return self.squares
[docs] def get_enemy(self, player):
if player == 'X':
return 'O'
return 'X'
[docs] def make_move(self, position, player):
"""place on square on the board"""
self.squares[position] = player
[docs] def alphabeta(self, node, player, alpha, beta):
if node.complete():
result = 0
if node.X_won():
result = 1
elif node.tied():
result = 0
elif node.O_won():
result = -1
if player == 'O':
result = result * -1
return result
for move in node.available_moves():
node.make_move(move, player)
val = self.alphabeta(node, self.get_enemy(player), alpha, beta)
node.make_move(move, None)
if player == 'O':
if val > alpha:
alpha = val
if alpha >= beta:
return beta
else:
if val < beta:
beta = val
if beta <= alpha:
return alpha
if player == 'O':
return alpha
else:
return beta
[docs]class TicTacToe(Matrix):
def __init__(self, layout, **kwds):
super().__init__(layout, **kwds)
self._showWinCount = 0
[docs] def pre_run(self):
self._board = Tic()
self._player = 'O'
self._nextPlayer = 'O'
[docs] def determine(self, board, player):
a = -2
choices = []
if len(board.available_moves()) == 9:
return random.randint(0, 8)
for move in board.available_moves():
board.make_move(move, player)
val = board.alphabeta(board, self._board.get_enemy(player), -2, 2)
board.make_move(move, None)
if val > a:
a = val
choices = [move]
elif val == a:
choices.append(move)
return random.choice(choices)
[docs] def drawMove(self, index, player):
x = index % 3
y = index // 3
cx = 3 + (x * 8)
cy = 3 + (y * 8)
if player == 'X':
self.layout.drawLine(cx - 2, cy - 2, cx + 2, cy + 2, COLORS.Green)
self.layout.drawLine(cx - 2, cy + 2, cx + 2, cy - 2, COLORS.Green)
elif player == 'O':
self.layout.drawCircle(cx, cy, 2, COLORS.Blue)
[docs] def step(self, amt=1):
winner = None
winCombo = None
complete = self._board.complete()
if complete:
winner = self._board.winner()
if winner:
winCombo = self._board.winningCombo(winner)
self.layout.all_off()
# right now just assuming 24x24, don't feel like doing the math for dynamic
self.layout.drawLine(7, 0, 7, 22, COLORS.Red)
self.layout.drawLine(15, 0, 15, 22, COLORS.Red)
self.layout.drawLine(0, 7, 22, 7, COLORS.Red)
self.layout.drawLine(0, 15, 22, 15, COLORS.Red)
for i in range(9):
self.drawMove(i, self._board.get_squares()[i])
if winCombo:
i = winCombo[0]
x1 = ((i % 3) * 8) + 3
y1 = ((i // 3) * 8) + 3
i = winCombo[2]
x2 = ((i % 3) * 8) + 3
y2 = ((i // 3) * 8) + 3
self.layout.drawLine(x1, y1, x2, y2, COLORS.White)
if complete and self._showWinCount > 2:
self._showWinCount = 0
self._board.clearBoard()
self._player = self._nextPlayer
self._nextPlayer = self._board.get_enemy(self._nextPlayer)
self.animComplete = True
elif complete:
self._showWinCount += 1
if not complete:
new_move = self.determine(self._board, self._player)
self._board.make_move(new_move, self._player)
self._player = self._board.get_enemy(self._player)