Skip to content

Commit

Permalink
Auto pairing rating range
Browse files Browse the repository at this point in the history
  • Loading branch information
gbtami committed Dec 24, 2024
1 parent 5ceecb6 commit 82fb505
Show file tree
Hide file tree
Showing 7 changed files with 116 additions and 31 deletions.
53 changes: 45 additions & 8 deletions client/lobby.ts
Original file line number Diff line number Diff line change
Expand Up @@ -503,9 +503,9 @@ export class LobbyController implements ChatController {
on: { input: e => this.setRatingMin(parseInt((e.target as HTMLInputElement).value)) },
hook: { insert: vnode => this.setRatingMin(parseInt((vnode.elm as HTMLInputElement).value)) },
}),
h('span.rating-min', '-500'),
'/',
h('span.rating-max', '+500'),
h('div.rating-min', '-1000'),
h('span', '/'),
h('div.rating-max', '+1000'),
h('input#rating-max.slider', {
props: { name: "rating-max", type: "range", min: 0, max: 1000, step: 50, value: vRatingMax },
on: { input: e => this.setRatingMax(parseInt((e.target as HTMLInputElement).value)) },
Expand Down Expand Up @@ -596,9 +596,15 @@ export class LobbyController implements ChatController {
if (inp.checked) tcs.push(autoPairingTCs[index]);
})

// console.log('autoPairingSubmit()', variants);
// console.log('autoPairingSubmit()', tcs);
this.doSend({ type: "create_auto_pairing", variants: variants, tcs: tcs });
const minEle = document.getElementById('auto-rating-min') as HTMLInputElement;
const rrMin = Number(minEle.value);
localStorage.auto_rating_min = minEle.value;

const maxEle = document.getElementById('auto-rating-max') as HTMLInputElement;
const rrMax = Number(maxEle.value);
localStorage.auto_rating_max = maxEle.value;

this.doSend({ type: "create_auto_pairing", variants: variants, tcs: tcs, rrmin: rrMin, rrmax: rrMax });
}

preSelectVariant(variantName: string, chess960: boolean=false) {
Expand Down Expand Up @@ -749,10 +755,16 @@ export class LobbyController implements ChatController {
this.setStartButtons();
}
private setRatingMin(val: number) {
document.querySelector("span.rating-min")!.innerHTML = String(val);
document.querySelector("div.rating-min")!.innerHTML = '-' + String(Math.abs(val));
}
private setRatingMax(val: number) {
document.querySelector("span.rating-max")!.innerHTML = String(val);
document.querySelector("div.rating-max")!.innerHTML = '+' + String(val);
}
private setAutoRatingMin(val: number) {
document.querySelector("div.auto-rating-min")!.innerHTML = '-' + String(Math.abs(val));
}
private setAutoRatingMax(val: number) {
document.querySelector("div.auto-rating-max")!.innerHTML = '+' + String(val);
}
private setFen() {
const e = document.getElementById('fen') as HTMLInputElement;
Expand Down Expand Up @@ -1005,7 +1017,31 @@ export class LobbyController implements ChatController {
const checked = localStorage[`tc_${tcName}`] ?? "false";
tcList.push(h('label', [h('input', { props: { name: `tc_${tcName}`, type: "checkbox" }, attrs: { checked: checked === "true" } }), tcName]));
})

patch(document.querySelector('div.timecontrols') as Element, h('div.timecontrols', tcList));

const aRatingMin = localStorage.auto_rating_min ?? -1000;
const aRatingMax = localStorage.auto_rating_max ?? 1000;
const aRatingRange = [
_('Rating range'),
h('div.rating-range', [
h('input#auto-rating-min.slider', {
props: { name: "rating-min", type: "range", min: -1000, max: 0, step: 50, value: aRatingMin },
on: { input: e => this.setAutoRatingMin(parseInt((e.target as HTMLInputElement).value)) },
hook: { insert: vnode => this.setAutoRatingMin(parseInt((vnode.elm as HTMLInputElement).value)) },
}),
h('div.auto-rating-min', '-1000'),
h('span', '/'),
h('div.auto-rating-max', '+1000'),
h('input#auto-rating-max.slider', {
props: { name: "rating-max", type: "range", min: 0, max: 1000, step: 50, value: aRatingMax },
on: { input: e => this.setAutoRatingMax(parseInt((e.target as HTMLInputElement).value)) },
hook: { insert: vnode => this.setAutoRatingMax(parseInt((vnode.elm as HTMLInputElement).value)) },
}),
]),
];

patch(document.querySelector('div.auto-rating-range') as Element, h('div.auto-rating-range', aRatingRange));
}

