A retro-style quiz game built with React, Vite, and NES.css, featuring a Google Sheets backend for question management and score tracking.
- π¨ Pixel Art Style: Retro 8-bit aesthetics using NES.css.
- π± Responsive Design: Works on desktop and mobile.
- π Dynamic Content: Loads questions from Google Sheets.
- πΌοΈ Avatar Integration: 100+ unique pixel avatars from DiceBear.
- π Score Tracking: Saves high scores and attempts to Google Sheets.
-
Install Dependencies
npm install
-
Run Locally
npm run dev
This game uses Google Sheets as a database. Follow these steps to set it up.
- Go to Google Sheets and create a new sheet.
- Rename the active sheet (tab) at the bottom to
Questions. - Create a second sheet (click the
+icon) and rename it toAnswer.
Sheet 1: Questions
Add these headers to the first row:
| A | B | C | D | E | F | G |
|---|---|---|---|---|---|---|
| Question Number | Question Title | A | B | C | D | Answer |
Sheet 2: Answer
Add these headers to the first row:
| A | B | C | D | E | F |
|---|---|---|---|---|---|
| ID | Total Score | Highest Score | First Pass Score | Number of Attempts | Recent Playtime |
- In your Google Sheet, click Extensions > Apps Script.
- Delete any code in the
Code.gsfile and paste the following:
/* Google Apps Script for Pixel Quiz Game */
const SCRIPT_PROP = PropertiesService.getScriptProperties();
function setup() {
const doc = SpreadsheetApp.getActiveSpreadsheet();
SCRIPT_PROP.setProperty("key", doc.getId());
}
function doGet(e) {
// Handle GET requests (fetching questions)
const action = e.parameter.action;
if (action === 'getQuestions') {
return getQuestions(e.parameter.count);
}
return ContentService.createTextOutput("Invalid Action");
}
function doPost(e) {
// Handle POST requests (submitting scores)
try {
const data = JSON.parse(e.postData.contents);
return recordScore(data);
} catch (err) {
return ContentService.createTextOutput(JSON.stringify({result: "error", error: err.toString()}));
}
}
function getQuestions(count) {
const doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key"));
const sheet = doc.getSheetByName("Questions");
const rows = sheet.getDataRange().getValues();
rows.shift(); // Remove header row
// Shuffle and select N questions
const shuffled = rows.sort(() => 0.5 - Math.random());
const selected = shuffled.slice(0, count || 5);
const questions = selected.map(row => ({
id: row[0],
title: row[1],
options: [row[2], row[3], row[4], row[5]],
answer: row[6]
}));
return ContentService.createTextOutput(JSON.stringify(questions))
.setMimeType(ContentService.MimeType.JSON);
}
function recordScore(data) {
const doc = SpreadsheetApp.openById(SCRIPT_PROP.getProperty("key"));
const sheet = doc.getSheetByName("Answer");
const rows = sheet.getDataRange().getValues();
const id = data.id.toString(); // Ensure ID is string comparison
let rowIndex = -1;
// Search for existing user ID
for (let i = 1; i < rows.length; i++) {
if (String(rows[i][0]) === id) {
rowIndex = i + 1;
break;
}
}
const now = new Date();
if (rowIndex === -1) {
// New User: Append row
sheet.appendRow([id, data.score, data.score, data.passed ? data.score : "", 1, now]);
} else {
// Existing User: Update stats
const stats = rows[rowIndex-1];
const currentHigh = stats[2];
const firstPass = stats[3];
const attempts = stats[4] + 1;
const newHigh = Math.max(currentHigh, data.score);
const newFirstPass = (firstPass === "" && data.passed) ? data.score : firstPass;
sheet.getRange(rowIndex, 2, 1, 5).setValues([[data.score, newHigh, newFirstPass, attempts, now]]);
}
return ContentService.createTextOutput(JSON.stringify({result: "success"})).setMimeType(ContentService.MimeType.JSON);
}- Inside the script editor, click the Save icon (disk).
- Run the
setupfunction once:- Select
setupfrom the dropdown menu (next to "Debug"). - Click Run.
- Grant the necessary permissions when prompted.
- Select
- Click Deploy (blue button top right) > New deployment.
- Click the "Select type" gear icon > Web app.
- Fill in the details:
- Description: Pixel Game API
- Execute as: Me (your email)
- Who has access: Anyone (Crucial for the game to work without login)
- Click Deploy.
- Copy the Web App URL.
- Open the
.envfile in this project. - Paste your URL:
VITE_GOOGLE_APPS_SCRIPT_URL=https://script.google.com/macros/s/YOUR_DEPLOYMENT_ID/exec - Restart your local server (
npm run dev) if it was running.
This project includes a GitHub Action to automatically build and deploy the game to GitHub Pages.
- Initialize git and push your code to a new GitHub repository.
- Go to your GitHub repository Settings > Secrets and variables > Actions.
- Click New repository secret.
- Add the secret:
- Name:
VITE_GOOGLE_APPS_SCRIPT_URL - Value: Your Web App URL (ending in
/exec)
- Name:
- (Optional) Click Variables tab > New repository variable to adjust game settings:
VITE_PASS_THRESHOLD(default: 3)VITE_QUESTION_COUNT(default: 5)
- Go to Settings > Pages.
- Under Build and deployment, select Source as GitHub Actions.
- The workflow will run automatically on your next push to
main.
Here are 10 questions formatted for the Questions sheet:
| Question Number | Question Title | A | B | C | D | Answer |
|---|---|---|---|---|---|---|
| 1 | What is the core architecture behind most modern LLMs? | CNN | RNN | Transformer | LSTM | Transformer |
| 2 | What is the role of "Temperature" in LLM outputs? | Controls generation speed | Controls randomness | Controls context length | Controls model size | Controls randomness |
| 3 | What is a "Token" in the context of LLMs? | A crypto coin | A user API key | A basic unit of text | A model weight | A basic unit of text |
| 4 | Which technique involves training a model on specific data after initial pre-training? | Fine-tuning | Pre-training | RAG | Zero-shot | Fine-tuning |
| 5 | What does "Hallucination" refer to in AI? | The AI becoming sentient | Generating confident but false info | Crashing due to errors | Seeing images in text | Generating confident but false info |
| 6 | What is RAG? | Retrieval-Augmented Generation | Random Access Generation | Real-time AI Generation | Robot Augmentation Gear | Retrieval-Augmented Generation |
| 7 | Which model type is primarily used for Image Generation? | Transformer | Diffusion Model | Linear Regression | K-Means | Diffusion Model |
| 8 | What is "Zero-shot learning"? | Learning with 0 epochs | Learning from 0 data | Performing tasks without examples | Firing 0 neurons | Performing tasks without examples |
| 9 | What is the Context Window? | The GUI of the AI | The limit of input/output tokens | The training duration | The server latency | The limit of input/output tokens |
| 10 | What is the primary purpose of RLHF? | Aligning AI with human feedback | Increasing training speed | Reducing model size | Adding image capabilities | Aligning AI with human feedback |