/** * 회전도형 (MR) — 50단계(같은도형25/다른도형25), Same/Different, 5초 (PRD §3.1.5) */ import { useMemo } from 'react'; import styled from 'styled-components'; import TestRunner from '../components/TestRunner'; import SameDiffButtons from '../components/SameDiffButtons'; import { MR_SAME_COUNT, MR_DIFF_COUNT, MR_STEP_DURATION, mrImage, INTRO_DATA } from '../data/tests'; import { http } from '../lib/api'; const rand = (min: number, max: number) => Math.floor(Math.random() * (max - min + 1)) + min; interface Spec { isSame: boolean; leftImage: string; rightImage: string; leftLabel: string; rightLabel: string; leftColor: number; rightColor: number; } const Stage = styled.div` flex: 1; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 16px; /* 도형 한 장 크기 — 두 도형 나란히, 가로화면에서 최대한 크게. - 너비 제약: 2장 나열이 가로폭(100vw - 셸패딩16 - 갭18)에 맞는 크기. - 높이 제약: 헤더(42+8) + 힌트(~20) + 버튼(40) + 셸패딩(16) + 갭(32) ≈ 158px 제외 후 남는 높이. min()으로 둘 중 작은 쪽 사용 → 절대 하단이 잘리지 않음. */ --mr-img: min( calc((100vw - 34px) / 2), calc(var(--test-vh, 100svh) - 180px) ); `; const Pair = styled.div` display: flex; gap: 18px; img { width: var(--mr-img); height: var(--mr-img); object-fit: contain; background: #fff; border-radius: 18px; padding: 10px; } `; const Hint = styled.p` color: var(--pcnt-text-sub); font-size: 14px; `; export default function TestMR() { const specs = useMemo(() => { const sameArr = [...Array(MR_SAME_COUNT).fill(true), ...Array(MR_DIFF_COUNT).fill(false)].sort( () => Math.random() - 0.5, ); return sameArr.map((isSame) => { const r1 = rand(1, 27); const r2 = rand(1, 27); if (isSame) { const s = rand(1, 10); return { isSame: true, leftImage: mrImage(s, r1), rightImage: mrImage(s, r2), leftLabel: `shape-${s}-${r1}`, rightLabel: `shape-${s}-${r2}`, leftColor: r1 % 3, rightColor: r2 % 3, }; } let s1 = rand(1, 10); let s2 = rand(1, 10); while (s2 === s1) s2 = rand(1, 10); return { isSame: false, leftImage: mrImage(s1, r1), rightImage: mrImage(s2, r2), leftLabel: `shape-${s1}-${r1}`, rightLabel: `shape-${s2}-${r2}`, leftColor: r1 % 3, rightColor: r2 % 3, }; }); }, []); const total = MR_SAME_COUNT + MR_DIFF_COUNT; return ( { const spec = specs[index]; return ( 두 도형이 같은 물체인가요? 왼쪽 도형 오른쪽 도형 record({ playTime, correct: spec.isSame === same, leftLabel: spec.leftLabel, rightLabel: spec.rightLabel, leftImage: spec.leftImage, rightImage: spec.rightImage, isSameShape: spec.isSame, leftColor: spec.leftColor, rightColor: spec.rightColor, }) } /> ); }} onTimeout={(index, record) => { const spec = specs[index]; record({ playTime: MR_STEP_DURATION, correct: false, leftLabel: spec.leftLabel, rightLabel: spec.rightLabel, leftImage: spec.leftImage, rightImage: spec.rightImage, isSameShape: spec.isSame, leftColor: spec.leftColor, rightColor: spec.rightColor, }); }} onSubmit={async (results) => { await http.put('/api/cntdata/MR', { data: results }); }} /> ); }