Sorting Gantt Bars (vb .net) - simplified [duplicate] - vb.net

This question already has answers here:
Sorting conundrum (try at your peril) <VB .NET>
(3 answers)
Closed 8 months ago.
Ok, i can see this question is problematic, so I've attempted to explain it in a simplified way, with more code. I suppose this boils down to list reordering and/or reallocation.
I've inserted some of the code from the app to hopefully help.
As mentioned, any time-range overlaps within the same posIndex needs to have an increment of 1 added to its PosIndex. In addition, an increase to all others below it must move by 1 also. As the code iterates through and finds another overlap that has the same PosIndex (not from one above), it again moves it by one, and all others below it. Those on the same index that are not overlapping must have their PosINdex untouched. Only by overlaps above it should their PosIndex increase by 1, pushing them down.
I hope this explanation helps. I'm really, really stuck with this.
I've left the old post below, if it helps.
Module ttest
Public Class barAllocations
Dim lstBars As List(Of barAllocations)
Public Property barText As String
Public Property FromTime() As DateTime
Public Property ToTime() As DateTime
Public Property PosIndex() As Integer
Public Sub addAlloBar(ByVal setBarText As String, ByVal FromTime As DateTime, ToTime As DateTime, PosIndex As Integer)
Dim alloBar As New barAllocations
alloBar.barText = setBarText
alloBar.FromTime = FromTime
alloBar.ToTime = ToTime
alloBar.PosIndex = PosIndex
lstBars.Add(alloBar)
End Sub
Public Sub sortList()
Dim prevIndex As Integer
Dim prevFromDT As DateTime
Dim prevToDT As DateTime
For Each bar As barAllocations In lstBars
If bar.ToTime > prevFromDT And bar.FromTime < prevToDT And bar.PosIndex = prevIndex Then
bar.PosIndex += 1
For Each bars As barAllocations In lstBars
If bar.PosIndex > prevIndex Then bars.PosIndex += 1
Next
End If
prevFromDT = bar.FromTime
prevToDT = bar.ToTime
prevIndex = bar.PosIndex
Next
End Sub
End Class
Private Sub barAdd()
Dim startDT As DateTime = Now().AddHours(-48)
Dim endDT As DateTime = startDT.AddHours(12)
Dim allogrid As New barAllocations
allogrid.addAlloBar("test1", startDT, endDT, 0)
allogrid.addAlloBar("test2", startDT, endDT, 0)
allogrid.addAlloBar("test3", startDT.AddHours(12), endDT.AddHours(12), 0)
allogrid.addAlloBar("test4", startDT.AddHours(24), endDT.AddHours(24), 0)
allogrid.addAlloBar("test5", startDT.AddHours(5), endDT.AddHours(5), 5)
allogrid.addAlloBar("test6", startDT.AddHours(2), endDT.AddHours(2), 5)
allogrid.addAlloBar("test7", startDT.AddHours(1), endDT.AddHours(1), 6)
allogrid.addAlloBar("test8", startDT.AddHours(5), endDT.AddHours(5), 6)
allogrid.addAlloBar("test9", startDT.AddHours(7), endDT.AddHours(7), 7)
allogrid.addAlloBar("test10", startDT.AddHours(8), endDT.AddHours(8), 7)
allogrid.addAlloBar("test11", startDT.AddHours(10), endDT.AddHours(10), 8)
allogrid.addAlloBar("test12", startDT.AddHours(1), endDT.AddHours(1), 8)
allogrid.addAlloBar("test13", startDT.AddHours(22), endDT.AddHours(34), 8)
allogrid.addAlloBar("test14", startDT.AddHours(20), endDT.AddHours(32), 8)
allogrid.addAlloBar("test15", startDT.AddHours(6), endDT.AddHours(6), 9)
allogrid.addAlloBar("test16", startDT.AddHours(11), endDT.AddHours(11), 9)
allogrid.sortList()
End Sub
End Module
////////////////OLD POST/////////////////
I am in the middle of creating a booking app, using a Gantt view to visualize bookings. I draw rectangles where the bookings are to take place based on a date range.
The problem I am having is sorting overlaps. There can be two bookings for the same bed at the same time. What I am trying to do is if there is an overlap, move the bar down one space, but also move all other bars by one space also (the grid and bed row lines will be adjusted at a later point, widening them, to incorporate 2 bars at once).
The bars are placed by assigning a PosIndex, so row 1 is PosIndex 0.
I've been really struggling with this for several days now to no avail.
I thought about using rectangles for intersection detection, which works well, but I'm not able to sort the bars properly. As you can see from the picture, there are overlaps that have not been separated and I have no clue how to do it.
Sorry if my explanation is vague, so please ask for more info if required.
Here's the code i've been working on:
Dim lst As List(Of barAllocation) = lstBars
Dim prevIndex As Integer
Dim prevRec As Rectangle
Dim msg As String = ""
lst = lst.OrderBy(Function(x) x.PosIndex).ThenBy(Function(y) y.FromTime).ToList
For Each bar In lst
Dim tsDate As TimeSpan = bar.ToTime - bar.FromTime
Dim recstart As Integer = bar.ToTime.Month & bar.ToTime.Day & bar.ToTime.Hour & bar.ToTime.Minute
Dim barRec As New Rectangle(recstart, bar.PosIndex * 10, tsDate.TotalMinutes, 10)
If prevRec.IntersectsWith(barRec) Then
If prevIndex = bar.PosIndex Then
If bar.PosIndex = 0 Then
bar.PosIndex += 1
barChanged = bar.PosIndex
Else
barChanged = bar.PosIndex
bar.PosIndex += 1
End If
End If
End If
If bar.PosIndex > barChanged Then bar.PosIndex += 1
prevRec = barRec
prevIndex = bar.PosIndex
Example of App

Here is a working solution for you. The principle is clear, you just need to port it to winforms and render it differently if you need to there.
Public Class _Default
Inherits System.Web.UI.Page
Private Class Booking
Public Property BookingUID As Guid
Public Property Bedroom As String
Public Property BookingName As String
Public Property DateFrom As Date
Public Property DateTo As Date
Public Property HasClash As Boolean = False
Public Sub New(Bedroom As String, BookingName As String, DateFrom As Date, DateTo As Date)
BookingUID = Guid.NewGuid
Me.Bedroom = Bedroom
Me.BookingName = BookingName
Me.DateFrom = DateFrom
Me.DateTo = DateTo
End Sub
End Class
Private Bookings As List(Of Booking) = New List(Of Booking)
Private Sub SortBookings()
' Simple sort
Bookings = (From x In Bookings Select x Order By x.DateFrom, x.DateTo).ToList
' Check for clashes (will force booking onto a new line in the chart
For Each Booking In Bookings
If (From x In Bookings Where x.DateFrom < Booking.DateTo And x.DateTo > Booking.DateFrom And x.Bedroom = Booking.Bedroom And x.BookingUID <> Booking.BookingUID).Any Then
Booking.HasClash = True
End If
Next
End Sub
Private Sub RenderBookings()
Dim s As New StringBuilder
Dim TotalDays As Integer = 0
' get a unique list of bedrooms
Dim Beds = (From x In Bookings Select x.Bedroom Distinct).OrderBy(Function(b) b)
Dim MinDate = (From x In Bookings Select x.DateFrom).Min
Dim MaxDate = (From x In Bookings Select x.DateTo).Max
s.AppendLine("<table style=""border:solid 1px black;"" cellpadding=""2"" cellspacing=""0"" border=""1"">")
' header rows
s.AppendLine("<tr><td rowspan=""2"">Bedroom</td>")
Dim Day, Hour As Integer
For Day = 0 To DateDiff(DateInterval.Day, MinDate, MaxDate) + 1
s.AppendLine("<td colspan=""24"" style=""text-align:center;"">" & MinDate.Date.AddDays(Day).ToString("dd/MM/yyyy") & "</td>")
TotalDays += 1
Next
s.AppendLine("</tr><tr>")
For Day = 0 To DateDiff(DateInterval.Day, MinDate, MaxDate) + 1
For Hour = 0 To 23
s.AppendLine("<td style=""text-align:center;"">" & Hour.ToString.PadLeft(2, "0") & "</td>")
Next
Next
s.AppendLine("</tr>")
' Loop bedrooms
For Each Bed In Beds
s.AppendLine("<tr style=""height:30px;""><td>" & Bed & "</td><td style=""position:relative;"" colspan=""" & TotalDays * 24 & """>")
Dim BedBookings = (From x In Bookings Where x.Bedroom = Bed Select x Order By x.DateFrom)
Dim InsertRows As String = ""
Dim First As Boolean = True
' Loop bookings for each bedroom
For Each Booking In BedBookings
' divide 100 % by the time period in minutes we're trying to cover
Dim Scale As Decimal = (100 / (TotalDays * 24 * 60)) ' 100% / total minutes in the row
' How many whole days are there between this booking and our MinDate.Date?
Dim OffsetDays As Integer = Math.Floor((Booking.DateFrom.Date - MinDate.Date).TotalDays)
' Calculate the left %
Dim Left As Decimal = ((OffsetDays * 24 * 60) + (Booking.DateFrom - Booking.DateFrom.Date).TotalMinutes) * Scale
' Calculate the witdht %
Dim Width As Decimal = (Booking.DateTo - Booking.DateFrom).TotalMinutes * Scale
' Get our Bar HTML
Dim Bar As String = "<div style=""overflow:hidden;position:absolute;border:solid 1px red;background-color:navy;color:#fff;top:3px;width:" & Width & "%;left:" & Left & "%"" title=""" & Booking.DateFrom.ToString("dd/MM/yyyy HH:mm") & " - " & Booking.DateTo.ToString("dd/MM/yyyy HH:mm") & " - " & Booking.Bedroom & """>" & Booking.BookingName & "</div>"
If Booking.HasClash And Not First Then
' We need an insert row here because it's a clash for the same room
InsertRows &= "<tr style=""height:30px;""><td></td><td style=""position:relative;"" colspan=""" & TotalDays * 24 & """>"
InsertRows &= Bar
InsertRows &= "</tr>"
Else
' Add to this row
s.AppendLine(Bar)
End If
First = False
Next
s.AppendLine("</td></tr>{INSERTROWS}")
' Insert our clashing rows
s = s.Replace("{INSERTROWS}", InsertRows)
Next
s.AppendLine("</table>")
lit_Bars.Text = s.ToString
End Sub
Protected Sub Page_Load(ByVal sender As Object, ByVal e As System.EventArgs) Handles Me.Load
' CREATE SOME FAKE BOOKINGS
Bookings.Add(New Booking("Bedroom 01", "Mr Jones", Date.Today.AddHours(9), Date.Today.AddHours(15)))
Bookings.Add(New Booking("Bedroom 02", "Mr & Mrs Williams", Date.Today.AddHours(8), Date.Today.AddHours(20)))
Bookings.Add(New Booking("Bedroom 03", "Mrs Ave", Date.Today.AddHours(10), Date.Today.AddHours(17)))
Bookings.Add(New Booking("Bedroom 04", "Mr Aubury", Date.Today.AddHours(12), Date.Today.AddHours(22)))
Bookings.Add(New Booking("Bedroom 05", "Mr King", Date.Today.AddHours(14), Date.Today.AddHours(20)))
' Clashes here
Bookings.Add(New Booking("Bedroom 06", "Miss Uber", Date.Today.AddHours(7), Date.Today.AddHours(13)))
Bookings.Add(New Booking("Bedroom 06", "Dr Jones", Date.Today.AddHours(6), Date.Today.AddHours(10)))
Bookings.Add(New Booking("Bedroom 06", "Mr Davis", Date.Today.AddHours(9), Date.Today.AddHours(30)))
Bookings.Add(New Booking("Bedroom 07", "Miss Davies", Date.Today.AddHours(8), Date.Today.AddHours(12)))
Bookings.Add(New Booking("Bedroom 08", "Mrs Amber", Date.Today.AddHours(6), Date.Today.AddHours(14)))
' Clashes here
Bookings.Add(New Booking("Bedroom 09", "Mr & Mrs Red", Date.Today.AddHours(10), Date.Today.AddHours(17)))
Bookings.Add(New Booking("Bedroom 09", "Mr Green", Date.Today.AddHours(2), Date.Today.AddHours(16)))
Bookings.Add(New Booking("Bedroom 09", "Mrs Brown", Date.Today.AddHours(7), Date.Today.AddHours(40)))
Bookings.Add(New Booking("Bedroom 10", "Mr Orange", Date.Today.AddHours(14), Date.Today.AddHours(19)))
Bookings.Add(New Booking("Bedroom 10", "Miss Pink", Date.Today.AddHours(26), Date.Today.AddHours(40)))
Bookings.Add(New Booking("Bedroom 11", "Miss Nathan", Date.Today.AddHours(13), Date.Today.AddHours(28)))
Bookings.Add(New Booking("Bedroom 12", "Mr Black", Date.Today.AddHours(7), Date.Today.AddHours(18)))
SortBookings()
RenderBookings()
End Sub
End Class

Well, i come up with a sort of solution, but it just seems very unnecessary coding. It doesn't deal with more than 2 entries on the same index at all, but i shouldn't need anymore than 2 assigned to each index
if anyone has any better ideas, love to here it.
For Each bar In lst
If prevFromDT < bar.ToTime And prevToDT > bar.FromTime And prevIndex = bar.PosIndex Then
bar.overLap = True
End If
next
For Each tsk In lst
If tsk.overLap Then
For Each tsk1 In lst
If tsk1.PosIndex > tsk.PosIndex Then tsk1.PosIndex += 1
Next
End If
Next
For Each tsk In lst
If tsk.overLap Then
tsk.PosIndex += 1
End If
Next

Related

Removing duplicates in Text Box and adding the corresponding values

I have a VB form with three TextBoxes. Here's an example of what I'd like the program to achieve:
So, that's the form ... the program sorts a text file and gets names, goals, and positions. E.g.
Jordan 26 Center
James 10 Mid
Jordan 4 Center
Jack 6 Forward
James 10 Mid
When the update button is clicked, the program should realize that James and Jordan are written twice, remove one of them and add their goals, so it should output:
Jordan 30 Center
James 20 Mid
Jack 6 Forward
To do this I've had the data transferred into ListBoxes which makes it easier to remove duplicates, the data is then transferred back into a multi-line TextBox so it is editable. Here's my code so far. It either gives the wrong results or an index out of range error.
Dim Count1 As Integer
Dim Count2 As Integer
Dim Count3 As Integer
Dim NewInt As Integer
Dim ValOne As Integer
Dim ValTwo As Integer
ListBox1.Items.Clear()
ListBox2.Items.Clear()
ListBox3.Items.Clear()
NewInt = 0
ValOne = 0
ValTwo = 0
ListBox1.Items.AddRange(Players.Text.Split(vbNewLine))
ListBox2.Items.AddRange(Goals.Text.Split(vbNewLine))
ListBox3.Items.AddRange(Positions.Text.Split(vbNewLine))
Count1 = ListBox1.Items.Count
Count2 = ListBox2.Items.Count
Count3 = ListBox3.Items.Count
If Count1 = Count2 And Count1 = Count3 And Count2 = Count3 Then
'Set two counters to compare all words with each other
For iFirstCounter As Integer = 0 To ListBox1.Items.Count - 1
For iSecondCounter As Integer = 0 To ListBox1.Items.Count - 1
'Make sure there will not be an 'out of range' error,
'because you are removing items from the listbox.
iSecondCounter = Convert.ToInt64(iSecondCounter)
iFirstCounter = Convert.ToInt64(iFirstCounter)
ListBox2.Items.RemoveAt(iSecondCounter)
ListBox2.Items.RemoveAt(iFirstCounter)
If iFirstCounter < iSecondCounter Then
ListBox2.Items.Insert(iFirstCounter, NewInt.ToString)
Else
ListBox2.Items.Insert(iSecondCounter, NewInt.ToString)
End If
Next
Next
Players.Text = ""
Goals.Text = ""
Positions.Text = ""
Dim i As Integer
For i = 0 To ListBox1.Items.Count - 1
If Players.Text = "" Then
Players.Text = ListBox1.Items(i)
Else
Players.Text = Players.Text & vbNewLine & ListBox1.Items(i)
End If
Next
Dim a As Integer
For a = 0 To ListBox2.Items.Count - 1
If Goals.Text = "" Then
Goals.Text = ListBox2.Items(a)
Else
Goals.Text = Goals.Text & vbNewLine & ListBox2.Items(a)
End If
Next
Dim b As Integer
For b = 0 To ListBox3.Items.Count - 1
If Positions.Text = "" Then
Positions.Text = ListBox3.Items(b)
Else
Positions.Text = Positions.Text & vbNewLine & ListBox3.Items(b)
End If
Next
Else
MessageBox.Show("The Text Boxes don't contain an equal number of values ... please add more/remove some values")
End If
Could be done in multiple ways, for example:
If TextBox2.Lines.Count > 1 Then
Dim LineList As List(Of String) = TextBox2.Lines.ToList 'textbox lines
Dim NewLines As List(Of String) = TextBox2.Lines.ToList 'can't edit list we're looping over, a copy of lines
Dim NamesList As New List(Of String)
For x = 0 To LineList.Count - 1
Dim linesplit As String() = LineList(x).Split({" "}, StringSplitOptions.RemoveEmptyEntries)
If NamesList.Contains(linesplit(0)) Then
NewLines.Remove(LineList(x))
Else
NamesList.Add(linesplit(0))
End If
Next
TextBox2.Lines = NewLines.ToArray
End If
Here's an example of code that does this via LINQ and Lambdas.
Module Module1
Sub Main()
Dim ungroupedPlayers(1) As String
ungroupedPlayers(0) = "Jordan 26 Center"
ungroupedPlayers(1) = "Jordan 4 Center"
Dim players = ungroupedPlayers.ToList().ConvertAll(Of Player)(Function(x As String) As Player
Dim split() As String = x.Split(" "c)
Dim p As New Player
p.PlayerName = split(0)
p.Count = split(1)
p.Position = split(2)
Return p
End Function)
Dim playersGrouped = From p In players
Group By PlayerName = p.PlayerName Into g = Group
Select PlayerName, Count = g.Sum(Function(ip As Player) ip.Count), Position = g.Min(Function(ip As Player) ip.Position.ToString())
Dim groupedPlayers() As String = playersGrouped.ToList().ConvertAll(Of String)(Function(ip)
Return ip.PlayerName.ToString() & " " & ip.Count.ToString() & " " & ip.Position.ToString()
End Function).ToArray()
For Each groupedPlayer as String in groupedPlayers
Console.WriteLine(groupedPlayer)
Next
Console.Read()
End Sub
Public Class Player
Public PlayerName As String
Public Count As Integer
Public Position As String
End Class
End Module
You don't need heavy ListBox control for working with players data.
Use List(Of T) and create class Player for better readability.
You can remove duplicates before you will display values in your form.
And instead of multiline textbox you can use DataGridView as "right tool for the editing data".
Public Class Player
Public Property Name As String
Public Property Position As String
Public Property Goals As Integer
End
Public Class PlayersForm : Form
Private Sub Form_Load(sender As Object, e As System.EventArgs) Handles MyBase.Load
Dim data As List(Of Player) = LoadPlayersData()
Dim players As List(Of Player) = NormalizeData(data)
' Use DataGridView
Me.DataGridView1.DataSource = players
End Sub
Private Function LoadPlayersData() As List(Of Player)
Dim rawData As String() = File.ReadAllLines("pathToTextFile")
Return rawData.Select(Function(line) LineToPlayer(line)).ToList()
End Function
Private Function NormalizeData(players As List(Of Player)) As List(Of Player)
Return players.Group(Function(player) player.Name)
.Select(Function(group)
Return New Player With
{
.Name = group.Key,
.Position = group.First().Position,
.Goals = group.Sum(Function(player) player.Goals)
}
End Function)
.ToList()
End Function
Private Function LineToPlayer(line As String) As Player
Dim values = line.Split(" "c)
Return New Player With
{
.Name = values(0),
.Position = values(2),
.Goals = Integer.Parse(values(1))
}
End Function
End Class
DataGridView control will automatically update your List(Of Players) when you make any change. which give you possibility to have some other controls which automatically display best scorers for example, without extra converting data from string to integer and back.

VB.net, How to Combine or Joining Nearest Similar Items in List of String

How to Combine or Joining nearest items are Similar for example
Dim _lstOfFontsList As New List(Of String)
From {"Arial", "Arial", "Arial", "GEORGIA", "Arial",
"Arial", "GEORGIA", "GEORGIA",
"ABOlD", "ABOlD", "Arial"}
_lstOfFontsList has 11 values itself, here 0,1,2 items are similar and 5,6 and 7,8 and 9,10 items are similar here i want to group the similar items so tried to groupby funtion like below
Dim Cntdd = _lstOfFontsList.GroupBy(Function(S) S).ToDictionary
(Function(a) a.Key, Function(a) a.Count)
but output is like below
[Arial, 6]
[GEORGIA, 3]
[ABOlD, 2]
but expected output is
Arial = 3
Georgia =1
Arial=2
Georgia=2
Abold=2
Arial=1
how to combine or group or join nearest (Next) similar items in list of string
Update :
i am trying to Loop condition for getting above values but the expected result won't coming here is the code
Dim _fntCnt% = 0
Dim _finalItms As New List(Of String)
Dim _finalFntName$ = ""
Dim LASTITEMS As New List(Of String)
For i = 0 To _lstOfFontsList.Count - 1
If i = 0 Then
_fntCnt += 1
_finalItms.Add(_lstOfFontsList(i) & "->" & _fntCnt)
_finalFntName$ = _lstOfFontsList(i)
End If
Trace.WriteLine(i)
If i <> _lstOfFontsList.Count - 1 Then
If _lstOfFontsList(i) = _lstOfFontsList(i + 1) Then
_fntCnt += 1
_finalItms.Add(_lstOfFontsList(i) & "->" & _fntCnt)
Else
For Each _itmm In _finalItms
LASTITEMS.Add(_itmm & " " & _finalFntName.Count)
Next
_finalItms.Clear()
End If
End If
Next
please can anybody help
I'm sure there must be simpler solutions, but this shows the working behind the idea too. As JM said you need a loop and a check to see if the item has more to come. You can't use a dictionary in this case as you need unique values, so we must use a list (of pair).
Hope this makes sense and helps!
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim _lstOfFontsList As New List(Of String) _
From {"Arial", "Arial", "Arial", "GEORGIA", "Arial",
"Arial", "GEORGIA", "GEORGIA",
"ABOlD", "ABOlD", "Arial"}
Dim Output As List(Of KeyValuePair(Of String, Integer)) = New List(Of KeyValuePair(Of String, Integer))
Dim Count As Integer = 1
For i = 0 To _lstOfFontsList.Count - 1
Dim Itm As String = _lstOfFontsList(i)
'Erro trap as the last i will error as there is no next item at the end of the list
Dim Nxt_Itm As String = Nothing
If i + 1 < _lstOfFontsList.Count Then Nxt_Itm = _lstOfFontsList(i + 1)
If Itm = Nxt_Itm Then
'Same as next - so we +1 to the count
Count += 1
Else
'Not same, so we need to add to the collection and reset the count to 1
Output.Add(New KeyValuePair(Of String, Integer)(Itm, Count))
Count = 1
End If
Next
For Each pair In Output
Debug.WriteLine(pair.Key & " | " & pair.Value)
'Will write "Arial | 3" for example...
Next
End Sub
Hth
Chicken

Programming a game loop for single player Blackjack

I've been working on a game of blackjack on visual basic but have been stuck on a particular task which states:
"Create a game loop to play a one-player version of the game. The game should end with the player going bust or holding on a valid score under 22."
I have gotten to code working up until that point with subroutines that manage "shuffling" the stack of figurative cards, dealing a card, checking if the player has busted or not and one that processes the player's turn.
(The commented out section is my attempt at the game loop, it does not work as once i input S or T it carries on asking if i want to stick or twist over and over)
Here is my full code:
Module Module1
Sub Main()
'task 1
Dim deck() As String = {"AH", "2H", "3H", "4H", "5H", "6H", "7H", "8H",
"9H", "10H", "JH", "QH", "KH",
"AD", "2D", "3D", "4D", "5D", "6D", "7D", "8D", "9D", "10D", "JD", "QD", "KD",
"AS", "2S", "3S", "4S", "5S", "6S", "7S", "8S", "9S", "10S", "JS", "QS", "KS",
"AC", "2C", "3C", "4C", "5C", "6C", "7C", "8C", "9C", "10C", "JC", "QC", "KC"}
Dim hand As New List(Of String)
Dim cardPile As New Stack(Of String)
cardPile = Shuffle(deck)
Dealing(cardPile, hand)
Dealing(cardPile, hand)
Dim BustOrNot As String
BustOrNot = Left(Bust(hand), 2)
Dim total As Integer
total = Mid(Bust(hand), 3, Bust(hand).Length)
If total = 21 Then
BlackJack(total)
End If
Console.WriteLine("Your total so far is: " & total)
'Dim stickOrTwist As Boolean
'stickOrTwist = PlayerTurn(hand)
'While BustOrNot = "NB" Or stickOrTwist = False Or total = 21
'Dealing(cardPile, hand)
'BustOrNot = Left(Bust(hand), 2)
'stickOrTwist = PlayerTurn(hand)
'End While
'If stickOrTwist = False Then
'Win(total)
'ElseIf BustOrNot = "BB" Then
'Lose(total)
'ElseIf total = 21 Then
'BlackJack(total)
'End If
Console.ReadKey()
End Sub
Function Shuffle(ByVal deck As Array)
'task 2
Dim rand As New Random()
Dim card As String
Dim card2 As String
Dim indexToShuffle As Integer
Dim indexToShuffle2 As Integer
Dim cardStack As New Stack(Of String)
For i = 1 To 1000
indexToShuffle = rand.Next(0, 51)
card = deck(indexToShuffle)
indexToShuffle2 = rand.Next(0, 51)
card2 = deck(indexToShuffle2)
deck(indexToShuffle2) = card
deck(indexToShuffle) = card2
Next
For j = 0 To 51
cardStack.Push(deck(j))
Next
Return cardStack
End Function
Sub Dealing(ByRef cardPile As Stack(Of String), hand As List(Of String))
'task 3
hand.Add(cardPile.Pop)
End Sub
Function Bust(ByVal hand As List(Of String))
'task 4
Dim total As Integer = 0
For Each item In hand
If IsNumeric(Left(item, 1)) Then
total = total + Left(item, 1)
ElseIf Left(item, 1) = "A" Then
total = total + 11
ElseIf Left(item, 1) = "J" Or Left(item, 1) = "Q" Or Left(item, 1) = "K" Then
total = total + 10
End If
Next
If total > 21 Then
Return ("BB" & total)
Else
Return ("NB" & total)
End If
End Function
Function PlayerTurn(ByRef hand As List(Of String))
'task 5
Console.WriteLine("Would you like to stick or twist? (Input S or T)")
Dim stickOrTwist As String = UCase(Console.ReadLine())
If stickOrTwist = "S" Then
Return False
ElseIf stickOrTwist = "T" Then
Return True
Else
Console.WriteLine("That isn't a valid input. Try again")
Console.WriteLine("...")
PlayerTurn(hand)
End If
End Function
Sub Lose(ByVal total As Integer)
Console.WriteLine("Oh no! you lose.")
Console.WriteLine("Your final score was: " & total)
Console.ReadKey()
End Sub
Sub Win(ByVal total As Integer)
Console.WriteLine("Well done! your score is: " & total)
Console.ReadKey()
End Sub
Sub BlackJack(ByVal total As Integer)
Console.WriteLine("Well done you scored blackjack! Your score is: " & total)
Console.ReadKey()
End Sub
End Module

Getting wrong output in label

I have code that I have written, that has 3 labels for number of hurricanes, average hurricanes, and the year with the most hurricanes from a txt file. The code is working and the first 2 labels are displaying the correct results. However the last label is displaying the number of the year with the most hurricanes instead of the year.
Here is what I have:
Option Strict On
Public Class frmHurricaneStatistics
' Class level Private variables.
Public Shared _intSizeOfArray As Integer = 20
Private _strYears(_intSizeOfArray) As String
Private _intNumberOfHurricans(_intSizeOfArray) As Integer
Private Sub frmHurricaneStatistics_Load(sender As Object, e As EventArgs
) Handles MyBase.Load
' This load event reads the inventory text file and fills
' the ComboBox object with the Hurricane Statistics.
' Initialize an instace of the streamreader object and declare variables.
Dim objReader As IO.StreamReader
Dim strHurricaneStatistics As String = "Hurricanes.txt"
Dim intCount As Integer = 0
Dim intFill As Integer
Dim strFileError As String = "The file is not available. Please restart the
application when the file is available."
' Verify the Hurricane.txt file exists.
If IO.File.Exists(strHurricaneStatistics) Then
objReader = IO.File.OpenText(strHurricaneStatistics)
' Read the file line by line until the file is completed.
Do While objReader.Peek <> -1
_strYears(intCount) = objReader.ReadLine()
_intNumberOfHurricans(intCount) = Convert.ToInt32(objReader.ReadLine())
intCount += 1
Loop
objReader.Close()
' The ComboBox objext is filled with the Years for Hurricanes.
For intFill = 0 To (_strYears.Length - 1)
cmbYears.Items.Add(_strYears(intFill))
Next
Else
MsgBox(strFileError, , "Error")
Close()
' If ComboBox is filled then enable the Display Statistics button.
' btnDisplayStatistics.Enabled = True
End If
End Sub
Private Sub btnDisplayStatistics_Click(sender As Object, e As EventArgs
) Handles btnDisplayStatistics.Click
' This click event calls the sub procedures for the selected years and
' the number of hurricans in that year.
Dim intSelectedYear As Integer
Dim strMissingSelection As String = "Missing Selection"
Dim strSelectAYearError As String = "Please Select a Year"
' If the ComboBox object has a selection, Display Statistics.
If cmbYears.SelectedIndex >= 0 Then
intSelectedYear = cmbYears.SelectedIndex
Else
MsgBox(strSelectAYearError, , strMissingSelection)
End If
' The procedure MakeLabelsVisible Is called to display the labels
' And the results.
MakeLabelsVisible()
Dim intAverage As Double
Dim intYear As Integer
For intIndex As Integer = 0 To _intNumberOfHurricans.Length - 1
If intYear < _intNumberOfHurricans(intIndex) Then
intYear = _intNumberOfHurricans(intIndex)
End If
intAverage = intAverage + _intNumberOfHurricans(intIndex)
Next
intAverage = intAverage / _intNumberOfHurricans.Length
' Display the statistics for the Storm Average in the selected Year
' and the most active year within the range of year.
lblNumberOfHurricanes.Text = "The Number of Hurricanes in the Year " &
_strYears(intSelectedYear) & " is " & _intNumberOfHurricans(intSelectedYear).ToString() & "."
lblAvergeNumberHurricanes.Text = "The Average Number of Storms was " & FormatNumber(intAverage, 0) & " Hurricanes."
lblMostStorms.Text = "The Year " & intYear & " Had The Most Storms Between " & (
_strYears(20) & " And " & (_strYears(0).ToString))
End Sub
Private Sub MakeLabelsVisible()
' This procedure displays the labels with the calculated results
lblNumberOfHurricanes.Visible = True
lblAvergeNumberHurricanes.Visible = True
lblMostStorms.Visible = True
End Sub
Updated code.
Looks like you're just populating intYear with the number of hurricanes?
intYear = _intNumberOfHurricans(intIndex)
I can't see where you're wanting to get a year value from. Does one even exist? Please post the rest of the code
Edit:
From what I understand (correct me if I'm wrong), you want the year that had the highest number of hurricanes? If so
Try
For intIndex As Integer = 0 To _intNumberOfHurricans.Length - 1
If _intNumberOfHurricans(intIndex) = _intNumberOfHurricans.Max Then
intYear = Integer.Parse(_strYears(intIndex))
End If
intAverage = intAverage + _intNumberOfHurricans(intIndex)
Next
What I'm doing here is looking for the highest value in _intNumberOfHurricans and comparing it to the number of hurricanes in the current iteration. If they're the same, then we are at the year with the highest number of hurricanes, so we populate intYear with _strYears(but as an Integer).
This code isn't perfect. For example, if the highest amount of hurricanes is 100, but there are 2 years where there are 100 hurricanes, it will only give the latest year, not the first year there were 100 hurricanes.
Because you set;
intYear = _intNumberOfHurricans(intIndex)
Not the year, number of hurricans. That should have point to a Year property.
intYear = _intNumberOfHurricans(intIndex).Year
Hope helps.

AQA AS Level Problems - VB - Comp 1

I seem to be having trouble with my program working and I am finding it hard to understand what I have done wrong, first of all I need a simple ( not really complicated) way of checking that the user cannot enter a string or a number over the requested amount (which currently is 1- 9 for menu options and 10 for a save option - which I need to do later) The code below is the code for the number and string checker relating to the menu and the code below the line is the whole code.
I have tried doing this but it just loops when you enter it for the row and lets you through whatever number you enter on the column. I need help also on other question relating to this like
Telling the user what ship they have hit,
Saving and Loading the game
And a score counter - I had this working then it got deleted when trying to fix first question
And a limit on the amount of goes they can have.
I will upload the code required tomorrow as cannot now, But if anybody has access to the AQA As Level free pseudocode that they give you - (its not illegal ! ) Please help me !
Sub GetRowColumn(ByRef Row As Integer, ByRef Column As Integer) ' Asks the user about where they want to go in the code
Console.WriteLine()
Dim checkcol, checkrow As String ' Defining the variables that I will user later
Dim AscCol, AscRow As Integer
Console.Write("Please enter a column:") ' Asks users to enter a column
checkcol = Console.ReadLine()
AscCol = Asc(checkcol(0)) ' It will check it on the ASCII scale to see if it isnt a letter
While AscCol > 57 Or AscCol < 48 ' If it doesnt fit in here, it is not one of the alloacated numbers
Console.WriteLine("This is not a number.")
Console.Write("Please enter a column")
checkcol = Console.ReadLine() ' Does the same for checkcol
AscCol = Asc(checkcol(0))
End While
checkcol = ((Chr(AscCol)))
Column = CInt(checkcol)
Console.WriteLine() ' This is a printed space for spacing when printed as a code
Do
If Column < 0 Or Column > 9 Then ' Now if it fits the column alloation e.g. 1 to 9 it will be allowed through
Console.WriteLine()
Console.WriteLine(" That is an invalid Input") ' Tell the user that they cannot go through as it doesn't fit the right requrirments
Column = Console.ReadLine()
End If
Console.WriteLine()
Loop Until Column < 10 And Column >= 0 ' This part of the code will run until their answer is under 10 and over 0
Console.Write("Please enter a row:") ' Here is same for rows as it is for columns
checkrow = Console.ReadLine()
AscRow = Asc(checkrow(0))
While AscRow > 57 Or AscRow < 48
Console.WriteLine("This is not a number.")
Console.Write("Please enter a row")
AscRow = Asc(checkrow(0))
End While
Row = CInt(checkrow)
Do
If Row < 0 Or Row > 9 Then
Console.WriteLine()
Console.WriteLine("That is an invalid Input.")
End If
Console.WriteLine()
Loop Until Row < 10 And Row >= 0
End Sub
Other code
'Skeleton Program for the AQA AS Paper 1 Summer 2016 examination
'this code should be used in conjunction with the Preliminary Material
'written by the AQA Programmer Team
'developed in the Visual Studio 2008 programming environment
'Version Number 1.0
Imports System.IO
Module Module1
Const TrainingGame As String = "Training.txt" ' Calls the training text file used by new players
Structure TShip ' Starts a new structure for use later that includes a stringed name and a size as an integer
Dim Name As String
Dim Size As Integer
End Structure
Sub MakePlayerMove(ByRef Board(,) As Char, ByRef Ships() As TShip) ' This part of the code advances on their column and row selection from earlier
Dim Row As Integer
Dim Column As Integer
GetRowColumn(Row, Column)
If Board(Row, Column) = "m" Or Board(Row, Column) = "h" Then ' m is miss h is a hit
Console.WriteLine("Sorry, you have already shot at the square (" & Column & "," & Row & "). Please try again.")
ElseIf Board(Row, Column) = "-" Then ' Message to user to say that they have shot in a sqaure they habe already shot in
Console.WriteLine("Sorry, (" & Column & "," & Row & ") is a miss.")
Board(Row, Column) = "m"
Else
Console.WriteLine("Hit at (" & Column & "," & Row & ").")
Board(Row, Column) = "h"
End If
End Sub
Sub SetUpBoard(ByRef Board(,) As Char)
Dim Row As Integer
Dim Column As Integer
For Row = 0 To 9
For Column = 0 To 9
Board(Row, Column) = "-"
Next
Next
End Sub
Sub LoadGame(ByVal Filename As String, ByRef Board(,) As Char)
Dim Row As Integer
Dim Column As Integer
Dim Line As String
Using FileReader As StreamReader = New StreamReader(Filename)
For Row = 0 To 9
Line = FileReader.ReadLine()
For Column = 0 To 9
Board(Row, Column) = Line(Column)
Next
Next
End Using
End Sub
Sub PlaceRandomShips(ByRef Board(,) As Char, ByVal Ships() As TShip)
Dim Valid As Boolean
Dim Row As Integer
Dim Column As Integer
Dim Orientation As Char
Dim HorV As Integer
For Each Ship In Ships
Valid = False
While Not Valid
Row = Int(Rnd() * 10)
Column = Int(Rnd() * 10)
HorV = Int(Rnd() * 2)
If HorV = 0 Then
Orientation = "v"
Else
Orientation = "h"
End If
Valid = ValidateBoatPosition(Board, Ship, Row, Column, Orientation)
End While
Console.WriteLine("Computer placing the " & Ship.Name)
PlaceShip(Board, Ship, Row, Column, Orientation)
Next
End Sub
Sub PlaceShip(ByRef Board(,) As Char, ByVal Ship As TShip, ByVal Row As Integer, ByVal Column As Integer, ByVal Orientation As Char)
Dim Scan As Integer
If Orientation = "v" Then
For Scan = 0 To Ship.Size - 1
Board(Row + Scan, Column) = Ship.Name(0)
Next
ElseIf Orientation = "h" Then
For Scan = 0 To Ship.Size - 1
Board(Row, Column + Scan) = Ship.Name(0)
Next
End If
End Sub
Function ValidateBoatPosition(ByVal Board(,) As Char, ByVal Ship As TShip, ByVal Row As Integer, ByVal Column As Integer, ByVal Orientation As Char)
Dim Scan As Integer
If Orientation = "v" And Row + Ship.Size > 10 Then
Return False
ElseIf Orientation = "h" And Column + Ship.Size > 10 Then
Return False
Else
If Orientation = "v" Then
For Scan = 0 To Ship.Size - 1
If Board(Row + Scan, Column) <> "-" Then
Return False
End If
Next
ElseIf (Orientation = "h") Then
For Scan = 0 To Ship.Size - 1
If Board(Row, Column + Scan) <> "-" Then
Return False
End If
Next
End If
End If
Return True
End Function
Function CheckWin(ByVal Board(,) As Char)
Dim Row As Integer
Dim Column As Integer
For Row = 0 To 9
For Column = 0 To 9
If Board(Row, Column) = "A" Or Board(Row, Column) = "B" Or Board(Row, Column) = "S" Or Board(Row, Column) = "D" Or Board(Row, Column) = "P" Then
Return False
End If
Next
Next
Return True
End Function
Sub PrintBoard(ByVal Board(,) As Char)
Dim Row As Integer
Dim Column As Integer
Console.WriteLine()
Console.WriteLine("The board looks like this: ")
Console.WriteLine()
Console.Write(" ")
For Column = 0 To 9
Console.Write(" " & Column & " ")
Next
Console.WriteLine()
For Row = 0 To 9
Console.Write(Row & " ")
For Column = 0 To 9
If Board(Row, Column) = "-" Then
Console.Write(" ")
ElseIf Board(Row, Column) = "A" Or Board(Row, Column) = "B" Or Board(Row, Column) = "S" Or Board(Row, Column) = "D" Or Board(Row, Column) = "P" Then
Console.Write(" ")
Else
Console.Write(Board(Row, Column))
End If
If Column <> 9 Then
Console.Write(" | ")
End If
Next
Console.WriteLine()
Next
End Sub
Sub DisplayMenu()
Console.WriteLine("MAIN MENU") ' Main Menu Screen that is displayed to the user
Console.WriteLine()
Console.WriteLine("1. Start new game")
Console.WriteLine("2. Load training game")
Console.WriteLine(" 3. Change game limit")
Console.WriteLine("4. Load Saved Game")
Console.WriteLine("9. Quit")
Console.WriteLine()
End Sub
Function GetMainMenuChoice() ' Will check if the menu choice is picked can go through
Dim Choice As Integer ' Dim choice as an integer
Try
Console.Write("Please enter your choice: ") ' Ask user to enter their choice for the menu option
Choice = Console.ReadLine() ' User enters here
Console.WriteLine()
If Choice <> "1" And Choice <> "2" And Choice <> "9" And Choice <> "10" Then
Console.WriteLine("ERROR: Invalid input!") ' If their choice doesnt fit 1, 2 or 9 then it says this message
End If
Return Choice ' Return the choice to another part of code
Catch Ex As Exception
Console.WriteLine("Please enter a valid input (1, 2,9 or 10)")
End Try
End Function
Sub PlayGame(ByVal Board(,) As Char, ByVal Ships() As TShip)
Dim GameWon As Boolean = False
Dim score As Integer = 0
Dim gamelimit As Integer = 50
Do
PrintBoard(Board)
MakePlayerMove(Board, Ships)
score = score + 1
Console.WriteLine("You have taken {0} number of moves,", score)
GameWon = CheckWin(Board)
If GameWon Then
Console.WriteLine("All ships sunk!")
Console.WriteLine()
End If
Loop Until GameWon Or score = 50
If score = 50 Then
Console.WriteLine("You used all your moves up. Try again ")
End If
End Sub
Sub SaveGame(ByRef Board(,) As Char)
Dim SaveGameWrite As StreamWriter
SaveGameWrite = New StreamWriter("TEST.txt", True)
For x As Integer = 0 To 9
For y As Integer = 0 To 9
SaveGameWrite.Write(Board(x, y))
Next
Next
SaveGameWrite.Close()
End Sub
Sub LoadSavedGame(ByVal Filename As String, ByRef Board(,) As Char)
Dim Row, Column As Integer
Dim Line As String
Console.WriteLine("Load training game or open a saved game? T for training or S for saved")
If Console.ReadLine = "" Then
Console.WriteLine("Enter the filename: ")
Filename = Console.ReadLine
End If
Using FileReader As StreamReader = New StreamReader("C:\" & Filename)
For Row = 0 To 9
Line = FileReader.ReadLine()
For Column = 0 To 9
Board(Row, Column) = Line(Column)
Next
Next
End Using
End Sub
Sub SetUpShips(ByRef Ships() As TShip)
Ships(0).Name = "Aircraft Carrier"
Ships(0).Size = 5
Ships(1).Name = "Battleship"
Ships(1).Size = 4
Ships(2).Name = "Submarine"
Ships(2).Size = 3
Ships(3).Name = "Destroyer"
Ships(3).Size = 3
Ships(4).Name = "Patrol Boat"
Ships(4).Size = 2
End Sub
Sub Main()
Dim Board(9, 9) As Char
Dim Ships(4) As TShip
Dim MenuOption As Integer
Do
SetUpBoard(Board)
SetUpShips(Ships)
DisplayMenu()
MenuOption = GetMainMenuChoice()
If MenuOption = 1 Then
PlaceRandomShips(Board, Ships)
PlayGame(Board, Ships)
ElseIf MenuOption = 2 Then
LoadGame(TrainingGame, Board)
PlayGame(Board, Ships)
ElseIf MenuOption = 3 Then
PlaceRandomShips(Board, Ships)
PlayGame(Board, Ships)
End If
Loop Until MenuOption = 9
End Sub
End Module
Thanks in advance,
The Scottish Warrior