Skip to content

Commit

Permalink
Merge pull request #51 from dexonsmith/handicap-rank-difference
Browse files Browse the repository at this point in the history
Add --handicap-rank-difference-{small,19x19} options
  • Loading branch information
anoek authored Dec 17, 2023
2 parents 6ab9e59 + 87c3fa8 commit 70c4d78
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 29 deletions.
12 changes: 9 additions & 3 deletions analysis/analyze_glicko2_daily_windows.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics:
black_base,
[
(
opponent.copy((1 if past_game.black_id != game.black_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap)),
opponent.copy((1 if past_game.black_id != game.black_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap,
komi=past_game.komi, size=past_game.size, rules=past_game.rules,
)),
past_game.winner_id == game.black_id
)
for past_game, opponent in self._storage.get_matches_newer_or_equal_to(
Expand All @@ -58,7 +60,9 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics:
white_base,
[
(
opponent.copy((1 if past_game.black_id != game.white_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap)),
opponent.copy((1 if past_game.black_id != game.white_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap,
komi=past_game.komi, size=past_game.size, rules=past_game.rules,
)),
past_game.winner_id == game.white_id
)
for past_game, opponent in self._storage.get_matches_newer_or_equal_to(
Expand All @@ -76,7 +80,9 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics:
skipped=False,
game=game,
expected_win_rate=black_cur.expected_win_probability(
white_cur, get_handicap_adjustment(black_cur.rating, game.handicap), ignore_g=True
white_cur, get_handicap_adjustment(black_cur.rating, game.handicap,
komi=game.komi, size=game.size, rules=game.rules,
), ignore_g=True
),
black_rating=black_cur.rating,
white_rating=white_cur.rating,
Expand Down
12 changes: 9 additions & 3 deletions analysis/analyze_glicko2_glickman_weekly_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics:
black_base,
[
(
opponent.copy((1 if past_game.black_id != game.black_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap)),
opponent.copy((1 if past_game.black_id != game.black_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap,
komi=past_game.komi, size=past_game.size, rules=past_game.rules,
)),
past_game.winner_id == past_game.black_id
)
for past_game, opponent in self._storage.get_matches_newer_or_equal_to(
Expand All @@ -66,7 +68,9 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics:
white_base,
[
(
opponent.copy((1 if past_game.black_id != game.white_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap)),
opponent.copy((1 if past_game.black_id != game.white_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap,
komi=past_game.komi, size=past_game.size, rules=past_game.rules,
)),
past_game.winner_id == past_game.white_id
)
for past_game, opponent in self._storage.get_matches_newer_or_equal_to(
Expand All @@ -84,7 +88,9 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics:
skipped=False,
game=game,
expected_win_rate=black_cur.expected_win_probability(
white_cur, get_handicap_adjustment(black_cur.rating, game.handicap), ignore_g=True
white_cur, get_handicap_adjustment(black_cur.rating, game.handicap,
komi=game.komi, size=game.size, rules=game.rules,
), ignore_g=True
),
black_rating=black_cur.rating,
white_rating=white_cur.rating,
Expand Down
16 changes: 12 additions & 4 deletions analysis/analyze_glicko2_one_game_at_a_time.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,12 +35,14 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics:
black = self._storage.get(game.black_id)
white = self._storage.get(game.white_id)


updated_black = glicko2_update(
black,
[
(
white.copy(-get_handicap_adjustment(white.rating, game.handicap)),
white.copy(-get_handicap_adjustment(white.rating, game.handicap,
komi=game.komi, size=game.size,
rules=game.rules,
)),
game.winner_id == game.black_id,
)
],
Expand All @@ -50,7 +52,10 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics:
white,
[
(
black.copy(get_handicap_adjustment(black.rating, game.handicap)),
black.copy(get_handicap_adjustment(black.rating, game.handicap,
komi=game.komi, size=game.size,
rules=game.rules,
)),
game.winner_id == game.white_id,
)
],
Expand All @@ -65,7 +70,10 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics:
skipped=False,
game=game,
expected_win_rate=black.expected_win_probability(
white, get_handicap_adjustment(black.rating, game.handicap), ignore_g=True
white, get_handicap_adjustment(black.rating, game.handicap,
komi=game.komi, size=game.size,
rules=game.rules,
), ignore_g=True
),
black_rating=black.rating,
white_rating=white.rating,
Expand Down
12 changes: 9 additions & 3 deletions analysis/analyze_glicko2_one_game_at_a_time_rating_grid.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,9 @@ def process_game(self, game: GameRecord) -> Dict[str, Glicko2Analytics]:
black,
[
(
src_white.copy(-get_handicap_adjustment(src_white.rating, game.handicap)),
src_white.copy(-get_handicap_adjustment(src_white.rating, game.handicap,
komi=game.komi, size=game.size, rules=game.rules,
)),
game.winner_id == game.black_id,
)
],
Expand All @@ -73,7 +75,9 @@ def process_game(self, game: GameRecord) -> Dict[str, Glicko2Analytics]:
white,
[
(
src_black.copy(get_handicap_adjustment(src_black.rating, game.handicap)),
src_black.copy(get_handicap_adjustment(src_black.rating, game.handicap,
komi=game.komi, size=game.size, rules=game.rules,
)),
game.winner_id == game.white_id,
)
],
Expand All @@ -89,7 +93,9 @@ def process_game(self, game: GameRecord) -> Dict[str, Glicko2Analytics]:
skipped=False,
game=game,
expected_win_rate=black.expected_win_probability(
white, get_handicap_adjustment(black.rating, game.handicap), ignore_g=True
white, get_handicap_adjustment(black.rating, game.handicap,
komi=game.komi, size=game.size, rules=game.rules,
), ignore_g=True
),
black_rating=black.rating,
white_rating=white.rating,
Expand Down
12 changes: 9 additions & 3 deletions analysis/analyze_glicko2_weekly_window_no_unxepected_changes.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics:
black_base,
[
(
opponent.copy((1 if past_game.black_id != game.black_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap)),
opponent.copy((1 if past_game.black_id != game.black_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap,
komi=past_game.komi, size=past_game.size, rules=past_game.rules,
)),
past_game.winner_id == past_game.black_id
)
for past_game, opponent in self._storage.get_matches_newer_or_equal_to(
Expand All @@ -63,7 +65,9 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics:
white_base,
[
(
opponent.copy((1 if past_game.black_id != game.white_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap)),
opponent.copy((1 if past_game.black_id != game.white_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap,
komi=past_game.komi, size=past_game.size, rules=past_game.rules,
)),
past_game.winner_id == past_game.white_id
)
for past_game, opponent in self._storage.get_matches_newer_or_equal_to(
Expand Down Expand Up @@ -96,7 +100,9 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics:
skipped=False,
game=game,
expected_win_rate=black_cur.expected_win_probability(
white_cur, get_handicap_adjustment(black_cur.rating, game.handicap), ignore_g=True
white_cur, get_handicap_adjustment(black_cur.rating, game.handicap,
komi=game.komi, size=game.size, rules=game.rules,
), ignore_g=True
),
black_rating=black_cur.rating,
white_rating=white_cur.rating,
Expand Down
12 changes: 9 additions & 3 deletions analysis/analyze_glicko2_weekly_window_reduce_rating_movement.py
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,9 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics:
black_base,
[
(
opponent.copy((1 if past_game.black_id != game.black_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap)),
opponent.copy((1 if past_game.black_id != game.black_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap,
komi=past_game.komi, size=past_game.size, rules=past_game.rules,
)),
past_game.winner_id == past_game.black_id
)
for past_game, opponent in self._storage.get_matches_newer_or_equal_to(
Expand All @@ -68,7 +70,9 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics:
white_base,
[
(
opponent.copy((1 if past_game.black_id != game.white_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap)),
opponent.copy((1 if past_game.black_id != game.white_id else -1) * get_handicap_adjustment(opponent.rating, past_game.handicap,
komi=past_game.komi, size=past_game.size, rules=past_game.rules,
)),
past_game.winner_id == past_game.white_id
)
for past_game, opponent in self._storage.get_matches_newer_or_equal_to(
Expand Down Expand Up @@ -112,7 +116,9 @@ def process_game(self, game: GameRecord) -> Glicko2Analytics:
skipped=False,
game=game,
expected_win_rate=black_cur.expected_win_probability(
white_cur, get_handicap_adjustment(black_cur.rating, game.handicap), ignore_g=True
white_cur, get_handicap_adjustment(black_cur.rating, game.handicap,
komi=game.komi, size=game.size, rules=game.rules,
), ignore_g=True
),
black_rating=black_cur.rating,
white_rating=white_cur.rating,
Expand Down
16 changes: 11 additions & 5 deletions analysis/analyze_gor.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,19 @@ def process_game(self, game: GameRecord) -> GorAnalytics:


updated_black = gor_update(
black.with_handicap(get_handicap_adjustment(black.rating, game.handicap)),
#white.with_handicap(-get_handicap_adjustment(white.rating, game.handicap)),
black.with_handicap(get_handicap_adjustment(black.rating, game.handicap,
komi=game.komi, size=game.size, rules=game.rules,
)),
#white.with_handicap(-get_handicap_adjustment(white.rating, game.handicap, ...)),
white,
1 if game.winner_id == game.black_id else 0,
)

updated_white = gor_update(
white,
black.with_handicap(get_handicap_adjustment(black.rating, game.handicap)),
black.with_handicap(get_handicap_adjustment(black.rating, game.handicap,
komi=game.komi, size=game.size, rules=game.rules,
)),
1 if game.winner_id == game.white_id else 0,
)

Expand All @@ -65,8 +69,10 @@ def process_game(self, game: GameRecord) -> GorAnalytics:
return GorAnalytics(
skipped=False,
game=game,
expected_win_rate=black.with_handicap(get_handicap_adjustment(white.rating, game.handicap)).expected_win_probability(
#white.copy(-get_handicap_adjustment(white.rating, game.handicap))
expected_win_rate=black.with_handicap(get_handicap_adjustment(white.rating, game.handicap,
komi=game.komi, size=game.size, rules=game.rules,
)).expected_win_probability(
#white.copy(-get_handicap_adjustment(white.rating, game.handicap, ...))
white
),
black_rating=black.rating,
Expand Down
79 changes: 74 additions & 5 deletions analysis/util/RatingMath.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,25 @@
P = 1
HALF_STONE_HANDICAP = False
HALF_STONE_HANDICAP_FOR_ALL_RANKS = False
HANDICAP_RANK_DIFFERENCE_SMALL = False
HANDICAP_RANK_DIFFERENCE_19X19 = False

cli.add_argument(
"--half-stone-handicap", dest="half_stone_handicap", const=1, default=False, action="store_const", help="Use a 0.5 rank adjustment for hc1",
)
cli.add_argument(
"--half-stone-handicap-for-all-ranks", dest="half_stone_handicap_for_all_ranks", const=1, default=False, action="store_const", help="use rankdiff -0.5 for handicap",
)
cli.add_argument(
"--handicap-rank-difference-small",
dest="handicap_rank_difference_small", const=1, default=False, action="store_const",
help="compute effective handicap rank difference with komi for small boards",
)
cli.add_argument(
"--handicap-rank-difference-19x19",
dest="handicap_rank_difference_19x19", const=1, default=False, action="store_const",
help="compute effective handicap rank difference with komi for 19x19 boards",
)

logarithmic = cli.add_argument_group(
"logarithmic ranking variables", "rating to ranks converted with `(log(rating / a) ** p) * c + d`",
Expand Down Expand Up @@ -77,14 +89,65 @@ def set_exhaustive_log_parameters(a: float, c:float, d:float, p:float = 1.0) ->
D = d
P = p

def get_handicap_adjustment(rating: float, handicap: int) -> float:

def get_handicap_rank_difference(handicap: int, size: int, komi: float, rules: str) -> float:
global HALF_STONE_HANDICAP
global HALF_STONE_HANDICAP_FOR_ALL_RANKS
global HANDICAP_RANK_DIFFERENCE_SMALL
global HANDICAP_RANK_DIFFERENCE_19X19

if (HANDICAP_RANK_DIFFERENCE_19X19 and size == 19) or (HANDICAP_RANK_DIFFERENCE_SMALL and size != 19):
# The territorial value of a free stone.
stone_value = 12

# Number of extra moves black makes before white responds.
num_extra_moves = handicap - 1 if handicap > 1 else 0

if rules == "japanese" or rules == "korean":
# Territory scoring.
area_bonus = 0
komi_bonus = 0
else:
# Bonus for the area value of a stone in area scoring.
area_bonus = 1

# Chinese and AGA rules add extra komi when there's a handicap but
# don't store it in the 'komi' field.
if rules == "chinese":
komi_bonus = 1 * handicap
elif rules == "aga":
komi_bonus = 1 * num_extra_moves
else:
komi_bonus = 0

# Figure out the point value of black's head start, if any, by
# subtracting the actual komi from the fair komi for an even game, and
# adding the point value of any extra moves.
fair_komi = stone_value / 2 + area_bonus + 0.5
actual_komi = komi + komi_bonus
value_extra_moves = (stone_value + area_bonus) * num_extra_moves
head_start = fair_komi - actual_komi + value_extra_moves

# Convert back to a fractional handicap, using scaling factors of 3x
# and 6x for 9x9 and 13x13.
if size == 9:
return head_start * 6 / stone_value
if size == 13:
return head_start * 3 / stone_value
return head_start / stone_value

if HALF_STONE_HANDICAP_FOR_ALL_RANKS:
return rank_to_rating(rating_to_rank(rating) + (handicap - 0.5 if handicap > 0 else 0)) - rating
return handicap - 0.5 if handicap > 0 else 0
if HALF_STONE_HANDICAP:
return rank_to_rating(rating_to_rank(rating) + (0.5 if handicap == 1 else handicap)) - rating
return rank_to_rating(rating_to_rank(rating) + handicap) - rating
return 0.5 if handicap == 1 else handicap
return handicap


def get_handicap_adjustment(rating: float, handicap: int, size: int, komi: float, rules: str) -> float:
rank_difference = get_handicap_rank_difference(handicap, size, komi, rules)
effective_rank = rating_to_rank(rating) + rank_difference
return rank_to_rating(effective_rank) - rating


def set_optimizer_rating_points(points: List[float]) -> None:
global optimizer_rating_control_points
Expand All @@ -99,9 +162,13 @@ def configure_rating_to_rank(args: argparse.Namespace) -> None:
global D
global HALF_STONE_HANDICAP
global HALF_STONE_HANDICAP_FOR_ALL_RANKS
global HANDICAP_RANK_DIFFERENCE_SMALL
global HANDICAP_RANK_DIFFERENCE_19X19

HALF_STONE_HANDICAP = args.half_stone_handicap
HALF_STONE_HANDICAP_FOR_ALL_RANKS = args.half_stone_handicap_for_all_ranks
HANDICAP_RANK_DIFFERENCE_SMALL = args.handicap_rank_difference_small
HANDICAP_RANK_DIFFERENCE_19X19 = args.handicap_rank_difference_19x19
system: str = args.ranks
a: float = args.a
c: float = args.c
Expand Down Expand Up @@ -260,7 +327,9 @@ def __rating_to_rank(rating: float) -> float:
else:
raise NotImplementedError

assert round(get_handicap_adjustment(1000.0, 0), 8) == 0
for size in [9, 13, 19]:
assert round(get_handicap_adjustment(1000.0, 0, size=size, rules="japanese", komi=6.5), 8) == 0
assert round(get_handicap_adjustment(1000.0, 0, size=size, rules="aga", komi=7.5), 8) == 0


def lerp(x:float, y:float, a:float):
Expand Down

0 comments on commit 70c4d78

Please sign in to comment.