Files
doodle-mini/miniprogram/focusPages/gridReasoning/gridReasoning.ts
T
2025-12-19 10:34:30 +08:00

262 lines
7.7 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import GridReasoningDraw from '../shared/service/gridReasoningDraw';
import { createFocusPage } from '../shared/common/focusPageMixin';
import {
GridReasoningData,
GridPosition,
} from '../shared/service/gridReasoningDraw';
import { NUMBER_COLORS } from '../../constants/colors';
createFocusPage({
canvas: null as Canvas | null,
ctx: null as RenderingContext | null,
boxHeight: 0,
boxWidth: 0,
drawService: null as GridReasoningDraw | null,
gridData: null as GridReasoningData | null,
data: {
pageTitle: '方格推理',
functionId: '',
hasContent: false,
showShareDialog: false,
currentOperatorType: 'mixed', // 'addition', 'subtraction', 'mixed'
currentOperatorTypeName: '混合运算',
operatorTypeActions: [
{ name: '加法运算', value: 'addition' },
{ name: '减法运算', value: 'subtraction' },
{ name: '混合运算', value: 'mixed' },
],
},
onLoad(options: { id?: string }) {
const functionId = options.id || 'grid-reasoning';
this.setData({
functionId,
});
this.initPageInfo(functionId, '方格推理');
},
onReady() {
this.initCanvas({
createDrawService: (
canvas: Canvas,
ctx: RenderingContext,
options?: Record<string, any>,
) => {
return new GridReasoningDraw(canvas, ctx, options);
},
drawServiceOptions: {
title: this.data.pageTitle,
subTitle: '仔细观察,推理出合并方格并连线',
functionId: this.data.functionId,
},
onCanvasReady: () => {
// 初始随机生成
this.onRandom();
},
});
},
/**
* 绘制Canvas内容
*/
async drawCanvas() {
if (!this.ctx || !this.drawService || !this.gridData) {
return;
}
try {
await this.drawService.draw(this.gridData);
this.setData({ hasContent: true });
} catch (error) {
console.error('绘制失败:', error);
this.setData({ hasContent: false });
}
},
/**
* 选择运算类型
*/
onSelectType(event: any) {
const { name, value } = event.detail;
this.setData({
currentOperatorType: value,
currentOperatorTypeName: name,
});
// 重新生成题目
this.onRandom();
},
/**
* 随机生成
*/
onRandom() {
// 随机选择一种颜色
const colorKeys = Object.keys(NUMBER_COLORS).map(Number);
const randomColorKey =
colorKeys[Math.floor(Math.random() * colorKeys.length)];
const selectedColor = NUMBER_COLORS[randomColorKey];
const rows: Array<{
grid1: GridPosition[];
grid2: GridPosition[];
operator: '+' | '-';
result: GridPosition[];
}> = [];
const operatorType = this.data.currentOperatorType;
// 生成5行的数据
for (let i = 0; i < 5; i++) {
// 根据选中的运算类型选择运算符
let operator: '+' | '-';
if (operatorType === 'addition') {
operator = '+';
} else if (operatorType === 'subtraction') {
operator = '-';
} else {
// 混合运算:随机选择运算符
operator = Math.random() > 0.5 ? '+' : '-';
}
// 生成第一个网格的涂色位置
// 如果是减法运算,第一个网格至少要有2个位置
let grid1Count: number;
if (operator === '-') {
grid1Count = Math.floor(Math.random() * 8) + 2; // 2-9个位置
} else {
grid1Count = Math.floor(Math.random() * 9) + 1; // 1-9个位置
}
const grid1Positions = this.generateRandomPositions(grid1Count);
let grid2Positions: GridPosition[];
let resultPositions: GridPosition[];
if (operator === '+') {
// 加法:生成第二个网格的涂色位置(至少1个)
const grid2Count = Math.floor(Math.random() * 9) + 1;
grid2Positions = this.generateRandomPositions(grid2Count);
// 结果 = 并集
resultPositions = this.unionPositions(
grid1Positions,
grid2Positions,
);
} else {
// 减法:生成第二个网格的涂色位置(必须少于第一个)
const maxGrid2Count = grid1Count - 1;
const grid2Count =
Math.floor(Math.random() * maxGrid2Count) + 1;
grid2Positions = this.generateRandomPositions(grid2Count);
// 结果 = 差集
resultPositions = this.subtractPositions(
grid1Positions,
grid2Positions,
);
}
rows.push({
grid1: grid1Positions,
grid2: grid2Positions,
operator: operator as '+' | '-',
result: resultPositions,
});
}
// 生成右侧答案选项
// 右侧展示左侧5行的结果(result),顺序随机
const answerOptions: Array<{
positions: GridPosition[];
rowIndex: number;
}> = [];
for (let i = 0; i < 5; i++) {
answerOptions.push({
positions: rows[i].result,
rowIndex: i,
});
}
// 随机打乱顺序
answerOptions.sort(() => Math.random() - 0.5);
this.gridData = {
rows,
answerOptions,
color: selectedColor,
};
this.drawCanvas();
},
/**
* 生成随机位置(不重复)
*/
generateRandomPositions(count: number): GridPosition[] {
const allPositions: GridPosition[] = [];
for (let y = 0; y < 3; y++) {
for (let x = 0; x < 3; x++) {
allPositions.push({ x, y });
}
}
// 随机打乱
const shuffled = [...allPositions].sort(() => Math.random() - 0.5);
// 返回前count个
return shuffled.slice(0, count);
},
/**
* 计算位置的并集
*/
unionPositions(pos1: GridPosition[], pos2: GridPosition[]): GridPosition[] {
const set = new Set<string>();
const result: GridPosition[] = [];
// 添加第一个数组的位置
for (const pos of pos1) {
const key = `${pos.x},${pos.y}`;
if (!set.has(key)) {
set.add(key);
result.push(pos);
}
}
// 添加第二个数组的位置(去重)
for (const pos of pos2) {
const key = `${pos.x},${pos.y}`;
if (!set.has(key)) {
set.add(key);
result.push(pos);
}
}
return result;
},
/**
* 计算位置的差集(pos1 - pos2
*/
subtractPositions(
pos1: GridPosition[],
pos2: GridPosition[],
): GridPosition[] {
const set2 = new Set<string>();
for (const pos of pos2) {
set2.add(`${pos.x},${pos.y}`);
}
const result: GridPosition[] = [];
for (const pos of pos1) {
const key = `${pos.x},${pos.y}`;
if (!set2.has(key)) {
result.push(pos);
}
}
return result;
},
});