643 lines
22 KiB
JavaScript
643 lines
22 KiB
JavaScript
export const lessons = [
|
|
{
|
|
id: 'lesson1',
|
|
title: '1. Introduction to Python',
|
|
difficulty: 'easy',
|
|
content: `
|
|
<p>Let's learn some Python..</p>
|
|
<p>We'll start with what's called a "Hello World" program to make sure everythings working.</p>
|
|
<p>In Python, this is done with the <code>print</code> function.</p>
|
|
<ol>
|
|
<li>In the code editor below, type <code>print("Hello World")</code></li>
|
|
<li>Click the "Run" button to execute your code</li>
|
|
<li>You should see "Hello World" printed in the output area</li>
|
|
</ol>
|
|
`,
|
|
doneCondition: (() => {
|
|
const progress = {
|
|
stringDone: false
|
|
};
|
|
|
|
return ({ code, consoleText, codeRanGood }) => {
|
|
if (codeRanGood && !progress.stringDone && consoleText.includes("Hello World")) {
|
|
progress.stringDone = true;
|
|
}
|
|
|
|
const missing = [];
|
|
if (!progress.stringDone) missing.push("Hello World");
|
|
|
|
let hint = "I still need you to print ";
|
|
if (missing.length === 0) {
|
|
hint = "";
|
|
} else if (missing.length === 1) {
|
|
hint += missing[0];
|
|
} else {
|
|
hint += missing.slice(0, -1).join(", ") + " and " + missing[missing.length - 1];
|
|
}
|
|
|
|
return {
|
|
done: progress.stringDone,
|
|
hint
|
|
};
|
|
};
|
|
})()
|
|
},
|
|
{
|
|
id: 'lesson2',
|
|
title: 'Data Types and Variables',
|
|
difficulty: 'easy',
|
|
content: `
|
|
<p>Did you try typeing <code>print(Hello World)</code>, without the quotation marks?</p>
|
|
<p>If you didn't, give it a try now.</p>
|
|
</br>
|
|
<p>Thats what we call a <strong>Syntax Error</strong>.</p>
|
|
<p>Python doesn't know what the term (Hello World) means, the "" we added before tell Python that this is a "string", a series of characters that it doesn't need to try and understand, just to repeat.</p>
|
|
</br>
|
|
String is just one of the many <strong>Data Types</strong> that Python has.</p>
|
|
To continue, I want you to print the following all at once, each on a new line:
|
|
<ol>
|
|
<li>String: <code>print("Hello World")</code></li>
|
|
<li>Integer: <code>print(42)</code></li>
|
|
<li>Float: <code>print(3.14)</code></li>
|
|
<li>Boolean: <code>print(True)</code></li>
|
|
</ol>
|
|
<p>Click the "Run" button to execute your code</p>
|
|
`,
|
|
doneCondition: (() => {
|
|
const progress = {
|
|
stringDone: false,
|
|
intDone: false,
|
|
floatDone: false,
|
|
boolDone: false,
|
|
// syntaxErrorDone: false, // optional
|
|
};
|
|
|
|
const stringRegex = /(["'])(?:(?=(\\?))\2.)*?\1/;
|
|
const intRegex = /(?<![\d.])[-+]?\d+(?![\d.])/;
|
|
const floatRegex = /[-+]?(?:\d+\.\d*|\.\d+)(?:[eE][-+]?\d+)?/;
|
|
const boolRegex = /\b(True|False|true|false)\b/;
|
|
|
|
return ({ code, consoleText, codeRanGood }) => {
|
|
if (!codeRanGood) {
|
|
return {
|
|
done: false,
|
|
hint: "Your code had an error — try fixing it and run again."
|
|
};
|
|
}
|
|
|
|
if (!progress.stringDone && stringRegex.test(consoleText)) {
|
|
progress.stringDone = true;
|
|
}
|
|
if (!progress.floatDone && floatRegex.test(consoleText)) {
|
|
progress.floatDone = true;
|
|
}
|
|
if (!progress.intDone && intRegex.test(consoleText)) {
|
|
progress.intDone = true;
|
|
}
|
|
if (!progress.boolDone && boolRegex.test(consoleText)) {
|
|
progress.boolDone = true;
|
|
}
|
|
|
|
const missing = [];
|
|
if (!progress.floatDone) missing.push("float");
|
|
if (!progress.intDone) missing.push("int");
|
|
if (!progress.boolDone) missing.push("boolean");
|
|
|
|
let hint = "I still need you to print a ";
|
|
if (missing.length === 0) {
|
|
hint = "";
|
|
} else if (missing.length === 1) {
|
|
hint += missing[0];
|
|
} else {
|
|
hint += missing.slice(0, -1).join(", ") + " and " + missing[missing.length - 1];
|
|
}
|
|
|
|
const done = progress.intDone && progress.floatDone && progress.boolDone;
|
|
return { done, hint };
|
|
};
|
|
})()
|
|
|
|
},
|
|
{
|
|
id: 'lesson3',
|
|
title: '3. Arithmetic Operations',
|
|
difficulty: 'easy',
|
|
content: `
|
|
<p>Let's get python to do our math homework for us.</p>
|
|
<p>Python can handle basic arithmetic operations like addition, subtraction, multiplication, and division.</p>
|
|
<p>Try the following operations in the code editor:</p>
|
|
<ol>
|
|
<li>Addition: <code>print(5 + 3)</code></li>
|
|
<li>Subtraction: <code>print(10 - 2)</code></li>
|
|
<li>Multiplication: <code>print(4 * 7)</code></li>
|
|
<li>Division: <code>print(20 / 5)</code></li>
|
|
</ol>
|
|
`,
|
|
doneCondition: (() => {
|
|
// Persistent tracking object inside closure
|
|
const progress = {
|
|
addDone: false,
|
|
subDone: false,
|
|
mulDone: false,
|
|
divDone: false,
|
|
|
|
};
|
|
|
|
|
|
|
|
return ({ code, consoleText, codeRanGood }) => {
|
|
if (!codeRanGood) {
|
|
return {
|
|
done: false,
|
|
hint: ""
|
|
};
|
|
}
|
|
|
|
// if (!progress.syntaxErrorDone && syntaxErrorRegex.test(text)) {
|
|
// progress.syntaxErrorDone = true;
|
|
// }
|
|
if (!progress.addDone && code.includes("+")) {
|
|
progress.addDone = true;
|
|
}
|
|
if (!progress.subDone && code.includes("-")) {
|
|
progress.subDone = true;
|
|
}
|
|
if (!progress.mulDone && code.includes("*")) {
|
|
progress.mulDone = true;
|
|
}
|
|
if (!progress.divDone && code.includes("/")) {
|
|
progress.divDone = true;
|
|
}
|
|
|
|
let missing = [];
|
|
|
|
//if (!progress.syntaxErrorDone) missing.push("syntax error");
|
|
//if (!progress.stringDone) missing.push("string");
|
|
if (!progress.addDone) missing.push("addition");
|
|
if (!progress.subDone) missing.push("subtraction");
|
|
if (!progress.mulDone) missing.push("multiplication");
|
|
if (!progress.divDone) missing.push("division");
|
|
let hint = "I still need you to do some ";
|
|
|
|
if (missing.length === 0) {
|
|
hint = "";
|
|
} else if (missing.length === 1) {
|
|
hint += missing[0];
|
|
} else {
|
|
// Join all but last with comma, then add 'and' + last
|
|
hint += missing.slice(0, -1).join(", ") + " and " + missing[missing.length - 1];
|
|
}
|
|
|
|
|
|
const done = progress.addDone && progress.subDone && progress.mulDone && progress.divDone;
|
|
return { done, hint };
|
|
};
|
|
})()
|
|
},
|
|
{
|
|
id: 'lesson4',
|
|
title: '4. Variables',
|
|
difficulty: 'easy',
|
|
content: `
|
|
<p>It's common for us to need to keep some data or objects over multiple lines, for that we use <strong>variables</strong>.</p>
|
|
<p>Think of a variable as a container that holds an object for us to use later.</p>
|
|
<p>To make one, we only need to give it a name, and assign it a value.</p>
|
|
<p><code>bob = 32</code></p>
|
|
<p>In this case, we created a variable called <code>bob</code> and assigned it the value <code>32</code>.</p>
|
|
<p>As far as Python is concerned, <code>bob</code> is just a name for the number <code>32</code>.</p>
|
|
<p>Lets test it</p>
|
|
<ol>
|
|
<li>Initialize bob with the value 32: <code>bob = 32</code></li>
|
|
<li>Print the value of bob: <code>print(bob)</code></
|
|
</ol>
|
|
`,
|
|
steps: [
|
|
{
|
|
content: `<p>First, try printing an addition: <code>print(2 + 3)</code></p>`,
|
|
doneCondition: (text) => text.includes("5"),
|
|
},
|
|
{
|
|
content: `<p>Nice! Now try subtraction: <code>print(5 - 2)</code></p>`,
|
|
doneCondition: (text) => text.includes("3"),
|
|
},
|
|
{
|
|
content: `<p>Now try multiplication: <code>print(4 * 2)</code></p>`,
|
|
doneCondition: (text) => text.includes("8"),
|
|
}
|
|
],
|
|
doneCondition: (() => {
|
|
const progress = {
|
|
varCreated: false,
|
|
varPrinted: false,
|
|
varName: null,
|
|
};
|
|
|
|
return ({ code, consoleText, codeRanGood }) => {
|
|
if (!codeRanGood) {
|
|
return {
|
|
done: false,
|
|
hint: ""
|
|
};
|
|
}
|
|
const assignMatch = code.match(/^\s*([a-zA-Z_][a-zA-Z0-9_]*)\s*=/m);
|
|
if (assignMatch) {
|
|
progress.varName = assignMatch[1];
|
|
progress.varCreated = true;
|
|
} else {
|
|
progress.varCreated = false;
|
|
progress.varName = null;
|
|
progress.varPrinted = false;
|
|
}
|
|
|
|
if (progress.varCreated && progress.varName) {
|
|
const printRegex = new RegExp(`print\\s*\\(\\s*${progress.varName}\\s*\\)`);
|
|
if (printRegex.test(code)) {
|
|
progress.varPrinted = true;
|
|
} else {
|
|
progress.varPrinted = false;
|
|
}
|
|
}
|
|
|
|
console.log('varCreated:', progress.varCreated, 'varPrinted:', progress.varPrinted);
|
|
|
|
let missing = [];
|
|
if (!progress.varCreated) missing.push("create a variable");
|
|
if (!progress.varPrinted) missing.push("print the variable");
|
|
|
|
let hint = "I still need you to ";
|
|
if (missing.length === 0) {
|
|
hint = "";
|
|
} else if (missing.length === 1) {
|
|
hint += missing[0];
|
|
} else {
|
|
hint += missing.slice(0, -1).join(", ") + " and " + missing[missing.length - 1];
|
|
}
|
|
|
|
const done = progress.varCreated && progress.varPrinted;
|
|
return { done, hint };
|
|
};
|
|
})()
|
|
},
|
|
{
|
|
id: 'lesson5',
|
|
title: '5. More on Variables',
|
|
difficulty: 'easy',
|
|
content: `
|
|
<p>A variable will act like any other object of its data type.</p>
|
|
<p>For example, if we have a variable called <code>bob</code> with the value <code>32</code>, we can do math with it:</p>
|
|
<p><code>print(bob + 10)</code> will print <code>42</code>.</p>
|
|
</br>
|
|
<p>We can also change the value of a variable:</p>
|
|
<pre><code>
|
|
bob = 32
|
|
print(bob)
|
|
bob = bob + 10
|
|
print(bob)
|
|
</code></pre>
|
|
<p>In this example, the first time you print bob it will give his initial value of 32, but the second time we have added 10 to bob.</p>
|
|
<p>Give it a try.</p>
|
|
`,
|
|
steps: [
|
|
{
|
|
content: `<p>First, try printing an addition: <code>print(2 + 3)</code></p>`,
|
|
doneCondition: (text) => text.includes("5"),
|
|
},
|
|
{
|
|
content: `<p>Nice! Now try subtraction: <code>print(5 - 2)</code></p>`,
|
|
doneCondition: (text) => text.includes("3"),
|
|
},
|
|
{
|
|
content: `<p>Now try multiplication: <code>print(4 * 2)</code></p>`,
|
|
doneCondition: (text) => text.includes("8"),
|
|
}
|
|
],
|
|
doneCondition: (() => {
|
|
const progress = {
|
|
varCreated: false,
|
|
varPrinted: false,
|
|
varArithmeticDone: false,
|
|
varPrintedTwice: false,
|
|
};
|
|
|
|
return ({ code, consoleText, codeRanGood }) => {
|
|
if (!codeRanGood) {
|
|
return {
|
|
done: false,
|
|
hint: ""
|
|
};
|
|
}
|
|
const assignRegex = /\b(\w+)\s*=\s*[\d'"]/; // variable assignment
|
|
const printRegex1 = /print\(\s*\1\s*\)/; // first print of the variable
|
|
const mathRegex = /\1\s*=\s*\1\s*[\+\-\*/]\s*[\d'"]/; // arithmetic using itself
|
|
const printRegex2 = /print\(\s*\1\s*\)/; // second print of the variable
|
|
|
|
const match = code.match(assignRegex);
|
|
if (!match) return false;
|
|
|
|
const varName = match[1];
|
|
const dynamicPrint1 = new RegExp(`print\\(\\s*${varName}\\s*\\)`);
|
|
const dynamicMath = new RegExp(`${varName}\\s*=\\s*${varName}\\s*[+\\-*/]\\s*[\\d'"]`);
|
|
const dynamicPrint2 = new RegExp(`print\\(\\s*${varName}\\s*\\)`, 'g');
|
|
|
|
progress.varCreated = match;
|
|
progress.dynamicPrint1 = dynamicPrint1.test(code);
|
|
progress.varArithmeticDone = dynamicMath.test(code);
|
|
progress.varPrintedTwice = (code.match(dynamicPrint2) || []).length >= 2
|
|
|
|
let missing = [];
|
|
if (!progress.varCreated) missing.push("create a variable");
|
|
if (!progress.dynamicPrint1) missing.push("print the variable");
|
|
if (!progress.varArithmeticDone) missing.push("alter the variable");
|
|
if (!progress.varPrintedTwice) missing.push("print the variable a second time");
|
|
|
|
let hint = "I still need you to ";
|
|
if (missing.length === 0) {
|
|
hint = "";
|
|
} else if (missing.length === 1) {
|
|
hint += missing[0];
|
|
} else {
|
|
hint += missing.slice(0, -1).join(", ") + " and " + missing[missing.length - 1];
|
|
}
|
|
|
|
const done = progress.varCreated && progress.dynamicPrint1 && progress.varArithmeticDone && progress.varPrintedTwice;
|
|
return { done, hint };
|
|
};
|
|
})()
|
|
},
|
|
{
|
|
id: 'lesson6',
|
|
title: '6. Conditionals',
|
|
difficulty: 'easy',
|
|
content: `
|
|
<p>Sometimes we want don't want part of our code to run, or we want it to run differently in different situations. That's when we use <strong>conditionals</strong>.</p>
|
|
<pre><code>
|
|
bob = 5
|
|
if bob > 10:
|
|
print("Bob is greater than 10")
|
|
</code></pre>
|
|
<p>In this example, the print function will only run if <code>bob</code> is 11 or higher.</p>
|
|
</br>
|
|
<p>One thing to note here is the formatting. Note that after the <code>if bob > 10:</code> there is a colon (:)<p>
|
|
<p>This tells Python that the next line is part of a new block of code.</p>
|
|
<p>In Python, we use indentation to define blocks of code, so the next line must be indented.</p>
|
|
<p>After the <code>else:</code> we also have a colon, and the next line is indented again.</p>
|
|
<p>Try it for yourself:</p>
|
|
<ol>
|
|
<li>Initialize a variable with a value</li>
|
|
<li>Use an <code>if</code> statement to check if the variable is greater or less than a value</li>
|
|
<li>Print a message based on the condition</li>
|
|
</ol>
|
|
<p>To test if two values are equal, you can use the <code>==</code> operator.</p>
|
|
<p>eg. <pre><code>
|
|
num = 10
|
|
if num == 10:
|
|
print("num is equal to 10")
|
|
</code></pre>
|
|
`,
|
|
|
|
doneCondition: (() => {
|
|
const progress = {
|
|
varCreated: false,
|
|
ifStatement: false,
|
|
printedSomething: false,
|
|
};
|
|
|
|
return ({ code, consoleText, codeRanGood }) => {
|
|
if (!codeRanGood) {
|
|
return {
|
|
done: false,
|
|
hint: ""
|
|
};
|
|
}
|
|
|
|
// Check for variable assignment
|
|
const assignRegex = /\b(\w+)\s*=\s*[\d'"]/;
|
|
progress.varCreated = assignRegex.test(code);
|
|
|
|
// Check for if statement
|
|
progress.ifStatement = /(^|\n)\s*if\s+.*:/.test(code);
|
|
|
|
// Check for print() anywhere
|
|
progress.printedSomething = /print\s*\(.*\)/.test(code);
|
|
|
|
// Build hint
|
|
const missing = [];
|
|
if (!progress.varCreated) missing.push("create a variable");
|
|
if (!progress.ifStatement) missing.push("use an if statement");
|
|
if (!progress.printedSomething) missing.push("print something");
|
|
|
|
let hint = "";
|
|
if (missing.length > 0) {
|
|
hint = "I still need you to " + (missing.length === 1
|
|
? missing[0]
|
|
: missing.slice(0, -1).join(", ") + " and " + missing[missing.length - 1]);
|
|
}
|
|
|
|
const done = progress.varCreated && progress.ifStatement && progress.printedSomething;
|
|
return { done, hint };
|
|
};
|
|
})()
|
|
|
|
},
|
|
{
|
|
id: 'lesson7',
|
|
title: '7. More Conditionals',
|
|
difficulty: 'easy',
|
|
content: `
|
|
<p>Sometimes we'll want to run one thing OR another, rather than just running something or not. To do this we chain our if statements together with <code>elif</code> and <code>else</code>.</p>
|
|
<pre><code>
|
|
bob = 10
|
|
if bob == 10:
|
|
print("bob is equal to 10")
|
|
elif bob < 10:
|
|
print("bob is less than 10")
|
|
else:
|
|
print("bob is greater than 10")
|
|
</code></pre>
|
|
<p>In this example, only one of the print statements can ever run, <code>elif</code> is short for "else if", and is a second if statement which is only tested it the <code>if</code> above it resolves to False.</p>
|
|
<p>The <code>else</code> statement is run if all previous conditions are False.</p>
|
|
<p>You can chain as many <code>elif</code> statements as you like, but only one <code>else</code> at the end.</p>
|
|
<p>Try it for yourself:</p>
|
|
`,
|
|
|
|
doneCondition: (() => {
|
|
const progress = {
|
|
varCreated: false,
|
|
ifStatement: false,
|
|
elifStatement: false,
|
|
elseStatement: false,
|
|
threeDistinctPrints: false,
|
|
};
|
|
|
|
return ({ code, consoleText, codeRanGood }) => {
|
|
if (!codeRanGood) {
|
|
return { done: false, hint: "" };
|
|
}
|
|
|
|
// 1. Variable assignment
|
|
const assignRegex = /\b(\w+)\s*=\s*[\d'"]/;
|
|
progress.varCreated = assignRegex.test(code);
|
|
|
|
// 2. Line-by-line block tracking
|
|
const lines = code.split('\n');
|
|
let currentBlock = null;
|
|
const printContents = {
|
|
if: null,
|
|
elif: null,
|
|
else: null,
|
|
};
|
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const line = lines[i].trim();
|
|
|
|
if (/^if\s+.*:/.test(line)) {
|
|
progress.ifStatement = true;
|
|
currentBlock = "if";
|
|
continue;
|
|
}
|
|
|
|
if (/^elif\s+.*:/.test(line)) {
|
|
progress.elifStatement = true;
|
|
currentBlock = "elif";
|
|
continue;
|
|
}
|
|
|
|
if (/^else\s*:/.test(line)) {
|
|
progress.elseStatement = true;
|
|
currentBlock = "else";
|
|
continue;
|
|
}
|
|
|
|
const printMatch = line.match(/print\s*\((.*?)\)/);
|
|
if (printMatch && currentBlock && !printContents[currentBlock]) {
|
|
const cleaned = printMatch[1].replace(/\s+/g, '').toLowerCase();
|
|
printContents[currentBlock] = cleaned;
|
|
}
|
|
}
|
|
|
|
const printedValues = Object.values(printContents).filter(Boolean);
|
|
const uniqueValues = new Set(printedValues);
|
|
progress.threeDistinctPrints = uniqueValues.size === 3;
|
|
|
|
// Hint generation
|
|
const missing = [];
|
|
if (!progress.varCreated) missing.push("create a variable");
|
|
if (!progress.ifStatement) missing.push("use an if statement");
|
|
if (!progress.elifStatement) missing.push("use an elif statement");
|
|
if (!progress.elseStatement) missing.push("use an else statement");
|
|
if (!progress.threeDistinctPrints) missing.push("print different things in the if, elif, and else blocks");
|
|
|
|
const hint = missing.length > 0
|
|
? "I still need you to " + (missing.length === 1
|
|
? missing[0]
|
|
: missing.slice(0, -1).join(", ") + " and " + missing[missing.length - 1])
|
|
: "";
|
|
|
|
const done = progress.varCreated && progress.ifStatement && progress.elifStatement &&
|
|
progress.elseStatement && progress.threeDistinctPrints;
|
|
|
|
return { done, hint };
|
|
};
|
|
})()
|
|
},
|
|
{
|
|
id: 'lesson8',
|
|
title: '8. Loops',
|
|
difficulty: 'easy',
|
|
content: `
|
|
<p>Most of the time we'll want our code to run multiple times, or even endlessly, in that case we need <strong>loops</strong>.</p>
|
|
<p>Python has two main types of loops: <code>for</code> loops and <code>while</code> loops.</p>
|
|
<p>The <code>while</code> loop will continue to run as long as a condition is True.</p>
|
|
<pre><code>
|
|
count = 0
|
|
while count < 5:
|
|
print("Count is:", count)
|
|
count = count + 1
|
|
print("Done!")
|
|
</code></pre>
|
|
<p>You have to be careful with <code>while</code> loops, if the condition never becomes False, the loop will run forever!</p>
|
|
|
|
`,
|
|
|
|
doneCondition: (() => {
|
|
const progress = {
|
|
varCreated: false,
|
|
ifStatement: false,
|
|
elifStatement: false,
|
|
elseStatement: false,
|
|
threeDistinctPrints: false,
|
|
};
|
|
|
|
return ({ code, consoleText, codeRanGood }) => {
|
|
if (!codeRanGood) {
|
|
return { done: false, hint: "" };
|
|
}
|
|
|
|
// 1. Variable assignment
|
|
const assignRegex = /\b(\w+)\s*=\s*[\d'"]/;
|
|
progress.varCreated = assignRegex.test(code);
|
|
|
|
// 2. Line-by-line block tracking
|
|
const lines = code.split('\n');
|
|
let currentBlock = null;
|
|
const printContents = {
|
|
if: null,
|
|
elif: null,
|
|
else: null,
|
|
};
|
|
|
|
for (let i = 0; i < lines.length; i++) {
|
|
const line = lines[i].trim();
|
|
|
|
if (/^if\s+.*:/.test(line)) {
|
|
progress.ifStatement = true;
|
|
currentBlock = "if";
|
|
continue;
|
|
}
|
|
|
|
if (/^elif\s+.*:/.test(line)) {
|
|
progress.elifStatement = true;
|
|
currentBlock = "elif";
|
|
continue;
|
|
}
|
|
|
|
if (/^else\s*:/.test(line)) {
|
|
progress.elseStatement = true;
|
|
currentBlock = "else";
|
|
continue;
|
|
}
|
|
|
|
const printMatch = line.match(/print\s*\((.*?)\)/);
|
|
if (printMatch && currentBlock && !printContents[currentBlock]) {
|
|
const cleaned = printMatch[1].replace(/\s+/g, '').toLowerCase();
|
|
printContents[currentBlock] = cleaned;
|
|
}
|
|
}
|
|
|
|
const printedValues = Object.values(printContents).filter(Boolean);
|
|
const uniqueValues = new Set(printedValues);
|
|
progress.threeDistinctPrints = uniqueValues.size === 3;
|
|
|
|
// Hint generation
|
|
const missing = [];
|
|
if (!progress.varCreated) missing.push("create a variable");
|
|
if (!progress.ifStatement) missing.push("use an if statement");
|
|
if (!progress.elifStatement) missing.push("use an elif statement");
|
|
if (!progress.elseStatement) missing.push("use an else statement");
|
|
if (!progress.threeDistinctPrints) missing.push("print different things in the if, elif, and else blocks");
|
|
|
|
const hint = missing.length > 0
|
|
? "I still need you to " + (missing.length === 1
|
|
? missing[0]
|
|
: missing.slice(0, -1).join(", ") + " and " + missing[missing.length - 1])
|
|
: "";
|
|
|
|
const done = progress.varCreated && progress.ifStatement && progress.elifStatement &&
|
|
progress.elseStatement && progress.threeDistinctPrints;
|
|
|
|
return { done, hint };
|
|
};
|
|
})()
|
|
},
|
|
];
|