private int LaskeSuoritusluku(Ottelulista ottelut) { if (ottelut == null) { throw new ArgumentNullException(nameof(ottelut)); } double low = MinRating; double high = MaxRating; double guess, we; int i; int elo; int scorex100 = (int)Math.Round(100F * (TurnauksenTulos2x / 2F)); if (scorex100 < 50) { return(-9999); } if (100 * VastustajienLkm - scorex100 < 1) { return(9999); } while (high - low > Epsilon) { we = 0.0; guess = (low + high) / 2.0; for (i = 0; i < VastustajienLkm; i++) { elo = (i == 0) ? ottelut.HaeEnsimmainen().Item1 : ottelut.HaeSeuraava().Item1; we += 0.5 * (1 + Erf((guess - elo) / 400F)); } if (100 * we < scorex100) { low = guess; } else { high = guess; } } return((int)Math.Round(high + 0.000001)); }
// 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(); }