Chess Validation Move input wanted - c++

So, I have gotten quite far in my mission to finish a chess game in c++. However, I have hit a bit of a small issue I would like to get some input on, please.
SITUATION:
My PAWN, KING, KNIGHT move validations work perfect. But;
When moving a piece(such as a white ROOK) it follows most of the rules. For example, it will only move vertical or horizontal, it will not pass another white piece, it will not replace a white piece, and lastly it WILL replace a black (opposing) piece.
The problem is when moving it past a another black piece, it allows passing in order to replace a piece that's past it. So lets say we have a white piece at x=2,y=6 and black piece at x=2,y=4, and another black piece at x=2,y=3. The White piece will be allowed to move to move to x=2,y=3, which should not be allowed. Would love to get some input on how to fix this. Current code below.
bool Rook:: canMove(int startx, int starty, int endx, int endy)
{
int i;
if(board[endx][endy] !=NULL && board[endx][endy]->color==color)
return false;
if (startx == ends) //Collision Detection...
{
// Horizontal move
if (starty < endy)
{
// Move down
for (i = starty + 1; i <= endy; ++i)
if (board[startx][i] != NULL && board[startx][i]->color==color)
return false;
}
else
{
// Move up
for (i = starty - 1; i >= endy; --i)
if (board[startx][i] != NULL && board[startx][i]->color==color) //cant allow passing of non color piece
return false;
}
}
else if (starty == endy)
{
// Vertical move
if (startx < endx)
{
// Move right
for (i = startx + 1; i <= endx; ++i)
if (board[i][starty] != NULL && board[i][starty]->color==color)
return false;
}
else
{
// Move left
for (i = startx - 1; i >= endx; --i)
if (board[i][starty] != NULL && board[i][starty]->color==color)
return false;
}
}
else
{
// Not a valid rook move (neither horizontal nor vertical)
return false;
}
return true;
}

your function has refers to a lot of member variables in the class, e.g. ends, color, board, which isn't good, and makes the function hard to test at a unit level
can you test that function standalone? No you can't.
but it looks like your loops aren't breaking when they should (when they have found a valid move perhaps?)
if the function is allowing move to (2,3) as well as (2,4), then it is looping past (2,4) to (2,3)
also, just using an array and ints for indexing the board isn't very good.
i would have expected a higher-level board class and maybe a coordinate class so you can easily iterate and index the board.

Related

Water in a falling sand simulation

