Last active
February 23, 2021 22:45
-
-
Save Eckankar/4566c3b025e7b1fc7fc109083354b446 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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