C# Code Verification Program - c#

Alright so I am making a program to verify a 4 digit code.
The computer generates a 4 digit code
The user types in a 4 digit code. Their guess.
the computer tells them how many digits are
guessed correctly in the correct place and how many digits have
been guessed correctly but in the wrong place.
The user gets 12 guesses to either win – guess the right code. Or
lose – run out of guesses.
So basically, my program doesn't seem to actually verify whether the code is correct but i cant see why not because i have if and for loops for verification, please take a look.
class Program
{
public static Random random = new Random();
static void Main(string[] args)
{
int DigitOne = random.Next(0, 10);
int DigitTwo = random.Next(0, 10);
int DigitThree = random.Next(0, 10);
int DigitFour = random.Next(0, 10);
byte[] code = new byte[4];
code[0] = Convert.ToByte(DigitOne);
code[1] = Convert.ToByte(DigitTwo);
code[2] = Convert.ToByte(DigitThree);
code[3] = Convert.ToByte(DigitFour);
bool CodeCorrect = false;
Console.WriteLine(code[0] +""+ code[1] +""+ code[2]+""+code [3] );
Console.WriteLine("You have 12 guesses before you will be permenantly locked out.\n");
int AmountOfGuesses = 0;
while (AmountOfGuesses < 12 && !CodeCorrect)
{
Console.WriteLine("Enter 4 digit code to unlock the safe: ");
int[] UserCode = new int[4];
for (int i = 0; i < 4; i++)
{
UserCode[i] = Convert.ToInt32(Console.Read()) - 48;
}
if (UserCode.Length != 4)
{
Console.WriteLine("Error. Try Again.\n");
}
else
{
int UserDigitOne = UserCode[0];
int UserDigitTwo = UserCode[1];
int UserDigitThree = UserCode[2];
int UserDigitFour = UserCode[3];
for (int i = 0; i < 4; i++)
{
if (UserCode[i] == code[i])
{
Console.WriteLine("The digit at position " + (i + 1) + " is correct.");
}
}
if (UserCode[0] == code[0] && UserCode[1] == code[1] && UserCode[2] == code[2] && UserCode[3] == code[3])
{
CodeCorrect = true;
Console.WriteLine("Code Correct. Safe unlocked.");
}
}
AmountOfGuesses++;
}
if (AmountOfGuesses > 12)
{
Console.WriteLine("Code Incorrect. Safe Locked permenantly.");
}
Console.ReadLine();
}

If you step through the code after it generated the number 1246, and then input the same number from the command line, convert it to a char array, then convert each char to a byte, you'll get the following four bytes:
49 50 52 54
These correspond to the ASCII representations of each char, NOT the actual numbers.
Try something like this:
int[] input = new int[4];
for(int i = 0; i < 4; i++ )
{
input[i] = Convert.ToInt32(Console.Read()) - 48;
}
The -48 should turn your ASCII code into the actual numerical representation that was provided. Console.Read() reads individual characters rather than the full line.
Also, you don't have to say:
CodeCorrect == false
This is more simply represented as:
!CodeCorrect
Similarly, if it was set to true, it would just be:
CodeCorrect
I also suggest using a for loop to set multiple elements in an array rather than manually writing out each line of code. It's not a big deal for small arrays, but it's good practice.
UPDATE: Here's a revised version of the full program:
class Program
{
public static Random random = new Random();
static void Main(string[] args)
{
int[] randCombination = new int[4];
for (int i = 0; i < 4; i++)
{
randCombination[i] = random.Next(0, 10);
Console.Write(randCombination[i].ToString());
}
bool CodeCorrect = false;
Console.WriteLine("\nYou have 12 guesses before you will be permenantly locked out.\n");
int AmountOfGuesses = 0;
while(AmountOfGuesses < 12 && !CodeCorrect)
{
Console.WriteLine("Enter 4 digit code to unlock the safe: ");
int[] UserCode = new int[4];
string input = Console.ReadLine();
int n;
bool isNumeric = int.TryParse(input, out n);
int correctCount = 0;
if(input.Length != 4 || !isNumeric)
{
Console.WriteLine("Error. Input code was not a 4 digit number.\n");
}
else
{
for(int i = 0; i < 4; i++)
{
UserCode[i] = Convert.ToInt32(input[i]) - 48;
if(UserCode[i] == randCombination[i])
{
Console.WriteLine("The digit at position " + (i + 1) + " is correct.");
correctCount++;
}
}
if(correctCount == 4)
{
CodeCorrect = true;
Console.WriteLine("Code Correct. Safe unlocked.");
}
}
AmountOfGuesses++;
}
if(AmountOfGuesses >= 12)
{
Console.WriteLine("Code Incorrect. Safe Locked permenantly.");
}
Console.ReadLine();
}
}
A couple of things were changed:
Added a for loop at the top that generates a random number, enters it into an array of ints and then prints it to standard output.
I changed the way user input is read back to the Console.ReadLine(). The reason for this is to check if the user inputted a four digit integer. the int.TryParse statement makes sure the input is an int, and the Length property checks the length.
I also used a counter to count each correct guess. If 4 correct digit guesses were made, the safe is unlocked.
Your final if statement would never have evaluated because Amount of Guesses would equal 12, not be greater than it. Changed it to >= from >. Always be on the lookout for small things like this.
EDIT #2: For more information on int.TryParse, see the following:
http://www.dotnetperls.com/int-tryparse
How the int.TryParse actually works

You are comparing numbers with the character representation of a number. Each value of code[] represents an actual number. You then compare those values with the values in UserCode which is a string, meaning there is a character at each index. It is never the case that ((byte)'4') == ((byte)4) (using 4 as an example, but works for any numerical digit).
One way around this is to parse each user input character into a byte using the byte.Parse method.
For fun learning purposes look at the output from the following code:
for (char i = '0'; i <= '9'; i++)
{
Console.WriteLine("char: " + i + "; value: " + ((byte)i));
}
The output is actually:
char: 0; value: 48
char: 1; value: 49
char: 2; value: 50
char: 3; value: 51
char: 4; value: 52
char: 5; value: 53
char: 6; value: 54
char: 7; value: 55
char: 8; value: 56
char: 9; value: 57
This is due to string encoding.
I would also recommend that one you have your code working that you submit it to the fine folks at the Code Review site to review other aspects of the code which could use work.

Related

Pertaining to random and non-repeating numbers

My code is supposed to generate random and non-repeating numbers, but when I print it it doesn't do it properly and sometimes it repeats itself. How can I fix?
Console.WriteLine("choose how many digits your password should be\nminimum 5 maximum 10 digits");
Console.Write("number of digits:");
int digit = int.Parse(Console.ReadLine());
int[] passwordarray = new int[digit];
Random r = new Random();
for (int i = 0; i < passwordarray.Length; i++)
{
if (digit <= 10 && digit >= 5)
{
do
{
passwordarray[i] = r.Next(0, 10);
} while (!(passwordarray.Contains(passwordarray[i])));
}
else
{
Console.WriteLine("your number of digits is less than 5 or more than 10");
}
}
I restructured your code:
Console.WriteLine("choose how many digits your password should be\nminimum 5 maximum 10 digits");
Console.Write("number of digits:");
int digit = int.Parse(Console.ReadLine());
int[] passwordarray = new int[digit];
Random r = new Random();
// check the digits first
if (digit <= 10 && digit >= 5)
{
int tempVal;
for (int i = 0; i < passwordarray.Length; i++)
{
// generate values until you have a value thats not in the array
do
{
tempVal = r.Next(0, 10);
} while (passwordarray.Contains(tempVal));
// add the value
passwordarray[i] = tempVal;
}
}
Basically you want to check first if the digits are between 5 and 10, then iterate trough your array, generate random values until you have one that isn't in the password array yet, and add this value. I just restructured your code, I didn't run it.
EDIT: obviously there are better solutions to implement this behavior, like Dmitrys answer, I restructured the code just to show you where your logic is wrong

How to generate a number from first or last digits of an array and check if it is divisible by a certain number in C#?

I am a beginner in C# and I am struck with this problem. To question is as follows
You are given an array of size n that contains integers. Here, n is an even number. You are required to perform the following operations:
1. Divide the array of numbers in two equal halves
Note: Here, two equal parts of a test case are created by dividing the array into two equal parts.
2. Take the first digit of the numbers that are available in the first half of the array (first 50% of
the test case)
3. Take the last digit of the numbers that are available in the second half of the array (second 50% of
the test case)
4. Generate a number by using the digits that have been selected in the above steps
Your task is to determine whether the newly-generated number is divisible by 11.
And this is my code-
using System;
namespace IsDivisible
{
class Program
{
static void Main(string[] args)
{
int n = Convert.ToInt32(Console.ReadLine());
int div = 0, digit;
string str = " ";
string[] numArray = Console.ReadLine().Split(' ');
int[] arr = new int[n];
for (int i = 0; i < n; i++)
{
arr[i] = Convert.ToInt32(str[i]);
if (i <= n / 2)
{
while (arr[i] >= 10)
{
div = arr[i] / 10;
}
str += div;
}
else
{
digit = arr[i] % 10;
str += digit;
}
}
long newNumber = Convert.ToInt64(str);
if (newNumber % 11 == 0)
Console.WriteLine("YES");
else
Console.WriteLine("NO");
Console.Read();
}
}
}```
It has no errors during compile time in visual studio. The code is not printing anything after I input the array and I am unable to figure out what's wrong. Please help.

How to write out odd numbers in an interval using for function

So my homework is I have to take two numbers from the user then I have to write out the odd numbers in that interval.But the code under doesn't work. It writes out "TrueFalseTrueFalse".
int szam;
int szam2=0;
int szam3=0;
int szam4=0;
Console.Write("Please give a number:");
szam = Convert.ToInt32(Console.ReadLine());
Console.Write("Please give another number:");
szam2 = Convert.ToInt32(Console.ReadLine());
if (szam>szam2)
{
for (szam3=szam, szam4 = szam2; szam4 < szam3; szam4++)
{
Console.Write(szam2 % 2==1);
}
}
else
{
for (szam3 = szam, szam4 = szam2; szam3 < szam4; szam3++)
{
Console.Write(szam3 % 2 ==1);
}
}
So if the two numbers would be 0 and 10, the program has to write out 1, 3, 5, 7, and 9
I would be careful when naming your variables yes its a small piece of code but it gets confusing to people trying to read it.
Based on the requirement, I would guess you want all the odd numbers given a certain range.
const string comma = ",";
static void Main(string[] args)
{
int start = getNumber();
int end = getNumber();
if(start > end)
{
int placeHolder = end;
end = start;
start = placeHolder;
}
string delimiter = string.Empty;
for(int i = start; i < end; i++)
{
if(i % 2 == 1)
{
Console.Write(string.Concat(delimiter,i.ToString()));
delimiter = comma;
}
}
Console.ReadLine();//otherwise you wont see the result
}
static int getNumber()
{
Console.Write("Please enter a number:");
string placeHolder = Console.ReadLine();
int toReturn = -1;
if (int.TryParse(placeHolder, out toReturn))
return toReturn;
return getNumber();
}
as Juharr mentioned in the comments, you need to check the result to print the actual number.
Width Linq you can write:
int szam = 20;
int szam2= 30;
var odds = Enumerable.Range(szam2 > szam ? szam : szam2, Math.Abs(szam-szam2))
.Where(x=>x % 2 != 0);
outputs:
21
23
25
27
29
// so we create a range from low to high (Enumerable.Range(..)
// take only the odd values (x % 2 != 0)
simply wrap it in string.Join to make a single string:
string text = String.Join(",",Enumerable.Range(szam2 > szam ? szam :
szam2,Math.Abs(szam-szam2))
.Where(x=>x % 2 != 0));

Reassigning a value to a character array not working

I am currently having issues reassigning a value to a character array. Below is my code (unfinished solution to find the next smallest palindrome):
public int nextSmallestPalindrome(int number)
{
string numberString = number.ToString();
// Case 1: Palindrome is all 9s
for (int i = 0; i < numberString.Length; i++)
{
if (numberString[i] != '9')
{
break;
}
int result = number + 2;
return result;
}
// Case 2: Is a palindrome
int high = numberString.Length - 1;
int low = 0;
bool isPalindrome = true;
for (low = 0; low <= high; low++, high--)
{
if (numberString[low] != numberString[high])
{
isPalindrome = false;
break;
}
}
char[] array = numberString.ToCharArray();
if (isPalindrome == true)
{
// While the middle character is 9
while (numberString[high] == '9' || numberString[low] == '9')
{
array[high] = '0';
array[low] = '0';
high++;
low--;
}
int replacedvalue1 = (int)Char.GetNumericValue(numberString[high]) + 1;
int replacedvalue2 = (int)Char.GetNumericValue(numberString[low]) + 1;
StringBuilder result = new StringBuilder(new string(array));
if (high == low)
{
result[high] = (char)replacedvalue1;
}
else
{
Console.WriteLine(result.ToString());
result[high] = (char)replacedvalue1;
Console.WriteLine(result.ToString());
result[low] = (char)replacedvalue2;
}
return Int32.Parse(result.ToString());
}
else return -1;
}
Main class runs:
Console.WriteLine(nextSmallestPalindrome(1001));
This returns 1001, then 101 and then gives a formatexception at the return Int32.Parse(result.ToString()); statement.
I am very confused, as I believe "result" should be 1101 after I assign result[high] = (char)replacedvalue1;. Printing replacedvalue1 gives me "1" as expected. However, debugging it line by line shows that "1001" turns into "1 1" at the end, signifying strange characters.
What could be going wrong?
Thanks
Characters and numbers aren't the same thing. I find it easiest to keep an ASCII chart open when doing this sort of thing.
If you look at one of those charts, you'll see that the character 0 actually has a decimal value of 48.
char c = (char)48; // Equals the character '0'
The reverse is also true:
char c = '0';
int i = (int)c; // Equals the number 48
You managed to keep chars and ints separate for the most part, but at the end you got them mixed up:
// Char.GetNumericValue('0') will return the number 0
// so now replacedvalue1 will equal 1
int replacedvalue1 = (int)Char.GetNumericValue(numberString[high]) + 1;
// You are casting the number 1 to a character, which according to the
// ASCII chart is the (unprintable) character SOH (start of heading)
result[high] = (char)replacedvalue1;
FYI you don't actually need to cast a char back-and-forth in order to perform operations on it. char c = 'a'; c++; is valid, and will equal the next character on the table ('b'). Similarly you can increment numeric characters:
char c = '0'; c++; // c now equals '1'
Edit: The easiest way to turn an integer 1 into the character '1' is to "add" the integer to the character '0':
result[high] = (char)('0' + replacedvalue1);
Of course there are much easier ways to accomplish what you are trying to do, but these techniques (converting and adding chars and ints) are good tools to know.
You do not have write that much code to do it.
Here is your IsPalindrome method;
private static bool IsPalindrome(int n)
{
string ns = n.ToString(CultureInfo.InvariantCulture);
var reversed = string.Join("", ns.Reverse());
return (ns == reversed);
}
private static int FindTheNextSmallestPalindrome(int x)
{
for (int i = x; i < 2147483647; i++)
{
if (IsPalindrome(i))
{
return i;
}
}
throw new Exception("Number must be less than 2147483647");
}
This is how you call it. You do not need an array to call it. You can just enter any number which is less than 2147483647(max value of int) and get the next palindrome value.
var mynumbers = new[] {10, 101, 120, 110, 1001};
foreach (var mynumber in mynumbers)
{
Console.WriteLine(FindTheNextPalindrome(mynumber));
}

Increase chances of name being picked from Array

For my program, I've prompted the user to put 20 names into an array (the array size is 5 for testing for now), this array is then sent to a text document. I need to make it so that it will randomly pick a name from the list and display it (which I have done). But I now need to make it increase the chances of a name being picked, how would I go about doing this?
Eg. I want to increase the chances of the name 'Jim' being picked from the array.
class Programt
{
static void readFile()
{
}
static void Main(string[] args)
{
string winner;
string file = #"C:\names.txt";
string[] classNames = new string[5];
Random RandString = new Random();
Console.ForegroundColor = ConsoleColor.White;
if (File.Exists(file))
{
Console.WriteLine("Names in the text document are: ");
foreach (var displayFile in File.ReadAllLines(file))
Console.WriteLine(displayFile);
Console.ReadKey();
}
else
{
Console.WriteLine("Please enter 5 names:");
for (int i = 0; i < 5; i++)
classNames[i] = Console.ReadLine();
File.Create(file).Close();
File.WriteAllLines(file, classNames);
Console.WriteLine("Writing names to file...");
winner = classNames[RandString.Next(0, classNames.Length)];
Console.ForegroundColor = ConsoleColor.Yellow;
Console.WriteLine("\nThe winner of the randomiser is: {0} Congratulations! ", winner);
Thread.Sleep(3000);
Console.Write("Completed");
Thread.Sleep(1000);
}
}
}
There's two ways of doing this. You can either produce a RNG with a normal distribution targeting one number.
Or the simpler way is a translational step. Generate in the range 0-100 and then produce code which translates to the answer in a biased way e.g.
0-5 : Answer 1
6-10: Answer 2
11-90: Answer 3
91-95: Answer 4
96-100: Answer 5
This gives an 80% chance of picking Answer 3, the others only get a 5% chance
So where you currently have RandString.Next(0, classNames.Length) you can replace that with a function something like GetBiasedIndex(0, classNames.Length, 3)
The function would look something like this (with test code):
public Form1()
{
InitializeComponent();
int[] results = new int[5];
Random RandString = new Random();
for (int i = 0; i < 1000; i++)
{
var output = GetBiasedIndex(RandString, 0, 4, 3);
results[output]++;
}
StringBuilder builder = new StringBuilder();
for (int i = 0; i < 5; i++)
{
builder.AppendLine(results[i].ToString());
}
label1.Text = builder.ToString();
}
private int GetBiasedIndex(Random rng, int start, int end, int target)
{
//Number between 0 and 100 (Helps to think percentage)
var check = rng.Next(0, 100);
//There's a few ways to do this next bit, but I'll try to keep it simple
//Allocate x% to the target and split the remaining y% among all the others
int x = 80;//80% chance of target
int remaining = 100 - x;//20% chance of something else
//Take the check for the target out of the last x% (we can take it out of any x% chunk but this makes it simpler
if (check > (100 - x))
{
return target;
}
else
{
//20% left if there's 4 names remaining that's 5% each
var perOther = (100 - x) / ((end - start) - 1);
//result is now in the range 0..4
var result = check / perOther;
//avoid hitting the target in this section
if (result >= target)
{
//adjust the index we are returning since we have already accounted for the target
result++;
}
//return the index;
return result;
}
}
and the output:
52
68
55
786
39
If you're going to call this function repeatedly you'll need to pass in the instance of the RNG so that you don't reset the seed each call.
If you want to target a name instead of an index you just need to look up that name first and have an else condition for when that name isn't found

Categories