I am currently working on a very simple 'Falling Sand' simulation game in C++ and SDL2, and am having problems with getting water to flow in a more realistic manner. I basically have a grid of cells that I iterate through bottom-to-top, left-to-right and if I find a water cell, I just check below, down to left, down to the right, left then right for empty cells and it moves into the first one its finds (it makes a random choice if both diagonal cells or both horizontal cells are free). I then mark the cell it moved into as processed so that it is not checked again for the rest of that loop.
My problem is a sort of 'left-bias' in how the particles move; if I spawn a square of water cells above a barrier, they will basically all shift to left without moving once the particles begin to reach the barrier, while the cells on the right will run down in the proper way. So instead of forming a nice triangular shape flowing out evenly to both sides, the whole shape will just move to the left. This effect is reversed whenever I iterate left-to-right, so I know it's something to do with that but so far I've been stumped trying to fix it. I initially thought it was a problem with how I marked the cells as processed but I've found no obvious bugs with that system in many hours of testing. Has anyone faced any similar challeneges in developing a simulation like this, or knows something that I'm missing? Any help would be very much appreciated.
EDIT:
Ok so I've made a little progress, however I've ran into another bug that seems to be unrelated to iteration, since now I save a copy of the old cells and read from that to decide an update, then update the original cells and display that. This already made the sand work better, however water, which checks horizontally for free cells, now 'disappears' when it does move horizontally. I've been testing it all morning and have yet to find a solution, I thought it might've been someting to do with how I was copying the arrays over, but it seems to work as far as I can tell.
New snippets:
Simulation.cpp
void Simulation::update()
{
copyStates(m_cells, m_oldCells); // so now oldcells is the last new state
for(int y = m_height - 1; y>= 0; y--)
for(int x = 0; x < m_width; x++)
{
Cell* c = getOldCell(x, y); // check in the old state for possible updates
switch(c->m_type)
{
case EMPTY:
break;
case SAND:
if(c->m_visited == false) update_sand(x, y);
break;
case WATER:
if(c->m_visited == false) update_water(x, y);
break;
default:
break;
}
}
}
void Simulation::update_water(int x, int y)
{
bool down = (getOldCell(x, y+1)->m_type == EMPTY) && checkBounds(x, y+1) && !getOldCell(x, y+1)->m_visited;
bool d_left = (getOldCell(x-1, y+1)->m_type == EMPTY) && checkBounds(x-1, y+1) && !getOldCell(x-1, y+1)->m_visited;
bool d_right = (getOldCell(x+1, y+1)->m_type == EMPTY) && checkBounds(x+1, y+1) && !getOldCell(x+1, y+1)->m_visited ;
bool left = (getOldCell(x-1, y)->m_type == EMPTY) && checkBounds(x-1, y) && !getOldCell(x-1, y)->m_visited ;
bool right = (getOldCell(x+1, y)->m_type == EMPTY) && checkBounds(x+1, y) && !getOldCell(x+1, y)->m_visited ;
// choose random dir if both are possible
if(d_left && d_right)
{
int r = rand() % 2;
if(r) d_right = false;
else d_left = false;
}
if(left && right)
{
int r = rand() % 2;
if(r) right = false;
else left = false;
}
if(down)
{
getCell(x, y+1)->m_type = WATER; // we now update the new state
getOldCell(x, y+1)->m_visited = true; // mark as visited so it will not be checked again in update()
} else if(d_left)
{
getCell(x-1, y+1)->m_type = WATER;
getOldCell(x-1, y+1)->m_visited = true;
} else if(d_right)
{
getCell(x+1, y+1)->m_type = WATER;
getOldCell(x+1, y+1)->m_visited = true;
} else if(left)
{
getCell(x-1, y)->m_type = WATER;
getOldCell(x-1, y)->m_visited = true;
} else if(right)
{
getCell(x+1, y)->m_type = WATER;
getOldCell(x+1, y)->m_visited = true;
}
if(down || d_right || d_left || left || right) // the original cell is now empty; update the new state
{
getCell(x, y)->m_type = EMPTY;
}
}
void Simulation::copyStates(Cell* from, Cell* to)
{
for(int x = 0; x < m_width; x++)
for(int y = 0; y < m_height; y++)
{
to[x + y * m_width].m_type = from[x + y * m_width].m_type;
to[x + y * m_width].m_visited = from[x + y * m_width].m_visited;
}
}
Main.cpp
sim.update();
Uint32 c_sand = 0xedec9a00;
for(int y = 0; y < sim.m_height; y++)
for(int x = 0; x < sim.m_width; x++)
{
sim.getCell(x, y)->m_visited = false;
if(sim.getCell(x, y)->m_type == 0) screen.setPixel(x, y, 0);
if(sim.getCell(x, y)->m_type == 1) screen.setPixel(x, y, c_sand);
if(sim.getCell(x, y)->m_type == 2) screen.setPixel(x, y, 0x0000cc00);
}
screen.render();
I've attached a gif showing the problem, hopefully this might help make it a little clearer. You can see the sand being placed normally, then the water and the strange patterns it makes after being placed (notice how it moves off to the left when it's spawned, unlike the sand)
You also have to mark the destination postion as visited to stop multiple cells moving in to the same place.

How to respond to a limitless possibility of outcomes?

