├── public
├── robots.txt
├── favicon.ico
└── sitemap.xml
├── firestore.indexes.json
├── .postcssrc
├── database.rules.json
├── .vscode
└── extensions.json
├── src
├── assets
│ ├── images
│ │ ├── algobanner.png
│ │ ├── algologofull.png
│ │ └── googleg.svg
│ ├── fonts
│ │ └── Poppins-Medium.ttf
│ ├── data
│ │ ├── update.json
│ │ └── gather.mjs
│ └── css
│ │ └── style.css
├── App.vue
├── toast.js
├── app-config.js
├── components
│ ├── Tooltip.vue
│ ├── Banner.vue
│ ├── Footer.vue
│ ├── Loader.vue
│ ├── Toast.vue
│ ├── HomeStatistics.vue
│ ├── FeedbackModal.vue
│ ├── Theme.vue
│ └── Navbar.vue
├── main.js
├── views
│ ├── 404View.vue
│ ├── AboutView.vue
│ ├── HomeView.vue
│ ├── ProblemView.vue
│ ├── ProfileView.vue
│ ├── LoginView.vue
│ ├── StatisticsView.vue
│ ├── SolveView.vue
│ └── MockView.vue
└── Router.js
├── storage.rules
├── .prettierrc.json
├── firestore.rules
├── .firebaserc
├── vite.config.mjs
├── tailwind.config.js
├── .gitpod.yml
├── .github
├── dependabot.yml
├── ISSUE_TEMPLATE
│ ├── feature_request.md
│ └── bug_report.md
└── workflows
│ └── firebase-hosting-pull-request.yml
├── eslint.config.js
├── firebase.json
├── index.html
├── package.json
├── README.md
└── .gitignore
/public/robots.txt:
--------------------------------------------------------------------------------
1 | User-agent: *
2 | Allow: /
--------------------------------------------------------------------------------
/firestore.indexes.json:
--------------------------------------------------------------------------------
1 | {
2 | "indexes": [],
3 | "fieldOverrides": []
4 | }
5 |
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IMGROOT2/algo/HEAD/public/favicon.ico
--------------------------------------------------------------------------------
/.postcssrc:
--------------------------------------------------------------------------------
1 | {
2 | "plugins": {
3 | "tailwindcss": {},
4 | "autoprefixer": {}
5 | }
6 | }
--------------------------------------------------------------------------------
/database.rules.json:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | ".read": false,
4 | ".write": false
5 | }
6 | }
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"]
3 | }
4 |
--------------------------------------------------------------------------------
/src/assets/images/algobanner.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IMGROOT2/algo/HEAD/src/assets/images/algobanner.png
--------------------------------------------------------------------------------
/src/assets/images/algologofull.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IMGROOT2/algo/HEAD/src/assets/images/algologofull.png
--------------------------------------------------------------------------------
/src/assets/fonts/Poppins-Medium.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/IMGROOT2/algo/HEAD/src/assets/fonts/Poppins-Medium.ttf
--------------------------------------------------------------------------------
/storage.rules:
--------------------------------------------------------------------------------
1 | rules_version = '2';
2 | service firebase.storage {
3 | match /b/{bucket}/o {
4 | match /{allPaths=**} {
5 | allow read, write: if false;
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://json.schemastore.org/prettierrc",
3 | "semi": false,
4 | "tabWidth": 2,
5 | "singleQuote": true,
6 | "printWidth": 100,
7 | "trailingComma": "none"
8 | }
--------------------------------------------------------------------------------
/firestore.rules:
--------------------------------------------------------------------------------
1 | rules_version = '2';
2 | service cloud.firestore {
3 | match /databases/{database}/documents {
4 | match /user_data/{userId} {
5 | allow read, write: if request.auth != null && request.auth.uid == userId;
6 | }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/assets/data/update.json:
--------------------------------------------------------------------------------
1 | {
2 | "analytics": {
3 | "users": "4,000",
4 | "views": "17,000",
5 | "countries": "90"
6 | },
7 | "release": {
8 | "version": "Banner is turned off.",
9 | "text": "Uncomment it in Banner.vue to turn it on."
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/.firebaserc:
--------------------------------------------------------------------------------
1 | {
2 | "projects": {
3 | "default": "algo-usaco"
4 | },
5 | "targets": {
6 | "algo-usaco": {
7 | "hosting": {
8 | "rd": [
9 | "algo-usaco"
10 | ],
11 | "algo-usaco": [
12 | "algo-usaco"
13 | ]
14 | }
15 | }
16 | },
17 | "etags": {}
18 | }
19 |
--------------------------------------------------------------------------------
/vite.config.mjs:
--------------------------------------------------------------------------------
1 | import { fileURLToPath, URL } from 'node:url'
2 | import { defineConfig } from 'vite'
3 | import vue from '@vitejs/plugin-vue'
4 |
5 | // https://vitejs.dev/config/
6 | export default defineConfig({
7 | plugins: [
8 | vue(),
9 | ],
10 | resolve: {
11 | alias: {
12 | '@': fileURLToPath(new URL('./src', import.meta.url))
13 | }
14 | }
15 | })
--------------------------------------------------------------------------------
/tailwind.config.js:
--------------------------------------------------------------------------------
1 | /** @type {import('tailwindcss').Config} */
2 | export default {
3 | content: [
4 | "./index.html",
5 | "./src/**/*.{vue,js,ts,jsx,tsx}",
6 | ],
7 | theme: {
8 | extend: {
9 | backgroundSize: {
10 | 'size-200': '200% 200%',
11 | },
12 | backgroundPosition: {
13 | 'pos-0': '0% 0%',
14 | 'pos-100': '100% 100%',
15 | },
16 | },
17 | },
18 | plugins: [],
19 | darkMode: "class"
20 | }
--------------------------------------------------------------------------------
/.gitpod.yml:
--------------------------------------------------------------------------------
1 | # This configuration file was automatically generated by Gitpod.
2 | # Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml)
3 | # and commit this file to your remote git repository to share the goodness with others.
4 |
5 | # Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart
6 |
7 | tasks:
8 | - init: npm install && npm run dev
9 | command: npm run dev
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # To get started with Dependabot version updates, you'll need to specify which
2 | # package ecosystems to update and where the package manifests are located.
3 | # Please see the documentation for all configuration options:
4 | # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file
5 |
6 | version: 2
7 | updates:
8 | - package-ecosystem: "npm"
9 | directory: "/"
10 | schedule:
11 | interval: "weekly"
12 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
22 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/workflows/firebase-hosting-pull-request.yml:
--------------------------------------------------------------------------------
1 | # This file was auto-generated by the Firebase CLI
2 | # https://github.com/firebase/firebase-tools
3 |
4 | name: Deploy to Firebase Hosting on PR
5 | 'on': pull_request
6 | jobs:
7 | build_and_preview:
8 | if: '${{ github.event.pull_request.head.repo.full_name == github.repository }}'
9 | runs-on: ubuntu-latest
10 | steps:
11 | - uses: actions/checkout@v3
12 | - run: npm ci && npm run build
13 | - uses: FirebaseExtended/action-hosting-deploy@v0
14 | with:
15 | repoToken: '${{ secrets.GITHUB_TOKEN }}'
16 | firebaseServiceAccount: '${{ secrets.FIREBASE_SERVICE_ACCOUNT_ALGO_USACO }}'
17 | projectId: algo-usaco
18 |
--------------------------------------------------------------------------------
/src/toast.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import Toast from './components/Toast.vue'
3 |
4 | let toastCount = 0
5 |
6 | export default function createToast(message, icon, id = `toast-${++toastCount}`) {
7 | // create a new instance of the Toast component with the message and icon props
8 | const toast = createApp(Toast, {
9 | message,
10 | icon
11 | })
12 |
13 | // mount the component to an element
14 | const toastDiv = document.getElementById('div-toast')
15 | const component = toast.mount(document.createElement('div'))
16 |
17 | // set the ID of the component's root element
18 | component.$el.id = id
19 |
20 | // append the component's root element to the toast div
21 | toastDiv.appendChild(component.$el)
22 |
23 | // return the ID of the toast
24 | const toastId = id
25 |
26 | return toastId
27 | }
28 |
--------------------------------------------------------------------------------
/src/assets/images/googleg.svg:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
8 |
10 |
11 |
--------------------------------------------------------------------------------
/src/app-config.js:
--------------------------------------------------------------------------------
1 | import { initializeApp } from 'firebase/app'
2 | import { getAuth } from 'firebase/auth'
3 | import { getAnalytics } from 'firebase/analytics'
4 | import { getFirestore } from 'firebase/firestore'
5 |
6 | export const firebaseConfig = {
7 | apiKey: 'AIzaSyAVd0HLl7_NMyeE5Msmg2eX1hqesGoyXRQ',
8 | authDomain: 'algo-usaco.firebaseapp.com',
9 | databaseURL: 'https://algo-usaco-default-rtdb.firebaseio.com',
10 | projectId: 'algo-usaco',
11 | storageBucket: 'algo-usaco.appspot.com',
12 | messagingSenderId: '56776411397',
13 | appId: '1:56776411397:web:5dcff79b859fd7f7ca4f6c',
14 | measurementId: 'G-KYL07WP16G'
15 | }
16 | export const app = initializeApp(firebaseConfig)
17 | export const analytics = getAnalytics(app)
18 | export const auth = getAuth(app)
19 | export const db = getFirestore(app)
20 | //
21 | // connectFirestoreEmulator(db, "localhost", 8080);
22 |
--------------------------------------------------------------------------------
/public/sitemap.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | https://algousaco.com/
4 | 2024-09-21T19:05:13+00:00
5 |
6 |
7 | https://algousaco.com/solve
8 | 2024-09-21T19:05:13+00:00
9 |
10 |
11 | https://algousaco.com/mock
12 | 2024-09-21T19:05:13+00:00
13 |
14 |
15 | https://algousaco.com/login
16 | 2024-09-21T19:05:13+00:00
17 |
18 |
19 | https://algousaco.com/profile
20 | 2024-09-21T19:05:13+00:00
21 |
22 |
23 | https://algousaco.com/statistics
24 | 2024-09-21T19:05:13+00:00
25 |
26 |
27 | https://algousaco.com/about
28 | 2024-09-21T19:05:13+00:00
29 |
30 |
--------------------------------------------------------------------------------
/src/components/Tooltip.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
12 | {{ message }}
13 |
14 |
15 |
16 |
17 |
30 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/src/components/Banner.vue:
--------------------------------------------------------------------------------
1 |
2 |
20 |
21 |
22 |
25 |
--------------------------------------------------------------------------------
/eslint.config.js:
--------------------------------------------------------------------------------
1 | import { includeIgnoreFile } from "@eslint/compat";
2 | import path from "node:path";
3 | import { fileURLToPath } from "node:url";
4 | import vue from 'eslint-plugin-vue';
5 | import prettier from 'eslint-config-prettier';
6 |
7 | const __filename = fileURLToPath(import.meta.url);
8 | const __dirname = path.dirname(__filename);
9 | const gitignorePath = path.resolve(__dirname, ".gitignore");
10 |
11 | export default [
12 | includeIgnoreFile(gitignorePath),
13 | ...vue.configs['flat/essential'],
14 |
15 | {
16 | files: ['**/*.{js,cjs,mjs,ts,tsx}'],
17 | rules: {
18 | 'no-unused-vars': 'warn',
19 | 'no-console': 'off',
20 | },
21 | languageOptions: {
22 | ecmaVersion: 'latest',
23 | },
24 | },
25 |
26 | {
27 | files: ['**/*.vue'],
28 | plugins: {
29 | vue,
30 | },
31 | rules: {
32 | ...vue.configs['vue3-essential'].rules,
33 | 'vue/multi-word-component-names': 'off',
34 | },
35 | languageOptions: {
36 | parserOptions: {
37 | ecmaVersion: 'latest',
38 | },
39 | },
40 | },
41 |
42 | {
43 | plugins: {
44 | prettier,
45 | },
46 | rules: {
47 | ...prettier.rules,
48 | },
49 | },
50 | ];
51 |
--------------------------------------------------------------------------------
/firebase.json:
--------------------------------------------------------------------------------
1 | {
2 | "firestore": {
3 | "rules": "firestore.rules",
4 | "indexes": "firestore.indexes.json"
5 | },
6 | "hosting": {
7 | "public": "dist",
8 | "ignore": [
9 | "firebase.json",
10 | "**/.*",
11 | "**/node_modules/**"
12 | ],
13 | "cleanUrls": true,
14 | "rewrites": [
15 | {
16 | "source": "**",
17 | "destination": "/index.html"
18 | }
19 | ]
20 | },
21 | "emulators": {
22 | "auth": {
23 | "port": 9099
24 | },
25 | "firestore": {
26 | "port": 8080
27 | },
28 | "hosting": {
29 | "port": 5000
30 | },
31 | "ui": {
32 | "enabled": true
33 | },
34 | "singleProjectMode": true,
35 | "functions": {
36 | "port": 5001
37 | }
38 | },
39 | "remoteconfig": {},
40 | "functions": [
41 | {
42 | "source": "functions",
43 | "codebase": "default",
44 | "ignore": [
45 | "node_modules",
46 | ".git",
47 | "firebase-debug.log",
48 | "firebase-debug.*.log"
49 | ],
50 | "predeploy": [
51 | "npm --prefix \"$RESOURCE_DIR\" run lint"
52 | ]
53 | }
54 | ],
55 | "database": {
56 | "rules": "database.rules.json"
57 | },
58 | "storage": {
59 | "rules": "storage.rules"
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 | Algo
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/src/components/Footer.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
12 |
13 |
14 |
15 |
18 |
24 |
25 |
26 |
·
27 |
About
28 |
·
29 |
Made by
30 |
Ruhan Gupta
33 |
34 |
35 |
36 |
37 |
38 |
42 |
--------------------------------------------------------------------------------
/src/components/Loader.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
15 |
19 |
20 |
Loading...
21 |
22 |
23 |
28 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import { auth, db } from './app-config'
2 | import { deleteField, doc, getDoc, updateDoc } from 'firebase/firestore'
3 | import { onAuthStateChanged } from 'firebase/auth'
4 | import { createApp } from 'vue'
5 | import App from './App.vue'
6 | import router from './Router'
7 | import '@/assets/css/style.css'
8 |
9 | if (location.host === 'algo-usaco.web.app') location.host = 'algousaco.com'
10 |
11 | const app = createApp(App)
12 | app.use(router)
13 | app.mount('#app')
14 |
15 | onAuthStateChanged(auth, async (user) => {
16 | if (user) {
17 | if (!localStorage.getItem('hasCheckedDatabase')) {
18 | await retrieveUserDoc(db, user).then((adoc) => {
19 | console.log(adoc.data())
20 | const fsdata = adoc.data()
21 | if (!Object.prototype.hasOwnProperty.call(fsdata, 'problemsSeen')) {
22 | updateDoc(doc(db, 'user_data', user.uid), {
23 | problemsSeen: [],
24 | problemsSolved: [],
25 | problemsSkipped: [],
26 | problemsUnsolved: []
27 | })
28 | }
29 | if (Object.prototype.hasOwnProperty.call(fsdata, 'problems-seen')) {
30 | updateDoc(doc(db, 'user_data', user.uid), {
31 | 'problems-seen': deleteField(),
32 | 'problems-solved': deleteField(),
33 | 'problems-skipped': deleteField(),
34 | 'problems-unsolved': deleteField()
35 | })
36 | }
37 | })
38 | localStorage.setItem('hasCheckedDatabase', 'true')
39 | }
40 | }
41 | })
42 |
43 | async function retrieveUserDoc(db, user) {
44 | return await getDoc(doc(db, 'user_data', user.uid))
45 | }
46 |
--------------------------------------------------------------------------------
/src/views/404View.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | 4
11 | 0
16 | 4
21 |
22 |
Oops! Page not found.
23 |
24 | The page you are looking for might not exist or may be temporarily unavailable.
25 |
26 |
Go back to
31 |
32 |
33 |
36 |
55 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "algo",
3 | "version": "1.0.0",
4 | "description": "Algo is a web application that supercharges your training for the USACO by randomly generating problems from past contests.",
5 | "browserslist": "> 0.1% and last 2 versions and not dead",
6 | "type": "module",
7 | "scripts": {
8 | "dev": "vite",
9 | "build": "vite build",
10 | "preview": "vite preview",
11 | "emulator": "firebase emulators:start --only auth,firestore,hosting:algo-usaco --import emulator-data",
12 | "emulator-no-import": "firebase emulators:start --only auth,firestore,hosting:algo-usaco",
13 | "lint": "eslint \"**/*.{vue,js,jsx,cjs,mjs,ts,tsx}\" --fix",
14 | "format": "prettier --write src/",
15 | "deploy": "firebase deploy --only hosting,storage,firestore,database",
16 | "publish": "npm run clean && vite build && npm run deploy",
17 | "clean": "npm run lint && npm run format"
18 | },
19 | "repository": {
20 | "type": "git",
21 | "url": "git+https://github.com/IMGROOT2/algo.git"
22 | },
23 | "author": "Ruhan Gupta",
24 | "license": "None",
25 | "bugs": {
26 | "url": "https://github.com/IMGROOT2/algo/issues"
27 | },
28 | "homepage": "https://github.com/IMGROOT2/algo#readme",
29 | "dependencies": {
30 | "chart.js": "^4.4.4",
31 | "firebase": "^10.13.2",
32 | "firebase-tools": "^13.18.0",
33 | "tailwindcss": "^3.4.12",
34 | "vue": "^3.5.7",
35 | "vue-router": "^4.4.5"
36 | },
37 | "devDependencies": {
38 | "@eslint/compat": "^1.1.1",
39 | "@rushstack/eslint-patch": "^1.10.4",
40 | "@vitejs/plugin-vue": "^5.1.4",
41 | "@vue/eslint-config-prettier": "^9.0.0",
42 | "autoprefixer": "^10.4.20",
43 | "eslint": "^9.11.0",
44 | "eslint-plugin-vue": "^9.28.0",
45 | "jsdom": "^25.0.0",
46 | "postcss": "^8.4.47",
47 | "prettier": "^3.3.3",
48 | "vite": "^5.4.7"
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/src/components/Toast.vue:
--------------------------------------------------------------------------------
1 |
2 |
8 |
11 |
12 | Icon
13 |
14 |
{{ message }}
15 |
21 | Close
22 |
29 |
36 |
37 |
38 |
39 |
40 |
41 |
60 |
61 |
68 |
--------------------------------------------------------------------------------
/src/assets/css/style.css:
--------------------------------------------------------------------------------
1 | /* START core */
2 |
3 | @tailwind base;
4 | @tailwind components;
5 | @tailwind utilities;
6 |
7 | @font-face {
8 | font-family: Poppins-M;
9 | src: url(../fonts/Poppins-Medium.ttf);
10 | }
11 |
12 | /* END core */
13 |
14 | body,
15 | html {
16 | font-family: Poppins-M;
17 | }
18 | ::-webkit-scrollbar {
19 | width: 20px;
20 | }
21 |
22 | ::-webkit-scrollbar-track {
23 | background-color: #1e1d1d;
24 | }
25 |
26 | ::-webkit-scrollbar-thumb {
27 | background-color: #384253;
28 | border-radius: 20px;
29 | border: 6px solid transparent;
30 | background-clip: content-box;
31 | }
32 |
33 | ::-webkit-scrollbar-thumb:hover {
34 | background-color: #62738a;
35 | }
36 |
37 | h4 {
38 | margin-top: 1rem !important;
39 | margin-bottom: 1rem !important;
40 | font-size: 1.6rem !important;
41 | font-weight: 500 !important;
42 | }
43 |
44 | @media screen and (max-width: 768px) {
45 | h4 {
46 | font-size: 1rem;
47 | }
48 | }
49 |
50 | #problem-text p {
51 | padding-top: 0.75rem !important;
52 | margin-bottom: 0.75rem !important;
53 | }
54 |
55 | pre {
56 | min-width: 25%;
57 | max-width: -moz-fit-content;
58 | max-width: fit-content;
59 | height: auto;
60 | border: 1px solid transparent;
61 | border-radius: 0.3rem;
62 | padding: 1rem;
63 | margin-top: 0.2rem;
64 | margin-bottom: 0.2rem;
65 | color: white;
66 | background-color: #70747a;
67 | overflow-x: auto; /* Changed from scroll to auto */
68 | }
69 |
70 | @media screen and (max-width: 768px) {
71 | pre {
72 | min-width: 50% !important;
73 | }
74 | }
75 |
76 | strong {
77 | color: white;
78 | }
79 |
80 | .page-link {
81 | text-decoration: underline !important;
82 | text-decoration-color: #3072d6 !important;
83 | }
84 | .page-link:hover {
85 | color: #3072d6 !important;
86 | }
87 |
88 | .is-bronze {
89 | background: #864d16 !important;
90 | }
91 | .is-silver {
92 | background: #4d4b4b !important;
93 | }
94 | .is-gold {
95 | background: #f1cb09 !important;
96 | }
97 | .is-platinum {
98 | background: #50a1cc !important;
99 | }
100 |
--------------------------------------------------------------------------------
/src/assets/data/gather.mjs:
--------------------------------------------------------------------------------
1 | import fs from 'fs'
2 | import { JSDOM } from 'jsdom'
3 |
4 | const data = JSON.parse(fs.readFileSync('data.json', 'utf8'))
5 |
6 | ;(async () => {
7 | for (let i = 0; i <= 2000; i++) {
8 | // 2000 is a rough max of CPIDs
9 | try {
10 | console.log('Trying ' + i + '...')
11 | const response = await fetch('http://usaco.org/index.php?page=viewproblem2&cpid=' + i)
12 | const body = await response.text()
13 | const dom = new JSDOM(body)
14 | const problem = dom.window.document.getElementById('probtext-text').innerHTML
15 | const subtitle = body.match(/(.*?)<\/h2>/)[1].trim()
16 | // set subtitle to the innertext of the second element
17 | const title = dom.window.document.getElementsByTagName('h2')[1].innerHTML.trim()
18 | const number = body.match(/Problem.*?(\d+)/)[1]
19 | const division = subtitle.match(/Bronze|Silver|Gold|Platinum/)[0].toLowerCase()
20 | const year = subtitle.match(/20\d\d/)[0]
21 | if (year > 2015) {
22 | console.log('ID ' + i + ' is in ' + year + ' and is ' + division + '.')
23 | if (data[division].indexOf(i) === -1) {
24 | console.log(subtitle)
25 | var toPush = {
26 | title: title,
27 | subtitle: subtitle,
28 | id: i,
29 | year: year,
30 | division: division,
31 | number: number,
32 | problem: problem,
33 | url: 'http://usaco.org/index.php?page=viewproblem2&cpid=' + i
34 | }
35 | data[division].push(toPush)
36 | console.log('Added to ' + division + '.')
37 | }
38 | fs.writeFileSync('data.json', JSON.stringify(data, null, 4))
39 | } else {
40 | console.log('ID ' + i + ' is too old (' + year + ').')
41 | }
42 | } catch (e) {
43 | console.log(e.message)
44 | console.log('ID ' + i + " doesn't exist.")
45 | }
46 | }
47 | console.log('Done!')
48 | })()
49 |
50 | // {
51 | // "bronze": [
52 |
53 | // ],
54 | // "silver": [
55 |
56 | // ],
57 | // "gold": [
58 |
59 | // ],
60 | // "platinum": [
61 |
62 | // ]
63 | // }
64 |
--------------------------------------------------------------------------------
/src/components/HomeStatistics.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
Users
7 |
8 | {{ update.analytics.users }}+
17 |
18 |
19 |
20 |
Page Views
21 |
22 | {{ update.analytics.views }}+
31 |
32 |
33 |
34 |
Countries Reached
35 |
36 | {{ update.analytics.countries }}+
44 |
45 |
46 |
47 |
48 |
49 |
50 |
53 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
USACO problem randomizer and trainer built for competitive programming enthusiasts.
4 |
5 |
6 |
7 | About Algo
8 | As more people get into competitive programming and the USACO, more resources are available to help you prepare for the competition. However, there was one piece missing- practice. It's essential to learn the concepts and techniques, but it's equally, if not more important, to practice them. Until I created Algo, there were two options: randomly clicking on previous contests' problems or using train.usaco.org.
9 |
10 | However, clicking on problems randomly is inefficient, and train.usaco.org is incredibly outdated (most of the website hasn't had a refresh or update since the early 2010s). So, I created Algo to help people practice USACO problems and help them get ready for the USACO.
11 |
12 | Algo is a web application designed to supercharge your USACO training. It randomly generates problems from past contests so that you can practice them. Algo will process and show the problems in a simple, clean interface. After solving the problem, you can submit your solution to the USACO website to check if your program works.
13 | Tech Stack
14 | Algo is built with the following:
15 |
24 |
25 | Contact, Feedback, Bugs, Get In Touch
26 | If you need to:
27 |
28 |
29 | Contact Me
30 | Submit Feedback
31 | Make a Bug Report
32 | Something Else
33 |
34 |
35 |
36 |
37 |
38 |
42 |
--------------------------------------------------------------------------------
/src/Router.js:
--------------------------------------------------------------------------------
1 | import { createRouter, createWebHistory } from 'vue-router'
2 | import Home from './views/HomeView.vue'
3 | const routes = [
4 | {
5 | path: '/',
6 | name: 'Home',
7 | meta: {
8 | description:
9 | 'Algo is a web application that supercharges your training for the USACO by randomly generating problems from past contests.'
10 | },
11 | component: Home
12 | },
13 | {
14 | path: '/profile',
15 | name: 'Profile',
16 | meta: {
17 | description: 'View and edit your Algo profile.'
18 | },
19 | component: () => import('./views/ProfileView.vue')
20 | },
21 | {
22 | path: '/solve',
23 | name: 'Solve',
24 | meta: {
25 | description: 'Solve USACO problems and track your progress with Algo.'
26 | },
27 | component: () => import('./views/SolveView.vue')
28 | },
29 | {
30 | path: '/about',
31 | name: 'About',
32 | meta: {
33 | description:
34 | 'About Algo, the web application that supercharges your training for the USACO by randomly generating problems from past contests.'
35 | },
36 | component: () => import('./views/AboutView.vue')
37 | },
38 | {
39 | path: '/statistics',
40 | name: 'Statistics',
41 | meta: {
42 | description: 'View and track your progress on Algo through statistics and graphs.'
43 | },
44 | component: () => import('./views/StatisticsView.vue')
45 | },
46 | {
47 | path: '/problem/:id',
48 | name: 'Problem - Algo',
49 | meta: {
50 | description: 'View a USACO Problem in Algo.'
51 | },
52 | props: true,
53 | component: () => import('./views/ProblemView.vue')
54 | },
55 | {
56 | path: '/login',
57 | name: 'Log in',
58 | meta: {
59 | description: 'Log in or register for Algo to save your progress and access more features.'
60 | },
61 | component: () => import('./views/LoginView.vue')
62 | },
63 | {
64 | path: '/mock',
65 | name: 'Mock Contest',
66 | meta: {
67 | description: 'Take a mock USACO contest with Algo.'
68 | },
69 | component: () => import('./views/MockView.vue')
70 | },
71 | {
72 | path: '/:pathMatch(.*)*',
73 | name: '404 Not Found',
74 | meta: {
75 | description: "Uh oh! The page you're looking for doesn't exist."
76 | },
77 | component: () => import('./views/404View.vue')
78 | }
79 | ]
80 | const router = createRouter({
81 | history: createWebHistory(import.meta.env.BASE_URL),
82 | routes
83 | })
84 |
85 | router.afterEach((to) => {
86 | if (to.path.startsWith('/problem/')) {
87 | const id = to.params.id
88 | document.title = `Problem ${id} - Algo`
89 | } else {
90 | document.title = to.name ? `${to.name} - Algo` : `Algo`
91 | }
92 | // set description meta tag
93 | document.getElementById('description-meta-tag-one')?.setAttribute('content', to.meta.description)
94 | document.getElementById('description-meta-tag-two')?.setAttribute('content', to.meta.description)
95 | })
96 |
97 | export default router
98 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | lerna-debug.log*
8 | .pnpm-debug.log*
9 |
10 | # Diagnostic reports (https://nodejs.org/api/report.html)
11 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
12 |
13 | # Runtime data
14 | pids
15 | *.pid
16 | *.seed
17 | *.pid.lock
18 |
19 | # Directory for instrumented libs generated by jscoverage/JSCover
20 | lib-cov
21 |
22 | # Coverage directory used by tools like istanbul
23 | coverage
24 | *.lcov
25 |
26 | # nyc test coverage
27 | .nyc_output
28 |
29 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
30 | .grunt
31 |
32 | # Bower dependency directory (https://bower.io/)
33 | bower_components
34 |
35 | # node-waf configuration
36 | .lock-wscript
37 |
38 | # Compiled binary addons (https://nodejs.org/api/addons.html)
39 | build/Release
40 |
41 | # Dependency directories
42 | node_modules/
43 | jspm_packages/
44 |
45 | # Snowpack dependency directory (https://snowpack.dev/)
46 | web_modules/
47 |
48 | # TypeScript cache
49 | *.tsbuildinfo
50 |
51 | # Optional npm cache directory
52 | .npm
53 |
54 | # Optional eslint cache
55 | .eslintcache
56 |
57 | # Optional stylelint cache
58 | .stylelintcache
59 |
60 | # Microbundle cache
61 | .rpt2_cache/
62 | .rts2_cache_cjs/
63 | .rts2_cache_es/
64 | .rts2_cache_umd/
65 |
66 | # Optional REPL history
67 | .node_repl_history
68 |
69 | # Output of 'npm pack'
70 | *.tgz
71 |
72 | # Yarn Integrity file
73 | .yarn-integrity
74 |
75 | # dotenv environment variable files
76 | .env
77 | .env.development.local
78 | .env.test.local
79 | .env.production.local
80 | .env.local
81 |
82 | # parcel-bundler cache (https://parceljs.org/)
83 | .cache
84 | .parcel-cache
85 |
86 | # Next.js build output
87 | .next
88 | out
89 |
90 | # Nuxt.js build / generate output
91 | .nuxt
92 | dist
93 |
94 | # Gatsby files
95 | .cache/
96 | # Comment in the src line in if your project uses Gatsby and not Next.js
97 | # https://nextjs.org/blog/next-9-1#public-directory-support
98 | # src
99 |
100 | # vuepress build output
101 | .vuepress/dist
102 |
103 | # vuepress v2.x temp and cache directory
104 | .temp
105 | .cache
106 |
107 | # Docusaurus cache and generated files
108 | .docusaurus
109 |
110 | # Serverless directories
111 | .serverless/
112 |
113 | # FuseBox cache
114 | .fusebox/
115 |
116 | # DynamoDB Local files
117 | .dynamodb/
118 |
119 | # TernJS port file
120 | .tern-port
121 |
122 | # Stores VSCode versions used for testing VSCode extensions
123 | .vscode-test
124 |
125 | # yarn v2
126 | .yarn/cache
127 | .yarn/unplugged
128 | .yarn/build-state.yml
129 | .yarn/install-state.gz
130 | .pnp.*
131 |
132 | # More
133 | .firebase/
134 | .idea/
135 | .parcel-cache/
136 | dist/
137 | emulator-data/
138 |
139 | # Logs
140 | logs
141 | *.log
142 | npm-debug.log*
143 | yarn-debug.log*
144 | yarn-error.log*
145 | pnpm-debug.log*
146 | lerna-debug.log*
147 |
148 | node_modules
149 | .DS_Store
150 | dist
151 | dist-ssr
152 | coverage
153 | *.local
154 |
155 | /cypress/videos/
156 | /cypress/screenshots/
157 |
158 | # Editor directories and files
159 | .vscode/*
160 | !.vscode/extensions.json
161 | .idea
162 | *.suo
163 | *.ntvs*
164 | *.njsproj
165 | *.sln
166 | *.sw?
167 | .vscode/
--------------------------------------------------------------------------------
/src/views/AboutView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | About
5 |
6 |
7 |
8 |
9 |
10 | Why
11 |
12 |
13 | As more people get into competitive programming and the USACO, more resources are
14 | available to help you prepare for the competition. However, there was one piece missing-
15 | practice. It's essential to learn the concepts and techniques, but it's equally, if not
16 | more important, to practice them. Until I created Algo, there were two options: randomly
17 | clicking on previous contests' problems or using train.usaco.org.
18 | However, clicking on problems randomly is inefficient, and train.usaco.org is
20 | incredibly outdated
21 |
22 | (most of the website hasn't had a refresh or update since the early 2010s). So, I
23 | created Algo to help people practice USACO problems and help them get ready for the
24 | USACO.
25 | Algo is a web application designed to supercharge your USACO training. It randomly
26 | generates problems from past contests so that you can practice them. Algo will process
27 | and show the problems in a simple, clean interface. After solving the problem, you can
28 | submit your solution to the USACO website to check if your program works.
29 |
30 |
31 |
32 |
33 |
34 |
35 | Credits
36 |
37 |
38 | Algo is solely created, developed, and maintained by
39 | Ruhan Gupta . For
40 | the full tech stack and libraries, check the
41 | GitHub Repository . Give it a star if you have an account! Thank you to my family and friends for
44 | supporting with Algo, including my brother (Reyansh) for testing.
45 | Algo is © 2023- Ruhan Gupta. All rights reserved.
47 |
48 | USACO problems are retrieved from
49 | usaco.org . All USACO
50 | problem credits go to their respective authors, who are credited at the end of each
51 | problem. I do not take credit or ownership of any USACO problems.
52 |
53 |
54 |
55 |
56 |
57 |
58 |
65 |
--------------------------------------------------------------------------------
/src/components/FeedbackModal.vue:
--------------------------------------------------------------------------------
1 |
2 |
9 |
12 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
Shape the future
25 |
We’d love your voice
26 |
27 |
28 |
29 |
30 |
31 | Got two minutes to share how
32 | Algo can do even more for you? Tell us what would level up your training.Plus, we’ll spotlight you on the About page.
36 |
37 |
38 |
39 |
40 | Suggest improvements or dream up entirely new features.
41 |
42 |
43 |
44 | Be featured for your feedback on Algo's About page!
45 |
46 |
47 |
48 |
53 | Maybe later
54 |
55 |
60 | Share feedback
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
105 |
--------------------------------------------------------------------------------
/src/views/HomeView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
15 |
16 |
20 |
21 |
22 |
32 |
33 |
40 |
45 |
46 |
47 |
48 |
49 |
50 |
68 |
69 |
70 |
71 |
72 | Go
76 | from
77 | Bronze
81 |
82 | to
83 | Platinum
87 | with
88 |
89 |
90 |
91 | Algo is a USACO problem randomizer that pushes your training to the next level.
92 |
93 |
94 |
95 |
98 | Start Solving
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
114 |
--------------------------------------------------------------------------------
/src/components/Theme.vue:
--------------------------------------------------------------------------------
1 |
2 |
5 |
10 |
11 |
12 |
17 |
18 |
19 |
24 |
25 |
26 |
27 |
28 |
123 |
--------------------------------------------------------------------------------
/src/views/ProblemView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
14 |
15 |
19 |
20 | View on usaco.org
21 |
22 |
23 |
24 |
25 |
32 |
33 |
34 |
39 |
40 |
136 |
--------------------------------------------------------------------------------
/src/views/ProfileView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
70 |
71 |
72 |
148 |
--------------------------------------------------------------------------------
/src/views/LoginView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
9 |
10 |
11 |
15 |
18 | Log in
19 |
20 |
24 |
25 |
26 |
27 |
28 |
29 |
Sign in with Google
30 |
31 |
32 |
33 |
38 |
69 |
70 | Forgot your
71 | password?
72 |
73 |
74 | Don't have an account?
75 | Sign up
76 |
77 |
78 |
79 |
83 |
86 | Sign Up
87 |
88 |
92 |
93 |
94 |
95 |
96 |
97 |
Sign up with Google
98 |
99 |
100 |
101 |
102 |
103 |
OR
104 |
105 |
106 |
145 |
146 | Forgot your
147 | password?
148 |
149 |
150 | Already have an account?
151 | Sign in
152 |
153 |
154 |
155 |
159 |
162 | Reset Password
163 |
164 |
183 |
184 | Back to Log in
187 |
188 |
189 |
190 |
191 |
192 |
193 |
369 |
--------------------------------------------------------------------------------
/src/views/StatisticsView.vue:
--------------------------------------------------------------------------------
1 |
2 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Search Problems
14 |
15 |
20 |
27 |
34 |
35 | Close modal
36 |
37 |
38 |
39 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 | Statistics for
75 |
79 |
80 |
81 | Click on a statistic card to view the problems.
82 |
83 |
84 |
85 |
94 |
95 |
96 |
100 |
101 |
102 |
Skipped
103 |
104 |
105 |
106 |
107 |
111 |
112 |
113 |
Unsolved
114 |
115 |
116 |
117 |
118 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
No Problems Seen!
136 |
137 | Go to Solve and solve a few
138 | problems!
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
548 |
--------------------------------------------------------------------------------
/src/components/Navbar.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 | Home
36 | Solve
41 | Mock Contest
49 |
50 |
51 |
52 |
55 |
56 |
57 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | Register - Log In
73 |
95 |
96 |
105 |
118 |
131 |
135 |
136 |
137 |
138 | Profile
139 |
140 |
144 |
145 |
146 |
147 | Statistics
148 |
149 |
154 |
155 |
156 |
157 | Log Out
158 |
159 |
160 |
161 |
162 |
163 |
189 |
190 |
191 |
196 |
197 |
198 |
199 |
200 |
201 |
Search Problems
202 |
207 |
214 |
221 |
222 | Close modal
223 |
224 |
225 |
226 |
251 |
252 |
255 |
260 | What is a USACO Problem ID?
261 |
262 |
263 |
264 |
265 |
266 |
267 |
507 |
512 |
--------------------------------------------------------------------------------
/src/views/SolveView.vue:
--------------------------------------------------------------------------------
1 |
2 |
3 |
11 |
19 |
125 |
130 |
131 |
132 |
137 |
138 | Generate a new problem to get started!
139 |
140 |
141 |
142 |
143 |
144 |
145 |
152 |
153 |
154 |
155 | View on usaco.org
156 |
157 |
158 |
159 |
160 |
169 |
170 |
171 |
172 |
177 |
178 |
741 |
746 |
--------------------------------------------------------------------------------
/src/views/MockView.vue:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
12 |
16 | Let's get set up.
17 |
18 |
22 |
25 |
Generate Problems
26 |
27 | Three problems are already generated for you, but you can change them.
28 |
29 |
30 |
31 |
32 |
33 |
39 | {{ currentOption }}
40 |
47 |
54 |
55 |
56 |
60 |
61 |
62 |
63 |
103 |
104 |
108 | {{ problemOne }}
109 |
110 |
114 | {{ problemTwo }}
115 |
116 |
120 | {{ problemThree }}
121 |
122 |
123 |
124 |
125 |
128 |
Select Duration
129 |
130 | Choose the duration for the mock contest. We recommend the standard four hours.
131 |
132 |
137 | 3 Hours
138 |
139 |
144 | 4 Hours
145 |
146 |
151 | 5 Hours
152 |
153 |
154 |
157 |
Ready?
158 |
You cannot stop the countdown once you start.
159 |
163 | Start
164 |
165 |
166 |
167 |
168 |
169 |
170 |
175 |
176 |
177 |
178 |
179 |
180 |
181 | Time's up!
182 |
183 |
184 |
185 |
186 |
187 | Submit your results, and calculate your score!
188 |
189 |
190 | For each problem, enter the number of correct cases out of total cases as a decimal.
191 | For example, if you got 7 out of 10 cases correct, write 0.7.
192 |
193 |
If you did not solve a problem,
195 | leave the input blank.
197 |
212 |
227 |
242 |
243 |
244 |
247 |
252 | Submit Results
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
266 |
270 | End Now
271 |
272 |
273 |
280 | Open main menu
281 |
282 |
283 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
331 |
332 |
333 |
334 | View on usaco.org
335 |
336 |
337 |
338 |
339 |
346 |
347 |
348 |
353 |
354 |
940 |
954 |
--------------------------------------------------------------------------------