Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
59 changes: 57 additions & 2 deletions task-1/Time.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,58 @@
let result = 0;

export class Time {
// Your code here
}
#secondsFromMidnight;
constructor(hours, minutes, seconds) {
this.#secondsFromMidnight = hours * 3600 + minutes * 60 + seconds;
this.hours = hours;
this.minutes = minutes;
this.seconds = seconds;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not very clear which variable(s) represent the "single source of truth" of the time value. The class encapsulates the time as a number of seconds as well as an hours, minutes and seconds value. Moreover, the #secondsFromMidnight property is made private, while the hours, minutes and seconds properties are public despite the fact that there are "getter" methods for these values.

It would be clearer to have a single private property, #secondsFromMidnight, to represent the time value and compute the hours, minutes, and seconds on the fly in the "getter" methods.

}

getHours() {
return this.hours;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rather than maintaining a separate class property for the hours value, you can simply compute the value using integer arithmetic from the #secondsFromMidnight property.

}

getMinutes() {
return this.minutes;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

}

getSeconds() {
return this.seconds;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And here

}

#normilized() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpick: spelling error -> normalized

if (this.#secondsFromMidnight < 0) {
this.#secondsFromMidnight =
86400 - Math.abs(this.#secondsFromMidnight % 86400);
}
if (this.#secondsFromMidnight >= 86400) {
this.#secondsFromMidnight = this.#secondsFromMidnight % 86400;
}
this.hours = Math.floor(this.#secondsFromMidnight / 3600);
this.minutes = Math.floor((this.#secondsFromMidnight % 3600) / 60);
this.seconds =
this.#secondsFromMidnight - this.hours * 3600 - this.minutes * 60;
return this.#secondsFromMidnight;

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you drop the hours, minutes and seconds properties, normalizing the #secondsFromMidnight value is simplified: no need to break down the value into its hours, minutes and seconds components.

}

addSeconds(sec) {
this.#secondsFromMidnight = this.#secondsFromMidnight + sec;
return this.#normilized();
}

addMinutes(min) {
this.#secondsFromMidnight = this.#secondsFromMidnight + min * 60;
return this.#normilized();
}

addHours(hrs) {
this.#secondsFromMidnight = this.#secondsFromMidnight + hrs * 3600;
return this.#normilized();
}

toString() {
return `${String(this.hours).padStart(2, "0")}:${String(this.minutes).padStart(2, "0")}:${String(this.seconds).padStart(2, "0")}`;
// return `${("0" + this.hours).slice(-2)}:${("0" + this.minutes).slice(-2)}:${("0" + this.seconds).slice(-2)}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remove commented code in pull request. No need for a reviewer to see what you tried but did not use.

}
}
5 changes: 3 additions & 2 deletions task-1/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { Time } from './Time.js';

const time = new Time(13, 37, 0);
console.log(time.toString()); // Output: "13:37:00"
const time = new Time(23, 59, 50);
// console.log(time.toString()); // Output: "13:37:00"
console.log(time.addSeconds(20))
30 changes: 0 additions & 30 deletions task-1/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions task-2/basePrompt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export const BASE_PROMPT = `You are a quiz master specializing in Ancient Egypt.
Generate exactly 1 unexpected quiz question about Ancient Egypt.

Return ONLY a valid JSON with no markdown, no explanation, no code fences.
Each element must have this exact shape:
{
"question": "string",
"answers": ["string", "string", "string", "string"],
"correct": 1
}
"correct" is the 1-based index of the right answer (1–4).

Vary difficulty: questions 1-3 easy, 4-7 medium, 8-10 hard.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The LLM does not remember its previous responses, unless you include that in the prompt (i.e. build up a context window in your prompt). So it will not know whether any previous questions were easy, medium or hard.

If you used the tip from the assignment description: Ask the LLM to generate all 10 questions in one reply before the start of the quiz, then it would be able to create a variety of questions with mixed difficulty because it would be contained in a single response, with the added advantage that it would cost you one request instead of 10.

Another issue with repeating the same prompt for each question is that there is no guarantee that the questions will be unique, i.e. no duplicates.

Cover a mix of subtopics: pharaohs, gods, monuments, daily life, writing, history.`
72 changes: 71 additions & 1 deletion task-2/index.js
Original file line number Diff line number Diff line change
@@ -1 +1,71 @@
// Write your code here. You may create as many files as you like.
import "dotenv/config";
import { OpenAI } from "openai/client.js";
import promptSync from "prompt-sync";
import chalk from "chalk";
import { BASE_PROMPT } from "./basePrompt.js";
import {userAnswer, isValidAnswer} from "./utils.js"

const prompt = promptSync();

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A user who wants to break off the quiz should either be able to press Ctrl-C or you should provide a menu option (say, x) to exit the quiz prematurely. The sigint option below enables Ctrl-C.

Suggested change
const prompt = promptSync();
const prompt = promptSync({ sigint: true });


const openai = new OpenAI({
baseURL: "https://models.github.ai/inference/",
apiKey: process.env.API_KEY,
});

async function app() {
try {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 for handling errors

console.log("Welcome to the quiz ANCIENT EGYPT! Ready to begin?");
prompt("Press enter to start the game");

const score = {
totalQuestions: 10,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This property is never mutated. It might as well have been a const, e.g.

const TOTAL_QUESTIONS = 10;

currentQuestion: 1,
correctAnswers: 0,
wrongAnswers: 0,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This property is updated but never reported. It serves no purpose.

};

while (score.currentQuestion <= score.totalQuestions) {
console.log(`Loading question ${score.currentQuestion}...`);
const response = await openai.chat.completions.create({
model: "openai/gpt-4o-mini",
response_format: { type: "json_object" },
messages: [{ role: "user", content: BASE_PROMPT }],
});

const responseContent = response.choices[0].message.content;
const result = JSON.parse(responseContent);

console.log(`
${chalk.blue(result.question)}
${result.answers.map((answer, i) => `${i + 1}. ${answer}`).join("\n")}
`);
Comment on lines +39 to +41

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To maintain correct indentation you could refactor this as:

Suggested change
${chalk.blue(result.question)}
${result.answers.map((answer, i) => `${i + 1}. ${answer}`).join("\n")}
`);
console.log(
`\n${chalk.blue(result.question)}\n${result.answers
.map((answer, i) => `${i + 1}. ${answer}`)
.join("\n")}`,
);


let answer = userAnswer();

while (true) {
if (isValidAnswer(answer)) {
break;
} else {
console.error("Please enter an integer number between 1 and 4: ");
answer = userAnswer();
}
}

if (result.correct === answer) {
console.log(chalk.green("Correct! Well done."));
score.correctAnswers += 1;
} else {
console.log(chalk.red(`Wrong! The correct answer was: ${result.correct}`));
score.wrongAnswers += 1;
}
score.currentQuestion += 1;
}

console.log(
chalk.yellow(`Quiz finished! Your final score is ${score.correctAnswers}/${score.totalQuestions}`),
);
} catch (error) {
console.error(error.message);
}
}
app();
104 changes: 104 additions & 0 deletions task-2/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 11 additions & 3 deletions task-2/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@
"description": "",
"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"
}
"type": "module",
"dependencies": {
"chalk": "^5.6.2",
"dotenv": "^17.3.1",
"openai": "^6.33.0",
"prompt-sync": "^4.2.0",
"readline-sync": "^1.4.10"
}
}
8 changes: 8 additions & 0 deletions task-2/utils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import promptSync from "prompt-sync";

const prompt = promptSync();

export const userAnswer = () => Number((prompt("Your answer (1-4): ") || "").trim());

export const isValidAnswer = (userAnswer) =>
userAnswer >= 1 && userAnswer <= 4 && Number.isInteger(userAnswer);