diff --git a/src/components/Common/PlaySetupModal.tsx b/src/components/Common/PlaySetupModal.tsx index e07d7389..65721474 100644 --- a/src/components/Common/PlaySetupModal.tsx +++ b/src/components/Common/PlaySetupModal.tsx @@ -14,6 +14,12 @@ import { } from 'src/types' import { ModalContext } from 'src/contexts' import { ModalContainer } from './ModalContainer' +import { Slider } from './Slider' +import { + customToPresetTimeControl, + parseTimeControl, + getPresetOptions, +} from 'src/lib/timeControlUtils' const maiaOptions = [ 'maia_kdd_1100', @@ -82,6 +88,9 @@ export const PlaySetupModal: React.FC = (props: Props) => { const [timeControl, setTimeControl] = useState( props.timeControl || TimeControlOptions[0], ) + const [useCustomTime, setUseCustomTime] = useState(false) + const [customMinutes, setCustomMinutes] = useState(10) + const [customIncrement, setCustomIncrement] = useState(0) const [isBrain, setIsBrain] = useState(props.isBrain || false) const [sampleMoves, setSampleMoves] = useState( props.sampleMoves || true, @@ -102,9 +111,18 @@ export const PlaySetupModal: React.FC = (props: Props) => { const [openMoreOptions, setMoreOptionsOpen] = useState(true) + // Helper function to get the effective time control + const getEffectiveTimeControl = (): TimeControl => { + if (useCustomTime) { + return customToPresetTimeControl(customMinutes, customIncrement) + } + return timeControl + } + const start = useCallback( (color: Color | undefined) => { const player = color ?? ['white', 'black'][Math.floor(Math.random() * 2)] + const effectiveTimeControl = getEffectiveTimeControl() if (fen && !new Chess().validateFen(fen).valid) { toast.error('Invalid Starting FEN provided') @@ -120,7 +138,7 @@ export const PlaySetupModal: React.FC = (props: Props) => { player: player, //maiaPartnerVersion: maiaPartnerVersion, maiaVersion: maiaVersion, - timeControl: timeControl, + timeControl: effectiveTimeControl, sampleMoves: sampleMoves, simulateMaiaTime: simulateMaiaTime, startFen: fen, @@ -133,7 +151,7 @@ export const PlaySetupModal: React.FC = (props: Props) => { player: player, maiaPartnerVersion: maiaPartnerVersion, maiaVersion: maiaVersion, - timeControl: timeControl, + timeControl: effectiveTimeControl, isBrain: isBrain, sampleMoves: sampleMoves, simulateMaiaTime: simulateMaiaTime, @@ -148,7 +166,7 @@ export const PlaySetupModal: React.FC = (props: Props) => { push, maiaPartnerVersion, maiaVersion, - timeControl, + getEffectiveTimeControl, sampleMoves, simulateMaiaTime, fen, @@ -250,18 +268,61 @@ export const PlaySetupModal: React.FC = (props: Props) => {
-
+ + {/* Toggle between preset and custom */} +
+ + {useCustomTime ? ( + /* Custom time controls with sliders */ +
+ + +
+ + Time control: {customMinutes}+{customIncrement} + +
+
+ ) : ( + /* Preset time controls */ +
+ +
+ )}
diff --git a/src/components/Common/Slider.tsx b/src/components/Common/Slider.tsx new file mode 100644 index 00000000..2b61526b --- /dev/null +++ b/src/components/Common/Slider.tsx @@ -0,0 +1,81 @@ +import React from 'react' + +interface SliderProps { + label: string + value: number + min: number + max: number + step?: number + unit?: string + onChange: (value: number) => void + id?: string +} + +export const Slider: React.FC = ({ + label, + value, + min, + max, + step = 1, + unit = '', + onChange, + id, +}) => { + const handleChange = (e: React.ChangeEvent) => { + onChange(Number(e.target.value)) + } + + return ( +
+
+ + + {value} + {unit} + +
+
+ + +
+
+ ) +} diff --git a/src/components/Common/index.ts b/src/components/Common/index.ts index 228b9577..f8aeedde 100644 --- a/src/components/Common/index.ts +++ b/src/components/Common/index.ts @@ -17,3 +17,4 @@ export * from './ModalContainer' export * from './ContinueAgainstMaia' export * from './AnimatedNumber' export * from './DownloadModelModal' +export * from './Slider' diff --git a/src/lib/timeControlUtils.ts b/src/lib/timeControlUtils.ts new file mode 100644 index 00000000..13aaf78e --- /dev/null +++ b/src/lib/timeControlUtils.ts @@ -0,0 +1,63 @@ +/** + * Utility functions for time control conversion between custom values and preset formats + */ + +import { TimeControl, TimeControlOptions } from 'src/types' + +export interface CustomTimeControl { + minutes: number + increment: number +} + +/** + * Convert custom time control values to the closest preset TimeControl format + */ +export const customToPresetTimeControl = ( + minutes: number, + increment: number, +): TimeControl => { + const customFormat = `${minutes}+${increment}` + + // Check if it matches any existing preset + if (TimeControlOptions.includes(customFormat as TimeControl)) { + return customFormat as TimeControl + } + + // For custom values that don't match presets, return the custom format + // The game logic will need to handle this appropriately + return customFormat as TimeControl +} + +/** + * Parse a TimeControl string into custom time control values + */ +export const parseTimeControl = ( + timeControl: TimeControl, +): CustomTimeControl => { + if (timeControl === 'unlimited') { + return { minutes: 0, increment: 0 } + } + + const [minutesStr, incrementStr] = timeControl.split('+') + return { + minutes: parseInt(minutesStr, 10) || 0, + increment: parseInt(incrementStr, 10) || 0, + } +} + +/** + * Check if a time control is a preset option + */ +export const isPresetTimeControl = (timeControl: TimeControl): boolean => { + return TimeControlOptions.includes(timeControl) +} + +/** + * Get preset time control options with labels + */ +export const getPresetOptions = () => { + return TimeControlOptions.map((option) => ({ + value: option, + label: option === 'unlimited' ? 'Unlimited' : option, + })) +}