Skip to content

About

Resources

Stars

11 stars

Watchers

1 watching

Forks

Repository files navigation

ggplot2 Battles

Interactive challenges to master data visualization with ggplot2. Practice your R skills with real datasets in a browser-based environment - no installation required!

🌐 Access the Site

Visit ggplotbattles.dev to start battling!

📖 About

ggplot battles presents you with target plots created with ggplot2. Your mission: recreate them as closely as possible using R code directly in your browser. Each challenge includes:

  • Real datasets including penguins, mpg, and more
  • Instant feedback with separate pixel and code similarity scores
  • Browser-based R environment powered by webR
  • Multiple difficulty levels for all skill ranges

🤝 Contributing New Challenges

We welcome contributions! Here's how to add a new challenge:

1. Create the Challenge File

Create a new .R file in the challenges-code/ directory with the following structure:

#| title: "Your Challenge Title"
#| dataset-name: "dataset_name"  
#| description: "Brief description of what the challenge teaches or focuses on"
#| colours: "none"  # or describe color scheme if relevant
#| plot-variable: "p"  # variable name that contains the final plot

library(ggplot2)
# Add other required libraries

# Load your dataset
data(your_dataset, package = "package_name")

# Your ggplot2 code here
p <- ggplot(your_dataset, aes(...)) +
  geom_...() +
  # ... rest of your plot code

# IMPORTANT: The plot must be printed!
print(p)

2. Hashpipe Syntax Requirements

Use these special comments at the top of your file:

  • #| title: - Display name for the challenge
  • #| dataset-name: - Name of the dataset (shown to users)
  • #| description: - What the challenge teaches/focuses on
  • #| colours: - Color information (use "none" if not applicable)
  • #| plot-variable: - Variable containing the final plot (optional, defaults to last_plot())

3. Important Requirements

  • The plot must be printed - Either use print(p) or just p on its own line
  • The file must be entirely self-contained - No external dependencies or files. It can be as long as you like, so feel free to include long data frames if needed.
  • Set white backgrounds - Ensure plots have white backgrounds for consistency

4. Testing the generation of Images and Manifest

After adding your challenge file, run:

source("printer.R")

This will:

  • Generate a PNG image of your plot in challenges-images/
  • Update the manifest.json with your challenge metadata

🛠 Technical Details

  • Frontend: HTML, CSS, JavaScript with Bootstrap
  • R Environment: webR for browser-based R
  • Plotting: ggplot2 with various extension packages
  • Comparison: Canvas-based pixel similarity plus syntax-tree overlap scoring for plot code
  • Leaderboard: Vercel API route backed by Supabase Postgres

🎨 Local Development

  1. Clone the repository
  2. Serve the files with a local web server (required for webR)
  3. For new challenges, run printer.R to regenerate images and manifest
# Example with Python
python -m http.server 8000

# Example with Node.js
npx http-server

🏝 Archipelago World

This repository also contains a procedural Archipelago world and browser client. The client builds independently from archipelago/ for deployment at ap.ggplotbattles.dev; the normal website remains at the repository root.

cd archipelago
npm test
npm run build

cd ../apworld
python -m unittest discover -s tests -v

Copy apworld/ggplot_battles into an Archipelago source checkout's worlds/ directory and use the launcher's Build APWorlds component to create the distributable package. Install that package through the Archipelago launcher and use apworld/examples/ggplot_battles.yaml to generate a slot.

🏆 Leaderboard Setup

The leaderboard is intentionally account-free: users enter a display name, then the site stores the display name, score, date, challenge id, and submitted code.

  1. Create a Supabase project.
  2. Run supabase/leaderboard.sql in the Supabase SQL editor.
  3. Deploy the site to Vercel so api/leaderboard.js is available.
  4. Add these Vercel environment variables:
SUPABASE_URL=https://your-project-ref.supabase.co
SUPABASE_SERVICE_ROLE_KEY=your-service-role-key
LEADERBOARD_RATE_LIMIT_MAX=5
LEADERBOARD_RATE_LIMIT_WINDOW_SECONDS=600
LEADERBOARD_RATE_LIMIT_SALT=change-me
LEADERBOARD_DELETE_TOKEN_SALT=change-me-too

If you already have a leaderboard table, run the updated SQL again to add the delete_token_hash column. The SQL also reloads Supabase's REST schema cache; without that, score submission may temporarily fail with a missing-column schema cache error.

The API validates names, scores, challenge ids, and code length. It hashes the request IP before storing it for basic per-challenge rate limiting, and the browser renders leaderboard text with textContent. New submissions also get a temporary remove token stored in sessionStorage, so users can remove entries they submitted in the current browser tab without needing accounts.

📄 License

Open source project for educational purposes.

👨‍💻 Created By

Made by Michael Lydeamore

Powered by webR and ggplot2

About

Resources

Stars

11 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages