Checking an array for existing values in C++ - c++

I've got this project I'm working on and it is an rpg game running in the cmd. The player navigates his character through the keyboard and fights enemies to level up and so on. I'm using a 2D array as a grid/map and in the demo version everything is working OK.
Problem: Now, in the more advanced version, I have a class which is used to load game/start new game. The function, that starts a new game, basicly creates .txt save files in which the information is stored. The problem is in the function that generates an enemy list. The enemy characteristics that are being generated, and where the problem is, are the X and Y coordinates. Here is a little bit of code showing the process:
void enemyGenerator(int level)
/* Declare the random generator */
std::default_random_engine generator((unsigned)time(0));
std::uniform_int_distribution<int> coordsX(1, 28); //There are 30 rows, from which the first and the last are border
std::uniform_int_distribution<int> coordsY(1, 48); //50 columns; first and last are border
/* Declare some variables */
int x, y;
bool ready = "False";
/* Check what level is demanded, for example level 1 */
if (level == 1)
{
while(true)
{
//Generate X and Y
x = coordsX(generator);
y = coordsY(generator);
//Now where the problem appears to be
//There will be 600 enemies = 1200 coordinates, so I have declared an array in the .h file
//Called coordinates[1200] = {}; now I want to check it bottom to top if
//the newly generated coordinates are already existing, so:
for (int i = 0; i < 1200; i += 2) //1200 = array size; += 2, because we're checking x & y at once
{
if (x != coordinates[i] && y != coordinates[i + 1] && x + y != 2) //x + y can't be 2, because this is where the player starts (1, 1)
{
if (i == 1198) //If these are the last x and y in the array
{
ready = "True";
break;
//Break the for loop with status ready
}
else
{
continue;
//If it isn't the end of the array simply continue checking
}
}
else
{
ready = "False";
break;
//If the x and y match with the ones in the array, then break with status not ready
}
}
if (ready)
{
break;
//If status is ready then break the loop and assign the rest of the stats
}
else
{
continue;
//If status is not ready then continue generating random values
}
}
//Here I define the values of the stats in private variables of the class
eX = x;
eY = y;
eLVL = 1;
//etc...
}
This is the generating code. And here is how I use it:
void newGame()
....
//I've reached to the point where I want to deploy for example 10 enemies of level 1
for (int i = 0; i < 10; i++)
{
enemyGenerator(1);
//I have an already defined fileWriter (std::fstream; std::ios::out)
fileWriter << eX << " " << eY << " " << eLVL; //<< " " etc...
}
....
Everything seems logical to me, the only illogical thing is that it is not working. The output in enemyList.txt I get is for example 3 5 1 (other stats) 3 5 1 (other stats) 3 5 1 (other stats), you get it.
Question: Can you spot any error? Can you show me the right way? If more of the code is required I can even send you the source file, just for the sake of curing my headache.

The problem there is with your random generator.
You are setting the seed of the generator everytime enemyGenerator() is called with the current time. But since you call enemyGenerator multiple times in the same fraction of a second, the time value is the same, hence the random generator seed is the same everytime, which will give you the same random pattern each successive call.
Either use the same generator for all the calls
...
std::default_random_engine random_generator((unsigned)time(0));
//I've reached to the point where I want to deploy for example 10 enemies of level 1
for (int i = 0; i < 10; i++)
{
enemyGenerator(random_generator, 1);
//I have an already defined fileWriter (std::fstream; std::ios::out)
fileWriter << eX << " " << eY << " " << eLVL; //<< " " etc...
}
....
with the enemyGenerator function is defined as
void enemyGenerator(std::default_random_engine& generator, int level)
or seed your generator with a different value each time.
Edit:
Well it seems it isn't the cause of your problem but you should still consider what I wrote.

Related

Printing on specific parts of the terminal

