Skip to content

Instantly share code, notes, and snippets.

@Eckankar
Last active February 23, 2021 22:45
Show Gist options
  • Save Eckankar/4566c3b025e7b1fc7fc109083354b446 to your computer and use it in GitHub Desktop.
Save Eckankar/4566c3b025e7b1fc7fc109083354b446 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
#
# Attempts to play Tic Tac Toe following https://imgs.xkcd.com/comics/tic_tac_toe.png
# Does so by trying to read off the image.
# Requires the file to be located in the same folder.
import cv2
import numpy as np
DEBUG = False
image = cv2.imread("tic_tac_toe.png")
print("Welcome to Tic Tac Toe - XKCD edition.")
player = None
while player not in ['X', 'O']:
player = input("Do you want to play as X or O? ")
if player == 'O':
image = image[155:878,9:730]
offset = 0
else:
image = image[939:1666,9:730]
offset = 2
# remove spaces
image = np.delete(image, slice(477+offset, 483+offset), 0)
image = np.delete(image, slice(236+offset, 242+offset), 0)
image = np.delete(image, slice(480, 486), 1)
image = np.delete(image, slice(235, 241), 1)
turn = 'X'
gameState = [None] * 9
def winner():
checks = [[0,1,2], [3,4,5], [6,7,8], [0,4,8], [2,4,6], [0,3,6], [1,4,7], [2,5,8]]
for check in checks:
elements = [gameState[i] for i in check]
first = elements[0]
if first is None: continue
if all(x == first for x in elements): return first
return False
def gameOver():
return winner() or None not in gameState
def showBoard():
for i in [0, 3, 6]:
elms = zip(gameState[i:i+3], range(i, i+3))
print("|".join(e if e else str(i) for (e, i) in elms))
if i < 6: print("-"*5)
def validMove(move):
return move is not None and move >= 0 and gameState[move] is None
def imageSegment(i):
global image
(h, w, d) = image.shape
dh = h // 3
dw = w // 3
newH = dh * (i // 3)
newW = dw * (i % 3)
return image[newH:newH+dh, newW:newW+dw]
while not gameOver():
if turn == player:
move = None
while not validMove(move):
showBoard()
move = input(f"Where do you want to place an {player}? ")
if not move.isnumeric():
move = None
else:
move = int(move)
gameState[move] = player
image = imageSegment(move)
if DEBUG:
cv2.imshow("image", image)
cv2.waitKey(0)
else:
bestScore = -999999
bestSegment = None
for i in range(9):
if gameState[i] is not None: continue
segment = imageSegment(i)
# Count red/black/white pixels in segment to deduce where the next move is intended to be.
(b, g, r) = cv2.split(segment)
blackCount = 0
redCount = 0
whiteCount = 0
for y in range(r.shape[0]):
for x in range(r.shape[1]):
if r[y,x] < 150: blackCount += 1
if r[y,x] > 150 and b[y,x] < 150: redCount += 1
if r[y,x] > 150 and b[y,x] > 150 and g[y,x] > 150: whiteCount += 1
score = 10*redCount + 0.25*whiteCount - 3*blackCount
if DEBUG:
print(f"segment {i}: redCount {redCount}, blackCount {blackCount}, whiteCount {whiteCount}, score {score}")
if score > bestScore:
bestScore = score
bestSegment = i
if DEBUG:
cv2.imshow(f"segment {i}", imageSegment(i))
cv2.waitKey(0)
print(f"AI places piece in spot {bestSegment}")
gameState[bestSegment] = 'X' if player == 'O' else 'O'
turn = 'X' if turn == 'O' else 'O'
showBoard()
won = winner()
if won:
print(f"Game over. Winner is {won}.")
else:
print("Game over. Game is a draw!")
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment