removed some constraints on quiz (uniqueness of puzzles, number of bits etc), added animation when the answer is wrong

This commit is contained in:
2022-11-11 10:28:52 +01:00
parent 82a9184b46
commit 2dc2f93e64
10 changed files with 2291 additions and 154 deletions

71
TODO.md
View File

@@ -1,35 +1,56 @@
- add progression
- add explanation/tutorial
- timed
- add column labels
- add spacing beyond X places
- add color and font
- optional super mario tyype sounds
- gamification via character upgrades
# Content
- explanation/tutorial
- 'about'/contact
## Curriculum
- how to negate a signed binary integer?
- how to convert decimal to binary?
- how to convert hex to binary?
- how to convert binary to hex?
- how many unsigned integers can be represented in ____? (a byte, nibble, word)
- how many _signed_ integers can be represented in ____? (a byte, nibble, word)
- how to determine if a binary int is even/odd?
- what can be said of a binary integer who ____ bits are 0?
- if all positions in a binary integer are 0 except position ____, what number does it represent?
- if all positions in a binary integer are 1 except position ____, what number does it represent?
- memorize powers of 2 up to 16 (speed round)
- what happens to the represented integer after shifting to the left?
- what happens to the represented integer after shifting to the right?
- what is the binary for the value 2^n - 1?
- what is sign extension? how do you do it?
- what is contraction?
- what is zero extension?
# Design
- color and font
- toggleable column labels
- animations
- [x] 'nuh-uh' shake for wrong answers
- [ ] nice reward animations for right answers
- [ ] make the placeholders become real text for a split second before starting
- processor graphic/animation
- diff processors for different bits
- mobile
## mobile
- make autofocus work on quiz
- remove "[enter]" from start button
- on-screen keyboard, like wordle
- make it all on one line
- make the font smaller or the div wider
- put a 'submit' button in
- put a 'submit' button in (or auto-submit?)
- add a range slider for easier input?
- make text inputs big enough for MAX_DIGITS_PROBLEMS and MAX_DIGITS_BITS
- show a message on submit
# Features
- keyboard shortcuts
- ctrl-c to go back to menu
- specify what you already know, then prove it (modular learning)
- save progress
- local storage
- login
- timed
- keyboard shortcuts with help overlay
- leaderboard
- show warning when specifying invalid number of bits or problems
- add contact info
- add 'about'
- add mailing list
- prompt at the end of a quiz to sign up for it
- uncomment PDF part
- add PDF export in pure front end
- add animations
- 'nuh-uh' shake for wrong answers
- nice reward animations for right answers
- make the placeholders become real text for a split second before starting
- super mario land [pixellation animation](http://blog.swishscripts.com/2019/06/19/snes-mosaic-effect/) ("mosaic")
- add user accounts using Userbase
- required for higher numbers
- required for PDF output?
- required after X uses?
- cheat/human detection

2226
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -14,12 +14,15 @@
"@rollup/plugin-node-resolve": "^8.0.0",
"rollup": "^2.3.4",
"rollup-plugin-livereload": "^1.0.0",
"rollup-plugin-svelte": "^5.0.3",
"rollup-plugin-svelte": "^6.1.1",
"rollup-plugin-terser": "^5.1.2",
"svelte": "^3.0.0"
"svelte": "^3.53.0"
},
"dependencies": {
"axios": "^0.19.2",
"sirv-cli": "^0.4.4"
"coc-svelte": "^0.4.2",
"g": "^2.0.1",
"sirv-cli": "^0.4.4",
"tree-sitter-svelte": "^0.10.1"
}
}

View File

@@ -101,3 +101,27 @@ input[type=number] {
.problem input[type=number] {
border: none;
}
/* borrowed from https://stackoverflow.com/a/15991184/4386191 */
.nuhuh {
animation: shake .5s linear;
}
@keyframes shake {
8%, 41% {
transform: translateX(-10px);
}
25%, 58% {
transform: translateX(10px);
}
75% {
transform: translateX(-5px);
}
92% {
transform: translateX(5px);
}
0%, 100% {
transform: translateX(0);
}
}

View File

@@ -1,7 +1,7 @@
<script>
import Inputs from "./components/Inputs.svelte";
import Quiz from "./components/Quiz.svelte";
import {activeQuiz} from './stores'
import { activeQuiz } from './stores'
</script>
<main style="margin: 2em;">
<h1>Binary Quiz!</h1>

View File

@@ -1,11 +1,11 @@
<script>
import download from '../download'
import download from '../download.js'
import {
MIN_BITS, MAX_BITS, MIN_PROBLEMS, MAX_PROBLEMS, DEFAULT_BITS, DEFAULT_NUM_PROBLEMS,
MAX_DIGITS_BITS, MAX_DIGITS_PROBLEMS
} from '../config'
import { bits, num_problems, valid, activeQuiz, problems } from '../stores'
import {generateProblems} from "../problems";
} from '../config.js'
import { bits, num_problems, valid, activeQuiz, problems } from '../stores.js'
import { generateProblems } from "../problems.js";
const downloadAndClear = () => {
download($bits || DEFAULT_BITS, $num_problems || DEFAULT_NUM_PROBLEMS)
@@ -54,7 +54,7 @@
</label>
</div>
<input class="primary-button" disabled={!$valid} type=submit value="Start Quiz [enter]" >
<!-- <input class="button" disabled={!$valid} type=button on:click={downloadAndClear} value="Download PDFs">-->
<!-- <input class="button" disabled={!$valid} type=button on:click={downloadAndClear} value="Download PDFs">-->
</form>
<style>

View File

@@ -1,30 +1,47 @@
<script>
import {checkAnswer} from "../problems";
import {problems, bits, activeProblemIndex, tally, num_problems, activeQuiz} from '../stores'
import { problems, bits, activeProblemIndex, tally, num_problems, activeQuiz } from '../stores';
import Tally from "./Tally.svelte";
$: problem = $problems[$activeProblemIndex]
let displaySummary = false
let solution
$: problem = $problems[$activeProblemIndex];
let displaySummary = false;
let solution;
let userInput;
let form;
let visible = true;
const check = () => {
function shakeAndClear() {
userInput.style.color = 'red';
userInput.classList.add('nuhuh');
setTimeout(function() {
userInput.classList.remove('nuhuh');
userInput.value = '';
userInput.style.color = '#333';
}, 550)
}
function check() {
if (!checkAnswer(solution, problem)) {
class_ = "incorrect"
shakeAndClear();
} else {
tally.update(() => $tally + 1)
tally.update(() => $tally + 1);
if ($num_problems === $activeProblemIndex + 1) {
displaySummary = true
displaySummary = true;
} else {
activeProblemIndex.update(() => $activeProblemIndex + 1)
visible = false;
setTimeout(function() {
activeProblemIndex.update(() => $activeProblemIndex + 1);
solution = null;
visible = true;
}, 550)
}
solution = null
}
}
const reset = () => {
activeQuiz.update(() => false)
activeProblemIndex.update(() => 0)
tally.update(() => 0)
activeQuiz.update(() => false)
bits.update(() => null)
num_problems.update(() => null)
displaySummary = false
@@ -43,12 +60,13 @@
</div>
{:else}
<Tally />
<form on:submit|preventDefault={check} class="problem">
<label>
{problem} =
<input autofocus type=number bind:value={solution}>
</label>
<input type=submit style="visibility: hidden">
</form>
{#if visible}
<form bind:this={form} on:submit|preventDefault={check} class="problem">
<label>
{problem} =
<input bind:this={userInput} autofocus type=number bind:value={solution} id="problem">
</label>
<input type=submit style="visibility: hidden">
</form>
{/if}
{/if}

View File

@@ -1,13 +1,9 @@
export const MIN_BITS = 3
export const MIN_BITS = 2
export const MAX_BITS = 16
export const MIN_PROBLEMS = 2
export const MIN_PROBLEMS = 1
export const MAX_PROBLEMS = 99
export const MAX_DIGITS_PROBLEMS = MAX_PROBLEMS.toString().length
export const MAX_DIGITS_BITS = MAX_BITS.toString().length
export const DEFAULT_BITS = 4
export const DEFAULT_NUM_PROBLEMS = 10
export const getMaxPermutations = (bits) => {
return Math.pow(2, bits)
}

View File

@@ -1,23 +1,20 @@
import {DEFAULT_BITS, DEFAULT_NUM_PROBLEMS} from "./config";
export const generateProblems = (bits, num_problems) => {
let probs = []
let counter = 0
while (counter < (num_problems || DEFAULT_NUM_PROBLEMS)) {
let problem = generateProblem(bits || DEFAULT_BITS)
if (!probs.includes(problem)) {
probs.push(problem)
counter++
}
}
return probs
export function generateProblems(bits, num_problems) {
return new Array(num_problems)
.fill(null)
.map(function() {
return generateProblem(bits)
})
}
export const generateProblem = bits => Array.from(Array(bits)).map((_, i) => Math.random() >= .5 ? '1' : '0').join('')
export function generateProblem(bits) {
return Array.from(Array(bits)).map((_, i) => Math.random() >= .5 ? '1' : '0').join('');
}
export const solveProblem = problem => parseInt(problem, 2)
export const checkAnswer = (answer, problem) => solveProblem(problem) === answer
export function solveProblem(problem) {
return parseInt(problem, 2);
}
export function checkAnswer(answer, problem) {
return solveProblem(problem) === answer
}

View File

@@ -1,6 +1,5 @@
import {derived, writable} from 'svelte/store'
import {generateProblem} from "./problems"
import {getMaxPermutations, MIN_BITS, MAX_BITS, MIN_PROBLEMS, MAX_PROBLEMS, DEFAULT_BITS, DEFAULT_NUM_PROBLEMS} from "./config"
import { derived, writable } from 'svelte/store'
import { MIN_BITS, MAX_BITS, MIN_PROBLEMS, MAX_PROBLEMS, DEFAULT_BITS, DEFAULT_NUM_PROBLEMS } from "./config"
export const bits = writable()
export const num_problems = writable()
@@ -14,6 +13,5 @@ export const valid = derived([bits, num_problems], ([$bits, $num_problems]) => (
&& ($bits || DEFAULT_BITS) >= MIN_BITS
&& ($num_problems || DEFAULT_NUM_PROBLEMS) >= MIN_PROBLEMS
&& ($num_problems || DEFAULT_NUM_PROBLEMS) <= MAX_PROBLEMS
&& ($num_problems || DEFAULT_NUM_PROBLEMS) <= getMaxPermutations($bits || DEFAULT_BITS)
))