Shortening C++ If condition - c++

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.

Related

What does this C++ for-loop expression mean?

I am working through the "Add Binary" problem on leetcode and a solution which I found online is the following:
#include <string>
using std::string;
class Solution {
public:
string addBinary(string a, string b) {
string ret;
bool carry{false};
for (auto apos=a.size(), bpos=b.size(); apos || bpos || carry; ) {
bool abit{apos && a[--apos] == '1'};
bool bbit{bpos && b[--bpos] == '1'};
ret = (abit ^ bbit ^ carry ? "1" : "0") + ret;
carry = abit + bbit + carry >= 2;
}
return ret;
}
};
My question is regarding the for loop above. I understand that two iterations are being instantiated with the first two expressions that are separated by a comma. However, I don't understand how the three units being or'd (ie: ||) is supposed to behave. I'm also curious why it's ok to exclude the iterator expression in this instance, ie the final expression in the for-loop.
Please help me to understand how this code functions.
basically the for loop consist of 3 parts separted by ';'(semi-colon)
1)first part, this part is about initialization of variables, again you can leave it if you want
2)second part, it defines the condition on basis of which for loop will keep running, again you can leave it if you want
3) third part, this is the part where you want to do some operations, conventially iteration value is increment, but again you can leave it if you want
so if you go with this model, I think you can easily break down what is happening in the for loop that you mentioned.
Sometimes it helps to consider the equivalent while loop:
for (auto apos=a.size(), bpos=b.size(); apos || bpos || carry; /*no increment*/) {
// ...
}
->
{
auto apos = a.size();
auto bpos = b.size();
while( apos || bpos || carry ) {
bool abit{apos && a[--apos] == '1'};
bool bbit{bpos && b[--bpos] == '1'};
ret = (abit ^ bbit ^ carry ? "1" : "0") + ret;
carry = abit + bbit + carry >= 2;
/* increment would be here*/
}
}
The loop initializes apos and bpos and continues to loop as long as the condition apos || bpos || carry yields true, ie as long as apos, bpos and carry are not all 0 (0 is converted to false any other number to true).

What is an efficient way to move in a specific direction relative to its data?

So I'm working on a mini-game where you are a player in an arena with monsters. The goal is to kill all the monsters by shooting them. You can move up/right/down/left, and so can the monsters. If the monsters touch you, you die.
I have to create a player AI for the player to try to stay alive.
So I have defined an integer for each "Danger Zone" for each direction (DangerUp/DangerDown/DangerRight/DangerLeft) and initialized them all to zero. So it looks like this:
int DangerUp = 0, DangerDown = 0, DangerRight = 0, DangerLeft = 0;
If there is a monster above of me, dangerUp will increment by one. If there is one below me, dangerDown will increment by one. Same with the other two.
So what I'm trying to write (in psuedeocode) is essentially:
if I'm surrounded by three (which means 3 of the 4 Danger variables have a value of 1), move to the direction where the danger is 0.
I was planning on doing this:
if (DangerUP == 1 && DangerDOWN == 1 && DangerLEFT == 1 && DangerRIGHT == 0) // if surrounded by 3 in the up, down, left direction
{
// move right;
player_x_direction++; // (this moves my character one direction to the right)
}
and then I plan on doing three else if's where I test for all combinations of having 3 of the 4 variables equal to 1.
Is there a simpler way to do this where I can just move in the direction of whatever danger direction is = 0?
Assuming that the danger values are always 0 or 1, then:
int danger_everywhere = (dangerUp << 3) | (dangerDown << 2)
| (dangerLeft << 1) | (dangerRight);
Then:
if (danger_everywhere == 8 + 4 + 2)
move_right();
if (danger_everywhere == 8 + 4 + 1)
move_left();
if (danger_everywhere == 8 + 2 + 1)
move_down();
if (danger_everywhere == 4 + 2 + 1)
move_up();

If-else statement in c++

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.

Determining if a number is either a multiple of ten or within a particular set of ranges

