Commit 1ad184e4 authored by M1888's avatar M1888

iltakoodailut

parent a0d0345c
......@@ -14,7 +14,19 @@
<value>100000</value>
</setting>
<setting name="Riveja" serializeAs="String">
<value>5</value>
<value>3</value>
</setting>
<setting name="Voimassa" serializeAs="String">
<value>6</value>
</setting>
<setting name="Nimi" serializeAs="String">
<value>Veikkaaja</value>
</setting>
<setting name="Arparivit" serializeAs="String">
<value>10</value>
</setting>
<setting name="ArparivitVoimassa" serializeAs="String">
<value>6</value>
</setting>
</Lottokone.Properties.Settings>
</userSettings>
......
......@@ -4,6 +4,17 @@
xmlns:local="clr-namespace:Lottokone"
StartupUri="MainWindow.xaml">
<Application.Resources>
<!-- Globaaleja tyylejä eri kontrolleille -->
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="WrapWithOverflow" />
<Setter Property="FontSize" Value="10" />
</Style>
<!-- Erikseen Style-propertyn käyttä käytettävät tyylit -->
<Style TargetType="TextBox" x:Key="Asetusboksi">
<Setter Property="DockPanel.Dock" Value="Right" />
<Setter Property="Width" Value="150" />
<Setter Property="Margin" Value="5" />
</Style>
</Application.Resources>
</Application>
......@@ -5,35 +5,61 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Lottokone"
mc:Ignorable="d"
Title="Asetukset" Height="450" Width="800">
Title="Asetukset" Height="450" Width="650">
<Grid>
<StackPanel>
<StackPanel Orientation="Horizontal" x:Name="spSettings">
<StackPanel Width="300">
<Label FontWeight="Bold">Simulaation asetukset</Label>
<StackPanel>
<StackPanel Margin="5" Orientation="Horizontal">
<DockPanel Margin="5">
<Label>Pelaajia</Label>
<TextBox Margin="5" x:Name="txbPelaajia" Text="{Binding Pelaajia, Mode=TwoWay}" Width="150" />
</StackPanel>
<Label Margin="25,-5,0,0" FontSize="10">Simulaatiossa käytettävien pelaajien määrä.</Label>
<TextBox Style="{StaticResource Asetusboksi}" x:Name="txbPelaajia" Text="{Binding Pelaajia, Mode=TwoWay}" />
</DockPanel>
<TextBlock Margin="25,-5,0,0">Simulaatiossa käytettävien pelaajien määrä.</TextBlock>
</StackPanel>
<StackPanel>
<StackPanel Margin="5" Orientation="Horizontal">
<DockPanel Margin="5">
<Label>Rivejä</Label>
<TextBox Margin="5" x:Name="txbRiveja" Text="{Binding Riveja, Mode=TwoWay}" Width="150" />
<TextBox Style="{StaticResource Asetusboksi}" x:Name="txbRiveja" Text="{Binding Riveja, Mode=TwoWay}" />
</DockPanel>
<TextBlock Margin="25,-5,0,0">Simuloitujen pelaajien rivien määrä per pelaaja.</TextBlock>
</StackPanel>
<Label Margin="25,-5,0,0" FontSize="10">Simuloitujen pelaajien rivien määrä per pelaaja.</Label>
<StackPanel>
<DockPanel Margin="5">
<Label>Rivit voimassa</Label>
<TextBox Style="{StaticResource Asetusboksi}" x:Name="txbVoimassa" Text="{Binding Voimassa, Mode=TwoWay}" />
</DockPanel>
<TextBlock Margin="25,-5,0,0">Kuinka monta kierrosta simuloitujen pelaajien rivit pysyvät voimassa, kunnes ne arvotaan uudelleen.</TextBlock>
</StackPanel>
</StackPanel>
<StackPanel>
<Label FontWeight="Bold">Omat rivit</Label>
<Label FontWeight="Bold">Omat asetukset</Label>
<StackPanel>
<StackPanel Orientation="Horizontal">
<DockPanel Margin="5">
<Label>Nimi</Label>
<TextBox Style="{StaticResource Asetusboksi}" x:Name="txbNimi" Text="{Binding Nimi, Mode=TwoWay}" />
</DockPanel>
<TextBlock Margin="25,-5,0,0">Tallennetaan high scoreen, jos joskus muka voitat päävoiton.</TextBlock>
</StackPanel>
<StackPanel>
<DockPanel Margin="5">
<Label>Arparivejä</Label>
<TextBox Style="{StaticResource Asetusboksi}" x:Name="txbArparivit" Text="{Binding Arparivit, Mode=TwoWay}" />
</DockPanel>
<TextBlock Margin="25,-5,0,0">Kuinka monta satunnaisesti arvottua riviä haluat pitää</TextBlock>
</StackPanel>
<StackPanel>
<DockPanel Margin="5">
<Label>Arparivit voimassa</Label>
<TextBox Style="{StaticResource Asetusboksi}" x:Name="txbArparivitVoimassa" Text="{Binding ArparivitVoimassa, Mode=TwoWay}" />
</DockPanel>
<TextBlock Margin="25,-5,0,0">Kuinka monen kierroksen välein rivisi arvotaan uudelleen?</TextBlock>
</StackPanel>
</StackPanel>
......
......@@ -19,8 +19,8 @@
<Label Content="{Binding Vuosi}" />
<Label Content="Viikko:"/>
<Label Content="{Binding Viikko}" />
<Label Content="Pelaajia:"/>
<Label Content="{Binding Pelaajamaara}"/>
<Label Content="Rivejä:"/>
<Label Content="{Binding Rivimaara}"/>
<Label Content="Potti:"/>
<Label Content="{Binding Potti}"/>
<Label Content="Voittorivi:"/>
......
......@@ -4,9 +4,11 @@ using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
......@@ -16,6 +18,26 @@ namespace Lottokone.Model
{
private bool kaynnissa = false;
// Mitataan kunkin kierroksen kesto, jotta
// osataan näyttää nättiä statistiikkaa.
private Stopwatch stopwatch;
// Kolme eri listaa eri voittotasoille:
// 5, 6 ja 7 oikein.
// Näihin laitetaan joka kierroksella voittaneet pelaajat,
// ja sitten jaetaan niistä tarkistuksen jälkeen voitot.
//
// Pienempiä voittotasoja ei tarvitse listata, koska niiden voittosumma
// ei riipu voittajien määristä vaan on aina sama. Ne voidaan hoitaa helpommin.
//
// Olisin mielummin toteuttanut tämän indeksoituna listana:
// eli oikein[5] takaa löytyisi lista viisi oikein pelaajista, jne..
// Parallelismin kannalta tämä oli kuitenkin hankalaa, ja valmiiden
// thread-safe ConcurrentBagien käyttö on järkevämpää, vaikkakin rumaa.
private ConcurrentBag<Pelaaja> oikein5;
private ConcurrentBag<Pelaaja> oikein6;
private ConcurrentBag<Pelaaja> oikein7;
private short viikko;
public short Viikko
{
......@@ -51,18 +73,8 @@ namespace Lottokone.Model
public ConcurrentBag<Pelaaja> Pelaajat { get; set; }
// neljä listaa eri voittotasoille:
// 4 oikein, 5 oikein, 6 oikein ja 7 oikein
// nämä olisi voinut periaatteessa asettaa listaan, ja
// käyttää indeksin kautta (oikein[5] olisi lista viisi oikein -tuloksista jne.)
// mutta se aiheuttaisi threadingin kanssa ongelmia.
public ConcurrentBag<Pelaaja> Oikein4 { get; set; }
public ConcurrentBag<Pelaaja> Oikein5 { get; set; }
public ConcurrentBag<Pelaaja> Oikein6 { get; set; }
public ConcurrentBag<Pelaaja> Oikein7 { get; set; }
private long potti;
public long Potti
private int potti;
public int Potti
{
get
{
......@@ -105,13 +117,17 @@ namespace Lottokone.Model
}
}
// pelaajamäärä pitää pitää inttinä, niin saadaan helposti bindattua se
// käyttöliittymän puolella.
public int Pelaajamaara
// Lasketaan kierroksella mukana olleiden rivien määrä.
public int Rivimaara
{
get
{
return Pelaajat.Count();
int n = 0;
foreach(Pelaaja p in Pelaajat)
{
n += p.Rivit.Count();
}
return n;
}
}
......@@ -121,12 +137,13 @@ namespace Lottokone.Model
viikko = 0;
vuosi = 1;
Pelaajat = new ConcurrentBag<Pelaaja>();
Voittorivi = new Voittorivi();
StatusHistory = new ObservableCollectionEx<string>(new List<String>());
StatusHistory = new ObservableCollectionEx<string>(new List<string>());
Status = "Veikkaus: Pelaa maltillisesti :):):):)";
stopwatch = Stopwatch.StartNew();
LisaaPelaajia(Properties.Settings.Default.Pelaajia, Properties.Settings.Default.Riveja);
stopwatch.Stop();
Status = $"Lisätty {Pelaajat.Count()} pelaajaa ({stopwatch.ElapsedMilliseconds}ms)";
}
public void LisaaPelaajia(int pelaajia, int riveja)
......@@ -149,28 +166,30 @@ namespace Lottokone.Model
public void Tick()
{
stopwatch.Reset();
stopwatch.Start();
Viikko++;
Oikein4 = new ConcurrentBag<Pelaaja>();
Oikein5 = new ConcurrentBag<Pelaaja>();
Oikein6 = new ConcurrentBag<Pelaaja>();
Oikein7 = new ConcurrentBag<Pelaaja>();
oikein5 = new ConcurrentBag<Pelaaja>();
oikein6 = new ConcurrentBag<Pelaaja>();
oikein7 = new ConcurrentBag<Pelaaja>();
// Uusi voittorivi ei jostain syystä päivity UI:hin,
// ellei sitä alusta kokonaan uudelleen täällä.
Voittorivi = new Voittorivi();
Voittorivi.ArvoRivi();
EtsiVoittajat();
Status = $"-Viikko {Viikko}-";
if (!Oikein4.IsEmpty)
Status = $"4 oikein: {Oikein4.Count()}";
if (!Oikein5.IsEmpty)
Status = $"5 oikein: {Oikein5.Count()}";
if (!Oikein6.IsEmpty)
Status = $"6 oikein: {Oikein6.Count()}";
if (!Oikein7.IsEmpty)
Status = $"7 oikein: {Oikein7.Count()}";
JaaVoitot();
stopwatch.Stop();
Status = $"-Viikko {Viikko} ({stopwatch.ElapsedMilliseconds}ms)-";
//JaaVoitot();
}
// Voittajat täytyy erikseen etsiä ja sitten vasta jakaa voitot.
// Samassa loopissa niitä ei voi tehdä, koska voittosumma riippuu voittajien
// määrästä. Se on selvillä vasta kun kaikki on käyty läpi.
private void EtsiVoittajat()
{
Parallel.ForEach(Pelaajat, (p) =>
......@@ -182,16 +201,23 @@ namespace Lottokone.Model
switch (r.Oikein(Voittorivi))
{
case 4:
Oikein4.Add(p);
// 4 oikein on tasan 10e voitto, joten se voidaan jakaa täällä.
// pottia voi käsitellä useampi säie samaan aikaan,
// siksi System.Threading.Interlocked.Add, joka on atominen operaatio
Interlocked.Add(ref potti, -10);
// tätä pelaajaa taas ei käsitellä kuin me tässä ja nyt.
p.Saldo += 10;
break;
case 5:
Oikein5.Add(p);
oikein5.Add(p);
break;
case 6:
Oikein6.Add(p);
oikein6.Add(p);
break;
case 7:
Oikein7.Add(p);
oikein7.Add(p);
break;
default:
break;
......@@ -201,21 +227,46 @@ namespace Lottokone.Model
}
// https://fi.wikipedia.org/wiki/Lotto_(Veikkaus)#Voittoluokat_ja_voiton_todenn%C3%A4k%C3%B6isyys
// 3+1: 2 e (todo)
// 4 oikein: 10 e
// 5 oikein: 3% potista voittajien kesken
// 6 oikein: 2,5% potista voittajien kesken
// 6+1: 3,8% potista voittajien kesken
// 6+1: 3,8% potista voittajien kesken (todo)
// 7: päävoitto
// Luotetaan, että aiemmat tarkistukset on oikein, eikä
// listoilla ole pelaajia joiden rivit eivät ole voittaneet...
private void JaaVoitot()
{
Task neljat = Task.Run(() =>
if (!oikein5.IsEmpty)
{
Pelaaja p;
while(Oikein4.TryTake(out p) != false)
int voittajia = oikein5.Count();
int voitto = ((potti / 100) * 3) / voittajia;
Status = $"5 oikein voittoja {voittajia} kpl ({voitto}e)";
while (oikein5.TryTake(out Pelaaja p) != false)
{
p.Saldo += voitto;
}
}
if (!oikein6.IsEmpty)
{
int voittajia = oikein6.Count();
int voitto = (int)((potti / 100) * 2.5) / voittajia;
Status = $"6 oikein voittoja {voittajia} kpl ({voitto}e)";
while(oikein6.TryTake(out Pelaaja p) != false)
{
p.Saldo += voitto;
}
}
if(!oikein7.IsEmpty)
{
int voittajia = oikein7.Count();
int voitto = (int)((potti / 100) * 60) / voittajia;
Status = $"!!! 7 oikein! {voitto}e ({voittajia} kpl) !!!";
}
});
}
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
......
......@@ -34,14 +34,18 @@ namespace Lottokone.Model
public void Tick()
{
// Käydään rivit takaperin läpi, niin voidaan tarvittaessa
// poistaa indeksistä jossa ollaan ja jatkaa looppia turvallisesti.
for(int i = Rivit.Count() - 1; i > 0; i--)
{
Saldo--;
Lottorivi r = Rivit[i];
r.Voimassa--;
if (r.Voimassa < 0)
Rivit.Remove(r);
}
// Luodaan uusia arparivejä tarpeen mukaan
int ero = Riveja - Rivit.Count();
if (ero > 0)
for (int i = 0; i < ero; i++)
......
......@@ -12,7 +12,7 @@ namespace Lottokone.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.7.0.0")]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.9.0.0")]
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
......@@ -37,7 +37,7 @@ namespace Lottokone.Properties {
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("5")]
[global::System.Configuration.DefaultSettingValueAttribute("3")]
public int Riveja {
get {
return ((int)(this["Riveja"]));
......@@ -46,5 +46,53 @@ namespace Lottokone.Properties {
this["Riveja"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("6")]
public int Voimassa {
get {
return ((int)(this["Voimassa"]));
}
set {
this["Voimassa"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("Veikkaaja")]
public string Nimi {
get {
return ((string)(this["Nimi"]));
}
set {
this["Nimi"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("10")]
public int Arparivit {
get {
return ((int)(this["Arparivit"]));
}
set {
this["Arparivit"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("6")]
public int ArparivitVoimassa {
get {
return ((int)(this["ArparivitVoimassa"]));
}
set {
this["ArparivitVoimassa"] = value;
}
}
}
}
......@@ -6,7 +6,19 @@
<Value Profile="(Default)">100000</Value>
</Setting>
<Setting Name="Riveja" Type="System.Int32" Scope="User">
<Value Profile="(Default)">5</Value>
<Value Profile="(Default)">3</Value>
</Setting>
<Setting Name="Voimassa" Type="System.Int32" Scope="User">
<Value Profile="(Default)">6</Value>
</Setting>
<Setting Name="Nimi" Type="System.String" Scope="User">
<Value Profile="(Default)">Veikkaaja</Value>
</Setting>
<Setting Name="Arparivit" Type="System.Int32" Scope="User">
<Value Profile="(Default)">10</Value>
</Setting>
<Setting Name="ArparivitVoimassa" Type="System.Int32" Scope="User">
<Value Profile="(Default)">6</Value>
</Setting>
</Settings>
</SettingsFile>
\ No newline at end of file
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment