Learning coding with my daughter again. This is our first C++ program. I'm sure there's much that could be improved on. Any comments gratefully received.
New version here: Noughts & Crosses (tic-tac-toe) revisited (using C++20 modules)
ttt.cpp
#include <iostream>
#include <cstdint>
#include <limits>
#include "Board.h"
using std::cout, std::cin, std::numeric_limits, std::streamsize;
enum Player : char { HUMAN = 'X', MACHINE = 'O' };
char currentPlayer = HUMAN;
Board board;
void switchPlayer() {
currentPlayer = (currentPlayer == HUMAN) ? MACHINE : HUMAN;
}
uint8_t get_human_move(){
int digit;
while (true) {
cout << "Enter your move (1 - 9): ";
if ((cin >> digit) && digit > 0 && digit <= 9)
break;
cout << "Oops... entry must be a single digit between 1 and 9\n";
cin.clear();
cin.ignore(numeric_limits<streamsize>::max(), '\n');
}
return static_cast<uint8_t>(digit);
}
void makeHumanMove(){
uint8_t position;
while (true){
position = get_human_move();
if (board.placeSymbol(position,HUMAN)) {
break;
}
cout << "Already taken, try again.\n";
}
}
void clear_screen() {
#ifdef _WIN32
system("cls");
#else
system("clear");
#endif
}
int main() {
board.display();
while (true) {
if (currentPlayer == HUMAN)
makeHumanMove();
else
board.placeSymbol(board.getMachineMove(),MACHINE);
clear_screen();
board.display();
if (board.win()){
cout << currentPlayer << " wins\n";
break;
}
if (board.tie()){
cout << "It's a tie!\n";
break;
}
switchPlayer();
}
return 0;
}
Board.h
#include <unordered_set>
#include <vector>
#include <algorithm>
#include <random>
#include <iostream>
class Board {
private:
char board[3][3] = {{'1','2','3'},{'4','5','6'},{'7','8','9'}};
// set of available positions from which machine generates its next move
std::unordered_set<uint8_t> availablePositions = {1, 2, 3, 4, 5, 6, 7, 8, 9};
void removeAvailablePosition(int position);
public:
Board();
void display();
bool placeSymbol(int position, char symbol);
bool tie();
bool win();
int getMachineMove();
};
Board.cpp
#include "Board.h"
using std::cout, std::cin;
Board::Board(){
//shuffle the unordered set to ensure different ordering each run
std::vector<int> vec(availablePositions.begin(), availablePositions.end());
std::random_device rd;
std::mt19937 gen(rd());
std::shuffle(vec.begin(), vec.end(), gen);
availablePositions.clear();
for (int elem : vec) {
availablePositions.insert(elem);
}
}
void Board::display() {
cout << "\n";
cout << " " << board[0][0] << " | " << board[0][1] << " | " << board[0][2] << "\n";
cout << "-----------\n";
cout << " " << board[1][0] << " | " << board[1][1] << " | " << board[1][2] << "\n";
cout << "-----------\n";
cout << " " << board[2][0] << " | " << board[2][1] << " | " << board[2][2] << "\n";
cout << std::flush;
}
void Board::removeAvailablePosition(int position){
availablePositions.erase(position);
}
bool Board::placeSymbol(int position, char symbol) {
uint8_t row = (position-1) / 3;
uint8_t col = (position-1) % 3;
if (board[row][col] == 'X' || board[row][col] == 'O') {
return false;
}
board[row][col] = symbol;
removeAvailablePosition(position);
return true;
}
bool Board::win() {
for (uint8_t n=0; n<3; n++) {
//col n
if (board[n][0] == board[n][1] && board[n][1] == board[n][2]) {
return true;
}
// row n
if (board[0][n] == board[1][n] && board[1][n] == board[2][n]) {
return true;
}
}
// diagonals
if (board[0][0] == board[1][1] && board[1][1] == board[2][2]) {
return true;
}
if (board[0][2] == board[1][1] && board[1][1] == board[2][0]) {
return true;
}
return false;
}
bool Board::tie(){
if (board[0][0] != '1' && board[0][1] != '2' && board[0][2] != '3' &&
board[1][0] != '4' && board[1][1] != '5' && board[1][2] != '6' &&
board[2][0] != '7' && board[2][1] != '8' && board[2][2] != '9') {
return true;
} else {
return false;
}
}
int Board::getMachineMove(){
// just gets the first available position from unordered set of available positions
// i.e. a random move!
uint8_t move = *availablePositions.begin();
removeAvailablePosition(move);
return move;
}