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
38 changes: 38 additions & 0 deletions Games/Linux_Command_Challenge/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
# Linux Command Challenge

A browser quiz for practicing ten everyday Linux commands: `pwd`, `ls`, `cd`,
`mkdir`, `touch`, `cat`, `cp`, `mv`, `rm`, and `grep`.

## How to play

Open `index.html` in a browser. No installation, server, account, or internet
connection is required for the game itself.

1. Choose one of four commands for each question.
2. Read the immediate feedback and explanation. The correct command is highlighted
and each question can only be scored once.
3. Select **Next question**, then **View results** after question ten.
4. Review your score, percentage, and all answers on the results screen.
5. Use **Restart challenge** at any time, or **Play again** after finishing.

Use the mouse, touch, or the **1–4** keys to answer. All buttons also support
**Tab**, **Shift+Tab**, **Enter**, and **Space**. After answering, focus moves to
the next button; press **Enter** to continue. Feedback is announced through an
ARIA live region. Progress indicates the number of questions answered.

Commands are examples only and are never executed. Restarting or refreshing resets
the session; scores are not stored or sent anywhere.

## Technology and files

- `index.html`: semantic structure, labeled progress, and quiz/results sections.
- `style.css`: responsive terminal-inspired layout and visible keyboard focus.
- `script.js`: ten questions, answer locking, feedback, scoring, review, and restart.

Built with HTML, CSS, and vanilla JavaScript; no dependencies or build step.

## Preview

![Linux Command Challenge](../../assets/images/Linux_Command_Challenge.jpg)