So for my university homework we are supposed to make a simple game of a 2D map with entities etc.
So I've figured a way of printing a map through it's dimensions and text formatting yet in our lessons it wasn't mentioned how we print on specific parts of the terminal. I've checked same questions but can't seem to get a solution.
Here is the code I use to print the map and make it's array. BLUE_B,STANDARD_B,OUTLINE and GREEN_B are declared above for the sake of color enrichment. Also IF POSSIBLE I don't want to use OS specific commands unless it's completely necessary. I use VS Code for Windows, compile with g++ on WSL Ubuntu-20.04.
for (int row = 0; row < i; row++) {
cout << OUTLINE "##";
for (int column = 0; column < j; column++) {
int n = rand() % 10; // According to "rand()"'s value we print either green, blue, or trees
if (n >= 3) { // We've assigned more values to green, in order to be more possible to be printed
cout << GREEN_B " "
STANDARD_B;
map[row][column] = 1;
} else if (n == 0 || n == 1) {
cout << BLUE_B " "
STANDARD_B;
map[row][column] = 0;
} else if (n == 2) {
int tree = rand() % 2;
cout << TREES "<>"
STANDARD_B;
map[row][column] = 0;
}
}
cout << OUTLINE "##"
STANDARD_B << endl;
}
for (i = 0; i < j + 2; i++) { // Bottom map border printing
cout << OUTLINE "##"
STANDARD_B;
}
If I understand the question correctly, you might be looking for iomanip. It is just one way of doing it. You can use setw and setfill to position different text in different areas. You can set different options for different outputs.
To move the text cursor to a specific line and column you need a “gotoxy”-style function.
Here is something that will work on both Linux terminals and the Windows Terminal. (It will not work on Windows Console without additional initialization help.)
#include <iostream>
const char * CSI = "\033[";
void gotoxy( int x, int y )
{
std::cout << CSI << (y+1) << ";" << (x+1) << "H";
}
Coordinates are (0,0) for the UL corner of the terminal. Here is a working example of use:
// continuing from above
#include <string>
int main()
{
// Clear a 40 x 10 box
for (int y = 0; y < 10; y++)
{
gotoxy( 0, y );
std::cout << std::string( 40, ' ' );
}
// Draw our centered text
gotoxy( 14, 5 );
std::cout << "Hello there!";
// Go to bottom of box and terminate
gotoxy( 0, 10 );
std::cout.flush();
}
For your game
I suggest you move the cursor to HOME (0,0) and draw the changed parts of your gameboard each frame.
I suppose that if you are on a local computer and your gameboard is relatively simple, you could probably get away with a complete redraw each frame...
Are you sure there is no professor-supplied macro or command to move the cursor home? (...as he has supplied magic macros to change the output color)?

Spiral Iteration

