BlackJack Project - Can't make aces change from 11 to 1's - vb.net

I'm currently making a blackjack game for my project in school in Visual Basic.
In blackjack, when you have aces (value initially 11) their value turns to 1 when the total value of the cards is > 21. In code, this would just take away 10 for every ace
I'm stuck on this.
This is the code I have (that doesn't work):
Do While PlayerValue > 21 And counter <= noAcesPlayer
counter += 1
PlayerValue -= 10
Loop
In a senario, I have a: 2, 8, A, 8 (=29)
But since there is an Ace, and the total value is > 21, the value should have 10 subtracted from it (=19) - the above code does not do this.
Another scenario would be 10, 8, A, A (=40)
Again, the two Aces should turn into 1's, since the total value > 21, giving 20.
Any help would be greatly appreciated. :)

Here is an approach
Public Enum CardFace
None
Ace
Two
Three
Four
Five
Six
Seven
Eight
Nine
Ten
Jack
Queen
King
End Enum
This code should produce a value of twenty
Dim cards As New List(Of CardFace) From {CardFace.Ten, CardFace.Eight, CardFace.Ace, CardFace.Ace}
Dim total As Integer = 0
Dim numofAces As Integer = 0
For Each c As CardFace In cards
Debug.WriteLine(c.ToString)
If c = CardFace.Ace Then
numofAces += 1
Else
total += c
End If
Next
If numofAces > 0 Then
If total + 11 + (numofAces - 1) > 21 Then
total += numofAces
Else
total += 11 + (numofAces - 1)
End If
End If
Debug.WriteLine(total)

The correct way to build a blackjack hand is the following (in pseudo-code):
Variables: total = 0, soft-flag = false
For each card in hand:
Add card value to total. Faces are 10, aces are 1.
If the card you added was an ace, set soft-flag = true
If total < 12 and soft-flag:
Add 10 to total
Else:
set soft-flag = false
That's it. Only one loop over the cards, no extraneous variables, and you're left with the total value and a flag indicating if the total is soft.

Related

VB.Net How to display the frequency distribution of grades from a text file

Evaluate Grades: The file Final.txt contains student grades on a final exam. Write a program that reads the Final.txt file into a string array, converts it into an integer array, calculates the number of grades and the average grade, and then uses loop(s) to calculate and display the percentage of grades that are above average, the standard deviation of the grades, and a frequency distribution showing grades in the following classes: 90-100, 80-89, 70-79, 60-69, and below 60.
I have been able to write the program that reads the text file, calculates the number of grades, the average grade, percentage of grades that are above average, and the standard deviation of the grades. I cannot figure out how to do the frequency distribution showing grades in the classes. Any help is appreciated.
The Grades
Interface
My Code
Your code shows you have created an array of doubles called "grades". Your question said that it should be an array of integers, which would be an easy change to make.
This code should tell you how many grades are in each range:
'This example assumes you have changed your "grades" array to integer
Dim freq90To100, freq80To90, freq70To80, freq60To70, freqBelow60 As Integer
For Each i As Integer In grades
If i < 60 Then
freqBelow60 += 1 'this is shorthand for "freqBelow60 = freqBelow60 + 1"
ElseIf i >= 60 And i < 70 Then
freq60To70 += 1
ElseIf i >= 70 And i < 80 Then
freq70To80 += 1
ElseIf i >= 80 And i < 90 Then
freq80To90 += 1
ElseIf i >= 90 Then
freq90To100 += 1
End If
Next
'Change the TextBox names as necessary to match your user interface
txtBelow60.Text = freqBelow60.ToString()
txt60To70.Text = freq60To70.ToString()
txt70To80.Text = freq70To80.ToString()
txt80To90.Text = freq80To90.ToString()
txt90To100.Text = freq90To100.ToString()

Calculating a total cost based on how many stripes someone wants on their clothes

I'm trying to make it so that if someone wants 3 or less stripes on their shorts it costs 50 cent per stripe on top of the 5.50 base cost for a pair of shorts and then every stripe after the third costs 2 euro each. It works if they chose 3 or less but once I enter any stripe amount above 3 it just displays the base 5.50 cost for the shorts. Not sure what to do any help is appreciated.
I have declared all my variables correctly, I assume the problem is with the code below
'calculate cost of Shorts
If mskShortStripes.Text <= 3 Then
dblTotalShorts += CDbl(mskShorts.Text * 5.5) + (mskShortStripes.Text * 0.5)
ElseIf mskShortStripes.Text > 3 Then
dblTotalShorts += CDbl(mskShorts.Text * 5.5) + (mskShortStripes.Text <= 3 * 0.5) + (mskShortStripes.Text > 3 * 2)
End If
You're asking for trouble working with the .Text property directly as if it were a number. It is not. Fun things happen when the value in your control is not actually a number.
Use Integer.TryParse to convert that string to a number:
Dim numberOfStripes As Integer
If Integer.TryParse(mskShortStripes.Text, numberOfStripes) Then
If numberOfStripes >= 0 Then
' ... now do some math in here with the "numberOfStripes" variable ...
Else
MessageBox.Show("Number of Stripes can't be negative!")
End If
Else
MessageBox.Show("Invalid Number of Stripes!")
End If