So let's say that there is an imaginary 2 by 2 grid comprised for 4 numbers ...
1 2
3 4
You can either flip the grid horizontally or vertically down the middle by imputing either H or V respectively. You can also flip the grid as many times as you wish, with the previous choice affecting your future outcome.
For example, you could flip the grid horizontally down the middle, and then vertically.
While solving this problem, I got enough code written down so that the program works, except for the part where the "flipping" happens. Since you can enter as many H's and V's as you would like, I have some trouble writing code that would support this action.
Since the program input could contain as many horizontal or vertical flips as the user would prefer, that prevents me from manually using if-statements; in other words, I can't say "if the 1st letter is H, flip horizontally, if the 2nd letter is V, flip vertically, etc.".
This is just a short snippet of what I have figured out so far...
void flipGrid(string str, int letterPlace)
{
while (letterPlace < str.length())
{
if (str.at(letterPlace) == 'H')
{
// flip grid horizontally
}
else if (str.at(letterPlace) == 'V')
{
// flip grid vertically
}
letterPlace += 1;
}
}
int main()
{
int increment = 0;
string userInput;
cin >> userInput;
flipGrid(userInput, increment);
return 0;
}
As you can probably tell, I need help with the parts specified by the comments. If the code were to run as planned, it should look something like this...
Input (example 1)
H
Output
3 4
1 2
Input (example 2)
HVVH
Output (the two H's and the two V's cancel out, leaving us with the original)
1 2
3 4
I feel like there should be an easier way to solve this problem, or is the method I'm currently working on the right way to approach this problem? Please let me know if I'm on the right track or not. Thanks!
I would do a few things. First, I would simply count the H's and V's and, when done, modulo 2 each count. This will leave you flipCountH and flipCountV each having 0 or 1. There's no need to do multiple flips, right? Then you'll at most do each action once.
void flipCounts(string str, int &flipCountH, int &flipCountY)
{
for (char c: str) {
if (c == 'H')
{
++flipCountH;
}
else if (c == 'V')
{
++clipCountY
}
}
}
Use that method, then:
flipCountH %= 2;
flipCountY %= 2;
if (flipCountH > 0) {
performHorizontalFlip();
}
if (flipCountV > 0) {
performVerticalFlip();
}
Now, HOW you flip is based on how you store the data. For this very specific problem, I would store it in an int[2][2].
void performVerticalFlip() {
int[2] topLine;
topLine[0] = grid[0][0];
topLine[1] = grid[0][1];
grid[0][0] = grid[1][0];
grid[0][1] = grid[1][1];
grid[1][0] = topLine[0];
grid[1][1] = topLine[1];
}
Now, you can probably make use of C++ move semantics, but that's an advanced topic. You could also make a swap method that swaps two integers. That's not so advanced.
void swap(int &a, int &b) {
int tmp = a;
a = b;
b = tmp;
}
Then the code above is simpler:
swap(grid[0][0], grid[1][0]);
swap(grid[0][1], grid[1][1]);
Horizontal flip is similar.
From the comments:
I don't know how to flip it in each statement
So, flipping a 2x2 grid vertically is simple:
int tmp = grid[0][0];
grid[0][0] = grid[1][0];
grid[1][0] = tmp;
tmp = grid[0][1];
grid[0][1] = grid[1][1];
grid[1][1] = tmp;
If you have a grid bigger than a 2x2, this will work as well:
// for half the height of the grid
for(unsigned int i = 0;i<Height/2;i++) {
// for the width of the grid
for(unsigned int j =0; j<Width) {
// store a copy of the old value
int tmp = grid[i][j];
// put the new value in
grid[i][j] = grid[Height-1-i][j]; // note, we are flipping this vertically,
// so we want something an equal distance away
// from the other end as us
// replace the value we were grabbing from with the saved value
grid[Height-1-i][j] = tmp;
}
}
In case this is homework, I'm going to leave a horizontal flip for you to figure out (hint, it's the same thing, but with the width and height reversed).

Recursive Mine Explosion Function On a Board Game

