Odd behavior from character array - c++

I am trying to write a helper program for Wordle which will display all of the letters which have not been eliminated and the letters which have been confirmed. I've broken the program into functions, and I'm having an issue with the confirmed_letters() function.
I have this array declared in the global scope:
char* word[NUM_LETTERS];
and it's initialized like this:
for (int i = 0; i < NUM_LETTERS; i++) word[i] = new char(char(42));
Here's the function:
void confirmed_letters() {
int num_let = 0; // the # of confirmed letters
int temp_num; // holds the position of the confirmed letter
char temp_letter;
std::cout << "\n\nHow many letters have you confirmed?\n" <<
"Enter only the letters whose positions are known. ";
std::cin >> num_let;
std::cin.ignore(1000, '\n');
if (num_let == 0) return;
std::cout << "Enter the integer position of the confirmed letter, " <<
"followed by the letter. Press the enter (return) key between each entry.\n";
for (int i = 0; i < num_let; i++) {
//temp_num = -1; // I don't think this is needed
std::cin >> temp_num;
std::cin.ignore(1000, '\n');
if (temp_num > 5 || temp_num < 1) {
std::cout << "Invalid letter position. Enter an integer between 1 and 5.\n";
i--;
goto end;
}
std::cin >> temp_letter;
std::cin.ignore(1000, '\n');
word[temp_num - 1] = &temp_letter;
end:
display_word();
}
return;
}
display_word() is just a function to display the word.
Here's the output I got:
How many letters have you confirmed?
Enter only the letters whose positions are known. 3
Enter the integer position of the confirmed letter, followed by the letter. Press the enter (return) key between each entry.
1
o
o * * * *
2
i
i i * * *
3
e
e e e * *
So, for some reason, each additional letter is modifying the previous letters. I don't understand what is going on here. Each time the value of one of the elements of word[] is modified, it should only modify one element, but it's modifying all of them. Thoughts?

word[temp_num - 1] = &temp_letter; stores the address of a local variable that will be reused on the next loop iteration and hold whatever new value the user inputs. The old value will be lost, so it'll look like all of the used slots store the same letter. Because they do. Worse, the variable goes out of scope at the end of the function and will no longer be valid. This is dangerous as the pointer lives on and might even give what looks to be correct results.
Solution: Don't store pointers to letters in word. Store the letters themselves.
char word[NUM_LETTERS];
Initialize like this:
for (int i = 0; i < NUM_LETTERS; i++) word[i] = '*';
// use the character not the code. It's easier to understand the intent of
// * than the number 42 and not all systems will use 42 for *.
and store
word[temp_num - 1] = temp_letter;

Related

