Skip to content

Instantly share code, notes, and snippets.

@JohnLBevan
Last active March 24, 2019 18:37
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save JohnLBevan/08b3bde2c00e0dd32e701e77e2fe297b to your computer and use it in GitHub Desktop.
Save JohnLBevan/08b3bde2c00e0dd32e701e77e2fe297b to your computer and use it in GitHub Desktop.
Simulation of the Coin Toss game, based on Matt Parker's Lecture https://youtu.be/6JwEYamjXpA?t=1870
#Simulation of https://youtu.be/6JwEYamjXpA?t=1870
function Get-CoinTossPossibleValues {@('H','T')}
function Get-CoinToss {
[CmdletBinding()]
Param (
[Parameter()]
[int]$NumberOfTosses = 1
,
[Parameter()]
[string[]]$PossibleValues = (Get-CoinTossPossibleValues)
)
Process {
@(1..$NumberOfTosses) | ForEach-Object {
Get-Random -InputObject $PossibleValues
}
}
}
function Get-CoinTossPrediction {
[CmdletBinding(DefaultParameterSetName = 'OpponentFirst')]
Param (
[Parameter(ParameterSetName = 'OpponentFirst')]
[ValidateScript({$_.Count -gt 0})]
[string[]]$OpponentsPrediction
,
[Parameter(ParameterSetName = 'MeFirst')]
[ValidateScript({$_ -gt 0})]
[int]$SequenceLength
,
[Parameter()]
[Switch]$Random #i.e.disable the intelligent/competitive answer, just throw out some values in any order
)
Process {
if ($Random.IsPresent) {
if ($PSCmdlet.ParameterSetName -eq 'OpponentFirst') {
$SequenceLength = $OpponentsPrediction.Count
}
1..$SequenceLength | ForEach-Object {Get-CoinToss}
} else {
if ($PSCmdlet.ParameterSetName -eq 'OpponentFirst') {
switch ($OpponentsPrediction.Count) {
0 { throw 'The opponent must make a prediction!' } #this will never happen thanks to validation above
1 { Get-CoinTossPossibleValues | Where-Object {$_ -ne $OpponentsPrediction[0]} | Select-Object -First 1 }
default {
if ($OpponentsPrediction[0] -eq $OpponentsPrediction[1]) {
Get-CoinTossPrediction -OpponentsPrediction @($OpponentsPrediction[0])
} else {
$OpponentsPrediction[0]
}
$OpponentsPrediction[0..($OpponentsPrediction.Count-2)]
}
}
} else {
$possibleValues = Get-CoinTossPossibleValues
0..($SequenceLength-1) | ForEach-Object {
#put in logic to mod from the list of possible values here
$possibleValues[$_ % $possibleValues.Count]
}
}
}
}
}
function Invoke-CoinTossGame {
[CmdletBinding(DefaultParameterSetName = 'PredefinedSequence')]
Param (
[Parameter(ParameterSetName = 'PredefinedSequence', Mandatory = $true)]
[string[]]$OpponentSequence
,
[Parameter(ParameterSetName = 'PredefinedSequence', Mandatory = $true)]
[string[]]$MySequence
#,
#at some point I may add other parametersets to allow sequences to be genertated within the game; i.e. to allow more realistic gameplay / taking turns / using the other approaches (e.g. random vs competitive, and user inputs), just so people can play around a bit more
,
[Parameter()]
[int]$BestOf = 1000
)
Begin {
if ($PSCmdlet.ParameterSetName -eq 'PredefinedSequence') {
if (Test-SequenceMatches -SequenceA $OpponentSequence -SequenceB $MySequence) {
throw @"
You must not guess the same as your opponent; that would result in a draw, which I can''t be bothered to add code for :/
Opponent Sequence $($OpponentSequence -join ', ')
My Sequence $($MySequence -join ', ')
"@
}
}
}
Process {
1..$BestOf | ForEach-Object {
[string[]]$Sequence = New-CoinTossSequence -StopWhenSequence @($OpponentSequence, $MySequence)
([PSCustomObject]@{
YouPlayed = $OpponentSequence -join ','
IPlayed = $MySequence -join ','
Actual = $Sequence -join ','
IWin = Test-SequenceMatches -SequenceA (Get-Tail -Sequence ([System.Collections.Generic.List[string]]::new($Sequence)) -Length $MySequence.Count) -SequenceB $MySequence
})
}
}
}
function Test-SequenceMatches {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[string[]]$SequenceA
,
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
[string[]]$SequenceB
)
Process {
[boolean]$result = $true
if ($SequenceA -eq $null) {throw 'Sequence A is null'}
if ($SequenceB -eq $null) {throw 'Sequence B is null'}
if ($SequenceA.Length -ne $SequenceB.Length) {
$result = $false
} else {
0..($SequenceA.Length-1) | ForEach-Object {
if ($SequenceA[$_] -ne $SequenceB[$_]) {$result = $false}
}
}
$result
}
}
function New-CoinTossSequence {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[string[][]]$StopWhenSequence #for now we'll just assume that all input sequences are the same length to avoid the overhead of checking each time
)
[System.Collections.Generic.List[string]]$Sequence = [System.Collections.Generic.List[string]]::new()
Get-CoinToss -NumberOfTosses $StopWhenSequence[0].Count | ForEach-Object {$Sequence.Add($_)}
while (-not ($StopWhenSequence | Test-SequenceMatches -SequenceA (Get-Tail -Sequence $Sequence -Length $StopWhenSequence[0].Count) | Where-Object {$_})) {
$newVal = (Get-CoinToss)
$Sequence.Add($newVal)
}
$Sequence.ToArray()
}
function Get-Tail {
[CmdletBinding()]
Param (
[Parameter(Mandatory = $true)]
[System.Collections.Generic.List[string]]$Sequence
,
[Parameter(Mandatory = $true)]
[int]$Length
)
Process {
$Sequence.GetRange(($Sequence.Count - $Length), $Length).ToArray()
}
}
function Invoke-CoinTossGameSimulation {
[CmdletBinding()]
Param()
Process {
[System.Collections.Generic.List[string[]]]$AllCombos = [System.Collections.Generic.List[string[]]]::new()
foreach ($a in (Get-CoinTossPossibleValues)) {foreach ($b in (Get-CoinTossPossibleValues)) {foreach ($c in (Get-CoinTossPossibleValues)) {
$AllCombos.Add(@($a,$b,$c))
}}}
$results = $AllCombos | ForEach-Object {
Write-Verbose "Testing Combo: $_"
$mySequence = Get-CoinTossPrediction -OpponentsPrediction $_
Invoke-CoinTossGame -OpponentSequence $_ -MySequence $mySequence
}
$results
$results | Group-Object YouPlayed, IPlayed | ForEach-Object {[PSCustomObject]@{
YouPlayed = $_.Group[0].YouPlayed
IPlayed = $_.Group[0].IPlayed
IWin = ($_.Group | Measure-Object IWin -Average | Select-Object -ExpandProperty Average).ToString("P")
Actual = 'TOTAL'
}}
[PSCustomObject]@{IWin = ($results | Measure-Object IWin -Average | Select-Object -ExpandProperty Average).ToString("P");YouPlayed='TOTAL';IPlayed='TOTAL';Actual='TOTAL'}
}
}
Clear-Host
Invoke-CoinTossGameSimulation -Verbose | Format-Table IWin, YouPlayed, IPlayed, Actual -AutoSize
<# Sample Output (truncated)
IWin YouPlayed IPlayed Actual
---- --------- ------- ------
True H,H,H T,H,H H,T,H,H
True H,H,H T,H,H T,T,T,T,T,T,T,T,H,H
True H,H,H T,H,H H,T,H,T,H,H
True H,H,H T,H,H T,T,H,T,T,T,H,T,T,H,T,H,H
False H,H,H T,H,H H,H,H
True H,H,H T,H,H T,H,H
## ... truncated ... ##
True T,T,T H,T,T H,H,H,T,H,H,T,T
True T,T,T H,T,T H,T,T
True T,T,T H,T,T H,H,T,H,T,T
87.60% H,H,H T,H,H TOTAL
73.50% H,H,T T,H,H TOTAL
66.00% H,T,H H,H,T TOTAL
69.70% H,T,T H,H,T TOTAL
66.90% T,H,H T,T,H TOTAL
65.40% T,H,T T,T,H TOTAL
73.00% T,T,H H,T,T TOTAL
87.80% T,T,T H,T,T TOTAL
73.74% TOTAL TOTAL TOTAL
#>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment