removed some constraints on quiz (uniqueness of puzzles, number of bits etc), added animation when the answer is wrong
This commit is contained in:
71
TODO.md
71
TODO.md
@@ -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
2226
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -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"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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}
|
||||
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user