Related issue: [kunjgit/GameZone#5266](https://github.com/kunjgit/GameZone/issues/5266).
59 changes: 59 additions & 0 deletions Games/Linux_Command_Challenge/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta name="description" content="Practice ten everyday Linux commands in a free, interactive terminal quiz.">
<title>Linux Command Challenge</title>
<link rel="stylesheet" href="style.css">
<script src="script.js" defer></script>
</head>
<body>
<div class="shell">
<header class="topbar">
<a class="brand" href="../../index.html"><span class="brand-icon" aria-hidden="true">&gt;_</span> GameZone <span class="slash">/</span> <span class="lab">the command lab</span></a>
<span class="session"><span aria-hidden="true">●</span> PRACTICE SESSION</span>
</header>

<main>
<aside class="intro" aria-labelledby="game-title">
<p class="eyebrow">LEARN BY DOING / VOL. 01</p>
<h1 id="game-title">Linux<br> Command<br> <span>Challenge.</span></h1>
<p class="intro-copy">Small commands. Big possibilities.<br>Find your feet in the terminal, one question at a time.</p>
<div class="facts"><span><strong>10</strong> questions</span><span><strong>∞</strong> attempts</span><span><strong>0</strong> pressure</span></div>
<div class="terminal-note"><p><span aria-hidden="true">$</span> ./learn-linux</p><p>No terminal needed.<br>Just a little curiosity.<span class="cursor" aria-hidden="true">β–Œ</span></p></div>
<p class="safety-note">This is a quiz. Commands shown here never run on your computer.</p>
</aside>

<section class="workspace" aria-label="Linux command quiz">
<div class="window-bar"><span class="window-dots" aria-hidden="true"><i></i><i></i><i></i></span><span>challenge.sh</span><span aria-hidden="true">~/practice</span></div>
<div class="game-body">
<div class="stats"><span id="question-count">QUESTION 01 / 10</span><span>Score <strong id="score">0 / 10</strong></span></div>
<label class="sr-only" for="progress">Questions answered</label>
<progress id="progress" max="10" value="0">0 of 10 answered</progress>

<div id="quiz">
<p id="category" class="eyebrow question-category"></p>
<h2 id="question" tabindex="-1"></h2>
<p class="question-help">Choose the command that gets the job done.</p>
<div id="options" class="options" role="group" aria-labelledby="question" aria-describedby="keyboard-help"></div>
<div id="feedback" class="feedback" role="status" aria-live="polite" aria-atomic="true"></div>
<div class="quiz-footer"><p id="keyboard-help">Press <kbd>1</kbd>–<kbd>4</kbd> to choose<br>or use <kbd>Tab</kbd> + <kbd>Enter</kbd></p><button id="next" class="primary" disabled>Next question <span aria-hidden="true">β†’</span></button></div>
</div>

<div id="results" hidden>
<p class="eyebrow question-category">SESSION COMPLETE</p>
<h2 id="result-title" tabindex="-1"></h2>
<p id="result-message" class="question-help"></p>
<p class="result-score"><strong id="final-score"></strong><span>correct answers</span></p>
<details><summary>Review all 10 answers</summary><ol id="review"></ol></details>
</div>
<button id="restart" class="restart" type="button">↻ Restart challenge</button>
</div>
</section>
</main>
<footer class="page-footer"><span>A little practice goes a long way.</span><span>HTML Β· CSS Β· JavaScript</span></footer>
</div>
<noscript><p class="noscript">Enable JavaScript to play Linux Command Challenge.</p></noscript>
</body>
</html>
132 changes: 132 additions & 0 deletions Games/Linux_Command_Challenge/script.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
"use strict";

(() => {
const questions = [
{ category: "Finding your way", question: "Which command shows your current directory?", options: ["ls", "pwd", "cd", "whoami"], answer: 1, explanation: "pwd means print working directory. It displays the full path of your current directory." },
{ category: "Looking around", question: "Which command lists files and directories?", options: ["ls", "mkdir", "pwd", "touch"], answer: 0, explanation: "ls lists the contents of a directory. Use ls -a to include hidden entries." },
{ category: "Moving around", question: "How do you move into a directory named Documents?", options: ["ls Documents", "mkdir Documents", "cat Documents", "cd Documents"], answer: 3, explanation: "cd changes your working directory. cd Documents enters the Documents directory if it exists here." },
{ category: "Making space", question: "How do you create a directory named projects?", options: ["touch projects", "cd projects", "mkdir projects", "ls projects"], answer: 2, explanation: "mkdir creates a directory. mkdir projects makes a new directory named projects." },
{ category: "Starting fresh", question: "How do you create an empty notes.txt if it does not exist?", options: ["touch notes.txt", "cat notes.txt", "mkdir notes.txt", "rm notes.txt"], answer: 0, explanation: "touch creates an empty file if it does not exist. For an existing file, it updates timestamps without clearing its contents." },
{ category: "Reading files", question: "Which command displays the contents of notes.txt?", options: ["cd notes.txt", "cat notes.txt", "mv notes.txt", "touch notes.txt"], answer: 1, explanation: "cat writes a file's contents to the terminal. It can also concatenate multiple files." },
{ category: "Keeping a copy", question: "How do you copy notes.txt to backup.txt?", options: ["mv notes.txt backup.txt", "cat notes.txt backup.txt", "cd notes.txt backup.txt", "cp notes.txt backup.txt"], answer: 3, explanation: "cp copies a file from source to destination. The original notes.txt stays in place." },
{ category: "A new name", question: "How do you rename old.txt to new.txt?", options: ["cp old.txt new.txt", "touch new.txt", "mv old.txt new.txt", "cat old.txt new.txt"], answer: 2, explanation: "mv moves or renames files. Here it changes the name from old.txt to new.txt." },
{ category: "Tidying up", question: "Which command removes a file named draft.txt?", options: ["rm draft.txt", "mkdir draft.txt", "pwd draft.txt", "cat draft.txt"], answer: 0, explanation: "rm removes files. In a real terminal it usually does not move them to a trash folder, so double-check the filename." },
{ category: "Finding a match", question: "How do you show lines containing error in app.log?", options: ["cat error app.log", "grep error app.log", "ls error app.log", "mv error app.log"], answer: 1, explanation: "grep searches text and prints matching lines. grep error app.log finds lines containing the case-sensitive text error." }
];

const get = (id) => document.getElementById(id);
const options = get("options");
const next = get("next");
const feedback = get("feedback");
let current = 0;
let score = 0;
let answers = [];

function updateProgress() {
get("score").textContent = `${score} / ${questions.length}`;
get("progress").value = answers.length;
get("progress").textContent = `${answers.length} of ${questions.length} answered`;
}

function renderQuestion(moveFocus = true) {
const item = questions[current];
get("question-count").textContent = `QUESTION ${String(current + 1).padStart(2, "0")} / ${questions.length}`;
get("category").textContent = item.category;
get("question").textContent = item.question;
feedback.replaceChildren();
feedback.className = "feedback";
next.disabled = true;
next.textContent = current === questions.length - 1 ? "View results β†’" : "Next question β†’";
options.replaceChildren();
item.options.forEach((command, index) => {
const button = document.createElement("button");
button.type = "button";
button.className = "option";
const number = document.createElement("span");
number.className = "option-number";
number.textContent = index + 1;
number.setAttribute("aria-hidden", "true");
const code = document.createElement("code");
code.textContent = command;
button.append(number, code);
button.setAttribute("aria-label", `Option ${index + 1}: ${command}`);
button.addEventListener("click", () => selectAnswer(index));
options.append(button);
});
if (moveFocus) get("question").focus();
}

function selectAnswer(index) {
// Lock the question before updating the UI so repeated input cannot earn extra points.
if (get("quiz").hidden || answers.length > current) return;
const item = questions[current];
const correct = index === item.answer;
answers.push(index);
if (correct) score += 1;
Array.from(options.children).forEach((button, optionIndex) => {
button.disabled = true;
if (optionIndex === item.answer) button.classList.add("correct");
else if (optionIndex === index) button.classList.add("incorrect");
});
const heading = document.createElement("strong");
heading.textContent = correct ? "βœ“ Correct. Nicely done!" : `βœ• Not quite. The answer is ${item.options[item.answer]}.`;
feedback.className = correct ? "feedback" : "feedback incorrect";
feedback.append(heading, document.createTextNode(item.explanation));
updateProgress();
next.disabled = false;
next.focus();
}

function showResults() {
get("quiz").hidden = true;
get("results").hidden = false;
get("question-count").textContent = "10 / 10 COMPLETED";
get("final-score").textContent = `${score} / ${questions.length}`;
get("result-title").textContent = score === 10 ? "Command line confidence." : score >= 6 ? "You're finding your flow." : "Every command is a start.";
get("result-message").textContent = `You got ${score} out of ${questions.length} correct (${score * 10}%). Review the commands below, or restart for another round.`;
const review = get("review");
review.replaceChildren();
questions.forEach((item, index) => {
const li = document.createElement("li");
const title = document.createElement("strong");
title.textContent = `${answers[index] === item.answer ? "βœ“" : "βœ•"} ${item.question}`;
const answer = document.createElement("p");
answer.textContent = `Your answer: ${item.options[answers[index]]}. Correct answer: ${item.options[item.answer]}.`;
const explanation = document.createElement("p");
explanation.textContent = item.explanation;
li.append(title, answer, explanation);
review.append(li);
});
get("restart").textContent = "↻ Play again";
get("result-title").focus();
}

next.addEventListener("click", () => {
if (answers.length <= current || get("quiz").hidden) return;
if (current === questions.length - 1) showResults();
else { current += 1; renderQuestion(); }
});

get("restart").addEventListener("click", () => {
current = 0;
score = 0;
answers = [];
get("results").hidden = true;
get("results").querySelector("details").open = false;
get("quiz").hidden = false;
get("restart").textContent = "↻ Restart challenge";
updateProgress();
renderQuestion();
});

document.addEventListener("keydown", (event) => {
if (event.repeat || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;
if (/^[1-4]$/.test(event.key) && !get("quiz").hidden) {
event.preventDefault();
selectAnswer(Number(event.key) - 1);
}
});

updateProgress();
renderQuestion(false);
})();
Loading
Loading