added ai!

This commit is contained in:
RezHackXYZ 2025-05-25 12:28:14 +05:30
parent ad959266a5
commit d1c8ae269b
No known key found for this signature in database
15 changed files with 311 additions and 146 deletions

View file

@ -4,7 +4,7 @@
"version": "0.0.1", "version": "0.0.1",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite dev --host", "dev": "vite dev",
"build": "vite build", "build": "vite build",
"preview": "vite preview", "preview": "vite preview",
"prepare": "svelte-kit sync || echo ''", "prepare": "svelte-kit sync || echo ''",

133
src/lib/config.js Normal file
View file

@ -0,0 +1,133 @@
export let AnswersSymbolAndColorScheme = [
{
color: "#6E0000",
selectedColor: "#AA2222",
hoverBorderColor: "#FF5D5D",
selectedBorderColor: "#FF0000",
symbol: "nf-md-triangle",
},
{
color: "#00316E",
selectedColor: "#2255AA",
hoverBorderColor: "#5D9CFF",
selectedBorderColor: "#0000FF",
symbol: "nf-fa-square",
},
{
color: "#6E6E00",
selectedColor: "#AAAA22",
hoverBorderColor: "#FFFF5D",
selectedBorderColor: "#DDFF00",
symbol: "nf-fa-circle",
},
{
color: "#006E00",
selectedColor: "#22AA22",
hoverBorderColor: "#5DFF5D",
selectedBorderColor: "#00FF00",
symbol: "nf-fa-diamond",
},
{
color: "#4B0082",
selectedColor: "#7F33B5",
hoverBorderColor: "#B066FF",
selectedBorderColor: "#9932CC",
symbol: "nf-md-star",
},
{
color: "#FF8C00",
selectedColor: "#FFB347",
hoverBorderColor: "#FFD580",
selectedBorderColor: "#FFA500",
symbol: "nf-md-hexagon",
},
{
color: "#008B8B",
selectedColor: "#33CCCC",
hoverBorderColor: "#66FFFF",
selectedBorderColor: "#00CED1",
symbol: "nf-md-octagon",
},
{
color: "#8B4513",
selectedColor: "#CD853F",
hoverBorderColor: "#DEB887",
selectedBorderColor: "#A0522D",
symbol: "nf-md-heart",
},
];
export let DefaultQuestions = [
{
name: "What should you do when you're free?",
answers: ["Do something in real life!", "Play video games", "Code!", "Touch grass!"],
correctAnswer: 2,
},
{
name: "Is RezHackXYZ the best programmer in the world?",
answers: ["Yes :)", "No :("],
correctAnswer: 0,
},
{
name: "Best place in the world?",
answers: [
"Google",
"Microsoft",
"Apple",
"Samsung",
"Hack Club!! :D",
"Amazon",
"Facebook",
"Twitter",
],
correctAnswer: 4,
},
];
export let AiPrompts = {
GenerateQuestionsUsingAI: `
You are the AI of a quiz game.
Generate a list of quiz questions with possible answers and the correct answer index.
Each question must have:
- A "name" (question text)
- An "answers" array (minimum 2, maximum 8 options)
- A "correctAnswer" (index starting from 0)
Ensure the questions are diverse.
Example format:
{
"name": "What is the capital of France?",
"answers": [
"Paris",
"London",
"Berlin",
"Madrid"
],
"correctAnswer": 0
}
JUST PROVIDE THE JSON AND NOTHING ELSE.
The user's topic of interest is:
[topic]`,
GenerateOptionsUsingAI: `
You are the AI of a quiz game.
Generate a list of answers relevant to the Question the correct answer index.
generate 2 things for the question:
- An "answers" array (minimum 2, maximum 8 options)
- A "correctAnswer" (index starting from 0)
Ensure the questions are diverse.
Example format if the question is "What is the capital of France?":
{
"answers": [
"Paris",
"London",
"Berlin",
"Madrid"
],
"correctAnswer": 0
}
JUST PROVIDE THE JSON AND NOTHING ELSE.
The user's Question that they want to generate options for is:
[question]
`
};

View file

@ -1,60 +0,0 @@
{
"AnswersSymbolAndColor": [
{
"Color": "#6E0000",
"SelectedColor": "#AA2222",
"HoverBorderColor": "#FF5D5D",
"SelectedBorderColor": "#FF0000",
"Symbol": "nf-md-triangle"
},
{
"Color": "#00316E",
"SelectedColor": "#2255AA",
"HoverBorderColor": "#5D9CFF",
"SelectedBorderColor": "#0000FF",
"Symbol": "nf-fa-square"
},
{
"Color": "#6E6E00",
"SelectedColor": "#AAAA22",
"HoverBorderColor": "#FFFF5D",
"SelectedBorderColor": "#DDFF00",
"Symbol": "nf-fa-circle"
},
{
"Color": "#006E00",
"SelectedColor": "#22AA22",
"HoverBorderColor": "#5DFF5D",
"SelectedBorderColor": "#00ff00",
"Symbol": "nf-fa-diamond"
},
{
"Color": "#4B0082",
"SelectedColor": "#7F33B5",
"HoverBorderColor": "#B066FF",
"SelectedBorderColor": "#9932CC",
"Symbol": "nf-md-star"
},
{
"Color": "#FF8C00",
"SelectedColor": "#FFB347",
"HoverBorderColor": "#FFD580",
"SelectedBorderColor": "#FFA500",
"Symbol": "nf-md-hexagon"
},
{
"Color": "#008B8B",
"SelectedColor": "#33CCCC",
"HoverBorderColor": "#66FFFF",
"SelectedBorderColor": "#00CED1",
"Symbol": "nf-md-octagon"
},
{
"Color": "#8B4513",
"SelectedColor": "#CD853F",
"HoverBorderColor": "#DEB887",
"SelectedBorderColor": "#A0522D",
"Symbol": "nf-md-heart"
}
]
}

View file

