private Syotetiedot alkuperaisetSyotteet; // store the initial input data with matches // -------------------------------------------------------------------------------- // Laskenta // -------------------------------------------------------------------------------- // AlustaLaskenta // // Ennen laskentaa alustetaan muuttujat saatujen syötteiden mukaan // private void AlustaLaskenta(Syotetiedot syotteet) { // // Alustetaan Selopelaajan paikalliset muuttujat // alkuperaisetSyotteet = syotteet; // selo, pelimaara, miettimisaika, lomakkeelle mm. seloero // laskettavat tiedot, selon ja pelimaaran laskenta aloitetaan syötetyistä arvoista UusiSelo = syotteet.AlkuperainenSelo; UusiPelimaara = syotteet.AlkuperainenPelimaara; TurnauksenTulos2x = 0; // lasketaan otteluista kokonaislukuna Odotustulos = 0; // summa yksittäisten otteluiden odotustuloksista // palautettava kerroin alkuperäisen selon mukaan // laskennassa käytetään sen hetkisestä selosta laskettua kerrointa Kerroin = MaaritaKerroin(UusiSelo); // vaihteluvälin alustus MinSelo = Vakiot.MAX_SELO; MaxSelo = Vakiot.MIN_SELO; // Lisäksi selvitä syötetiedoista (tarvitaan laskennassa, tulostetaan lomakkeelle) // - vastustajien eli otteluiden lkm // - turnauksen eli vastustajien keskivahvuus VastustajienLkm = syotteet.Ottelut.Lukumaara; UudenPelaajanPelitLKM = syotteet.UudenPelaajanPelitEnsinLKM; TurnauksenKeskivahvuus = syotteet.Ottelut.Keskivahvuus; TurnauksenKeskivahvuus10x = syotteet.Ottelut.Keskivahvuus10x; //VastustajaMin = syotteet.Ottelut.MinVahvuus; //VastustajaMax = syotteet.Ottelut.MaxVahvuus; }
// Laske tulokset, syöte on jo tarkistettu tätä ennen // // Kutsuttu: // -SelolaskuriForm.cs // -Selolaskuri.Tests/UnitTest1.cs // // Lisäksi kopioi lasketut tulokset tietorakenteeseen Tulokset, josta ne myöhemmin // näytetään (SelolaskuriForm.cs) tai ) yksikkötestauksessa tarkistetaan (Selolaskuri.Tests/UnitTest1.cs) // public Selopelaaja SuoritaLaskenta(Syotetiedot syotteet) { // *** NYT LASKETAAN *** // selopelaaja.PelaaKaikkiOttelut(syotteet); // pelaa kaikki ottelut listalta // *** PALAUTA TULOKSET *** return(selopelaaja); }
// TarkistaSyote // // Kutsuttu: // -SelolaskuriForm.cs // -Selolaskuri.Tests/UnitTest1.cs // // Tarkistaa // -miettimisaika-valintanapit (ei voi olla virhettä) // -oma SELO eli nykyinen vahvuusluku (onko kelvollinen numero) // -oma pelimäärä (kelvollinen numero tai tyhjä) // -vastustajan SELO (onko numero) tai vastustajien SELOT (onko turnauksen tulos+selot tai selot tuloksineen) // -yhtä ottelua syötettäessä tuloksen valintanapit (jos yksi vastustaja, niin tulos pitää valita) // // Syotetiedot syotteet = oma nykyinen selo ja pelimäärä, vastustajan selo, ottelun tulos sekä merkkijono // // Tuloksena // syotteet.ottelut sisältää listan vastustajista tuloksineen: ottelu(selo, tulos) // syotteet.VastustajanSeloYksittainen on joko yhden vastustajan selo tai 0 (jos monta ottelua) // // Virhetilanteet: // Kenttiä tarkistetaan yo. järjestyksessä ja lopetetaan, kun kohdataan ensimmäinen virhe. // Palautetaan tarkka virhestatus ja virheilmoitukset näytetään ylemmällä tasolla. // public int TarkistaSyote(Syotetiedot syotteet) { if (syotteet == null) { throw new ArgumentNullException(nameof(syotteet)); } int tulos; // = Vakiot.SYOTE_STATUS_OK; // tyhjennä ottelulista, johon tallennetaan vastustajat tuloksineen //syotteet.ottelut.Tyhjenna(); Ei tarvitse, kun aiempi new Syotetiedot() tyhjentää // ************ TARKISTA SYÖTE ************ do { // ENSIN TARKISTA MIETTIMISAIKA. if ((tulos = TarkistaMiettimisaika(syotteet.Miettimisaika)) == Vakiot.SYOTE_VIRHE_MIETTIMISAIKA_CSV) { break; } // Hae ensin oma nykyinen vahvuusluku ja pelimäärä if ((tulos = TarkistaOmaSelo(syotteet.AlkuperainenSelo_str)) == Vakiot.SYOTE_VIRHE_OMA_SELO) { break; } syotteet.AlkuperainenSelo = tulos; if ((tulos = TarkistaPelimaara(syotteet.AlkuperainenPelimaara_str)) == Vakiot.SYOTE_VIRHE_PELIMAARA) { break; } syotteet.AlkuperainenPelimaara = tulos; // Voi olla PELIMAARA_TYHJA tai numero >= 0 // JOS YKSI OTTELU, saadaan sen yhden vastustajan vahvuusluku, eikä otteluja ole listassa. // ottelumäärän tarkistamisen jälkeen tässä tehdään yhden ottelun lista // JOS MONTA OTTELUA, palautuu 0 ja ottelut on tallennettu tuloksineen listaan // // JOS MONTA OTTELUA ja VÄLISSÄ '/'-merkki, NIIN SYÖTETTY ENSIN UUDEN PELAAJAN LASKENTA JA SITTEN NORMAALI LASKENTA // Tällöin syotteet,.AlkuperainenPelimaara oltava enintään 10 selopelaaja.UudenPelaajanPelitLKM = 0; // XXX: oletus ettei vaihdeta laskentaa ja voidaan tarkistaa, ettei ole '/' kahdesti // 1) Usea ottelu: syotteet.Ottelut sisältää listan otteluista tuloksineen // // 2) yksi ottelu: syotteet.Ottelut yksi ottelu // // TULOSSA: // 3) "tulos ottelumäärä keskiselo" esim. 5.5 8 1888.6 // Selopelaaja.AnnettuTurnauksenTulos2x, TurnauksenKeskivahvuus, TurnauksenKeskivahvuus10x, VastustajienLkm if ((tulos = TarkistaVastustajanSelo(syotteet.Ottelut, syotteet.VastustajienSelot_str)) < Vakiot.SYOTE_STATUS_OK) { break; } if (selopelaaja.UudenPelaajanPelitLKM > 0) { if (syotteet.AlkuperainenPelimaara > 10) { // ei voinut olla uuden pelaajan laskenta, jos alkuperäinen pelimäärä oli yli 10 tulos = Vakiot.SYOTE_VIRHE_UUDEN_PELAAJAN_OTTELUT_ENINT_10; break; } if (selopelaaja.UudenPelaajanPelitLKM + syotteet.AlkuperainenPelimaara < 11) { // jos alkuperäinen pelimäärä + nyt uuden pelaajan laskentaan saatu pelimäärä eivät ole vähintään 11 tulos = Vakiot.SYOTE_VIRHE_UUDEN_PELAAJAN_OTTELUT_VAHINT_11; break; } syotteet.UudenPelaajanPelitEnsinLKM = selopelaaja.UudenPelaajanPelitLKM; } // tässä siis voi olla vahvuusluku tai 0 syotteet.YksiVastustajaTulosnapit = tulos; // vain jos otteluita ei jo ole listalla (ja TarkistaVastustajanSelo palautti kelvollisen vahvuusluvun), // niin tarkista ottelutuloksen valintanapit -> TarkistaOttelunTulos() // ja sitten lisää tämä yksi ottelu listaan! if (syotteet.Ottelut.Lukumaara == 0) { // // Vastustajan vahvuusluku on nyt vastustajanSeloYksittainen-kentässä // Haetaan vielä ottelunTulos -kenttään tulospisteet tuplana (0=tappio,1=tasapeli,2=voitto) // Tarvitaan tulos (voitto, tasapeli tai tappio) if ((tulos = TarkistaOttelunTulos(syotteet.OttelunTulos)) == Vakiot.SYOTE_VIRHE_BUTTON_TULOS) { break; } // Nyt voidaan tallentaa ainoan ottelun tiedot listaan (vastustajanSelo, ottelunTulos), josta // ne on helppo hakea laskennassa. // Myös vastustajanSeloYksittainen jää alustetuksi, koska siitä nähdään että vahvuusluku oli // annettu erikseen, jolloin myös ottelun tuloksen on oltava annettuna valintapainikkeilla. syotteet.Ottelut.LisaaOttelunTulos(syotteet.YksiVastustajaTulosnapit, syotteet.OttelunTulos); } tulos = Vakiot.SYOTE_STATUS_OK; // syötekentät OK, jos päästy tänne asti ja ottelu/ottelut ovat listassa } while (false); // Virheen käsittelyt ja virheilmoitus ovat kutsuvissa rutiineissa return(tulos); }
// Pelaa kaikki listalta (syotteet.Ottelut) löytyvät ottelut! // // Tapaukset: // 1) Uuden pelaajan laskenta, jossa tulokset formaatissa "1.5 1622 1880 1683" // 2) Normaali laskenta, jossa käydään kaikki listan ottelut läpi, tulokset "+1525 =1600 -1611 +1558" // 3) Uuden pelaajan laskenta, jossa tulokset formaatissa "1.5 1622 1880 1683" // // Päivittää: UusiSelo, UusiPelimaara, turnauksenTulos, MinSelo ja MaxSelo // Palauttaa: - // public void PelaaKaikkiOttelut(Syotetiedot syotteet) { if (syotteet == null) { throw new ArgumentNullException(nameof(syotteet)); } // XXX: KÄSITTELE ERIKOISTAPAUS, JOSSA ON VAIN annettuTurnauksenTulos2x, VastustajienLkm ja Turnauksenkeskivahvuus // XXX: silloin ei ole ottelulistaa Ottelulista ottelulista = syotteet.Ottelut; // asettaa omat tiedot (selo ja pelimäärä) seloPelaaja-luokkaan, nollaa tilastotiedot ym. AlustaLaskenta(syotteet); VastustajaMin = ottelulista.VastustajaMin; VastustajaMax = ottelulista.VastustajaMax; // XXX: Kun ensimmäinen ottelu, niin UusiSelo ja UusiPelimaara ovat käyttäjän antamat alkuarvot omaSelo ja pelimaara // XXX: Laskennan edetessä niitä päivitetään // Erikoistapauksena uuden pelaajan tuloksien laskenta turnauksesta, // jossa tulokset on ilmoitettu formaatissa "1.5 1622 1880 1683" // if (OnkoAnnettuTurnauksenTulos && UudenPelaajanLaskenta) { // selo += pistemäärä - ottelut/2 * 200 // 1 ottelu: // 1525 + 0.5 1525 -> tulos 1525 // 2 ottelua: // 2 1525 1441 summa: 2966 keskim. 1483 tulos on keskim+200 // keskitulos/matsi = 1 // apumuuttujia (lausekkeiden selkiyttämiseksi ja lyhentämiseksi) float keskimTulos = (annettuTurnauksenTulos2x / 2F) / VastustajienLkm; // 0.0 - 1.0 float muutos = 400 * (keskimTulos - 0.5F) + 0.5F; // tuloksella tasapeli pysytään samassa kuin keskimTulos // vanhan selon painoarvo ja uuden lasketun selon painoarvo riippuvat pelimääristä UusiSelo = ((UusiSelo * UusiPelimaara) + (int)(TurnauksenKeskivahvuus + muutos) * VastustajienLkm) / (UusiPelimaara + VastustajienLkm); UusiPelimaara += VastustajienLkm; // turnauksen tulos annettu, joten ei laskettavaa TurnauksenTulos2x = annettuTurnauksenTulos2x; // koska laskenta tehtiin kerralla, ei saatu minSeloa ja maxSeloa MinSelo = UusiSelo; MaxSelo = UusiSelo; //return; } else { // Varsinainen laskenta: Käydään läpi kaikki listan ottelut, jotka olivat formaatissa // "+1525 =1600 -1611 +1558". Tällöin myös MinSelo ja MaxSelo voidaan selvittää. // var ottelu = ottelulista.HaeEnsimmainen(); // vastustajanSelo, ottelunTulos int pelattuLKM = 0; // Kun lista on tyhjä, saadaan ottelun tulos TULOS_MAARITTELEMATON while (ottelu.Item2 != Vakiot.OttelunTulos_enum.TULOS_MAARITTELEMATON) { // päivitä seloa ja tilastoja jokaisen ottelun laskennassa, myös laske Odotustulos UusiSelo = PelaaOttelu(ottelu.Item1, ottelu.Item2, (syotteet.UudenPelaajanPelitEnsinLKM > 0 && pelattuLKM >= syotteet.UudenPelaajanPelitEnsinLKM)); // päivitä pelimäärää vain jos oli annettu if (UusiPelimaara != Vakiot.PELIMAARA_TYHJA) { UusiPelimaara++; pelattuLKM++; } ottelu = ottelulista.HaeSeuraava(); } // Onko pikashakin pelit annettu "väärässä formaatissa", kun pitäisi olla esim. "1.5 1622 1880 1683" if (!OnkoAnnettuTurnauksenTulos && alkuperaisetSyotteet.Miettimisaika <= Vakiot.Miettimisaika_enum.MIETTIMISAIKA_ENINT_10MIN) { // asetetaan turnauksen tulokseksi otteluista "laskettu" tulos // ja sitten voidaan käyttää oikeaa pikashakin vahvuusluvun laskentakaavaa SetAnnettuTurnauksenTulos(TurnauksenTulos2x / 2.0F); } // Entä jos vanhan pelaajan ottelut olivatkin formaatissa "1.5 1622 1880 1683"? // Jos näin oli, niin unohdetaan vanha laskenta, josta käytetään vain Odotustulos sekä UusiPelimaara. // // HUOM! Seuraava ei toimisi uudella pelaajalla, mutta se erikoistapaus onkin käsitelty aiemmin // if (OnkoAnnettuTurnauksenTulos) { // // Aiemmasta laskennasta tarvitaan Odotustulos // apumuuttuja selo, koska sitä tarvitaan kaavassa usein // int vanha = alkuperaisetSyotteet.AlkuperainenSelo; // aloitetaan alusta, oma apumuuttuja TurnauksenTulos2x = annettuTurnauksenTulos2x; // turnauksen tulos annettu, joten ei laskettavaa if (alkuperaisetSyotteet.Miettimisaika <= Vakiot.Miettimisaika_enum.MIETTIMISAIKA_ENINT_10MIN) { // // PELO: pikashakilla on oma laskentakaavansa // // http://skore.users.paivola.fi/selo.html kertoo: // Pikashakin laskennassa Odotustulos lasketaan samoin, mutta ilman 0,85 - sääntöä. // Itse laskentakaava onkin sitten hieman vaikeampi: // pelo = vanha pelo + 200 - 200 * e(Odotustulos - tulos) / 10 , kun saavutettu tulos on odotustulosta suurempi // pelo = vanha pelo - 200 + 200 * e(tulos - Odotustulos) / 10 , kun saavutettu tulos on odotustulosta pienempi // Loppuosan pitää olla e((tulos - Odotustulos) / 10) eli sulut lisää, jakolasku ensin. // // turnauksen tulos on kokonaislukuna, pitää jakaa 2:lla // Odotustulos on kokonaisluku ja pitää jakaa 100:lla // // Laskentakaavaan lisätty pyöristys Math.Round, jonka jälkeen kaikista Joukkuepikashakin laskennoista saadaan samat tulokset if ((annettuTurnauksenTulos2x / 2.0) > (Odotustulos / 100.0)) { UusiSelo = (int)Math.Round(vanha + 200.0 - 200.0 * Math.Pow(Math.E, (Odotustulos / 100.0 - annettuTurnauksenTulos2x / 2.0) / 10.0) + 0.0001); } else { UusiSelo = (int)Math.Round(vanha - 200.0 + 200.0 * Math.Pow(Math.E, (annettuTurnauksenTulos2x / 2.0 - Odotustulos / 100.0) / 10.0) + 0.0001); } } else { // // SELO: pidemmän miettimisajan pelit eli > 10 min // float lisakerroin = MaaritaLisakerroin(vanha, alkuperaisetSyotteet.Miettimisaika); // Lisätään vielä pelattujen pelien lkm * 0.1 UusiSelo = (int)Math.Round((vanha + MaaritaKerroin(vanha) * lisakerroin * (annettuTurnauksenTulos2x / 2.0 - Odotustulos / 100.0)) + ottelulista.Lukumaara * 0.1 + 0.0001); } // koska laskenta tehtiin kerralla, ei saatu minSeloa ja maxSeloa MinSelo = UusiSelo; MaxSelo = UusiSelo; } } Suoritusluku = LaskeSuoritusluku(syotteet.Ottelut); SuorituslukuFIDE = LaskeSuorituslukuFIDE(); SuorituslukuLineaarinen = LaskeSuorituslukuLineaarinen(); }