I need an Algorithm that I will use to scan pixels out from the center. Problem is with different lengths and sizes, it sometimes can't get to the position (See Image below blue part).
To illustrate the problem more I will show the example output:
If you compare the pictures you will notice that it goes in a spiral and the outputs match with a regular for loop and obviously the problem that it doesn't print the blue part correctly
Here is the code:
#include<iostream>
#include<string>
#include<math.h>
int arr[] = { 1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 };
int arrSize = sizeof(arr) / sizeof(arr[0]);
int width = 5;
int height = 3;
void normal2DArray() {
int index = 0;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
std::cout << std::to_string(x) << "," << std::to_string(y) << " = " << std::to_string(arr[index]) << "\n";
index++;
}
}
}
int convertToInex(int x, int y) {
int left = x * y; // elements to the left
int right = (width - x) * y; // elements to the right
return left + right + x;
}
void spiralArray() {
// calculate middle point, which is also the start point
int x = round((float)width / 2) - 1;
int y = round((float)height / 2) - 1;
int direction = 0; // 0=right, 1=up, 2=left, 3=down
int turnCounter = 1;
int numSteps = 1;
int step = 1;
int index;
while (true) {
index = convertToInex(x, y); // defines the index position in arr
std::cout << std::to_string(x) << "," << std::to_string(y) << " = " << std::to_string(arr[index]) << "\n";
switch (direction) {
case 0: x++; break;
case 1: y--; break;
case 2: x--; break;
case 3: y++; break;
}
index = convertToInex(x, y);
if (step % numSteps == 0) {
direction = (direction + 1) % 4;
turnCounter++;
if (turnCounter % 2 == 0) numSteps++;
}
step++;
if (step > arrSize) break;
}
}
void main() {
std::cout << "Output of Normal 2D Array:\n";
normal2DArray();
std::cout << "\n"; // better spacing
std::cout << "Output of Spiral Array:\n";
spiralArray();
}
I tried to keep the code as simple and small as possible. It should be ready to import and use.
And yes I already searched for my answer online but I didn't find anything that covered up the problem here nor had a similar setup like I have(1D arr and combined 2D array WIDTH/HEIGHT) and for sure not in c++.
❗ Also I need a Solution that works with all widths and heights and arr sizes and also works for any side ❗
I hope you can provide me with helpful answers and would be grateful with good and fast algorithm implementations/optimizations
EDIT:
Thanks to the replies in this Thread. I decided to go with the solution from #ldog for now even though I'm not completely satisfied with it.
Here are the edited code parts:
int failcounter = 0;
while (true) {
index = convertToInex(x, y); // defines the index position in arr
if (index < 0 || index > arrSize) failcounter++;
else std::cout << std::to_string(x) << "," << std::to_string(y) << " = " << std::to_string(arr[index]) << "\n";
// unchanged code inbetween
if (step > arrSize + failcounter) break;
Based on your comment:
#Beta they don't need to connect. It just has to detect that it's outside the array size (in that case -1) and don't scan them and find the next continue point. So it would continue like this: 5, 1, 6, 11
it seems you don't care that the spiral goes "out-of-bounds". In this case, the trivial answer is, embed the shapes that have no spiral in one that is always guaranteed to have one.
Thus if your input rectangle is N x M, then embed it in a rectangle of size max(M,N) x max(M,N), solve the problem in the latter, and when printing just ignore non-existent numbers in the original problem. Your printed sequence then will always be unique up to how the embedding occurs. The most reasonable embedding would try to center the smaller rectangle as much as possible in the larger rectangle, but this is up to you.
In this case you don't need an algorithm as you can compute everything analytically if you care to do the book-keeping and figure out the formulas involved.
You can hit a dead end (meaning exit the grid) in four spots. In each case, jump to the next live pixel you would have reached, if any live cells remain.
You can do this fairly easily by keeping track of the four corners you've visited furthest from the starting pixel. Using compass coords and N for up, these are the NE, NW, SW, and SE extremes visited.
If you hit a dead end going N from the NE pixel, jump to the pixel one to the left of the NW pixel and set the movement direction to down. If that is also a dead end, jump to one below the SW pixel and set the movement direction to right. Etc... When all four corners and dead ends then you're done.

How can I add limited coins to the coin change problem? (Bottom-up - Dynamic programming)

I am new to dynamic programming (and C++ but I have more experience, some things are still unknown to me). How can I add LIMITED COINS to the coin change problem (see my code below - is a bit messy but I'm still working on it). I have a variable nr[100] that registers the number of coins (also created some conditions in my read_values() ). I don't know where can I use it in my code.
The code considers that we have an INFINITE supply of coins (which I don't want that).
It is made in the bottom-up method (dynamic programming).
My code is inspired from this video: Youtube
#include <iostream>
using namespace std;
int C[100], b[100], n, S, s[100], nr[100], i, condition=0, ok=1;
void read_values() //reads input
{
cin >> n; // coin types
cin >> S; // amount to change
for (i=1; i<=n; i++)
{
cin >> b[i]; //coin value
cin>>nr[i]; //coin amount
if(nr[i]==0)b[i]=0; //if there are no coin amount then the coin is ignored
condition+=b[i]*nr[i]; //tests to see if we have enough coins / amount of coins to create a solution
if(b[i]>S)
{
b[i]=0;
}
}
if(S>condition)
{
cout<<endl;
cout<<"Impossible!";
ok=0;
}
}
void payS()
{
int i, j;
C[0] = 0; // if amount to change is 0 then the solution is 0
for (j=1; j<=S; j++)
{
C[j] = S+1;
for (i=1; i<=n; i++)
{
if (b[i] <= j && 1 + C[j - b[i]] < C[j])
{
C[j] = 1 + C[j - b[i]];
s[j] = b[i];
}
}
}
cout << "Minimum ways to pay the amount: " << C[S] << endl;
}
void solution(int j)
{
if (j > 0)
{
solution(j - s[j]);
cout << s[j] << " ";
}
}
int main()
{
read_values();
if(ok!=0)
{
payS();
cout << "The coins that have been used are: ";
solution(S);
}
}
I'm working under the assumption that you need to generate change for a positive integer value, amount using your nbr table where nbr[n] is the number of coins available of value n. I'm also working under the assumption that nbr[0] is effectively meaningless since it would only represent coins of no value.
Most dynamic programming problems are typically recursing on a binary decision of choosing option A vs option B. Often times one option is "pick this one" and other is "don't pick this one and use the rest of the available set". This problem is really no different.
First, let's solve the recursive dynamic problem without a cache.
I'm going to replace your nbr variable with a data structure called a "cointable". This is used to keep track of both the available set of coins and the set of coins selected for any given solution path:
struct cointable
{
static const int MAX_COIN_VALUE = 100;
int table[MAX_COIN_VALUE+1]; // table[n] maps "coin of value n" to "number of coins availble at amount n"
int number; // number of coins in table
};
cointable::table is effectively the same thing as your nbr array. coinbase::number is the summation of the values in table. It's not used to keep track of available coins, but it is used to keep track of the better solution.
Now we can introduce the recursive solution without a lookup cache.
Each step of the recursion does this:
Look for the highest valuable coin that is in the set of available coins not greater than the target amount being solved for
Recurse on option A: Pick this coin selected from step 1. Now solve (recursively) for the reduced amount using the reduced set of available coins.
Recurse on option B: Don't pick this coin, but instead recurse with the first coin of lesser value than what was found in step 1.
Compare the recursion results of 2 and 3. Pick the one with lesser number of coins used
Here's the code - without using an optimal lookup cache
bool generateChange(int amount, cointable& available, cointable& solution, int maxindex)
{
if ((maxindex == 0) || (amount < 0))
{
return false;
}
if (amount == 0)
{
return true;
}
int bestcoin = 0;
// find the highest available coin that not greater than amount
if (maxindex > amount)
{
maxindex = amount;
}
// assert(maxindex <= cointable::MAX_COIN_VALUE)
for (int i = maxindex; i >= 1; i--)
{
if (available.table[i] > 0)
{
bestcoin = i;
break;
}
}
if (bestcoin == 0)
{
return false; // out of coins
}
// go down two paths - one with picking this coin. Another not picking it
// option 1
// pick this coin (clone available and result)
cointable a1 = available;
cointable r1 = solution;
a1.table[bestcoin]--;
r1.table[bestcoin]++;
r1.number++;
bool result1 = generateChange(amount - bestcoin, a1, r1, bestcoin);
// option2 - don't pick this coin and start looking for solutions with lesser
// coins (not the use of references for a2 and r2 since we haven't changed anything)
cointable& a2 = available;
cointable& r2 = solution;
bool result2 = generateChange(amount, a2, r2, bestcoin - 1);
bool isSolvable = result1 || result2;
if (!isSolvable)
{
return false;
}
// note: solution and r2 are the same object, no need to reassign solution=r2
if (
((result1 && result2) && (r1.number < r2.number))
|| (result2 == false)
)
{
solution = r1;
}
return true;
}
And then a quick demonstration for how to calculate change for 128 cents given a limited amount of coins in the larger denominations: {1:100, 5:20, 10:10, 25:1, 50:1}
int main()
{
cointable available = {}; // zero-init
cointable solution = {}; // zero-init
available.table[1] = 100;
available.table[5] = 20;
available.table[10] = 10;
available.table[25] = 1;
available.table[50] = 1;
int amount = 128;
bool result = generateChange(amount, available, solution, cointable::MAX_COIN_VALUE);
if (result == true)
{
for (int i = 1; i < 100; i++)
{
if (solution.table[i] > 0)
{
std::cout << i << " : " << solution.table[i] << "\n";
}
}
}
else
{
cout << "no solution\n";
}
}
And that should work. And it might be fast enough for most making change for anything under a dollar such that a cache is not warranted. So it's possible we can stop right here and be done.
And I am going to stop right here
I started to work on a solution that introduces a "cache" to avoid redundant recursions. But after benchmarking it and studying how the algorithm finds the best solution quickly, I'm not so sure a cache is warranted. My initial attempt to insert a cache table for both solvable and unsolvable solutions just made the code slower. I'll need to study how to make it work - if it's even warranted at all.
Maybe you wanted us to fix your code, but instead I implemented my own version of solution. Hopefully my own version will be useful somehow for you, at least educationally.
Of course I used Dynamic Programming approach for that.
I keep a vector of possible to compose changes. Each next sums is composed of previous sums by adding several coins of same value.
History of used coins is also kept, this allows us to restore each change as combination of exactly given coins.
After code you can see console output that shows example of composing change 13 out of coins 2x4, 3x3, 5x2, 10x1 (here second number is amount of coins).
Input coins and their amount is given inside coins vector at start of main() function, you can fill this vector with anything you want, for example by taking console user input. Needed to be represented change is given inside variable change.
Don't forget to see Post Scriptum (PS.) after code and console output, it has some more details about algorithm.
Full code below:
Try it online!
#include <cstdint>
#include <vector>
#include <unordered_map>
#include <set>
#include <algorithm>
#include <functional>
#include <iostream>
using u32 = uint32_t;
using u64 = uint64_t;
int main() {
std::vector<std::pair<u32, u32>> const coins =
{{2, 4}, {3, 3}, {5, 2}, {10, 1}};
u32 const change = 13;
std::vector<std::unordered_map<u32, std::pair<u64, std::set<u32>>>>
sums = {{{0, {1, {}}}}};
for (auto [coin_val, coin_cnt]: coins) {
sums.push_back({});
for (auto const & [k, v]: sums.at(sums.size() - 2))
for (size_t icnt = 0; icnt <= coin_cnt; ++icnt) {
auto & [vars, prevs] = sums.back()[k + coin_val * icnt];
vars += v.first;
prevs.insert(icnt);
}
}
std::vector<std::pair<u32, u32>> path;
std::vector<std::vector<std::pair<u32, u32>>> paths;
std::function<bool(u32, u32, u32)> Paths =
[&](u32 sum, u32 depth, u32 limit){
if (sum == 0) {
paths.push_back(path);
std::reverse(paths.back().begin(), paths.back().end());
return paths.size() < limit;
}
auto const coin = coins.at(depth - 1).first;
auto const & [_, prevs] = sums.at(depth).at(sum);
for (auto const cnt: prevs) {
if (cnt > 0)
path.push_back({coin, cnt});
if (!Paths(sum - coin * cnt, depth - 1, limit))
return false;
if (cnt > 0)
path.pop_back();
}
return true;
};
if (!sums.back().count(change)) {
std::cout << "Change " << change
<< " can NOT be represented." << std::endl;
return 0;
}
std::cout << "Change " << change << " can be composed "
<< std::get<0>(sums.back().at(change)) << " different ways." << std::endl;
Paths(change, coins.size(), 20);
std::cout << "First " << paths.size() << " variants:" << std::endl;
for (auto const & path: paths) {
std::cout << change << " = ";
for (auto [coin, cnt]: path)
std::cout << coin << "x" << cnt << " + ";
std::cout << std::endl;
}
}
Output:
Change 13 can be composed 5 different ways.
First 5 variants:
13 = 2x2 + 3x3 +
13 = 2x4 + 5x1 +
13 = 2x1 + 3x2 + 5x1 +
13 = 3x1 + 5x2 +
13 = 3x1 + 10x1 +
PS. As you may have noticed, main Dynamic Programming part of algorithm is very tiny, just following lines:
std::vector<std::unordered_map<u32, std::pair<u64, std::set<u32>>>>
sums = {{{0, {1, {}}}}};
for (auto [coin_val, coin_cnt]: coins) {
sums.push_back({});
for (auto const & [k, v]: sums.at(sums.size() - 2))
for (size_t icnt = 0; icnt <= coin_cnt; ++icnt) {
auto & [vars, prevs] = sums.back()[k + coin_val * icnt];
vars += v.first;
prevs.insert(icnt);
}
}
This part keeps all currently composable sums (changes). Algo starts from money change of 0, then incrementally adds 1-by-1 coin to all possible current changes (sums), thus forming new sums (including this new coin).
Each sum keeps a counter of all possible ways to compose it plus it keeps track of all last coins that lead to this sum. This last coins set allows to do back-tracking in order to restore concrete combinations of coins, not just amount of ways to compute this sum.

Moving a number in a zig zag pattern through a 2D array C++

#include <iostream>
#include <ctime>
#include <cstdlib>
using namespace std;
//function protypes
void gameBoard(); //prints board
void diceRoll();
void move();
//global variables
int roll;
int playerOne = 0;
int playerTwo = 99;
int board[5][8] = {{33,34,35,36,37,38,39,40},
{32,31,30,29,28,27,26,25},
{17,18,19,20,21,22,23,24},
{16,15,14,13,12,11,10, 9},
{ 1, 2, 3, 4, 5, 6, 7, 8}};
void diceRoll() { //asks player to input a charater to roll a dice
srand(time(0));
roll = ((rand() % 6) + 1); //sets num to a random number between 1 and 6
}
void gameBoard(){ // prints game board
for (int i = 0; i < 5; ++i){
for (int j = 0; j < 8; ++j){
cout << board[i][j] << " ";
if (board[i][j] <= 8){
cout << " ";
}
}
cout << endl << endl;
}
}
void move(int player, int& colPos, int& rowPos){
int tempCol;
int tempRow;
int previous;
for (int i = 0; i <= 2; i++){
if(i % 2 == 1){
tempCol = colPos + roll;
colPos = tempCol;
tempCol = colPos - roll;
if(colPos > 7){
colPos = 7;
rowPos--;
}
board[rowPos][colPos] = player;
}
}
}
int main() {
int turn = 1;
int colPos1 = 0;
int rowPos2 = 4;
int colPos1 = 0;
int rowPos2 = 0;
while(winner == false){
if(turn == 1){
turn = 2; //allows to switch back and forth bewteen turns
diceRoll(player1); //rolls die
move(playerOne, colPos1, rowPos2);
gameBoard(); //prints game board
}else{
turn = 1; //allows to switch back and forth bewteen turns
diceRoll(player2); //rolls die
move(playerTwo, colPos2, rowPos2);
gameBoard(); //prints game board
}
}
return 0;
}
So the code above is for a chutes and ladders game. The program above should run without any errors. I am almost done with this code but am having issues with the move function. Here I am trying to move through a 2D array (the gameboard) as each player rolls a die. There are two issues with the current code. One after a player moves a space the space that they have previously left remains marked with the player. In addition, once it goes across through an entire row it does not advance to the next row. Thanks for your help in advance.
Note: I deleted a lot of code to make it more relevant to the question so it may have errors now.
"After a player moves a space the space that they have previously left remains marked with the player"
In move(int player, int& colPos, int& rowPos)you already know which player you are updating and the current row and column position so you can use this as an opportunity to remove the mark on the current board piece and set it back to its default value e.g. once you step into move call something like void ResetBoardPositition(int colPos, int rowPos) and then update the new position with the player marker (alternatively you could not change the game board at all and just print the player position at the draw stage since you store their positions anyway)
"Once it goes across through an entire row it does not advance to the next row"
Your code isn't doing anything to take account of the fact you need to reverse direction every time you change rows. Each time you go past column 7 you just set the column to 7 and reduce the row. This means that your next roll will immediately take you past the column threshold again, set it to 7 and reduce the row count. You'll need to use some form of direction modifier for the roll so you have something like:
int direction = 1;
if (rowPos % 2 == 1)
direction = -1;
tempCol = colPos + (roll * direction);
colPos = tempCol;
tempCol = colPos - (roll * direction);
Your check for the colPos would need to account for this too
if (colPos > 7) {
colPos = 7;
rowPos--;
} else if (colPos < 0) {
colPos = 0;
rowPos--;
}
There is also the issue that you do not account for the full roll here i.e. you set the column to the flat value meaning if you were on column 7, roll a 3 (which should put you at column 5 on the next row) you only move 1 space to the next row and stop there.
Hope these answer your main questions but I think there are some other logic issues going on here with your move code.
To answer your questions, the reason the previous spaces still have that player's name in it is because that value is never reset after the player leaves. What you could do is create an int for the current position before the player gets there, move the player to the new position, and then set the old position to the int you created. I saw that your move function has an int previous, so just use that.
Second, when I ran it, the player would go through the first row, then just move up the last column. From what I saw, your move function only manipulates tempCol and colPos, but not tempRow or rowPos. Moving those will help the player move between the rows.
Also, on a side note, I saw a few things for you to look out for. You declared your function as
void diceRoll();
But when you defined it, you wrote it like this:
void diceRoll(string name) { //asks player to input a charater to roll a dice
I would suggest rather adding in your parameters when you declare it or just declare it and define it all at once.
Second, you probably haven't messed with it much, but the checkWinner() function doesn't change the value of winner.
if (pos1 || pos2 != board[0][7]) {
winner == false;
}
else {
winner == true;
}
Using == is for checking the value, not for assigning, so take out one of them.

Indicating ships are shot down in game in C++

I am writing the battleship game in C++. I have set the values as below.
//const int empty = 0; // contains water
//const int occupied = 1; // contains a ship
//const int missed = 2; // shot into ocean W
//const int shot = 3; // ship is shot down H
//const int shipdown = 4; // whole ship is shot down S
When the user hit the ship, the value is changed from 1 to 3. The problem i face is how do I indicate that the whole ship is down.
int Grid[64];
int currentSquareValue = Grid[(row-1)*8+(column-1)];
switch(currentSquareValue)
{
case 0:
Grid[(row-1)*8+(column-1)] = 2;
break;
case 1:
Grid[(row-1)*8+(column-1)] = 3;
break;
default:
break;
}
//Printing out the Grid
for(int i = 0 ; i <=8; i++)
{
//Print Row Header
if(i != 0) cout << i << char(179);
for(int j = 0; j <= 8; j++)
{
if(i == 0)
{
//Print Column Header
cout << j << char(179);
}
else
{
//Avoid the first row and column header
if(j > 0 && i > 0)
{
int currentSquareValue = Grid[(i-1)*8+(j-1)];
switch(currentSquareValue)
{
case 0:
cout << " " << char(179);
break;
case 1:
cout << " " << char(179);
break;
case 2:
cout << "W" << char(179);
break;
case 3:
cout << "H" << char(179);
break;
case 4:
cout << "S" << char(179);
break;
default:
break;
}
}
}
}
I have already done the ship being hit, but I am not sure how to show that the whole ship is shot down after the third shot like in this:
Need some guidance on this... Not sure how to start on this..
A simple solution, not necessarily the best. Make your grid of a struct instead of an int. This struct contains a flag for whether a ship is present, the ID of a ship there, (this lets you tell them apart; if no ship is present the value shouldn't be used) and a separate flag for whether the cell has been hit. Now make an array for each ship ID in your game, which contains the number of cells that make up your ship. So [0] -> 3, means ship ID 0 takes up 3 squares. Whenever a new hit is registered against a cell containing this ship ID, decrease the value in the array. When it is 0, you know the whole ship has been hit.
You need to either store the coordinates of the ships in another data structure (so you can find the ship from a hit, and then mark all its squares as sunk) or make your matrix store more complex data (a ship ID) so you can mark all cases for that ship as sunk.
The latter would give you data such as:
const unsigned int empty = 0x0000;
const unsigned int shipIdMask = 0x00FF;
const unsigned int hitFlag = 0x0100;
const unsigned int sunkFlag = 0x0200;
Display-wise, you just do a if((value & shipIdMask) != 0) to check if there is a ship in there, and you check hits likewise. When a ship is hit, you can go the lazy way and simply sweep the entire matrix for squares with the same ship ID. If all of them are hit, you sweep them again and mark them all as sunk.
Both techniques can be combined if you don't want to sweep the whole matrix each time (use the ship ID to get the ship's actual coordinates in an array).
Maybe you could consider a bitboard representation of the board. Typically I've seen it used for chess, but since your board has 64 squares it seems appropriate here as well. The basic idea is that each location on the grid is represented by exactly one bit in a 64 bit int. Then operations can be performed quickly and easily via bit manipulation. In that type of representation, you would determine if a ship is sunk via something like this:
bool is_sunk(uint64_t board, uint64_t ship) {
return board & ship == ship;
}
And other operations, are equally easy.
For example, is a ship hit?
bool is_hit(uint64_t board, uint64_t ship) {
return board & ship != 0;
}
Did I win the game?
bool is_won(uint64_t board, uint64_t* ships, int size) {
uint64_6 opponents_ships = 0;
for (int i = 0; i < size; i++) opponents_ships |= *ships;
return is_sunk(board, opponents_ships);
}
Apply a move to the board:
bool make_move(uint64_t& board, uint64_t move) {
board &= move;
}
I suggest you make your grid cells have information, such as a pointer to the ship at that location.
Another idea is to have a container of ships and each ship will contain the coordinates of each of it's cells (locations). The ship would contain the status of those cells (visible, hit, sunk, etc.).