From 5e71d6eb0e2884a5adc68df9a176741822f7af60 Mon Sep 17 00:00:00 2001 From: Halyna Romanyshyn <30292604+halyna1995@users.noreply.github.com> Date: Tue, 24 Mar 2026 22:21:26 +0100 Subject: [PATCH 1/2] task 1 complete --- task-1/Time.js | 56 +++++++++++++++++++++++++++++++++++++++- task-1/package-lock.json | 32 ++--------------------- 2 files changed, 57 insertions(+), 31 deletions(-) diff --git a/task-1/Time.js b/task-1/Time.js index 97ca7e2..cc40560 100644 --- a/task-1/Time.js +++ b/task-1/Time.js @@ -1,3 +1,57 @@ export class Time { - // Your code here + #secondsFromMidnight; + + constructor(hours, minutes, seconds) { + const totalSeconds = hours * 3600 + minutes * 60 + seconds; + + if (totalSeconds < 0 || totalSeconds >= 86400) { + throw new Error('Invalid time'); + } + + this.#secondsFromMidnight = totalSeconds; + } + + getHours() { + return Math.floor(this.#secondsFromMidnight / 3600); + } + + getMinutes() { + return Math.floor((this.#secondsFromMidnight % 3600) / 60); + } + + getSeconds() { + return this.#secondsFromMidnight % 60; + } + + addSeconds(seconds) { + this.#secondsFromMidnight = + ((this.#secondsFromMidnight + seconds) % 86400 + 86400) % 86400; + } + + addMinutes(minutes) { + this.addSeconds(minutes * 60); + } + + addHours(hours) { + this.addSeconds(hours * 3600); + } + + toString() { + let hour = this.getHours(); + let minute = this.getMinutes(); + let second = this.getSeconds(); + + if (hour < 10) { + hour = '0' + hour; + } + + if (minute < 10) { + minute = '0' + minute; + } + + if (second < 10) { + second = '0' + second; + } + return `${hour}:${minute}:${second}`; + } } \ No newline at end of file diff --git a/task-1/package-lock.json b/task-1/package-lock.json index aba2728..14d0d66 100644 --- a/task-1/package-lock.json +++ b/task-1/package-lock.json @@ -161,9 +161,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -180,9 +177,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -199,9 +193,6 @@ "cpu": [ "ppc64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -218,9 +209,6 @@ "cpu": [ "s390x" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -237,9 +225,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MIT", "optional": true, "os": [ @@ -256,9 +241,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MIT", "optional": true, "os": [ @@ -706,9 +688,6 @@ "cpu": [ "arm64" ], - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -729,9 +708,6 @@ "cpu": [ "arm64" ], - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -752,9 +728,6 @@ "cpu": [ "x64" ], - "libc": [ - "glibc" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -775,9 +748,6 @@ "cpu": [ "x64" ], - "libc": [ - "musl" - ], "license": "MPL-2.0", "optional": true, "os": [ @@ -885,6 +855,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.4.tgz", "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -1032,6 +1003,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-8.0.2.tgz", "integrity": "sha512-1gFhNi+bHhRE/qKZOJXACm6tX4bA3Isy9KuKF15AgSRuRazNBOJfdDemPBU16/mpMxApDPrWvZ08DcLPEoRnuA==", "license": "MIT", + "peer": true, "dependencies": { "lightningcss": "^1.32.0", "picomatch": "^4.0.3", From f1952fc3e20dc48e6325d908a0dabc19580dd797 Mon Sep 17 00:00:00 2001 From: Halyna Romanyshyn <30292604+halyna1995@users.noreply.github.com> Date: Wed, 25 Mar 2026 23:47:13 +0100 Subject: [PATCH 2/2] task 2 complete --- task-2/index.js | 184 ++++++++++++++++++++++++++++++++++++++- task-2/package-lock.json | 59 +++++++++++++ task-2/package.json | 13 ++- 3 files changed, 251 insertions(+), 5 deletions(-) create mode 100644 task-2/package-lock.json diff --git a/task-2/index.js b/task-2/index.js index 02acffa..2e74aeb 100644 --- a/task-2/index.js +++ b/task-2/index.js @@ -1 +1,183 @@ -// Write your code here. You may create as many files as you like. +import dotenv from 'dotenv'; +import promptSync from 'prompt-sync'; + +dotenv.config({ path: './.env' }); + +const prompt = promptSync({ sigint: true }); + +console.log('ENV token =', process.env.GITHUB_TOKEN); +// ANSI colors for terminal +const GREEN = "\x1b[32m"; +const RED = "\x1b[31m"; +const CYAN = "\x1b[36m"; +const YELLOW = "\x1b[33m"; +const RESET = "\x1b[0m"; + +function parseJsonFromModel(content) { + if (typeof content !== "string") { + throw new Error("Model content is not a string"); + } + + let cleaned = content.trim(); + + // remove ```json ... ``` + cleaned = cleaned.replace(/^```json\s*/i, ""); + cleaned = cleaned.replace(/^```\s*/i, ""); + cleaned = cleaned.replace(/\s*```$/i, ""); + cleaned = cleaned.trim(); + + try { + return JSON.parse(cleaned); + } catch (error) { + throw new Error("Could not parse JSON from model response:\n" + cleaned); + } +} + +function validateQuestions(questions) { + if (!Array.isArray(questions)) { + throw new Error("Model did not return an array"); + } + + if (questions.length !== 10) { + throw new Error("Model did not return exactly 10 questions"); + } + + for (let i = 0; i < questions.length; i++) { + const q = questions[i]; + + if (!q || typeof q !== "object") { + throw new Error(`Question ${i + 1} is not an object`); + } + + if (typeof q.question !== "string" || q.question.trim() === "") { + throw new Error(`Question ${i + 1} has no valid question text`); + } + + if (!Array.isArray(q.options) || q.options.length !== 4) { + throw new Error(`Question ${i + 1} must have exactly 4 options`); + } + + for (let j = 0; j < q.options.length; j++) { + if (typeof q.options[j] !== "string" || q.options[j].trim() === "") { + throw new Error(`Question ${i + 1}, option ${j + 1} is invalid`); + } + } + + if ( + typeof q.answerIndex !== "number" || + !Number.isInteger(q.answerIndex) || + q.answerIndex < 0 || + q.answerIndex > 3 + ) { + throw new Error(`Question ${i + 1} has invalid answerIndex`); + } + } +} + +async function getQuestions(topic) { + const token = process.env.GITHUB_TOKEN; + + if (!token) { + throw new Error("GITHUB_TOKEN is missing in .env"); + } + + if (typeof fetch !== "function") { + throw new Error("fetch is not available. Use Node 18+ or newer."); + } + + const requestBody = { + model: "openai/gpt-4.1-mini", + messages: [ + { + role: "user", + content: + `Create exactly 10 multiple choice quiz questions about "${topic}". ` + + `Return ONLY valid JSON. No markdown. No explanation. ` + + `[{"question":"...","options":["...","...","...","..."],"answerIndex":0}]`, + }, + ], + temperature: 0.7, +}; + const response = await fetch( + "https://models.github.ai/inference/chat/completions", + { + method: "POST", + headers: { + Authorization: `Bearer ${token}`, + "Content-Type": "application/json", + }, + body: JSON.stringify(requestBody), + }, + ); + + if (!response.ok) { + const errorText = await response.text(); + throw new Error(`API error ${response.status}: ${errorText}`); + } + + const data = await response.json(); + + const content = data?.choices?.[0]?.message?.content; + + if (!content) { + throw new Error("Model returned empty content"); + } + + const questions = parseJsonFromModel(content); + validateQuestions(questions); + + return questions; +} + +async function startQuiz() { + try { + console.log(CYAN + "Welcome to the AI Quiz Game!" + RESET); + + let topic = prompt("Choose a topic: ").trim(); + if (!topic) { + topic = "general knowledge"; + } + + console.log(YELLOW + "\nGenerating 10 questions...\n" + RESET); + + const questions = await getQuestions(topic); + + let score = 0; + + for (let i = 0; i < questions.length; i++) { + const q = questions[i]; + + console.log(CYAN + `Question ${i + 1}/10` + RESET); + console.log(q.question); + + for (let j = 0; j < q.options.length; j++) { + console.log(`${j + 1}. ${q.options[j]}`); + } + + let answer = prompt("Your answer (1-4): ").trim(); + + while (!["1", "2", "3", "4"].includes(answer)) { + answer = prompt("Please type 1, 2, 3, or 4: ").trim(); + } + + const userIndex = Number(answer) - 1; + + if (userIndex === q.answerIndex) { + score++; + console.log(GREEN + "Correct! +1 point\n" + RESET); + } else { + console.log( + RED + + `Wrong! Correct answer: ${q.answerIndex + 1}. ${q.options[q.answerIndex]}\n` + + RESET, + ); + } + } + + console.log(YELLOW + `Final score: ${score}/10` + RESET); + } catch (error) { + console.error(RED + "Error: " + error.message + RESET); + } +} + +startQuiz(); diff --git a/task-2/package-lock.json b/task-2/package-lock.json new file mode 100644 index 0000000..504c99b --- /dev/null +++ b/task-2/package-lock.json @@ -0,0 +1,59 @@ +{ + "name": "task-2", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "task-2", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "dotenv": "^17.3.1", + "prompt-sync": "^4.2.0" + } + }, + "node_modules/ansi-regex": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-4.1.1.tgz", + "integrity": "sha512-ILlv4k/3f6vfQ4OoP2AGvirOktlQ98ZEL1k9FaQjxa3L1abBgbuTDAdPOpvbGncC0BTVQrl+OM8xZGK6tWXt7g==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/dotenv": { + "version": "17.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-17.3.1.tgz", + "integrity": "sha512-IO8C/dzEb6O3F9/twg6ZLXz164a2fhTnEWb95H23Dm4OuN+92NmEAlTrupP9VW6Jm3sO26tQlqyvyi4CsnY9GA==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/prompt-sync": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/prompt-sync/-/prompt-sync-4.2.0.tgz", + "integrity": "sha512-BuEzzc5zptP5LsgV5MZETjDaKSWfchl5U9Luiu8SKp7iZWD5tZalOxvNcZRwv+d2phNFr8xlbxmFNcRKfJOzJw==", + "license": "MIT", + "dependencies": { + "strip-ansi": "^5.0.0" + } + }, + "node_modules/strip-ansi": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-5.2.0.tgz", + "integrity": "sha512-DuRs1gKbBqsMKIZlrffwlug8MHkcnpjs5VPmL1PAh+mA30U0DTotfDZ0d2UUsXpPmPmMMJ6W773MaA3J+lbiWA==", + "license": "MIT", + "dependencies": { + "ansi-regex": "^4.1.0" + }, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/task-2/package.json b/task-2/package.json index a992f45..8033ce7 100644 --- a/task-2/package.json +++ b/task-2/package.json @@ -1,13 +1,18 @@ { "name": "task-2", "version": "1.0.0", - "description": "", + "description": "Quiz game", "main": "index.js", "scripts": { - "test": "echo \"Error: no test specified\" && exit 1" + "test": "echo \"Error: no test specified\" && exit 1", + "start": "node index.js" }, "keywords": [], "author": "", "license": "ISC", - "type": "module" -} \ No newline at end of file + "type": "module", + "dependencies": { + "dotenv": "^17.3.1", + "prompt-sync": "^4.2.0" + } +}