What do I put for the target 't' variable in the search_array function call? [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 2 months ago.
Improve this question
This program is a secret 5-digit code guessing game. Each digit of the code is between 0 and 9, and digits cannot repeat in the code. After entering a digit one at a time, the player receives feedback for each digit in the form of a symbol: *, +, or -.
Each symbol has a different meaning:
means the digit is in the code and it is in the correct position
means the digit is in the code but it is not in the correct position
means the digit is not in the code
I am having trouble with the search_array function, specifically what value should be in the function call for the parameter 't'.
Secondly, I can't get the second do-while loop to work correctly, I figured that these problems went hand-in-hand with each other.
This code is from a past assignment and the directions stated that we couldn't change any of the functions given to us, but that we could add variables and addtional functions as needed. I would like to adhere to these rules as much as possible.
I have tried multiple things to solve this issue, such as putting in the number of digits (5) and trying to put an array of all the numbers in the five digit code (which wouldn't work because t is an int). I put 'i' in there as a placeholder until I could find a solution.
Here is my current code:
#include <iostream>
using namespace std;
void game_instructions(int n);
bool contains_duplicates(int a[], int n);
bool search_array(int a[], int n, int t);
int main()
{
//Step 1: Declare your variables and constants
const int SIZE = 5; //this constant stores the length/size of the code
int randcode[SIZE]; //this array stores the code generated randomly by the computer
int guess[SIZE];
const int SIZE2 = 5;
int target[SIZE2]{ 1, 7, 4, 0, 9 };
//this array stores the code inputted by the player/user
//you may add more variables as needed
//Step 2: Output game instructions by calling function game_instructions
game_instructions(SIZE);
//Step 3: Generate a random code and store it in the array randcode one digit at a time.
//Each digit should be between 0 and 9 and should be stored as one array element
//Recall that rand() % 10 can be used to generate a number between 0 and 9
do
{
for (int i = 0; i < SIZE; i++)
{
randcode[i] = rand() % 10;
cout << randcode[i];
}
//Step 4: Repeat step 3 if the array randcode contains duplicates
//You must use function contains_duplicates to implement this step
} while (contains_duplicates(randcode, SIZE));
do
{
//Step 5: Ask the user for his guess and store it in the array guess.
//Read one digit at a time, validate it to make sure it is between 0 and 9, and store it as one array element
cout << "\nEnter your guess for the 5 digit code(one digit at a time) :\n";
for (int g = 0; g < SIZE; g++)
{
cout << "Enter digit " << (g + 1) << ":";
cin >> guess[g];
while (guess[g] < 0 || guess[g] > 9)
{
cout << "Your guess must be between 0 and 9.\n";
cout << "Please re-enter: ";
cin >> guess[g];
}
}
//Step 6: Output the array guess on a single line with a space after each element (see the sample output provided)
for (int g = 0; g < SIZE; g++)
{
cout << guess[g] << " ";
}
cout << "\n";
//Step 7: Compare the randomly generated code (randcode) with the user's guess (guess)
//and display feedback for each digit as: *, + or –, as explained below:
//For each digit in the user's guess do the following:
// If it matches the digit from the random code (both value and position), output *
// Otherwise, if it appears anywhere in the random code, output + (use function search_array here)
// Otherwise, output -
for (int i = 0; i < SIZE; i++)
{
if (guess[i] == randcode[i])
cout << "* ";
else if (search_array(randcode, 5, i))
{
cout << "+ ";
}
else
cout << "- ";
}
} while (guess != randcode);
//Step 8: Repeat steps 5,6,7 until all digits have been guessed correctly
//Step 9: Output congratulations message
cout << "\nGood job! You guessed the code!";
}
void game_instructions(int n)
//This function outputs the game instructions.
//Its parameter n represents the length of the code.
{
cout << "A random " << n << " digit code has been generated. You have to guess it. \n";
cout << "For every digit you will receive feedback in the form of *, + or - \n";
cout << " * means the digit is in the code and it is in the correct position.\n";
cout << " + means the digit is in the code but it is not in the correct position.\n";
cout << " - means the digit is not in the code.\n";
}
bool search_array(int a[], int n, int t)
//This function searches the array a (of size n) for a target value t.
//If t is found in the array the function returns true; otherwise it returns false.
{
for (int i = 0; i < n; i++)
{
if (a[i] == t) return true;
}
return false;
}
bool contains_duplicates(int a[], int n)
//This function searches the array a (of size n) and returns true if the array contains duplicates; otherwise, it returns false.
{
for (int i = 0; i < n; i++)
{
//compare element a[i] with all the remaining elements from index i+1 to n
for (int j = i + 1; j < n; j++)
{
if (a[i] == a[j]) return true;
}
}
return false;
}
You can get a lot of insight just from adding some debug output to the program.
Also, compiling with warnings will tell you:
SIZE2 and target variables are unused, and, mainly,
the array comparison guess != randcode is deprecated since C++20. You are better off changing it for a memcmp.
The call search_array(randcode, 5, i) looks for the value i in the array randcode (of size 5). But what that block is trying to achieve is knowing if a given number input by the user is in randcode; and that given number is guess[i].
For each of the numbers input by the user:
First, you check if guess[i] == randcode[i], i.e., if the user guessed both the number and the position. In that case a '*' is printed.
Then, in case they didn't, you check if at least the number is in the code to be guessed. In that case a '+' is printed; otherwise, a '-'.
[Demo]

Counting occurences of same array value in C++

I recently have been building a program where:
A user is asked to enter a number that will represent the size of a character array.
Then they are asked whether they will want the program to fill the values automatically, or they could press M so they could enter the values manually. They may only enter a-zA-Z values, or they will see an error.
At the end of the program, I am required to count every duplicate value and display it, for example:
An array of 5 characters consists of A;A;A;F;G;
The output should be something like:
A - 3
F - 1
G - 1
I could do this easily, however, the teacher said I may not use an additional array, but I could make a good use of a few more variables and I also can't use a switch element. I'm totally lost and I can't find a solution. I've added the code down below. I have done everything, but the counting part.
#pragma hdrstop
#pragma argsused
#include <tchar.h>
#include <iostream.h>
#include <conio.h>
#include <math.h>
#include <stdio.h>
#include <time.h>
#include <ctype.h>
void main() {
int n, i = 0;
char masiva_izvele, array[100], masiva_burts;
cout << "Enter array size: ";
cin >> n;
clrscr();
cout << "You chose an array of size " << n << endl;
cout << "\nPress M to enter array values manually\nPress A so the program could do it for you.\n\n";
cout << "Your choice (M/A): ";
cin >> masiva_izvele;
if (masiva_izvele == 'M' || masiva_izvele == 'm') {
clrscr();
for (i = 0; i < n; i++) {
do {
cout << "Enter " << i + 1 << " array element: ";
flushall();
cin >> masiva_burts;
cout << endl << int(masiva_burts);
if (isalpha(masiva_burts)) {
clrscr();
array[i] = masiva_burts;
}
else {
clrscr();
cout << "Unacceptable value, please enter a value from the alphabet!\n\n";
}
}
while (!isalpha(masiva_burts));
}
}
else if (masiva_izvele == 'A' || masiva_izvele == 'a') {
clrscr();
for (i = 0; i < n; i++) {
array[i] = rand() % 25 + 65;
}
}
clrscr();
cout << "Masivs ir izveidots! \nArray size is " << n <<
"\nArray consists of following elements:\n\n";
for (i = 0; i < n; i++) {
cout << array[i] << "\t";
}
cout << "\n\nPress any key to view the amount of every element in array.";
//The whole thing I miss goes here, teacher said I would need two for loops but I can't seem to find the solution.
getch();
}
I would be very thankful for a solution so I could move on and forgive my C++ amateur-ness as I've picked this language up just a few days ago.
Thanks.
EDIT: Edited title to suit the actual problem, as suggested in comments.
One possible way is to sort the array, and then iterate over it counting the current letter. When the letter changes (for example from 'A' to 'F' as in your example) print the letter and the count. Reset the counter and continue counting the next character.
The main loop should run foreach character in your string.
The secondary loop should run each time the main "passes by" to check if the current letter is in array. If it's there, then ++.
Add the array char chars[52] and count chars in this array. Then print out chars corresponding to the array, which count is more than 1.
std::unordered_map<char, int> chars;
...
char c = ...;
if ('A' <= c && c <= 'Z')
++chars[c];
else if ('a' <= c && c <= 'z')
++chars[c];
else
// unexpected char
...
for (const auto p : chars)
std::cout << p.first << ": " << p.second << " ";
Assuming upper and lower case letters are considered to be equal (otherwise, you need an array twice the size as the one proposed:
std::array<unsigned int, 26> counts; //!!!
// get number of characters to read
for(unsigned int i = 0; i < charactersToRead; ++i)
{
char c; // get a random value or read from console
// range check, calculate value in range [0; 25] from letter...
// now the trick: just do not store the value in an array,
// evaluate it d i r e c t l y instead:
++counts[c];
}
// no a d d i t i o n a l array so far...
char c = 'a';
for(auto n : counts)
{
if(n > 0) // this can happen now...
{
// output c and n appropriately!
}
++c; // only works on character sets without gaps in between [a; z]!
// special handling required if upper and lower case not considered equal!
}
Side note: (see CiaPan's comment to the question): If only true duplicates to be counted, must be if(n > 1) within last loop!

inputing char for a symmetric cipher to be used in a game

I am looking for a simple function that would allow an user (player) to assign letters of the alphabet to other letters. For example: "a = n" . Then placing theses letters in an array: alb[n,..]. Should I use pointers?
use std::vector
Place to start, think what the translate function would look like. That tells you the shape of the data you need. Initially hard code the translation table in a test program otherwise you will get distracted with worrying about talking to the user.
Once you understand the tx table you will need and how t store it then worry about inputting it.
I have the answer. Here some test code that uses the first three letters of the alphabet.
int i = 0, j = 0;
string alp[] = { "a", "b", "c" }, alpc[] = { "0", "1", "2" };
while (i <= 2) {
cout << "Enter the letter substitution: " << alp[i] << " = ";
cin >> alpc[i];
for (j = 0; j < i; ++j) {
while (alpc[j] == alpc[i]) {
cout << "Please enter a non-redundant letter substitution: ";
cin >> alpc[i];
}
}
++i;
}

using strings in C++ // how do i?

I need help changing my current program so that it displays the letters already entered by the user, and displays the letters again immediately before prompting the user to enter another letter. what I have so far is below.
int main()
{
char another = 'Y';
string message = "";
while (toupper(another) == 'Y')
{
cout << "Enter a message: ";
getline(cin, message);
for (int x = 0; x < message.length(); x += 1)
cout << message.substr(x) << endl;
cout << endl << "Another message (Y/N)? ";
cin >> another;
cin.ignore(100, '\n');
}
system("pause");
return 0;
}
If you want all the characters (you entered at any time) printed, you can do the following:
Start off with two empty strings. One is a buffer, that stores the string that's currently added and the other holds all of the strings already added, like so:
string buf = "";
string messages = "";
Read your characters into your buffer, via:
getline(cin, buf);
Append the string to the other messages, already entered:
messages.append(buf);
Append a string delimiter to your messages, so you know which sequence of characters (including whitespaces) belong to the message you entered:
messages.append(";");
(BTW: Using "-quotes here is really important, to let the compiler know you are comparing strings, not characters, as there is no string::append(char s)-method defined, only string::append(string s).)
Iterate through your messages-string, and check if the character at position x is equal to ';' (Using '-quotes here is also important, because string::operator[] return a character not a string!!!). For instance you might code:
for (int x = 0; x < messages.length(); x++) {
//Test if string delimiter is reached, if so, jump to next line.
if(messages[x] == ';') {
std::cout << "\n";
}
//Else just print the string:
else {
std::cout << messages[x];
}
}
Test if another message should be entered.
OR you create a linked list of strings. Adding new strings to the list, every time you enter a new string. This might be the more elegant way to do this, however it is slightly more involved. (I'm assuming you're fairly new to programming, if not I apologize!). Check Wikipedia or cplusplus.com for more info on linked lists!
Hope I could answer your question,
lindebear

Integer input restricted to four digits only

I'm doing a problem where it asks to input an account number, which consists only of four digits. This has to be accomplished with basic beginner C++.
I need to figure out a way to restrict the input of the integer to four digits. A user should be able to put in 0043 or 9023 or 0001 and it should be an acceptable value....
I think I know how to accomplish it with a string.... getline(cin,input) and then check if input.length()==4?
But I've no idea how I would even do this with an integer input.
Note that if 0043 is intended to be distinct from 43, then the input is not in fact a number, but a digit string, just like a telephone "number".
Read the line as a string input.
Check that the length of input is 4.
Check that each character in the string is <= '9' and >= '0'.
Something like:
std::string read4DigitStringFromConsole()
{
bool ok = false;
std::string result;
while (!ok)
{
std::cin >> result;
if (result.length() == 4)
{
bool allDigits = true;
for(unsigned index = 0; index < 4; ++index)
{
allDigits = allDigits && (
(result[index] >= '0') &&
(result[index] <='9')
);
}
ok = allDigits;
}
}
return result;
}
Something like this should work. Once the user enters something with exactly four characters you can validate it. The rest of the logic is up to you.
#include <iostream>
#include <string>
int main() {
std::cout << "Enter a PIN Number: ";
std::string pinStr;
while(std::getline(std::cin,pinStr) && pinStr.size() != 4) {
std::cout << "Please enter a valid value\n";
}
}
Should you want to store it in an integer form, holding the integers in an std::vector might be beneficial. You can do this easily (loop unrolling was for clarity):
#include <iostream>
#include <string>
#include <vector>
int main() {
std::cout << "Enter a PIN Number: ";
std::string pinStr;
while(std::getline(std::cin,pinStr) && pinStr.size() != 4 ) {
std::cout << "Please enter a valid value\n";
}
std::vector<int> pin;
pin[0] = pinStr[0] - '0';
pin[1] = pinStr[1] - '0';
pin[2] = pinStr[2] - '0';
pin[3] = pinStr[3] - '0';
//pin now holds the integer value.
for(auto& i : pin)
std::cout << i << ' ';
}
You can see it running here
I like your idea to use a string as the input. This makes sense because an account "number" is simply an identifier. You don't use it in calculations. By if (sizeof(input)==4) I think you are trying to check the length of the string. The correct way to do this is if (input.length() == 4). This will check that the user inputs 4 characters. Now you need to make sure that each of the characters is also a digit. You can do this easily by taking advantage of the fact that the ASCII codes for digit characters are ordered as you expect. So if (input[i] >= '0' && input[i] <= '9') will do the trick with an appropriate for loop for the index i. Also, you probably need some kind of loop which continues to ask for input until the user enters something which is deemed to be correct.
Edit:
As an alternative to checking that each character is a digit, you can attempt to convert the string to an int with int value = atoi(input.c_str());. Then you can easily check if the int is a four-or-less-digit number.
// generic solution
int numDigits(int number)
{
int digits = 0;
if (number < 0) digits = 1; // remove this line if '-' counts as a digit
while (number) {
number /= 10;
digits++;
}
return digits;
}
similar to this post.
Then you can call this function to check if the input is 4 digits.
You probably want your code to be responsive to the user input, so I would suggest getting each character at a time instead of reading a string:
std::string fourDigits;
char currentDigit;
std::cout << "Enter 4 digits\n";
for(int i = 0; i < 4; ++i)
{
currentDigit = getch();
if(isdigit(currentDigit))
{
fourDigits += currentDigit;
std::cout << currentDigit; // getch won't display the input, if it was a PIN you could simply std::cout << "*";
}
else
{
// Here we reset the whole thing and let the user know he entered an invalid value
i = 0;
fourDigits = "";
std::cout << "Please enter only numeric values, enter 4 digits\n";
}
}
std::cout << "\nThe four digits: " << fourDigits.c_str();
This way you can handle gracefully invalid character instantly. When using strings, the input will only be validated once the user hits Enter.
So I was going over how I can use an integer type to get the input, and looked at char... since it's technically the smallest integer type, it can be used to get the code... I was able to come up with this, but it's definitely not refined yet (and I'm not sure if it can be):
int main() {
int count=0;
while(!(count==4)){
char digit;
cin.get(digit);
count++;
}
return 0;
}
So, the loop keeps going until 4 characters are collected. Well, in theory it should. But it doesn't work. It'll stop at 2 digits, 5 digits, etc.... I think it could be the nature of cin.get() grabbing white space, not sure.