@ -1,22 +1,28 @@
<script> <script>
import UseDemoQuestions from './components/buttons/UseDemoQuestions.svelte'; import UseDemoQuestions from "./components/buttons/UseDemoQuestions.svelte";
import NewQuestion from './components/buttons/NewQuestion.svelte'; import NewQuestion from "./components/buttons/NewQuestion.svelte";
import StartGame from './components/buttons/StartGame.svelte'; import StartGame from "./components/buttons/StartGame.svelte";
import Question from './components/Questions/question.svelte'; import Question from "./components/Questions/question.svelte";
import { questions } from './logic/GameCreateData.svelte.js'; import { questions, Wait } from "./logic/GameCreateData.svelte.js";
import WaitStartGame from "./components/buttons/WaitStartGame.svelte";
$inspect(questions); import GenerateQuetionsUsingAi from "./components/buttons/GenerateQuetionsUsingAI.svelte";
</script> </script>
<div class="bg-grey-900 flex justify-center p-5"> <div class="bg-grey-900 flex justify-center p-5">
<div class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"> <div
<UseDemoQuestions /> class="flex flex-col items-center justify-center gap-1 rounded-lg bg-gray-900 p-8 shadow-lg"
>
<div class="flex gap-3"><UseDemoQuestions /> <GenerateQuetionsUsingAi /></div>
{#each questions.v as question, index} {#each questions.v as question, index}
<Question {index} /> <Question {index} />
{/each} {/each}
<div class="flex gap-3"> <div class="flex gap-3">
<NewQuestion /> <NewQuestion />
<StartGame /> {#if Wait.v == false}
<StartGame />
{:else}
<WaitStartGame />
{/if}
</div> </div>
</div> </div>
</div> </div>

View file

@ -1,5 +1,6 @@
<script> <script>
import DeleteQuestion from "../buttons/DeleteQuestion.svelte"; import DeleteQuestion from "../buttons/DeleteQuestion.svelte";
import GenerateOptionsUsingAI from "../buttons/GenerateOptionsUsingAI.svelte";
import Answers from "./answers.svelte"; import Answers from "./answers.svelte";
import { questions } from "../../logic/GameCreateData.svelte.js"; import { questions } from "../../logic/GameCreateData.svelte.js";
@ -20,16 +21,13 @@
<select <select
bind:value={questions.v[index].answers.length} bind:value={questions.v[index].answers.length}
onchange={(e) => { onchange={(e) => {
const newLength = parseInt(e.target.value); const newLength = questions.v[index].answers.length;
const currentAnswers = questions.v[index].answers; const currentAnswers = questions.v[index].answers;
if (newLength > currentAnswers.length) { if (newLength > currentAnswers.length) {
// Add more answers // Add more answers
while (questions.v[index].answers.length < newLength) { while (questions.v[index].answers.length < newLength) {
questions.v[index].answers = [ questions.v[index].answers.push("");
...questions.v[index].answers,
{ text: "", correct: false },
];
} }
} else if (newLength < currentAnswers.length) { } else if (newLength < currentAnswers.length) {
// Remove excess answers // Remove excess answers
@ -47,9 +45,11 @@
</div> </div>
<div class="flex flex-col gap-2"> <div class="flex flex-col gap-2">
{#each questions.v[index].answers as answer, answersIndex} {#each questions.v[index].answers as _, answersIndex}
<Answers questionsIndex={index} {answersIndex} /> <Answers questionsIndex={index} {answersIndex} />
{/each} {/each}
</div> </div>
<input type="file">
<GenerateOptionsUsingAI {index} />
</div> </div>
</div> </div>

View file

@ -0,0 +1,19 @@
<script>
import { GenerateOptionsUsingAI } from "../../logic/GenerateOptionsUsingAI.js";
import { questions } from "../../logic/GameCreateData.svelte.js";
let props = $props();
let index = props.index;
</script>
{#if questions.v[index].answers.every((answer) => answer === "")}
<button
onclick={() => {
GenerateOptionsUsingAI(index);
}}
class="mt-1 mb-1 flex h-fit cursor-pointer items-center justify-center gap-2 rounded-xl bg-blue-700 p-2 transition-all hover:scale-110 hover:-rotate-5"
>
<i class="nf nf-cod-sparkle"></i>
Generate Options using AI
</button>
{/if}

View file

@ -0,0 +1,14 @@
<script>
import { GenerateQuestionsUsingAI } from "../../logic/GenerateQuestionsUsingAI.js";
import { questions } from "../../logic/GameCreateData.svelte.js";
</script>
{#if questions.v.length === 0 || (questions.v.length === 1 && questions.v[0].name === "" && questions.v[0].answers.every((answer) => answer === "") && questions.v[0].correctAnswer === undefined)}
<button
onclick={GenerateQuestionsUsingAI}
class="-mt-5 mb-3 flex h-fit cursor-pointer items-center justify-center gap-2 rounded-xl bg-blue-700 p-2 transition-all hover:scale-110 hover:-rotate-5"
>
<i class="nf nf-cod-sparkle"></i>
Generate questions using AI
</button>
{/if}

View file

@ -1,10 +1,10 @@
<script> <script>
import { startGame } from '../../logic/StartGame.js'; import { startGame } from "../../logic/StartGame.js";
</script> </script>
<button <button
onclick={startGame} onclick={startGame}
class="flex h-fit cursor-pointer items-center justify-center gap-1 rounded-xl bg-gray-700 p-2" class="flex h-fit cursor-pointer items-center justify-center gap-1 rounded-xl bg-gray-700 p-2 transition-all hover:scale-110"
><svg ><svg
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"
height="24px" height="24px"

View file

@ -1,54 +1,36 @@
import { DefaultQuestions } from "$lib/config.js";
export let Wait = $state({
v: false,
});
export let questions = $state({ export let questions = $state({
v: [ v: [
{ {
name: '', name: "",
answers: ['', '', '', ''], answers: ["", "", "", ""],
correctAnswer: undefined correctAnswer: undefined,
} },
] ],
}); });
let DemoQuestions = [
{
name: 'Is hack club awesome?',
answers: ['its ok', 'its the BEST place in the world', 'its bad', 'Gu Gu Ga Ga'],
correctAnswer: 1
},
{
name: 'Who is the best programer in the world?',
answers: ['Some person', 'Bill Gates', 'RezHackXYZ', 'another person'],
correctAnswer: 2
},
{
name: 'What was the 5/11 incident?',
answers: [
'mass pings of @/birds',
'twin towers getting blasted by planes',
'the opening ceremony of the store of 7/11 on the 5/11 date',
'the opening ceremony of the competitor store of 7/11'
],
correctAnswer: 0
}
];
export function SetQuestionsToDemoQuestions() { export function SetQuestionsToDemoQuestions() {
questions.v = DemoQuestions; questions.v = DefaultQuestions;
} }
export function AddQuestion() { export function AddQuestion() {
questions.v.push({ questions.v.push({
name: '', name: "",
answers: ['', '', '', ''], answers: ["", "", "", ""],
correctAnswer: undefined correctAnswer: undefined,
}); });
} }
export function DeleteQuestion(index) { export function DeleteQuestion(index) {
if (questions.v.length > 1) { if (questions.v.length > 1) {
if (confirm('Are you sure you want to delete this question? You cant undo this.')) { if (confirm("Are you sure you want to delete this question? You cant undo this.")) {
questions.v.splice(index, 1); questions.v.splice(index, 1);
} }
} else { } else {
alert('You need at least one question.'); alert("You need at least one question.");
} }
} }

View file

@ -0,0 +1,34 @@
import { questions } from "./GameCreateData.svelte.js";
import { AiPrompts } from "$lib/config.js";
export function GenerateOptionsUsingAI(index) {
fetch("https://ai.hackclub.com/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
messages: [
{
role: "user",
content: AiPrompts.GenerateOptionsUsingAI.replace(
"[question]",
questions.v[index].name,
),
},
],
}),
})
.then((response) => response.json())
.then((data) => {
let question = questions.v[index].name;
questions.v[index] = JSON.parse(data.choices[0].message.content);
questions.v[index].name = question;
})
.catch((error) => {
alert("Error:" + error);
return;
});
alert("added!");
}

View file

@ -0,0 +1,38 @@
import { questions } from "./GameCreateData.svelte.js";
import { AiPrompts } from "$lib/config.js";
export function GenerateQuestionsUsingAI() {
let topic = window.prompt(
"What is the topic of the questions?\nand the number of questions in the topic?",
);
if (!topic) {
return;
}
fetch("https://ai.hackclub.com/chat/completions", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
messages: [
{
role: "user",
content: AiPrompts.GenerateQuestionsUsingAI.replace("[topic]", topic),
},
],
}),
})
.then((response) => response.json())
.then((data) => {
console.log(data);
questions.v = JSON.parse(data.choices[0].message.content);
})
.catch((error) => {
alert("Error:" + error);
return;
});
alert("added!");
}

View file

@ -1,16 +1,15 @@
import { supabase } from '$lib/supabase'; import { supabase } from "$lib/supabase";
export async function createGame(questions, gamePin) { export async function createGame(questions, gamePin) {
const { data: gameData, error: gameError } = await supabase.from("games").insert({
const { data: gameData, error: gameError } = await supabase.from('games').insert({ creator: "anonymous",
creator: 'anonymous',
creationdate: new Date().toISOString(), creationdate: new Date().toISOString(),
status: 'lobby', status: "lobby",
gamepin: gamePin gamepin: gamePin,
}); });
if (gameError) { if (gameError) {
alert('Failed to create game: ' + gameError.message + '\n\nPlease try again.'); alert("Failed to create game: " + gameError.message + "\n\nPlease try again.");
return; return;
} }
@ -18,16 +17,16 @@ export async function createGame(questions, gamePin) {
const questionsData = questions.map((q, index) => ({ const questionsData = questions.map((q, index) => ({
gameid: gamePin, gameid: gamePin,
questionstext: q.name, questionstext: q.name,
correctanswer: q.correctAnswer correctanswer: q.correctAnswer,
})); }));
const { data: questionsResult, error: questionsError } = await supabase const { data: questionsResult, error: questionsError } = await supabase
.from('questions') .from("questions")
.insert(questionsData) .insert(questionsData)
.select('id'); .select("id");
if (questionsError) { if (questionsError) {
alert('Failed to insert questions: ' + questionsError.message + '\n\nPlease try again.'); alert("Failed to insert questions: " + questionsError.message + "\n\nPlease try again.");
return; return;
} }
@ -36,15 +35,17 @@ export async function createGame(questions, gamePin) {
questions[index].answers.forEach((answer, answerIndex) => { questions[index].answers.forEach((answer, answerIndex) => {
answersData.push({ answersData.push({
questionid: question.id, questionid: question.id,
content: answer content: answer,
}); });
}); });
}); });
const { error: answersError } = await supabase.from('answers').insert(answersData); const { error: answersError } = await supabase.from("answers").insert(answersData);
if (answersError) { if (answersError) {
alert('Failed to insert answers: ' + answersError.message + '\n\nPlease try again.'); alert("Failed to insert answers: " + answersError.message + "\n\nPlease try again.");
return; return;
} }
window.location.href = "/host/" + gamePin;
} }

View file

@ -1,9 +1,7 @@
import { createGame } from "./InsertGameInDB.js"; import { createGame } from "./InsertGameInDB.js";
import { questions } from "./GameCreateData.svelte.js"; import { questions,Wait } from "./GameCreateData.svelte.js";
export async function startGame() { export async function startGame() {
if (questions.v.some((q) => q.name === "")) return alert("Please fill all questions"); if (questions.v.some((q) => q.name === "")) return alert("Please fill all questions");
if (questions.v.some((q) => q.answers.some((a) => a === ""))) return alert("Fill all options"); if (questions.v.some((q) => q.answers.some((a) => a === ""))) return alert("Fill all options");
if (questions.v.some((q) => q.correctAnswer === undefined)) if (questions.v.some((q) => q.correctAnswer === undefined))
@ -13,7 +11,6 @@ export async function startGame() {
.toString() .toString()
.padStart(6, "0"); .padStart(6, "0");
await createGame(questions.v, gamePin); Wait.v = true;
window.location.href = "/host/" + gamePin; await createGame(questions.v, gamePin);}
}

View file

@ -1,6 +1,7 @@
<script> <script>
import { CurrentQuestionDetails } from "../../../logic/HostsData.svelte.js"; import { CurrentQuestionDetails } from "../../../logic/HostsData.svelte.js";
import config from "$lib/config.json";
import { AnswersSymbolAndColorScheme } from "$lib/config.js";
</script> </script>
<div class="mt-5 grid grid-cols-2 gap-5 gap-x-3"> <div class="mt-5 grid grid-cols-2 gap-5 gap-x-3">
@ -10,12 +11,12 @@
<label <label
for="O{index}" for="O{index}"
style=" style="
--border-color: {config.AnswersSymbolAndColor[index].Color}; --border-color: {AnswersSymbolAndColorScheme[index].Color};
--bg-color: {config.AnswersSymbolAndColor[index].Color}; --bg-color: {AnswersSymbolAndColorScheme[index].Color};
" "
class="w-full rounded-lg border-[5px] border-[var(--border-color)] bg-[var(--bg-color)] pt-1 pr-2 pb-1 pl-2 text-center text-3xl transition-all" class="w-full rounded-lg border-[5px] border-[var(--border-color)] bg-[var(--bg-color)] pt-1 pr-2 pb-1 pl-2 text-center text-3xl transition-all"
> >
<i class="nf {config.AnswersSymbolAndColor[index].Symbol}"></i> <i class="nf {AnswersSymbolAndColorScheme[index].Symbol}"></i>
{answer} {answer}
</label> </label>
</div> </div>

View file

@ -1,6 +1,6 @@
<script> <script>
import { questions, Selected } from "../../logic/HostsData.svelte.js"; import { questions, Selected } from "../../logic/HostsData.svelte.js";
import config from "$lib/config.json"; import { AnswersSymbolAndColorScheme } from "$lib/config.js";
</script> </script>
<div class="mt-5 grid grid-cols-2 gap-5 gap-x-3"> <div class="mt-5 grid grid-cols-2 gap-5 gap-x-3">
@ -18,17 +18,17 @@
<label <label
for="O{index}" for="O{index}"
style=" style="
--border-color: {config.AnswersSymbolAndColor[index].Color}; --border-color: {AnswersSymbolAndColorScheme[index].Color};
--bg-color: {config.AnswersSymbolAndColor[index].Color}; --bg-color: {AnswersSymbolAndColorScheme[index].Color};
--border-color-checked: {config.AnswersSymbolAndColor[index].SelectedColor}; --border-color-checked: {AnswersSymbolAndColorScheme[index].SelectedColor};
--bg-color-checked: {config.AnswersSymbolAndColor[index].SelectedColor}; --bg-color-checked: {AnswersSymbolAndColorScheme[index].SelectedColor};
--border-color-hover: {config.AnswersSymbolAndColor[index].HoverBorderColor}; --border-color-hover: {AnswersSymbolAndColorScheme[index].HoverBorderColor};
--border-color-checked: {config.AnswersSymbolAndColor[index].SelectedBorderColor}; --border-color-checked: {AnswersSymbolAndColorScheme[index].SelectedBorderColor};
--border-color-hover: {config.AnswersSymbolAndColor[index].HoverBorderColor}; --border-color-hover: {AnswersSymbolAndColorScheme[index].HoverBorderColor};
" "
class="w-full cursor-pointer rounded-lg border-[5px] border-[var(--border-color)] bg-[var(--bg-color)] pt-1 pr-2 pb-1 pl-2 text-center text-3xl transition-all peer-checked:border-[var(--border-color-checked)] peer-checked:border-[var(--border-color-checked)] peer-checked:bg-[var(--bg-color-checked)] hover:border-[var(--border-color-hover)]" class="w-full cursor-pointer rounded-lg border-[5px] border-[var(--border-color)] bg-[var(--bg-color)] pt-1 pr-2 pb-1 pl-2 text-center text-3xl transition-all peer-checked:border-[var(--border-color-checked)] peer-checked:border-[var(--border-color-checked)] peer-checked:bg-[var(--bg-color-checked)] hover:border-[var(--border-color-hover)]"
> >
<i class="nf {config.AnswersSymbolAndColor[index].Symbol}"></i> <i class="nf {AnswersSymbolAndColorScheme[index].Symbol}"></i>
{answer} {answer}
</label> </label>
</div> </div>