I'm trying to verify that what a user inputs in to one section of a union is actually an integer. I have been trying to use the isdigit function (see below) but I am having no success. All I need to do is make sure the user enters ONLY numbers for the date, but I am having a lot of trouble with it.
My code:
#define STRSIZE 30
#define PROFSIZE 30
#define NBRASSI 2
#define TRUE 1
#define FALSE 0
struct assignment
{
char name[STRSIZE];
char prof[PROFSIZE];
int duedate;
float value;
};
.
.
.
struct assignment populate_structure(struct assignment assi[], int assi_nbr)
{
int count;
int date_check = FALSE;
for (count = 0; count < assi_nbr; count++)
{
flushall();
printf("Enter Assignment #%d name (max %d chars):\n", count + 1,
STRSIZE);
gets(assi[count].name);
while (date_check == FALSE)
{
printf("Enter due date for Assignment #%d (YYYYMMDD):\n",
count + 1);
scanf("%d", &assi[count].duedate);
if (isdigit(assi[count].duedate))
{
date_check = TRUE;
}
else
{
printf("Invalid");
date_check = FALSE;
}
}
printf("Enter %% of final grade for Assignment #%d:\n", count + 1);
scanf("%f", &assi[count].value);
flushall();
printf("Enter Professor's name for Assignment #%d (max %d chars):\n",
count + 1, PROFSIZE);
gets(assi[count].prof);
printf("\n\n");
}
return assi[count];
}
It gives me no errors in Visual Studio, but when I run the program I get an Abort error whenever ANY value is input for assi[].duedate
If I remove if (isdigit(assi[count].duedate)), the program runs fine (so long as they only input an integer for duedate). Any help is greatly appreciated.
As the commenters already noted: isdigit() is for single characters only. Also: the way you use scanf() already assures the result to be a number.
But you should read a string instead, it is easier to test. For example
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
int main(void)
{
int c;
int position = 0;
int res;
char date[9] = {'\0'};
char input[20] = {'\0'};
puts("try if date is in the format \"YYYYMMDD\"");
res = scanf("%19s",input);
if(res != 1){
fprintf(stderr,"input was a mess, aborting\n");
goto __FAILURE;
}
c = input[position];
while(c != '\0'){
switch(position){
case 0:
if( c == '2' || c == '1' ){
date[position] = (char) c;
} else {
goto __FAILURE;
}
break;
case 1:
if( c == '9' || c == '0' ){
// check valid digit by looking back here
// (not implemented)
date[position] = (char) c;
} else {
goto __FAILURE;
}
break;
case 2:
if( isdigit(c) ){
date[position] = (char) c;
} else {
goto __FAILURE;
}
break;
case 3:
if( isdigit(c) ){
date[position] = (char) c;
} else {
goto __FAILURE;
}
break;
case 4: // month
if( c == '0' || c == '1' ){
date[position] = (char) c;
} else {
goto __FAILURE;
}
break;
case 5: // month
if( isdigit(c) ){
// check valid digit by looking back here
// (not implemented)
date[position] = (char) c;
} else {
goto __FAILURE;
}
break;
case 6: // day
if( c == '0' || c == '1' || c == '2' || c == '3'){
date[position] = (char) c;
} else {
goto __FAILURE;
}
break;
case 7: // day
if( isdigit(c)){
// check valid digit by looking back here
// (not implemented)
date[position] = (char) c;
} else {
goto __FAILURE;
}
break;
default:
break;
}
c = input[++position];
}
printf("Date was correct and is %s\n",date);
exit(EXIT_SUCCESS);
__FAILURE:
printf("Date was wrong at position %d with character %c or just too short/long\n",position,c);
exit(EXIT_FAILURE);
}
Fill out the rest (check if date is valid and in the correct range). You could also part the date into individual year/month/day sections instead of putting the string into another string and convert them into numbers via atoi ( you can use atoi `because you already made sure to have a valid number) or by converting manually inside the switch without extra variables and functions. Checking ranges arithmetically is simpler than comparing strings/characters.
Here is my offering - any invalid entry calls exit(1) though a better error handler would be preferred. The data is entered as a string, and then extracted and validated.
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
typedef struct {
int year;
int month;
int day;
} mydate_t;
int main(void){
int i;
mydate_t dat = {0};
int dayspermon[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
char str[12];
printf("Enter a date (YYYYMMDD): ");
fflush(stdout);
if(fgets(str, sizeof str, stdin) == NULL) {
exit(1);
}
for(i=0; i<8; i++) {
if(!isdigit(str[i])) {
exit(1); // also fails when the string is too short
}
}
// extract
for(i=0; i<4; i++) {
dat.year = dat.year * 10 + str[i] - '0';
}
for(i=4; i<6; i++) {
dat.month = dat.month * 10 + str[i] - '0';
}
for(i=6; i<8; i++) {
dat.day = dat.day * 10 + str[i] - '0';
}
// validate
if(dat.year < 2000 || dat.year > 3000) // arbitrary range
exit(1);
if(dat.year % 4 == 0 && (dat.year % 100 != 0 || dat.year % 400 == 0)) {
dayspermon[1]++; // leap year adjustment
}
if(dat.month < 1 || dat.month > 12)
exit(1);
if(dat.day < 1 || dat.day > dayspermon[dat.month - 1])
exit(1);
printf("Date: %04d %02d %02d\n", dat.year, dat.month, dat.day);
}
Related
I'm coding this pattern game, but I am having difficulties in storing the previous input of the users "uno" and "dos" to a array so it can be compared, if same it will ask for another input which has not been picked. Example of This
Round 1:
Player 1 Inputs : -> A
Player 2 Inputs : -> B
Valid
Round 2:
Player 1 Inputs : -> L
Player 2 Inputs : -> V
Valid
Round 3:
Player 1 Inputs : -> A (Invalid Already been used)
enter char again
That above is an example i am trying to achieve. I will post the code below. I would appreciate the help very much.
#include <stdio.h>
#include <string.h>
#define SWITCH(_g0,_g1) \
(_g0 << 8) | (_g1 << 0)
#define CASE(_g0,_g1) \
case SWITCH(_g0,_g1)
intgetval(const char *prompt)
{
char *cp;
char buf[100];
int val;
while (1) {
printf("%s: ",prompt);
fflush(stdout);
cp = fgets(buf,sizeof(buf),stdin);
// handle end of file
if (cp == NULL) {
val = -1;
break;
// get the first char on the line
val = buf[0];
if (val != '\n')
break;
}
return val;
}
int main ()
{
int i = 0;
int roundCount = 1;
int pos = 0;
int over = 0;
int f = 1;
char G[9];
char uno,dos;
printf("Game Start!\n");
do {
printf("Round %d!\n", roundCount++);
printf("Input selection upon prompt.\n");
printf("Player 1: ");
scanf(" %c", &uno );
printf("Player 2: ");
scanf(" %c", &dos);
if()
//printf("DEBUG: %2.2X %2.2X\n",G[0],G[1]);
switch (SWITCH(uno,dos)) {
CASE('L','V'):
CASE('V','S'):
CASE('S','P'):
CASE('P','R'):
CASE('R','L'):
CASE('R','S'):
CASE('P','V'):
CASE('S','L'):
CASE('V','R'):
CASE('L','P'):
f++;
pos--;
printf("Uno Wins! Pos[%d]\n\n", pos);
break;
CASE('R','P'):
CASE('L','R'):
CASE('R','V'):
CASE('P','S'):
CASE('P','L'):
CASE('S','R'):
CASE('S','V'):
CASE('L','S'):
CASE('V','P'):
CASE('V','L'):
f++;
pos++;
printf("Dos Wins Pos[%d]!\n\n", pos);
break;
CASE('R','R'):
CASE('P','P'):
CASE('S','S'):
CASE('L','L'):
CASE('V','V'):
f++;
pos = pos;
break;
}
if (pos == -3 || pos == 3) {
printf("Game over\n");
break;
}
if (f == 5 && pos != -3 && pos != 3) {
switch (SWITCH(uno,dos)) {
CASE('L','V'):
CASE('V','S'):
CASE('S','P'):
CASE('P','R'):
CASE('R','L'):
CASE('R','S'):
CASE('P','V'):
CASE('S','L'):
CASE('V','R'):
CASE('L','P'):
printf("Uno:Wins!\n");
break;
CASE('R','P'):
CASE('L','R'):
CASE('R','V'):
CASE('P','S'):
CASE('P','L'):
CASE('S','R'):
CASE('S','V'):
CASE('L','S'):
CASE('V','P'):
CASE('V','L'):
printf("Dos Win!\n");
break;
}
}
} while (f < 5);
return 0;
}
im trying to implement a for loop for this but it wouldn't work , i am not getting the result that i want, adding this code. I would appreciate it if you would rewrite the code with this loop or if there is any other way please tell me how.
for ( i = 0; i < sizeof(useduno); i++)
{
if (uno == useduno[i]) // where useduno is the array, where uno is to be stored, so it cannot be used again
{
printf("You already used the letter. Use another letter: ");
scanf(" %c", &uno);
}
}
So, I've been trying to validate CNIC because Pakistan requires male CNIC's to end in odd number and female CNIC's to end in even number.
CNIC is stored as a string in a structure containing information about a bank user.
The problem is when i apply the logic, it doesn't seem to work as intended and always prints the invalid CNIC prompt even when it is correctly input.
Here is the relevant code:
struct account//The structure used to store the records
{
int mm,dd,yyyy,Deposit;
long long int accountnum;
char name[50];
char address[50];
char Account[50];
char CNIC[50];
char Num[50];
char gender;
} s[100];
void ValidCNIC(int j) {
int i = 0, check = 0;
char ch;
printf("\n \tCNIC: ");
fgets(s[j].CNIC,sizeof(s[j].CNIC),stdin);
fflush(stdin);
while(i < strlen(s[j].CNIC)-1) {
ch = s[j].CNIC[i];
if(ch >= '0' && ch <= '9') {
i++;
}
else{
printf(" \tThe CNIC can contain numbers only!\n");
fordelay(1000000000);
ValidCNIC(j);
}
}
if (strnlen(s[j].CNIC,sizeof(s[j].CNIC)) < 14)
{
printf(" \tThe CNIC is too short\n \tPlease reenter\n");
fordelay(1000000000);
ValidCNIC(j);
}
else if (strnlen(s[j].CNIC,sizeof(s[j].CNIC)) > 14) {
printf(" \tThe CNIC is too long\n \tPlease reenter\n");
fordelay(1000000000);
ValidCNIC(j);
}
int len = strnlen(s[j].CNIC,sizeof(s[j].CNIC));
if((s[j].gender == 'm') && ((s[j].CNIC[len] !='1')||(s[j].CNIC[len] !='3')||(s[j].CNIC[len] !='5')||(s[j].CNIC[len] !='7')||(s[j].CNIC[len] !='9')))
{
printf("invalid CNIC, male CNIC must always end in an odd number");
ValidCNIC(j);
}
else if((s[j].gender == 'w') && ((s[j].CNIC[len] !='0')||(s[j].CNIC[len] !='2')||(s[j].CNIC[len] !='4')||(s[j].CNIC[len] !='6')||(s[j].CNIC[len] !='8')))
{
printf("Invalid CNIC, female CNIC must end in an even number");
ValidCNIC(j);
}
You ought not take input in your validation function; just validate. Something like:
#include <stdio.h>
#include <ctype.h>
int
isValidCNIC(const char *cnic, char gender)
{
size_t len = 0;
const char *s = cnic;
char c;
if( gender != 'm' && gender != 'f' ){
fprintf(stderr, "invalid gender: %c\n", gender);
return 0;
}
while( *cnic ){
len += 1;
if( ! isdigit(c = *cnic++) ){
fprintf(stderr, "%s: invalid character %c\n", s, c);
return 0;
}
}
if( len != 14 ){
fprintf(stderr, "%s: too %s\n", s, len < 14 ? "short" : "long");
return 0;
}
if( ((c - '0') % 2) != ( gender == 'm' ) ){
fprintf(stderr, "%s: invalid parity\n", s);
return 0;
}
return 1;
}
int
main(void)
{
struct test { char *cnic; int gender; } t[] = {
{ "34576271345678", 'f' },
{ "34576271345677", 'm' },
{ "34576271345678", 'm' },
{ "3457627134678", 'm' },
{ "345762713456788", 'm' },
{ "3457a271345788", 'k' },
{ NULL, 0 }
};
for( struct test *p = t; p->cnic; p++ ){
if( isValidCNIC( p->cnic, p->gender) ){
printf("valid: %s\n", p->cnic);
}
}
}
But note that strspn does most of the work that you are trying to do, and you could refactor to:
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int
isValidCNIC(const char *cnic, char gender)
{
char err[256] = "";
size_t len = strspn(cnic, "0123456789");
const char *s = cnic;
if( gender != 'm' && gender != 'f' ){
strcat(err, "invalid gender, ");
}
if( cnic[len] ){
strcat(err, "invalid character, ");
}
if( len != 14 ){
strcat(err, len < 14 ? "too short, " : " too long, ");
}
if( ((cnic[len - 1] - '0') % 2) != ( gender == 'm' ) ){
strcat(err, "invalid parity, ");
}
if( *err ){
err[strlen(err) - 2] = '\0';
fprintf(stderr, "%s, %c: %s\n", s, gender, err);
}
return !*err;
}
int
main(void)
{
struct test { char *cnic; int gender; } t[] = {
{ "34576271345678", 'f' },
{ "34576271345677", 'm' },
{ "34576271345678", 'm' },
{ "3457627134678", 'm' },
{ "345762713456788", 'm' },
{ "3457a271345788", 'k' },
{ NULL, 0 }
};
for( struct test *p = t; p->cnic; p++ ){
if( isValidCNIC( p->cnic, p->gender) ){
printf("valid: %s\n", p->cnic);
}
}
}
Recursion does not seem well suited to this task.
Consider a loop until the input is ok.
strspn can be used to test the input for digits. index will be the first character that is not a digit. Following fgets and all digits, that character should be a newline ( '\n').
Use modulus ( '% 2') to determine if the last digit is odd or even.
This is not exactly what is needed but should give ideas that can be adapted.
#include <stdio.h>
#include <string.h>
#define LENGTH 50
int main ( void) {
char CNIC[LENGTH] = "";
int ok = 0;
int gender = 'm';
while ( ! ok) {
printf ( "enter CNIC\n");
fgets ( CNIC, sizeof CNIC, stdin);
size_t index = strspn ( CNIC, "0123456789");
if ( 14 <= index && '\n' == CNIC[index]) {
int mod = CNIC[index - 1] % 2;
if ( 'm' == gender) {
if ( mod ) {//odd
ok = 1;
}
else {
printf ( "male CNIC must be odd\n");
}
}
if ( 'w' == gender) {
if ( ! mod ) {//even
ok = 1;
}
else {
printf ( "female CNIC must be odd\n");
}
}
}
else {
printf ( "enter digits only, at least 14. try again\n");
}
}
return 0;
}
Use '0' and '9' instead of 48 and 57.
Do not recursively call ValidCNIC from within itself. This can lead to infinite recursion
Add an (e.g.) outer that checks an error flag/value and loops if there is an error
Using s[j].CNIC[i] everywhere is cumbersome. Better to set (e.g.) char *cnic = s[j].CNIC; and use that.
No need to call strlen in the loop. Just call it once and save the value.
Your even/odd check can be simplified
Here's a refactored version. It is annotated. It compiles but I've not tested it:
#include <stdio.h>
#include <string.h>
//The structure used to store the records
struct account {
int mm, dd, yyyy, Deposit;
long long int accountnum;
char name[50];
char address[50];
char Account[50];
char CNIC[50];
char Num[50];
char gender;
} s[100];
#define ERRSET(_fmt) \
{ \
err = _fmt; \
break; \
}
void
ValidCNIC(int j)
{
int i;
char ch;
const char *err;
while (1) {
printf("\n \tCNIC: ");
struct account *acct = &s[j];
fgets(acct->CNIC, sizeof(acct->CNIC), stdin);
char *cnic = acct->CNIC;
// strip the newline
cnic[strcspn(cnic,"\n")] = 0;
size_t cnlen = strlen(cnic);
err = NULL;
do {
// ensure CNIC has only digits
for (i = 0; i < cnlen; ++i) {
ch = cnic[i];
if ((ch < '0') || (ch > '9'))
ERRSET(" \tThe CNIC can contain numbers only!\n");
}
if (err != NULL)
break;
// ensure CNIC length is correct
if (cnlen < 14)
ERRSET(" \tThe CNIC is too short\n \tPlease reenter\n");
if (cnlen > 14)
ERRSET(" \tThe CNIC is too long\n \tPlease reenter\n");
int isodd = cnic[cnlen - 1];
isodd -= '0';
isodd %= 2;
// ensure even/odd of CNIC matches gender
switch (acct->gender) {
case 'm':
if (! isodd)
ERRSET("invalid CNIC, male CNIC must always end in an odd number");
break;
case 'w':
if (isodd)
ERRSET("Invalid CNIC, female CNIC must end in an even number");
break;
}
} while (0);
// no errors
if (err == NULL)
break;
// show the error
printf("%s",err);
}
}
UPDATE:
It still doesn't seem to work either, keeps giving the same prompt of Invalid CNIC. – Bas_Anar
Oops, my bad ... I forgot to remove the newline after the fgets. I've edited the example code above.
Btw what does that preprocessor directive do? – Bas_Anar
The macro arg is _fmt. The macro just does:
err = _fmt;
break;
where it is used.
So, it sets an error string and breaks out of the containing loop.
At the bottom, if err is NULL, there is no error and we're done.
Otherwise, it prints the error string and the outer loop reprompts the user.
Here's an example of the expanded macro text:
// ensure CNIC length is correct
if (cnlen < 14) {
err = " \tThe CNIC is too short\n \tPlease reenter\n";
break;
}
i'm having real hard time with my code, and my due date is today. i'm given an error "main.c: In function ‘main’:
main.c:186:7: error: expected declaration or statement at end of input
}".
I've been trying for hours to play with the brackets and fix it but with no luck. i'm hoping you could help me fix it, as you are far more experienced than me.
it's basically a simple program that takes input from stdin(might be from keyboard or from a file using redirection) and applies the following:
1)puts a new line between sentences.
2)doesn't print numbers.
3)if inside a bracket, then letters must be capitalized(bold).
4)put's an uppercase on a first character of a sentence.
5)if not in a bracket nor in a beginning of a sentence, then it should make it a lowercase.
notes:
a)there's no limit on the length of the input, and each sentence can be written on several lines(the input).
b)if a dot (.) is inside a brackets, it doesn't make it a new sentence(no need to write a newline).
c)if two dots are given, then it's an empty sentence and should be written like the example.
basically, i just ask you to help me fix my code so it will run, as i've already done all of that(thought of i missed something and you can help me improve it - i will be very glad!)
example:
if given input:
i love to play hockey.. I NEED TO PLAY HOCKEY.. "hockey is life 3333".
the desired output will be:
I love to play hockey.
. I need to play hockey.
.
"HOCKEY IS LIFE"
the code in "temp.h" is:
#define go 0
#define endOfASentence 1
#define isAFirstQuotation 2
#define isASecondQuotation 3
#define inAQuotation 4
#define beginningOfSentence 5
#define middleOfSentence 6
the code in main program is:
#include <stdio.h>
#include <ctype.h>
#include "letters.h"
int checkIfLetter (char a); /*checking if char is a letter */
int checkIfnumber (char a); /*checking if char is a number */
int checkIfUpperCase (char a); /*checking if char is upper case */
int checkIfLowerCase (char a); /*checking if char is lower case */
int checkIfQuotation (char a); /*check if char is a quotation */
int checkIfDot (char a); /*check if char is a dot */
int
main (int argc, const char *argv[])
{
int status = go;
char a;
int beginning = 0; /*if beginning equals 0 it's the beginning of a sentence, if it's 1 then it's the middle of it */
int secondQuote = 0; /*if second quote equals 1, then it's inside of a quotation */
while ((a = getchar ()) != EOF)
{
switch (status)
{
case go:
if (a == '.')
{
status = endOfASentence;
}
else if (a == '"' && secondQuote == '0')
{
status = isAFirstQuotation;
}
else if (a == '"' && secondQuote == '1')
{
status = isASecondQuotation;
}
else if (checkIfLetter (a) == '1' && secondQuote == '1')
{
status = inAQuotation;
}
else if (checkIfnumber (a) == '1')
{
continue;
} /*a number should not be on the output, so we just ignore it and not using it */
else if (checkIfLetter (a) == '1' && beginning == '0')
{
status = beginningOfSentence;
} /*i tried to differentiate between beginning and middle of the sentence using int beginning */
else if (checkIfLetter (a) == '1' && beginning == '1')
{
status = middleOfSentence;
}
case beginningOfSentence:
if (checkIfQuotation (a) && checkIfDot (a)
&& checkIfnumber (a) != 1)
{
if (checkIfUpperCase (a) == '1')
{
printf ("%c", toupper (a));
beginning = 1;
status = go;
}
} break; /*set to upper and return to go */
case middleOfSentence:
if (checkIfQuotation (a) && checkIfDot (a)
&& checkIfnumber (a) != 1)
{
if (checkIfLowerCase (a) == '1')
{
printf ("%c", tolower (a));
status = go;
}
} break;
case endOfASentence:
if (checkIfDot (a) == '1')
{
printf ("%c/n", a);
beginning = 0;
status = go;
}break; /*i tried to append(add) a new line after the dot and to set beginning to 0, to signify that after it's a beginning of a sentence */
case isAFirstQuotation: /*if it's a quotation, continue to the next char and make it upper case as long as it's a lower case, until you get another quotation */
while (checkIfLowerCase (a) == '1')
{
secondQuote == '1';
status = go;
}break;
case isASecondQuotation:
if (checkIfQuotation (a) == '1' && secondQuote == '1')
{
secondQuote = 0;
status = go;
}break;
case inAQuotation:
if (secondQuote == '1' && checkIfLetter (a) == '1')
{
printf ("%c", toupper (a));
status = go;
} break;
}
}
return 0;
}
int checkIfLetter (char a)
{
if (isalpha (a))
{
return 1;
}
else
{
return 0;
}
}
int checkIfnumber (char a)
{
if (isdigit (a))
{
return 1;
}
else
{
return 0;
}
}
int checkIfUpperCase (char a)
{
if (checkIfLetter (a) == '1')
{
if (isupper (a))
{
return 1;
}
else
{
return 0;
}
}
}
int checkIfLowerCase (char a)
{
if (checkIfLetter (a) == '1')
{
if (islower (a))
{
return 1;
}
else
{
return 0;
}
}
}
int checkIfQuotation (char a)
{
if (a == '"')
{
return 1;
}
else
{
return 0;
}
}
int checkIfDot (char a)
{
if (a == '.')
{
return 1;
}
else
{
return 0;
}
}
i don't know how to fix it and i've spent hours on it. would be very grateful if you could help.
i've tried to be very elaborative and to abide the rules
You can try this to see if it produces the desired results.
Characters that are not letters, space, newline or dot are rejected at the top of the while and all letters are set to lower case.
Then the choice is to print one upper case letter at the start of the sentence or all upper case inside double quotes.
There are no breaks as oneUpper needs to fall through to allUpper. allUpper needs to fall through to default.
getchar returns an int so int a instead of char a
#include <stdio.h>
#include <ctype.h>
#define oneUpper 1
#define allUpper 2
int main (int argc, const char *argv[])
{
int status = oneUpper;
int a;
while ( EOF != (a = getchar ()))
{
//discard non letter except space, newline and .
if ( !isalpha ( a) && a != ' ' && a != '\"' && a != '.') {
continue;
}
//set all to lower and let oneUpper or allUpper do the rest.
a = tolower ( a);
switch (status)
{
case oneUpper:
if ( a == ' ' || a == '\n') {
putchar ( a);//print leading space and continue
continue;
}
case allUpper:
a = toupper ( a);
default:
putchar ( a);
if ( a == '\"') {
if ( status == allUpper) {
status = 0;//turn all upper off
}
else {
status = allUpper;//turn all upper on
}
}
if ( status == oneUpper) {
status = 0;//after printing one upper turn it off
}
if ( a == '.') {
if ( status != allUpper) {
putchar ( '\n');
status = oneUpper;//after a . turn one upper on
}
}
}
}
return 0;
}
I'm going through some c programming questions and I'm currently stuck on a pointer related question
Q: Write a function that takes the values of a two-card blackjack HAND as input, and returns the point total of the hand. The value
of the cards '2' through '9' is equal to their face value, the cards 'T', 'K', 'Q', 'J' are worth 10 points and the ace ('A') is worth 11 points
unless it comes with another ace, then that second ace is worth 1 point. The program should be able to catch incorrect input.
Examples:
Enter cards: A Q
The score is 21
Enter cards: A A
The score is 12
I've tackled this question before, but this time I'd have to use pointers which I'm still fairly new towards. Getting card values and calculating cards must be done in one function. Here's what i have so far:
#include <stdio.h>
#define HAND 2
struct player_hand
{
char card1;
char card2;
};
void getHandValue(struct player_hand * hnd_ptr, char size, char size2)
{
int first_card;
int second_card;
//get cards from user
scanf("%c %c",&hnd_ptr->card1, &hnd_ptr->card2);
printf("Enter Cards: %c %c", &hnd_ptr->card1, &hnd_ptr->card2);
//check value of first card in hand
if(hnd_ptr->card1<='9' && hnd_ptr->card1>='2')
{
first_card=(int)hnd_ptr->card1 -48;
}
//check for special cards: king, queen, jack, ten
else if(hnd_ptr->card1=='T'||hnd_ptr->card1=='K'||hnd_ptr->card1=='Q'||hnd_ptr->card1=='J')
{
first_card=10;
}
//if first card is Ace
else if(hnd_ptr->card1=='A')
{
first_card=11;
}
else
{
//card not valid
printf("Not a valid card: %c",hnd_ptr->card1);
return;
}
//check value of 2nd card
if(hnd_ptr->card2<='9' && hnd_ptr->card2>='2')
{
second_card=(int)hnd_ptr->card2 -48;
}
//if 2nd card is a special kind
else if(hnd_ptr->card2=='T'||hnd_ptr->card2=='K'||hnd_ptr->card2=='Q'||hnd_ptr->card2=='J')
{
second_card=10;
}
//if 2nd card is Ace
else if(hnd_ptr->card2=='A')
{
if(hnd_ptr->card1=='A')
second_card=1;
else
second_card=11;
}
else
{
//if 2nd card not valid
printf("Not a valid card: %c",hnd_ptr->card2);
return;
}
add cards
printf("\nThe total card value is: %d",first_card+second_card);
}
//call function, test if works
//calling it wrong?
int main(void)
{
struct player_hand hnd [HAND] = { {'A', 'A'}};
getHandValue (hnd, HAND);
return;
}
You have a few bugs.
Incorrect call in main.
The function doesn't need size arguments and if it did they should be int.
Bad return from main.
In the function, the printf is wrong.
Things are much more complicated than they need to be because the struct uses two scalars instead of an array.
I've created two versions of your program. One with bugs annotated. And another that cleans things up.
Here's the annotated version:
#include <stdio.h>
#define HAND 2
struct player_hand {
char card1;
char card2;
};
// NOTE/BUG: use 'int' for size and size2
void
getHandValue(struct player_hand *hnd_ptr, char size, char size2)
{
int first_card;
int second_card;
// get cards from user
scanf("%c %c", &hnd_ptr->card1, &hnd_ptr->card2);
// NOTE/BUG: this would print the _address_ of the values vs. the values
printf("Enter Cards: %c %c", &hnd_ptr->card1, &hnd_ptr->card2);
// NOTE/BUG [sort of]: the code below is cut-n-paste replication because you
// have separate card1 and card2 in the struct -- this "cries out" for an
// array and a loop. Consider the general case where you have 5 cards in the
// hand (e.g. five card charlie). The code would be easier even with an array
// of only two
// check value of first card in hand
if (hnd_ptr->card1 <= '9' && hnd_ptr->card1 >= '2') {
first_card = (int) hnd_ptr->card1 - 48;
}
// check for special cards: king, queen, jack, ten
else if (hnd_ptr->card1 == 'T' || hnd_ptr->card1 == 'K' || hnd_ptr->card1 == 'Q' || hnd_ptr->card1 == 'J') {
first_card = 10;
}
// if first card is Ace
else if (hnd_ptr->card1 == 'A') {
first_card = 11;
}
else {
// card not valid
printf("Not a valid card: %c", hnd_ptr->card1);
return;
}
// check value of 2nd card
if (hnd_ptr->card2 <= '9' && hnd_ptr->card2 >= '2') {
second_card = (int) hnd_ptr->card2 - 48;
}
// if 2nd card is a special kind
else if (hnd_ptr->card2 == 'T' || hnd_ptr->card2 == 'K' || hnd_ptr->card2 == 'Q' || hnd_ptr->card2 == 'J') {
second_card = 10;
}
// if 2nd card is Ace
else if (hnd_ptr->card2 == 'A') {
if (hnd_ptr->card1 == 'A')
second_card = 1;
else
second_card = 11;
}
else {
// if 2nd card not valid
printf("Not a valid card: %c", hnd_ptr->card2);
return;
}
printf("\nThe total card value is: %d", first_card + second_card);
}
//call function, test if works
//calling it wrong?
int
main(void)
{
// NOTE: based on usage, this is only an array because you're not using &hnd
// below
struct player_hand hnd[HAND] = {
{'A', 'A'}
};
// NOTE/BUG: too few arguments to function, but why pass count at all?
getHandValue(hnd, HAND);
// NOTE/BUG: need to return value (e.g. return 0)
return;
}
Here's the cleaned up version:
#include <stdio.h>
#define CARDS_PER_HAND 2
struct player_hand {
char card[CARDS_PER_HAND];
};
void
getHandValue(struct player_hand *hnd_ptr)
{
int idx;
int card;
int sum;
int count[CARDS_PER_HAND];
// get cards from user
printf("Enter Cards:");
fflush(stdout);
for (idx = 0; idx < CARDS_PER_HAND; ++idx)
scanf(" %c", &hnd_ptr->card[idx]);
// print cards
printf("Cards entered:");
for (idx = 0; idx < CARDS_PER_HAND; ++idx)
printf(" %c", hnd_ptr->card[idx]);
printf("\n");
for (idx = 0; idx < CARDS_PER_HAND; ++idx) {
card = hnd_ptr->card[idx];
// simple cards
if (card <= '9' && card >= '2') {
count[idx] = (card - '2') + 2;
continue;
}
switch (card) {
case 'A':
count[idx] = 11;
if ((idx == 1) && (count[0] == 11))
count[idx] = 1;
break;
case 'T':
case 'K':
case 'Q':
case 'J':
count[idx] = 10;
break;
default:
printf("Not a valid card: %c", card);
return;
break;
}
}
sum = 0;
for (idx = 0; idx < CARDS_PER_HAND; ++idx)
sum += count[idx];
printf("The total card value is: %d\n", sum);
}
int
main(void)
{
struct player_hand hnd;
getHandValue(&hnd);
return 0;
}
If, in addition to the other answers, your intent was to pass a 2-hand array, you would need to handle both hands within a loop in your scoring function. For example:
#include <stdio.h>
#define HAND 2
struct player_hand
{
char card1;
char card2;
};
void getHandValue (struct player_hand *hnd_ptr, int size)
{
int first_card;
int second_card;
/* get cards from user */
for (int i = 0; i < size; i++) {
printf ("\nenter cards for hand %d (card1 card2): ", i);
/* you must handle the '\n' that remains after last char */
if (scanf ("%c %c%*c", &hnd_ptr[i].card1, &hnd_ptr[i].card2) != 2) {
fprintf (stderr, "error: invalid entry.\n");
return;
}
printf ("you entered: %c %c\n", hnd_ptr[i].card1, hnd_ptr[i].card2);
}
for (int i = 0; i < size; i++)
{
/* check value of first card in hand */
if(hnd_ptr[i].card1 <= '9' && hnd_ptr[i].card1 >= '2')
{
first_card = (int)hnd_ptr[i].card1 - '0';
}
/* check for special cards: king, queen, jack, ten */
else if (hnd_ptr[i].card1 == 'T' || hnd_ptr[i].card1 == 'K' ||
hnd_ptr[i].card1 == 'Q' || hnd_ptr[i].card1 == 'J')
{
first_card = 10;
}
/* if first card is Ace */
else if (hnd_ptr[i].card1 == 'A')
{
first_card = 11;
}
else
{
/* card not valid */
printf("Not a valid card: %c",hnd_ptr[i].card1);
return;
}
/* check value of 2nd card */
if(hnd_ptr[i].card2 <= '9' && hnd_ptr[i].card2 >= '2')
{
second_card=(int)hnd_ptr[i].card2 - '0';
}
/* if 2nd card is a special kind */
else if (hnd_ptr[i].card2 == 'T' || hnd_ptr[i].card2 == 'K' ||
hnd_ptr[i].card2 == 'Q' || hnd_ptr[i].card2 == 'J')
{
second_card = 10;
}
/* if 2nd card is Ace */
else if (hnd_ptr[i].card2 == 'A')
{
if (hnd_ptr[i].card1 == 'A')
second_card = 1;
else
second_card = 11;
}
else
{
/* if 2nd card not valid */
printf ("Not a valid card: %c", hnd_ptr[i].card2);
return;
}
/* add cards */
printf ("\nThe total cards value (hand %d) is: %d\n",
i, first_card + second_card);
}
}
int main(void)
{
struct player_hand hnd[HAND] = { {'A', 'A'}, {'A', 'A'} };
getHandValue (hnd, HAND);
return 0;
}
Example Use/Output
$ ./bin/cards
enter cards for hand 0 (card1 card2): A A
you entered: A A
enter cards for hand 1 (card1 card2): 8 K
you entered: 8 K
The total cards value (hand 0) is: 12
The total cards value (hand 1) is: 18
If your intent was not to pass an array of struct, then obviously looping would not be necessary. Note: two loops were used. The first to get the cards for both hands, the second to compute the scores for both. (you could do it with one, but it looked as if you intended to have all cards entered before scoring) Look things over and let me know if you have further questions.
You're not passing the address of hnd to the function getHandValue(). To do this you have to pass the address using the & operator getHandValue(&hnd).
You're also not initializing struct player_hand hnd correctly. There's one set of {} too many.
Here's an edited version of your main() code that works, just some minor edits to how your pointer is set up.
// main
int main(void)
{
// minor edits to fix the code here
struct player_hand hnd = {'A', 'A'};
struct player_hand *hndPtr = &hnd;
getHandValue (hndPtr);
return 0;
}
First question: Can I send the type to a function?
Example if I write a generic function that works for any type.
And the second question:
I want to write a function to make sure that I read one of the following data types: int, float, double, long long.
Here's how I want it to work if I read an int:
If I enter "abcd" the function will print a message asking to enter a valid int number.
If I enter "123abc" the function will print a message asking to enter a valid int number.
The only valid cases are :
If I enter "123"
or
If I enter "123 456" in this case the function will read only the first number letting the second one in buffer.
Here is my code for this function:
int citesteNumar(char mesaj[])
{
char c;
int nr=0;
int semn = 1;
int cifre = 0;
bool ok = false;
while(1)
{
nr = 0;
while(1)
{
c = getchar();
if(c == ' ' && !ok)
continue;
if((((c < '0') || ('9' < c)) && (c != '-')) || (c == '\n'))
{
if(c != ' ')
fflush(stdin);
if((c != '\n') && (c != ' ') && ok)
ok = false;
break;
}
else if(c == '-')
{
if(!ok)
semn = -1;
else
{
fflush(stdin);
break;
}
}
else
{
nr = nr*10 + (c - '0');
ok = true;
cifre ++;
if(cifre == 10)
break;
}
}
if(!ok)
printf("%s",mesaj);
else return semn*nr;
}
return -1;
}
Can I write a generic function to read this types: int, float, double, long long ?
The first invalid case it can be solved using the value returned by scanf function but I don't know how to solve the second case (only with the method above).
And the function needs to be portable.
#include <stdio.h>
#include <stdlib.h>
#define PROC(type) \
if(*endp){\
printf("enter a valid " #type " number.\n");\
return NULL;\
}\
if(ret = malloc(sizeof(v)))\
*(type*)ret = v;\
/**/
void *GetNumber(const char *type){
void *ret = NULL;
char temp[32], *endp;
scanf("%31s", temp);
if(strcmp("int", type)==0){
int v = strtol(temp, &endp, 10);
PROC(int)
} else if(strcmp("long long", type)==0){
long long v = strtoll(temp, &endp, 10);
PROC(long long)
} else if(strcmp("float", type)==0){
float v = strtod(temp, &endp);
PROC(float)
} else if(strcmp("double", type)==0){
double v = strtod(temp, &endp);
PROC(double)
}
return ret;
}
int main(){
int *vi;
float *vf;
while((vi = GetNumber("int")) == NULL)
;
printf("%d\n", *vi);
free(vi);
if(vf = GetNumber("float"))
printf("%f\n", *vf);
free(vf);
return 0;
}
No you can't send the type, but you can send an enum :
typedef enum {
MY_NONE,
MY_INT,
MY_FLOAT,
MY_DOUBLE,
MY_LL
} Mytype;
In your function, use a switch on the enum. Plus, use sscanf on your buffer, with the corresponding format. You will get troubles to read an int with a blank inside; perhaps look at the locale, you might find solutions.