From 56193e1944ae67b50f1a8a0f256bdbf887fc4f1d Mon Sep 17 00:00:00 2001 From: Akita Noek Date: Wed, 18 Dec 2024 13:45:43 -0700 Subject: [PATCH 1/5] Move from computer/human opponent selection to buttons --- .vscode/cspell.json | 1 + .../ChallengeModal/ChallengeModal.styl | 60 ++ .../ChallengeModal/ChallengeModal.tsx | 170 ++++- src/lib/bots.ts | 1 + src/views/Play/CustomGames.tsx | 14 +- src/views/Play/QuickMatch.styl | 184 ++--- src/views/Play/QuickMatch.tsx | 653 +++++++----------- 7 files changed, 502 insertions(+), 581 deletions(-) diff --git a/.vscode/cspell.json b/.vscode/cspell.json index 42a9f85cf8..cac7699074 100644 --- a/.vscode/cspell.json +++ b/.vscode/cspell.json @@ -23,6 +23,7 @@ "Baduk", "badukpop", "benjito", + "bezier", "bitfield", "boardsize", "byoyomi", diff --git a/src/components/ChallengeModal/ChallengeModal.styl b/src/components/ChallengeModal/ChallengeModal.styl index 211f14f209..9bb8610d4a 100644 --- a/src/components/ChallengeModal/ChallengeModal.styl +++ b/src/components/ChallengeModal/ChallengeModal.styl @@ -131,4 +131,64 @@ display: inline-flex; align-items: middle; } + + .ogs-react-select__menu { + right: 0; + width: 20rem; + max-width: 90vw; + + .ogs-react-select__group-heading { + text-transform: none; + font-size: 0.9rem; + } + + .option { + padding-top: 0.5rem; + padding-bottom: 0.5rem; + + &.focused { + themed-important: background-color shade3; + } + + &.selected { + themed-important: background-color shade3; + } + + themed: color fg; + } + + .option-label { + font-weight: bold; + padding-right: 0.5rem; + padding-left: 0.5rem; + } + + .option-description { + font-size: 0.9rem; + font-style: italic; + themed: color shade1; + padding-right: 0.5rem; + padding-left: 0.5rem; + } + } + + .option-label { + font-weight: bold; + padding-right: 0.5rem; + padding-left: 0.5rem; + } + + .option-description { + font-size: 0.9rem; + font-style: italic; + themed: color shade1; + padding-right: 0.5rem; + padding-left: 0.5rem; + } + + .option-label, .ogs-react-select__single-value, .option-label > span { + display: flex !important; + align-items: center; + gap: 0.5rem; + } } diff --git a/src/components/ChallengeModal/ChallengeModal.tsx b/src/components/ChallengeModal/ChallengeModal.tsx index cfc1e08922..3f5a7e1636 100644 --- a/src/components/ChallengeModal/ChallengeModal.tsx +++ b/src/components/ChallengeModal/ChallengeModal.tsx @@ -19,13 +19,14 @@ import * as React from "react"; import * as data from "@/lib/data"; import * as player_cache from "@/lib/player_cache"; +import Select, { components } from "react-select"; import { OgsResizeDetector } from "@/components/OgsResizeDetector"; import { browserHistory } from "@/lib/ogsHistory"; -import { _, pgettext, interpolate } from "@/lib/translate"; +import { _, pgettext, interpolate, llm_pgettext } from "@/lib/translate"; import { post, del } from "@/lib/requests"; import { Modal, openModal } from "@/components/Modal"; import { socket } from "@/lib/sockets"; -import { rankString, getUserRating, amateurRanks, allRanks } from "@/lib/rank_utils"; +import { rankString, amateurRanks, allRanks } from "@/lib/rank_utils"; import { CreatedChallengeInfo, RuleSet } from "@/lib/types"; import { errorLogger, errorAlerter, rulesText, dup } from "@/lib/misc"; import { PlayerIcon } from "@/components/PlayerIcon"; @@ -41,7 +42,7 @@ import { notification_manager, NotificationManagerEvents, } from "@/components/Notifications/NotificationManager"; -import { one_bot, bot_count, bots_list } from "@/lib/bots"; +import { Bot, one_bot, bot_count, bots_list, getAcceptableTimeSetting } from "@/lib/bots"; import { goban_view_mode } from "@/views/Game/util"; import { GobanRenderer, @@ -61,6 +62,7 @@ import { saveTimeControlSettings, updateSystem, } from "@/components/TimeControl/TimeControlUpdates"; +import { SPEED_OPTIONS } from "@/views/Play/SPEED_OPTIONS"; export type ChallengeDetails = rest_api.ChallengeDetails; @@ -984,6 +986,59 @@ export class ChallengeModalBody extends React.Component< // game name and privacy basicSettings = () => { + const user = data.get("user"); + let available_bots = bots_list().filter((b) => b.id > 0); + const board_size = `${this.state.challenge.game.width}x${this.state.challenge.game.height}`; + console.log(board_size, this.state.challenge.game.speed, this.state.time_control.system); + console.log(this.state.challenge.game.speed); + + available_bots = available_bots.filter((b) => { + console.log( + this.state, + this.state.challenge.game.width, + this.state.challenge.game.height, + this.state.time_control.speed, + this.state.time_control.system, + ); + + const settings = { + rank: user.ranking, + width: this.state.challenge.game.width, + height: this.state.challenge.game.height, + ranked: true, + handicap: this.state.challenge.game.handicap, + system: this.state.time_control.system, + speed: this.state.time_control.speed, + [this.state.time_control.system]: (SPEED_OPTIONS as any)[board_size][ + this.state.time_control.speed + ][this.state.time_control.system], + }; + const [options, message] = getAcceptableTimeSetting(b, settings); + if (!options) { + b.disabled = message || undefined; + } else if (options && options._config_version && options._config_version === 0) { + b.disabled = llm_pgettext( + "Bot is not configured correctly", + "Bot is not configured correctly", + ); + } else { + b.disabled = undefined; + } + + return true; + }); + + available_bots.sort((a, b) => { + if (a.disabled && !b.disabled) { + return 1; + } + if (b.disabled && !a.disabled) { + return -1; + } + return (a.ranking || 0) - (b.ranking || 0); + }); + const selected_bot_value = available_bots.find((b) => b.id === this.state.conf.bot_id); + const mode = this.props.mode; return (
- -   - - - + setLowerRankDiff(parseInt(ev.target.value))} - disabled={automatch_search_active} - > - {user.anonymous ? ( - - ) : ( - [9, 8, 7, 6, 5, 4, 3, 2, 1, 0].map((v) => ( - - )) - )} - - {" - "} - -
-
{_("Rank range")}
-
-
{ - if (automatch_search_active || game_clock === "multiple") { - return; - } - setOpponent("bot"); - }} - > -
- {pgettext("Play a computer opponent", "Computer")} -
-
0 && - opponent === "bot" && - (!selected_bot || - !selected_bot_value || - selected_bot_value.disabled) - ? "error" - : "") - } - > - o.value === lower_rank_diff.toString(), + )} + isSearchable={false} + isDisabled={automatch_search_active} + onChange={(opt) => { + if (opt) { + setLowerRankDiff(parseInt(opt.value)); + } + }} + options={[ + { + options: lower_rank_diff_options, + }, + ]} + components={{ + Option: RenderOptionWithDescription, + }} + /> - {bot_spinner && ( -
-
- cancel_bot_game.current()} - > - {_("Cancel")} - -
-
- )} + {/* + + */} - {correspondence_spinner && ( -
-
{_("Finding you a game...")}
-
- {_( - 'This can take several minutes. You will be notified when your match has been found. To view or cancel your automatch requests, please see the list below labeled "Your Automatch Requests".', - )} -
-
- -
-
- )} - {user.anonymous && ( -
- {_("Please sign in to play")} -
- {_("Register for Free")} - {" | "} - {_("Sign in")} -
-
- )} + {" - "} - {!automatch_search_active && !user.anonymous && ( -
+ +
+ {/* Bot */} + {user.anonymous && ( +
+ {_("Please sign in to play")} +
+ {_("Register for Free")} + {" | "} + {_("Sign in")} +
+
+ )} + + {!automatch_search_active && !user.anonymous && ( + + )} + + {/* Human */} + {automatch_manager.active_live_automatcher && ( +
+ + {pgettext("Cancel automatch", "Searching for game...")} + +
+ )} + + {correspondence_spinner && ( +
+
{_("Finding you a game...")}
+
+ {_( + 'This can take several minutes. You will be notified when your match has been found. To view or cancel your automatch requests, please see the list below labeled "Your Automatch Requests".', + )} +
+
+ +
+
+ )} + {user.anonymous && ( +
+ {_("Please sign in to play")} +
+ {_("Register for Free")} + {" | "} + {_("Sign in")} +
+
+ )} + + {!automatch_search_active && !user.anonymous && ( + + )} +
); } From 04340c1b997302b72f68fc8b1d1b7394e874618b Mon Sep 17 00:00:00 2001 From: Akita Noek Date: Wed, 18 Dec 2024 15:02:26 -0700 Subject: [PATCH 2/5] Switch to showing all available bots in the bot challenge modal --- .../ChallengeModal/ChallengeModal.styl | 112 ++++-- .../ChallengeModal/ChallengeModal.tsx | 336 +++++++++--------- 2 files changed, 234 insertions(+), 214 deletions(-) diff --git a/src/components/ChallengeModal/ChallengeModal.styl b/src/components/ChallengeModal/ChallengeModal.styl index 9bb8610d4a..302c1bd356 100644 --- a/src/components/ChallengeModal/ChallengeModal.styl +++ b/src/components/ChallengeModal/ChallengeModal.styl @@ -132,37 +132,53 @@ align-items: middle; } - .ogs-react-select__menu { - right: 0; - width: 20rem; - max-width: 90vw; - - .ogs-react-select__group-heading { - text-transform: none; - font-size: 0.9rem; - } - - .option { - padding-top: 0.5rem; - padding-bottom: 0.5rem; - - &.focused { - themed-important: background-color shade3; + /* + .ogs-react-select__menu { + right: 0; + width: 20rem; + max-width: 90vw; + + .ogs-react-select__group-heading { + text-transform: none; + font-size: 0.9rem; } - - &.selected { - themed-important: background-color shade3; + + .option { + padding-top: 0.5rem; + padding-bottom: 0.5rem; + + &.focused { + themed-important: background-color shade3; + } + + &.selected { + themed-important: background-color shade3; + } + + themed: color fg; + } + + .option-label { + font-weight: bold; + padding-right: 0.5rem; + padding-left: 0.5rem; + } + + .option-description { + font-size: 0.9rem; + font-style: italic; + themed: color shade1; + padding-right: 0.5rem; + padding-left: 0.5rem; } - - themed: color fg; } - + .option-label { font-weight: bold; padding-right: 0.5rem; padding-left: 0.5rem; } - + .option-description { font-size: 0.9rem; font-style: italic; @@ -170,25 +186,47 @@ padding-right: 0.5rem; padding-left: 0.5rem; } + + .option-label, .ogs-react-select__single-value, .option-label > span { + display: flex !important; + align-items: center; + gap: 0.5rem; + } + */ + .computer-opponents { + width: 100%; + display: flex; + flex-direction: column; + gap: 0.5rem; } - .option-label { - font-weight: bold; - padding-right: 0.5rem; - padding-left: 0.5rem; - } - - .option-description { - font-size: 0.9rem; - font-style: italic; - themed: color shade1; - padding-right: 0.5rem; - padding-left: 0.5rem; + .bot-options { + display: flex; + gap: 0.5rem; + flex-wrap: wrap; + width: 100%; + justify-content: space-around; } - .option-label, .ogs-react-select__single-value, .option-label > span { - display: flex !important; + .bot-option { + display: flex; align-items: center; gap: 0.5rem; + cursor: pointer; + border-radius: 0.75rem; + padding-right: 0.5rem; + gap: 1rem; + + &:hover { + themed: background-color shade4; + } + + .PlayerIcon { + border-radius: 0.75rem; + } + + &.selected { + themed: background-color shade2; + } } } diff --git a/src/components/ChallengeModal/ChallengeModal.tsx b/src/components/ChallengeModal/ChallengeModal.tsx index 3f5a7e1636..d823a9fd03 100644 --- a/src/components/ChallengeModal/ChallengeModal.tsx +++ b/src/components/ChallengeModal/ChallengeModal.tsx @@ -19,7 +19,6 @@ import * as React from "react"; import * as data from "@/lib/data"; import * as player_cache from "@/lib/player_cache"; -import Select, { components } from "react-select"; import { OgsResizeDetector } from "@/components/OgsResizeDetector"; import { browserHistory } from "@/lib/ogsHistory"; import { _, pgettext, interpolate, llm_pgettext } from "@/lib/translate"; @@ -42,7 +41,7 @@ import { notification_manager, NotificationManagerEvents, } from "@/components/Notifications/NotificationManager"; -import { Bot, one_bot, bot_count, bots_list, getAcceptableTimeSetting } from "@/lib/bots"; +import { one_bot, bot_count, bots_list, getAcceptableTimeSetting } from "@/lib/bots"; import { goban_view_mode } from "@/views/Game/util"; import { GobanRenderer, @@ -986,60 +985,10 @@ export class ChallengeModalBody extends React.Component< // game name and privacy basicSettings = () => { - const user = data.get("user"); - let available_bots = bots_list().filter((b) => b.id > 0); - const board_size = `${this.state.challenge.game.width}x${this.state.challenge.game.height}`; - console.log(board_size, this.state.challenge.game.speed, this.state.time_control.system); - console.log(this.state.challenge.game.speed); - - available_bots = available_bots.filter((b) => { - console.log( - this.state, - this.state.challenge.game.width, - this.state.challenge.game.height, - this.state.time_control.speed, - this.state.time_control.system, - ); - - const settings = { - rank: user.ranking, - width: this.state.challenge.game.width, - height: this.state.challenge.game.height, - ranked: true, - handicap: this.state.challenge.game.handicap, - system: this.state.time_control.system, - speed: this.state.time_control.speed, - [this.state.time_control.system]: (SPEED_OPTIONS as any)[board_size][ - this.state.time_control.speed - ][this.state.time_control.system], - }; - const [options, message] = getAcceptableTimeSetting(b, settings); - if (!options) { - b.disabled = message || undefined; - } else if (options && options._config_version && options._config_version === 0) { - b.disabled = llm_pgettext( - "Bot is not configured correctly", - "Bot is not configured correctly", - ); - } else { - b.disabled = undefined; - } - - return true; - }); - - available_bots.sort((a, b) => { - if (a.disabled && !b.disabled) { - return 1; - } - if (b.disabled && !a.disabled) { - return -1; - } - return (a.ranking || 0) - (b.ranking || 0); - }); - const selected_bot_value = available_bots.find((b) => b.id === this.state.conf.bot_id); - const mode = this.props.mode; + const bots = bots_list(); + const selected_bot = bots.find((bot) => bot.id === this.state.conf.bot_id); + return (
{pgettext("Computer opponent", "AI Player")} -
-
-