Matricial distribution

I have a list of projects with hours to be consumed, distributed linearly through the months:
Hours Aug Sep Oct Nov Dec
100 20 20 20 20 20
200 40 40 40 40 40
300 60 60 60 60 60
600 120 120 120 120 120
Some projects will end in different months, so it shoudn't mark any hour in the next months (must be 0):
Hours Aug Sep Oct Nov Dec End
100 33.3 33.3 33.3 0 0 Oct
200 50 50 50 50 0 Nov
300 60 60 60 60 60 Dec
600 143.3 143.3 143.3 110 60
However, we have to keep the proportion of 20% of the sum (20% * 600 = 120. I've put 20% because we have 5 months, but it could be different percentages) on each month, so:
Hours Aug Sep Oct Nov Dec End
100 20 30 50 0 0 Oct
200 60 50 30 60 0 Nov
300 40 40 40 60 120 Dec
600 120 120 120 120 120
I have a Sudoku-like problem here, where I need to respect the proportion of the column and keep the sum of the line on each project. I've tried in many ways (VBA or functions) to make this distribution, but I've failed so far. I believe someone has crossed with this problem before, so is there a way of doing this distribution programatically? Is there a name for this kind of distribution?
See the image below. Once you have the marginal sums filled in, enter this formula into B2 and fill in the rest of the table (enter it as an array formula with ctrl+shift+enter):
=IF(MATCH($H2,$B$1:$F$1,0)<COLUMNS($B$1:B$1),0,($A2-SUM(C2:$G2))/(SUM(IF(MATCH($H$2:$H$4,$B$1:$F$1,0)<COLUMNS($B$1:B$1),0,$A$2:$A$4))-SUM(IF(MATCH($H$2:$H$4,$B$1:$F$1,0)<COLUMNS($B$1:B$1),0,C$2:$G$4)))*B$5)
The example given will not have a unique solution, so the aim with the above formula is to balance the hours by starting at the end and moving backwards in time while applying hours proportionally to how many hours are left on each project. For example, after resolving Dec, the project on row 2 still has 200 hours to allocate and the project on row 3 still has 180 hours to allocate. The formula will therefore apply 120 * 200 / (200 + 180) hours in Nov from the project on row 2 and 120 * 180 / (200 + 180) hours from the project in row 3.
This methodology assumes that all projects start at the same time. If this assumption doesn't hold, VBA would probably be the way to go. I would sort the months by the number of active projects, smallest to largest, then apply the same sort of calculation as here.
I'm not 100% sure I got your question right, but hopefully yes because it took me quite a lot of time. It actually looks deceptively easy, but is in fact quite tricky.
So let's presume we have this table:
Now if I got it right, we basically want to count, how many hours we
used up already, and now we want to distribute what we have left
depending on the unused (empty) months
So, for example, in Row 3 (100 hours) we don't want to distribute any hours, because we already used up all 100 out of 100 hours
In the next row (4), we want to distribute 115 hours (200-85) to the remaining 2 cells => which would leave us with 57.5 hours per month left on shift
and et cetera...
Under presumption, that's what you want the algorhytm to do:
Private Sub divide_time()
Dim tbl As ListObject: Set tbl = Sheets("Sheet1").ListObjects("Table1")
Dim hour_dist() As Integer
ReDim hour_dist(1 To 3)
' first we need to learn how many hours total we have available per project
For i = 1 To 3
With tbl.ListColumns(1)
hour_dist(i) = .Range(i + 1) ' we store each value into an array per project
End With
Next i
Dim current As Double
Dim sumof As Double
Dim hours_left As Double
Dim empty_counter As Integer
For i = 1 To 3
'we reset all of the counters per row
sumof = 0
empty_counter = 0
'looping through all the column values in the row
For j = 2 To 6
current = tbl.ListRows(1).Range(i, j)
sumof = sumof + current ' we get a sum of the current values in the row
If (current = 0) Then 'if there is an empty cell, we keep track of it _
(so we know into how many cells we can still divide the remaining time)
empty_counter = empty_counter + 1
End If
Next j
' so we get how many hours we have left for the project _
in comparison to how many months are free to distribute
hours_left = (hour_dist(i) - sumof)
'if we also want to store the info, _
'what month we ended on before we distribute the remaining hours
If (hours_left = 0) Then
Select Case empty_counter
Case 0
tbl.ListRows(1).Range(i, 7) = "Dec"
Case 1
tbl.ListRows(1).Range(i, 7) = "Nov"
Case 2
tbl.ListRows(1).Range(i, 7) = "Oct"
Case 3
tbl.ListRows(1).Range(i, 7) = "Sep"
Case 4
tbl.ListRows(1).Range(i, 7) = "Aug"
End Select
Else
tbl.ListRows(1).Range(i, 7) = "Dec"
End if
If (empty_counter <> 0) Then '( we dont want to be dividing by 0 )
For n = 6 To (6 - empty_counter + 1) Step -1
'for each month we divide what we have left _
depending on the % of the months available
tbl.ListRows(1).Range(i, n) = (hours_left / empty_counter)
Next n
End If
' and we loop it for each and every row
Next i
End Sub
The resulting table will look like this:
I guess you need to think "backwards".
First distribute hours among active projects in month n (December).
Deduct these hours from respective project and move to month n-1, etc.

Calculate amount of combinations with conditions

I'd like to calculate how many different variations of a certain amount of numbers are possible. The number of elements is variable.
Example:
I have 5 elements and each element can vary between 0 and 8. Only the first element is a bit more defined and can only vary between 1 and 8. So far I'd say I have 8*9^4 possibilities. But I have some more conditions. As soon as one of the elements gets zero the next elements should be automatically zero as well.
E.G:
6 5 4 7 8 is ok
6 3 6 8 0 is ok
3 6 7 0 5 is not possible and would turn to 3 6 7 0 0
Would somebody show me how to calculate the amount of combinations for this case and also in general, because I'd like to be able to calculate it also for 4 or 8 or 9 etc. elements. Later on I'd like to calculate this number in VBA to be able give the user a forecast how long my calculations will take.
Since once a 0 is present in the sequence, all remaining numbers in the sequence will also be 0, these are all of the possibilities: (where # below represents any digit from 1 to 8):
##### (accounts for 8^5 combinations)
####0 (accounts for 8^4 combinations)
...
#0000 (accounts for 8^1 combinations)
Therefore, the answer is (in pseudocode):
int sum = 0;
for (int x = 1; x <= 5; x++)
{
sum = sum + 8^x;
}
Or equivalently,
int prod = 0;
for (int x = 1; x <= 5; x++)
{
prod = 8*(prod+1);
}
great thank you.
Sub test()
Dim sum As Single
Dim x As Integer
For x = 1 To 6
sum = sum + 8 ^ x
Next
Debug.Print sum
End Sub
With this code I get exactly 37488. I tried also with e.g. 6 elements and it worked as well. Now I can try to estimate the calculation time

Hash function to iterate through a matrix

Given a NxN matrix and a (row,column) position, what is a method to select a different position in a random (or pseudo-random) order, trying to avoid collisions as much as possible?
For example: consider a 5x5 matrix and start from (1,2)
0 0 0 0 0
0 0 X 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
I'm looking for a method like
(x,y) hash (x,y);
to jump to a different position in the matrix, avoiding collisions as much as possible
(do not care how to return two different values, it doesn't matter, just think of an array).
Of course, I can simply use
row = rand()%N;
column = rand()%N;
but it's not that good to avoid collisions.
I thought I could apply twice a simple hash method for both row and column and use the results as new coordinates, but I'm not sure this is a good solution.
Any ideas?
Can you determine the order of the walk before you start iterating? If your matrices are large, this approach isn't space-efficient, but it is straightforward and collision-free. I would do something like:
Generate an array of all of the coordinates. Remove the starting position from the list.
Shuffle the list (there's sample code for a Fisher-Yates shuffle here)
Use the shuffled list for your walk order.
Edit 2 & 3: A modular approach: Given s array elements, choose a prime p of form 2+3*n, p>s. For i=1 to p, use cells (iii)%p when that value is in range 1...s-1. (For row-length r, cell #c subscripts are c%r, c/r.)
Effectively, this method uses H(i) = (iii) mod p as a hash function. The reference shows that as i ranges from 1 to p, H(i) takes on each of the values from 0 to p-1, exactly one time each.
For example, with s=25 and p=29 or 47, this uses cells in following order:
p=29: 1 8 6 9 13 24 19 4 14 17 22 18 11 7 12 3 15 10 5 16 20 23 2 21 0
p=47: 1 8 17 14 24 13 15 18 7 4 10 2 6 21 3 22 9 12 11 23 5 19 16 20 0
according to bc code like
s=25;p=29;for(i=1;i<=p;++i){t=(i^3)%p; if(t<s){print " ",t}}
The text above shows the suggestion I made in Edit 2 of my answer. The text below shows my first answer.
Edit 0: (This is the suggestion to which Seamus's comment applied): A simple method to go through a vector in a "random appearing" way is to repeatedly add d (d>1) to an index. This will access all elements if d and s are coprime (where s=vector length). Note, my example below is in terms of a vector; you could do the same thing independently on the other axis of your matrix, with a different delta for it, except a problem mentioned below would occur. Note, "coprime" means that gcd(d,s)=1. If s is variable, you'd need gcd() code.
Example: Say s is 10. gcd(s,x) is 1 for x in {1,3,7,9} and is not 1 for x in {2,4,5,6,8,10}. Suppose we choose d=7, and start with i=0. i will take on values 0, 7, 14, 21, 28, 35, 42, 49, 56, 63, 70, which modulo 10 is 0, 7, 4, 1, 8, 5, 2, 9, 6, 3, 0.
Edit 1 & 3: Unfortunately this will have a problem in the two-axis case; for example, if you use d=7 for x axis, and e=3 for y-axis, while the first 21 hits will be distinct, it will then continue repeating the same 21 hits. To address this, treat the whole matrix as a vector, use d with gcd(d,s)=1, and convert cell numbers to subscripts as above.
If you just want to iterate through the matrix, what is wrong with row++; if (row == N) {row = 0; column++}?
If you iterate through the row and the column independently, and each cycles back to the beginning after N steps, then the (row, column) pair will interate through only N of the N^2 cells of the matrix.
If you want to iterate through all of the cells of the matrix in pseudo-random order, you could look at questions here on random permutations.
This is a companion answer to address a question about my previous answer: How to find an appropriate prime p >= s (where s = the number of matrix elements) to use in the hash function H(i) = (i*i*i) mod p.
We need to find a prime of form 3n+2, where n is any odd integer such that 3*n+2 >= s. Note that n odd gives 3n+2 = 3(2k+1)+2 = 6k+5 where k need not be odd. In the example code below, p = 5+6*(s/6); initializes p to be a number of form 6k+5, and p += 6; maintains p in this form.
The code below shows that half-a-dozen lines of code are enough for the calculation. Timings are shown after the code, which is reasonably fast: 12 us at s=half a million, 200 us at s=half a billion, where us denotes microseconds.
// timing how long to find primes of form 2+3*n by division
// jiw 20 Sep 2011
#include <stdlib.h>
#include <stdio.h>
#include <sys/time.h>
double ttime(double base) {
struct timeval tod;
gettimeofday(&tod, NULL);
return tod.tv_sec + tod.tv_usec/1e6 - base;
}
int main(int argc, char *argv[]) {
int d, s, p, par=0;
double t0=ttime(0);
++par; s=5000; if (argc > par) s = atoi(argv[par]);
p = 5+6*(s/6);
while (1) {
for (d=3; d*d<p; d+=2)
if (p%d==0) break;
if (d*d >= p) break;
p += 6;
}
printf ("p = %d after %.6f seconds\n", p, ttime(t0));
return 0;
}
Timing results on 2.5GHz Athlon 5200+:
qili ~/px > for i in 0 00 000 0000 00000 000000; do ./divide-timing 500$i; done
p = 5003 after 0.000008 seconds
p = 50021 after 0.000010 seconds
p = 500009 after 0.000012 seconds
p = 5000081 after 0.000031 seconds
p = 50000021 after 0.000072 seconds
p = 500000003 after 0.000200 seconds
qili ~/px > factor 5003 50021 500009 5000081 50000021 500000003
5003: 5003
50021: 50021
500009: 500009
5000081: 5000081
50000021: 50000021
500000003: 500000003
Update 1 Of course, timing is not determinate (ie, can vary substantially depending on the value of s, other processes on machine, etc); for example:
qili ~/px > time for i in 000 004 010 058 070 094 100 118 184; do ./divide-timing 500000$i; done
p = 500000003 after 0.000201 seconds
p = 500000009 after 0.000201 seconds
p = 500000057 after 0.000235 seconds
p = 500000069 after 0.000394 seconds
p = 500000093 after 0.000200 seconds
p = 500000099 after 0.000201 seconds
p = 500000117 after 0.000201 seconds
p = 500000183 after 0.000211 seconds
p = 500000201 after 0.000223 seconds
real 0m0.011s
user 0m0.002s
sys 0m0.004s
Consider using a double hash function to get a better distribution inside the matrix,
but given that you cannot avoid colisions, what I suggest is to use an array of sentinels
and mark the positions you visit, this way you are sure you get to visit a cell once.