Commit 9e03ca56 authored by M1888's avatar M1888

aamupäiväkoodailut

parent 1ad184e4
......@@ -28,6 +28,12 @@
<setting name="ArparivitVoimassa" serializeAs="String">
<value>6</value>
</setting>
<setting name="Rivihinta" serializeAs="String">
<value>1</value>
</setting>
<setting name="Vakiorivit" serializeAs="String">
<value>0</value>
</setting>
</Lottokone.Properties.Settings>
</userSettings>
</configuration>
\ No newline at end of file
......@@ -7,14 +7,24 @@
<!-- Globaaleja tyylejä eri kontrolleille -->
<Style TargetType="TextBlock">
<Setter Property="TextWrapping" Value="WrapWithOverflow" />
<Setter Property="FontSize" Value="10" />
<Setter Property="FontSize" Value="12" />
</Style>
<Style TargetType="ListViewItem">
<Setter Property="FontSize" Value="14" />
</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>
<Style TargetType="Label" x:Key="Isompi">
<Setter Property="FontWeight" Value="Bold" />
<Setter Property="FontSize" Value="16" />
</Style>
</Application.Resources>
</Application>
......@@ -35,6 +35,14 @@
<TextBlock Margin="25,-5,0,0">Kuinka monta kierrosta simuloitujen pelaajien rivit pysyvät voimassa, kunnes ne arvotaan uudelleen.</TextBlock>
</StackPanel>
<StackPanel>
<DockPanel Margin="5">
<Label>Rivihinta</Label>
<TextBox Style="{StaticResource Asetusboksi}" x:Name="txbHinta" Text="{Binding Rivihinta, Mode=TwoWay}" />
</DockPanel>
<TextBlock Margin="25,-5,0,0">Paljonko yksi lottorivi maksaa?</TextBlock>
</StackPanel>
</StackPanel>
<StackPanel>
<Label FontWeight="Bold">Omat asetukset</Label>
......@@ -61,8 +69,17 @@
</DockPanel>
<TextBlock Margin="25,-5,0,0">Kuinka monen kierroksen välein rivisi arvotaan uudelleen?</TextBlock>
</StackPanel>
<StackPanel>
<DockPanel Margin="5">
<Label>Vakiorivejä</Label>
<TextBox Style="{StaticResource Asetusboksi}" x:Name="txbVakiorivit" Text="{Binding Vakiorivit, Mode=TwoWay}" />
</DockPanel>
<TextBlock Margin="25,-5,0,0">Montako vakioriviä haluat pelata?</TextBlock>
</StackPanel>
</StackPanel>
</StackPanel>
<Button x:Name="btnSave" Content="Tallenna &amp; sulje" Click="btnSave_Click" Margin="15" />
</StackPanel>
......
......@@ -30,5 +30,10 @@ namespace Lottokone
Properties.Settings.Default.Save();
this.Close();
}
private void BtnRivit_Click(object sender, RoutedEventArgs e)
{
}
}
}
......@@ -34,6 +34,9 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="MedallionRandom, Version=1.1.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>packages\MedallionRandom.1.1.0\lib\net452\MedallionRandom.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
......@@ -81,7 +84,6 @@
<Compile Include="Model\Pelaaja.cs" />
<Compile Include="Model\Rivi.cs" />
<Compile Include="Model\Voittorivi.cs" />
<Compile Include="ObservableCollectionEx.cs" />
<Compile Include="Properties\AssemblyInfo.cs">
<SubType>Code</SubType>
</Compile>
......@@ -99,6 +101,7 @@
<Generator>ResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
</EmbeddedResource>
<None Include="packages.config" />
<None Include="Properties\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>Settings.Designer.cs</LastGenOutput>
......@@ -107,5 +110,8 @@
<ItemGroup>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Resource Include="Resources\lotto.png" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
</Project>
\ No newline at end of file
<Window x:Class="Lottokone.Lottorivit"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Lottokone"
mc:Ignorable="d"
Title="Lottorivit" Height="450" Width="800">
<Grid>
</Grid>
</Window>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Shapes;
namespace Lottokone
{
/// <summary>
/// Interaction logic for Lottorivit.xaml
/// </summary>
public partial class Lottorivit : Window
{
public Lottorivit()
{
InitializeComponent();
NaytaKontrollit();
}
public void NaytaKontrollit()
{
StackPanel rivit = new StackPanel();
for (int i = 0; i < Properties.Settings.Default.Vakiorivit; i++)
{
StackPanel rivi = new StackPanel();
rivi.Orientation = Orientation.Horizontal;
rivi.Name = "spRivi{i}";
for (int j = 0; j < 7; j++)
{
TextBox tb = new TextBox();
tb.Style = Resources["Lottonumero"] as Style;
tb.TextChanged += txbChanged;
rivi.Children.Add(tb);
}
rivit.Children.Add(rivi);
}
grdRivit.Children.Add(rivit);
}
public void txbChanged(object sender, TextChangedEventArgs e)
{
TextBox tb = sender as TextBox;
if(tb != null)
{
if(int.TryParse(tb.Text, out int result))
{
if(result < 1)
{
tb.Text = "1";
}
else if (result > 40)
{
tb.Text = "40";
}
}
else
{
MessageBox.Show("Syötä vain numeroita 1-40");
}
}
}
}
}
......@@ -5,30 +5,37 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Lottokone"
mc:Ignorable="d"
Title="MainWindow" Height="450" Width="800">
Title="Lottokone" Height="450" Width="950">
<Window.Background>
<ImageBrush ImageSource="Resources/lotto.png" />
</Window.Background>
<Grid>
<StackPanel Orientation="Horizontal">
<StackPanel>
<Button x:Name="btnNext" Content="Seuraava viikko" Click="btnNext_Click"/>
<Button x:Name="btnStart" Content="Käynnistä" Click="btnStart_Click"/>
<Button x:Name="btnStop" Content="Pysäytä" Click="btnStop_Click"/>
<Button x:Name="btnSettings" Content="Asetukset" Click="btnSettings_Click" Margin="0,50,0,0"/>
<StackPanel Margin="0,10,0,0">
<Button x:Name="btnNext" Margin="0,5,0,0" Content="Seuraava viikko" Click="btnNext_Click"/>
<Button x:Name="btnStart" Margin="0,5,0,0" Content="Käynnistä" Click="btnStart_Click"/>
<Button x:Name="btnStop" Margin="0,5,0,0" Content="Pysäytä" Click="btnStop_Click"/>
<Button x:Name="btnSettings" Margin="0,50,0,0" Content="Asetukset" Click="btnSettings_Click" />
</StackPanel>
<StackPanel x:Name="spLotto" Width="250">
<Label Content="Vuosi:"/>
<Label Content="{Binding Vuosi}" />
<Label Content="Viikko:"/>
<Label Content="{Binding Viikko}" />
<Label Content="Rivejä:"/>
<Label Content="{Binding Rivimaara}"/>
<Label Content="Potti:"/>
<Label Content="{Binding Potti}"/>
<Label Content="Voittorivi:"/>
<Label Content="{Binding Voittorivi}"/>
<Label Style="{StaticResource Isompi}" Content="{Binding Vuosi}" ContentStringFormat="Vuosi: {0}"/>
<Label Style="{StaticResource Isompi}" Content="{Binding Viikko}" ContentStringFormat="Viikko: {0}"/>
<Label Style="{StaticResource Isompi}" Content="{Binding Rivimaara}" ContentStringFormat="Rivejä: {0:N0}"/>
<Label Style="{StaticResource Isompi}" Content="{Binding Potti}" ContentStringFormat="Potti: {0:C0}"/>
<Label Style="{StaticResource Isompi}">Voittorivi:</Label>
<Label Style="{StaticResource Isompi}" Content="{Binding Voittorivi}" />
</StackPanel>
<StackPanel>
<Label Content="Status:"/>
<ListView Height="350" Width="300" x:Name="lstStatus" ItemsSource="{Binding Value, UpdateSourceTrigger=PropertyChanged}"/>
<Label Style="{StaticResource Isompi}" Content="Status:"/>
<ListView Height="350" Width="300" x:Name="lstStatus" ItemsSource="{Binding Value, UpdateSourceTrigger=PropertyChanged}" Opacity="0.7"/>
</StackPanel>
<StackPanel x:Name="spPlayer" Width="300" Margin="5,0,0,0">
<Label Style="{StaticResource Isompi}" Content="{Binding Nimi}" ContentStringFormat="Nimi: {0}" />
<Label Style="{StaticResource Isompi}" Content="{Binding Saldo}" ContentStringFormat="Tilin saldo: {0:C0}" />
<Label Style="{StaticResource Isompi}" Content="{Binding Riveja}" ContentStringFormat="Rivejä: {0:N0}" />
<Label Style="{StaticResource Isompi}" Content="Omat voitot:" Margin="0,100,0,0"/>
<ListView x:Name="lstVoitot" Height="180" Width="300" Opacity="0.7">
</ListView>
</StackPanel>
</StackPanel>
</Grid>
......
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Globalization;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Markup;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
......@@ -22,50 +26,155 @@ namespace Lottokone
/// </summary>
public partial class MainWindow : Window
{
// Lottokoneen pääolio
private Lotto kone;
// Meidän pelaaja
private Pelaaja pelaaja;
// Meidän voitot, ja lukko-objekti jotta voidaan käyttää sitä threadeista
private ObservableCollection<string> voitot;
private object voittolukko;
// Tämä ajaa meidän pelilooppia kun starttia painetaan
private Thread runner;
private bool kaynnissa = false;
// Tässä kyseinen pelilooppi
// Tämä olisi ollut ehkä järkevä toteuttaa muualla kuin täällä MainWindow.xaml.cs:ssä,
// mutta tällä nyt mennään....
private void GameLoop()
{
do
{
kone.Tick();
// meidän pitää tarkistaa täällä käsin pelaajamme voitot uudestaan, jotta
// saadaan niistä tieto myös toiseen listaikkunaan.
foreach (Lottorivi r in pelaaja.Rivit)
{
string s = "";
switch (r.Oikein(kone.Voittorivi))
{
case 3:
if (r.Numerot.Contains(kone.Voittorivi.Lisanumero))
{
s = $"Kierros {kone.Kierros}: 3+1 oikein";
}
break;
case 4:
s = $"Kierros {kone.Kierros}: 4 oikein";
break;
case 5:
s = $"Kierros {kone.Kierros}: 5 oikein";
break;
case 6:
s = $"Kierros {kone.Kierros}: ";
// taas lisänumeron tarkistus
if (r.Numerot.Contains(kone.Voittorivi.Lisanumero))
s += "6+1 oikein!!! Melkein jo kannattaa?!";
else
s += "6 oikein!";
break;
case 7:
// Päävoitto jos osuu, niin pelin voinee pysäyttää.
s = $"Kierros {kone.Kierros}: PÄÄVOITTO!!!! JUMANTSUIKKA! Kannattaa ehkä lotota oikeastikin?! " +
"Ellet käyttänyt kaikkea onneasi tähän....";
kaynnissa = false;
break;
default:
break;
}
if(s.Length > 0)
{
lock(voittolukko)
{
voitot.Insert(0, s);
}
}
}
} while (kaynnissa);
}
public MainWindow()
{
InitializeComponent();
kone = new Lotto();
// sidotaan lottokone stackpanelin datacontekstiksi
// tällä saadaan käyttöliittymässä luvut ja rahamäärät näkymään oikein.
// https://stackoverflow.com/questions/7454024/setting-culture-en-in-globally-in-wpf-app
var culture = new CultureInfo("fi-FI");
CultureInfo.DefaultThreadCurrentCulture = culture;
CultureInfo.DefaultThreadCurrentUICulture = culture;
FrameworkElement.LanguageProperty.OverrideMetadata(typeof(FrameworkElement), new
FrameworkPropertyMetadata(XmlLanguage.GetLanguage(CultureInfo.CurrentCulture.IetfLanguageTag)));
// luodaan lottokone ja sidotaan se stackpanelin datacontekstiksi
kone = new Lotto();
spLotto.DataContext = kone;
// luodaan meidän pelaaja, ja lisätään se lottoon.
pelaaja = new Pelaaja(Properties.Settings.Default.Nimi);
pelaaja.Riveja = Properties.Settings.Default.Arparivit + Properties.Settings.Default.Vakiorivit;
pelaaja.RivitVoimassa = Properties.Settings.Default.ArparivitVoimassa;
kone.Pelaajat.Add(pelaaja);
// sidotaan meidät pelaajan tietoikkunaan
spPlayer.DataContext = pelaaja;
// status-ikkunaan skrollaava historia eventeistä
// EnableCollectionSynchronization() mahdollistaa listan käyttämisen muista
// threadeista, ja UI-thread saa siitä silti CollectionUpdated eventin.
BindingOperations.EnableCollectionSynchronization(kone.StatusHistory, kone.StatusLock);
lstStatus.ItemsSource = kone.StatusHistory;
((INotifyCollectionChanged)lstStatus.Items).CollectionChanged += lstStatus_CollectionChanged;
// voitoille samat kun ylempi
voitot = new ObservableCollection<string>();
voittolukko = new object();
BindingOperations.EnableCollectionSynchronization(voitot, voittolukko);
lstVoitot.ItemsSource = voitot;
btnStop.IsEnabled = false;
}
private void btnNext_Click(object sender, RoutedEventArgs e)
{
kone.Tick();
}
btnStart.IsEnabled = false;
btnNext.IsEnabled = false;
private void lstStatus_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
if (e.Action == NotifyCollectionChangedAction.Add)
{
// ajetaan looppi kerran. kun kaynnissa = false, niin se palautuu heti.
GameLoop();
}
btnNext.IsEnabled = true;
btnStart.IsEnabled = true;
}
private void btnStart_Click(object sender, RoutedEventArgs e)
{
kone.Start();
btnStop.IsEnabled = true;
btnStart.IsEnabled = false;
btnNext.IsEnabled = false;
btnSettings.IsEnabled = false;
kaynnissa = true;
runner = new Thread(GameLoop);
runner.IsBackground = true;
runner.Start();
}
private void btnStop_Click(object sender, RoutedEventArgs e)
{
kone.Stop();
kaynnissa = false;
// odotetaan max 2sek
runner.Join(2000);
btnNext.IsEnabled = true;
btnStart.IsEnabled = true;
btnStop.IsEnabled = false;
btnSettings.IsEnabled = true;
}
private void btnSettings_Click(object sender, RoutedEventArgs e)
{
Asetukset a = new Asetukset();
a.Show();
new Asetukset().ShowDialog();
}
}
}
This diff is collapsed.
......@@ -9,12 +9,18 @@ namespace Lottokone.Model
public class Lottorivi : Rivi
{
public short Viikko { get; set; }
public short Voimassa { get; set; }
// Viikkoja, montako voimassa. null = ikuinen
public Nullable<int> Voimassa { get; set; }
public Lottorivi() : base()
{
// oletuksena rivit voimassa yhden arvonnan
Voimassa = 1;
Voimassa = Properties.Settings.Default.Voimassa;
}
public Lottorivi(Nullable<int> voimassa) : base()
{
Voimassa = voimassa;
}
}
}
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace Lottokone.Model
{
public class Pelaaja
public class Pelaaja : INotifyPropertyChanged
{
public string Nimi { get; set; }
public long Saldo { get; set; }
public event PropertyChangedEventHandler PropertyChanged;
// Pelaajan nimi. Ei pakollinen
private string nimi;
public string Nimi
{
get
{
return nimi;
}
set
{
nimi = value;
RaisePropertyChanged();
}
}
// Tilin saldo. Nollista lähdetään.
private int saldo;
public int Saldo {
get
{
return saldo;
}
set
{
saldo = value;
RaisePropertyChanged();
}
}
// Lista lottoriveistä
public List<Lottorivi> Rivit { get; set; }
public int Riveja { get; set; }
// Määrä siitä, montako riviä pelaajalla pitäisi olla
private int riveja;
public int Riveja {
get
{
return riveja;
}
set
{
riveja = value;
RaisePropertyChanged();
}
}
// Kuinka monta viikkoa pelaajan rivit on voimassa?
public int RivitVoimassa { get; set; }
public Pelaaja()
{
......@@ -25,31 +74,51 @@ namespace Lottokone.Model
Nimi = nimi;
}
public Pelaaja(int riveja) : this()
public Pelaaja(int riveja, int voimassa) : this()
{
Riveja = riveja;
RivitVoimassa = voimassa;
for (int i = 0; i < Riveja; i++)
Rivit.Add(new Lottorivi());
{
Rivit.Add(new Lottorivi(RivitVoimassa));
}
}
public void Tick()
{
// Veloitetaan lottorivien hinta tililtä.
// Tilin saldo on int, ja rivihinta on float, joten pyöristysvirheitä saattaa sattua.
// Kerrotaan rivihinta kaikkien rivien määrällä, ja pyöristetään se, niin ei pitäisi mennä
// liian kauas metsään.
Saldo -= (int)Math.Round(Rivit.Count() * Properties.Settings.Default.Rivihinta);
// 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--)
for (int i = Rivit.Count() - 1; i > 0; i--)
{
Saldo--;
Lottorivi r = Rivit[i];
r.Voimassa--;
if (r.Voimassa < 0)
Rivit.Remove(r);
if (r.Voimassa != null)
{
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++)
Rivit.Add(new Lottorivi());
{
Rivit.Add(new Lottorivi(RivitVoimassa));
}
}
}
protected void RaisePropertyChanged([CallerMemberName] string propertyName = null)
{
if (PropertyChanged != null)
PropertyChanged(this, new PropertyChangedEventArgs(propertyName));
}
}
}
......@@ -3,17 +3,17 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Medallion; // thread-safe staattinen Rand
namespace Lottokone.Model
{
public class Rivi
{
private Random rnd;
public short[] Numerot { get; set; }
public Rivi()
{
rnd = new Random();
Numerot = new short[7];
ArvoRivi();
}
public Rivi(short[] numerot)
......@@ -38,18 +38,18 @@ namespace Lottokone.Model
Numerot = Array.ConvertAll(
// otetaan numerot 1-40, järjestellään satunnaisesti, valitaan 7 ekaa, ja tehdään niistä taulukko
Enumerable.Range(1, 40).OrderBy(x => rnd.Next()).Take(7).ToArray(),
Enumerable.Range(1, 40).OrderBy(x => Rand.Next(0, Int32.MaxValue)).Take(7).ToArray(),
// Enumerable.Range tuottaa inttejä, jotka pitää sitten muuntaa shorteiksi
Convert.ToInt16);
// järjestellään rivi arpomisen jälkeen heti
// suuruusjärjestykseen, niin vertailu on myöhemmin helpompaa
// suuruusjärjestykseen, niin vertailu on myöhemmin nopeampaa
Array.Sort(Numerot);
}
// tämä metodi tarkistaa, kuinka monta numeroa rivissä on oikein
// annettuun riviin verrattuna
public int Oikein(object obj)
public virtual int Oikein(object obj)
{
Rivi r = obj as Rivi;
if (r == null)
......
......@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Medallion; // thread-safe staattinen Rand
namespace Lottokone.Model
{
......@@ -10,10 +11,25 @@ namespace Lottokone.Model
{
public short Lisanumero { get; set; }
public Voittorivi()
{
Numerot = new short[7];
ArvoRivi();
}
public override void ArvoRivi()
{
base.ArvoRivi();
// todo: lisänumero
// arvotaan voittoriville vielä lisänumero
do {
Lisanumero = (short)Rand.Next(0, 41);
} while (Numerot.Contains(Lisanumero));
}
public override string ToString()
{
return base.ToString() + $" + [ {Lisanumero} ]";
}
}
}
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.Windows.Threading;
/* Thread-safe ObservableCollection
*
* Kopioitu täältä:
* https://www.codeproject.com/Tips/414407/Thread-Safe-Improvement-for-ObservableCollection
*/
namespace Custom.Collections
{
public class ObservableCollectionEx<t> : ObservableCollection<t>
{
// Override the event so this class can access it
public override event NotifyCollectionChangedEventHandler CollectionChanged;
public ObservableCollectionEx(IEnumerable<t> collection) : base(collection) { }
public ObservableCollectionEx(List<t> collection) : base(collection) { }
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
// Be nice - use BlockReentrancy like MSDN said
using (BlockReentrancy())
{
var eventHandler = CollectionChanged;
if (eventHandler != null)
{
Delegate[] delegates = eventHandler.GetInvocationList();
// Walk thru invocation list
foreach (NotifyCollectionChangedEventHandler handler in delegates)
{
var dispatcherObject = handler.Target as DispatcherObject;
// If the subscriber is a DispatcherObject and different thread
if (dispatcherObject != null && dispatcherObject.CheckAccess() == false)
// Invoke handler in the target dispatcher's thread
dispatcherObject.Dispatcher.Invoke(DispatcherPriority.DataBind,
handler, this, e);
else // Execute handler as is
handler(this, e);
}
}
}
}
}
}
......@@ -94,5 +94,29 @@ namespace Lottokone.Properties {
this["ArparivitVoimassa"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]