feat: new task
This commit is contained in:
Binary file not shown.
@@ -0,0 +1,54 @@
|
||||
# Space Exploration 4
|
||||
|
||||
**Title:** Исследуй Планету
|
||||
**Subtitle:** Собери ресурсы, решая примеры!
|
||||
**Footer bubble:** Итого собрано ресурсов:
|
||||
|
||||
## Layout
|
||||
|
||||
- 2 columns, 20 problems per page
|
||||
- Icons: pack1 (pages 1–6), pack2 (pages 7–9)
|
||||
|
||||
## Problem Pattern
|
||||
|
||||
A ± B, where:
|
||||
- B is from 1 to 10
|
||||
- Result is from 1 to 29
|
||||
- Each problem crosses a 10-boundary: either 10 or 20 (strictly, not just touching)
|
||||
|
||||
Types:
|
||||
- Add crossing 10: A ∈ [1,9], B such that A+B ∈ [11,29]
|
||||
- Add crossing 20: A ∈ [11,19], B such that A+B ∈ [21,29]
|
||||
- Sub crossing 10: A ∈ [11,19], B such that A−B ∈ [1,9]
|
||||
- Sub crossing 20: A ∈ [21,29], B such that A−B ∈ [11,19]
|
||||
|
||||
All 4 types are mixed randomly on every page.
|
||||
|
||||
## Pages
|
||||
|
||||
### Page 1 (pack1)
|
||||
- **Hero:** spaceship1.jpeg, direction: row-reverse
|
||||
|
||||
### Page 2 (pack1)
|
||||
- **Hero:** spaceship2.jpeg, direction: row
|
||||
|
||||
### Page 3 (pack1)
|
||||
- **Hero:** spaceship3.png, direction: row-reverse
|
||||
|
||||
### Page 4 (pack1)
|
||||
- **Hero:** spaceship4.jpeg, direction: row
|
||||
|
||||
### Page 5 (pack1)
|
||||
- **Hero:** spaceship5.jpeg, direction: row-reverse
|
||||
|
||||
### Page 6 (pack1)
|
||||
- **Hero:** spaceship6.jpeg, direction: row
|
||||
|
||||
### Page 7 (pack2)
|
||||
- **Hero:** spaceship7.jpeg, direction: row-reverse
|
||||
|
||||
### Page 8 (pack2)
|
||||
- **Hero:** spaceship8.jpeg, direction: row
|
||||
|
||||
### Page 9 (pack2)
|
||||
- **Hero:** spaceship9.jpeg, direction: row-reverse
|
||||
File diff suppressed because it is too large
Load Diff
@@ -92,6 +92,16 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="doc-card">
|
||||
<div class="doc-preview"><img src="/public/previews/docs/space-exploration-4.png" alt=""></div>
|
||||
<div class="doc-info">
|
||||
<h3>Space Exploration 4</h3>
|
||||
<div class="doc-details"><span class="tag tag-pages">9 pages</span><span class="tag tag-type">pack1 + pack2</span><br>Addition & subtraction crossing 10 and 20 • A ± B, B ∈ [1,10], result ∈ [1,29]</div>
|
||||
<div class="doc-actions">
|
||||
<a class="btn-preview" href="/tasks/space-exploration/docs/space-exploration-4.template.html">Preview</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,211 @@
|
||||
/**
|
||||
* Generate space-exploration-4.template.html — 9-page worksheet with
|
||||
* addition and subtraction crossing 10 and 20.
|
||||
*
|
||||
* Usage: node tasks/space-exploration/scripts/generate-crossing.mjs
|
||||
* Output: tasks/space-exploration/docs/space-exploration-4.template.html
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import path from 'path';
|
||||
import { fileURLToPath } from 'url';
|
||||
|
||||
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
||||
const ROOT = path.resolve(__dirname, '../../..');
|
||||
|
||||
const pack1Icons = fs.readdirSync(path.join(ROOT, 'assets/themes/nms/icons/pack1'))
|
||||
.filter(f => f.endsWith('.png'))
|
||||
.map(f => `assets/themes/nms/icons/pack1/${f}`);
|
||||
|
||||
const pack2Icons = fs.readdirSync(path.join(ROOT, 'assets/themes/nms/icons/pack2'))
|
||||
.filter(f => f.endsWith('.png'))
|
||||
.map(f => `assets/themes/nms/icons/pack2/${f}`);
|
||||
|
||||
function shuffle(arr) {
|
||||
const a = [...arr];
|
||||
for (let i = a.length - 1; i > 0; i--) {
|
||||
const j = Math.floor(Math.random() * (i + 1));
|
||||
[a[i], a[j]] = [a[j], a[i]];
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
function randInt(min, max) {
|
||||
return Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
}
|
||||
|
||||
function pickAlignment() {
|
||||
const opts = ['justify-start', 'justify-center', 'justify-end'];
|
||||
return opts[Math.floor(Math.random() * opts.length)];
|
||||
}
|
||||
|
||||
// Generate 20 unique crossing problems (A ± B crosses 10 or 20)
|
||||
function genCrossingProblems() {
|
||||
const problems = [];
|
||||
const seen = new Set();
|
||||
|
||||
while (problems.length < 20) {
|
||||
const isAdd = Math.random() < 0.5;
|
||||
const crossAt = Math.random() < 0.5 ? 10 : 20;
|
||||
let a, b;
|
||||
|
||||
if (isAdd) {
|
||||
if (crossAt === 10) {
|
||||
// A ∈ [1,9], A+B ∈ [11,min(29,A+10)]
|
||||
a = randInt(1, 9);
|
||||
const minB = 11 - a;
|
||||
const maxB = Math.min(10, 29 - a);
|
||||
if (minB > maxB) continue;
|
||||
b = randInt(minB, maxB);
|
||||
} else {
|
||||
// A ∈ [11,19], A+B ∈ [21,min(29,A+10)]
|
||||
a = randInt(11, 19);
|
||||
const minB = 21 - a;
|
||||
const maxB = Math.min(10, 29 - a);
|
||||
if (minB > maxB) continue;
|
||||
b = randInt(minB, maxB);
|
||||
}
|
||||
const result = a + b;
|
||||
if (result < 1 || result > 29) continue;
|
||||
const expr = `${a} + ${b}`;
|
||||
if (!seen.has(expr)) { seen.add(expr); problems.push(expr); }
|
||||
} else {
|
||||
if (crossAt === 10) {
|
||||
// A ∈ [11,19], A−B ∈ [1,9]
|
||||
a = randInt(11, 19);
|
||||
const minB = a - 9;
|
||||
const maxB = Math.min(10, a - 1);
|
||||
if (minB > maxB || minB < 1) continue;
|
||||
b = randInt(Math.max(1, minB), maxB);
|
||||
} else {
|
||||
// A ∈ [21,29], A−B ∈ [11,19]
|
||||
a = randInt(21, 29);
|
||||
const minB = a - 19;
|
||||
const maxB = Math.min(10, a - 11);
|
||||
if (minB > maxB || minB < 1) continue;
|
||||
b = randInt(Math.max(1, minB), maxB);
|
||||
}
|
||||
const result = a - b;
|
||||
if (result < 1 || result > 29) continue;
|
||||
const expr = `${a} − ${b}`;
|
||||
if (!seen.has(expr)) { seen.add(expr); problems.push(expr); }
|
||||
}
|
||||
}
|
||||
|
||||
return problems;
|
||||
}
|
||||
|
||||
const pages = [
|
||||
{ hero: 'assets/themes/nms/hero-images/spaceship1.jpeg', footer: 'assets/themes/nms/footers/planet1.jpeg', dir: 'flex-row-reverse', pack: 'pack1' },
|
||||
{ hero: 'assets/themes/nms/hero-images/spaceship2.jpeg', footer: 'assets/themes/nms/footers/planet2.jpeg', dir: '', pack: 'pack1' },
|
||||
{ hero: 'assets/themes/nms/hero-images/spaceship3.png', footer: 'assets/themes/nms/footers/planet3.jpeg', dir: 'flex-row-reverse', pack: 'pack1' },
|
||||
{ hero: 'assets/themes/nms/hero-images/spaceship4.jpeg', footer: 'assets/themes/nms/footers/planet4.jpeg', dir: '', pack: 'pack1' },
|
||||
{ hero: 'assets/themes/nms/hero-images/spaceship5.jpeg', footer: 'assets/themes/nms/footers/planet5.jpeg', dir: 'flex-row-reverse', pack: 'pack1' },
|
||||
{ hero: 'assets/themes/nms/hero-images/spaceship6.jpeg', footer: 'assets/themes/nms/footers/planet6.jpeg', dir: '', pack: 'pack1' },
|
||||
{ hero: 'assets/themes/nms/hero-images/spaceship7.jpeg', footer: 'assets/themes/nms/footers/planet7.jpeg', dir: 'flex-row-reverse', pack: 'pack2' },
|
||||
{ hero: 'assets/themes/nms/hero-images/spaceship8.jpeg', footer: 'assets/themes/nms/footers/planet8.jpeg', dir: '', pack: 'pack2' },
|
||||
{ hero: 'assets/themes/nms/hero-images/spaceship9.jpeg', footer: 'assets/themes/nms/footers/planet9.jpeg', dir: 'flex-row-reverse', pack: 'pack2' },
|
||||
];
|
||||
|
||||
// Icons: pages 0-5 → pack1 (shuffled twice across 3-page sections), pages 6-8 → pack2
|
||||
const iconSections = [
|
||||
shuffle(pack1Icons), // pages 0-2
|
||||
shuffle(pack1Icons), // pages 3-5
|
||||
shuffle(pack2Icons), // pages 6-8
|
||||
];
|
||||
|
||||
function buildProblemCard(icon, expression, alignment) {
|
||||
return ` <div class="flex ${alignment}">
|
||||
<div class="flex items-center gap-1">
|
||||
<img src="../../../${icon}" class="w-[58px] h-[58px] shrink-0 object-contain" alt="">
|
||||
<div class="pl-3 pr-4 py-1 border-[1.5px] border-indigo-100 rounded-full bg-gradient-to-br from-white to-indigo-50/40 shadow-sm shadow-indigo-100/30">
|
||||
<span class="text-[1.2rem] font-semibold text-indigo-950 whitespace-nowrap">${expression} = <span class="inline-block w-16 border-b-[2px] border-indigo-300"> </span></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
function buildPage(pageIdx, isLast) {
|
||||
const pg = pages[pageIdx];
|
||||
const sectionIdx = Math.floor(pageIdx / 3);
|
||||
const posInSection = pageIdx % 3;
|
||||
const sectionIcons = iconSections[sectionIdx];
|
||||
const pageIcons = sectionIcons.slice(posInSection * 20, posInSection * 20 + 20);
|
||||
|
||||
const problems = genCrossingProblems();
|
||||
const dirClass = pg.dir ? ` ${pg.dir}` : '';
|
||||
const breakStyle = isLast ? '' : ' style="break-after: page;"';
|
||||
|
||||
const cards = problems.map((expr, i) =>
|
||||
buildProblemCard(pageIcons[i], expr, pickAlignment())
|
||||
).join('\n\n');
|
||||
|
||||
return ` <!-- PAGE ${pageIdx + 1} -->
|
||||
<div class="w-[210mm] h-[297mm] relative overflow-hidden mx-auto bg-white"${breakStyle}>
|
||||
|
||||
<div class="absolute bottom-0 left-0 right-0 h-[80mm] z-0">
|
||||
<div class="absolute top-0 left-0 right-0 h-full z-10" style="background: linear-gradient(to bottom, white 0%, rgba(255,255,255,0.6) 25%, transparent 50%);"></div>
|
||||
<img src="../../../${pg.footer}" class="w-full h-full object-cover object-top" alt="">
|
||||
</div>
|
||||
|
||||
<div class="absolute bottom-[12mm] left-0 right-0 z-20 flex justify-center">
|
||||
<div style="background: rgba(255,255,255,0.85);" class="rounded-full px-6 py-1.5 border-[1.5px] border-indigo-200 shadow-md">
|
||||
<span class="text-[0.95rem] font-bold text-indigo-950">Итого собрано ресурсов: <span class="inline-block w-14 border-b-2 border-indigo-400 text-center ml-1"> </span></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="relative z-10 w-full h-full px-[12mm] pt-[4mm] pb-[65mm] flex flex-col">
|
||||
|
||||
<div class="flex items-center gap-4 mb-4${dirClass}">
|
||||
<img src="../../../${pg.hero}" class="w-[48%] shrink-0 object-contain" alt="">
|
||||
<div class="flex-1 flex flex-col items-center justify-center text-center">
|
||||
<h1 class="text-2xl font-extrabold leading-tight tracking-tight text-indigo-950">Исследуй Планету</h1>
|
||||
<p class="text-sm font-medium text-indigo-400 mt-1">Собери ресурсы, решая примеры!</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="grid grid-cols-2 gap-x-3 gap-y-[3px] flex-1 content-start">
|
||||
|
||||
${cards}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>`;
|
||||
}
|
||||
|
||||
const htmlPages = pages.map((_, i) => buildPage(i, i === pages.length - 1)).join('\n\n');
|
||||
|
||||
const html = `<!DOCTYPE html>
|
||||
<html lang="ru">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<script src="https://cdn.tailwindcss.com"></script>
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
<link href="https://fonts.googleapis.com/css2?family=Nunito:wght@400;600;700;800;900&display=swap" rel="stylesheet">
|
||||
<title>Исследуй Планету</title>
|
||||
<script>
|
||||
tailwind.config = {
|
||||
theme: {
|
||||
extend: {
|
||||
fontFamily: { nunito: ['Nunito', 'sans-serif'] },
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style>
|
||||
@page { size: A4; margin: 0; }
|
||||
</style>
|
||||
</head>
|
||||
<body class="bg-gray-200 font-nunito">
|
||||
|
||||
${htmlPages}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
`;
|
||||
|
||||
const outPath = path.join(__dirname, '..', 'docs', 'space-exploration-4.template.html');
|
||||
fs.writeFileSync(outPath, html, 'utf-8');
|
||||
console.log(`Generated: ${outPath} (${pages.length} pages, ${pages.length * 20} problems)`);
|
||||
@@ -0,0 +1,21 @@
|
||||
function cars(A = [0, 1, 0, 1, 1]) {
|
||||
let s = 0;
|
||||
const countedA = A.reverse().map(x => {
|
||||
if (!x) {
|
||||
return 0
|
||||
}
|
||||
s++;
|
||||
return s
|
||||
}).reverse();
|
||||
|
||||
let sum = 0;
|
||||
while(countedA.length) {
|
||||
const car = countedA.shift();
|
||||
if(car) {
|
||||
continue
|
||||
}
|
||||
const count = countedA.find(n => n > 0);
|
||||
sum = sum + count;
|
||||
}
|
||||
return sum
|
||||
}
|
||||
Reference in New Issue
Block a user