I have just finished my first true console game using C++. I would love if I could get some feedback on my program, I have a few specific comments in my code that I have questions on and will list a few more that I ran into while building this game.
- Does main.cpp have enough data abstraction/any comments on my abstraction, where it could use improvement?
- As a new programmer, I have most likely overlooked some things that may be general design flaws, so any general criticisms you could give me that would improve my program would be much appreciated as well as personal preferences with an explanation as to an improvement would be very appreciated.
- In my Board.cpp file, I left a comment about how to structure my conditionals to test for winning combinations.
- In my Board.hpp file, I left a comment about trying to use the a vector from the STL, however when I try to implement a vector instead of the array I get an error. I copied my code for the vector into my main.cpp file and it worked fine, so I am not sure what I am overlooking on this one.
- If you could comment on the structure of my Board.hpp file, specifically is the organization of my member functions appropriate? Where could it use work? I also would like to know if I should have created a separate player class and file? Also any comments on why or why not to add an additional class.
main.cpp
//
// Tic-Tac-Toe
//
#include "Board.hpp"
#include <iostream>
#include <string>
using namespace std;
int main() {
bool newgame{true};
while(newgame == true) {
Board board;
board.reset_winner();
string username,playAgain;
//Select player gamepieces.
cout << "Enter player X username: ";
cin >> username;
board.set_playerX(username);
cout << "Enter player O username: ";
cin >> username;
board.set_playerO(username);
cout << "\n\nPlayer X: " << board.get_playerX() << " " << "Player O: " << board.get_playerO() << endl;
cout << "\nPlayers select a game space 1 - 9 to select a play." << endl;
board.displayGrid();
//Gameplay
while (board.get_isWinner() == false) {
int selection{0};
//Player X's move.
board.set_validSelection(false);
while (board.get_validSelection() == false) {
cout << board.get_playerX() << "'s move:";
cin >> selection;
board.selection_playerX(selection);
}
board.display();
board.winOrTie('X');
//Player O's move.
if (board.get_isWinner() == false) {
board.set_validSelection(false);
while (board.get_validSelection() == false) {
cout << board.get_playerO() << "'s move:";
cin >> selection;
board.selection_playerO(selection);
}
board.display();
board.winOrTie('O');
}
}
//Announce winner.
if (board.get_winner() == 'X') {
cout << board.get_playerX() << " is the winner!" << endl;
}
else if (board.get_winner() == 'O') {
cout << board.get_playerO() << " is the winner!" << endl;
}
else if (board.get_winner() == 'T') {
cout << "Game ends in a tie." << endl;
}
//Play again?
cout << "Would you like to play again?" << "\nType: \"Y\" or \"N\"" << endl;
cin >> playAgain;
if (cin.fail()) {
cin.clear();
cout << "Please type \"Y\" or \"N\":" << endl;
cin >> playAgain;
}
else if (cin.good()) {
if (playAgain == "Y" || playAgain == "y") {
newgame = true;
}
else if (playAgain == "N" || playAgain == "n") {
newgame = false;
}
else {
newgame = false;
}
}
}
return(0);
}
Board.hpp
//
// Board.hpp
// tictactoe
//
#ifndef Board_hpp
#define Board_hpp
#include <stdio.h>
#include <string>
#include <vector>
#endif /* Board_hpp */
class Board {
private:
std::string playerX;
std::string playerO;
char game_space[10]{'\0',' ',' ',' ',' ',' ',' ',' ',' ',' '}; //Why wont std::vector<char> game_space(10,' ') work here? *Note: I did try adding #include <vector> too.
bool isWinner{false};
bool validSelection{true};
char winner;
protected:
public:
//Player info
void set_playerX(std::string username);
void set_playerO(std::string username);
std::string get_playerX();
std::string get_playerO();
//Board info
void displayGrid();
void display();
void selection_playerX(int);
void selection_playerO(int);
//Game info
bool get_isWinner();
void set_isWinner(bool);
bool get_validSelection();
void set_validSelection(bool);
void winOrTie(char);
char get_winner();
void reset_winner();
};
Board.cpp
//
// Board.cpp
// tictactoe
//
#include "Board.hpp"
#include <iostream>
//Player Info
void Board::set_playerX(std::string username) {
playerX = username;
}
void Board::set_playerO(std::string username) {
playerO = username;
}
std::string Board::get_playerX() {
return playerX;
}
std::string Board::get_playerO() {
return playerO;
}
//Board info
void Board::displayGrid() {
std::cout << std::endl;
std::cout << " +---+---+---+" << std::endl;
std::cout << " | 1 | 2 | 3 |" << std::endl;
std::cout << " +---+---+---+" << std::endl;
std::cout << " | 4 | 5 | 6 |" << std::endl;
std::cout << " +---+---+---+" << std::endl;
std::cout << " | 7 | 8 | 9 |" << std::endl;
std::cout << " +---+---+---+" << std::endl;
std::cout << std::endl;
}
void Board::display() {
std::cout << std::endl;
std::cout << " +---+---+---+" << std::endl;
std::cout << " | " << game_space[1] << " | " << game_space[2] << " | " << game_space[3] << " |" << std::endl;
std::cout << " +---+---+---+" << std::endl;
std::cout << " | " << game_space[4] << " | " << game_space[5] << " | " << game_space[6] << " |" << std::endl;
std::cout << " +---+---+---+" << std::endl;
std::cout << " | " << game_space[7] << " | " << game_space[8] << " | " << game_space[9] << " | " << std::endl;
std::cout << " +---+---+---+" << std::endl;
std::cout << std::endl;
}
void Board::selection_playerX(int selection) {
if (game_space[selection] == ' ') {
game_space[selection] = 'X';
validSelection = true;
}
else {
std::cout << "Board position already taken." << std::endl;
validSelection = false;
}
}
void Board::selection_playerO(int selection) {
if (game_space[selection] == ' ') {
game_space[selection] = 'O';
validSelection = true;
}
else {
std::cout << "Board position already taken." << std::endl;
validSelection = false;
}
}
//Gameplay
bool Board::get_isWinner() {
return isWinner;
}
void Board::set_isWinner(bool kanyewest) {
isWinner = kanyewest;
}
bool Board::get_validSelection() {
return validSelection;
}
void Board::set_validSelection(bool selection) {
validSelection = selection;
}
void Board::winOrTie(char gamepiece) {
bool fullBoard{false};
/* 1 2 3
4 5 6
7 8 9 */
//Vertical Win
if ((game_space[1] == gamepiece) && (game_space[4] == gamepiece) && (game_space[7] == gamepiece)) {
//Question: Why wont "if ((game_space[1] && game_space[4] && game_space[7]) == gamepiece)" work as a conditional?
//Also is there a better way to go about the conditionals presented here for combination testing?
isWinner = true;
winner = gamepiece;
}
else if ((game_space[2] == gamepiece) && (game_space[5] == gamepiece) && (game_space[8] == gamepiece)) {
isWinner = true;
winner = gamepiece;
}
else if ((game_space[3] == gamepiece) && (game_space[6] == gamepiece) && (game_space[9] == gamepiece)) {
isWinner = true;
winner = gamepiece;
}
//Horizontal Win
else if ((game_space[1] == gamepiece) && (game_space[2] == gamepiece) && (game_space[3] == gamepiece)) {
isWinner = true;
winner = gamepiece;
}
else if ((game_space[4] == gamepiece) && (game_space[5] == gamepiece) && (game_space[6] == gamepiece)) {
isWinner = true;
winner = gamepiece;
}
else if ((game_space[7] == gamepiece) && (game_space[8] == gamepiece) && (game_space[9] == gamepiece)) {
isWinner = true;
winner = gamepiece;
}
//Diagonal Win
else if ((game_space[1] == gamepiece) && (game_space[5] == gamepiece) && (game_space[9] == gamepiece)) {
isWinner = true;
winner = gamepiece;
}
else if ((game_space[3] == gamepiece) && (game_space[5] == gamepiece) && (game_space[7] == gamepiece)) {
isWinner = true;
winner = gamepiece;
}
else {
//Determine if board is full
if ((game_space[1] == ' ') || (game_space[2] == ' ') || (game_space[3] == ' ') || (game_space[4] == ' ') || (game_space[5] == ' ') || (game_space[6] == ' ')
|| (game_space[7] == ' ') || (game_space[8] == ' ') || (game_space[9] == ' ')) {
fullBoard = false;
}
else {fullBoard = true;}
if (fullBoard == true) {
isWinner = true;
winner = 'T';
}
}
}
char Board::get_winner() {
return(winner);
}
void Board::reset_winner() {
winner = 'T';
}