#ifndef PLAYER_H
#define PLAYER_H
class Player
{
public:
Player();
~Player();
void AddScore(int score = 1);
int GetScore();
private:
int mScore;
};
#endif
#include "Player.h"
Player::Player()
{
mScore = 0;
}
Player::~Player() {}
void Player::AddScore(int score)
{
mScore += score;
}
int Player::GetScore()
{
return mScore;
}
#ifndef FIELD_H
#define FIELD_H
#include <Windows.h>
#define FC_X 0 //FieldContent is 'X'
#define FC_O 1 //FieldContent is 'O'
#define FC_EMPTY 3 //Field has no content yet
class Field
{
public:
Field();
~Field();
int GetSign();
void SetSign(int sign);
void GetCornerPoints(POINT& p1, POINT& p2); //coordinates of the corners of the field (to check mouse click and draw signs)
void SetCornerPoints(POINT p1, POINT p2);
private:
int mSign;
POINT mP1;
POINT mP2;
};
#endif
#include "Field.h"
Field::Field() {}
Field::~Field() {}
int Field::GetSign()
{
return mSign;
}
void Field::SetSign(int sign)
{
mSign = sign;
}
void Field::GetCornerPoints(POINT& p1, POINT& p2)
{
p1 = mP1;
p2 = mP2;
}
void Field::SetCornerPoints(POINT p1, POINT p2)
{
mP1 = p1;
mP2 = p2;
}
#ifndef BOARD_H
#define BOARD_H
#include "Field.h"
#include "Player.h"
#include <string>
#include <sstream>
using namespace std;
class Board
{
public:
Board();
~Board();
void DrawBoard(HDC hdc, Player player[]); //Function to draw the entire gameboard
void SetFieldSign(int field_id, int sign);
void ResetFields();
int GetFieldSign(int field_id);
int GetClickedFieldID(int click_point_x, int click_point_y);
private:
Field Fields[9];
HPEN pen;
HBRUSH brush;
};
#endif
#include "Board.h"
Board::Board()
{
POINT p1;
POINT p2;
ResetFields();
//Setting the corner points of the X and O signs to draw
p1.x = 0;
p1.y = 50;
p2.x = 200;
p2.y = 250;
Fields[0].SetCornerPoints(p1, p2);
p1.x = 200;
p1.y = 50;
p2.x = 400;
p2.y = 250;
Fields[1].SetCornerPoints(p1, p2);
p1.x = 400;
p1.y = 50;
p2.x = 600;
p2.y = 250;
Fields[2].SetCornerPoints(p1, p2);
p1.x = 0;
p1.y = 250;
p2.x = 200;
p2.y = 450;
Fields[3].SetCornerPoints(p1, p2);
p1.x = 200;
p1.y = 250;
p2.x = 400;
p2.y = 450;
Fields[4].SetCornerPoints(p1, p2);
p1.x = 400;
p1.y = 250;
p2.x = 600;
p2.y = 450;
Fields[5].SetCornerPoints(p1, p2);
p1.x = 0;
p1.y = 450;
p2.x = 200;
p2.y = 650;
Fields[6].SetCornerPoints(p1, p2);
p1.x = 200;
p1.y = 450;
p2.x = 400;
p2.y = 650;
Fields[7].SetCornerPoints(p1, p2);
p1.x = 400;
p1.y = 450;
p2.x = 600;
p2.y = 650;
Fields[8].SetCornerPoints(p1, p2);
}
Board::~Board()
{
DeleteObject(pen);
DeleteObject(brush);
}
void Board::DrawBoard(HDC hdc, Player player[])
{
POINT PenWidth;
PenWidth.x = 10;
PenWidth.y = 10;
POINT d_pt1; //draw point
POINT d_pt2;
LOGPEN lp;
LOGBRUSH lb; //for the score lane at the top
//Score Display
lp.lopnStyle = BS_SOLID;
lp.lopnWidth = PenWidth;
lb.lbStyle = BS_SOLID;
lb.lbColor = RGB(255, 0, 0); // red
brush = CreateBrushIndirect(&lb);
SelectObject(hdc, brush);
Rectangle(hdc, 0, 0, 200, 50);
lb.lbColor = RGB(160, 160, 160); //grey
brush = CreateBrushIndirect(&lb);
SelectObject(hdc, brush);
Rectangle(hdc, 200, 0, 400, 50);
lb.lbColor = RGB(0, 0, 255); //blue
brush = CreateBrushIndirect(&lb);
SelectObject(hdc, brush);
Rectangle(hdc, 400, 0, 600, 50);
lb.lbStyle = BS_NULL; //disable brush to draw the signs
brush = CreateBrushIndirect(&lb);
SelectObject(hdc, brush);
//Putting the scores into a string
//Source: http://www.cplusplus.com/articles/D9j2Nwbp/
string Player1 = static_cast<ostringstream*>(&(ostringstream() << player[0].GetScore()))->str();
string Player2 = static_cast<ostringstream*>(&(ostringstream() << player[1].GetScore()))->str();
string Tie = static_cast<ostringstream*>(&(ostringstream() << player[2].GetScore()))->str();
int x[3] = { 90, 290, 490 }; //x coordinates to range back the scores if they are more than 10 or 100
if (player[0].GetScore() >= 10) x[0] = 75;
if (player[2].GetScore() >= 10) x[1] = 275;
if (player[1].GetScore() >= 10) x[2] = 470;
if (player[0].GetScore() >= 100) x[0] = 65;
if (player[2].GetScore() >= 100) x[1] = 265;
if (player[1].GetScore() >= 100) x[2] = 460;
HFONT hFont = CreateFont(40, 0, 0, 0, FW_DONTCARE, 0, 0, 0, DEFAULT_CHARSET, OUT_OUTLINE_PRECIS, CLIP_DEFAULT_PRECIS, CLEARTYPE_QUALITY, VARIABLE_PITCH, TEXT("Verdana"));
SelectObject(hdc, hFont);
SetBkColor(hdc, RGB(255, 0, 0)); //red
TextOutA(hdc, x[0], 3, Player1.c_str(), Player1.size());
SetBkColor(hdc, RGB(160, 160, 160)); //grey
TextOutA(hdc, x[1], 3, Tie.c_str(), Tie.size());
SetBkColor(hdc, RGB(0, 0, 255)); //blue
TextOutA(hdc, x[2], 3, Player2.c_str(), Player2.size());
//Drawing the signs
//Adding and substracting 30 from the coordinates because I don't want to draw the shapes right from the corners
for (int i = 0; i < 9; i++)
{
if (Fields[i].GetSign() == FC_O)
{
lp.lopnColor = RGB(255, 0, 0); //red
pen = CreatePenIndirect(&lp);
SelectObject(hdc, pen);
Fields[i].GetCornerPoints(d_pt1, d_pt2);
d_pt1.x += 30;
d_pt1.y += 30;
d_pt2.x -= 30;
d_pt2.y -= 30;
Ellipse(hdc, d_pt1.x, d_pt1.y, d_pt2.x, d_pt2.y);
}
else if (Fields[i].GetSign() == FC_X)
{
lp.lopnColor = RGB(0, 0, 255); //blue
pen = CreatePenIndirect(&lp);
SelectObject(hdc, pen);
Fields[i].GetCornerPoints(d_pt1, d_pt2);
d_pt1.x += 30;
d_pt1.y += 30;
d_pt2.x -= 30;
d_pt2.y -= 30;
MoveToEx(hdc, d_pt1.x, d_pt1.y, 0); //one line of X
LineTo(hdc, d_pt2.x, d_pt2.y);
MoveToEx(hdc, d_pt1.x + 140, d_pt1.y, 0); //other line of X
LineTo(hdc, d_pt2.x - 140 , d_pt2.y);
}
}
//Drawing the lines of the board
PenWidth.x = 5;
PenWidth.y = 5;
lp.lopnStyle = BS_SOLID;
lp.lopnWidth = PenWidth;
lp.lopnColor = RGB(0, 0, 0); //black
pen = CreatePenIndirect(&lp);
SelectObject(hdc, pen);
//Vertical lines
MoveToEx(hdc, 200, 0, 0);
LineTo(hdc, 200, 650);
MoveToEx(hdc, 400, 0, 0);
LineTo(hdc, 400, 650);
//Horizontal lines
MoveToEx(hdc, 0, 50, 0);
LineTo(hdc, 600, 50);
MoveToEx(hdc, 0, 250, 0);
LineTo(hdc, 600, 250);
MoveToEx(hdc, 0, 450, 0);
LineTo(hdc, 600, 450);
}
void Board::SetFieldSign(int field_id, int sign)
{
if (sign == FC_O) Fields[field_id].SetSign(sign);
if (sign == FC_X) Fields[field_id].SetSign(sign);
if (sign == FC_EMPTY) Fields[field_id].SetSign(sign);
}
void Board::ResetFields()
{
for (int i = 0; i < 9; i++) Fields[i].SetSign(FC_EMPTY);
}
int Board::GetFieldSign(int field_id)
{
return Fields[field_id].GetSign();
}
int Board::GetClickedFieldID(int click_point_x, int click_point_y)
{
POINT p1;
POINT p2;
for (int i = 0; i < 9; i++)
{
Fields[i].GetCornerPoints(p1, p2);
if (click_point_x > p1.x && click_point_x < p2.x &&
click_point_y > p1.y && click_point_y < p2.y)
{
return i;
}
}
return -1;
}
#ifndef GAME_H
#define GAME_H
#include "Board.h"
#include <Windows.h>
#include "resource.h"
class Game
{
public:
Game();
~Game();
int CheckWin(Board gb);
void SwitchActivePlayer(HCURSOR& cursorO, HCURSOR& cursorX);
int GetActivePlayer();
void IncrementStep();
void ResetSteps();
int GetSteps();
private:
int mStep; //number of steps made by players
int mActivePlayer; //variable to indicate which player has to move
};
#endif
#include "Game.h"
Game::Game()
{
mStep = 0;
mActivePlayer = 0;
}
Game::~Game() {}
int Game::CheckWin(Board gb)
{
//First vertical column
if (gb.GetFieldSign(0) == gb.GetFieldSign(3) && gb.GetFieldSign(3) == gb.GetFieldSign(6))
{
if (gb.GetFieldSign(0) == FC_X)
return 1;
if (gb.GetFieldSign(0) == FC_O)
return 0;
}
//Second vertical column
if (gb.GetFieldSign(1) == gb.GetFieldSign(4) && gb.GetFieldSign(4) == gb.GetFieldSign(7))
{
if (gb.GetFieldSign(1) == FC_X)
return 1;
if (gb.GetFieldSign(1) == FC_O)
return 0;
}
//Third vertical column
if (gb.GetFieldSign(2) == gb.GetFieldSign(5) && gb.GetFieldSign(5) == gb.GetFieldSign(8))
{
if (gb.GetFieldSign(2) == FC_X)
return 1;
if (gb.GetFieldSign(2) == FC_O)
return 0;
}
//First row
if (gb.GetFieldSign(0) == gb.GetFieldSign(1) && gb.GetFieldSign(1) == gb.GetFieldSign(2))
{
if (gb.GetFieldSign(0) == FC_X)
return 1;
if (gb.GetFieldSign(0) == FC_O)
return 0;
}
//Second row
if (gb.GetFieldSign(3) == gb.GetFieldSign(4) && gb.GetFieldSign(4) == gb.GetFieldSign(5))
{
if (gb.GetFieldSign(3) == FC_X)
return 1;
if (gb.GetFieldSign(3) == FC_O)
return 0;
}
//Third row
if (gb.GetFieldSign(6) == gb.GetFieldSign(7) && gb.GetFieldSign(7) == gb.GetFieldSign(8))
{
if (gb.GetFieldSign(6) == FC_X)
return 1;
if (gb.GetFieldSign(6) == FC_O)
return 0;
}
//top left - bottom right diagonal
if (gb.GetFieldSign(0) == gb.GetFieldSign(4) && gb.GetFieldSign(4) == gb.GetFieldSign(8))
{
if (gb.GetFieldSign(0) == FC_X)
return 1;
if (gb.GetFieldSign(0) == FC_O)
return 0;
}
//top right - bottom left diagonal
if (gb.GetFieldSign(2) == gb.GetFieldSign(4) && gb.GetFieldSign(4) == gb.GetFieldSign(6))
{
if (gb.GetFieldSign(2) == FC_X)
return 1;
if (gb.GetFieldSign(2) == FC_O)
return 0;
}
if (mStep == 9) //if all field are full and no winner yet
return 2; //Tie
else
return -2; //No winner yet
}
void Game::SwitchActivePlayer(HCURSOR& cursorO, HCURSOR& cursorX)
{
if (mActivePlayer == 0)
{
mActivePlayer = 1;
SetCursor(cursorX);
}
else
{
mActivePlayer = 0;
SetCursor(cursorO);
}
}
int Game::GetActivePlayer()
{
return mActivePlayer;
}
void Game::IncrementStep()
{
mStep++;
}
void Game::ResetSteps()
{
mStep = 0;
}
int Game::GetSteps()
{
return mStep;
}
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by TicTacToe.rc
//
#define IDC_CURSOR1 101
#define IDC_CURSOR2 102
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
#include <Windows.h>
#include "Board.h"
#include "Field.h"
#include "Game.h"
#include "Player.h"
#include "resource.h"
//#define DEBUG //uncomment this, if you want to use the console window to write out variables or whatever you want
HWND ghMainWnd = 0;
HINSTANCE ghAppInst = 0;
HCURSOR hCursorO;
HCURSOR hCursorX;
Board gGameBoard;
Player gPlayer[3]; //The third player[2] is a "ghost player". Only used to store the tie scores
Game gGame;
LRESULT CALLBACK
WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
HDC hdc = 0;
PAINTSTRUCT ps;
switch (msg)
{
case WM_LBUTTONDOWN:
#ifdef DEBUG
printf("ClickID: %i", gGameBoard.GetClickedFieldID(LOWORD(lParam), HIWORD(lParam)));
printf("\n");
printf("ActivePlayer: %i", gGame.GetActivePlayer());
printf("\n");
for (int i = 0; i < 9; i++)
{
printf("Field[%i]: %i", i, gGameBoard.GetFieldSign(i));
printf("\n");
}
#endif
int field_id;
field_id = gGameBoard.GetClickedFieldID(LOWORD(lParam), HIWORD(lParam));
if (gGameBoard.GetFieldSign(field_id) == FC_EMPTY)
{
if (gGame.GetActivePlayer() == 0)
gGameBoard.SetFieldSign(field_id, FC_O);
if (gGame.GetActivePlayer() == 1)
gGameBoard.SetFieldSign(field_id, FC_X);
gGame.IncrementStep();
gGame.SwitchActivePlayer(hCursorO, hCursorX);
InvalidateRect(hWnd, 0, true);
if (gGame.CheckWin(gGameBoard) != -2)
{
switch (gGame.CheckWin(gGameBoard))
{
case 0:
{
MessageBox(hWnd, L"Red player wins!", L"Winner", MB_OK);
break;
}
case 1:
{
MessageBox(hWnd, L"Blue player wins!", L"Winner", MB_OK);
break;
}
case 2:
{
MessageBox(hWnd, L"Tie round!", L"Winner", MB_OK);
break;
}
}
gPlayer[gGame.CheckWin(gGameBoard)].AddScore();
gGameBoard.ResetFields();
gGame.ResetSteps();
InvalidateRect(hWnd, 0, true);
}
}
return 0;
case WM_PAINT:
#ifdef DEBUG
AllocConsole();
freopen("CONIN$", "r", stdin);
freopen("CONOUT$", "w", stdout);
freopen("CONOUT$", "w", stderr);
printf("Player[0]: %i\n", gPlayer[0].GetScore());
printf("Player[1]: %i\n", gPlayer[1].GetScore());
printf("Player[2]: %i\n", gPlayer[2].GetScore());
#endif
hdc = BeginPaint(hWnd, &ps);
gGameBoard.DrawBoard(hdc, gPlayer);
EndPaint(hWnd, &ps);
ShowCursor(true);
return 0;
case WM_KEYDOWN:
if (wParam == VK_ESCAPE)
DestroyWindow(ghMainWnd);
return 0;
case WM_DESTROY:
PostQuitMessage(0);
return 0;
}
return DefWindowProc(hWnd, msg, wParam, lParam);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrev, PSTR cmdLine, int showCmd)
{
ghAppInst = hInstance;
hCursorO = LoadCursor(ghAppInst, MAKEINTRESOURCE(IDC_CURSOR2));
hCursorX = LoadCursor(ghAppInst, MAKEINTRESOURCE(IDC_CURSOR1));
WNDCLASS wc;
wc.style = CS_HREDRAW | CS_VREDRAW;
wc.lpfnWndProc = WndProc;
wc.cbClsExtra = 0;
wc.cbWndExtra = 0;
wc.hInstance = ghAppInst;
wc.hIcon = LoadIcon(0, IDI_APPLICATION);
wc.hCursor = LoadCursor(ghAppInst, IDC_ICON);
wc.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
wc.lpszMenuName = 0;
wc.lpszClassName = L"TicTacToe";
RegisterClass(&wc);
ghMainWnd = CreateWindow(wc.lpszClassName, L"Tic Tac Toe", WS_OVERLAPPED | WS_MINIMIZEBOX | WS_SYSMENU, 412, 107, 615, 685, 0, 0, ghAppInst, 0);
if (!ghMainWnd)
{
MessageBox(0, L"The window cannot be created. The application is closing.", L"Error", MB_OK | MB_ICONERROR);
return 0;
}
ShowWindow(ghMainWnd, showCmd);
UpdateWindow(ghMainWnd);
MSG msg;
ZeroMemory(&msg, sizeof(MSG));
while (GetMessage(&msg, 0, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return (int)msg.wParam;
}