I'm writing a Tic Tac Toe Game and I would like to know how I can make an efficient function to check who won. A two dimensional array congaing X's, O's, or blank spaces represents the board.
char CheckWin(const char board[][NUM_COLS], int& sum) // tic tac toe board - IN
{
char tmp;
int lcv;
tmp = ' ';
if (sum == 9)
{
return 'T';
}
else if (sum != 9)
{
if (((tmp = board[1][1]) != ' ' && board[0][0] == tmp && board[2][2] == tmp) || (board[2][0] == tmp && board[0][2] == tmp))
{
return tmp;
}
for (lcv = 0; lcv < 3; lcv++)
{
if ((tmp = board[lcv][0]) != ' ' && board[lcv][1] == tmp && board[lcv][2] == tmp)
{
return tmp;
}
else if ((tmp = board[lcv][0]) != ' ' && board[lcv][1] == tmp && board[lcv][2] == tmp)
{
return tmp;
}
}
}
return 'N';
}
Besides doing something similar to this over and over again, how could I check who won and return an X if X has won, an O if O has one, a T if it's a tie, and N if no one has one yet. Thanks in advance. I'm trying to get familiar with C++ and programming in general still.
EDIT: I just went with the simple method, but I somehow messed it up, anybody know how? It looks like it's not return anything because when I call it in the main after a player picks a row and column(that's working fine), it doesn't output anything
You could convert the array into two nine-bit values, one for the O positions and one for the X position, and a count of blank spaces:
x_mask = 0
y_mask = 0
empty_count = 0
mask = 1
for each square
if x then x_mask |= mask
if y then y_mask |= mask
if empty then empty_count++
mask <<= 1
Then compare the x_mask and y_mask against the eight possible winning combinations:
for each player
for each winning combination
if player_mask & winning_mask == winning_mask then player has won
and then handle the cases neither player has won:
if neither player won
if empty_count == 0
its a tie
else
moves still available
A simple "structured" approach
If you think of the board as:
A B C
D E F
G H I
Then one minimal selection of boxes that any winning layout must touch would be:
A B C
D
G
You can conceive the movement from any of these locations in a winning line in terms of a shift of 0, 1 or -1 positions in each of the X and Y directions. We can list the movements that you'd need to check:
A: (++x) (++x, ++y) (++y)
B: (++y)
C: (++y) (--x, ++y)
D: (++x)
E: (++x)
In C++, you can create a list/vector of the x/y coordinates of the starting points and the +/-/0 x/y movement deltas shown above, then use three nested loops to evaluate each line across the board.
This is considerably more work than just hardcoding the two loops over x and y coordinates and the two diagonals (below), but it's a more algorithmic approach that might appeal intellectually: more like what you might have to do if you were handling a much bigger board.
Obvious brute force approach
For the record, that simpler approach would look like this:
int x;
for (row = 0; row < 3; ++row)
if ((x = board[row][0]) != Empty &&
board[row][1] == x && board[row][2] == x)
return x;
// similar loop for columns...
...
// hardcode diagonals...
if ((x = board[1][1]) != Empty &&
(board[0][0] == x && board[2][2] == x ||
board[2][0] == x && board[0][2] == x))
return x
I suppose you could assign each winning board possibility a number (basically a hash value) and then check if the current board matches any of the values in the table by generating its hash value. On the other hand, I wouldn't suggest spending too much time trying to make the CheckWin function super-efficient. Unless it's being called millions of times or something and needs to be really fast, spend your time on something else--it probably won't be a bottleneck.
Related
TLDR: I am making a tic tac toe game in c++. My win condition checking if statements are failing and I don't know why :(
The board state is maintained by a global 2D board array
int board[3][3]{ {0,0,0}, {0,0,0}, {0,0,0}};
As the game plays on, a 1 or 2 is inserted to represent an X or O. Periodically I check for a winner with a function that uses 8 if statements to check for win conditions (3 horizontals, 3 verticals, or 2 diagonals).
int winCheck()
{
int winner = 0;
// check horizontal 1
if ((board[0][0] == board[0][1] == board[0][2]) && (board[0][2] > 0))
{
winner = board[0][0];
}
// check horizontal 2
...
return winner;
}
Any ideas? I think the logic is fine but my syntax is off.
Sigh. After pulling my brain out for 4 hours. It appears that you can't logically compare 3 things in an if statement ie:
if (A == B == C)
You must instead do 2 comparisons...
if (A == B && B == C)
Maybe this will help someone someday...
I'm working on a program that hides a number in a grid (2d array, 10 columns and 10 rows labeled with array filled with a character such as a . or -) and prints the grid to screen.
a num for row and column are random generated and saved to array as an 'X', only shown when found
The user enters the row and column....then displays that user's guess on the grid to the screen as a character. after that, choose which direction to move...and give hints on where the randomized location for that item is hidden in the array within ten tries total or less(user cannot go to previous guess played during the program).
for example:
while (guess <9 || found == false){
cout << "Enter your next move....n, e, s, w, ne, nw, se, sw";
cin >> d;
if (d == 'E' || d == 'e')
{
col++;
guess++;
array[col][row] = guess;
cout << "moved east";
}
else if (d == 'W' || d == 'w')
{
col--;
guess++;
array[col][row] = guess;
cout << "west...";
}
....etc
else {
guess++;
cout << "not valid move. turn is incremented";
}
then after the entire while loop on all those, i display the array again but I can't get the moves to show up other than the first step(need it to do 1, 2, 3...10 or until found=true to display 'X') and am not sure how to give the hint on where the item is hidden based on guess direction. if the item is north above where the user guessed their first starting place(column and row), it should display hint based on that location. How could I implement that?
Must be: while(guess < 9&&!found), either condition not met shall leave to exiting, so you only stay in the loop if both are met... Side note: It is better code style not to compare booleans, just use if(condition) or if(!condition) as in the fixed loop condition.
Be aware that you should check your board's borders:if(col > 0)
{
--col;
++guess;Analogously for the other directions...
You need to check, if the field intended to visit already has been visited. Assuming the fields unvisited carry value 0, you can add the following condition to the border check:if(col > 0 && array[col - 1][row] != 0)Again, other directions analogously.
For the hints, calculate the distances of the coordinates between target and current position; if negative, add 'n' or 'w' to your hint for vertical or horizontal direction respectively, if positive, 's' or 'e', otherwise, no hint for the respective direction (easiest variant, prefers diagonal directions first).
Side note array[col][row]: Typically, you name the indices of the array the other way round: array[row][col]; if iterating, you use outer loop for rows, inner loop for cols (to profit from data locality):
for(r = 0; r < rows; ++r)
for(c = 0; c < cols; ++c)
std::cout << getCharFor(array[r][c]);
North and south, with code above, still are associated with vertical, east and west with horizontal direction...
Edit in response to your comment:
You do not need to check the border you are moving away from...if(d == "e")
{
if(/*col > 0 &&*/ col <= 10)
//^^^^^^^^^ obsolete...
}
If your array has a size of NxN, then the maximum index you can access is N - 1, so you only can go east if you are currently maximally at position N - 2 (if(col < N-1))
If you go diagonally, you have to check both horizontal and vertical direction:if(d == "sw")
{
if(row < N - 1 && col > 0)
}
If you have determined that you do not exit the board with the designated move, then you can access the field in the designated direction:if(d == "sw")
{
if(array[row + 1][col - 1] != 0)
// visited already; assuming you have initialized the array with 0...
}
Side note: For the hints, there is yet an unconsidered problem left: What, if the user does not follow them, and due to this, the program gives a hint to a field that already has been visited???
int trees = 3;
int tree_x, tree_y;
for(int r = 0; r < m_townsize; r++)
{
for(int c = 0; c < m_townsize; c++)
{
if(r == 0 || c == 0 || r == (m_townsize - 1) || c == (m_townsize - 1))
m_town[r][c] = 'W';
while(trees > 0)
{
tree_x = random() % m_townsize;
tree_y = random() % m_townsize;
cout << tree_y << "," << tree_x << endl;
if(m_town[tree_y][tree_x] == ' ')
{
m_town[tree_y][tree_x] = 'T';
trees -= 1;
}
}
}
}
According the code I have written, if there is a space character at the coordinate of the tree, it should place a tree and lower the tree count by 1.
If there is not a space there, it should skip placing a tree, thus not decrementing. This should cause it to pick another set of coordinates and run through again.
However, if you look at this particular output it is running to the if-statement skipping the first option to replace it with a T--since it is a W--but still decrementing by 1. I don't get it. It should skip the statement all together, not skip just the first line. Netbeans tells me my brackets are right, so it shouldn't be an issue with the assignment belonging to the if and the decrement belonging to the while.
If I make a do-while loop it places a whole bunch. I don't know what's happening.
This output placed 2 trees.
You are walking over each coordinate.
If it is on the edge you put a 'W'. Then you randomly place a tree 'T'.
Then you proceed to the next coordinate.
This means you can place some trees in squares before you overwrite with a 'W'.
Finish all the walls before placing trees. Consider a more efficient way to place walls to, like doing each edge instead of loopimg over the middle abd doing nothing.
i have a problem with the next question, i need to solve it by using if/else, i wrote the code but i don't know if it's the solve of the question or not:
Write a program in which the user enters the coordinates of the black pawns (a, b) on the chessboard.
The program must determine whether the pawn may move to get to one field (c, d):
1. In the ordinary move;
2. When it "hit" piece or pawn opponent.
Note: Black pawn move on the board from the bottom up.
char CoordinY;
int CoordinX;
if (CoordinY > 'b' && CoordinX <= 1 && CoordinX>8)
{
cout << "Error . . . \n";
}
else
{
if (CoordinX >= 2 && CoordinX <= 8 && CoordinY == 'a' || CoordinY == 'b'*)
{
// arbitrary move:
cout << "will not get to the field (c, d) in the ordinary move.\n";
// when it "hits" enemy's figure or pawn
cout << "will not get to the field (c, d) when ше hit a figure or pawn opponent.\n";
}
else if (CoordinX>1 && CoordinX < 8 && CoordinY == 'b')
{
// arbitrary move
cout << "will not get to the field (c, d) in the ordinary move.\n";
// when it "hits" enemy's figure or pawn
cout << "will not get to the field (c, d) when it hit a figure or pawn opponent.\n";
}
In the answer I assume the following classic chess board and the fact that I am moving white pawn:
It is important because in your problem definition, blacks are moving bottom up, which is incorrect.
In my example, I will use the following variables:
char a, c; int b, d; // E2 - E4 is: a = 'e', b = 2, c = 'e', d = 4.
Arbitrary move
Where can a pawn go with an arbitrary move in chess?
One step ahead
Two steps ahead if it is standing at row 2
So, in general, a pawn at (a; b) can move to (c; d), if they stand in the same row (a == c) AND if it is one step ahead or two steps ahead for b equal to 2.
So, let's implement it:
if (a == c && (d - b == 1 || (d - b == 2 && b == 2)))
cout << "Abitrary move: YES";
} else {
cout << "Arbitrary move: NO";
}
Attack
A pawn can move with an attack if only an enemy is standing in the next row, one cell to the left or to the right:
if ((c == a + 1 || c == a - 1) && (d - b == 1))
cout << "Attack: YES";
} else {
cout << "Attack: NO";
}
Note that this solution is not working for the case which is called en passant (is it more well-known as "битое поле" or "взятие на проходе" in Russian).
This is a solution in pseudocode:
if (d == b - 1) // destination is one square up
if (c == a) // pawn is on a square in the same column as destination
return true; // yes, pawn can move forwards to destination
if (c == a - 1 || c == a + 1) // destination is one square to left or right
return true; // yes, pawn can take a white pawn to move to destination
end if
return false
First we do the check which is true for both cases... is the destination only one step in front (note that in chess pawns can also move two squares on their first turn, but you didn't request that solution so I haven't added it)?
Next we check if the move is either straight ahead, or diagonal.
You might want to check that a,b c,d are all valid chess coordinates to start with, which would prevent illegal moves being marked as ok.
Edit: also I'm assuming that the bottom of the board has a larger 'y' coordinate than the top. If the coordinates are reversed you would check for b + 1 in the first conditional.
I have a very long if statement that checks for winning conditions of a tic-tac-toe board:
if
((tttPositions[i][j][0] && tttPositions[i][j][1] && tttPositions[i][j][2] == 1)
|| (tttPositions[i][j][3] && tttPositions[i][j][4] && tttPositions[i][j][5] == 1)
|| (tttPositions[i][j][6] && tttPositions[i][j][7] && tttPositions[i][j][8] == 1)
|| (tttPositions[i][j][0] && tttPositions[i][j][3] && tttPositions[i][j][6] == 1)
|| (tttPositions[i][j][1] && tttPositions[i][j][4] && tttPositions[i][j][7] == 1)
|| (tttPositions[i][j][2] && tttPositions[i][j][5] && tttPositions[i][j][8] == 1)
|| (tttPositions[i][j][0] && tttPositions[i][j][4] && tttPositions[i][j][8] == 1)
|| (tttPositions[i][j][2] && tttPositions[i][j][4] && tttPositions[i][j][6] == 1))
{
if (j = 0)
cout << "x wins" << endl;
if (j = 1)
cout << "o wins" << endl;
}
This looks really ugly.. is there a way to keep track of the winning conditions separately to greatly reduce the length of this if statement?
I put the multiple tic tac toe boards into a 3D vector where each box is 2x9, the first row represents the 'x' positions and the second row represents the 'o' positions, so like:
110000011
001110000
represents:
x x o
o o _
_ x x
You can make a lambda that accepts 3 integers, and checks if they are set in the array. You can also bind tttPositions[i][j] to a reference just to make typing it a little less cumbersome.
auto& pos = tttPositions[i][j];
auto check = [&pos](int a, int b, int c) {
return (pos[a] == 1) && (pos[b] == 1) && (pos[c] == 1);
};
// these three lines don't shorten the code, but
// they do make the if statememt more readable, imo
bool horizontal = check(0, 1, 2) || check(3, 4, 5) || check(6, 7, 8);
bool vertical = check(0, 3, 6) || check(1, 4, 7) || check(2, 5, 8);
bool diagonal = check(0, 4, 8) || check(2, 4, 6);
With this, your if becomes much simpler (and self documenting due to the variable names):
if (horizontal || vertical || diagonal)
A further simplification (again, not in code size, but in readability) would be to factor all this out into a function. I'm not sure what type tttPositions[i][j] is, but assuming it is an array of 9 ints, you could do this:
bool check_win_condition(int const (&pos)[9]) {
// all the stuff I did before, except instead of an if statement, just return
return horizontal || vertical || diagonal;
}
Then in your other function, the if becomes simply this:
if (check_win_condition(tttPositions[i][j]))
You might want to consider creating a data structure at compile time to store the winning lines and then traverse that data structure at runtime to check if a player position includes a winning line instead of a lot of ifs.
I noticed you have a lot of duplicate checks in your condition. For example you are checking tttPositions[i][j][4] four times. The cost is probably insignificant but it would be nice to reduce that.
One data structure you could use to store the winning lines would be a kind of tree. At the top of the tree would be a carefully chosen selection of positions that are included in all winning lines and for each of those parent positions as children you have the winning lines that include those positions.
The tree would only need to have two levels which I've unimaginatively called Parent and Child and stored in a flat arrays. The Parent stores the start and end index of it's children. The Child stores the two other positions in the winning line:
struct Parent {
int pos;
int children_start;
int children_end;
};
using Child = std::pair<int, int>;
using Parents = std::array<Parent, 4>;
using Children = std::array<Child, 8>;
// Position indexes:
// 0 | 1 | 2
// ----------
// 3 | 4 | 5
// ----------
// 6 | 7 | 8
// centre top-left bottom-right
constexpr Parents parents = {{ {4,0,4}, {0,4,6}, {8,6,8} }};
// | | |
// +-----+--+--+-----+ +--+--+ +--+--+
// | | | | | | | |
constexpr Children children = {{ {0,8},{2,6},{1,7},{3,5},{1,2},{3,6},{2,5},{7,6} }};
Add a couple of begin and end functions so we can do a range-based for loop on a Parent:
Children::const_iterator begin(const Parent& p) {
return children.begin() + p.children_start;
}
Children::const_iterator end(const Parent& p) {
return children.begin() + p.children_end;
}
And with that in place we are then able to write a function that checks if a position is a win with:
using PlayerPositions = std::array<int, 9>;
bool isWin(const PlayerPositions& pos) {
for (auto& parent : parents) {
if (pos[parent.pos]) {
for (auto& child : parent) {
if (pos[child.first] && pos[child.second])
return true;
}
}
}
return false;
}
Live demo.
It's arguable whether this makes things much simpler in this case but it would be easier to generalise to more complicated game rules. You could even load your winning positions at runtime.