From 9ce77b22f42cbb2affba548bdeed361026f53d4e Mon Sep 17 00:00:00 2001 From: victor-zheng-codes Date: Mon, 25 Dec 2023 00:52:02 -0500 Subject: [PATCH 01/13] configured correct ratings --- src/ratings_calculator/rating_tests.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/ratings_calculator/rating_tests.py b/src/ratings_calculator/rating_tests.py index 7062018..fe83142 100644 --- a/src/ratings_calculator/rating_tests.py +++ b/src/ratings_calculator/rating_tests.py @@ -4,11 +4,11 @@ class TestBasicFunctionalities(unittest.TestCase): - def test_default_CFC_ratings(self) -> None: + def test_default_CFC_ratings_without_bonus(self) -> None: ratings = RatingsCalculator() - new_rating = ratings.established_ratings(1450, 1450, 4, [1237, 1511, 1214, 1441, 1579, 2133]) + new_rating = ratings.established_ratings(1600, 1450, 4, [1237, 1511, 1214, 1441, 1579, 2133]) print("New Rating is: ", new_rating) - self.assertEqual(new_rating, 1516.12) # add assertion here + self.assertEqual(int(new_rating), 1496) # this is from the handbook def test_null_values(self) -> None: ratings = RatingsCalculator() From 9d2c105447cf923ce9ff7b72048f987d3ea61676 Mon Sep 17 00:00:00 2001 From: victor-zheng-codes Date: Mon, 25 Dec 2023 01:03:59 -0500 Subject: [PATCH 02/13] add additional test cases --- src/ratings_calculator/rating_tests.py | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/src/ratings_calculator/rating_tests.py b/src/ratings_calculator/rating_tests.py index fe83142..98eec89 100644 --- a/src/ratings_calculator/rating_tests.py +++ b/src/ratings_calculator/rating_tests.py @@ -22,6 +22,27 @@ def test_one_game(self) -> None: print("New Rating is: ", new_rating) self.assertEqual(new_rating, 1200) # add assertion here + def test_victor_october_open(self) -> None: + # test the calc against perf at october open for 150532 + ratings = RatingsCalculator() + new_rating = ratings.established_ratings(2160, 2014, 3, [2276, 2151, 1972, 2155, 2100]) + print("New Rating is: ", new_rating) + self.assertEqual(round(new_rating), 2073) # add assertion here + + def test_victor_can_junior_open(self) -> None: + # test the calc against perf at october open for 150532 + ratings = RatingsCalculator() + new_rating = ratings.established_ratings(2160, 2068, 2.5, [1949, 1982, 1892, 1843, 1898, 1936]) + print("New Rating is: ", new_rating) + self.assertEqual(round(new_rating), 2014) # add assertion here + + def test_victor(self) -> None: + # test the calc against perf at october open for 150532 + ratings = RatingsCalculator() + new_rating = ratings.established_ratings(2160, 2068, 2.5, [1949, 1982, 1892, 1843, 1898, 1936]) + print("New Rating is: ", new_rating) + self.assertEqual(round(new_rating), 2014) # add assertion here + if __name__ == '__main__': unittest.main() From c07e7a87b981d3b8acd39995383808ddb14e0328 Mon Sep 17 00:00:00 2001 From: victor-zheng-codes Date: Mon, 25 Dec 2023 10:25:07 -0500 Subject: [PATCH 03/13] configured correct quick ratings --- src/ratings_calculator/main.py | 11 +++++++++-- src/ratings_calculator/rating_tests.py | 19 +++++++++++++------ 2 files changed, 22 insertions(+), 8 deletions(-) diff --git a/src/ratings_calculator/main.py b/src/ratings_calculator/main.py index 666228b..4f753b8 100644 --- a/src/ratings_calculator/main.py +++ b/src/ratings_calculator/main.py @@ -51,6 +51,8 @@ RChangeThreshold = 13 (a constant). The numerical values in the bonus point equation may be adjusted from time to time by the Rating Auditor as deemed necessary and in consultation with the CFC Executive. + +From 411.1 Quick Chess, quick ratings will be calculated with 1/2 of the k factor """ """ @@ -90,14 +92,15 @@ def provisional_unrated_players(self, ratings: list, wins: int, losses: int, gam Rp = Rc + 400 * (wins - losses) / games_played return Rp - def established_ratings(self, all_time_high_score: int, old_rating: int, score: float, opponent_ratings: list) -> float: + def established_ratings(self, all_time_high_score: int, old_rating: int, score: float, opponent_ratings: list, quick = False) -> float: """ Returns the established rating from the dictionary of ratings :param all_time_high_score: the all time high of the player :param score: the total score from the tournament :param opponent_ratings: a list of opponent ratings :param old_rating: the player's old rating - :return: returns the established rating of the player. + :param quick: if the time control used is between 5 and 14 minutes + :return: returns the established rating of the player >>> ratingTest = RatingsCalculator() >>> # expected_dict = ratingTest.expected_scores_init() >>> ratingTest.established_ratings(1450, 1450, 4, [1237, 1511, 1214, 1441, 1579, 2133]) @@ -110,6 +113,10 @@ def established_ratings(self, all_time_high_score: int, old_rating: int, score: else: multiplier = 32 + # if quick (where time control used is between 5-14 min) + if quick: + multiplier = multiplier // 2 + # expected_scores_dict = self.expected_scores_init() expected_total = self.expected_total_score(old_rating, opponent_ratings) diff --git a/src/ratings_calculator/rating_tests.py b/src/ratings_calculator/rating_tests.py index 98eec89..3bf6f4d 100644 --- a/src/ratings_calculator/rating_tests.py +++ b/src/ratings_calculator/rating_tests.py @@ -1,5 +1,4 @@ import unittest - from src.ratings_calculator.main import RatingsCalculator @@ -18,9 +17,9 @@ def test_null_values(self) -> None: def test_one_game(self) -> None: ratings = RatingsCalculator() - new_rating = ratings.established_ratings(1000, 1000, 1, [1200]) + new_rating = ratings.established_ratings(2160, 2052, 0, [2208]) print("New Rating is: ", new_rating) - self.assertEqual(new_rating, 1200) # add assertion here + self.assertEqual(round(new_rating), 2043) # add assertion here def test_victor_october_open(self) -> None: # test the calc against perf at october open for 150532 @@ -36,12 +35,20 @@ def test_victor_can_junior_open(self) -> None: print("New Rating is: ", new_rating) self.assertEqual(round(new_rating), 2014) # add assertion here - def test_victor(self) -> None: + def test_quick_ratings_blitz(self) -> None: # test the calc against perf at october open for 150532 ratings = RatingsCalculator() - new_rating = ratings.established_ratings(2160, 2068, 2.5, [1949, 1982, 1892, 1843, 1898, 1936]) + # Can transnational of 14 games + new_rating = ratings.established_ratings(1892, 2002, 7, [1927, 1876, 1899, 1841, 1429], quick=True) print("New Rating is: ", new_rating) - self.assertEqual(round(new_rating), 2014) # add assertion here + self.assertAlmostEquals(round(new_rating), 2092, 3) # add assertion here + + def test_quick_ratings_with_lifetime_high(self) -> None: + # test the calc against perf at october open for 150532 + ratings = RatingsCalculator() + new_rating = ratings.established_ratings(2002, 2002, 5, [1927, 1876, 1899, 1841, 1429], quick=False) + print("New Rating is: ", new_rating) + self.assertAlmostEquals(round(new_rating), 2092, 3) # add assertion here if __name__ == '__main__': From 023c41a18f3ddf2ba59da830155d79cbbf8b61b5 Mon Sep 17 00:00:00 2001 From: victor-zheng-codes Date: Mon, 25 Dec 2023 10:40:46 -0500 Subject: [PATCH 04/13] updated test cases for quick rating blitz --- src/ratings_calculator/main.py | 3 +++ src/ratings_calculator/rating_tests.py | 12 +++++++----- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/ratings_calculator/main.py b/src/ratings_calculator/main.py index 4f753b8..7dcd4f8 100644 --- a/src/ratings_calculator/main.py +++ b/src/ratings_calculator/main.py @@ -106,6 +106,9 @@ def established_ratings(self, all_time_high_score: int, old_rating: int, score: >>> ratingTest.established_ratings(1450, 1450, 4, [1237, 1511, 1214, 1441, 1579, 2133]) 1487 """ + if score > len(opponent_ratings): + raise Exception("Cannot have higher score than games played") + # K=32 for players under 2200 and K=16 for players at or over 2200; # Rn = Ro + 32 (S - Sx) if old_rating >= 2199: diff --git a/src/ratings_calculator/rating_tests.py b/src/ratings_calculator/rating_tests.py index 3bf6f4d..6d08352 100644 --- a/src/ratings_calculator/rating_tests.py +++ b/src/ratings_calculator/rating_tests.py @@ -35,20 +35,22 @@ def test_victor_can_junior_open(self) -> None: print("New Rating is: ", new_rating) self.assertEqual(round(new_rating), 2014) # add assertion here - def test_quick_ratings_blitz(self) -> None: + def test_quick_ratings_gtcl_rapid(self) -> None: # test the calc against perf at october open for 150532 ratings = RatingsCalculator() # Can transnational of 14 games - new_rating = ratings.established_ratings(1892, 2002, 7, [1927, 1876, 1899, 1841, 1429], quick=True) + new_rating = ratings.established_ratings(2002, 2002, 5, [1927, 1876, 1899, 1841, 1429], quick=False) print("New Rating is: ", new_rating) - self.assertAlmostEquals(round(new_rating), 2092, 3) # add assertion here + # within three points + self.assertTrue(abs(round(new_rating) - 2092) <= 3) # add assertion here def test_quick_ratings_with_lifetime_high(self) -> None: # test the calc against perf at october open for 150532 ratings = RatingsCalculator() - new_rating = ratings.established_ratings(2002, 2002, 5, [1927, 1876, 1899, 1841, 1429], quick=False) + new_rating = ratings.established_ratings(1856, 1856, 9, [1431, 1431, 2024, 2024, 1478, 1478, 1710, 1710, 1938, 1938, 1535, 1535, 1986, 1986], quick=False) print("New Rating is: ", new_rating) - self.assertAlmostEquals(round(new_rating), 2092, 3) # add assertion here + self.assertEqual(round(new_rating), 1882) # add assertion here + # note for this tournament that this should be set to quick as true? if __name__ == '__main__': From 02e72c6d669e6ae440aad02d2928ea6039dfa2ae Mon Sep 17 00:00:00 2001 From: victor-zheng-codes Date: Mon, 25 Dec 2023 12:14:55 -0500 Subject: [PATCH 05/13] added notes regarding todos --- src/ratings_calculator/main.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/src/ratings_calculator/main.py b/src/ratings_calculator/main.py index 7dcd4f8..11f9f18 100644 --- a/src/ratings_calculator/main.py +++ b/src/ratings_calculator/main.py @@ -52,7 +52,20 @@ The numerical values in the bonus point equation may be adjusted from time to time by the Rating Auditor as deemed necessary and in consultation with the CFC Executive. -From 411.1 Quick Chess, quick ratings will be calculated with 1/2 of the k factor +TODO: From 411.1 Quick Chess, quick ratings will be calculated with 1/2 of the k factor +- this appears to not be in effect? + +TODO: This is not covered by the current calculation: +416. For a player with a pre-event rating below 800. + +TODO: If a player’s post tournament rating (including any participation and bonus points) is less than 200, the player is entered in the rating list at 200. This applies to both provisional and permanent ratings. + +TODO: Provisional ratings: 3 to 24 games + + +TODO: For players who start a tournament above 2200 and during the event drop below 2200, we currently assume that the k factor remians the same. +However, this is not correct as the ratings can change. + """ """ From 41962335ce5d1acc27e3171be0b69cdf5cb919a9 Mon Sep 17 00:00:00 2001 From: victor-zheng-codes Date: Mon, 25 Dec 2023 12:35:16 -0500 Subject: [PATCH 06/13] added files for player data and test users' info --- src/ratings_calculator/main.py | 40 +- src/ratings_calculator/player_info.json | 1540 +++++++++++++++++++++++ 2 files changed, 1576 insertions(+), 4 deletions(-) create mode 100644 src/ratings_calculator/player_info.json diff --git a/src/ratings_calculator/main.py b/src/ratings_calculator/main.py index 11f9f18..94f1de2 100644 --- a/src/ratings_calculator/main.py +++ b/src/ratings_calculator/main.py @@ -1,4 +1,4 @@ -import math +import math, requests from csv import reader """ @@ -73,6 +73,37 @@ """ +class Profile: + """User id of the user that we are trying to get data for""" + + # default constructor for ratings calculator + def __init__(self, user_id: int) -> None: + self.user_id = user_id + self.profile = self.initialize_profile() + return + + def initialize_profile(self) -> dict: + """ + Gets the profile of the user + :return: json dictionary mapping of the player and its fields + """ + URL = f"https://server.chess.ca/api/player/v1/{self.user_id}" + page = requests.get(URL) + return page.json() + + def get_events(self) -> int: + """ + Gets the number of events that this user has participated in + :return: events that this user has played in + """ + numEvents = 0 + if self.profile["player"]["events"] == []: + return 0 + + else: + return len(self.profile["player"]["events"]) + + class RatingsCalculator: """Ratings Calculator is a class that calculates ratings, cfc-style""" @@ -105,7 +136,8 @@ def provisional_unrated_players(self, ratings: list, wins: int, losses: int, gam Rp = Rc + 400 * (wins - losses) / games_played return Rp - def established_ratings(self, all_time_high_score: int, old_rating: int, score: float, opponent_ratings: list, quick = False) -> float: + def established_ratings(self, all_time_high_score: int, old_rating: int, score: float, opponent_ratings: list, + quick=False) -> float: """ Returns the established rating from the dictionary of ratings :param all_time_high_score: the all time high of the player @@ -286,7 +318,7 @@ def expected_scores_init(self) -> dict: n = int(numInput) ratings_list = [] - for i in range(1, n+1): + for i in range(1, n + 1): ele = input("Rating of player " + str(i) + ": ") if not ele.isnumeric(): @@ -313,7 +345,7 @@ def expected_scores_init(self) -> dict: if rating_type == 1: # new_rating = established_ratings(1450, 1450, 4, [1237, 1511, 1214, 1441, 1579, 2133]) - calc_new_rating = ratingsCalc.established_ratings(all_time_high, current_rating, (wins+draws), ratings_list) + calc_new_rating = ratingsCalc.established_ratings(all_time_high, current_rating, (wins + draws), ratings_list) else: calc_new_rating = ratingsCalc.provisional_unrated_players(ratings_list, n, wins, losses) diff --git a/src/ratings_calculator/player_info.json b/src/ratings_calculator/player_info.json new file mode 100644 index 0000000..6cd648c --- /dev/null +++ b/src/ratings_calculator/player_info.json @@ -0,0 +1,1540 @@ +{ + "updated": "2023-12-21", + "player": { + "cfc_id": 150532, + "cfc_expiry": "2024-06-30", + "fide_id": 2620189, + "name_first": "Victor", + "name_last": "Zheng", + "addr_city": "Toronto", + "addr_province": "ON", + "regular_rating": 2073, + "regular_indicator": 2160, + "quick_rating": 2092, + "quick_indicator": 2092, + "events": [ + { + "id": 202312054, + "name": "2023 GTCL Cup U1800", + "date_end": "2023-11-27", + "rating_type": "Q", + "games_played": 5, + "score": 5.0, + "rating_pre": 2002, + "rating_perf": 2202, + "rating_post": 2092, + "rating_indicator": 2092 + }, + { + "id": 202311050, + "name": "2023 ACC Hallowe'en Rapid", + "date_end": "2023-10-30", + "rating_type": "Q", + "games_played": 5, + "score": 4.0, + "rating_pre": 1882, + "rating_perf": 2132, + "rating_post": 2002, + "rating_indicator": 2002 + }, + { + "id": 202310075, + "name": "Hart House October Open - Crown", + "date_end": "2023-10-22", + "rating_type": "R", + "games_played": 5, + "score": 3.0, + "rating_pre": 2014, + "rating_perf": 2211, + "rating_post": 2073, + "rating_indicator": 2160 + }, + { + "id": 202307026, + "name": "2023 Canadian Junior Open", + "date_end": "2023-07-09", + "rating_type": "R", + "games_played": 6, + "score": 3.5, + "rating_pre": 2068, + "rating_perf": 1850, + "rating_post": 2014, + "rating_indicator": 2160 + }, + { + "id": 202306079, + "name": "Cdn Transnational Blitz General", + "date_end": "2023-06-03", + "rating_type": "Q", + "games_played": 14, + "score": 9.0, + "rating_pre": 1856, + "rating_perf": 1843, + "rating_post": 1882, + "rating_indicator": 1882 + }, + { + "id": 202303124, + "name": "5th Ivy League Challenge", + "date_end": "2023-03-26", + "rating_type": "R", + "games_played": 1, + "score": 1.0, + "rating_pre": 2050, + "rating_perf": 2503, + "rating_post": 2068, + "rating_indicator": 2160 + }, + { + "id": 202302001, + "name": "CUCC - Championship Section", + "date_end": "2023-01-29", + "rating_type": "R", + "games_played": 5, + "score": 2.5, + "rating_pre": 2043, + "rating_perf": 2080, + "rating_post": 2050, + "rating_indicator": 2160 + }, + { + "id": 202212022, + "name": "Hart House Holidays Open - Crown", + "date_end": "2022-12-11", + "rating_type": "R", + "games_played": 1, + "score": 0.0, + "rating_pre": 2052, + "rating_perf": 1808, + "rating_post": 2043, + "rating_indicator": 2160 + }, + { + "id": 202212042, + "name": "GTCL Cup Crown", + "date_end": "2022-12-05", + "rating_type": "Q", + "games_played": 5, + "score": 2.0, + "rating_pre": 1818, + "rating_perf": 1943, + "rating_post": 1856, + "rating_indicator": 1856 + }, + { + "id": 202211105, + "name": "Toronto Closed - Reserves", + "date_end": "2022-11-21", + "rating_type": "R", + "games_played": 9, + "score": 3.0, + "rating_pre": 2139, + "rating_perf": 1905, + "rating_post": 2052, + "rating_indicator": 2160 + }, + { + "id": 202209014, + "name": "NAYCC U18 Open", + "date_end": "2022-08-14", + "rating_type": "R", + "games_played": 9, + "score": 4.5, + "rating_pre": 2160, + "rating_perf": 2105, + "rating_post": 2139, + "rating_indicator": 2160 + }, + { + "id": 202205047, + "name": "2022 Ontario Open - U2200", + "date_end": "2022-05-23", + "rating_type": "R", + "games_played": 6, + "score": 5.0, + "rating_pre": 1918, + "rating_perf": 2336, + "rating_post": 2160, + "rating_indicator": 2160 + }, + { + "id": 202002048, + "name": "BC Open Premier", + "date_end": "2020-02-17", + "rating_type": "R", + "games_played": 6, + "score": 2.5, + "rating_pre": 1950, + "rating_perf": 1841, + "rating_post": 1918, + "rating_indicator": 2000 + }, + { + "id": 202001037, + "name": "2020 Victoria Open - Premier", + "date_end": "2020-01-19", + "rating_type": "R", + "games_played": 5, + "score": 3.0, + "rating_pre": 1965, + "rating_perf": 1898, + "rating_post": 1950, + "rating_indicator": 2000 + }, + { + "id": 201911058, + "name": "2019 BC Junior Championship", + "date_end": "2019-11-11", + "rating_type": "R", + "games_played": 5, + "score": 4.0, + "rating_pre": 1942, + "rating_perf": 2051, + "rating_post": 1965, + "rating_indicator": 2000 + }, + { + "id": 201910042, + "name": "New West Fall Open Premier", + "date_end": "2019-10-14", + "rating_type": "R", + "games_played": 6, + "score": 3.0, + "rating_pre": 1979, + "rating_perf": 1818, + "rating_post": 1942, + "rating_indicator": 2000 + }, + { + "id": 201908008, + "name": "New West Summer Open Premier", + "date_end": "2019-08-05", + "rating_type": "R", + "games_played": 6, + "score": 3.0, + "rating_pre": 1976, + "rating_perf": 1985, + "rating_post": 1979, + "rating_indicator": 2000 + }, + { + "id": 201905094, + "name": "Paul Keres Memorial Premier", + "date_end": "2019-05-20", + "rating_type": "R", + "games_played": 6, + "score": 2.0, + "rating_pre": 2000, + "rating_perf": 1904, + "rating_post": 1976, + "rating_indicator": 2000 + }, + { + "id": 201904097, + "name": "GPO 2019 Premier", + "date_end": "2019-04-22", + "rating_type": "R", + "games_played": 6, + "score": 3.0, + "rating_pre": 1961, + "rating_perf": 2038, + "rating_post": 2000, + "rating_indicator": 2000 + }, + { + "id": 201903041, + "name": "2019 BCYCC U16 Open", + "date_end": "2019-03-10", + "rating_type": "R", + "games_played": 4, + "score": 3.0, + "rating_pre": 1978, + "rating_perf": 1601, + "rating_post": 1961, + "rating_indicator": 1978 + }, + { + "id": 201902048, + "name": "2019 BC Open Premier", + "date_end": "2019-02-18", + "rating_type": "R", + "games_played": 6, + "score": 3.0, + "rating_pre": 1951, + "rating_perf": 1984, + "rating_post": 1978, + "rating_indicator": 1978 + }, + { + "id": 201901053, + "name": "2019 Victoria Open Premier", + "date_end": "2019-01-20", + "rating_type": "R", + "games_played": 5, + "score": 3.5, + "rating_pre": 1869, + "rating_perf": 2073, + "rating_post": 1951, + "rating_indicator": 1951 + }, + { + "id": 201811026, + "name": "2018 BC Junior Championship", + "date_end": "2018-11-12", + "rating_type": "R", + "games_played": 5, + "score": 3.0, + "rating_pre": 1876, + "rating_perf": 1799, + "rating_post": 1869, + "rating_indicator": 1876 + }, + { + "id": 201810029, + "name": "New West Open 2018 Premier", + "date_end": "2018-10-08", + "rating_type": "R", + "games_played": 6, + "score": 4.0, + "rating_pre": 1772, + "rating_perf": 2016, + "rating_post": 1876, + "rating_indicator": 1876 + }, + { + "id": 201808026, + "name": "2018 Semiahmoo Open Premier", + "date_end": "2018-08-06", + "rating_type": "R", + "games_played": 6, + "score": 1.0, + "rating_pre": 1808, + "rating_perf": 1666, + "rating_post": 1772, + "rating_indicator": 1852 + }, + { + "id": 201804019, + "name": "Grand Pacific Open U2000", + "date_end": "2018-04-02", + "rating_type": "R", + "games_played": 6, + "score": 4.0, + "rating_pre": 1788, + "rating_perf": 1875, + "rating_post": 1808, + "rating_indicator": 1852 + }, + { + "id": 201802024, + "name": "2018 BC Open Premier", + "date_end": "2018-02-12", + "rating_type": "R", + "games_played": 5, + "score": 1.5, + "rating_pre": 1808, + "rating_perf": 1715, + "rating_post": 1788, + "rating_indicator": 1852 + }, + { + "id": 201711054, + "name": "2017 BC Junior Championship", + "date_end": "2017-11-13", + "rating_type": "R", + "games_played": 5, + "score": 3.0, + "rating_pre": 1814, + "rating_perf": 1764, + "rating_post": 1808, + "rating_indicator": 1852 + }, + { + "id": 201705101, + "name": "Keres Memorial Premier", + "date_end": "2017-05-22", + "rating_type": "R", + "games_played": 5, + "score": 2.0, + "rating_pre": 1819, + "rating_perf": 1819, + "rating_post": 1814, + "rating_indicator": 1852 + }, + { + "id": 201704070, + "name": "11th Grand Pacific Open Premier", + "date_end": "2017-04-17", + "rating_type": "R", + "games_played": 6, + "score": 2.0, + "rating_pre": 1814, + "rating_perf": 1825, + "rating_post": 1819, + "rating_indicator": 1852 + }, + { + "id": 201702066, + "name": "2017 BC Open Premier", + "date_end": "2017-02-13", + "rating_type": "R", + "games_played": 6, + "score": 1.5, + "rating_pre": 1849, + "rating_perf": 1706, + "rating_post": 1814, + "rating_indicator": 1852 + }, + { + "id": 201611047, + "name": "2016 BC Junior Championship", + "date_end": "2016-11-13", + "rating_type": "R", + "games_played": 5, + "score": 2.5, + "rating_pre": 1822, + "rating_perf": 1965, + "rating_post": 1849, + "rating_indicator": 1852 + }, + { + "id": 201608025, + "name": "2016 NAYCC U12", + "date_end": "2016-08-11", + "rating_type": "R", + "games_played": 9, + "score": 5.0, + "rating_pre": 1852, + "rating_perf": 1755, + "rating_post": 1822, + "rating_indicator": 1852 + }, + { + "id": 201606004, + "name": "2016 Paul Keres U2000", + "date_end": "2016-05-23", + "rating_type": "R", + "games_played": 6, + "score": 4.5, + "rating_pre": 1717, + "rating_perf": 1970, + "rating_post": 1852, + "rating_indicator": 1852 + }, + { + "id": 201603087, + "name": "10th Grand Pacific Open Premier", + "date_end": "2016-03-28", + "rating_type": "R", + "games_played": 6, + "score": 1.5, + "rating_pre": 1712, + "rating_perf": 1771, + "rating_post": 1717, + "rating_indicator": 1764 + }, + { + "id": 201602021, + "name": "2016 BC Open U1900", + "date_end": "2016-02-08", + "rating_type": "R", + "games_played": 6, + "score": 4.5, + "rating_pre": 1688, + "rating_perf": 1785, + "rating_post": 1712, + "rating_indicator": 1764 + }, + { + "id": 201511042, + "name": "Vancouver West Open #8", + "date_end": "2015-11-15", + "rating_type": "R", + "games_played": 6, + "score": 3.0, + "rating_pre": 1743, + "rating_perf": 1495, + "rating_post": 1688, + "rating_indicator": 1764 + }, + { + "id": 201509048, + "name": "Vancouver West Open #7", + "date_end": "2015-09-27", + "rating_type": "R", + "games_played": 6, + "score": 2.5, + "rating_pre": 1764, + "rating_perf": 1694, + "rating_post": 1743, + "rating_indicator": 1764 + }, + { + "id": 201505066, + "name": "2015 Paul Keres Memorial U2000", + "date_end": "2015-05-18", + "rating_type": "R", + "games_played": 6, + "score": 3.0, + "rating_pre": 1701, + "rating_perf": 1855, + "rating_post": 1764, + "rating_indicator": 1764 + }, + { + "id": 201504043, + "name": "9th Grand Pacific Open U1900", + "date_end": "2015-04-06", + "rating_type": "R", + "games_played": 6, + "score": 4.0, + "rating_pre": 1539, + "rating_perf": 1837, + "rating_post": 1701, + "rating_indicator": 1701 + }, + { + "id": 201504012, + "name": "BCYCC U12 Open", + "date_end": "2015-03-29", + "rating_type": "R", + "games_played": 5, + "score": 3.0, + "rating_pre": 1494, + "rating_perf": 1606, + "rating_post": 1539, + "rating_indicator": 1539 + }, + { + "id": 201503015, + "name": "Vancouver West Open #4", + "date_end": "2015-03-08", + "rating_type": "R", + "games_played": 6, + "score": 3.0, + "rating_pre": 1371, + "rating_perf": 1780, + "rating_post": 1494, + "rating_indicator": 1494 + }, + { + "id": 201502019, + "name": "2015 BC Open U1900", + "date_end": "2015-02-09", + "rating_type": "R", + "games_played": 6, + "score": 2.0, + "rating_pre": 1365, + "rating_perf": 1391, + "rating_post": 1371, + "rating_indicator": 1481 + }, + { + "id": 201501023, + "name": "West Vancouver Open #3", + "date_end": "2015-01-25", + "rating_type": "R", + "games_played": 5, + "score": 1.5, + "rating_pre": 1428, + "rating_perf": 1182, + "rating_post": 1365, + "rating_indicator": 1481 + }, + { + "id": 201412019, + "name": "Vancouver West Open #2", + "date_end": "2014-12-07", + "rating_type": "R", + "games_played": 6, + "score": 2.5, + "rating_pre": 1427, + "rating_perf": 1529, + "rating_post": 1428, + "rating_indicator": 1481 + }, + { + "id": 201411027, + "name": "2014 BC Junior Championship", + "date_end": "2014-11-09", + "rating_type": "R", + "games_played": 5, + "score": 1.5, + "rating_pre": 1399, + "rating_perf": 1576, + "rating_post": 1427, + "rating_indicator": 1481 + }, + { + "id": 201410050, + "name": "2014 Vancouver West Open", + "date_end": "2014-10-26", + "rating_type": "R", + "games_played": 6, + "score": 3.0, + "rating_pre": 1420, + "rating_perf": 1401, + "rating_post": 1399, + "rating_indicator": 1481 + }, + { + "id": 201408021, + "name": "BC Day Open, 2014", + "date_end": "2014-08-04", + "rating_type": "R", + "games_played": 6, + "score": 2.5, + "rating_pre": 1451, + "rating_perf": 1303, + "rating_post": 1420, + "rating_indicator": 1481 + }, + { + "id": 201405043, + "name": "2014 Keres Memorial - U1600", + "date_end": "2014-05-19", + "rating_type": "R", + "games_played": 6, + "score": 4.5, + "rating_pre": 1463, + "rating_perf": 1376, + "rating_post": 1451, + "rating_indicator": 1481 + }, + { + "id": 201404069, + "name": "8th Grand Pacific Open - U1800", + "date_end": "2014-04-21", + "rating_type": "R", + "games_played": 6, + "score": 3.5, + "rating_pre": 1481, + "rating_perf": 1371, + "rating_post": 1463, + "rating_indicator": 1481 + }, + { + "id": 201402017, + "name": "2014 BC Open - U1800", + "date_end": "2014-02-10", + "rating_type": "R", + "games_played": 6, + "score": 4.5, + "rating_pre": 1324, + "rating_perf": 1617, + "rating_post": 1481, + "rating_indicator": 1481 + }, + { + "id": 201311087, + "name": "2013 November VCS Open", + "date_end": "2013-11-24", + "rating_type": "R", + "games_played": 5, + "score": 2.5, + "rating_pre": 1305, + "rating_perf": 1434, + "rating_post": 1324, + "rating_indicator": 1390 + }, + { + "id": 201311050, + "name": "2013 BCCF Junior Reserves", + "date_end": "2013-11-10", + "rating_type": "R", + "games_played": 5, + "score": 3.5, + "rating_pre": 1334, + "rating_perf": 1075, + "rating_post": 1305, + "rating_indicator": 1390 + }, + { + "id": 201311042, + "name": "2013 October VCS U1600", + "date_end": "2013-10-27", + "rating_type": "R", + "games_played": 5, + "score": 2.5, + "rating_pre": 1390, + "rating_perf": 1062, + "rating_post": 1334, + "rating_indicator": 1390 + }, + { + "id": 201311039, + "name": "2013 Vancouver Open", + "date_end": "2013-10-15", + "rating_type": "R", + "games_played": 6, + "score": 3.0, + "rating_pre": 1178, + "rating_perf": 1799, + "rating_post": 1390, + "rating_indicator": 1390 + }, + { + "id": 201309042, + "name": "2013 Vancouver September U1800", + "date_end": "2013-09-22", + "rating_type": "R", + "games_played": 4, + "score": 3.0, + "rating_pre": 1136, + "rating_perf": 1267, + "rating_post": 1178, + "rating_indicator": 1178 + }, + { + "id": 201308025, + "name": "2013 NAYCC - U10", + "date_end": "2013-08-18", + "rating_type": "R", + "games_played": 9, + "score": 5.0, + "rating_pre": 1016, + "rating_perf": 1227, + "rating_post": 1136, + "rating_indicator": 1136 + }, + { + "id": 201304020, + "name": "VCS April Juniors", + "date_end": "2013-04-14", + "rating_type": "R", + "games_played": 3, + "score": 2.0, + "rating_pre": 1013, + "rating_perf": 1042, + "rating_post": 1016, + "rating_indicator": 1016 + }, + { + "id": 201304001, + "name": "7th Annual Grand Pacific Open U1800", + "date_end": "2013-04-01", + "rating_type": "R", + "games_played": 6, + "score": 2.5, + "rating_pre": 859, + "rating_perf": 1348, + "rating_post": 1013, + "rating_indicator": 1013 + }, + { + "id": 201304057, + "name": "2013 Victoria YCC U10", + "date_end": "2013-03-29", + "rating_type": "Q", + "games_played": 5, + "score": 5.0, + "rating_pre": 745, + "rating_perf": 1267, + "rating_post": 1180, + "rating_indicator": 6 + }, + { + "id": 201302029, + "name": "Vancouver Chess School Feb 2013", + "date_end": "2013-02-17", + "rating_type": "R", + "games_played": 3, + "score": 1.0, + "rating_pre": 872, + "rating_perf": 764, + "rating_post": 859, + "rating_indicator": 872 + }, + { + "id": 201212006, + "name": "2012 BC Junior Ch Reserves", + "date_end": "2012-12-02", + "rating_type": "R", + "games_played": 5, + "score": 2.5, + "rating_pre": 836, + "rating_perf": 948, + "rating_post": 872, + "rating_indicator": 872 + }, + { + "id": 201207018, + "name": "2012 Canadian Youth Ch U8 Open", + "date_end": "2012-07-06", + "rating_type": "R", + "games_played": 7, + "score": 4.0, + "rating_pre": 846, + "rating_perf": 791, + "rating_post": 836, + "rating_indicator": 846 + }, + { + "id": 201204025, + "name": "2012 BCYCC U8 Open", + "date_end": "2012-04-22", + "rating_type": "R", + "games_played": 5, + "score": 4.0, + "rating_pre": 745, + "rating_perf": 987, + "rating_post": 846, + "rating_indicator": 846 + }, + { + "id": 201111017, + "name": "2011 BC Junior Ch Boosters", + "date_end": "2011-11-13", + "rating_type": "R", + "games_played": 5, + "score": 3.0, + "rating_pre": 745, + "rating_perf": 682, + "rating_post": 745, + "rating_indicator": 745 + }, + { + "id": 201110029, + "name": "October Junior Open Intermediate", + "date_end": "2011-10-15", + "rating_type": "R", + "games_played": 4, + "score": 1.0, + "rating_pre": 745, + "rating_perf": 530, + "rating_post": 745, + "rating_indicator": 745 + }, + { + "id": 201105009, + "name": "2011 BCYCC U8 Open", + "date_end": "2011-05-01", + "rating_type": "R", + "games_played": 5, + "score": 3.5, + "rating_pre": 684, + "rating_perf": 966, + "rating_post": 745, + "rating_indicator": 745 + }, + { + "id": 201104018, + "name": "2011 BC Chess Challenge Gr 1", + "date_end": "2011-04-09", + "rating_type": "R", + "games_played": 5, + "score": 4.5, + "rating_pre": 633, + "rating_perf": 869, + "rating_post": 684, + "rating_indicator": 684 + }, + { + "id": 201103046, + "name": "Vancouver Regional Grade K-1", + "date_end": "2011-03-12", + "rating_type": "R", + "games_played": 5, + "score": 4.0, + "rating_pre": 595, + "rating_perf": 731, + "rating_post": 633, + "rating_indicator": 633 + }, + { + "id": 201102045, + "name": "2011 Valentines Scholastic Gr 1", + "date_end": "2011-02-20", + "rating_type": "R", + "games_played": 5, + "score": 3.0, + "rating_pre": 590, + "rating_perf": 585, + "rating_post": 595, + "rating_indicator": 595 + }, + { + "id": 201101029, + "name": "New Year Scholastic Grade K to 1", + "date_end": "2011-01-15", + "rating_type": "R", + "games_played": 4, + "score": 4.0, + "rating_pre": 532, + "rating_perf": 795, + "rating_post": 590, + "rating_indicator": 22 + }, + { + "id": 201010050, + "name": "October Junior Beginner U800", + "date_end": "2010-10-23", + "rating_type": "R", + "games_played": 5, + "score": 2.5, + "rating_pre": 441, + "rating_perf": 768, + "rating_post": 532, + "rating_indicator": 18 + }, + { + "id": 201004029, + "name": "2010 BC Provincials Gr K", + "date_end": "2010-04-11", + "rating_type": "R", + "games_played": 3, + "score": 3.0, + "rating_pre": 293, + "rating_perf": 908, + "rating_post": 441, + "rating_indicator": 13 + }, + { + "id": 201002065, + "name": "Vanc Reg Chess Chall Grade K 1", + "date_end": "2010-02-21", + "rating_type": "R", + "games_played": 5, + "score": 1.0, + "rating_pre": 306, + "rating_perf": 260, + "rating_post": 293, + "rating_indicator": 10 + }, + { + "id": 201001048, + "name": "Sprott Shaw Scholastic Gr K to 2", + "date_end": "2010-01-24", + "rating_type": "R", + "games_played": 5, + "score": 2.0, + "rating_pre": 0, + "rating_perf": 296, + "rating_post": 306, + "rating_indicator": 5 + } + ], + "orgarb": [ + { + "id": 202312038, + "name": "Hart House Holidays Open - Crown", + "date_end": "2023-12-10", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "R", + "n_players": 16, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + }, + { + "id": 202312039, + "name": "Hart House Holidays Open - U1300", + "date_end": "2023-12-10", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "R", + "n_players": 47, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + }, + { + "id": 202312040, + "name": "Hart House Holidays Open - U1600", + "date_end": "2023-12-10", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "R", + "n_players": 44, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + }, + { + "id": 202312041, + "name": "Hart House Holidays Open - U1900", + "date_end": "2023-12-10", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "R", + "n_players": 45, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + }, + { + "id": 202312042, + "name": "Hart House Holidays Open - U2200", + "date_end": "2023-12-10", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "R", + "n_players": 36, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + }, + { + "id": 202312043, + "name": "Hart House Holidays Open Jrs U1100", + "date_end": "2023-12-10", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 111715, + "pairings": "SS", + "rating_type": "R", + "n_players": 11, + "n_rounds": 6, + "organizer_name": "Victor Zheng", + "arbiter_name": "Michael Corrie" + }, + { + "id": 202312044, + "name": "Hart House Holidays Open Jrs U800", + "date_end": "2023-12-10", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 111715, + "pairings": "SS", + "rating_type": "R", + "n_players": 13, + "n_rounds": 6, + "organizer_name": "Victor Zheng", + "arbiter_name": "Michael Corrie" + }, + { + "id": 202311052, + "name": "2023 Hart House Rapid Championship", + "date_end": "2023-11-05", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "Q", + "n_players": 25, + "n_rounds": 6, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202311053, + "name": "2023 Hart House Rapid U1200", + "date_end": "2023-11-05", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "Q", + "n_players": 34, + "n_rounds": 6, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202311054, + "name": "2023 Hart House Rapid U1800", + "date_end": "2023-11-05", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "Q", + "n_players": 49, + "n_rounds": 6, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202308085, + "name": "Hart House Summer Junior U1300", + "date_end": "2023-08-26", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "R", + "n_players": 25, + "n_rounds": 4, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202308086, + "name": "Hart House Summer Junior U900", + "date_end": "2023-08-26", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "R", + "n_players": 27, + "n_rounds": 4, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202307041, + "name": "Hart House Summer Blitz Premier", + "date_end": "2023-07-15", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "Q", + "n_players": 64, + "n_rounds": 14, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202307042, + "name": "Hart House Summer Blitz U1500", + "date_end": "2023-07-15", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "Q", + "n_players": 42, + "n_rounds": 14, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202307043, + "name": "Hart House Summer Rapid Premier", + "date_end": "2023-07-15", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "Q", + "n_players": 49, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202307044, + "name": "Hart House Summer Rapid U1000", + "date_end": "2023-07-15", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "Q", + "n_players": 32, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202307045, + "name": "Hart House Summer Rapid U1600", + "date_end": "2023-07-15", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "Q", + "n_players": 42, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202305031, + "name": "Hart House Youth Chess U10", + "date_end": "2023-04-29", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "Q", + "n_players": 25, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202305032, + "name": "Hart House Youth Chess U12", + "date_end": "2023-04-29", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "Q", + "n_players": 38, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202305033, + "name": "Hart House Youth Chess U14", + "date_end": "2023-04-29", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "Q", + "n_players": 48, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202305034, + "name": "Hart House Youth Chess U16", + "date_end": "2023-04-29", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "Q", + "n_players": 10, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202305035, + "name": "Hart House Youth Chess U18", + "date_end": "2023-04-29", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "Q", + "n_players": 8, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202305036, + "name": "Hart House Youth Chess U8", + "date_end": "2023-04-29", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "Q", + "n_players": 26, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202303124, + "name": "5th Ivy League Challenge", + "date_end": "2023-03-26", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "R", + "n_players": 29, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + }, + { + "id": 202303076, + "name": "Hart House Spring Junior Open 2023", + "date_end": "2023-03-18", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "R", + "n_players": 10, + "n_rounds": 4, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202303077, + "name": "Hart House Spring Junior U1300", + "date_end": "2023-03-18", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "R", + "n_players": 29, + "n_rounds": 4, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202303078, + "name": "Hart House Spring Junior U900", + "date_end": "2023-03-18", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "R", + "n_players": 28, + "n_rounds": 4, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202303011, + "name": "GTCL 2023 Bronze Cup", + "date_end": "2023-02-28", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "R", + "n_players": 19, + "n_rounds": 3, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202303012, + "name": "GTCL 2023 Gold Cup", + "date_end": "2023-02-28", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "R", + "n_players": 19, + "n_rounds": 3, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202303013, + "name": "GTCL 2023 Silver Cup", + "date_end": "2023-02-28", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "R", + "n_players": 16, + "n_rounds": 3, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202302081, + "name": "Hart House Reading Week Crown", + "date_end": "2023-02-20", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "R", + "n_players": 25, + "n_rounds": 6, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + }, + { + "id": 202302082, + "name": "Hart House Reading Week U1000", + "date_end": "2023-02-20", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "R", + "n_players": 28, + "n_rounds": 6, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + }, + { + "id": 202302083, + "name": "Hart House Reading Week U1300", + "date_end": "2023-02-20", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "R", + "n_players": 50, + "n_rounds": 6, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + }, + { + "id": 202303001, + "name": "GTCL 2023 Pool B", + "date_end": "2023-02-07", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "R", + "n_players": 33, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202303002, + "name": "GTCL 2023 Pool A", + "date_end": "2023-02-07", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "R", + "n_players": 37, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202302020, + "name": "Hart House Weekly Winter Swiss", + "date_end": "2023-01-31", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 150532, + "pairings": "SS", + "rating_type": "R", + "n_players": 16, + "n_rounds": 4, + "organizer_name": "Victor Zheng", + "arbiter_name": "Victor Zheng" + }, + { + "id": 202212022, + "name": "Hart House Holidays Open - Crown", + "date_end": "2022-12-11", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "R", + "n_players": 22, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + }, + { + "id": 202212023, + "name": "Hart House Holidays Open - U1000", + "date_end": "2022-12-11", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "R", + "n_players": 32, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + }, + { + "id": 202212024, + "name": "Hart House Holidays Open - U1300", + "date_end": "2022-12-11", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "R", + "n_players": 39, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + }, + { + "id": 202212025, + "name": "Hart House Holidays Open - U1600", + "date_end": "2022-12-11", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "R", + "n_players": 57, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + }, + { + "id": 202212026, + "name": "Hart House Holidays Open - U1900", + "date_end": "2022-12-11", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "R", + "n_players": 42, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + }, + { + "id": 202212027, + "name": "Hart House Holidays Open - U2200", + "date_end": "2022-12-11", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "R", + "n_players": 24, + "n_rounds": 5, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + }, + { + "id": 202211086, + "name": "Hart House Rapid & Raffle U1400", + "date_end": "2022-11-12", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "Q", + "n_players": 21, + "n_rounds": 6, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + }, + { + "id": 202211087, + "name": "Hart House Rapid Raffle Fundraiser", + "date_end": "2022-11-12", + "province": "ON", + "organizer_id": 150532, + "arbiter_id": 127516, + "pairings": "SS", + "rating_type": "Q", + "n_players": 29, + "n_rounds": 6, + "organizer_name": "Victor Zheng", + "arbiter_name": "Alex T. Ferreira" + } + ], + "is_organizer": true, + "is_arbiter": true + }, + "apicode": 0, + "error": "" +} From 28f00d4a64abecabfb9bac65fa95e9e92679513d Mon Sep 17 00:00:00 2001 From: victor-zheng-codes Date: Mon, 25 Dec 2023 12:54:08 -0500 Subject: [PATCH 07/13] added basic test functionality for profile --- src/ratings_calculator/main.py | 32 ++++++++++++++++++-------- src/ratings_calculator/rating_tests.py | 10 +++++++- 2 files changed, 32 insertions(+), 10 deletions(-) diff --git a/src/ratings_calculator/main.py b/src/ratings_calculator/main.py index 94f1de2..f8df13e 100644 --- a/src/ratings_calculator/main.py +++ b/src/ratings_calculator/main.py @@ -1,4 +1,4 @@ -import math, requests +import math, requests, json from csv import reader """ @@ -77,21 +77,36 @@ class Profile: """User id of the user that we are trying to get data for""" # default constructor for ratings calculator - def __init__(self, user_id: int) -> None: + def __init__(self, user_id: int, request=True) -> None: self.user_id = user_id - self.profile = self.initialize_profile() + self.profile = self.initialize_profile(request) return - def initialize_profile(self) -> dict: + def initialize_profile(self, request=True) -> dict: """ Gets the profile of the user + :param request: boolean indicating whether to use the web to search for this fvalue or not. :return: json dictionary mapping of the player and its fields """ - URL = f"https://server.chess.ca/api/player/v1/{self.user_id}" - page = requests.get(URL) - return page.json() + if request: + URL = f"https://server.chess.ca/api/player/v1/{self.user_id}" + page = requests.get(URL) + return page.json() + else: + # open the json file and place the file as the value into the page + filepath = "player_info.json" + f = open(filepath) + data = json.load(f) + return data + + def get_profile(self) -> dict: + """ + Gets the profile of the current user + :return: json dictionary mapping of the player and its fields + """ + return self.profile - def get_events(self) -> int: + def get_events_played(self) -> int: """ Gets the number of events that this user has participated in :return: events that this user has played in @@ -99,7 +114,6 @@ def get_events(self) -> int: numEvents = 0 if self.profile["player"]["events"] == []: return 0 - else: return len(self.profile["player"]["events"]) diff --git a/src/ratings_calculator/rating_tests.py b/src/ratings_calculator/rating_tests.py index 6d08352..9b03f48 100644 --- a/src/ratings_calculator/rating_tests.py +++ b/src/ratings_calculator/rating_tests.py @@ -1,5 +1,13 @@ import unittest -from src.ratings_calculator.main import RatingsCalculator +from src.ratings_calculator.main import RatingsCalculator, Profile + + +class TestProfileFunctionality(unittest.TestCase): + def test_name_input_correct(self) -> None: + profile = Profile(150532, False) # for now, input int + profile = profile.get_profile() + self.assertEqual(profile["player"]["name_first"], "Victor") + self.assertEqual(profile["player"]["name_last"], "Zheng") class TestBasicFunctionalities(unittest.TestCase): From a439664603b0864da2a9d4075b129dd307027aed Mon Sep 17 00:00:00 2001 From: victor-zheng-codes Date: Mon, 25 Dec 2023 13:08:09 -0500 Subject: [PATCH 08/13] added last few tournaments played data --- src/ratings_calculator/main.py | 25 +++++++++++++++++++++++-- src/ratings_calculator/rating_tests.py | 8 +++++--- 2 files changed, 28 insertions(+), 5 deletions(-) diff --git a/src/ratings_calculator/main.py b/src/ratings_calculator/main.py index f8df13e..a0b7b1b 100644 --- a/src/ratings_calculator/main.py +++ b/src/ratings_calculator/main.py @@ -58,12 +58,15 @@ TODO: This is not covered by the current calculation: 416. For a player with a pre-event rating below 800. -TODO: If a player’s post tournament rating (including any participation and bonus points) is less than 200, the player is entered in the rating list at 200. This applies to both provisional and permanent ratings. +TODO: If a player’s post tournament rating (including any participation and bonus points) is less than 200, the player +is entered in the rating list at 200. This applies to both provisional and permanent ratings. TODO: Provisional ratings: 3 to 24 games -TODO: For players who start a tournament above 2200 and during the event drop below 2200, we currently assume that the k factor remians the same. +TODO: For players who start a tournament above 2200 and during the event drop below 2200, we currently assume that the +k factor remians the same. + However, this is not correct as the ratings can change. """ @@ -117,6 +120,24 @@ def get_events_played(self) -> int: else: return len(self.profile["player"]["events"]) + def get_last_tournaments(self, num_tournaments: int) -> []: + """ + Gets the number of events that this user has participated in + :param num_tournaments: previous n tournaments to get. + :return: events that this user has played in + """ + + tournament_data = [] + + if num_tournaments > len(self.profile["player"]["events"]): + # if the number of tournaments is greater than the number that exists in the json, take that number + num_tournaments = len(self.profile["player"]["events"]) + + for i in range(num_tournaments): + tournament_data.append(self.profile["player"]["events"][i]) + + return tournament_data + class RatingsCalculator: """Ratings Calculator is a class that calculates ratings, cfc-style""" diff --git a/src/ratings_calculator/rating_tests.py b/src/ratings_calculator/rating_tests.py index 9b03f48..96797a8 100644 --- a/src/ratings_calculator/rating_tests.py +++ b/src/ratings_calculator/rating_tests.py @@ -5,10 +5,12 @@ class TestProfileFunctionality(unittest.TestCase): def test_name_input_correct(self) -> None: profile = Profile(150532, False) # for now, input int - profile = profile.get_profile() - self.assertEqual(profile["player"]["name_first"], "Victor") - self.assertEqual(profile["player"]["name_last"], "Zheng") + profile_dict = profile.get_profile() + self.assertEqual(profile_dict["player"]["name_first"], "Victor") + self.assertEqual(profile_dict["player"]["name_last"], "Zheng") + tour_data = profile.get_last_tournaments(5) + print(tour_data) class TestBasicFunctionalities(unittest.TestCase): def test_default_CFC_ratings_without_bonus(self) -> None: From 23d8bf7d86e72fe3c18f2847b249bdfa2b2413c3 Mon Sep 17 00:00:00 2001 From: victor-zheng-codes Date: Mon, 25 Dec 2023 13:15:46 -0500 Subject: [PATCH 09/13] configured ratings profile information to be in a separate class and main to be separated as well --- src/ratings_calculator/Profile.py | 72 ++++++ src/ratings_calculator/RatingsCalculator.py | 178 ++++++++++++++ src/ratings_calculator/main.py | 242 +------------------- src/ratings_calculator/rating_tests.py | 6 +- 4 files changed, 255 insertions(+), 243 deletions(-) create mode 100644 src/ratings_calculator/Profile.py create mode 100644 src/ratings_calculator/RatingsCalculator.py diff --git a/src/ratings_calculator/Profile.py b/src/ratings_calculator/Profile.py new file mode 100644 index 0000000..d5a0c2c --- /dev/null +++ b/src/ratings_calculator/Profile.py @@ -0,0 +1,72 @@ +"""Class to get cfc profile information of the user""" +import json +import requests + + +class CFCProfile: + """User id of the user that we are trying to get data for""" + + # default constructor for ratings calculator + def __init__(self, user_id: int, request=True) -> None: + self.user_id = user_id + self.profile = self.initialize_profile(request) + return + + def initialize_profile(self, request=True) -> dict: + """ + Gets the profile of the user + :param request: boolean indicating whether to use the web to search for this fvalue or not. + :return: json dictionary mapping of the player and its fields + """ + if request: + URL = f"https://server.chess.ca/api/player/v1/{self.user_id}" + page = requests.get(URL) + return page.json() + else: + # open the json file and place the file as the value into the page + filepath = "player_info.json" + f = open(filepath) + data = json.load(f) + return data + + def get_profile(self) -> dict: + """ + Gets the profile of the current user + :return: json dictionary mapping of the player and its fields + """ + return self.profile + + def get_events_played(self) -> int: + """ + Gets the number of events that this user has participated in + :return: events that this user has played in + """ + numEvents = 0 + if self.profile["player"]["events"] == []: + return 0 + else: + return len(self.profile["player"]["events"]) + + def get_last_tournaments(self, num_tournaments: int) -> []: + """ + Gets the number of events that this user has participated in + :param num_tournaments: previous n tournaments to get. + :return: events that this user has played in + """ + + tournament_data = [] + + if num_tournaments > len(self.profile["player"]["events"]): + # if the number of tournaments is greater than the number that exists in the json, take that number + num_tournaments = len(self.profile["player"]["events"]) + + for i in range(num_tournaments): + tournament_data.append(self.profile["player"]["events"][i]) + + return tournament_data + + +class FIDEProfile: + """Gets user id data for FIDE profile""" + + pass diff --git a/src/ratings_calculator/RatingsCalculator.py b/src/ratings_calculator/RatingsCalculator.py new file mode 100644 index 0000000..ba0659f --- /dev/null +++ b/src/ratings_calculator/RatingsCalculator.py @@ -0,0 +1,178 @@ +import math +from csv import reader + + +class RatingsCalculator: + """Ratings Calculator is a class that calculates ratings, cfc-style""" + + # default constructor for ratings calculator + def __init__(self) -> None: + self.expected_scores = self.expected_scores_init() + + return + + def average_rating(self, ratings: list) -> float: + """ + Average ratings of the list of the ratings + :param ratings: a list of ratings + :return: average rating + """ + ave_rating = 0 + for rating in ratings: + ave_rating += rating + return ave_rating / len(ratings) + + def provisional_unrated_players(self, ratings: list, wins: int, losses: int, games_played: int) -> object: + """ + wins is the number of wins, + losses is the number of losses + games_played is the number of games + ratings is the list of opponent's ratings + """ + Rc = self.average_rating(ratings) + # Rp = Rc + 400 (W - L) / N + Rp = Rc + 400 * (wins - losses) / games_played + return Rp + + def established_ratings(self, all_time_high_score: int, old_rating: int, score: float, opponent_ratings: list, + quick=False) -> float: + """ + Returns the established rating from the dictionary of ratings + :param all_time_high_score: the all time high of the player + :param score: the total score from the tournament + :param opponent_ratings: a list of opponent ratings + :param old_rating: the player's old rating + :param quick: if the time control used is between 5 and 14 minutes + :return: returns the established rating of the player + >>> ratingTest = RatingsCalculator() + >>> # expected_dict = ratingTest.expected_scores_init() + >>> ratingTest.established_ratings(1450, 1450, 4, [1237, 1511, 1214, 1441, 1579, 2133]) + 1487 + """ + if score > len(opponent_ratings): + raise Exception("Cannot have higher score than games played") + + # K=32 for players under 2200 and K=16 for players at or over 2200; + # Rn = Ro + 32 (S - Sx) + if old_rating >= 2199: + multiplier = 16 + else: + multiplier = 32 + + # if quick (where time control used is between 5-14 min) + if quick: + multiplier = multiplier // 2 + + # expected_scores_dict = self.expected_scores_init() + + expected_total = self.expected_total_score(old_rating, opponent_ratings) + new_rating = old_rating + multiplier * (score - expected_total) + + if new_rating > all_time_high_score: + all_time_high_valid = 1 + else: + all_time_high_valid = 0 + + multiplier_e = multiplier // 32 # ratio of the multiplier to ratings under 2200. + bonus = self.bonuses(all_time_high_valid, multiplier_e, new_rating, old_rating, len(opponent_ratings)) + sum_of_scores = new_rating + bonus + return sum_of_scores + + def bonuses(self, a: int, k_factor_e: int, r_new: float, r_old: int, n: int) -> int: + """ + Returns the total sum of all bonuses available + :param a: is = 1 if all time high, 0 otherwise + :param k_factor_e: ratio of the player's k factor to the k factor used for players under 2200 + :param r_new: post-event rating + :param r_old: pre-event rating + :param n: number of games played + :return: value of the new rating + >>> ratingTest = RatingsCalculator() + >>> ratingTest.bonuses(0, 1, 1487, 1450, 6) + 1496 + """ + if n < 4: # no bonus points awarded if less than 4 games played + return 0 + + R_MAX_BONUS = 20 # constants set + R_CHANGE_BONUS = 1.75 + R_CHANGE_THRESHOLD = 13 + + bonus1 = a * R_MAX_BONUS * k_factor_e + threshold = R_CHANGE_THRESHOLD * k_factor_e * math.sqrt(n) + + if r_new > (r_old + threshold): + b = 1 + else: + b = 0 + + bonus2 = b * R_CHANGE_BONUS * (r_new - r_old - threshold) * k_factor_e + total_bonus = round(bonus1 + bonus2) + return total_bonus + + def expected_total_score(self, rating: int, opponent_ratings: list): + """ + Returns the expected total score from the tournament + :param rating: is the player's rating + :param opponent_ratings: is the opponent's rating in a lists + >>> ratingTest = RatingsCalculator() + >>> ratingTest.expected_total_score(1450, [1237, 1511, 1214, 1441, 1579, 2133]) + 2.84 + """ + sum_expected = 0 + for opp_rating in opponent_ratings: + if rating >= opp_rating: # your rating is bigger than the opponents + sum_expected += self.find_expected_value(rating - opp_rating, 0) + else: + sum_expected += self.find_expected_value(opp_rating - rating, 1) + + return sum_expected + + def find_expected_value(self, difference: int, lower_val: int): + """ + Returns the expected value of the individual based on whether it is a lower or higher score. + :param lower_val: whether the number is a lower value + :param difference: is the difference in ratings + :return: expected value based on the expected dictionary + """ + expected_dict = self.expected_scores + expected = expected_dict[int(difference)] + if lower_val == 1: + expected = round(1 - expected, 2) + else: + # keep the older value + pass + + return expected + + def expected_scores_init(self) -> dict: + """ + Returns the expected score dictionary + :return: dictionary containing expected scores + """ + # file is recommend to be in the following format: starting rank, Name of Player, CFC ID + # information of the event + file_name = "C:\\Users\\zheng\\PycharmProjects\\ratings-calculator\\ExpectedScores.csv" + expected_scores_higher = {} + + with open(file_name, 'r') as f: + csv_reader = reader(f) + next(csv_reader) + + for row in csv_reader: + # iterate through each row + # print(row) + # if it's the last line + max_diff = 2000 + if row[0][0:3] == "735": + for j in range(735, max_diff): + expected_scores_higher[j] = 1 + else: + row_vals = row[0].split("-") + starting_val = int(row_vals[0]) + end_val = int(row_vals[1]) + + for j in range(starting_val, end_val + 1): + expected_scores_higher[j] = float(row[1]) # the location of the csv file + + return expected_scores_higher diff --git a/src/ratings_calculator/main.py b/src/ratings_calculator/main.py index a0b7b1b..eff0841 100644 --- a/src/ratings_calculator/main.py +++ b/src/ratings_calculator/main.py @@ -1,5 +1,4 @@ -import math, requests, json -from csv import reader +import RatingsCalculator """ @author Victor Zheng @@ -76,245 +75,6 @@ """ -class Profile: - """User id of the user that we are trying to get data for""" - - # default constructor for ratings calculator - def __init__(self, user_id: int, request=True) -> None: - self.user_id = user_id - self.profile = self.initialize_profile(request) - return - - def initialize_profile(self, request=True) -> dict: - """ - Gets the profile of the user - :param request: boolean indicating whether to use the web to search for this fvalue or not. - :return: json dictionary mapping of the player and its fields - """ - if request: - URL = f"https://server.chess.ca/api/player/v1/{self.user_id}" - page = requests.get(URL) - return page.json() - else: - # open the json file and place the file as the value into the page - filepath = "player_info.json" - f = open(filepath) - data = json.load(f) - return data - - def get_profile(self) -> dict: - """ - Gets the profile of the current user - :return: json dictionary mapping of the player and its fields - """ - return self.profile - - def get_events_played(self) -> int: - """ - Gets the number of events that this user has participated in - :return: events that this user has played in - """ - numEvents = 0 - if self.profile["player"]["events"] == []: - return 0 - else: - return len(self.profile["player"]["events"]) - - def get_last_tournaments(self, num_tournaments: int) -> []: - """ - Gets the number of events that this user has participated in - :param num_tournaments: previous n tournaments to get. - :return: events that this user has played in - """ - - tournament_data = [] - - if num_tournaments > len(self.profile["player"]["events"]): - # if the number of tournaments is greater than the number that exists in the json, take that number - num_tournaments = len(self.profile["player"]["events"]) - - for i in range(num_tournaments): - tournament_data.append(self.profile["player"]["events"][i]) - - return tournament_data - - -class RatingsCalculator: - """Ratings Calculator is a class that calculates ratings, cfc-style""" - - # default constructor for ratings calculator - def __init__(self) -> None: - self.expected_scores = self.expected_scores_init() - - return - - def average_rating(self, ratings: list) -> float: - """ - Average ratings of the list of the ratings - :param ratings: a list of ratings - :return: average rating - """ - ave_rating = 0 - for rating in ratings: - ave_rating += rating - return ave_rating / len(ratings) - - def provisional_unrated_players(self, ratings: list, wins: int, losses: int, games_played: int) -> object: - """ - wins is the number of wins, - losses is the number of losses - games_played is the number of games - ratings is the list of opponent's ratings - """ - Rc = self.average_rating(ratings) - # Rp = Rc + 400 (W - L) / N - Rp = Rc + 400 * (wins - losses) / games_played - return Rp - - def established_ratings(self, all_time_high_score: int, old_rating: int, score: float, opponent_ratings: list, - quick=False) -> float: - """ - Returns the established rating from the dictionary of ratings - :param all_time_high_score: the all time high of the player - :param score: the total score from the tournament - :param opponent_ratings: a list of opponent ratings - :param old_rating: the player's old rating - :param quick: if the time control used is between 5 and 14 minutes - :return: returns the established rating of the player - >>> ratingTest = RatingsCalculator() - >>> # expected_dict = ratingTest.expected_scores_init() - >>> ratingTest.established_ratings(1450, 1450, 4, [1237, 1511, 1214, 1441, 1579, 2133]) - 1487 - """ - if score > len(opponent_ratings): - raise Exception("Cannot have higher score than games played") - - # K=32 for players under 2200 and K=16 for players at or over 2200; - # Rn = Ro + 32 (S - Sx) - if old_rating >= 2199: - multiplier = 16 - else: - multiplier = 32 - - # if quick (where time control used is between 5-14 min) - if quick: - multiplier = multiplier // 2 - - # expected_scores_dict = self.expected_scores_init() - - expected_total = self.expected_total_score(old_rating, opponent_ratings) - new_rating = old_rating + multiplier * (score - expected_total) - - if new_rating > all_time_high_score: - all_time_high_valid = 1 - else: - all_time_high_valid = 0 - - multiplier_e = multiplier // 32 # ratio of the multiplier to ratings under 2200. - bonus = self.bonuses(all_time_high_valid, multiplier_e, new_rating, old_rating, len(opponent_ratings)) - sum_of_scores = new_rating + bonus - return sum_of_scores - - def bonuses(self, a: int, k_factor_e: int, r_new: float, r_old: int, n: int) -> int: - """ - Returns the total sum of all bonuses available - :param a: is = 1 if all time high, 0 otherwise - :param k_factor_e: ratio of the player's k factor to the k factor used for players under 2200 - :param r_new: post-event rating - :param r_old: pre-event rating - :param n: number of games played - :return: value of the new rating - >>> ratingTest = RatingsCalculator() - >>> ratingTest.bonuses(0, 1, 1487, 1450, 6) - 1496 - """ - if n < 4: # no bonus points awarded if less than 4 games played - return 0 - - R_MAX_BONUS = 20 # constants set - R_CHANGE_BONUS = 1.75 - R_CHANGE_THRESHOLD = 13 - - bonus1 = a * R_MAX_BONUS * k_factor_e - threshold = R_CHANGE_THRESHOLD * k_factor_e * math.sqrt(n) - - if r_new > (r_old + threshold): - b = 1 - else: - b = 0 - - bonus2 = b * R_CHANGE_BONUS * (r_new - r_old - threshold) * k_factor_e - total_bonus = round(bonus1 + bonus2) - return total_bonus - - def expected_total_score(self, rating: int, opponent_ratings: list): - """ - Returns the expected total score from the tournament - :param rating: is the player's rating - :param opponent_ratings: is the opponent's rating in a lists - >>> ratingTest = RatingsCalculator() - >>> ratingTest.expected_total_score(1450, [1237, 1511, 1214, 1441, 1579, 2133]) - 2.84 - """ - sum_expected = 0 - for opp_rating in opponent_ratings: - if rating >= opp_rating: # your rating is bigger than the opponents - sum_expected += self.find_expected_value(rating - opp_rating, 0) - else: - sum_expected += self.find_expected_value(opp_rating - rating, 1) - - return sum_expected - - def find_expected_value(self, difference: int, lower_val: int): - """ - Returns the expected value of the individual based on whether it is a lower or higher score. - :param lower_val: whether the number is a lower value - :param difference: is the difference in ratings - :return: expected value based on the expected dictionary - """ - expected_dict = self.expected_scores - expected = expected_dict[int(difference)] - if lower_val == 1: - expected = round(1 - expected, 2) - else: - # keep the older value - pass - - return expected - - def expected_scores_init(self) -> dict: - """ - Returns the expected score dictionary - :return: dictionary containing expected scores - """ - # file is recommend to be in the following format: starting rank, Name of Player, CFC ID - # information of the event - file_name = "C:\\Users\\zheng\\PycharmProjects\\ratings-calculator\\ExpectedScores.csv" - expected_scores_higher = {} - - with open(file_name, 'r') as f: - csv_reader = reader(f) - next(csv_reader) - - for row in csv_reader: - # iterate through each row - # print(row) - # if it's the last line - max_diff = 2000 - if row[0][0:3] == "735": - for j in range(735, max_diff): - expected_scores_higher[j] = 1 - else: - row_vals = row[0].split("-") - starting_val = int(row_vals[0]) - end_val = int(row_vals[1]) - - for j in range(starting_val, end_val + 1): - expected_scores_higher[j] = float(row[1]) # the location of the csv file - - return expected_scores_higher - - if __name__ == "__main__": print("Welcome to Ratings Calculator\n\n" "This project is about calculating more accurate CFC ratings than ever before. \n" diff --git a/src/ratings_calculator/rating_tests.py b/src/ratings_calculator/rating_tests.py index 96797a8..2418855 100644 --- a/src/ratings_calculator/rating_tests.py +++ b/src/ratings_calculator/rating_tests.py @@ -1,10 +1,11 @@ import unittest -from src.ratings_calculator.main import RatingsCalculator, Profile +from src.ratings_calculator.Profile import CFCProfile +from src.ratings_calculator.RatingsCalculator import RatingsCalculator class TestProfileFunctionality(unittest.TestCase): def test_name_input_correct(self) -> None: - profile = Profile(150532, False) # for now, input int + profile = CFCProfile(150532, False) # for now, input int profile_dict = profile.get_profile() self.assertEqual(profile_dict["player"]["name_first"], "Victor") self.assertEqual(profile_dict["player"]["name_last"], "Zheng") @@ -12,6 +13,7 @@ def test_name_input_correct(self) -> None: tour_data = profile.get_last_tournaments(5) print(tour_data) + class TestBasicFunctionalities(unittest.TestCase): def test_default_CFC_ratings_without_bonus(self) -> None: ratings = RatingsCalculator() From b8bc11a53c81cd92b8c021e3e846007219056f33 Mon Sep 17 00:00:00 2001 From: victor-zheng-codes Date: Mon, 25 Dec 2023 13:16:59 -0500 Subject: [PATCH 10/13] fix main config --- src/ratings_calculator/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ratings_calculator/main.py b/src/ratings_calculator/main.py index eff0841..0c59ea3 100644 --- a/src/ratings_calculator/main.py +++ b/src/ratings_calculator/main.py @@ -1,4 +1,4 @@ -import RatingsCalculator +from src.ratings_calculator.RatingsCalculator import RatingsCalculator """ @author Victor Zheng From 716090a8989239ee83a268c94a027a974103e443 Mon Sep 17 00:00:00 2001 From: victor-zheng-codes Date: Tue, 26 Dec 2023 20:31:02 -0800 Subject: [PATCH 11/13] added current html page from chesscanada --- src/ratings_calculator/chesscanada.html | 323 ++++++++++++++++++++++++ 1 file changed, 323 insertions(+) create mode 100644 src/ratings_calculator/chesscanada.html diff --git a/src/ratings_calculator/chesscanada.html b/src/ratings_calculator/chesscanada.html new file mode 100644 index 0000000..37bf65d --- /dev/null +++ b/src/ratings_calculator/chesscanada.html @@ -0,0 +1,323 @@ + + + CFC Ratings Calculator + + + + + + + +

