first fonctionnal game
This commit is contained in:
commit
171657d616
7 files changed
+539
No files matched your search
@@ -0,0 +1,117 @@
|
||||
function basicFrenchQuestion(argumentValue){
|
||||
return `Quel est la valeur retournée par la fonction si la valeur de l'argument x est ${argumentValue}?`
|
||||
}
|
||||
|
||||
function basicQuestionValidator(expected){
|
||||
return (answer) => answer.toString() === expected.toString();
|
||||
}
|
||||
|
||||
export function question001(x){
|
||||
if(x === 0){
|
||||
x = x + 1;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
|
||||
question001.questions = [0, 16].map( x => {
|
||||
return {
|
||||
question_fr: basicFrenchQuestion(x),
|
||||
validate: basicQuestionValidator(question001(x)),
|
||||
answer: question001(x),
|
||||
};
|
||||
});
|
||||
|
||||
export function question002(x){
|
||||
if(x === 0){
|
||||
x = x + 1;
|
||||
} else {
|
||||
x = x - 1
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
question002.questions = [0, 6].map( x => {
|
||||
return {
|
||||
question_fr: basicFrenchQuestion(x),
|
||||
validate: basicQuestionValidator(question002(x)),
|
||||
answer: question002(x),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
export function question003(x){
|
||||
if(x === 0){
|
||||
x = x + 1;
|
||||
} else {
|
||||
x = x - 1
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
question003.questions = [0, 17].map( x => {
|
||||
return {
|
||||
question_fr: basicFrenchQuestion(x),
|
||||
validate: basicQuestionValidator(question003(x)),
|
||||
answer: question003(x),
|
||||
};
|
||||
});
|
||||
|
||||
export function question004(x){
|
||||
if(x === 0){
|
||||
x = 3;
|
||||
} else if (x === 3){
|
||||
x = x + 1;
|
||||
}else {
|
||||
x = x - 1
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
question004.questions = [0, 3, 8].map( x => {
|
||||
return {
|
||||
question_fr: basicFrenchQuestion(x),
|
||||
validate: basicQuestionValidator(question004(x)),
|
||||
answer: question004(x),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
export function question005(x){
|
||||
for(let i = 0; i < 10; i ++ ){
|
||||
if (i == x){
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
question005.questions = [0, 3, 8, -2, 10].map( x => {
|
||||
return {
|
||||
question_fr: basicFrenchQuestion(x),
|
||||
validate: basicQuestionValidator(question005(x)),
|
||||
answer: question005(x),
|
||||
};
|
||||
});
|
||||
|
||||
|
||||
export function question006(x){
|
||||
if(x < 10){
|
||||
return 54;
|
||||
}
|
||||
if(x < 20){
|
||||
return 23;
|
||||
}
|
||||
if(x < 30){
|
||||
return 232;
|
||||
}
|
||||
return 15;
|
||||
}
|
||||
|
||||
question006.questions = [-1, 3, 10, 20, 30, 26, 31, 12].map( x => {
|
||||
return {
|
||||
question_fr: basicFrenchQuestion(x),
|
||||
validate: basicQuestionValidator(question006(x)),
|
||||
answer: question006(x),
|
||||
};
|
||||
});
|
||||
+162
@@ -0,0 +1,162 @@
|
||||
import { Application, Router } from "https://deno.land/x/oak/mod.ts";
|
||||
import staticFiles from "https://deno.land/x/static_files@1.1.6/mod.ts";
|
||||
import * as rawQuestions from "./questions.js";
|
||||
import { renderToString } from "https://esm.sh/preact-render-to-string@5.1.19?deps=preact@10.5.15";
|
||||
import * as prand from 'https://unpkg.com/pure-rand/lib/esm/pure-rand.js';
|
||||
import {QuestionSource, QuestionMeta} from './shared.ts';
|
||||
|
||||
|
||||
type Question = [QuestionSource, QuestionMeta];
|
||||
|
||||
|
||||
const questions: Question = Object.values(rawQuestions).flatMap(s => s.questions.map( q => [s, q]));
|
||||
|
||||
const router = new Router();
|
||||
const app = new Application();
|
||||
|
||||
function hashString(str: string): number {
|
||||
let hash = 0
|
||||
for (let i = 0; i < str.length; i++) {
|
||||
const chr = str.charCodeAt(i);
|
||||
hash = ((hash << 5) - hash) + chr;
|
||||
hash |= 0; // Convert to 32bit integer
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
|
||||
|
||||
function randomPermutation(seed: number, n: number): number[] {
|
||||
const rand = prand.mersenne(seed)
|
||||
const base = [...Array(n).keys()];
|
||||
for(let i = 0; i < (n -1); i++){
|
||||
const j = prand.unsafeUniformIntDistribution(i, n-1, rand);
|
||||
const tmp = base[i];
|
||||
base[i] = base[j];
|
||||
base[j] = tmp;
|
||||
}
|
||||
return base;
|
||||
}
|
||||
|
||||
|
||||
function getRandomQuestion<T>(seed: string, index: number, questions: Question[]): Question {
|
||||
const numberSeed = hashString(seed);
|
||||
const permutations = randomPermutation(numberSeed, questions.length)
|
||||
return questions[permutations[index]];
|
||||
|
||||
}
|
||||
|
||||
function getQuestionPage(question: Question, nextQuestionUrl: string, validationUrl: string, answerQuestionUrl: string){
|
||||
return (<div>
|
||||
<pre> { question[0].toString() }</pre>
|
||||
<div class="content mt-2 mb-2">
|
||||
{ question[1].question_fr }
|
||||
</div>
|
||||
|
||||
<div id="message">
|
||||
</div>
|
||||
|
||||
<form>
|
||||
<div class="field is-grouped">
|
||||
<div class="control is-expanded">
|
||||
<input class="input" type="text" placeholder="Answer" name="answer"></input>
|
||||
</div>
|
||||
<div class="control">
|
||||
<button class="button is-info"
|
||||
hx-trigger="click"
|
||||
hx-target="#message"
|
||||
hx-post={validationUrl} >
|
||||
Verify Answer
|
||||
</button>
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-info"
|
||||
href="#"
|
||||
hx-trigger="next-question from:body delay:1500ms, click"
|
||||
hx-get={nextQuestionUrl}
|
||||
hx-params="none"
|
||||
hx-target="#question-body" >
|
||||
Next Question
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="control">
|
||||
<a class="button is-info"
|
||||
href="#"
|
||||
hx-trigger="click"
|
||||
hx-get={answerQuestionUrl}
|
||||
hx-params="none"
|
||||
hx-target="#message" >
|
||||
Show Answer
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</div>);
|
||||
}
|
||||
|
||||
function getValidAnswerMessage(){
|
||||
return (<div class="notification is-primary">
|
||||
Fantastic! Good answer!
|
||||
</div>)
|
||||
}
|
||||
|
||||
function getInvalidAnswerMessage(){
|
||||
return (<div class="notification is-danger">
|
||||
Unfortunatly, this is not the right answer.
|
||||
</div>)
|
||||
}
|
||||
|
||||
function isValidAnswer(question: Question, answer: string): boolean {
|
||||
return question[1].validate(answer);
|
||||
}
|
||||
|
||||
function processInvalidAnswer(ctx, answer: string): void {
|
||||
ctx.response.body = renderToString(getInvalidAnswerMessage());
|
||||
}
|
||||
|
||||
function processValidAnswer(ctx, answer: string): void {
|
||||
ctx.response.headers.append("HX-Trigger", "next-question");
|
||||
ctx.response.body = renderToString(getValidAnswerMessage());
|
||||
}
|
||||
|
||||
|
||||
router.all("/api/v1/random-questions/:seed/:idx/validate", async (ctx)=> {
|
||||
const seed = ctx.params.seed;
|
||||
const index = Number.parseInt(ctx.params.idx??"0");
|
||||
const answer = (await ctx.request.body().value).get("answer");
|
||||
const nextQuestionUrl = `/api/v1/random-questions/${seed}/${index + 1}`;
|
||||
const q = getRandomQuestion(seed, index, questions);
|
||||
if(isValidAnswer(q, answer)){
|
||||
processValidAnswer(ctx, nextQuestionUrl)
|
||||
}else{
|
||||
processInvalidAnswer(ctx, nextQuestionUrl)
|
||||
}
|
||||
})
|
||||
|
||||
router.get("/api/v1/random-questions/:seed/:idx/answer", (ctx)=> {
|
||||
const seed = ctx.params.seed;
|
||||
const index = Number.parseInt(ctx.params.idx??"0");
|
||||
const q = getRandomQuestion(seed, index, questions);
|
||||
ctx.response.body = renderToString(
|
||||
<div class="notification is-warning">
|
||||
The answer is {q[1].answer}
|
||||
</div>);
|
||||
})
|
||||
|
||||
router.get("/api/v1/random-questions/:seed/:idx", (ctx)=> {
|
||||
const seed = ctx.params.seed;
|
||||
const index = Number.parseInt(ctx.params.idx??"0");
|
||||
const q = getRandomQuestion(seed, index, questions);
|
||||
const nextQuestionUrl = `/api/v1/random-questions/${seed}/${index + 1}`;
|
||||
const validationUrl = `/api/v1/random-questions/${seed}/${index}/validate`;
|
||||
const answerQuestionUrl = `/api/v1/random-questions/${seed}/${index}/answer`
|
||||
|
||||
ctx.response.body = renderToString(getQuestionPage(q, nextQuestionUrl, validationUrl, answerQuestionUrl));
|
||||
});
|
||||
|
||||
|
||||
app.use(router.routes());
|
||||
app.use(staticFiles("public", {brotli: true, fetch: true}));
|
||||
|
||||
|
||||
await app.listen({ port: 8000 });
|
||||
@@ -0,0 +1,9 @@
|
||||
interface QuestionSource {
|
||||
toString(): string;
|
||||
}
|
||||
|
||||
export type QuestionMeta = {
|
||||
question_fr: string,
|
||||
validate: (answer: string) => boolean,
|
||||
answer: string | number
|
||||
}
|
||||
Reference in new issue
Block a user