I am trying to implement a board game on C++ and its some features are below:
I have 4 sources named as Mine (M), Water (W), Food (F) and Medical Supplies (S)
The Sources will be distributed to the board randomly (which I completed)
User will enter two coordinates and if there is mine on these coordinates they will just blow up and destroy the cells around them depending on their place. For example if the mine is on somewhere in the middle it will destroy the 8 cells around it and if there is another mine around the one which is exploded it will make the other one explode, too.
And there are some exceptions for example if the coordinate is on the corner it will just blow up 3 cell around it.
Let's come to the real problem. When I try to implement it I saw that it is tons of codes actually and I need to make it recursive to give the ability to blow up other cells so for every single possilibility I need to check if the blown cell is a mine or not. Is there an efficient way to implement this or do I need to just write the whole code?
void explode_mines(int x,int y) {
if (x == 0 && y == 0) {
grid[0][0] = 'X';
grid[0][1] = 'X';
if (grid[0][1] == 'X') explode_mines(0, 1);
grid[1][0] = 'X';
//...
grid[1][1] = 'X';
//...
}
//Is there any efficient way?
Pseudo code:
void ExploreCell(int x, int y)
{
if (x or y are out of bounds (less than zero/greater than max))
or (this cell is a mountain, because mountains don't explode))
return
else if this location is a mine
ExplodeMine(x, y) //This cell is a mine, so it blows up again
else
DestroyCell(x, y) //This cell is a valid, non-mine target
}
void ExplodeMine(int x, int y)
{
ExploreCell(x-1, y-1);
ExploreCell(x-1, y);
....
ExploreCell(x+1, y+1);
}
void DestroyCell(int x, int y)
{
//Take care of business
}
I think there's a typo in your code:
grid[0][1] = 'X';
if (grid[0][1] == 'X') explode_mines(0, 1);
How would location (0,1) not be 'X" at this point?
It doesn't have to be recursive, but information theory does say that you have to make 8 checks. You can make it more readable, however. For general purposes, I've found the basic perimeter check to be maintainable. Here, I'll let "O" be a crater and ""M" be a mine.
grid[x][y] = ' '
for (row = x-1; row <= x+1; row++) {
for (col = x-1; col <= x+1; col++) {
if grid[row][col] == "M"
explode_mines(row, col)
}
}
Now, if you have to worry about the time spent for a huge chain reaction, then you can alter your algorithm to keep two lists:
Squares that need checking
Squares with mines to blow up
In this case, explode_mines looks more like this:
Mark x,y as a dead square
Add adjacent squares to the checking list; do *not* add a duplicate
... and you get a new routine check_for_mine that looks like this:
while check list is not empty {
while mine list is not empty {
explode the top mine on the list
}
take the top square from the check list and check it
}
You can play with the nesting, depending on what chain-reaction order you'd like. For breadth-first explosions, you check all squares on the check list, then explode all the mines on the mine list; repeat that until both lists are empty. For depth-first, you can simplify the loops a little: explode every mine as soon as you find it, which means that you don't need a mine list at all.
Hoping this helps [caution: not tested] ('d' for "destroied", 'b' for "bomb")
void destroy (int x, int y)
{
char oldVal;
if ( (x >= 0) && (x < maxX) && (y >= 0) && (y < maxY)
&& ('d' != (oldVal = grid[x][y])) ) // 'd' for destroyed
{
grid[x][y] = 'd'; // set "destroyed"
if ( 'b' == oldVal ) // if it was a bomb, destroy surrounding
{
destroy(x-1, y-1);
destroy(x-1, y);
destroy(x-1, y+1);
destroy(x, y-1);
destroy(x, y+1);
destroy(x+1, y-1);
destroy(x+1, y);
destroy(x+1, y+1);
}
}
}

Implementing Alpha Beta into Minimax

I'm trying to add Alpha Beta pruning into my minimax, but I can't understand where I'm going wrong.
At the moment I'm going through 5,000 iterations, where I should be going through approximately 16,000 according to a friend. When choosing the first position, it is returning -1 (a loss) whereas it should be able to definitely return a 0 at this point (a draw) as it should be able to draw from an empty board, however I can't see where I'm going wrong as I follow my code it seems to be fine
Strangely if I switch returning Alpha and Beta inside my checks (to achieve returning 0) the computer will attempt to draw but never initiate any winning moves, only blocks
My logical flow
If we are looking for alpha:
If the score > alpha, change alpha. if alpha and beta are overlapping, return alpha
If we are looking for beta:
If the score < beta, change beta. if alpha and beta are overlapping, return beta
Here is my
Recursive call
int MinimaxAB(TGameBoard* GameBoard, int iPlayer, bool _bFindAlpha, int _iAlpha, int _iBeta)
{
//How is the position like for player (their turn) on iGameBoard?
int iWinner = CheckForWin(GameBoard);
bool bFull = CheckForFullBoard(GameBoard);
//If the board is full or there is a winner on this board, return the winner
if(iWinner != NONE || bFull == true)
{
//Will return 1 or -1 depending on winner
return iWinner*iPlayer;
}
//Initial invalid move (just follows i in for loop)
int iMove = -1;
//Set the score to be instantly beaten
int iScore = INVALID_SCORE;
for(int i = 0; i < 9; ++i)
{
//Check if the move is possible
if(GameBoard->iBoard[i] == 0)
{
//Put the move in
GameBoard->iBoard[i] = iPlayer;
//Recall function
int iBestPositionSoFar = -MinimaxAB(GameBoard, Switch(iPlayer), !_bFindAlpha, _iAlpha, _iBeta);
//Replace Alpha and Beta variables if they fit the conditions - stops checking for situations that will never happen
if (_bFindAlpha == false)
{
if (iBestPositionSoFar < _iBeta)
{
//If the beta is larger, make the beta smaller
_iBeta = iBestPositionSoFar;
iMove = i;
if (_iAlpha >= _iBeta)
{
GameBoard->iBoard[i] = EMPTY;
//If alpha and beta are overlapping, exit the loop
++g_iIterations;
return _iBeta;
}
}
}
else
{
if (iBestPositionSoFar > _iAlpha)
{
//If the alpha is smaller, make the alpha bigger
_iAlpha = iBestPositionSoFar;
iMove = i;
if (_iAlpha >= _iBeta)
{
GameBoard->iBoard[i] = EMPTY;
//If alpha and beta are overlapping, exit the loop
++g_iIterations;
return _iAlpha;
}
}
}
//Remove the move you just placed
GameBoard->iBoard[i] = EMPTY;
}
}
++g_iIterations;
if (_bFindAlpha == true)
{
return _iAlpha;
}
else
{
return _iBeta;
}
}
Initial call (when computer should choose a position)
int iMove = -1; //Invalid
int iScore = INVALID_SCORE;
for(int i = 0; i < 9; ++i)
{
if(GameBoard->iBoard[i] == EMPTY)
{
GameBoard->iBoard[i] = CROSS;
int tempScore = -MinimaxAB(GameBoard, NAUGHT, true, -1000000, 1000000);
GameBoard->iBoard[i] = EMPTY;
//Choosing best value here
if (tempScore > iScore)
{
iScore = tempScore;
iMove = i;
}
}
}
//returns a score based on Minimax tree at a given node.
GameBoard->iBoard[iMove] = CROSS;
Any help regarding my logical flow that would make the computer return the correct results and make intelligent moves would be appreciated
Does your algorithm work perfectly without alpha-beta pruning? Your initial call should be given with false for _bFindAlpha as the root node behaves like an alpha node, but it doesn't look like this will make a difference:
int tempScore = -MinimaxAB(GameBoard, NAUGHT, false, -1000000, 1000000);
Thus I will recommend for you to abandon this _bFindAlpha nonsense and convert your algorithm to negamax. It behaves identically to minimax but makes your code shorter and clearer. Instead of checking whether to maximize alpha or minimize beta, you can just swap and negate when recursively invoking (this is the same reason you can return the negated value of the function right now). Here's a slightly edited version of the Wikipedia pseudocode:
function negamax(node, α, β, player)
if node is a terminal node
return color * the heuristic value of node
else
foreach child of node
val := -negamax(child, -β, -α, -player)
if val ≥ β
return val
if val > α
α := val
return α
Unless you love stepping through search trees, I think that you will find it easier to just write a clean, correct version of negamax than debug your current implementation.

2D Box Collisions - What am I doing wrong? (C++)

I'm trying to make a platformer game in C++ and I have made a vector of blocks,
and I simply loop through the vector and check for the collision individually:
//Pseudo code
class Block{
...int x
...int y
...int width
...int height
};
class Player{
int x
int y
int width
int height
int hsp //horizontal speed
int vsp //vertical speed
int facing //0 = no direction, -1 = left, 1 = right
...
void loop()
{
if(keyboard_pressed(key_left) { x-=hsp; facing = -1;}
if(keyboard_pressed(key_right) {x+=hsp; facing = 1;}
if(keyboard_pressed(key_up) {y-=vsp;}
if(keyboard_pressed(key_down) {y+=vsp;}
if(keyboard_released(key_left | key_right) {facing = 0;}
for(int i = 0; i < blocks.size(); i++)
{
Block b = blocks.at(i);
check_Collision(b);
}
}
};
As you can see, my player simply moves according to hsp and vsp. Simple enough.
The main portion of my question is in check_Collision(). First I check to see if the player
is on top of the block, and if he is, let him stay there.
Then I check if the player is at the sides of the block.
But for some reason there's a problem. For some reason when I go under the top of the block,
he stays at the top, but then he gets shifted to the left side.
I honestly don't know where to go with this.
The following code only checks for the top and the left side:
check_Collision(){
///////////////////////////////////
var myLeft, myRight, myTop, myBot;
var bLeft, bRight, bTop, bBot;
myLeft = x;
myRight = x + width;
myTop = y;
myBot = y + height;
/////////////////////
bLeft = b.x;
bRight = b.x + b.width;
bTop = b.y;
bBot = b.y + b.height;
//////////////////////////////////
//Check if we are at the top
if(myBot + vsp > bTop+1){
y = bTop - height;
}
//Check if we are at the sides
if(myBot > bTop+2){
if(myRight + hsp > bLeft)
{
x = bLeft - width;
}
}
}
If anyone can point me into some tutorial on 2D box collision that would be great.
The logic you're using doesn't make sense to me. It's not enough just to check that the player is under the block: don't you also need to make sure that the player is standing on it, ie, isn't too far to the right or left of the block? Similarly, in your second check, you've got to make sure that the player isn't jumping over the block (or standing on it). Your if partially checks this, but doesn't take into account the fact that the first check might have modified player position.
Can you assume that the player can't ever walk under the block? Can you assume that the player will never move fast enough to "tunnel" completely through the block (hsp > b.width, etc)?
If the answer to either of these is no, you will need significantly more sophisticated collision detection.