├── .firebaserc ├── .github ├── ISSUE_TEMPLATE │ ├── bug_report.md │ └── feature_request.md ├── dependabot.yml └── workflows │ └── firebase-hosting-pull-request.yml ├── .gitignore ├── .gitpod.yml ├── .postcssrc ├── .prettierrc.json ├── .vscode └── extensions.json ├── README.md ├── database.rules.json ├── eslint.config.js ├── firebase.json ├── firestore.indexes.json ├── firestore.rules ├── index.html ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── robots.txt └── sitemap.xml ├── src ├── App.vue ├── Router.js ├── app-config.js ├── assets │ ├── css │ │ └── style.css │ ├── data │ │ ├── data.json │ │ ├── gather.mjs │ │ └── update.json │ ├── fonts │ │ └── Poppins-Medium.ttf │ └── images │ │ ├── algobanner.png │ │ ├── algologo.svg │ │ ├── algologofull.png │ │ └── googleg.svg ├── components │ ├── Banner.vue │ ├── Footer.vue │ ├── HomeStatistics.vue │ ├── Loader.vue │ ├── Navbar.vue │ ├── Theme.vue │ ├── Toast.vue │ └── Tooltip.vue ├── main.js ├── toast.js └── views │ ├── 404View.vue │ ├── AboutView.vue │ ├── HomeView.vue │ ├── LoginView.vue │ ├── MockView.vue │ ├── ProblemView.vue │ ├── ProfileView.vue │ ├── SolveView.vue │ └── StatisticsView.vue ├── storage.rules ├── tailwind.config.js └── vite.config.mjs /.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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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/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 | -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.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/ -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /.postcssrc: -------------------------------------------------------------------------------- 1 | { 2 | "plugins": { 3 | "tailwindcss": {}, 4 | "autoprefixer": {} 5 | } 6 | } -------------------------------------------------------------------------------- /.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 | } -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": ["Vue.volar", "Vue.vscode-typescript-vue-plugin"] 3 | } 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 |
2 | Algo 3 |

USACO problem randomizer and trainer built for competitive programming enthusiasts.

4 |

https://algousaco.com

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 | 35 |
36 |

Use this Google Form!

37 | 38 | 42 | -------------------------------------------------------------------------------- /database.rules.json: -------------------------------------------------------------------------------- 1 | { 2 | "rules": { 3 | ".read": false, 4 | ".write": false 5 | } 6 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /firestore.indexes.json: -------------------------------------------------------------------------------- 1 | { 2 | "indexes": [], 3 | "fieldOverrides": [] 4 | } 5 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IMGROOT2/algo/8f95d0c99bf0221ea62d7c784f231f9b3d4b9310/public/favicon.ico -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Allow: / -------------------------------------------------------------------------------- /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/App.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 20 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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/assets/data/update.json: -------------------------------------------------------------------------------- 1 | { 2 | "analytics": { 3 | "users": "3,600", 4 | "views": "16,000", 5 | "countries": "88" 6 | }, 7 | "release": { 8 | "version": "Banner is turned off.", 9 | "text": "Uncomment it in Banner.vue to turn it on." 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /src/assets/fonts/Poppins-Medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IMGROOT2/algo/8f95d0c99bf0221ea62d7c784f231f9b3d4b9310/src/assets/fonts/Poppins-Medium.ttf -------------------------------------------------------------------------------- /src/assets/images/algobanner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IMGROOT2/algo/8f95d0c99bf0221ea62d7c784f231f9b3d4b9310/src/assets/images/algobanner.png -------------------------------------------------------------------------------- /src/assets/images/algologofull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/IMGROOT2/algo/8f95d0c99bf0221ea62d7c784f231f9b3d4b9310/src/assets/images/algologofull.png -------------------------------------------------------------------------------- /src/assets/images/googleg.svg: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 8 | 10 | 11 | -------------------------------------------------------------------------------- /src/components/Banner.vue: -------------------------------------------------------------------------------- 1 | 22 | 25 | -------------------------------------------------------------------------------- /src/components/Footer.vue: -------------------------------------------------------------------------------- 1 | 37 | 38 | 42 | -------------------------------------------------------------------------------- /src/components/HomeStatistics.vue: -------------------------------------------------------------------------------- 1 | 50 | 53 | -------------------------------------------------------------------------------- /src/components/Loader.vue: -------------------------------------------------------------------------------- 1 | 23 | 28 | -------------------------------------------------------------------------------- /src/components/Navbar.vue: -------------------------------------------------------------------------------- 1 | 267 | 507 | 512 | -------------------------------------------------------------------------------- /src/components/Theme.vue: -------------------------------------------------------------------------------- 1 | 28 | 123 | -------------------------------------------------------------------------------- /src/components/Toast.vue: -------------------------------------------------------------------------------- 1 | 40 | 41 | 60 | 61 | 68 | -------------------------------------------------------------------------------- /src/components/Tooltip.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 30 | -------------------------------------------------------------------------------- /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/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/views/404View.vue: -------------------------------------------------------------------------------- 1 | 33 | 36 | 55 | -------------------------------------------------------------------------------- /src/views/AboutView.vue: -------------------------------------------------------------------------------- 1 | 58 | 65 | -------------------------------------------------------------------------------- /src/views/HomeView.vue: -------------------------------------------------------------------------------- 1 | 108 | 109 | 115 | -------------------------------------------------------------------------------- /src/views/LoginView.vue: -------------------------------------------------------------------------------- 1 | 193 | 369 | -------------------------------------------------------------------------------- /src/views/MockView.vue: -------------------------------------------------------------------------------- 1 | 354 | 940 | 954 | -------------------------------------------------------------------------------- /src/views/ProblemView.vue: -------------------------------------------------------------------------------- 1 | 40 | 136 | -------------------------------------------------------------------------------- /src/views/ProfileView.vue: -------------------------------------------------------------------------------- 1 | 72 | 148 | -------------------------------------------------------------------------------- /src/views/SolveView.vue: -------------------------------------------------------------------------------- 1 | 178 | 741 | 746 | -------------------------------------------------------------------------------- /src/views/StatisticsView.vue: -------------------------------------------------------------------------------- 1 | 146 | 548 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | } -------------------------------------------------------------------------------- /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 | }) --------------------------------------------------------------------------------