-
Notifications
You must be signed in to change notification settings - Fork 0
/
day21-visual-basic.vb
206 lines (163 loc) · 6.3 KB
/
day21-visual-basic.vb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
Imports System
Module Program
Sub Main()
' Inputs go here
Dim p1 = 7
Dim p2 = 10
Console.WriteLine($"Task 1: {Task1(p1, p2)}")
Console.WriteLine($"Task 2: {Task2(p1, p2)}")
End Sub
Class GameState
Public ReadOnly P1 As PlayerState
Public ReadOnly P2 As PlayerState
Public ReadOnly Player1Turn As Boolean
Public Sub New(p1 As Long, p2 As Long)
Me.New(New PlayerState(p1, 0), New PlayerState(p2, 0), True)
End Sub
Public Sub New(p1 As PlayerState, p2 As PlayerState, player1Turn As Boolean)
Me.Player1Turn = player1Turn
Me.P1 = p1
Me.P2 = p2
End Sub
Public ReadOnly Property CurrentPlayer As PlayerState
Get
Return If(Player1Turn, P1, P2)
End Get
End Property
Public ReadOnly Property PreviousPlayer As PlayerState
Get
Return If(Player1Turn, P2, P1)
End Get
End Property
Function Updated(a As Long) As GameState
If Player1Turn Then
Return New GameState(P1.Updated(a), P2, Not Player1Turn)
Else
Return New GameState(P1, P2.Updated(a), Not Player1Turn)
End If
End Function
Public Overrides Function Equals(obj As Object) As Boolean
If Not TypeOf obj Is GameState Then Return False
Dim ps = CType(obj, GameState)
Return P1.Equals(ps.P1) And P2.Equals(ps.P2) And Player1Turn = ps.Player1Turn
End Function
Public Overrides Function GetHashCode() As Integer
Return HashCode.Combine(P1, P2, Player1Turn)
End Function
Public Overrides Function ToString() As String
Return $"P1: {P1}; P2: {P2}; Turn: {If(Player1Turn, 1, 2)}"
End Function
End Class
Class PlayerState
Public ReadOnly Position As Long = 0
Public ReadOnly Score As Long = 0
Public Sub New(position As Long, score As Long)
Me.Position = position
Me.Score = score
End Sub
Function Updated(a As Long) As PlayerState
Dim pos = Position + a
While pos > 10
pos -= 10
End While
Return New PlayerState(pos, Score + pos)
End Function
Public Overrides Function ToString() As String
Return $"({Position}, {Score})"
End Function
Public Overrides Function Equals(obj As Object) As Boolean
If Not TypeOf obj Is PlayerState Then Return False
Dim ps = CType(obj, PlayerState)
Return Position.Equals(ps.Position) And Score.Equals(ps.Score)
End Function
Public Overrides Function GetHashCode() As Integer
Return Position.GetHashCode() Xor Score.GetHashCode()
End Function
End Class
Private Class DeterministicDie
Dim _die = 0
Dim _rolls = 0
Public ReadOnly Property Rolls As Long
Get
Return _rolls
End Get
End Property
Private Function Roll() As Long
_die += 1
_rolls += 1
If _die > 100
_die = 1
End If
Return _die
End Function
Function Roll3() As Long
return Roll() + Roll() + Roll()
End Function
End Class
Private Function Task1(p1 As Long, p2 As Long) As Long
Dim die = New DeterministicDie()
Dim game = New GameState(p1, p2)
While True
game = game.Updated(die.Roll3())
If game.P1.Score >= 1000 Then Exit While
game = game.Updated(die.Roll3())
If game.P2.Score >= 1000 Then Exit While
End While
Return Math.Min(game.P1.Score, game.P2.Score)*die.Rolls
End Function
Private Function Expand(ByRef states As SortedDictionary(Of GameState, Long)) As Boolean
Dim unexpandedStates = states.
Where(Function(pair) Not pair.Key.P1.Score >= 21 And
Not pair.Key.P2.Score >= 21)
If not unexpandedStates.Any() Then Return False
Dim nextExpansion = unexpandedStates.First()
Dim count = nextExpansion.Value
Dim state = nextExpansion.Key
For i = 1 To 3
For j = 1 To 3
For k = 1 To 3
Dim newState = state.Updated(i + j + k)
If Not states.ContainsKey(newState) Then
states.Add(newState, 0)
End If
states.Item(newState) += count
Next
Next
Next
states.Remove(nextExpansion.Key)
Return True
End Function
Private Class GameStateComparer
Implements IComparer(of GameState)
Public Function Compare(x As GameState, y As GameState) As Integer _
Implements IComparer(Of GameState).Compare
Dim a
a = x.CurrentPlayer.Score.CompareTo(y.CurrentPlayer.Score)
If a <> 0 Then Return a
a = x.PreviousPlayer.Score.CompareTo(y.PreviousPlayer.Score)
If a <> 0 Then Return a
a = x.CurrentPlayer.Position.CompareTo(y.CurrentPlayer.Position)
If a <> 0 Then Return a
a = x.PreviousPlayer.Position.CompareTo(y.PreviousPlayer.Position)
If a <> 0 Then Return a
Return x.Player1Turn.CompareTo(y.Player1Turn)
End Function
End Class
Private Function Task2(p1 As Long, p2 As Long) As Long
Dim states As New SortedDictionary(Of GameState, Long)(New GameStateComparer())
states.Add(New GameState(p1, p2), 1)
While Expand(states)
End While
Dim player1Wins = states.
Where(Function(pair) pair.Key.P1.Score > pair.Key.P2.Score).
Select(Function(pair) pair.Value).
Aggregate(Function (a, b) a + b)
Dim player2Wins = states.
Where(Function(pair) pair.Key.P2.Score > pair.Key.P1.Score).
Select(Function(pair) pair.Value).
Aggregate(Function (a, b) a + b)
' Console.WriteLine(player1Wins)
' Console.WriteLine(player2Wins)
Return Math.Max(player1Wins, player2Wins)
End Function
End Module