Skip to content

Commit

Permalink
display variables
Browse files Browse the repository at this point in the history
  • Loading branch information
Tatehito committed Feb 20, 2024
1 parent 9b2b73e commit 37b2001
Show file tree
Hide file tree
Showing 10 changed files with 143 additions and 69 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export const CheckpointProblem: React.FC<CheckpointProblemProps> = ({
}) => {
const turtleGraphicsRef = useRef<TurtleGraphicsHandle>(null);

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

const handleClickResetButton = (): void => {
turtleGraphicsRef.current?.init();
Expand Down Expand Up @@ -99,7 +99,10 @@ export const CheckpointProblem: React.FC<CheckpointProblemProps> = ({
programmingLanguageId={selectedLanguageId}
/>
</Box>
<Variables characters={characters} />
<Variables
characterVariables={beforeCheckpointResult?.characterVariables}
variables={beforeCheckpointResult?.variables}
/>
<HStack>
<Button onClick={() => handleClickResetButton()}>リセット</Button>
<Button onClick={() => handleClickAnswerButton()}>解答</Button>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ export const StepProblem: React.FC<StepProblemProps> = ({
}) => {
const turtleGraphicsRef = useRef<TurtleGraphicsHandle>(null);

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

const handleClickResetButton = (): void => {
turtleGraphicsRef.current?.init();
Expand Down Expand Up @@ -92,7 +92,10 @@ export const StepProblem: React.FC<StepProblemProps> = ({
programmingLanguageId={selectedLanguageId}
/>
</Box>
<Variables characters={characters} />
<Variables
characterVariables={beforeCheckpointResult?.characterVariables}
variables={beforeCheckpointResult?.variables}
/>
<HStack>
<Button onClick={() => handleClickResetButton()}>リセット</Button>
<Button onClick={() => handleClickAnswerButton()}>解答</Button>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { Box, Table, TableCaption, TableContainer, Tbody, Td, Th, Thead, Tr, VStack } from '@chakra-ui/react';
import React from 'react';

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

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

const directionJapanese: { [key in CharacterDirection]: string } = {
Expand All @@ -19,7 +20,7 @@ const penStateJapanese = (penDown: boolean): string => {
return penDown ? 'おいている' : 'あげている';
};

export const Variables: React.FC<VariablesProps> = ({ characters }) => {
export const Variables: React.FC<VariablesProps> = ({ characterVariables, variables }) => {
return (
<VStack>
<TableContainer w="100%">
Expand All @@ -35,15 +36,15 @@ export const Variables: React.FC<VariablesProps> = ({ characters }) => {
</Tr>
</Thead>
<Tbody>
{characters?.map((character) => (
<Tr key={character.id}>
<Td></Td>
<Td>{character.name}</Td>
{characterVariables?.map((variable) => (
<Tr key={variable.value.id}>
<Td>{variable.name}</Td>
<Td>{variable.value.name}</Td>
<Td>
<Box bg={character.color} h="20px" w="20px" />
<Box bg={variable.value.color} h="20px" w="20px" />
</Td>
<Td>{directionJapanese[character.direction]}</Td>
<Td>{penStateJapanese(character.penDown)}</Td>
<Td>{directionJapanese[variable.value.direction]}</Td>
<Td>{penStateJapanese(variable.value.penDown)}</Td>
</Tr>
))}
</Tbody>
Expand All @@ -59,8 +60,12 @@ export const Variables: React.FC<VariablesProps> = ({ characters }) => {
</Tr>
</Thead>
<Tbody>
<Td></Td>
<Td></Td>
{variables?.map((variable) => (
<Tr key={variable.name}>
<Td>{variable.name}</Td>
<Td>{variable.value}</Td>
</Tr>
))}
</Tbody>
</Table>
</TableContainer>
Expand Down
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, 6];
const checkPointLines = [5, 8];

const [selectedLanguageId, setSelectedLanguageId] = useState('');
const [problemProgram, setProblemProgram] = useState<string>('');
Expand Down
9 changes: 9 additions & 0 deletions src/app/lib/Variable.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export class Variable {
name: string;
value: string;

constructor({ name, value }: { name: string; value: string }) {
this.name = name;
this.value = value;
}
}
81 changes: 64 additions & 17 deletions src/app/lib/solveProblem.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import type { History, SolveProblemResult } from '../../types';
import type { CharacterVariable, History, SolveProblemResult } from '../../types';

import { Board as BoardClass } from './Board';
import { Character as CharacterClass } from './Character';
import { Variable as VariableClass } from './Variable';

export function parseProgram(program: string): string[] {
return program
Expand All @@ -10,25 +11,45 @@ export function parseProgram(program: string): string[] {
.filter((line) => line !== '');
}

export function executeEval(command: string): CharacterClass[] {
export function getVariableValue(command: string, variableName: string): string {
const Character = CharacterClass; // eslint-disable-line
const Board = BoardClass; // eslint-disable-line
const characterVariableName = 'character';
const charactersVariables = extractVariables(characterVariableName, command);
const Variable = VariableClass; // eslint-disable-line

const semicolonEndedCommand = (() => {
if (command.endsWith(';')) return command;
return (command += ';');
})();

const returnValueCommand = `
[${charactersVariables}];
${variableName};
`;

const mergedCommand = semicolonEndedCommand + '\n' + returnValueCommand;

return eval(mergedCommand);
}

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

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

return result;
}

export function extractVariables(variableName: string, command: string): string[] {
const regex = new RegExp(`${variableName}\\d+`, 'g');
const matches = command.match(regex);
Expand All @@ -38,10 +59,23 @@ export function extractVariables(variableName: string, command: string): string[
return [];
}

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 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, variables: [] }];

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

const characters = executeEval(mergedCommand);
const characterVariables = executeEval(mergedCommand);

// TODO: extractVariableNames と extractVariablesをまとめたい
// TODO: executeEval と getVariableValue をまとめたい
// TODO: Vraiableクラス必要?
const variableNames = extractVariableNames(mergedCommand).filter((name) => !name.startsWith('character'));

const variables = variableNames.map((name) => {
const value = getVariableValue(mergedCommand, name);
return new VariableClass({ name, value });
});

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);
}

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

const result: SolveProblemResult = {
characters: histories?.at(-1)?.characters,
characterVariables: histories?.at(-1)?.characterVariables,
variables: histories?.at(-1)?.variables,
board: histories?.at(-1)?.board || board,
histories,
};
Expand All @@ -85,10 +130,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
2 changes: 2 additions & 0 deletions src/problems/problemData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ export function generateProgram(programId: ProgramId, languageId: LanguageId): s
character1.moveForward();
character1.turnLeft();
character1.moveForward();
let i = 0;
i = i + 1;
character1.moveForward();
character1.moveForward();
character1.moveForward();
Expand Down
9 changes: 7 additions & 2 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import type React from 'react';

import type { Board } from './app/lib/Board';
import type { Character } from './app/lib/Character';
import type { Variable } from './app/lib/Variable';

export interface LayoutProps {
children: React.ReactNode;
Expand All @@ -18,14 +19,18 @@ export type SelectedCell = {
y: number;
};

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

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

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

0 comments on commit 37b2001

Please sign in to comment.