Files
gcf-practice/src/gcf.tsx
2024-03-02 16:46:08 -05:00

155 lines
5.0 KiB
TypeScript

import { useState } from 'preact/hooks'
import './gcf.css'
import { gcf } from './lib'
import type { JSX } from 'preact'
type SolutionRow = [Number, Number, Number, Number]
type SolutionKey = Array<SolutionRow>
type SolutionParams = {
rows : SolutionKey
}
function Solution({rows}:SolutionParams) {
const colors = [
"red",
"blue",
"green",
"brown",
"purple",
"orange"
]
const displayRow = (row : SolutionRow, index : number) => {
let range : number[] = [0, 1, 2]
const styles = range.map((i)=>{return `padding:3px;color:white;background:${colors[(i+index)%colors.length]}`})
return <p>
<span style={styles[0]}>{row[0]}</span>&nbsp;&divide;&nbsp;<span style={styles[1]}>{row[2]}</span>&nbsp;=&nbsp;{row[1]}&nbsp;R&nbsp;<span style={styles[2]}>{row[3]}</span>
</p>
}
return <div className="solution text-center" data-bs-theme="light">
{rows.map(displayRow)}
</div>
}
export function GCF() {
// choose number between 2 and max inclusive
const getRandomInt = (max : number) : number => Math.floor(Math.random() * (max - 1) + 2)
const chooseFactorsRandom = () : [number, number] => [ getRandomInt(200), getRandomInt(200) ]
const _chooseMultiplesOf = (f : number) : [number, number] => {
const max = Math.floor(200 / f)
return [ f * getRandomInt(max), f * getRandomInt(max) ]
}
const chooseMultiplesOf = (f : number) : () => [ number, number ] => {
return () => _chooseMultiplesOf(f)
}
const chooseMultiples = () : [number, number] => {
const f = getRandomInt(50)
return _chooseMultiplesOf(f)
}
const chooseFactors = () : [number, number] => {
const probabilities : Array<[number, () => [number, number]]>= [
[ 0.2, chooseFactorsRandom ],
[ 0.2, chooseMultiples ],
[ 0.2, chooseMultiplesOf(3) ],
[ 0.2, chooseMultiplesOf(7) ],
[ 0.1, chooseMultiplesOf(11) ],
[ 0.1, chooseMultiplesOf(17) ],
]
const r = Math.random()
var i = 0, accum = 0
for (i = 0; i < probabilities.length; i++) {
console.log([r, i, accum, probabilities[i]])
if ((r > accum) && (r <= accum + probabilities[i][0])) break
accum += probabilities[i][0]
}
if (i >= probabilities.length) i = probabilities.length - 1
return probabilities[i][1]()
}
const getSolution = (a : number, b : number) : SolutionKey => {
var result : SolutionKey = [ ]
if (b > a) {
var t = b
b = a
a = t
}
while (b != 0) {
result.push([ a, Math.floor(a / b), b, a % b ])
var t = a % b
a = b
b = t
}
return result
}
const doCheck = (e : Event) : void => {
e.preventDefault()
const rightAnswer = gcf(factors[0], factors[1])
const yourAnswer = (document.getElementById("inlineFormInputResponse") as HTMLInputElement).value
if (rightAnswer == Number(yourAnswer)) {
setCorrect(true)
setFeedback(true)
} else {
setFeedback(true)
}
}
const doNext = (e : Event) : void => {
e.preventDefault()
setFeedback(false)
setCorrect(false)
setFactors(chooseFactors())
setSolution(null)
document.forms[0].reset()
}
const doSolution = (e : Event) : void => {
e.preventDefault()
if (solution === null) {
var answerBox = document.getElementById("inlineFormInputResponse") as HTMLInputElement
answerBox.value = String(gcf(factors[0], factors[1]))
setSolution(<Solution rows={getSolution(factors[0], factors[1])} />)
setCorrect(true)
//setFeedback(true)
} else {
setSolution(null)
}
}
const [factors, setFactors] = useState(chooseFactors())
const [correct, setCorrect] = useState(false)
const [feedback, setFeedback] = useState(false)
const [solution, setSolution] = useState<JSX.Element|null>(null)
return (
<div className="px-4 py-5 my-5 text-center">
<h1 className="my-5">Greatest Common Factor Practice</h1>
<p className="fs-3">What is the greatest common factor of <mark>{factors[0]}</mark> and <mark>{factors[1]}</mark>?</p>
<form className="row row-cols-lg-auto g-3 align-items-center justify-content-center">
<div className="col-12">
<label className="visually-hidden" for="inlineFormInputResponse">Response</label>
<div className="input-group">
<input type="text" size={3} autocomplete="off" className="form-control text-center" id="inlineFormInputResponse" />
</div>
</div>
<div className="col-12">
<button type="submit" onClick={correct ? doNext : doCheck} className="btn btn-primary">{correct ? "Next" : "Check"}</button>
<button type="submit" onClick={doSolution} className="ms-2 btn btn-secondary">{solution !== null ? "Hide" : "Solve"}</button>
</div>
</form>
<div className={feedback ? "visible" : "invisible"}>
<div className={correct ? "alert alert-success m-4" : "alert alert-danger m-4"}>{correct ? "Correct" : "Try again!"}</div>
</div>
{solution !== null ? solution : <></>}
</div>
)
}