This is the first little project I’ve made that didn’t feel it was complete gibberish. But I couldn’t tell.
The biggest problem I had with this was using the BoardValue
enum
working like I wanted to. I understand that classes should have a level of abstraction to them and I suspect that the way I implemented the at(int)
returning a char
over a BoardValue
took away from that. However, I though having to convert the return from at(int)
to a char if it returned a BoardValue
would be redundant. For example, using a statement like this
char print_char = Board.at(some_index) == BoardValue::o ? 'O' : 'X';
I hope I’ve done a decent job describing my dilemma.
Overall, I’m hoping for some overall general code style tips and pointers on how to write better code from here.
tictactoe.h
#ifndef TICTACTOE #define TICTACTOE #include <array> #include <iostream> enum BoardValue : char{ none = ' ', o = 'O', x = 'X' }; class Board { public: Board() { for(auto begin = board.begin(),end = board.end();begin != end; ++begin) *begin = BoardValue::none; } char at(int index) const{ return board.at(index); } inline bool check_win(BoardValue) const; bool place(int, BoardValue); private: bool check_diagonals(BoardValue) const; bool check_horizontals(BoardValue) const; bool check_verticals(BoardValue) const; std::array<char, 9> board{}; }; inline bool Board::check_win(BoardValue check) const { if(check == BoardValue::none) throw "Trying to check_win for check == BoardValue::none!"; return check_diagonals(check) || check_horizontals(check) || check_verticals(check); } #endif
tictactoe.cpp
#include "tictactoe.h" #include <iostream> //returns false if index is occupied bool Board::place(int index, BoardValue value) { if(board.at(index) != BoardValue::none) return false; board.at(index) = value; return true; } bool Board::check_diagonals(BoardValue check) const { //if middle is not check no diagnols will pass if(board.at(4) != check) return false; //backward diagonal '\' if(board.at(0) == check && board.at(4) == check) return true; //forward diaganol '/' if(board.at(2) == check && board.at(6) == check) return true; return false; } bool Board::check_horizontals(BoardValue check) const { for(int row = 0; row < 3; ++row){ if(board.at(row) == check && board.at(row + 3) == check && board.at(row + 6) == check) return true; } return false; } bool Board::check_verticals(BoardValue check) const { for(int col = 0; col < 3; ++col){ if(board.at(col * 3) == check && board.at(col * 3 + 1) == check && board.at(col * 3 + 2 ) == check) return true; } return false; }
main.cpp
#include "tictactoe.h" #include <iostream> int ask_input(char player, bool retry = false) { if(!retry) std::cout << "It's " << player << "'s turn. Where do you want to go(e.g. A1 B3 C2)? "; else std::cout << "No, no, no " << player << "! Input a letter followed bt a number: "; std::string input; std::cin >> input; if(input.size() < 2) return ask_input(player, true); int col_input{}; switch(*input.begin()) { case 'A': case 'a': col_input = 0; break; case 'B': case 'b': col_input = 1; break; case 'C': case 'c': col_input = 2; break; default: return ask_input(player, true); } int row_input = *(input.begin() + 1) - '0'; //convers char '1' to int 1 --row_input; return col_input * 3 + row_input; } BoardValue ask_turn() //ask whos first if return true O goes first { BoardValue turn; std::string input; std::cout << "Who goes first(X or O)? "; for(bool valid_input{false}; !valid_input;) { std::cin >> input; switch(input.front()) //input cannot be null at this point { case 'x': case 'X': valid_input = true; turn = BoardValue::x; break; case '0': case 'o': case 'O': valid_input = true; turn = BoardValue::x; break; default: std::cout << "Invalid input! Try X or O :"; } } return turn; } std::ostream &print_board(std::ostream &os,const Board &board) { os << " |A|B|C\n"; for(int row = 0; row < 3; ++row) { os << std::string( 8, '-') << '\n'; os << row + 1 << '|'; for(int col = 0; col < 3; ++col) { char follow_char{ col == 2 ? '\n' : '|' }; os << board.at(col * 3 + row) << follow_char; } } os << std::endl; return os; } int main(){ Board board{}; BoardValue turn{ ask_turn() }; //turn will be set back to appropriate value at start of game loop turn = turn == BoardValue::o ? BoardValue::x : BoardValue::o; int turn_count{0}; while(board.check_win(turn) == false) { turn = turn == BoardValue::o ? BoardValue::x : BoardValue::o; print_board(std::cout, board); bool input_valid{false}; while(input_valid == false) { int input; input = ask_input(turn); input_valid = board.place(input, turn); if( input_valid == false ) std::cout << "That place is take! Try again..\n"; } if(++turn_count == 9) //max amount of turns game is tie break; } print_board(std::cout, board); if(turn_count == 9)//game is tie std::cout << "Looks like its a tie...\n"; else std::cout << (char)turn << " wins!\n"; }
The post First Program Tic-Tac-Toe. Trouble with abstraction? appeared first on 100% Private Proxies - Fast, Anonymous, Quality, Unlimited USA Private Proxy!.