Convert string to pre-defined variable name in Visual Basic? - vb.net

I'm programming a basic slot machine in Visual basic, and want to use a for loop to randomly choose the image for each slot, display the image in each slot, and change the slotName variable (so I can check later on which symbols are in the slots) for each slot.
The problem I'm finding with a for loop is that the variables and objects for each slot have different names (slot1Name, slot2Name, slot3Name, lblSlot1, lblSlot2, lblSlot3, etc). Is there any way I could have something like:
currentSlotName = "slot" & i & "Name"
This is the code at the moment, this code is repeated (with different variable and object names), for each of the 3 slots, which is pretty inefficient. How can I tidy this code up?
' Randomise numbers and assign images to slots based on random numbers, if the hold isn't on
' Slot 1
If Not held1 Then
slot1Value = Int(Rnd() * numbersGenerated + 0.5)
Select Case slot1Value
Case 0 To 5
lblSlot1.Image = imgBanana
slot1Name = "Banana"
Case 6 To 11
lblSlot1.Image = imgOrange
slot1Name = "Orange"
Case 12 To 16
lblSlot1.Image = imgCherries
slot1Name = "Cherries"
Case 17 To 19
lblSlot1.Image = imgSeven
slot1Name = "Seven"
Case 20
lblSlot1.Image = imgBatman
slot1Name = "Batman"
Case Else
lblSlot1.Text = "Error. slot1value = " & slot1Value
End Select
End If
I have searched around for this, but I'm very new to Visual Basic, and want to keep my code as simple as possible.

Too much to explain. Arrays is what you need to learn next.
http://msdn.microsoft.com/en-us/library/wak0wfyt.aspx

Related

How to Optimize an Overuse of If Statements in Roblox Studio

