First Program Tic-Tac-ToeTic Tic Tic Tac Tac Tac Toe Toe ToeFirst Tic Tac Toe gameTic Tac Toe C++First...
Gravitational Force Between Numbers
What is the purpose of the yellow wired panels on the IBM 360 Model 20?
Why do the i8080 I/O instructions take a byte-sized operand to determine the port?
How to remove new line added by readarray when using a delimiter?
What is the function of は in the context?
Where is Jon going?
Piping the output of comand columns
Why A=2 and B=1 in the call signs for Spirit and Opportunity?
Why do Russians almost not use verbs of possession akin to "have"?
How does the Earth's center produce heat?
Why is 'additive' EQ more difficult to use than 'subtractive'?
Is there an idiom that means that you are in a very strong negotiation position in a negotiation?
How did the Allies achieve air superiority on Sicily?
Can flying creatures choose to hover, even if they don't have hover in their flying speed?
Did Game of Thrones end the way that George RR Martin intended?
Why does the Starter Set wizard have six spells in their spellbook?
Is there a simple example that empirical evidence is misleading?
Can a UK national work as a paid shop assistant in the USA?
Who wrote “A writer only begins a book. A reader finishes it.”?
What is the use case for non-breathable waterproof pants?
Complications of displaced core material?
One word for 'the thing that attracts me'?
The disk image is 497GB smaller than the target device
Is keeping the forking link on a true fork necessary (Github/GPL)?
First Program Tic-Tac-Toe
Tic Tic Tic Tac Tac Tac Toe Toe ToeFirst Tic Tac Toe gameTic Tac Toe C++First Console Game: Tic-Tac-ToeTic-Tac-Toe programSimple text-based tic-tac-toeFunction-based Tic-Tac-Toe game in CFirst Python program: Tic-Tac-ToeFirst Python program: Tic-Tac-Toe (Followup)First proper program, Tic Tac Toe
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty,.everyoneloves__bot-mid-leaderboard:empty{ margin-bottom:0;
}
$begingroup$
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|Cn";
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";
}
c++ tic-tac-toe
New contributor
$endgroup$
add a comment |
$begingroup$
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|Cn";
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";
}
c++ tic-tac-toe
New contributor
$endgroup$
add a comment |
$begingroup$
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|Cn";
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";
}
c++ tic-tac-toe
New contributor
$endgroup$
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|Cn";
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";
}
c++ tic-tac-toe
c++ tic-tac-toe
New contributor
New contributor
edited 3 hours ago
Jamal♦
30.7k12122228
30.7k12122228
New contributor
asked 7 hours ago
MarcinMarcin
312
312
New contributor
New contributor
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
$begingroup$
Here are some things that may help you improve your code.
Use the required #include
s
The code uses std::string
which means that it should #include <string>
. It was not difficult to infer, but it helps reviewers if the code is complete.
Have you run a spell check on comments?
If you run a spell check on your comments, you'll find a number of things such as "diagnols" and "diaganol" instead of "diagonals" and "diagonal." Since your code is nicely commented, it's worth the extra step to eliminate spelling errors.
Be wary of recursive calls
The ask_input
routine has a subtle flaw. In particular, because it is recursive, it may be possible for a malicious user to crash the program by exhausting the stack. All that would be required would be to continue to input improperly formatted data. For this reason, as well as to make the code more understandable, I'd suggest instead to make retry
a local variable and use that, as in a while
loop, to re-ask if needed.
Fix the bug
The ask_input
has a not-so-subtle flaw as well. It checks the letter, but not the number, so a user could input C9
or A0
and the program would attempt to use that!
Don't use std::endl
if you don't really need it
The difference betweeen std::endl
and 'n'
is that 'n'
just emits a newline character, while std::endl
actually flushes the stream. This can be time-consuming in a program with a lot of I/O and is rarely actually needed. It's best to only use std::endl
when you have some good reason to flush the stream and it's not very often needed for simple programs such as this one. Avoiding the habit of using std::endl
when 'n'
will do will pay dividends in the future as you write more complex programs with more I/O and where performance needs to be maximized.
Be judicious with the use of inline
If a function is small and time critical, it makes sense to declare it inline
. However, the check_win
function is not really time critical, so I would say that there's no reason to make it inline
.
Consider using a stream inserter
The existing print_board
function is written exactly as one would write as one would write a stream inserter. The only thing that would change would be the declaration:
std::ostream &operator<<(std::ostream& os, const Board& board) { /* ... */ }
Simplify your constructor
The Board
constructor is currently defined like this:
Board()
{
for(auto begin = board.begin(),end = board.end();begin != end; ++begin)
*begin = BoardValue::none;
}
There are at least three ways to simplify it. One would be to use a "range-for" syntax:
Board()
{
for(auto space : board) {
space = BoardValue::none;
}
}
Another would be use fill
:
Board() {
board.fill(BoardValue::none);
}
A third way would allow you omit the constructor entirely. Do that by using aggregate initialization in the declaration of board
:
std::array<char, 9> board{
' ',' ',' ',
' ',' ',' ',
' ',' ',' ',
};
Think carefully about the class design
The structure of the code is not bad, but some things to think about are what things should be the responsibility of the Board
class and which are not. For example, I think it might make more sense for Board
to keep track of the number of turns.
Simplify the code
This line is not easy to read or understand:
turn = turn == BoardValue::o ? BoardValue::x : BoardValue::o;
I would suggest instead having turn
be a bool
that represents O
. Then flipping back and forth would simply be turn = !turn;
.
$endgroup$
1
$begingroup$
inline
has exactly one effect left: It allows the symbol to be supplied, with identical definition, by multiple translation-units. As compilers can only inline what they can see, without lto the candidate must be in the same TU.
$endgroup$
– Deduplicator
3 hours ago
add a comment |
Your Answer
StackExchange.ifUsing("editor", function () {
StackExchange.using("externalEditor", function () {
StackExchange.using("snippets", function () {
StackExchange.snippets.init();
});
});
}, "code-snippets");
StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "196"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);
StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});
function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});
}
});
Marcin is a new contributor. Be nice, and check out our Code of Conduct.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f220597%2ffirst-program-tic-tac-toe%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
$begingroup$
Here are some things that may help you improve your code.
Use the required #include
s
The code uses std::string
which means that it should #include <string>
. It was not difficult to infer, but it helps reviewers if the code is complete.
Have you run a spell check on comments?
If you run a spell check on your comments, you'll find a number of things such as "diagnols" and "diaganol" instead of "diagonals" and "diagonal." Since your code is nicely commented, it's worth the extra step to eliminate spelling errors.
Be wary of recursive calls
The ask_input
routine has a subtle flaw. In particular, because it is recursive, it may be possible for a malicious user to crash the program by exhausting the stack. All that would be required would be to continue to input improperly formatted data. For this reason, as well as to make the code more understandable, I'd suggest instead to make retry
a local variable and use that, as in a while
loop, to re-ask if needed.
Fix the bug
The ask_input
has a not-so-subtle flaw as well. It checks the letter, but not the number, so a user could input C9
or A0
and the program would attempt to use that!
Don't use std::endl
if you don't really need it
The difference betweeen std::endl
and 'n'
is that 'n'
just emits a newline character, while std::endl
actually flushes the stream. This can be time-consuming in a program with a lot of I/O and is rarely actually needed. It's best to only use std::endl
when you have some good reason to flush the stream and it's not very often needed for simple programs such as this one. Avoiding the habit of using std::endl
when 'n'
will do will pay dividends in the future as you write more complex programs with more I/O and where performance needs to be maximized.
Be judicious with the use of inline
If a function is small and time critical, it makes sense to declare it inline
. However, the check_win
function is not really time critical, so I would say that there's no reason to make it inline
.
Consider using a stream inserter
The existing print_board
function is written exactly as one would write as one would write a stream inserter. The only thing that would change would be the declaration:
std::ostream &operator<<(std::ostream& os, const Board& board) { /* ... */ }
Simplify your constructor
The Board
constructor is currently defined like this:
Board()
{
for(auto begin = board.begin(),end = board.end();begin != end; ++begin)
*begin = BoardValue::none;
}
There are at least three ways to simplify it. One would be to use a "range-for" syntax:
Board()
{
for(auto space : board) {
space = BoardValue::none;
}
}
Another would be use fill
:
Board() {
board.fill(BoardValue::none);
}
A third way would allow you omit the constructor entirely. Do that by using aggregate initialization in the declaration of board
:
std::array<char, 9> board{
' ',' ',' ',
' ',' ',' ',
' ',' ',' ',
};
Think carefully about the class design
The structure of the code is not bad, but some things to think about are what things should be the responsibility of the Board
class and which are not. For example, I think it might make more sense for Board
to keep track of the number of turns.
Simplify the code
This line is not easy to read or understand:
turn = turn == BoardValue::o ? BoardValue::x : BoardValue::o;
I would suggest instead having turn
be a bool
that represents O
. Then flipping back and forth would simply be turn = !turn;
.
$endgroup$
1
$begingroup$
inline
has exactly one effect left: It allows the symbol to be supplied, with identical definition, by multiple translation-units. As compilers can only inline what they can see, without lto the candidate must be in the same TU.
$endgroup$
– Deduplicator
3 hours ago
add a comment |
$begingroup$
Here are some things that may help you improve your code.
Use the required #include
s
The code uses std::string
which means that it should #include <string>
. It was not difficult to infer, but it helps reviewers if the code is complete.
Have you run a spell check on comments?
If you run a spell check on your comments, you'll find a number of things such as "diagnols" and "diaganol" instead of "diagonals" and "diagonal." Since your code is nicely commented, it's worth the extra step to eliminate spelling errors.
Be wary of recursive calls
The ask_input
routine has a subtle flaw. In particular, because it is recursive, it may be possible for a malicious user to crash the program by exhausting the stack. All that would be required would be to continue to input improperly formatted data. For this reason, as well as to make the code more understandable, I'd suggest instead to make retry
a local variable and use that, as in a while
loop, to re-ask if needed.
Fix the bug
The ask_input
has a not-so-subtle flaw as well. It checks the letter, but not the number, so a user could input C9
or A0
and the program would attempt to use that!
Don't use std::endl
if you don't really need it
The difference betweeen std::endl
and 'n'
is that 'n'
just emits a newline character, while std::endl
actually flushes the stream. This can be time-consuming in a program with a lot of I/O and is rarely actually needed. It's best to only use std::endl
when you have some good reason to flush the stream and it's not very often needed for simple programs such as this one. Avoiding the habit of using std::endl
when 'n'
will do will pay dividends in the future as you write more complex programs with more I/O and where performance needs to be maximized.
Be judicious with the use of inline
If a function is small and time critical, it makes sense to declare it inline
. However, the check_win
function is not really time critical, so I would say that there's no reason to make it inline
.
Consider using a stream inserter
The existing print_board
function is written exactly as one would write as one would write a stream inserter. The only thing that would change would be the declaration:
std::ostream &operator<<(std::ostream& os, const Board& board) { /* ... */ }
Simplify your constructor
The Board
constructor is currently defined like this:
Board()
{
for(auto begin = board.begin(),end = board.end();begin != end; ++begin)
*begin = BoardValue::none;
}
There are at least three ways to simplify it. One would be to use a "range-for" syntax:
Board()
{
for(auto space : board) {
space = BoardValue::none;
}
}
Another would be use fill
:
Board() {
board.fill(BoardValue::none);
}
A third way would allow you omit the constructor entirely. Do that by using aggregate initialization in the declaration of board
:
std::array<char, 9> board{
' ',' ',' ',
' ',' ',' ',
' ',' ',' ',
};
Think carefully about the class design
The structure of the code is not bad, but some things to think about are what things should be the responsibility of the Board
class and which are not. For example, I think it might make more sense for Board
to keep track of the number of turns.
Simplify the code
This line is not easy to read or understand:
turn = turn == BoardValue::o ? BoardValue::x : BoardValue::o;
I would suggest instead having turn
be a bool
that represents O
. Then flipping back and forth would simply be turn = !turn;
.
$endgroup$
1
$begingroup$
inline
has exactly one effect left: It allows the symbol to be supplied, with identical definition, by multiple translation-units. As compilers can only inline what they can see, without lto the candidate must be in the same TU.
$endgroup$
– Deduplicator
3 hours ago
add a comment |
$begingroup$
Here are some things that may help you improve your code.
Use the required #include
s
The code uses std::string
which means that it should #include <string>
. It was not difficult to infer, but it helps reviewers if the code is complete.
Have you run a spell check on comments?
If you run a spell check on your comments, you'll find a number of things such as "diagnols" and "diaganol" instead of "diagonals" and "diagonal." Since your code is nicely commented, it's worth the extra step to eliminate spelling errors.
Be wary of recursive calls
The ask_input
routine has a subtle flaw. In particular, because it is recursive, it may be possible for a malicious user to crash the program by exhausting the stack. All that would be required would be to continue to input improperly formatted data. For this reason, as well as to make the code more understandable, I'd suggest instead to make retry
a local variable and use that, as in a while
loop, to re-ask if needed.
Fix the bug
The ask_input
has a not-so-subtle flaw as well. It checks the letter, but not the number, so a user could input C9
or A0
and the program would attempt to use that!
Don't use std::endl
if you don't really need it
The difference betweeen std::endl
and 'n'
is that 'n'
just emits a newline character, while std::endl
actually flushes the stream. This can be time-consuming in a program with a lot of I/O and is rarely actually needed. It's best to only use std::endl
when you have some good reason to flush the stream and it's not very often needed for simple programs such as this one. Avoiding the habit of using std::endl
when 'n'
will do will pay dividends in the future as you write more complex programs with more I/O and where performance needs to be maximized.
Be judicious with the use of inline
If a function is small and time critical, it makes sense to declare it inline
. However, the check_win
function is not really time critical, so I would say that there's no reason to make it inline
.
Consider using a stream inserter
The existing print_board
function is written exactly as one would write as one would write a stream inserter. The only thing that would change would be the declaration:
std::ostream &operator<<(std::ostream& os, const Board& board) { /* ... */ }
Simplify your constructor
The Board
constructor is currently defined like this:
Board()
{
for(auto begin = board.begin(),end = board.end();begin != end; ++begin)
*begin = BoardValue::none;
}
There are at least three ways to simplify it. One would be to use a "range-for" syntax:
Board()
{
for(auto space : board) {
space = BoardValue::none;
}
}
Another would be use fill
:
Board() {
board.fill(BoardValue::none);
}
A third way would allow you omit the constructor entirely. Do that by using aggregate initialization in the declaration of board
:
std::array<char, 9> board{
' ',' ',' ',
' ',' ',' ',
' ',' ',' ',
};
Think carefully about the class design
The structure of the code is not bad, but some things to think about are what things should be the responsibility of the Board
class and which are not. For example, I think it might make more sense for Board
to keep track of the number of turns.
Simplify the code
This line is not easy to read or understand:
turn = turn == BoardValue::o ? BoardValue::x : BoardValue::o;
I would suggest instead having turn
be a bool
that represents O
. Then flipping back and forth would simply be turn = !turn;
.
$endgroup$
Here are some things that may help you improve your code.
Use the required #include
s
The code uses std::string
which means that it should #include <string>
. It was not difficult to infer, but it helps reviewers if the code is complete.
Have you run a spell check on comments?
If you run a spell check on your comments, you'll find a number of things such as "diagnols" and "diaganol" instead of "diagonals" and "diagonal." Since your code is nicely commented, it's worth the extra step to eliminate spelling errors.
Be wary of recursive calls
The ask_input
routine has a subtle flaw. In particular, because it is recursive, it may be possible for a malicious user to crash the program by exhausting the stack. All that would be required would be to continue to input improperly formatted data. For this reason, as well as to make the code more understandable, I'd suggest instead to make retry
a local variable and use that, as in a while
loop, to re-ask if needed.
Fix the bug
The ask_input
has a not-so-subtle flaw as well. It checks the letter, but not the number, so a user could input C9
or A0
and the program would attempt to use that!
Don't use std::endl
if you don't really need it
The difference betweeen std::endl
and 'n'
is that 'n'
just emits a newline character, while std::endl
actually flushes the stream. This can be time-consuming in a program with a lot of I/O and is rarely actually needed. It's best to only use std::endl
when you have some good reason to flush the stream and it's not very often needed for simple programs such as this one. Avoiding the habit of using std::endl
when 'n'
will do will pay dividends in the future as you write more complex programs with more I/O and where performance needs to be maximized.
Be judicious with the use of inline
If a function is small and time critical, it makes sense to declare it inline
. However, the check_win
function is not really time critical, so I would say that there's no reason to make it inline
.
Consider using a stream inserter
The existing print_board
function is written exactly as one would write as one would write a stream inserter. The only thing that would change would be the declaration:
std::ostream &operator<<(std::ostream& os, const Board& board) { /* ... */ }
Simplify your constructor
The Board
constructor is currently defined like this:
Board()
{
for(auto begin = board.begin(),end = board.end();begin != end; ++begin)
*begin = BoardValue::none;
}
There are at least three ways to simplify it. One would be to use a "range-for" syntax:
Board()
{
for(auto space : board) {
space = BoardValue::none;
}
}
Another would be use fill
:
Board() {
board.fill(BoardValue::none);
}
A third way would allow you omit the constructor entirely. Do that by using aggregate initialization in the declaration of board
:
std::array<char, 9> board{
' ',' ',' ',
' ',' ',' ',
' ',' ',' ',
};
Think carefully about the class design
The structure of the code is not bad, but some things to think about are what things should be the responsibility of the Board
class and which are not. For example, I think it might make more sense for Board
to keep track of the number of turns.
Simplify the code
This line is not easy to read or understand:
turn = turn == BoardValue::o ? BoardValue::x : BoardValue::o;
I would suggest instead having turn
be a bool
that represents O
. Then flipping back and forth would simply be turn = !turn;
.
edited 5 hours ago
answered 5 hours ago
EdwardEdward
48.6k380217
48.6k380217
1
$begingroup$
inline
has exactly one effect left: It allows the symbol to be supplied, with identical definition, by multiple translation-units. As compilers can only inline what they can see, without lto the candidate must be in the same TU.
$endgroup$
– Deduplicator
3 hours ago
add a comment |
1
$begingroup$
inline
has exactly one effect left: It allows the symbol to be supplied, with identical definition, by multiple translation-units. As compilers can only inline what they can see, without lto the candidate must be in the same TU.
$endgroup$
– Deduplicator
3 hours ago
1
1
$begingroup$
inline
has exactly one effect left: It allows the symbol to be supplied, with identical definition, by multiple translation-units. As compilers can only inline what they can see, without lto the candidate must be in the same TU.$endgroup$
– Deduplicator
3 hours ago
$begingroup$
inline
has exactly one effect left: It allows the symbol to be supplied, with identical definition, by multiple translation-units. As compilers can only inline what they can see, without lto the candidate must be in the same TU.$endgroup$
– Deduplicator
3 hours ago
add a comment |
Marcin is a new contributor. Be nice, and check out our Code of Conduct.
Marcin is a new contributor. Be nice, and check out our Code of Conduct.
Marcin is a new contributor. Be nice, and check out our Code of Conduct.
Marcin is a new contributor. Be nice, and check out our Code of Conduct.
Thanks for contributing an answer to Code Review Stack Exchange!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
Use MathJax to format equations. MathJax reference.
To learn more, see our tips on writing great answers.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f220597%2ffirst-program-tic-tac-toe%23new-answer', 'question_page');
}
);
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown
Required, but never shown