Spaces:
Sleeping
Sleeping
File size: 5,669 Bytes
6fd9719 af71f44 4c67d79 6fd9719 6bb08c4 c6ef322 4c67d79 6bb08c4 6fd9719 4c67d79 6371c28 b715c89 ba7c9fe 6371c28 ba7c9fe 6bb08c4 6371c28 6bb08c4 5c4a167 7994c21 3785729 5c4a167 4eee6c0 7d70895 4eee6c0 5c4a167 6371c28 5c4a167 6fd9719 b715c89 6fd9719 b715c89 ba7c9fe b715c89 6371c28 f9cbedb 6371c28 6fd9719 b715c89 4eee6c0 6371c28 4eee6c0 6371c28 b715c89 ba7c9fe b715c89 5c4a167 6fd9719 4c67d79 6fd9719 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
'use client';
import { Button, ButtonGroup } from '@heroui/button';
import { Card, CardBody } from '@heroui/card';
import { Checkbox } from '@heroui/checkbox';
import { Slider } from '@heroui/slider';
import { useRef, useState } from 'react';
import { useControlContext } from '@/contexts/control-context';
import { Actions } from './consts';
import { rotationController } from './rotation-controller';
import { StateModal, StateModalRef } from './state-modal';
export const UIControls = () => {
const stateModalRef = useRef<StateModalRef | null>(null);
const [isSolving, setIsSolving] = useState(false);
const [isControlsOpen, setIsControlsOpen] = useState(true);
const {
rubiksCubeRef,
showRotationIndicators,
setShowRotationIndicators,
setBackground,
cubeRoughness,
setCubeRoughness,
cubeSpeed,
setCubeSpeed,
scrambleLength,
setScrambleLength,
} = useControlContext();
const scramble = () => {
const scrambleSteps = Array.from(
{ length: scrambleLength },
() => Actions[Math.floor(Math.random() * Actions.length)],
);
rubiksCubeRef?.current?.rotate(scrambleSteps);
};
const reset = () => {
rubiksCubeRef?.current?.reset();
setIsSolving(false);
};
const showState = () => {
const state = rotationController.getState();
stateModalRef.current?.open(state);
};
const train = () => {
window.open('https://github.com/crossentropy-ai/rlcube', '_blank');
};
const solve = async () => {
try {
setIsSolving(true);
const response = await fetch('/api/solve', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ state: rotationController.getState() }),
});
if (response.status === 422) {
alert('Unable to solve the cube.');
return;
}
if (!response.ok) {
throw new Error('Server error', { cause: response });
}
const { steps } = await response.json();
rotationController.addRotationStepCode(...steps);
} catch (err) {
alert('An error occurred. Check the console for details.');
console.error(err);
} finally {
setIsSolving(false);
}
};
return (
<div className="z-10 pointer-events-none">
<Card className="max-w-sm bg-white/30 border border-white/80 backdrop-blur-xl pointer-events-auto">
<CardBody className="flex flex-col">
<div className="flex justify-between items-center">
<div className="text-2xl font-bold">Controls</div>
<Button variant="light" size="sm" onPress={() => setIsControlsOpen(!isControlsOpen)}>
{isControlsOpen ? 'Hide' : 'Show'}
</Button>
</div>
<div
className={`
flex flex-col gap-6 transition-all duration-500 ease-in-out
${isControlsOpen ? 'max-h-[1000px] opacity-100' : 'max-h-0 opacity-0 pointer-events-none'}
overflow-hidden
`}
style={{ willChange: 'max-height, opacity' }}
>
<div className="flex flex-col gap-2 mt-6">
<div className="flex items-center justify-between">
<div className="text-sm">Rotation Indicators</div>
<Checkbox isSelected={showRotationIndicators} onValueChange={setShowRotationIndicators} />
</div>
<div className="flex items-center justify-between">
<div className="text-sm">Background</div>
<ButtonGroup size="sm">
<Button onPress={() => setBackground('sunset')}>Sunset</Button>
<Button onPress={() => setBackground('dawn')}>Dawn</Button>
<Button onPress={() => setBackground('forest')}>Forest</Button>
</ButtonGroup>
</div>
<Slider
size="sm"
label="Cube Roughness"
value={cubeRoughness}
onChange={(value) => setCubeRoughness(value as number)}
minValue={0.2}
maxValue={1}
step={0.01}
/>
<Slider
size="sm"
label="Cube Speed"
value={cubeSpeed}
onChange={(value) => setCubeSpeed(value as number)}
minValue={1}
maxValue={10}
step={1}
/>
<Slider
size="sm"
label="Scramble Length"
value={scrambleLength}
onChange={(value) => setScrambleLength(value as number)}
minValue={1}
maxValue={6}
step={1}
/>
</div>
<div className="flex flex-col gap-2">
<div className="flex gap-2">
<ButtonGroup size="sm">
<Button onPress={scramble}>Scramble</Button>
<Button onPress={reset}>Reset</Button>
</ButtonGroup>
<Button variant="light" size="sm" onPress={showState}>
Show State
</Button>
<Button size="sm" className="ms-auto" color="success" onPress={solve} isLoading={isSolving}>
Solve
</Button>
</div>
<div className="text-sm italic font-bold underline text-primary cursor-pointer" onClick={train}>
Train your own model!
</div>
</div>
</div>
</CardBody>
</Card>
<StateModal ref={stateModalRef} />
</div>
);
};
|