I have a few loops that I need in my program. I can write out the pseudo code, but I'm not entirely sure how to write them logically.
I need -
if (num is a multiple of 10) { do this }
if (num is within 11-20, 31-40, 51-60, 71-80, 91-100) { do this }
else { do this } //this part is for 1-10, 21-30, 41-50, 61-70, 81-90
This is for a snakes and ladders board game, if it makes any more sense for my question.
I imagine the first if statement I'll need to use modulus. Would if (num == 100%10) be correct?
The second one I have no idea. I can write it out like if (num > 10 && num is < 21 || etc.), but there has to be something smarter than that.
For the first one, to check if a number is a multiple of use:
if (num % 10 == 0) // It's divisible by 10
For the second one:
if(((num - 1) / 10) % 2 == 1 && num <= 100)
But that's rather dense, and you might be better off just listing the options explicitly.
Now that you've given a better idea of what you are doing, I'd write the second one as:
int getRow(int num) {
return (num - 1) / 10;
}
if (getRow(num) % 2 == 0) {
}
It's the same logic, but by using the function we get a clearer idea of what it means.
if (num is a multiple of 10) { do this }
if (num % 10 == 0) {
// Do something
}
if (num is within 11-20, 31-40, 51-60, 71-80, 91-100) { do this }
The trick here is to look for some sort of commonality among the ranges. Of course, you can always use the "brute force" method:
if ((num > 10 && num <= 20) ||
(num > 30 && num <= 40) ||
(num > 50 && num <= 60) ||
(num > 70 && num <= 80) ||
(num > 90 && num <= 100)) {
// Do something
}
But you might notice that, if you subtract 1 from num, you'll have the ranges:
10-19, 30-39, 50-59, 70-79, 90-99
In other words, all two-digit numbers whose first digit is odd. Next, you need to come up with a formula that expresses this. You can get the first digit by dividing by 10, and you can test that it's odd by checking for a remainder of 1 when you divide by 2. Putting that all together:
if ((num > 0) && (num <= 100) && (((num - 1) / 10) % 2 == 1)) {
// Do something
}
Given the trade-off between longer but maintainable code and shorter "clever" code, I'd pick longer and clearer every time. At the very least, if you try to be clever, please, please include a comment that explains exactly what you're trying to accomplish.
It helps to assume the next developer to work on the code is armed and knows where you live. :-)
If you are using GCC or any compiler that supports case ranges you can do this, but your code will not be portable.
switch(num)
{
case 11 ... 20:
case 31 ... 40:
case 51 ... 60:
case 71 ... 80:
case 91 ... 100:
// Do something
break;
default:
// Do something else
break;
}
This is for future visitors more so than a beginner. For a more general, algorithm-like solution, you can take a list of starting and ending values and check if a passed value is within one of them:
template<typename It, typename Elem>
bool in_any_interval(It first, It last, const Elem &val) {
return std::any_of(first, last, [&val](const auto &p) {
return p.first <= val && val <= p.second;
});
}
For simplicity, I used a polymorphic lambda (C++14) instead of an explicit pair argument. This should also probably stick to using < and == to be consistent with the standard algorithms, but it works like this as long as Elem has <= defined for it. Anyway, it can be used like this:
std::pair<int, int> intervals[]{
{11, 20}, {31, 40}, {51, 60}, {71, 80}, {91, 100}
};
const int num = 15;
std::cout << in_any_interval(std::begin(intervals), std::end(intervals), num);
There's a live example here.
The first one is easy. You just need to apply the modulo operator to your num value:
if ( ( num % 10 ) == 0)
Since C++ is evaluating every number that is not 0 as true, you could also write:
if ( ! ( num % 10 ) ) // Does not have a residue when divided by 10
For the second one, I think this is cleaner to understand:
The pattern repeats every 20, so you can calculate modulo 20.
All elements you want will be in a row except the ones that are dividable by 20.
To get those too, just use num-1 or better num+19 to avoid dealing with negative numbers.
if ( ( ( num + 19 ) % 20 ) > 9 )
This is assuming the pattern repeats forever, so for 111-120 it would apply again, and so on. Otherwise you need to limit the numbers to 100:
if ( ( ( ( num + 19 ) % 20 ) > 9 ) && ( num <= 100 ) )
With a couple of good comments in the code, it can be written quite concisely and readably.
// Check if it's a multiple of 10
if (num % 10 == 0) { ... }
// Check for whether tens digit is zero or even (1-10, 21-30, ...)
if ((num / 10) % 2 == 0) { ... }
else { ... }
You basically explained the answer yourself, but here's the code just in case.
if((x % 10) == 0) {
// Do this
}
if((x > 10 && x < 21) || (x > 30 && x < 41) || (x > 50 && x < 61) || (x > 70 && x < 81) || (x > 90 && x < 101)) {
// Do this
}
You might be overthinking this.
if (x % 10)
{
.. code for 1..9 ..
} else
{
.. code for 0, 10, 20 etc.
}
The first line if (x % 10) works because (a) a value that is a multiple of 10 calculates as '0', other numbers result in their remainer, (b) a value of 0 in an if is considered false, any other value is true.
Edit:
To toggle back-and-forth in twenties, use the same trick. This time, the pivotal number is 10:
if (((x-1)/10) & 1)
{
.. code for 10, 30, ..
} else
{
.. code for 20, 40, etc.
}
x/10 returns any number from 0 to 9 as 0, 10 to 19 as 1 and so on. Testing on even or odd -- the & 1 -- tells you if it's even or odd. Since your ranges are actually "11 to 20", subtract 1 before testing.
A plea for readability
While you already have some good answers, I would like to recommend a programming technique that will make your code more readable for some future reader - that can be you in six months, a colleague asked to perform a code review, your successor, ...
This is to wrap any "clever" statements into a function that shows exactly (with its name) what it is doing. While there is a miniscule impact on performance (from "function calling overhead") this is truly negligible in a game situation like this.
Along the way you can sanitize your inputs - for example, test for "illegal" values. Thus you might end up with code like this - see how much more readable it is? The "helper functions" can be hidden away somewhere (the don't need to be in the main module: it is clear from their name what they do):
#include <stdio.h>
enum {NO, YES, WINNER};
enum {OUT_OF_RANGE=-1, ODD, EVEN};
int notInRange(int square) {
return(square < 1 || square > 100)?YES:NO;
}
int isEndOfRow(int square) {
if (notInRange(square)) return OUT_OF_RANGE;
if (square == 100) return WINNER; // I am making this up...
return (square % 10 == 0)? YES:NO;
}
int rowType(unsigned int square) {
// return 1 if square is in odd row (going to the right)
// and 0 if square is in even row (going to the left)
if (notInRange(square)) return OUT_OF_RANGE; // trap this error
int rowNum = (square - 1) / 10;
return (rowNum % 2 == 0) ? ODD:EVEN; // return 0 (ODD) for 1-10, 21-30 etc.
// and 1 (EVEN) for 11-20, 31-40, ...
}
int main(void) {
int a = 12;
int rt;
rt = rowType(a); // this replaces your obscure if statement
// and here is how you handle the possible return values:
switch(rt) {
case ODD:
printf("It is an odd row\n");
break;
case EVEN:
printf("It is an even row\n");
break;
case OUT_OF_RANGE:
printf("It is out of range\n");
break;
default:
printf("Unexpected return value from rowType!\n");
}
if(isEndOfRow(10)==YES) printf("10 is at the end of a row\n");
if(isEndOfRow(100)==WINNER) printf("We have a winner!\n");
}
For the first one:
if (x % 10 == 0)
will apply to:
10, 20, 30, .. 100 .. 1000 ...
For the second one:
if (((x-1) / 10) % 2 == 1)
will apply for:
11-20, 31-40, 51-60, ..
We basically first do x-1 to get:
10-19, 30-39, 50-59, ..
Then we divide them by 10 to get:
1, 3, 5, ..
So we check if this result is odd.
As others have pointed out, making the conditions more concise won't speed up the compilation or the execution, and it doesn't necessarily help with readability either.
It can help in making your program more flexible, in case you decide later that you want a toddler's version of the game on a 6 x 6 board, or an advanced version (that you can play all night long) on a 40 x 50 board.
So I would code it as follows:
// What is the size of the game board?
#define ROWS 10
#define COLUMNS 10
// The numbers of the squares go from 1 (bottom-left) to (ROWS * COLUMNS)
// (top-left if ROWS is even, or top-right if ROWS is odd)
#define firstSquare 1
#define lastSquare (ROWS * COLUMNS)
// We haven't started until we roll the die and move onto the first square,
// so there is an imaginary 'square zero'
#define notStarted(num) (num == 0)
// and we only win when we land exactly on the last square
#define finished(num) (num == lastSquare)
#define overShot(num) (num > lastSquare)
// We will number our rows from 1 to ROWS, and our columns from 1 to COLUMNS
// (apologies to C fanatics who believe the world should be zero-based, which would
// have simplified these expressions)
#define getRow(num) (((num - 1) / COLUMNS) + 1)
#define getCol(num) (((num - 1) % COLUMNS) + 1)
// What direction are we moving in?
// On rows 1, 3, 5, etc. we go from left to right
#define isLeftToRightRow(num) ((getRow(num) % 2) == 1)
// On rows 2, 4, 6, etc. we go from right to left
#define isRightToLeftRow(num) ((getRow(num) % 2) == 0)
// Are we on the last square in the row?
#define isLastInRow(num) (getCol(num) == COLUMNS)
// And finally we can get onto the code
if (notStarted(mySquare))
{
// Some code for when we haven't got our piece on the board yet
}
else
{
if (isLastInRow(mySquare))
{
// Some code for when we're on the last square in a row
}
if (isRightToLeftRow(mySquare))
{
// Some code for when we're travelling from right to left
}
else
{
// Some code for when we're travelling from left to right
}
}
Yes, it's verbose, but it makes it clear exactly what's happening on the game board.
If I was developing this game to display on a phone or tablet, I'd make ROWS and COLUMNS variables instead of constants, so they can be set dynamically (at the start of a game) to match the screen size and orientation.
I'd also allow the screen orientation to be changed at any time, mid-game - all you need to do is switch the values of ROWS and COLUMNS, while leaving everything else (the current square number that each player is on, and the start/end squares of all the snakes and ladders) unchanged.
Then you 'just' have to draw the board nicely, and write code for your animations (I assume that was the purpose of your if statements) ...
You can try the following:
// Multiple of 10
if ((num % 10) == 0)
{
// Do something
}
else if (((num / 10) % 2) != 0)
{
// 11-20, 31-40, 51-60, 71-80, 91-100
}
else
{
// Other case
}
I know that this question has so many answers, but I will thrown mine here anyway...
Taken from Steve McConnell's Code Complete, 2nd Edition:
"Stair-Step Access Tables:
Yet another kind of table access is the stair-step method. This access method isn’t as direct as an index structure, but it doesn’t waste as much data space. The general idea of stair-step structures, illustrated in Figure 18-5, is that entries in a table are valid for ranges of data rather than for distinct data points.
Figure 18-5 The stair-step approach categorizes each entry by determining the level at which it hits a “staircase.” The “step” it hits determines its category.
For example, if you’re writing a grading program, the “B” entry range might be from 75 percent to 90 percent. Here’s a range of grades you might have to program someday:
To use the stair-step method, you put the upper end of each range into a table and then write a loop to check a score against the upper end of each range. When you find the point at which the score first exceeds the top of a range, you know what the grade is. With the stair-step technique, you have to be careful to handle the endpoints of the ranges properly. Here’s the code in Visual Basic that assigns grades to a group of students based on this example:
Although this is a simple example, you can easily generalize it to handle multiple students, multiple grading schemes (for example, different grades for different point levels on different assignments), and changes in the grading scheme."
Code Complete, 2nd Edition, pages 426 - 428 (Chapter 18).

Checking Who Won Tic Tac Toe More Efficient C++

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.