The goal of this code is to spawn a ball "GlowyBall" in 1 of 5 preset locations randomly. This script activates when a player hits a button. The ball also needs to spawn as 1 of 3 colors randomly. The code works for the most part, but I am struggling when it comes to making this code optimized. I don't know which datatype I should or even can use to replace these if statements. I am just trying to learn different avenues that can be taken. The reason this code needs to be optimized is that it could be used thousands of times per minute, and I don't want the game to be held back by the code.
...
-- Says that there will be 3 colors
local ColorRange = 3
-- Says that there will be 5 spawn locations
local range = 5
-- Makes the code run continuously
while true do
local ColorNumber = math.random(1, ColorRange)
local Number = math.random(1, range)
-- Chooses the random color
if ColorNumber == 1 then
game.ServerStorage.GlowyBallsSideA.GlowyBallGroup1.Glowyball1.Color = Color3.new(1, 0, 0)
end
if ColorNumber == 2 then
game.ServerStorage.GlowyBallsSideA.GlowyBallGroup1.Glowyball2.Color = Color3.new(0, 1, 0)
end
if ColorNumber == 3 then
game.ServerStorage.GlowyBallsSideA.GlowyBallGroup1.Glowyball3.Color = Color3.new(0, 0, 1)
end
-- Chooses which ball will get cloned
if Number == 1 then
ClonePart = game.ServerStorage.GlowyBallsSideA.GlowyBallGroup1.Glowyball1
end
if Number == 2 then
ClonePart = game.ServerStorage.GlowyBallsSideA.GlowyBallGroup1.Glowyball2
end
if Number == 3 then
ClonePart = game.ServerStorage.GlowyBallsSideA.GlowyBallGroup1.Glowyball3
end
if Number == 4 then
ClonePart = game.ServerStorage.GlowyBallsSideA.GlowyBallGroup1.Glowyball4
end
if Number == 5 then
ClonePart = game.ServerStorage.GlowyBallsSideA.GlowyBallGroup1.Glowyball5
end
wait(.6)
local Clone = ClonePart:Clone()
script.Parent.ClickDetector.MouseClick:connect(function()
Clone.Parent = game.Workspace
Clone.Anchored = false
end)
end
...
I am fairly new to programming as a whole so feel free to teach me a few things, thanks.
Instances in Roblox can be accessed in a few different ways; the most common is dot notation (eg. game.Workspace.Part). It's also possible to access instances like items in a table (eg. game["Workspace"]["Part"]). This is useful when accessing an instance with a space in its name, or to add something to the start/end of a string before accessing it.
The if statements for choosing which ball to clone can then be reduced to the following:
-- Chooses which ball will get cloned
ClonePart = game.ServerStorage.GlowyBallsSideA.GlowyBallGroup1["Glowyball" .. Number] -- Add Number to the end of "Glowyball" before accessing it
The same could be done with choosing the random color, as well as substituting multiple if statements for one if-elseif statement could make the code more readable.
-- Chooses the random color
local Glowyball = game.ServerStorage.GlowyBallsSideA.GlowyBallGroup1["Glowyball" .. ColorNumber]
if ColorNumber == 1 then
Glowyball.Color = Color3.new(1, 0, 0)
elseif ColorNumber == 2 then
Glowyball.Color = Color3.new(0, 1, 0)
elseif ColorNumber == 3 then
Glowyball.Color = Color3.new(0, 0, 1)
end
Heliodex did a good job explaining how to simplify finding the instances, but you could take it a step further and remove the if-statements entirely by replacing them with arrays.
Typically, from a general programming standpoint, when you find that you have a lot of very similar if-statements, the solution is to use a switch-statement instead of if-elseif chains. Switch statements allow you to define a bunch of possible cases and when it executes, it will jump specifically to the case that matches. However, neither lua nor luau support switch statements, but we can get the benefit of switch-statements.
Since you are using random numbers already, you can pre-define your colors into an array and have the random number simply pick an index to use...
-- make some colors to pick from
local colors = {
Color3.new(1, 0, 0),
Color3.new(0, 1, 0),
Color3.new(0, 0, 1),
}
-- Set up the list of spawn locations
local locationGroup = game.ServerStorage.GlowyBallsSideA.GlowyBallGroup1
local spawnLocations = {
locationGroup.Glowyball1,
locationGroup.Glowyball2,
locationGroup.Glowyball3,
locationGroup.Glowyball4,
locationGroup.Glowyball5,
}
-- could this be simplified to ...?
-- local spawnLocations = locationGroup:GetChildren()
-- when someone clicks the button, spawn the part
script.Parent.ClickDetector.MouseClick:Connect(function()
-- choose a color and spawn location
local colorNumber = math.random(1, #colors)
local spawnNumber = math.random(1, #spawnLocations)
local clonePart = spawnLocations[spawnNumber]
local color = colors[colorNumber]
-- spawn it
local clone = clonePart:Clone()
clone.Color = color
clone.Parent = game.Workspace
clone.Anchored = false
end)
The advantage of this approach is that you can simply add more colors and spawn locations to those arrays, and the rest of your code will still work.

How to find every combination of a binary 16 digit number

I have 16 different options in my program and i have a 16 character variable which is filled with 1's or 0's depending on the options that are selected (0000000000000000 means nothing is selected, 0010101010000101 means options 3,5,7,9,14 and 16 are selected, 1111111111111111 means everything is selected.)
When i run my program, the code looks (using an if statement) for a 1 in the designated character of the 16 digit number and if there is one there then it runs the code for that option, otherwise it skips it..
e.g option 3 looks too see if the 3rd character (0010000000000000) is a 1 and if it is it runs the code.
Now what i am trying to do is generate a list of every different combination that is possible so I can create an option for it to just loop through and run every possible option:
0000000000000001
0000000000000010
0000000000000011
...
1111111111111100
1111111111111110
1111111111111111
I have tried this but i think it may take a couple of years to run jaja:
Dim binString As String
Dim binNUM As Decimal = "0.0000000000000001"
Do Until binNUM = 0.11111111111111111
binString = binNUM.ToString
If binString.Contains(1) Then
If binString.Contains(2) Or binString.Contains(3) Or binString.Contains(4) Or binString.Contains(5) Or binString.Contains(6) Or binString.Contains(7) Or binString.Contains(8) Or binString.Contains(9) Then
Else
Debug.Print(binNUM)
End If
End If
binNUM = binNUM + 0.0000000000000001
After the code above is complete i would then take the output list and remove any instances of "0." and then any lines which had fewer than 16 chararcters (because the final character would be a 0 and not show) I would add a 0 until there was 16 characters. I know this bit might be stupid but its as far a ive got
Is there a faster way I can I generate a list like this in VB.net?
You should be able to get the list by using Convert.ToString as follows:
Dim sb As New System.Text.StringBuilder
For i As Integer = 0 To 65535
sb.AppendLine(Convert.ToString(i, 2).PadLeft(16, "0"c))
Next
Debug.Print(sb.ToString())
BTW: This should finish in under one second, depending on your system ;-)
Create an enum with FlagAttributes, which allows you to do the key functions you list. Here is an example of setting it up in a small project I am working on:
<FlagsAttribute>
Public Enum MyFlags As Integer
None = 0
One = 1
Two = 2
Three = 4
Four = 8
Five = 16
Recon = 32
Saboteur = 64
Mine = 128
Headquarters = 256
End Enum
e.g.
Dim temp as MyFlags
Dim doesIt as Boolean
temp = MyFlags.One
doesIt = temp.HasFlag(MyFlags.Two)
temp = temp OR MyFlags.Three
'etc.
The real advantage is how it prints out, if you want something other than 0, 1 and is much more human friendly.

Possible to store a value in variable in SPSS?

Is is possible in SPSS to store a value in a variable (not a variable created in a data set)?
For example I have a loop for which I want to pass the value 4 to all the locations in the loop that say NumLvl.
NumLvl = 4.
VECTOR A1L(NumLvl-1).
LOOP #i = 1 to NumLvl-1.
COMPUTE A1L(#i) = 0.
IF(att1 = #i) A1L(#i) = 1.
IF(att1 = NumLvl) A1L(#i) = -1.
END LOOP.
EXECUTE.
You can do this using DEFINE / !ENDDEFINE SPSSs Macro Facility, for example:
DEFINE !MyVar () 4 !ENDDEFINE.
You can then use !MyVar as a substitute for 4 wherever in your syntax you wish.
See DEFINE / !ENDDEFINE documentation for further notes.

Iterate through vars without a index?

I set some vars like this:
local var1Age = 10
local var2Age = 20
local var3Age = 30
Now I want to iterate them with a loop like this:
for i=1, 3 do
if var..i..Age >= 21 then
print("yep")
end
end
I can't change the vars, or create a table instead. Is it possible somehow with this vars?
Edit:
I could do something like this:
if var1Age >= 21 then
print("yep")
end
if var2Age >= 21 then
print("yep")
end
if var3Age >= 21 then
print("yep")
end
But I have ~50 vars like that. That's why I search a way to do it with a loop.
Edit2:
The vars are set by a class I can't change, so I can't change the way the vars are set.
For example I can't set the vars like this:
local varAge = {}
varAge[1] = 10
varAge[2] = 20
varAge[3] = 30
Edit3:
The class saves the vars in a table like this: http://ideone.com/iO4I8N
You could iterate through all local variables via debug.getlocal and filter variables you're interested in by name. http://www.lua.org/pil/23.1.1.html
Here is example on how to use it.
local var1Age = 10
local var2Age = 20
local var3Age = 30
function local_var_value(n)
local a = 1
while true do
local name, value = debug.getlocal(2, a)
if not name then break end
if name == n then
return value
end
a = a + 1
end
end
for i=1, 3 do
local v = local_var_value("var"..i.."Age")
if v and v >= 21 then
print("yep")
end
end
Are you really sure you want to stretch the language usage this far? The use of debug library should be left for advanced use when you cannot do otherwise.
Maybe your programming problem could be solved in a more elegant way using "regular" Lua facilities. To have a sequence of variables indexed by a number, simply use a table as an array:
local varAge = {}
varAge[1] = 10
varAge[2] = 20
varAge[3] = 30
for i=1,#varAge do
if varAge[i] >= 21 then
print("yep")
end
end
EDIT
If you really need to use debug.getlocal and performance is really an issue, you can avoid the potential O(n2) behavior scanning the locals only once and storing their values in a table:
local var1Age = 10
local var2Age = 20
local var3Age = 30
local function GetLocalVars( level )
local result = {}
for i = 1, math.huge do
local name, value = debug.getlocal( level, i )
if not name then break end
result[ name ] = value
end
return result
end
local local_vars = GetLocalVars( 2 )
for i = 1, 3 do
local name = "var"..i.."Age"
local v = local_vars[ name ]
if v and v >= 21 then
print("yep")
end
end
Based on the sample code you provided from your comment here, You should be able to iterate through your data structure without ever needing to use debug.getlocal.
local vars = varcount(DTClass)
for i = 1, vars do
local vari = "var" .. i
local variAge = DTClass[vari.."Age"]
if variAge and variAge >= 21 then
print(DTClass[vari.."Weight"])
end
end
This should work whether DTClass is a table or a userdata assuming it provides a suitable __index. Of course you need some way to determine total elements in DTClass. Just implement the varcount function to do this.
If DTClass is a table, varcount can be as simple as return #DTClass / var_fields.

What function does .NET NPV() use? Doesn't match manual calculations

I am using the NPV() function in VB.NET to get NPV for a set of cash flows.
However, the result of NPV() is not consistent with my results performing the calculation manually (nor the Investopedia NPV calc... which matches my manual results)
My correct manual results and the NPV() results are close, within 5%.. but not the same...
Manually, using the NPV formula:
NPV = C0 + C1/(1+r)^1 + C2/(1+r)^2 + C3/(1+r)^3 + .... + Cn/(1+r)^n
The manual result is stored in RunningTotal
With rate r = 0.04
and period n = 10
Here is my relevant code:
EDIT: Do I have OBOB somewhere?
YearCashOutFlow = CDbl(TxtAnnualCashOut.Text)
YearCashInFlow = CDbl(TxtTotalCostSave.Text)
YearCount = 1
PAmount = -1 * (CDbl(TxtPartsCost.Text) + CDbl(TxtInstallCost.Text))
RunningTotal = PAmount
YearNPValue = PAmount
AnnualRateIncrease = CDbl(TxtUtilRateInc.Text)
While AnnualRateIncrease > 1
AnnualRateIncrease = AnnualRateIncrease / 100
End While
AnnualRateIncrease = 1 + AnnualRateIncrease
' ZERO YEAR ENTRIES
ListBoxNPV.Items.Add(Format(PAmount, "currency"))
ListBoxCostSave.Items.Add("$0.00")
ListBoxIRR.Items.Add("-100")
ListBoxNPVCum.Items.Add(Format(PAmount, "currency"))
CashFlows(0) = PAmount
''''
Do While YearCount <= CInt(TxtLifeOfProject.Text)
ReDim Preserve CashFlows(YearCount)
CashFlows(YearCount) = Math.Round(YearCashInFlow - YearCashOutFlow, 2)
If CashFlows(YearCount) > 0 Then OnePos = True
YearNPValue = CashFlows(YearCount) / (1 + DiscountRate) ^ YearCount
RunningTotal = RunningTotal + YearNPValue
ListBoxNPVCum.Items.Add(Format(Math.Round(RunningTotal, 2), "currency"))
ListBoxCostSave.Items.Add(Format(YearCashInFlow, "currency"))
If OnePos Then
ListBoxIRR.Items.Add((IRR(CashFlows, 0.1)).ToString)
ListBoxNPV.Items.Add(Format(NPV(DiscountRate, CashFlows), "currency"))
Else
ListBoxIRR.Items.Add("-100")
ListBoxNPV.Items.Add(Format(RunningTotal, "currency"))
End If
YearCount = YearCount + 1
YearCashInFlow = AnnualRateIncrease * YearCashInFlow
Loop
EDIT: Using the following values:
Discount Rate = 4%
Life of Project = 10 years
Cash Flow 0 = -78110.00
Cash Flow 1 = 28963.23
Cash Flow 2 = 30701.06
Cash Flow 3 = 32543.12
Cash Flow 4 = 34495.71
Cash Flow 5 = 36565.45
Cash Flow 6 = 38759.38
Cash Flow 7 = 41084.94
Cash Flow 8 = 43550.03
Cash Flow 9 = 46163.04
Cash Flow 10 = 48932.82
Using the calculator at http://www.investopedia.com/calculator/NetPresentValue.aspx
And following the manual "textbook" formula I arrive at the same result:
Net Present Value: $225,761.70
I cannot seem to get NPV() to replicate this result... it spits out $217,078.59
I iterate it manually using the example same value... so they must be using a different function than I am...
The MSDN page example clearly states that the initial expense should be included in the cash flows list.
Normally you wouldn't include the first cashflow in the Visual Basic NPV() function (or at least we don't in the leasing world). You would discount all but the first cash flow, then add that first cash flow amount onto your Net Present Value. Here's an example of what I've done before in a calculation engine (minus error checking to simplify the example):
Dim leaseRentalsDiscounted As Double = 0.0
Dim rebatedCashFlows As IEnumerable(Of LeasePayment) = GetLeaseRentalsPaymentStream()
Dim firstFlow As LeasePayment = rebatedCashFlows(0)
Dim doubleStream As Double() = PaymentToDoubleArray(rebatedCashFlows.Skip(1))
If doubleStream.Length > 0 Then
Dim rate As Decimal = New Decimal(Me.Lease.DiscountRate / 100.0 / 12.0)
leaseRentalsDiscounted = NPV(rate, doubleStream)
End If
leaseRentalsDiscounted += firstFlow.Amount
Return leaseRentalsDiscounted
That could account for your 5% -- I know I've run into an issue like this before. To me, in the manual NPV formula you posted, C0 doesn't need to be in the stream that is discounted, so that's why I don't include it in the NPV() function.
The MSDN page notes that if your cash outflow begins at the beginning of the first period (instead of the end) the first value must be added to the NPV value and not included in the cash flows array.
Your manual calculation shows that your cash outflow (C0) occurs at time zero (present value), which indicates you should follow the MSDN page's suggestion.
Cory Larson is right, in part... but the MSDN documentation seems in error to me.
The problem is that the NPV() function is discounting the very first (n=0) element of the array when it should not; it is beginning at n=1
Even though the MSDN documentation specifics that the first element of the array should be the initial expense this is not the case with their function.
In the NPV() function, the first element of the array (as Cory Larson implied) should be the first real cash flow. Then, after the function returns a result, the result should have the initial expense subtracted.
This is because the NPV() function begins with n=1
using the NPV formula: NPV = C0 + C1/(1+r)^1 + C2/(1+r)^2 + C3/(1+r)^3 + .... + Cn/(1+r)^n
In the manual formula, Cn/(1+r)^n, for n=0 you use your initial expense... then the denominator is 1 (because n=0)
In my opinion, the MSDN example at http://msdn.microsoft.com/en-us/library/microsoft.visualbasic.financial.npv.aspx should be amended to the following:
Exclude the initial -70000 value from the array, shift all element down one in index, and decrease the array size by 1.
Then add the initial expense (-70000) to the variable NetPVal to arrive at the actual result.
Somebody should like MS know about their OBOB :D
(But it's actually a feature, right?)
EDIT: And the section which says " The array must contain at least one negative value (a payment) and one positive value (a receipt)."
In not accurate.
As Cory Larson pointed out: a negative value is not required in the array (and, in fact, should be left out!)