onMessage(evt: MessageEvent) {
Expand Down Expand Up @@ -1289,6 +1325,7 @@ export function lobbyView(model: PyChessModel): VNode[] {
h('div.auto-container', {attrs: {id: 'panel-4', role: 'tabpanel', tabindex: '-1', 'aria-labelledby': 'tab-4'}}, [
h('div.seeks-table', [h('div.seeks-wrapper', [h('div.auto-pairing', [
h('div.auto-pairing-actions'),
h('div.auto-rating-range'),
h('div.timecontrols'),
h('div.variants'),
])])])
Expand Down
5 changes: 3 additions & 2 deletions server/auto_pair.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ async def auto_pair(app_state, user, auto_variant_tc, other_user=None, matching_

def find_matching_user(app_state, user, variant_tc):
"""Return first compatible user from app_state.auto_pairing_users if there is any, else None"""
variant, chess960, _, _, _ = variant_tc
return next(
(
user_candidate
Expand All @@ -56,7 +57,7 @@ def find_matching_user(app_state, user, variant_tc):
and user.ready_for_auto_pairing
and auto_pairing_user.ready_for_auto_pairing
)
if user.compatible_with_other_user(user_candidate)
if user.auto_compatible_with_other_user(user_candidate, variant, chess960)
),
None,
)
Expand All @@ -80,7 +81,7 @@ def find_matching_seek(app_state, user, variant_tc):
and seek.color == "r"
and seek.fen == ""
)
if user.compatible_with_seek(seek_candidate)
if user.auto_compatible_with_seek(seek_candidate)
),
None,
)
12 changes: 8 additions & 4 deletions server/pychess_global_app_state.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,7 @@ def __init__(self, app: web.Application):
self.sent_lichess_team_msg: List[date] = []

self.seeks: dict[str, Seek] = {}
self.auto_pairing_users: set = set()
self.auto_pairing_users: dict[User, (int, int)] = {}
self.auto_pairings: dict[str, set] = {}
self.games: dict[str, Game] = {}
self.invites: dict[str, Seek] = {}
Expand Down Expand Up @@ -243,10 +243,11 @@ async def init_from_db(self):
if variant_tc not in self.auto_pairings:
self.auto_pairings[variant_tc] = set()

for username in doc["users"]:
for username, rrange in doc["users"]:
user = await self.users.get(username)
self.auto_pairing_users.add(user)
self.auto_pairings[variant_tc].add(user)
if user not in self.auto_pairing_users:
self.auto_pairing_users[user] = rrange

# Load seeks from database
async for doc in self.db.seek.find():
Expand Down Expand Up @@ -467,7 +468,10 @@ async def server_shutdown(self):
auto_pairings = [
{
"variant_tc": variant_tc,
"users": [user.username for user in self.auto_pairings[variant_tc]],
"users": [
(user.username, self.auto_pairing_users[user])
for user in self.auto_pairings[variant_tc]
],
}
for variant_tc in self.auto_pairings
]
Expand Down
2 changes: 1 addition & 1 deletion server/seek.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ def __init__(
self.color = color
self.fen = "" if fen is None else fen
self.rated = rated
self.rating = creator.get_rating(variant, chess960).rating_prov[0]
self.rating = creator.get_rating_value(variant, chess960)
self.rrmin = rrmin if (rrmin is not None and rrmin != -1000) else -10000
self.rrmax = rrmax if (rrmax is not None and rrmax != 1000) else 10000
self.base = base
Expand Down
38 changes: 34 additions & 4 deletions server/user.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ def __init__(
self.username = username

self.seeks: dict[int, Seek] = {}

self.ready_for_auto_pairing = False
self.lobby_sockets: Set[WebSocketResponse] = set()
self.tournament_sockets: dict[str, WebSocketResponse] = {} # {tournamentId: set()}
Expand Down Expand Up @@ -269,7 +270,7 @@ async def clear_seeks(self):

def remove_from_auto_pairings(self):
try:
self.app_state.auto_pairing_users.remove(self)
del self.app_state.auto_pairing_users[self]
except KeyError:
pass
[
Expand Down Expand Up @@ -371,14 +372,43 @@ def remove_ws_for_game(self, game_id, ws) -> bool:
else:
return False

def compatible_with_other_user(self, other_user):
def auto_compatible_with_other_user(self, other_user, variant, chess960):
"""Users are compatible when their auto pairing rating ranges are overlapped
and the users are not blocked by each other"""

rating = self.get_rating_value(variant, chess960)
rr = self.app_state.auto_pairing_users[self]
a = (rating + rr[0], rating + rr[1])

rating = other_user.get_rating_value(variant, chess960)
rr = self.app_state.auto_pairing_users[other_user]
b = (rating + rr[0], rating + rr[1])

return (other_user.username not in self.blocked) and (
self.username not in other_user.blocked
self.username not in other_user.blocked and max(a[0], b[0]) <= min(a[1], b[1])
)

def auto_compatible_with_seek(self, seek):
"""Seek is auto pairing compatible when the rating ranges are overlapped
and the users are not blocked by each other"""

rating = self.get_rating_value(seek.variant, seek.chess960)
rr = self.app_state.auto_pairing_users[self]
a = (rating + rr[0], rating + rr[1])

other_user = seek.creator
rating = other_user.get_rating_value(seek.variant, seek.chess960)
b = (rating + seek.rrmin, rating + seek.rrmax)

return (other_user.username not in self.blocked) and (
self.username not in other_user.blocked and max(a[0], b[0]) <= min(a[1], b[1])
)

def compatible_with_seek(self, seek):
"""Seek is compatible when my rating is inside the seek rating range
and the users are not blocked by each other"""

self_rating = self.get_rating_value(seek.variant, seek.chess960)
print(self.username, seek.variant, seek.chess960, self_rating)
seek_user = self.app_state.users[seek.creator.username]
return (
(seek_user.username not in self.blocked)
Expand Down
15 changes: 10 additions & 5 deletions server/wsl.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ async def finally_logic(app_state: PychessGlobalAppState, ws, user):


async def process_message(app_state: PychessGlobalAppState, user, ws, data):
print(user.username, data)
if data["type"] == "create_ai_challenge":
await handle_create_ai_challenge(app_state, ws, user, data)
elif data["type"] == "create_seek":
Expand Down Expand Up @@ -375,6 +376,12 @@ async def handle_create_auto_pairing(app_state, ws, user, data):
matching_user = None
matching_seek = None

rrmin = data["rrmin"]
rrmax = data["rrmax"]
rrmin = rrmin if (rrmin != -1000) else -10000
rrmax = rrmax if (rrmax != 1000) else 10000
app_state.auto_pairing_users[user] = (rrmin, rrmax)

for variant_tc in product(data["variants"], data["tcs"]):
variant_tc = (
variant_tc[0][0],
Expand Down Expand Up @@ -410,15 +417,13 @@ async def handle_create_auto_pairing(app_state, ws, user, data):
)

if not auto_paired:
app_state.auto_pairing_users.add(user)
user.ready_for_auto_pairing = True
for user_ws in user.lobby_sockets:
await ws_send_json(user_ws, {"type": "auto_pairing_on"})


# print("AUTO_PAIRING USERS", [user.username for user in app_state.auto_pairing_users])
# for key, value in app_state.auto_pairings.items():
# print(key, [user.username for user in app_state.auto_pairings[key]])
# print("AUTO_PAIRING USERS", [item for item in app_state.auto_pairing_users.items()])
# for key, value in app_state.auto_pairings.items():
# print(key, [user.username for user in app_state.auto_pairings[key]])


async def send_game_in_progress_if_any(app_state: PychessGlobalAppState, user, ws):
Expand Down
22 changes: 15 additions & 7 deletions static/lobby.css
Original file line number Diff line number Diff line change
Expand Up @@ -135,20 +135,28 @@ div.rating-range .slider {
margin: 0;
}
div.tc-block, div#rating-range-setting, div.timecontrols {
padding: 1em;
background: var(--bg-color2);
border-top: 1px solid var(--border-color);
border-bottom: 1px solid var(--border-color);
padding: 1em;
background: var(--bg-color2);
border-top: 1px solid var(--border-color);
border-bottom: 1px solid var(--border-color);
}
div.timecontrols {
padding: 1em 0 1em 0;
}
div.auto-rating-range {
display: flex;
padding: 8px;
}
.rating-range {
justify-content: center;
display: flex;
flex-flow: row nowrap;
align-items: center;
}
.rating-range .rating-min, .rating-range .rating-max {
font-weight: inherit;
flex: 0 0 7ch;
.auto-rating-min, .rating-min, .auto-rating-max, .rating-max {
font-weight: normal;
padding: 0 1ch 0 1ch;
width: 7ch;
}
div.auto-pairing {
display: grid;
Expand Down

0 comments on commit 82fb505

Please sign in to comment.