+ + + + + + + + + +
+
+ + CFC Ratings Calculator +
+ + + Using the current CFC Ratings formulas as of August 1st, 2012. + + +
+ +

+


+ +

+ To use this calculator, enter your current rating as well as the result and ratings of your opponents. + The calculator will display your new ratings! + + +

+

+ +

+ + + + +
+ Required Information: +

+ Your + current rating
+

+ Enter your opponents' ratings separated by spaces; +/-/= for win/loss/draw. Example: +1600 + -1850 =1725.
+
+ + +

+ +

+

+ + + + +
+ Number of games +

+ Your score +

+ + Your performance + rating +

+ + Your new rating + (no bonus) +

+ + Your new rating + (regular bonus) +

+ Your new rating + (lifetime high bonus) +

+

+ +

+ For new rating, no bonus would mean under normal circumstances. If the regular bonus field is higher, + that will be your new rating. + The lifetime high bonus rating will only be yours if the no bonus rating exceeds your highest ever + rating. Regular Bonus does not apply unless you play at least four games. +

+ Also please note that due to rounding issues, results here may not exactly duplicate your CFC rating. + They should always be within 1 point, however. +


+ +
+ + + + + From 353a4821fdf2bc8dc7bfa79ce460933fd2a20446 Mon Sep 17 00:00:00 2001 From: victor-zheng-codes Date: Wed, 27 Dec 2023 20:58:57 -0800 Subject: [PATCH 12/13] configure console app to ask for quick or not quick ratings --- src/ratings_calculator/main.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/ratings_calculator/main.py b/src/ratings_calculator/main.py index 0c59ea3..9e9e6b4 100644 --- a/src/ratings_calculator/main.py +++ b/src/ratings_calculator/main.py @@ -136,7 +136,10 @@ all_time_high = int(input("All Time High Rating: ")) # established or provisional rating? (can be removed in the future)) - rating_type = int(input("Rating Type: (1 for established, 0 for provisional: ")) + rating_type = int(input("Rating Type: (1 for established, 0 for provisional): ")) + + # established or provisional rating? (can be removed in the future)) + quickTourney = int(input("Quick Tournament: (1 for quick less than 15 minutes total, 0 for classical): ")) if rating_type == 1: # new_rating = established_ratings(1450, 1450, 4, [1237, 1511, 1214, 1441, 1579, 2133]) From 0e79142797f528cba0a3223d34875e3403e08676 Mon Sep 17 00:00:00 2001 From: victor-zheng-codes Date: Wed, 27 Dec 2023 21:05:16 -0800 Subject: [PATCH 13/13] reconfigure pytests --- .github/workflows/python-app.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/python-app.yml b/.github/workflows/python-app.yml index f3d4fca..e6ae101 100644 --- a/.github/workflows/python-app.yml +++ b/.github/workflows/python-app.yml @@ -36,4 +36,6 @@ jobs: flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics - name: Test with pytest run: | - pytest + pip install pytest pytest-cov + pytest tests.py --doctest-modules --junitxml=junit/test-results.xml --cov=com --cov-report=xml --cov-report=html + continue-on-error: true