Skip to content

Commit

Permalink
merge branch main into 13-problemTemplete
Browse files Browse the repository at this point in the history
  • Loading branch information
taisii committed Feb 22, 2024
2 parents c36a081 + 3cc94dd commit 4ca9de9
Show file tree
Hide file tree
Showing 8 changed files with 190 additions and 68 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { SyntaxHighlighter } from '../../../../../../components/organisms/Syntax
import type { TurtleGraphicsHandle } from '../../../../../../components/organisms/TurtleGraphics';
import { TurtleGraphics } from '../../../../../../components/organisms/TurtleGraphics';
import type { GeneratedProgram, ProblemType } from '../../../../../../types';
import { solveProblem } from '../../../../../lib/solveProblem';

import { Variables } from './Variables';

interface CheckpointProblemProps {
problemProgram: GeneratedProgram;
Expand All @@ -31,6 +34,8 @@ export const CheckpointProblem: React.FC<CheckpointProblemProps> = ({
}) => {
const turtleGraphicsRef = useRef<TurtleGraphicsHandle>(null);

const beforeCheckpointResult = solveProblem(problemProgram.excuteProgram).histories?.at(beforeCheckPointLine);

const handleClickResetButton = (): void => {
turtleGraphicsRef.current?.init();
};
Expand Down Expand Up @@ -86,14 +91,18 @@ export const CheckpointProblem: React.FC<CheckpointProblemProps> = ({
</VStack>
<VStack align="end" minW="50%" overflow="hidden">
<Button colorScheme="gray">解説</Button>
<Box h="840px" w="100%">
<Box h="640px" w="100%">
<SyntaxHighlighter
beforeCheckPointLine={beforeCheckPointLine}
code={problemProgram.displayProgram}
currentCheckPointLine={currentCheckPointLine}
programmingLanguageId={selectedLanguageId}
/>
</Box>
<Variables
characterVariables={beforeCheckpointResult?.characterVariables}
variables={beforeCheckpointResult?.otherVariables}
/>
<HStack>
<Button onClick={() => handleClickResetButton()}>リセット</Button>
<Button onClick={() => handleClickAnswerButton()}>解答</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import { SyntaxHighlighter } from '../../../../../../components/organisms/Syntax
import type { TurtleGraphicsHandle } from '../../../../../../components/organisms/TurtleGraphics';
import { TurtleGraphics } from '../../../../../../components/organisms/TurtleGraphics';
import type { GeneratedProgram } from '../../../../../../types';
import { solveProblem } from '../../../../../lib/solveProblem';

import { Variables } from './Variables';

interface StepProblemProps {
beforeCheckPointLine: number;
Expand All @@ -29,6 +32,8 @@ export const StepProblem: React.FC<StepProblemProps> = ({
}) => {
const turtleGraphicsRef = useRef<TurtleGraphicsHandle>(null);

const beforeCheckpointResult = solveProblem(problemProgram.excuteProgram).histories?.at(beforeCheckPointLine);

const handleClickResetButton = (): void => {
turtleGraphicsRef.current?.init();
};
Expand Down Expand Up @@ -80,14 +85,18 @@ export const StepProblem: React.FC<StepProblemProps> = ({
</VStack>
<VStack align="end" minW="50%" overflow="hidden">
<Button colorScheme="gray">解説</Button>
<Box h="840px" w="100%">
<Box h="640px" w="100%">
<SyntaxHighlighter
beforeCheckPointLine={beforeCheckPointLine}
code={problemProgram.displayProgram}
currentCheckPointLine={currentCheckPointLine}
programmingLanguageId={selectedLanguageId}
/>
</Box>
<Variables
characterVariables={beforeCheckpointResult?.characterVariables}
variables={beforeCheckpointResult?.otherVariables}
/>
<HStack>
<Button onClick={() => handleClickResetButton()}>リセット</Button>
<Button onClick={() => handleClickAnswerButton()}>解答</Button>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { Box, Table, TableCaption, TableContainer, Tbody, Td, Th, Thead, Tr, VStack } from '@chakra-ui/react';
import React from 'react';

import type { CharacterDirection, CharacterVariable, Variable } from '../../../../../../types';

interface VariablesProps {
characterVariables?: CharacterVariable[];
variables?: Variable[];
}

const directionJapanese: { [key in CharacterDirection]: string } = {
up: '上',
down: '下',
left: '左',
right: '右',
};

const penStateJapanese = (penDown: boolean): string => {
return penDown ? 'おいている' : 'あげている';
};

export const Variables: React.FC<VariablesProps> = ({ characterVariables, variables }) => {
return (
<VStack>
<TableContainer w="100%">
<Table variant="simple">
<TableCaption placement="top">キャラクター</TableCaption>
<Thead>
<Tr>
<Th>変数名</Th>
<Th>名前</Th>
<Th>線の色</Th>
<Th>向き</Th>
<Th>ペンの状態</Th>
</Tr>
</Thead>
<Tbody>
{characterVariables?.map((variable) => (
<Tr key={variable.value.id}>
<Td>{variable.name}</Td>
<Td>{variable.value.name}</Td>
<Td>
<Box bg={variable.value.color} h="20px" w="20px" />
</Td>
<Td>{directionJapanese[variable.value.direction]}</Td>
<Td>{penStateJapanese(variable.value.penDown)}</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
<TableContainer mb="4" w="100%">
<Table variant="simple">
<TableCaption placement="top">変数</TableCaption>
<Thead>
<Tr>
<Th>変数名</Th>
<Th></Th>
</Tr>
</Thead>
<Tbody>
{variables?.map((variable) => (
<Tr key={variable.name}>
<Td>{variable.name}</Td>
<Td>{variable.value}</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
</VStack>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ const ProblemPage: NextPage<{ params: { courseId: string; programId: string } }>
const courseId = params.courseId;
const programId = params.programId;
// TODO: チェックポイントを取得する処理が実装できたら置き換える
const checkPointLines = [1, 4];
const checkPointLines = [2, 6, 8, 12];

const [selectedLanguageId, setSelectedLanguageId] = useState('');
const [problemProgram, setProblemProgram] = useState<GeneratedProgram>();
Expand Down
66 changes: 42 additions & 24 deletions src/app/lib/solveProblem.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import type { History, SolveProblemResult } from '../../types';
import type { CharacterVariable, History, SolveProblemResult, Variable } from '../../types';

import { Board as BoardClass } from './Board';
import { Character as CharacterClass } from './Character';
Expand All @@ -10,38 +10,51 @@ export function parseProgram(program: string): string[] {
.filter((line) => line !== '');
}

export function executeEval(command: string): CharacterClass[] {
export function executeEval(command: string): (CharacterVariable | Variable)[] {
const Character = CharacterClass; // eslint-disable-line
const Board = BoardClass; // eslint-disable-line
const characterVariableName = 'character';
const charactersVariables = extractVariables(characterVariableName, command);
const variableNames = extractVariableNames(command);
const semicolonEndedCommand = (() => {
if (command.endsWith(';')) return command;
return (command += ';');
})();

const returnValueCommand = `
[${charactersVariables}];
`;
const result = variableNames.map((variableName) => {
const returnValueCommand = `
${variableName};
`;
const mergedCommand = semicolonEndedCommand + '\n' + returnValueCommand;
return { name: variableName, value: eval(mergedCommand) };
});

const mergedCommand = semicolonEndedCommand + '\n' + returnValueCommand;
return result;
}

return eval(mergedCommand);
export function selectCharacterVariables(variables: (CharacterVariable | Variable)[]): CharacterVariable[] {
return variables.filter((variable) => variable.value instanceof CharacterClass) as CharacterVariable[];
}

export function extractVariables(variableName: string, command: string): string[] {
const regex = new RegExp(`${variableName}\\d+`, 'g');
const matches = command.match(regex);
export function selectOtherVariables(variables: (CharacterVariable | Variable)[]): Variable[] {
return variables.filter((variable) => !(variable.value instanceof CharacterClass)) as Variable[];
}

export function extractVariableNames(command: string): string[] {
// 'const' 'let' 'var' で始まる変数名を comand から抽出する
const regex = /(?:const|let|var)\s+(\w+)\s*=\s*(.*?);/g;
const matches = [...command.matchAll(regex)];

if (matches) {
return [...new Set(matches)];
return matches.map((match) => {
return match[1];
});
}
return [];
}

export function solveProblem(program: string): SolveProblemResult {
const commands = parseProgram(program);
const board = new BoardClass();
const histories: History[] = [{ step: 0, characters: [], board }];
const histories: History[] = [{ step: 0, characterVariables: [], board, otherVariables: [] }];

for (let i = 0; i < commands.length; i++) {
if (i < commands.length) {
Expand All @@ -51,26 +64,29 @@ export function solveProblem(program: string): SolveProblemResult {
mergedCommand += commands[j];
}

const characters = executeEval(mergedCommand);
const variables = executeEval(mergedCommand);
const characterVariables = selectCharacterVariables(variables);
const otherVariables = selectOtherVariables(variables);

const board = new BoardClass();
for (const history of histories) {
if (!history.characters) continue;
if (!history.characterVariables) continue;

for (const character of history.characters) {
board.updateGrid(character);
for (const character of history.characterVariables) {
board.updateGrid(character.value);
}
}
for (const character of characters) {
board.updateGrid(character);
for (const character of characterVariables) {
board.updateGrid(character.value as CharacterClass);
}

histories.push({ step: histories.length + 1, characters, board });
histories.push({ step: histories.length + 1, characterVariables, board, otherVariables });
}
}

const result: SolveProblemResult = {
characters: histories?.at(-1)?.characters,
characterVariables: histories?.at(-1)?.characterVariables,
otherVariables: histories?.at(-1)?.otherVariables,
board: histories?.at(-1)?.board || board,
histories,
};
Expand All @@ -85,10 +101,12 @@ export function isAnswerCorrect(
): boolean {
const correctAnswer = solveProblem(problemProgram).histories?.at(step || -1);

if (!correctAnswer || !correctAnswer.characters) return false;
if (!correctAnswer || !correctAnswer.characterVariables) return false;

// 順番は関係なく、id以外のキャラクターの状態が一致しているかチェック
const isCorrectCharacters: boolean = correctAnswer.characters.every((correctCharacter) => {
const isCorrectCharacters: boolean = correctAnswer.characterVariables.every((characterVariable) => {
const correctCharacter = characterVariable.value;

const character = answerCharacters.find(
(answerCharacter) =>
answerCharacter.name === correctCharacter.name &&
Expand Down
2 changes: 1 addition & 1 deletion src/components/organisms/TurtleGraphics.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export const TurtleGraphics = forwardRef<TurtleGraphicsHandle, TurtleGraphicsPro

const solveResult = solveProblem(problemProgram).histories?.at(beforeCheckPointLine);
const initBoard = solveResult?.board;
const initCharacters = solveResult?.characters;
const initCharacters = solveResult?.characterVariables?.map((character) => character.value);

setBoard(initBoard || new Board());
setCharacters(initCharacters || []);
Expand Down
10 changes: 8 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,20 @@ export type SelectedCell = {
y: number;
};

export type CharacterVariable = { name: string; value: Character };

export type Variable = { name: string; value: string };

export type History = {
step: number;
characters: Character[] | undefined;
characterVariables: CharacterVariable[] | undefined;
board: Board;
otherVariables: Variable[];
};

export type SolveProblemResult = {
characters: Character[] | undefined;
characterVariables: CharacterVariable[] | undefined;
otherVariables: Variable[] | undefined;
board: Board;
histories: History[] | undefined;
};
Expand Down
Loading

0 comments on commit 4ca9de9

Please sign in to comment.