Created
July 11, 2015 01:53
-
-
Save henvic/2c78320dd509af384e39 to your computer and use it in GitHub Desktop.
Tic-tac-toe
This file contains 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
#include "opencv2/imgproc/imgproc.hpp" | |
#include "opencv2/highgui/highgui.hpp" | |
#include "opencv2/core/core.hpp" | |
#include <math.h> | |
#include <iostream> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <vector> | |
using namespace cv; | |
using namespace std; | |
char MAIN_WINDOW[] = "tictactoe"; | |
char board[9]; | |
RNG rng(12345); | |
int iLowH = 0; | |
int iHighH = 179; | |
int iLowS = 0; | |
int iHighS = 255; | |
int iLowV = 0; | |
int iHighV = 255; | |
int iLastX = -1; | |
int iLastY = -1; | |
int selectedPosition = -1; | |
int piece = 'o'; | |
void printBoard(Mat& frame, int width, int height) { | |
// 1 = firstLine + secondLine + (2 * thickness) | |
double firstLine = 0.33; | |
double secondLine = 0.66; | |
double thicknessLine = 0.005; | |
int thicknessWidth = thicknessLine * width; | |
int thicknessHeight = thicknessLine * height; | |
Point p0 = Point(firstLine * width, 0); | |
Point p1 = Point(firstLine * width, height); | |
Point p3 = Point(secondLine * width, 0); | |
Point p4 = Point(secondLine * width, height); | |
Point p5 = Point(0, firstLine * height); | |
Point p6 = Point(width, firstLine * height); | |
Point p7 = Point(0, secondLine * height); | |
Point p8 = Point(width, secondLine * height); | |
Scalar color = Scalar(10, 10, 10); | |
line(frame, p0, p1, color, thicknessWidth, CV_AA); | |
line(frame, p3, p4, color, thicknessWidth, CV_AA); | |
line(frame, p5, p6, color, thicknessHeight, CV_AA); | |
line(frame, p7, p8, color, thicknessHeight, CV_AA); | |
} | |
void printMeta(Mat& frame, double t) { | |
char text [100]; | |
sprintf(text, "v - toggle view | space to play | c - clear board %fs", t); | |
putText(frame, text, Point(40, 40), | |
CV_FONT_HERSHEY_SIMPLEX, | |
1, | |
Scalar(255, 255, 255), | |
1, | |
1); | |
} | |
int getPosX(int x, Size board) { | |
return ((2 * x) + 1) * (board.width / 6); | |
} | |
int getPosY(int y, Size board) { | |
return ((2 * y) + 1) * (board.height / 6); | |
} | |
void printCircle(Mat& frame, int x, int y, Size boardSize) { | |
Point center = Point(getPosX(x, boardSize), getPosY(y, boardSize)); | |
Scalar color = Scalar(0, 255, 200); | |
circle(frame, center, 50, color, CV_FILLED, CV_AA); | |
} | |
void printCross(Mat& frame, int x, int y, Size boardSize) { | |
int off = 50; | |
int thickness = 20; | |
int centerX = getPosX(x, boardSize); | |
int centerY = getPosY(y, boardSize); | |
Point topLeft = Point(centerX - off, centerY - off); | |
Point topRight = Point(centerX + off, centerY - off); | |
Point bottomLeft = Point(centerX - off, centerY + off); | |
Point bottomRight = Point(centerX + off, centerY + off); | |
Scalar color = Scalar(30, 55, 200); | |
line(frame, topLeft, bottomRight, color, thickness, CV_AA); | |
line(frame, topRight, bottomLeft, color, thickness, CV_AA); | |
} | |
void printPieces(Mat& frame, Size boardSize) { | |
for (int counter = 0; counter < 9; counter += 1) { | |
int x = counter % 3; | |
int y = counter / 3; | |
if (board[counter] == 'o') { | |
printCircle(frame, x, y, boardSize); | |
continue; | |
} | |
if (board[counter] == 'x') { | |
printCross(frame, x, y, boardSize); | |
} | |
} | |
} | |
void findPieces(Mat& frame, Mat& imgThresholded, Size boardSize) { | |
Mat frameHSV; | |
// based on http://opencv-srf.blogspot.com.br/2010/09/object-detection-using-color-seperation.html | |
cvtColor(frame, frameHSV, COLOR_BGR2HSV); | |
inRange(frameHSV, Scalar(iLowH, iLowS, iLowV), Scalar(iHighH, iHighS, iHighV), imgThresholded); | |
erode(imgThresholded, imgThresholded, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) ); | |
dilate( imgThresholded, imgThresholded, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) ); | |
dilate(imgThresholded, imgThresholded, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) ); | |
erode(imgThresholded, imgThresholded, getStructuringElement(MORPH_ELLIPSE, Size(5, 5)) ); | |
Moments oMoments = moments(imgThresholded); | |
double dM01 = oMoments.m01; | |
double dM10 = oMoments.m10; | |
double dArea = oMoments.m00; | |
if (dArea > 100000 && dArea < 100000000) { | |
int posX = dM10 / dArea; | |
int posY = dM01 / dArea; | |
if (iLastX >= 0 && iLastY >= 0 && posX >= 0 && posY >= 0) | |
{ | |
line(frame, Point(posX, posY), Point(iLastX, iLastY), Scalar(0, 0, 255), 7, CV_AA); | |
line(imgThresholded, Point(posX, posY), Point(iLastX, iLastY), Scalar(0, 0, 0), 60, CV_AA); | |
} | |
iLastX = posX; | |
iLastY = posY; | |
// +1 deliberately added to avoid the edge case of division by zero | |
int quadX = (iLastX / (boardSize.width / 3)); | |
int quadY = (iLastY / (boardSize.height / 3)); | |
selectedPosition = (quadY * 3) + quadX; | |
// printf("(X, Y, pos) = (%d, %d, %d)\n", quadX, quadY, selectedPosition); | |
// printf("selected position = %d\n", selectedPosition); | |
} else { | |
selectedPosition = -1; | |
} | |
} | |
void clearPieces() { | |
for (int counter = 0; counter < 9; counter += 1) { | |
board[counter] = '\0'; | |
} | |
} | |
void printWinner(Mat& frame, vector<int> winners, Size boardSize) { | |
int thickness = 70; | |
Scalar color = Scalar(0, 0, 0); | |
Point piece1Point = Point(getPosX(winners[0] % 3, boardSize), getPosY(winners[0] / 3, boardSize)); | |
Point piece3Point = Point(getPosX(winners[2] % 3, boardSize), getPosY(winners[2] / 3, boardSize)); | |
line(frame, piece1Point, piece3Point, color, thickness, CV_AA); | |
} | |
bool hasPiece(char place) { | |
return place == 'o' || place == 'x'; | |
} | |
vector<int> getWinner() { | |
char pivotD = board[4]; | |
char pivotV; | |
char pivotH; | |
// diagonal 1 | |
if (hasPiece(pivotD) && board[0] == pivotD && board[8] == pivotD) { | |
return {0, 4, 8}; | |
} | |
// diagonal 2 | |
if (hasPiece(pivotD) && board[2] == pivotD && board[6] == pivotD) { | |
return {2, 4, 6}; | |
} | |
// vertical and horizontal | |
for (int counter = 0; counter < 3; counter += 1) { | |
pivotV = board[counter]; | |
pivotH = board[3 * counter]; | |
if (!hasPiece(pivotV)) { | |
continue; | |
} | |
if (board[counter + 3] == pivotV && board[counter + 6] == pivotV) { | |
return {counter, counter + 3, counter + 6}; | |
} | |
if (board[(3 * counter) + 1] == pivotH && board[(3 * counter) + 2] == pivotH) { | |
return {3 * counter, board[(3 * counter) + 1], (3 * counter) + 2}; | |
} | |
} | |
return {}; | |
} | |
void play() { | |
if (selectedPosition < 0 || board[selectedPosition] == 'o' || board[selectedPosition] == 'x') { | |
return; | |
} | |
board[selectedPosition] = piece; | |
if (piece == 'o') { | |
printf("setting new piece"); | |
piece = 'x'; | |
} else { | |
piece = 'o'; | |
} | |
} | |
int main( int argc, char**) { | |
VideoCapture cap(0); | |
if(!cap.isOpened()) { | |
return -1; | |
} | |
Size size = Size(cap.get(CV_CAP_PROP_FRAME_WIDTH), | |
cap.get(CV_CAP_PROP_FRAME_HEIGHT)); | |
cout << size; | |
cout << cap.get(CV_CAP_PROP_FPS); | |
namedWindow(MAIN_WINDOW, CV_WINDOW_AUTOSIZE); | |
Mat edges; | |
Mat imgThresholded; | |
bool v = false; | |
vector<int> winners; | |
// http://opencv-srf.blogspot.com.br/2010/09/object-detection-using-color-seperation.html | |
cvCreateTrackbar("LowHue", MAIN_WINDOW, &iLowH, 179); //Hue (0 - 179) | |
cvCreateTrackbar("HighHue", MAIN_WINDOW, &iHighH, 179); | |
cvCreateTrackbar("LowSaturation", MAIN_WINDOW, &iLowS, 255); //Saturation (0 - 255) | |
cvCreateTrackbar("HighSaturation", MAIN_WINDOW, &iHighS, 255); | |
cvCreateTrackbar("LowValue", MAIN_WINDOW, &iLowV, 255); //Value (0 - 255) | |
cvCreateTrackbar("HighValue", MAIN_WINDOW, &iHighV, 255); | |
for(;;) { | |
double t = (double)getTickCount(); | |
cap >> edges; | |
cv::flip(edges, edges, 1); | |
if (winners.size() < 3) { | |
findPieces(edges, imgThresholded, size); | |
} | |
printBoard(edges, size.width, size.height); | |
printPieces(edges, size); | |
winners = getWinner(); | |
if (winners.size() == 3) { | |
printWinner(edges, winners, size); | |
} | |
int key = waitKey(30); | |
if(key >= 0) { | |
switch (key) { | |
case 'v': | |
v = !v; | |
break; | |
case 'c': | |
clearPieces(); | |
break; | |
case ' ': | |
if (winners.size() < 3) { | |
play(); | |
} | |
break; | |
} | |
} | |
t = ((double)getTickCount() - t)/getTickFrequency(); | |
printMeta(edges, t); | |
if (v) { | |
imshow(MAIN_WINDOW, imgThresholded); | |
} else { | |
imshow(MAIN_WINDOW, edges); | |
} | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment