├── .env.example ├── .eslintrc.json ├── .gitattributes ├── .github ├── dependabot.yml └── workflows │ ├── automerge.yml │ └── test.yml ├── .gitignore ├── LICENSE ├── README.md ├── app.mjs ├── bin ├── disasm.js ├── migrate-contest.js ├── migrate.js ├── rejudge.js └── writeup-gen.js ├── config └── passport.js ├── contests ├── 4.js ├── 5.js ├── 6.js ├── hackathon2018.js ├── index.js ├── komabasai2018-day1.js ├── komabasai2018-day2.js ├── komabasai2019.js ├── komabasai2022.js ├── mayfes2018-day1.js ├── mayfes2018-day2.js ├── mayfes2019-day1.js ├── mayfes2019-day2.js ├── mayfes2020-day1.js ├── mayfes2020-day2.js ├── mayfes2021-day1.js ├── mayfes2021-day2.js ├── mayfes2021-practice1.js └── mayfes2021-practice2.js ├── controllers ├── api.js ├── contest.js ├── home.js ├── submission.js ├── user.js └── utils.js ├── data ├── build-infos.js ├── build-langs.rb ├── infos.json ├── langs.json ├── languages │ ├── 1.js │ ├── 2.js │ ├── 3.js │ ├── 4.js │ ├── 5.js │ ├── 6.js │ ├── hackathon2018.js │ ├── index.js │ ├── komabasai2018-day1.js │ ├── komabasai2018-day2.js │ ├── komabasai2019.js │ ├── komabasai2022.js │ ├── mayfes2018-day1.js │ ├── mayfes2018-day2.js │ ├── mayfes2019-day1.js │ ├── mayfes2019-day2.js │ ├── mayfes2020-day1.js │ ├── mayfes2020-day2.js │ ├── mayfes2021-day1.js │ ├── mayfes2021-day2.js │ └── mayfes2021-practice1.js ├── snub-dodecahedron.js └── truncated-cuboctahedron.js ├── engines └── docker.js ├── firestore.indexes.json ├── lib ├── discord.js ├── ptrace.js ├── sass-middleware.js ├── secret.v2.js ├── secret.v3.js ├── socket-io.js └── validation.js ├── models ├── Contest.js ├── Execution.js ├── Language.js ├── Submission.js └── User.js ├── package-lock.json ├── package.json ├── public ├── css │ ├── contests │ │ ├── 1.scss │ │ ├── 2.scss │ │ ├── 3.scss │ │ ├── 4.scss │ │ ├── 5.scss │ │ ├── 6.scss │ │ ├── hackathon2018.scss │ │ ├── mayfes2018.scss │ │ └── mayfes2020-day2.scss │ ├── lib │ │ ├── bootstrap-social.scss │ │ └── font-awesome │ │ │ ├── _animated.scss │ │ │ ├── _bordered-pulled.scss │ │ │ ├── _core.scss │ │ │ ├── _fixed-width.scss │ │ │ ├── _icons.scss │ │ │ ├── _larger.scss │ │ │ ├── _list.scss │ │ │ ├── _mixins.scss │ │ │ ├── _path.scss │ │ │ ├── _rotated-flipped.scss │ │ │ ├── _screen-reader.scss │ │ │ ├── _stacked.scss │ │ │ ├── _variables.scss │ │ │ └── font-awesome.scss │ ├── main.scss │ └── themes │ │ ├── default │ │ ├── _default.scss │ │ └── _variables.scss │ │ ├── flatly │ │ ├── _flatly.scss │ │ └── _variables.scss │ │ ├── gsdk │ │ ├── gsdk.scss │ │ └── gsdk │ │ │ ├── _alerts.scss │ │ │ ├── _buttons.scss │ │ │ ├── _carousel.scss │ │ │ ├── _collapse.scss │ │ │ ├── _dropdown.scss │ │ │ ├── _inputs.scss │ │ │ ├── _labels-and-progress-bars.scss │ │ │ ├── _misc.scss │ │ │ ├── _mixins.scss │ │ │ ├── _modal.scss │ │ │ ├── _navbars.scss │ │ │ ├── _responsive.scss │ │ │ ├── _sliders.scss │ │ │ ├── _social-buttons.scss │ │ │ ├── _tabs-navs-pagination.scss │ │ │ ├── _tooltips.scss │ │ │ ├── _typography.scss │ │ │ ├── _variables.scss │ │ │ └── mixins │ │ │ ├── _buttons.scss │ │ │ ├── _inputs.scss │ │ │ ├── _labels.scss │ │ │ ├── _navbars.scss │ │ │ ├── _tabs.scss │ │ │ ├── _transparency.scss │ │ │ └── _vendor-prefixes.scss │ │ └── modern │ │ ├── _modern.scss │ │ └── _variables.scss ├── favicon.ai ├── favicon.png ├── fonts │ ├── FontAwesome.otf │ ├── fontawesome-webfont.eot │ ├── fontawesome-webfont.svg │ ├── fontawesome-webfont.ttf │ ├── fontawesome-webfont.woff │ └── fontawesome-webfont.woff2 ├── img │ └── contest │ │ ├── 1-headline.ai │ │ ├── 1-headline.svg │ │ ├── 2-headline.ai │ │ ├── 2-headline.svg │ │ ├── 3-headline.ai │ │ ├── 3-headline.svg │ │ ├── 4-headline.ai │ │ ├── 4-headline.png │ │ ├── 5-headline.ai │ │ ├── 5-headline.svg │ │ ├── 6-headline.ai │ │ ├── 6-headline.svg │ │ ├── hackathon2018-headline.ai │ │ ├── hackathon2018-headline.svg │ │ ├── komabasai2018-day1-headline.ai │ │ ├── komabasai2018-day1-headline.svg │ │ ├── komabasai2018-day2-headline.ai │ │ ├── komabasai2018-day2-headline.svg │ │ ├── komabasai2019-headline.ai │ │ ├── komabasai2019-headline.svg │ │ ├── mayfes2018-day1-headline.ai │ │ ├── mayfes2018-day1-headline.svg │ │ ├── mayfes2018-day2-headline.ai │ │ ├── mayfes2018-day2-headline.svg │ │ ├── mayfes2019-day1-headline.ai │ │ ├── mayfes2019-day1-headline.svg │ │ ├── mayfes2019-day2-headline.ai │ │ ├── mayfes2019-day2-headline.svg │ │ ├── mayfes2020-day1-headline.ai │ │ ├── mayfes2020-day1-headline.svg │ │ ├── mayfes2020-day2-headline.ai │ │ ├── mayfes2020-day2-headline.svg │ │ └── noise.png └── js │ ├── api.js │ ├── check.babel.js │ └── contests │ ├── 4 │ ├── app.jsx │ ├── index.babel.js │ └── map.js │ ├── 5 │ ├── app.jsx │ └── index.babel.js │ ├── 6 │ ├── app.jsx │ └── index.babel.js │ ├── hackathon2018 │ ├── app.jsx │ └── index.babel.js │ ├── mayfes2018 │ ├── app.jsx │ └── index.babel.js │ ├── mayfes2020-day2 │ ├── app.jsx │ ├── index.babel.js │ └── japan.json │ └── mayfes2021-day2 │ ├── app.jsx │ ├── index.babel.js │ └── map.js ├── test ├── app.mjs └── models.js ├── views ├── account │ ├── login.pug │ └── profile.pug ├── admin.pug ├── check.pug ├── contest.pug ├── home.pug ├── layout.pug ├── maps │ ├── 1.pug │ ├── 2.pug │ └── 3.pug ├── mixins.pug ├── partials │ ├── countdown.pug │ ├── flash.pug │ ├── footer.pug │ └── header.pug ├── rule.pug ├── submission.pug └── submissions.pug ├── webpack.config.js └── worker.js /.env.example: -------------------------------------------------------------------------------- 1 | MONGODB_URI=mongodb://localhost:27017/esolang-battle 2 | MONGOLAB_URI=mongodb://localhost:27017/esolang-battle 3 | 4 | SESSION_SECRET=Your Session Secret goes here 5 | 6 | TWITTER_KEY=XXXXXXXXXXXXXXXXXXXXXXXXX 7 | TWITTER_SECRET=XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX 8 | 9 | # Unix 10 | GOOGLE_APPLICATION_CREDENTIALS=$HOME/.gcloud/foobar-xxxxxxxxxxx.json 11 | # Windows 12 | GOOGLE_APPLICATION_CREDENTIALS=$HOMEPATH/.gcloud/foobar-xxxxxxxxxxx.json 13 | 14 | PORT=3000 15 | SERVER_ORIGIN= 16 | DISCORD_TOKEN= 17 | DISCORD_CHANNEL= 18 | SLACK_TOKEN=xoxb-**********-************-XXXXXXXXXXXXXXXXXXXXXXXX 19 | SLACK_CHANNEL=CXXXXXXXXXX 20 | -------------------------------------------------------------------------------- /.eslintrc.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@hakatashi", 3 | "parserOptions": { 4 | "ecmaVersion": "latest" 5 | }, 6 | "rules": { 7 | "camelcase": "off", 8 | "no-console": "off", 9 | "max-len": "off", 10 | "no-underscore-dangle": "off", 11 | "prefer-object-spread": "off", 12 | "private-props/no-use-outside": "off", 13 | "react/forbid-component-props": "off", 14 | "react/jsx-handler-names": "off" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | package-lock.json binary 2 | *.svg binary 3 | *.ai binary 4 | -------------------------------------------------------------------------------- /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: npm 4 | directory: '/' 5 | schedule: 6 | interval: daily 7 | -------------------------------------------------------------------------------- /.github/workflows/automerge.yml: -------------------------------------------------------------------------------- 1 | name: automerge 2 | on: pull_request_target 3 | 4 | jobs: 5 | dependabot: 6 | runs-on: ubuntu-latest 7 | if: github.event.pull_request.user.login == 'dependabot[bot]' && github.repository == 'hakatashi/esolang-battle' 8 | steps: 9 | - name: Dependabot metadata 10 | id: metadata 11 | uses: dependabot/fetch-metadata@v2 12 | with: 13 | github-token: "${{ secrets.USER_GITHUB_TOKEN }}" 14 | - name: Enable auto-merge for Dependabot PRs 15 | run: gh pr merge --auto --merge "$PR_URL" 16 | env: 17 | PR_URL: ${{ github.event.pull_request.html_url }} 18 | GH_TOKEN: ${{ secrets.USER_GITHUB_TOKEN }} -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: 4 | - push 5 | - pull_request 6 | 7 | jobs: 8 | build: 9 | runs-on: ubuntu-latest 10 | 11 | steps: 12 | - name: Git checkout 13 | uses: actions/checkout@v2 14 | 15 | - name: Install Node.js 16 | uses: actions/setup-node@v1 17 | with: 18 | node-version: 18 19 | 20 | - name: Start MongoDB 21 | uses: supercharge/mongodb-github-action@1.4.0 22 | with: 23 | mongodb-version: 4.4 24 | 25 | - run: cp .env.example .env 26 | 27 | - run: npm install 28 | 29 | - run: npm test 30 | env: 31 | CI: true 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | lib-cov 2 | uploads 3 | languages.js 4 | *.seed 5 | *.log 6 | *.csv 7 | *.dat 8 | *.out 9 | *.pid 10 | *.gz 11 | *.swp 12 | 13 | pids 14 | logs 15 | results 16 | tmp 17 | 18 | #Build 19 | public/css/main.css 20 | 21 | # API keys and secrets 22 | .env 23 | 24 | # Dependency directory 25 | node_modules 26 | bower_components 27 | 28 | # Editors 29 | .idea 30 | *.iml 31 | 32 | # OS metadata 33 | .DS_Store 34 | Thumbs.db 35 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014-2016 Sahat Yalkabov 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # esolang-battle 2 | 3 | ![](https://esolang.hakatashi.com/favicon.png) 4 | 5 | > contest system for the codegolf competitions held by TSG 6 | 7 | Check out https://esolang.hakatashi.com 8 | -------------------------------------------------------------------------------- /bin/disasm.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const langs = require('../data/langs.json'); 3 | const docker = require('../engines/docker'); 4 | const Language = require('../models/Language'); 5 | const Submission = require('../models/Submission'); 6 | 7 | require('../models/User'); 8 | 9 | mongoose.Promise = global.Promise; 10 | 11 | (async () => { 12 | await mongoose.connect('mongodb://localhost:27017/esolang-battle'); 13 | 14 | // rollback 15 | for (const langInfo of langs) { 16 | if (langInfo.disasm !== true) { 17 | continue; 18 | } 19 | 20 | console.log(`Disassembling ${langInfo.slug}...`); 21 | const languages = await Language.find({slug: langInfo.slug}); 22 | 23 | for (const language of languages) { 24 | const submissions = await Submission.find({language}); 25 | 26 | for (const submission of submissions) { 27 | console.log(`Disassembling submission ${submission._id}...`); 28 | const disasmInfo = await docker({ 29 | id: langInfo.slug, 30 | code: submission.code, 31 | stdin: '', 32 | trace: false, 33 | disasm: true, 34 | }); 35 | console.log('disasm info:', disasmInfo); 36 | 37 | if (typeof disasmInfo !== 'object') { 38 | throw new Error('disasm info is not object'); 39 | } 40 | 41 | const {stdout: disasm} = disasmInfo; 42 | submission.disasm = disasm; 43 | await submission.save(); 44 | } 45 | } 46 | } 47 | 48 | mongoose.connection.close(); 49 | })(); 50 | -------------------------------------------------------------------------------- /bin/rejudge.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const mongoose = require('mongoose'); 3 | const languagesData = require('../data/languages'); 4 | const validation = require('../lib/validation'); 5 | const Contest = require('../models/Contest'); 6 | const Language = require('../models/Language'); 7 | const Submission = require('../models/Submission'); 8 | 9 | require('../models/User'); 10 | 11 | mongoose.Promise = global.Promise; 12 | 13 | (async () => { 14 | await mongoose.connect('mongodb://localhost:27017/esolang-battle'); 15 | const contest = await Contest.findOne({id: '5'}); 16 | const languages = await Language.find({ 17 | contest, 18 | solution: {$ne: null}, 19 | slug: 'canvas', 20 | }); 21 | 22 | // rollback 23 | for (const language of languages) { 24 | console.log(`Rejudging language ${language.slug}...`); 25 | const languageData = languagesData[contest.id].find( 26 | (l) => l && l.slug === language.slug, 27 | ); 28 | assert(languageData); 29 | 30 | while (true) { 31 | const submission = await Submission.findOne({ 32 | contest, 33 | language, 34 | }) 35 | .sort({createdAt: -1}) 36 | .exec(); 37 | 38 | if (!submission) { 39 | console.log('Solution not found.'); 40 | language.solution = null; 41 | await language.save(); 42 | break; 43 | } 44 | 45 | console.log(`Rejudging submission ${submission._id}...`); 46 | 47 | await validation.validate({ 48 | submission, 49 | language: languageData, 50 | solution: null, 51 | contest, 52 | noInputGeneration: true, 53 | }); 54 | 55 | const newSubmission = await Submission.findOne({_id: submission._id}); 56 | assert(newSubmission); 57 | console.log(newSubmission); 58 | 59 | if (newSubmission.status === 'success') { 60 | console.log(`Solution found as ${newSubmission._id}`); 61 | language.solution = newSubmission; 62 | await language.save(); 63 | break; 64 | } 65 | 66 | console.log(`${newSubmission._id} is invalid`); 67 | } 68 | } 69 | 70 | mongoose.connection.close(); 71 | })(); 72 | -------------------------------------------------------------------------------- /bin/writeup-gen.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | const isValidUTF8 = require('utf-8-validate'); 3 | const Contest = require('../models/Contest'); 4 | const Language = require('../models/Language'); 5 | require('../models/Submission'); 6 | require('../models/User'); 7 | const langs = require('../data/langs.json'); 8 | const {stripIndent} = require('common-tags'); 9 | 10 | mongoose.Promise = global.Promise; 11 | 12 | (async () => { 13 | await mongoose.connect('mongodb://localhost:27017/esolang-battle'); 14 | 15 | const contest = await Contest.find({id: '5'}); 16 | const languages = await Language.find({contest}) 17 | .populate({ 18 | path: 'solution', 19 | populate: {path: 'user'}, 20 | }) 21 | .exec(); 22 | 23 | languages.sort(({slug: slugA}, {slug: slugB}) => slugA.localeCompare(slugB)); 24 | for (const language of languages) { 25 | if (language.solution === null) { 26 | continue; 27 | } 28 | 29 | const lang = langs.find(({slug}) => slug === language.slug); 30 | console.log(stripIndent` 31 | # [${lang.name}](https://esolang.hakatashi.com/contests/5/submissions/${ 32 | language.solution._id 33 | }) (@${language.solution.user.email.replace(/@.+$/, '')}, ${ 34 | language.solution.size 35 | } bytes) 36 | `); 37 | console.log(''); 38 | const isVisible = isValidUTF8(language.solution.code); 39 | if (isVisible) { 40 | if (language.solution.size >= 500) { 41 | console.log( 42 | ['
', '', 'コードを見る', ''].join('\n'), 43 | ); 44 | } 45 | console.log('```'); 46 | console.log(language.solution.code.toString()); 47 | console.log('```'); 48 | console.log(''); 49 | if (language.solution.size >= 500) { 50 | console.log('
'); 51 | console.log(''); 52 | } 53 | } 54 | } 55 | 56 | mongoose.connection.close(); 57 | })(); 58 | -------------------------------------------------------------------------------- /config/passport.js: -------------------------------------------------------------------------------- 1 | const _ = require('lodash'); 2 | const passport = require('passport'); 3 | const TwitterStrategy = require('passport-twitter').Strategy; 4 | 5 | const User = require('../models/User'); 6 | 7 | const colors = [ 8 | '#777777', 9 | '#b80000', 10 | '#1273de', 11 | '#fccb00', 12 | '#5300eb', 13 | '#006b76', 14 | '#db3e00', 15 | '#004dcf', 16 | '#008b02', 17 | ]; 18 | 19 | passport.serializeUser((user, done) => { 20 | done(null, user.id); 21 | }); 22 | 23 | passport.deserializeUser((id, done) => { 24 | User.findById(id).then((user) => { 25 | done(null, user); 26 | }).catch((error) => { 27 | done(error, null); 28 | }); 29 | }); 30 | 31 | // Sign in with Twitter. 32 | 33 | passport.use( 34 | new TwitterStrategy( 35 | { 36 | consumerKey: process.env.TWITTER_KEY, 37 | consumerSecret: process.env.TWITTER_SECRET, 38 | callbackURL: `${process.env.SERVER_ORIGIN}/auth/twitter/callback`, 39 | passReqToCallback: true, 40 | }, 41 | // eslint-disable-next-line max-params 42 | async (req, accessToken, tokenSecret, profile, done) => { 43 | try { 44 | if (req.user) { 45 | const existingUser = await User.findOne({twitter: profile.id}); 46 | 47 | if (existingUser) { 48 | req.flash('errors', { 49 | msg: 50 | 'There is already a Twitter account that belongs to you. Sign in with that account or delete it, then link it with your current account.', 51 | }); 52 | done(); 53 | return; 54 | } 55 | 56 | const user = await User.findById(req.user.id); 57 | 58 | user.twitter = profile.id; 59 | user.tokens.push({kind: 'twitter', accessToken, tokenSecret}); 60 | user.profile.name = user.profile.name || profile.displayName; 61 | user.profile.location = 62 | user.profile.location || profile._json.location; 63 | user.profile.picture = 64 | user.profile.picture || profile._json.profile_image_url_https; 65 | 66 | await user.save(); 67 | req.flash('info', {msg: 'Twitter account has been linked.'}); 68 | done(null, user); 69 | } else { 70 | const existingUser = await User.findOne({twitter: profile.id}); 71 | if (existingUser) { 72 | done(null, existingUser); 73 | return; 74 | } 75 | 76 | const count = User.count({}); 77 | const user = new User(); 78 | 79 | // Twitter will not provide an email address. Period. 80 | // But a person’s twitter username is guaranteed to be unique 81 | // so we can "fake" a twitter email address as follows: 82 | user.email = `${profile.username}@twitter.com`; 83 | user.color = colors[count % colors.length]; 84 | user.twitter = profile.id; 85 | user.tokens.push({kind: 'twitter', accessToken, tokenSecret}); 86 | user.profile.name = profile.displayName; 87 | user.profile.location = profile._json.location; 88 | user.profile.picture = profile._json.profile_image_url_https; 89 | await user.save(); 90 | 91 | done(null, user); 92 | } 93 | } catch (error) { 94 | if (error) { 95 | done(error); 96 | } 97 | } 98 | }, 99 | ), 100 | ); 101 | 102 | /* 103 | * Login Required middleware. 104 | */ 105 | module.exports.isAuthenticated = (req, res, next) => { 106 | if (req.isAuthenticated()) { 107 | next(); 108 | return; 109 | } 110 | res.redirect('/login'); 111 | }; 112 | 113 | /* 114 | * Authorization Required middleware. 115 | */ 116 | module.exports.isAuthorized = (req, res, next) => { 117 | const provider = req.path.split('/').slice(-1)[0]; 118 | 119 | if (_.find(req.user.tokens, {kind: provider})) { 120 | next(); 121 | return; 122 | } 123 | 124 | res.redirect(`/auth/${provider}`); 125 | }; 126 | -------------------------------------------------------------------------------- /contests/4.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const shuffle = require('lodash/shuffle'); 3 | const chunk = require('lodash/chunk'); 4 | const random = require('lodash/random'); 5 | const math = require('mathjs'); 6 | const snubDodecahedron = require('../data/snub-dodecahedron.js'); 7 | 8 | module.exports.getPrecedingIndices = (cellIndex) => { 9 | const faces = [...snubDodecahedron.triangles, ...snubDodecahedron.pentagons]; 10 | const face = faces[cellIndex]; 11 | 12 | return Array(92) 13 | .fill() 14 | .map((_, index) => index) 15 | .filter((index) => { 16 | if (index === cellIndex) { 17 | return false; 18 | } 19 | 20 | const testFace = faces[index]; 21 | const sharedVertices = testFace.filter((vertice) => face.includes(vertice)); 22 | 23 | return sharedVertices.length === 2; 24 | }); 25 | }; 26 | 27 | const generateTestInput = () => chunk( 28 | shuffle([ 29 | random(1, 9), 30 | ...Array(11) 31 | .fill() 32 | .map(() => random(1, 99)), 33 | ]), 34 | 3, 35 | ); 36 | 37 | const getDeterminant = (vectors) => { 38 | const [A, B, C, D] = vectors; 39 | return math.det([ 40 | math.subtract(B, A), 41 | math.subtract(C, A), 42 | math.subtract(D, A), 43 | ]); 44 | }; 45 | 46 | module.exports.generateInput = () => { 47 | let vectors = null; 48 | let determinant = null; 49 | while (true) { 50 | vectors = generateTestInput(); 51 | determinant = getDeterminant(vectors); 52 | if (determinant % 6 === 0 && Math.abs(determinant) >= 30000) { 53 | break; 54 | } 55 | } 56 | 57 | const validVectors = 58 | determinant > 0 59 | ? vectors 60 | : [vectors[0], vectors[1], vectors[3], vectors[2]]; 61 | 62 | return validVectors 63 | .map( 64 | (vector) => `${vector 65 | .map((value) => value.toString(10).padStart(2, '0')) 66 | .join(' ')}\n`, 67 | ) 68 | .join(''); 69 | }; 70 | 71 | module.exports.isValidAnswer = (input, output) => { 72 | if (process.env.NODE_ENV !== 'production') { 73 | // return true; 74 | } 75 | 76 | const answer = getDeterminant(chunk(input.split(/\s/), 3)) / 6; 77 | assert(Number.isInteger(answer)); 78 | assert(answer >= 5000); 79 | 80 | const correctOutput = answer.toString(); 81 | 82 | // Trim 83 | const trimmedOutput = output 84 | .toString() 85 | .trim() 86 | .replace(/^0+/, ''); 87 | console.log('info:', {input, correctOutput, output, trimmedOutput}); 88 | 89 | return trimmedOutput === correctOutput; 90 | }; 91 | -------------------------------------------------------------------------------- /contests/hackathon2018.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const sample = require('lodash/sample'); 3 | const shuffle = require('lodash/shuffle'); 4 | const languages = require('../data/languages/hackathon2018.js'); 5 | 6 | const digits = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]; 7 | 8 | module.exports.getPrecedingIndices = () => languages.map((_, i) => i); 9 | 10 | const generateInputCase = () => { 11 | const tokens = [...Array(81).fill('x'), '0123456789', '9876543210']; 12 | 13 | let input = shuffle(tokens).join(''); 14 | 15 | while (input.includes('x')) { 16 | const xIndex = input.indexOf('x'); 17 | const prevChar = input[xIndex - 1]; 18 | const nextChar = input[xIndex + 1]; 19 | const candidates = digits.filter( 20 | (digit) => digit.toString() !== prevChar && digit.toString() !== nextChar, 21 | ); 22 | input = input.replace('x', sample(candidates).toString()); 23 | } 24 | 25 | assert(input.length === 101); 26 | 27 | return `${input}\n`; 28 | }; 29 | 30 | module.exports.generateInput = () => { 31 | let input = null; 32 | 33 | while (!input || input.match(/(00|11|22|33|44|55|66|77|88|99)/)) { 34 | input = generateInputCase(); 35 | } 36 | 37 | return input; 38 | }; 39 | 40 | module.exports.isValidAnswer = (input, output) => { 41 | if (process.env.NODE_ENV !== 'production') { 42 | return true; 43 | } 44 | 45 | const correctOutput = input 46 | .trim() 47 | .split('') 48 | .map((char, i, chars) => { 49 | if (i + 1 >= chars.length) { 50 | return ''; 51 | } 52 | 53 | const nextChar = chars[i + 1]; 54 | return nextChar > char ? '1' : '0'; 55 | }) 56 | .join(''); 57 | assert(correctOutput.length === 100); 58 | 59 | // Trim 60 | const trimmedOutput = output.toString().replace(/\s/g, ''); 61 | 62 | console.log('info:', {input, correctOutput, output, trimmedOutput}); 63 | 64 | return trimmedOutput === correctOutput; 65 | }; 66 | -------------------------------------------------------------------------------- /contests/index.js: -------------------------------------------------------------------------------- 1 | const contest4 = require('./4'); 2 | const contest5 = require('./5'); 3 | const contest6 = require('./6'); 4 | const hackathon2018 = require('./hackathon2018'); 5 | const komabasai2018Day1 = require('./komabasai2018-day1'); 6 | const komabasai2018Day2 = require('./komabasai2018-day2'); 7 | const komabasai2019 = require('./komabasai2019'); 8 | const komabasai2022 = require('./komabasai2022'); 9 | const mayfes2018Day1 = require('./mayfes2018-day1'); 10 | const mayfes2018Day2 = require('./mayfes2018-day2'); 11 | const mayfes2019Day1 = require('./mayfes2019-day1'); 12 | const mayfes2019Day2 = require('./mayfes2019-day2'); 13 | const mayfes2020Day1 = require('./mayfes2020-day1'); 14 | const mayfes2020Day2 = require('./mayfes2020-day2'); 15 | const mayfes2021Day1 = require('./mayfes2021-day1'); 16 | const mayfes2021Day2 = require('./mayfes2021-day2'); 17 | const mayfes2021Practice1 = require('./mayfes2021-practice1'); 18 | const mayfes2021Practice2 = require('./mayfes2021-practice2'); 19 | 20 | module.exports = { 21 | 4: contest4, 22 | 'mayfes2018-day1': mayfes2018Day1, 23 | 'mayfes2018-day2': mayfes2018Day2, 24 | hackathon2018, 25 | 'komabasai2018-day1': komabasai2018Day1, 26 | 'komabasai2018-day2': komabasai2018Day2, 27 | 'mayfes2019-day1': mayfes2019Day1, 28 | 'mayfes2019-day2': mayfes2019Day2, 29 | 5: contest5, 30 | komabasai2019, 31 | 6: contest6, 32 | 'mayfes2020-day1': mayfes2020Day1, 33 | 'mayfes2020-day2': mayfes2020Day2, 34 | 'mayfes2021-day1': mayfes2021Day1, 35 | 'mayfes2021-day2': mayfes2021Day2, 36 | 'mayfes2021-practice1': mayfes2021Practice1, 37 | 'mayfes2021-practice2': mayfes2021Practice2, 38 | 'komabasai2022-practice': mayfes2021Practice1, 39 | komabasai2022, 40 | }; 41 | -------------------------------------------------------------------------------- /contests/komabasai2018-day1.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const inRange = require('lodash/inRange'); 3 | const random = require('lodash/random'); 4 | 5 | module.exports.getPrecedingIndices = (cellIndex) => { 6 | assert(cellIndex >= 0); 7 | assert(cellIndex < 16); 8 | 9 | const x = cellIndex % 4; 10 | const y = Math.floor(cellIndex / 4); 11 | 12 | const precedingCells = []; 13 | 14 | if (x - 1 >= 0) { 15 | precedingCells.push(y * 4 + (x - 1)); 16 | } 17 | 18 | if (x + 1 < 4) { 19 | precedingCells.push(y * 4 + (x + 1)); 20 | } 21 | 22 | if (y - 1 >= 0) { 23 | precedingCells.push((y - 1) * 4 + x); 24 | } 25 | 26 | if (y + 1 < 4) { 27 | precedingCells.push((y + 1) * 4 + x); 28 | } 29 | 30 | return precedingCells.filter((cell) => ![0, 3, 12, 15].includes(cell)); 31 | }; 32 | 33 | module.exports.generateInput = () => `${[random(10, 99), random(10, 99)].join('\n')}\n`; 34 | 35 | module.exports.isValidAnswer = (input, output) => { 36 | if (process.env.NODE_ENV !== 'production') { 37 | return true; 38 | } 39 | 40 | const [height, width] = input 41 | .trim() 42 | .split('\n') 43 | .map((token) => parseInt(token)); 44 | assert(inRange(height, 10, 100)); 45 | assert(inRange(width, 10, 100)); 46 | 47 | const correctOutput = [ 48 | '*'.repeat(width), 49 | ...Array(height - 2) 50 | .fill() 51 | .map(() => `*${' '.repeat(width - 2)}*`), 52 | '*'.repeat(width), 53 | ].join('\n'); 54 | 55 | // Trim 56 | const trimmedOutput = output.toString().trim(); 57 | 58 | console.log('info:', {input, correctOutput, output, trimmedOutput}); 59 | 60 | return trimmedOutput === correctOutput; 61 | }; 62 | -------------------------------------------------------------------------------- /contests/komabasai2018-day2.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const countBy = require('lodash/countBy'); 3 | const random = require('lodash/random'); 4 | const sample = require('lodash/sample'); 5 | 6 | module.exports.getPrecedingIndices = (cellIndex) => { 7 | assert(cellIndex >= 0); 8 | assert(cellIndex < 16); 9 | 10 | const x = cellIndex % 4; 11 | const y = Math.floor(cellIndex / 4); 12 | 13 | const precedingCells = []; 14 | 15 | if (x - 1 >= 0) { 16 | precedingCells.push(y * 4 + (x - 1)); 17 | } 18 | 19 | if (x + 1 < 4) { 20 | precedingCells.push(y * 4 + (x + 1)); 21 | } 22 | 23 | if (y - 1 >= 0) { 24 | precedingCells.push((y - 1) * 4 + x); 25 | } 26 | 27 | if (y + 1 < 4) { 28 | precedingCells.push((y + 1) * 4 + x); 29 | } 30 | 31 | return precedingCells.filter((cell) => ![3, 12].includes(cell)); 32 | }; 33 | 34 | const getAnswers = (chars) => { 35 | const answers = []; 36 | for (const i of Array(chars.length - 16).keys()) { 37 | if ( 38 | chars[i] === '1' && 39 | chars[i + 5] === '1' && 40 | chars[i + 12] === '1' && 41 | (i + 17 === chars.length || chars[i + 17] === '1') 42 | ) { 43 | answers.push(i); 44 | } 45 | } 46 | return answers; 47 | }; 48 | 49 | module.exports.generateInput = () => { 50 | const index = random(0, 50 - 17 - 1); 51 | let seed = ['1', ...Array(49).fill('0')]; 52 | 53 | seed[index] = '1'; 54 | seed[index + 5] = '1'; 55 | seed[index + 12] = '1'; 56 | seed[index + 17] = '1'; 57 | 58 | while (countBy(seed)['1'] < 15 || Math.random() > 0.1) { 59 | const dopingIndex = sample( 60 | seed 61 | .map((char, index) => ({char, index})) 62 | .filter(({char}) => char === '0'), 63 | ).index; 64 | assert(seed[dopingIndex] === '0'); 65 | 66 | const tempSeed = seed.slice(); 67 | tempSeed[dopingIndex] = '1'; 68 | if (getAnswers(tempSeed).length === 1) { 69 | seed = tempSeed; 70 | } 71 | } 72 | 73 | assert(getAnswers(seed).length === 1); 74 | 75 | return seed.join(''); 76 | }; 77 | 78 | module.exports.isValidAnswer = (input, output) => { 79 | if (process.env.NODE_ENV !== 'production') { 80 | // return true; 81 | } 82 | 83 | const answers = getAnswers(input.trim().split('')); 84 | assert(answers.length === 1); 85 | 86 | const correctOutput = Array(50) 87 | .fill() 88 | .map((_, i) => (answers[0] === i ? '1' : '0')) 89 | .join(''); 90 | 91 | // Trim 92 | const trimmedOutput = output.toString().replace(/\s/g, ''); 93 | 94 | console.log('info:', {input, correctOutput, output, trimmedOutput}); 95 | 96 | return trimmedOutput === correctOutput; 97 | }; 98 | -------------------------------------------------------------------------------- /contests/komabasai2019.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const random = require('lodash/random'); 3 | const range = require('lodash/range'); 4 | const shuffle = require('lodash/shuffle'); 5 | 6 | module.exports.getPrecedingIndices = (cellIndex) => { 7 | assert(cellIndex >= 0); 8 | assert(cellIndex < 25); 9 | 10 | const x = cellIndex % 5; 11 | const y = Math.floor(cellIndex / 5); 12 | 13 | const precedingCells = []; 14 | 15 | if (x - 1 >= 0) { 16 | precedingCells.push(y * 5 + (x - 1)); 17 | } 18 | 19 | if (x + 1 < 5) { 20 | precedingCells.push(y * 5 + (x + 1)); 21 | } 22 | 23 | if (y - 1 >= 0) { 24 | precedingCells.push((y - 1) * 5 + x); 25 | } 26 | 27 | if (y + 1 < 5) { 28 | precedingCells.push((y + 1) * 5 + x); 29 | } 30 | 31 | return precedingCells.filter((cell) => ![0, 4, 20, 24].includes(cell)); 32 | }; 33 | 34 | module.exports.generateInput = () => { 35 | const count = 100; 36 | 37 | const lines = shuffle([ 38 | ...range(10, 100), 39 | ...range(count - 90).map(() => random(10, 99)), 40 | ]); 41 | 42 | assert(lines.length === 100); 43 | 44 | return `${lines.join('\n')}\n`; 45 | }; 46 | 47 | module.exports.isValidAnswer = (input, output) => { 48 | if (process.env.NODE_ENV !== 'production') { 49 | return true; 50 | } 51 | 52 | const lines = input.split('\n').filter((line) => line.length > 0); 53 | 54 | assert(lines.length === 100); 55 | 56 | let correctOutput = ''; 57 | 58 | for (const line of lines) { 59 | const n = parseInt(line); 60 | if (range(1, 10).some((i) => n % i === 0 && n / i <= 9)) { 61 | correctOutput += '1'; 62 | } else { 63 | correctOutput += '0'; 64 | } 65 | } 66 | 67 | const trimmedOutput = output.toString().replace(/\s/g, ''); 68 | 69 | console.log('info:', {input, correctOutput, output, trimmedOutput}); 70 | 71 | return trimmedOutput === correctOutput; 72 | }; 73 | -------------------------------------------------------------------------------- /contests/komabasai2022.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | 3 | module.exports.getPrecedingIndices = (cellIndex) => { 4 | const width = 5; 5 | const height = 5; 6 | assert(cellIndex >= 0); 7 | assert(cellIndex < width * height); 8 | 9 | const x = cellIndex % width; 10 | const y = Math.floor(cellIndex / width); 11 | 12 | const precedingCells = []; 13 | if (x - 1 >= 0) { 14 | precedingCells.push(y * width + (x - 1)); 15 | } 16 | if (x + 1 < width) { 17 | precedingCells.push(y * width + (x + 1)); 18 | } 19 | if (y - 1 >= 0) { 20 | precedingCells.push((y - 1) * width + x); 21 | } 22 | if (y + 1 < height) { 23 | precedingCells.push((y + 1) * width + x); 24 | } 25 | 26 | return precedingCells.filter( 27 | (cell) => ![0, 4, 5, 9, 10, 14, 15, 19, 20, 24].includes(cell), 28 | ); 29 | }; 30 | 31 | module.exports.generateInput = () => { 32 | const dominoes = [...new Array(512)].map((v, i) => (i + 512) 33 | .toString(2) 34 | .replace(/0/g, '_') 35 | .replace(/1/g, '|')); 36 | for (let a = 512; a > 0; a--) { 37 | const j = Math.floor(Math.random() * a); 38 | [dominoes[a - 1], dominoes[j]] = [dominoes[j], dominoes[a - 1]]; 39 | } 40 | const lines = `${dominoes.slice(0, 30).join('\n')}\n`; 41 | assert(lines.length === 330); 42 | 43 | return lines; 44 | }; 45 | 46 | module.exports.isValidAnswer = (input, output) => { 47 | const ansArray = input.trim().split('\n').map((d) => (d.slice(0, (d.indexOf('__') + 10) % 10 + 1).match(/\|/g) || []).length); 48 | const outArray = output.toString().trim().split(/\s+/).map((s) => parseInt(s)); 49 | return ansArray.toString() === outArray.toString(); 50 | }; 51 | -------------------------------------------------------------------------------- /contests/mayfes2018-day1.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const random = require('lodash/random'); 3 | 4 | const A = 'A'.charCodeAt(0); 5 | 6 | module.exports.getPrecedingIndices = (cellIndex) => { 7 | assert(cellIndex >= 0); 8 | assert(cellIndex < 9); 9 | 10 | const x = cellIndex % 3; 11 | const y = Math.floor(cellIndex / 3); 12 | 13 | const precedingCells = []; 14 | 15 | if (x - 1 >= 0) { 16 | precedingCells.push(y * 3 + (x - 1)); 17 | } 18 | 19 | if (x + 1 < 3) { 20 | precedingCells.push(y * 3 + (x + 1)); 21 | } 22 | 23 | if (y - 1 >= 0) { 24 | precedingCells.push((y - 1) * 3 + x); 25 | } 26 | 27 | if (y + 1 < 3) { 28 | precedingCells.push((y + 1) * 3 + x); 29 | } 30 | 31 | return precedingCells; 32 | }; 33 | 34 | module.exports.generateInput = () => `${Array(26) 35 | .fill() 36 | .map(() => String.fromCharCode(A + random(0, 25))) 37 | .join('')}\n`; 38 | 39 | module.exports.isValidAnswer = (input, output) => { 40 | if (process.env.NODE_ENV !== 'production') { 41 | return true; 42 | } 43 | 44 | const correctOutput = input 45 | .trim() 46 | .split('') 47 | .map((char, i) => String.fromCharCode(((char.charCodeAt(0) - A + i + 1) % 26) + A)) 48 | .join(''); 49 | assert(correctOutput.length === 26); 50 | 51 | // Trim 52 | const trimmedOutput = output.toString().replace(/\s/g, ''); 53 | 54 | console.log('info:', {input, correctOutput, output, trimmedOutput}); 55 | 56 | return trimmedOutput === correctOutput; 57 | }; 58 | -------------------------------------------------------------------------------- /contests/mayfes2018-day2.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const random = require('lodash/random'); 3 | const sum = require('lodash/sum'); 4 | 5 | module.exports.getPrecedingIndices = (cellIndex) => { 6 | assert(cellIndex >= 0); 7 | assert(cellIndex < 9); 8 | 9 | const x = cellIndex % 3; 10 | const y = Math.floor(cellIndex / 3); 11 | 12 | const precedingCells = []; 13 | 14 | if (x - 1 >= 0) { 15 | precedingCells.push(y * 3 + (x - 1)); 16 | } 17 | 18 | if (x + 1 < 3) { 19 | precedingCells.push(y * 3 + (x + 1)); 20 | } 21 | 22 | if (y - 1 >= 0) { 23 | precedingCells.push((y - 1) * 3 + x); 24 | } 25 | 26 | if (y + 1 < 3) { 27 | precedingCells.push((y + 1) * 3 + x); 28 | } 29 | 30 | return precedingCells; 31 | }; 32 | 33 | module.exports.generateInput = () => `${Array(100) 34 | .fill() 35 | .map(() => random()) 36 | .join('')}\n`; 37 | 38 | module.exports.isValidAnswer = (input, output) => { 39 | if (process.env.NODE_ENV !== 'production') { 40 | return true; 41 | } 42 | 43 | const inputNumbers = input 44 | .trim() 45 | .split('') 46 | .map((n) => parseInt(n)); 47 | const correctOutput = inputNumbers 48 | .map((n, i) => sum(inputNumbers.slice(0, i + 1)) % 2) 49 | .join(''); 50 | assert(correctOutput.length === 100); 51 | 52 | // Trim 53 | const trimmedOutput = output.toString().replace(/\s/g, ''); 54 | 55 | console.log('info:', {input, correctOutput, output, trimmedOutput}); 56 | 57 | return trimmedOutput === correctOutput; 58 | }; 59 | -------------------------------------------------------------------------------- /contests/mayfes2019-day1.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const flatten = require('lodash/flatten'); 3 | const range = require('lodash/range'); 4 | const sample = require('lodash/sample'); 5 | const shuffle = require('lodash/shuffle'); 6 | const zip = require('lodash/zip'); 7 | 8 | module.exports.getPrecedingIndices = (cellIndex) => { 9 | assert(cellIndex >= 0); 10 | assert(cellIndex < 25); 11 | 12 | const x = cellIndex % 5; 13 | const y = Math.floor(cellIndex / 5); 14 | 15 | const precedingCells = []; 16 | 17 | if (x - 1 >= 0) { 18 | precedingCells.push(y * 5 + (x - 1)); 19 | } 20 | 21 | if (x + 1 < 5) { 22 | precedingCells.push(y * 5 + (x + 1)); 23 | } 24 | 25 | if (y - 1 >= 0) { 26 | precedingCells.push((y - 1) * 5 + x); 27 | } 28 | 29 | if (y + 1 < 5) { 30 | precedingCells.push((y + 1) * 5 + x); 31 | } 32 | 33 | return precedingCells.filter( 34 | (cell) => ![0, 3, 4, 5, 19, 20, 21, 24].includes(cell), 35 | ); 36 | }; 37 | 38 | const alphabets = range(26).map((i) => String.fromCharCode('a'.charCodeAt() + i)); 39 | 40 | module.exports.generateInput = () => { 41 | const letters = shuffle([ 42 | ...alphabets, 43 | ...range(50 - alphabets.length).map(() => sample(alphabets)), 44 | ]); 45 | 46 | const numbers = flatten( 47 | shuffle([ 48 | ...range(2, 10), 49 | ...range(50 - 8 - 3 - 1).map(() => sample(range(1, 10))), 50 | [1, 1, 1], 51 | ]), 52 | ).concat([sample(range(2, 10))]); 53 | 54 | assert(letters.length === 50); 55 | assert(numbers.length === 50); 56 | 57 | return `${zip(letters, numbers) 58 | .map(([letter, number]) => (number === 1 ? letter : `${letter}${number}`)) 59 | .join('')}\n`; 60 | }; 61 | 62 | module.exports.isValidAnswer = (input, output) => { 63 | if (process.env.NODE_ENV !== 'production') { 64 | return true; 65 | } 66 | 67 | const chunks = input.split(/(?=[a-z])/); 68 | 69 | assert(chunks.length === 50); 70 | 71 | const correctOutput = chunks 72 | .map((chunk) => { 73 | const [letter, count = '1'] = chunk.split(''); 74 | return letter.repeat(parseInt(count)); 75 | }) 76 | .join(''); 77 | 78 | const trimmedOutput = output.toString().replace(/\s/g, ''); 79 | 80 | console.log('info:', {input, correctOutput, output, trimmedOutput}); 81 | 82 | return trimmedOutput === correctOutput; 83 | }; 84 | -------------------------------------------------------------------------------- /contests/mayfes2019-day2.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const flatten = require('lodash/flatten'); 3 | const range = require('lodash/range'); 4 | const sample = require('lodash/sample'); 5 | const shuffle = require('lodash/shuffle'); 6 | 7 | module.exports.getPrecedingIndices = (cellIndex) => { 8 | assert(cellIndex >= 0); 9 | assert(cellIndex < 25); 10 | 11 | const x = cellIndex % 5; 12 | const y = Math.floor(cellIndex / 5); 13 | 14 | const precedingCells = []; 15 | 16 | if (x - 1 >= 0) { 17 | precedingCells.push(y * 5 + (x - 1)); 18 | } 19 | 20 | if (x + 1 < 5) { 21 | precedingCells.push(y * 5 + (x + 1)); 22 | } 23 | 24 | if (y - 1 >= 0) { 25 | precedingCells.push((y - 1) * 5 + x); 26 | } 27 | 28 | if (y + 1 < 5) { 29 | precedingCells.push((y + 1) * 5 + x); 30 | } 31 | 32 | return precedingCells.filter( 33 | (cell) => ![0, 3, 4, 5, 19, 20, 21, 24].includes(cell), 34 | ); 35 | }; 36 | 37 | const wins = ['01', '12', '20']; 38 | const loses = ['10', '21', '02']; 39 | 40 | module.exports.generateInput = () => { 41 | const answer = sample(range(20, 190)); 42 | 43 | const lines = flatten([ 44 | ...shuffle([ 45 | ...range(answer).map(() => sample(wins)), 46 | ...range(99).map(() => sample(loses)), 47 | ]), 48 | sample(loses), 49 | ...range(300 - answer - 100).map(() => sample([...wins, ...loses])), 50 | ]); 51 | 52 | assert(lines.length === 300); 53 | 54 | return `${lines.join('\n')}\n`; 55 | }; 56 | 57 | module.exports.isValidAnswer = (input, output) => { 58 | if (process.env.NODE_ENV !== 'production') { 59 | return true; 60 | } 61 | 62 | const lines = input.split('\n').filter((line) => line.length > 0); 63 | 64 | assert(lines.length === 300); 65 | 66 | let winCount = 0; 67 | let loseCount = 0; 68 | 69 | for (const line of lines) { 70 | if (wins.includes(line)) { 71 | winCount++; 72 | } 73 | if (loses.includes(line)) { 74 | loseCount++; 75 | } 76 | if (loseCount === 100) { 77 | break; 78 | } 79 | } 80 | 81 | const correctOutput = '1'.repeat(winCount); 82 | 83 | const trimmedOutput = output.toString().replace(/\s/g, ''); 84 | 85 | console.log('info:', {input, correctOutput, output, trimmedOutput}); 86 | 87 | return trimmedOutput === correctOutput; 88 | }; 89 | -------------------------------------------------------------------------------- /contests/mayfes2020-day1.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const flatten = require('lodash/flatten'); 3 | const range = require('lodash/range'); 4 | const sample = require('lodash/sample'); 5 | const shuffle = require('lodash/shuffle'); 6 | const zip = require('lodash/zip'); 7 | 8 | module.exports.getPrecedingIndices = (cellIndex) => { 9 | const width = 5; 10 | const height = 5; 11 | assert(cellIndex >= 0); 12 | assert(cellIndex < width * height); 13 | 14 | const x = cellIndex % width; 15 | const y = Math.floor(cellIndex / width); 16 | 17 | const precedingCells = []; 18 | if (x - 1 >= 0) { 19 | precedingCells.push(y * width + (x - 1)); 20 | } 21 | if (x + 1 < width) { 22 | precedingCells.push(y * width + (x + 1)); 23 | } 24 | if (y - 1 >= 0) { 25 | precedingCells.push((y - 1) * width + x); 26 | } 27 | if (y + 1 < height) { 28 | precedingCells.push((y + 1) * width + x); 29 | } 30 | 31 | return precedingCells.filter( 32 | (cell) => ![0, 4, 5, 9, 10, 14, 15, 19, 20, 24].includes(cell), 33 | ); 34 | }; 35 | 36 | const alphabets = range(26).map( 37 | (i) => String.fromCharCode('a'.charCodeAt() + i) + 38 | String.fromCharCode('A'.charCodeAt() + i), 39 | ); 40 | 41 | module.exports.generateInput = () => { 42 | const n = sample(range(1, 26)); 43 | const numbers = shuffle(('1'.repeat(n) + '0'.repeat(26 - n)).split('')); 44 | const letters = numbers.map((n, i) => String.fromCharCode(i + (n === '1' ? 'A' : 'a').charCodeAt())); 45 | 46 | assert(letters.length === 26); 47 | 48 | return `${letters.join('')}\n`; 49 | }; 50 | 51 | module.exports.isValidAnswer = (input, output) => { 52 | const chunks = input.split(/(?=[a-zA-Z])/); 53 | 54 | assert(chunks.length === 26); 55 | 56 | const correctOutput = chunks 57 | .map((chunk) => (chunk === chunk.toUpperCase() ? 1 : 0)) 58 | .join(''); 59 | const trimmedOutput = output.toString().replace(/\s/g, ''); 60 | 61 | console.log('info:', {input, correctOutput, output, trimmedOutput}); 62 | 63 | return trimmedOutput === correctOutput; 64 | }; 65 | -------------------------------------------------------------------------------- /contests/mayfes2020-day2.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const flatten = require('lodash/flatten'); 3 | const range = require('lodash/range'); 4 | const sample = require('lodash/sample'); 5 | const shuffle = require('lodash/shuffle'); 6 | 7 | /* 8 | 0: 京都府-兵庫13/大阪10/奈良20/三重12/滋賀4/福井19 9 | 1: 愛知県-三重12/岐阜9/長野11/静岡6 10 | 2: 栃木県-群馬5/埼玉17/茨城7 11 | 3: 山梨県-長野11/静岡6/神奈川22/東京16/埼玉17 12 | 4: 滋賀県-京都0/三重12/岐阜9/福井19 13 | 5: 群馬県-栃木2/新潟21/長野11/埼玉17 14 | 6: 静岡県-愛知1/山梨3/長野11/神奈川22 15 | 7: 茨城県(0-base)-栃木2/埼玉17/千葉14 16 | 8: 和歌山県-大阪10/奈良20/三重12 17 | 9: 岐阜県-愛知1/滋賀4/三重12/福井19/石川18/富山15/長野11 18 | 10: 大阪府-兵庫13/京都0/奈良20/和歌山8 19 | 11: 長野県-新潟21/群馬5/埼玉17/山梨3/静岡6/愛知1/岐阜9/富山15 20 | 12: 三重県-和歌山8/奈良20/京都0/滋賀4/岐阜9/愛知1 21 | 13: 兵庫県(1-base)-京都0/大阪10 22 | 14: 千葉県-東京16/埼玉17/茨城7 23 | 15: 富山県(0-base)-石川18/長野11/岐阜9/新潟21 24 | 16: 東京都(1-base)-千葉14/埼玉17/山梨3/神奈川22 25 | 17: 埼玉県-栃木2/山梨3/群馬5/茨城7/長野11/千葉14/東京16 26 | 18: 石川県-富山15/岐阜9/福井19 27 | 19: 福井県-石川18/岐阜9/滋賀4/京都0 28 | 20: 奈良県-京都0/和歌山8/大阪10/三重12 29 | 21: 新潟県-群馬5/長野11/富山15 30 | 22: 神奈川県-山梨3/静岡6/東京16 31 | */ 32 | 33 | const edges = [ 34 | [4, 10, 12, 13, 19, 20], 35 | [6, 9, 11, 12], 36 | [5, 7, 17], 37 | [6, 11, 16, 17, 22], 38 | [0, 9, 12, 19], 39 | [2, 11, 17, 21], 40 | [1, 3, 11, 12], 41 | [2, 14, 17], 42 | [10, 12, 20], 43 | [1, 4, 11, 12, 15, 18, 19], 44 | [0, 8, 13, 20], 45 | [1, 3, 5, 6, 9, 15, 17, 21], 46 | [0, 1, 4, 8, 9, 20], 47 | [0, 10], 48 | [7, 16, 17], 49 | [9, 11, 18, 21], 50 | [3, 14, 17, 22], 51 | [2, 3, 5, 7, 11, 14, 16], 52 | [9, 15, 19], 53 | [0, 4, 9, 18], 54 | [0, 8, 10, 12], 55 | [5, 11, 15], 56 | [3, 6, 16], 57 | ]; 58 | 59 | module.exports.getPrecedingIndices = (cellIndex) => edges[cellIndex]; 60 | 61 | const numbers = Array(90) 62 | .fill() 63 | .map((_, x) => (x + 10).toString()); 64 | 65 | module.exports.generateInput = () => { 66 | // input generator 67 | const nums = shuffle(numbers).filter((_, x) => x < 50); 68 | const input = `${nums.join('\n')}\n`; 69 | console.log(input); 70 | return input; 71 | }; 72 | 73 | module.exports.isValidAnswer = (input, output) => { 74 | if (process.env.NODE_ENV !== 'production') { 75 | return true; 76 | } 77 | 78 | const lines = input.split('\n').filter((line) => line.length > 0); 79 | 80 | assert(lines.length === 50); 81 | 82 | let minValue = parseInt(lines[0]) + 1; 83 | let cnt = ''; 84 | 85 | for (const line of lines) { 86 | const val = parseInt(line); 87 | if (val < minValue) { 88 | minValue = val; 89 | cnt += '1'; 90 | } else { 91 | cnt += '0'; 92 | } 93 | } 94 | 95 | const correctOutput = cnt; 96 | 97 | const trimmedOutput = output.toString().replace(/\s/g, ''); 98 | 99 | console.log('info:', {input, correctOutput, output, trimmedOutput}); 100 | 101 | return trimmedOutput === correctOutput; 102 | }; 103 | -------------------------------------------------------------------------------- /contests/mayfes2021-day1.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const concat = require('lodash/concat'); 3 | const includes = require('lodash/includes'); 4 | const range = require('lodash/range'); 5 | const sample = require('lodash/sample'); 6 | const shuffle = require('lodash/shuffle'); 7 | 8 | module.exports.getPrecedingIndices = (cellIndex) => { 9 | const width = 5; 10 | const height = 5; 11 | assert(cellIndex >= 0); 12 | assert(cellIndex < width * height); 13 | 14 | const x = cellIndex % width; 15 | const y = Math.floor(cellIndex / width); 16 | 17 | const precedingCells = []; 18 | if (x - 1 >= 0) { 19 | precedingCells.push(y * width + (x - 1)); 20 | } 21 | if (x + 1 < width) { 22 | precedingCells.push(y * width + (x + 1)); 23 | } 24 | if (y - 1 >= 0) { 25 | precedingCells.push((y - 1) * width + x); 26 | } 27 | if (y + 1 < height) { 28 | precedingCells.push((y + 1) * width + x); 29 | } 30 | 31 | return precedingCells.filter( 32 | (cell) => ![0, 4, 5, 9, 10, 14, 15, 19, 20, 24].includes(cell), 33 | ); 34 | }; 35 | 36 | const ascendingCases = ['0123', '1230', '2301', '3012']; 37 | const descendingCases = ['3210', '0321', '1032', '2103']; 38 | const cases = concat(ascendingCases, descendingCases); 39 | 40 | const lineNum = 32; 41 | 42 | assert(lineNum > cases.length); 43 | 44 | module.exports.generateInput = () => { 45 | const determinedNumbers = range(cases.length); 46 | const randomNumbers = range(lineNum - cases.length).map(() => sample(range(cases.length))); 47 | const numbers = shuffle(concat(determinedNumbers, randomNumbers)); 48 | const lines = numbers.map((n) => cases[n]); 49 | 50 | return `${lines.join('\n')}\n`; 51 | }; 52 | 53 | module.exports.isValidAnswer = (input, output) => { 54 | const inputLines = input.trim().split('\n'); 55 | 56 | assert(inputLines.length === lineNum); 57 | 58 | const correctOutput = inputLines 59 | .map((line) => (includes(ascendingCases, line) ? 1 : 0)) 60 | .join(''); 61 | const trimmedOutput = output.toString().replace(/\s/g, ''); 62 | 63 | console.log('info:', {input, correctOutput, output, trimmedOutput}); 64 | 65 | return trimmedOutput === correctOutput; 66 | }; 67 | -------------------------------------------------------------------------------- /contests/mayfes2021-day2.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const range = require('lodash/range'); 3 | const shuffle = require('lodash/shuffle'); 4 | const truncatedCuboctahedron = require('../data/truncated-cuboctahedron'); 5 | 6 | module.exports.getPrecedingIndices = (cellIndex) => { 7 | const faces = [ 8 | ...truncatedCuboctahedron.squares, 9 | ...truncatedCuboctahedron.hexagons, 10 | ...truncatedCuboctahedron.octagons, 11 | ]; 12 | const face = faces[cellIndex]; 13 | 14 | return Array(26) 15 | .fill() 16 | .map((_, index) => index) 17 | .filter((index) => { 18 | if (index === cellIndex) { 19 | return false; 20 | } 21 | 22 | const testFace = faces[index]; 23 | const sharedVertices = testFace.filter((vertice) => face.includes(vertice)); 24 | 25 | return sharedVertices.length === 2; 26 | }); 27 | }; 28 | 29 | module.exports.generateInput = () => { 30 | const syllables = [ 31 | ...range(10).map(() => 'to'), 32 | ...range(10).map(() => 'kyo'), 33 | ]; 34 | assert(syllables.length === 20); 35 | 36 | const line = shuffle(syllables).join(''); 37 | assert(line.length === 50); 38 | assert(line.includes('tokyo') || line.includes('kyoto')); 39 | 40 | return `${line}\n`; 41 | }; 42 | 43 | module.exports.isValidAnswer = (input, output) => { 44 | const count = (s, t) => s.split(t).length - 1; 45 | 46 | const tokyoCount = count(input, 'tokyo'); 47 | const kyotoCount = count(input, 'kyoto'); 48 | 49 | const trimmedOutput = output.toString().replace(/\s/g, ''); 50 | 51 | console.log('info:', {input, tokyoCount, kyotoCount, output, trimmedOutput}); 52 | 53 | let t = 0; 54 | let k = 0; 55 | 56 | for (const char of trimmedOutput) { 57 | if (char === 't' || char === 'T') { 58 | t += 1; 59 | } else if (char === 'k' || char === 'K') { 60 | k += 1; 61 | } else { 62 | return false; 63 | } 64 | } 65 | 66 | return (t === tokyoCount && k === 0) || (t === 0 && k === kyotoCount); 67 | }; 68 | -------------------------------------------------------------------------------- /contests/mayfes2021-practice1.js: -------------------------------------------------------------------------------- 1 | // the same as that of mayfes2021-day1 2 | module.exports.getPrecedingIndices = require('./mayfes2021-day1').getPrecedingIndices; 3 | 4 | // the same as those of mayfes2020-day1 5 | const mayfes2020Day1 = require('./mayfes2020-day1'); 6 | 7 | module.exports.generateInput = mayfes2020Day1.generateInput; 8 | module.exports.isValidAnswer = mayfes2020Day1.isValidAnswer; 9 | -------------------------------------------------------------------------------- /contests/mayfes2021-practice2.js: -------------------------------------------------------------------------------- 1 | // the same as that of mayfes2021-day2 2 | module.exports.getPrecedingIndices = require('./mayfes2021-day2').getPrecedingIndices; 3 | 4 | // the same as those of mayfes2020-day1 5 | const mayfes2020Day2 = require('./mayfes2020-day2'); 6 | 7 | module.exports.generateInput = mayfes2020Day2.generateInput; 8 | module.exports.isValidAnswer = mayfes2020Day2.isValidAnswer; 9 | -------------------------------------------------------------------------------- /controllers/contest.js: -------------------------------------------------------------------------------- 1 | const qs = require('querystring'); 2 | const classnames = require('classnames'); 3 | const MarkdownIt = require('markdown-it'); 4 | const {getLanguageMap} = require('../controllers/utils'); 5 | const Contest = require('../models/Contest'); 6 | const User = require('../models/User'); 7 | 8 | /* 9 | * Middleware for all /contest/:contest routes 10 | */ 11 | module.exports.base = async (req, res, next) => { 12 | const contest = await Contest.findOne({id: req.params.contest}); 13 | 14 | if (!contest) { 15 | res.sendStatus(404); 16 | return; 17 | } 18 | 19 | req.contest = contest; 20 | next(); 21 | }; 22 | 23 | /* 24 | * GET / 25 | * Home page. 26 | */ 27 | module.exports.index = async (req, res) => { 28 | const languageMap = await getLanguageMap({contest: req.contest}); 29 | res.render('contest', { 30 | title: '', 31 | contest: req.contest, 32 | languageMap, 33 | classnames, 34 | hideFooter: true, 35 | }); 36 | }; 37 | 38 | module.exports.rule = (req, res) => { 39 | const markdown = new MarkdownIt(); 40 | res.render('rule', { 41 | contest: req.contest, 42 | title: 'Rule', 43 | description: { 44 | ja: markdown.render(req.contest.description.ja), 45 | en: markdown.render(req.contest.description.en), 46 | }, 47 | }); 48 | }; 49 | 50 | /* 51 | * GET /contest/:contest/admin 52 | */ 53 | module.exports.getAdmin = async (req, res) => { 54 | if (!req.user.admin) { 55 | res.sendStatus(403); 56 | return; 57 | } 58 | 59 | if (req.query.user && req.query.team) { 60 | const user = await User.findOne({_id: req.query.user}); 61 | user.setTeam(req.contest, req.query.team); 62 | await user.save(); 63 | res.redirect(`/contests/${req.params.contest}/admin`); 64 | return; 65 | } 66 | 67 | const users = await User.find(); 68 | 69 | res.render('admin', { 70 | contest: req.contest, 71 | users, 72 | teams: ['Red', 'Blue', 'Green', 'Orange', 'Purple'], 73 | colors: ['#ef2011', '#0e30ec', '#167516', '#f57f17', '#6a1b9a'], 74 | qs, 75 | }); 76 | }; 77 | 78 | /* 79 | * GET /contest/:contest/check 80 | */ 81 | module.exports.getCheck = async (req, res) => { 82 | if (!req.contest.isOpen()) { 83 | res.redirect(`/contests/${req.contest.id}`); 84 | return; 85 | } 86 | 87 | const languages = await getLanguageMap({contest: req.contest}); 88 | const availableLanguages = languages 89 | .filter(({type}) => type === 'language') 90 | .sort(({name: nameA}, {name: nameB}) => nameA.localeCompare(nameB)); 91 | 92 | res.render('check', { 93 | title: 'Check', 94 | contest: req.contest, 95 | availableLanguages, 96 | }); 97 | }; 98 | -------------------------------------------------------------------------------- /controllers/home.js: -------------------------------------------------------------------------------- 1 | const {sample} = require('lodash'); 2 | const Contest = require('../models/Contest'); 3 | 4 | /* 5 | * GET / 6 | * Home page. 7 | */ 8 | module.exports.index = async (req, res) => { 9 | const contests = await Contest.find() 10 | .sort({_id: -1}) 11 | .exec(); 12 | 13 | res.render('home', { 14 | title: 'Home', 15 | contests, 16 | helloworld: sample([ 17 | '"Hello, World!', // abe 18 | 'iiisa-<*>P/>is+iP>PPm-iiiPi+siskkkOPP', // 3var 19 | '"!dlroW ,olleH"d&O`@', // Alice 20 | '64+"!dlroW ,olleH">:#,_@', // Befunge-93 21 | '--<-<<+[+[<+>--->->->-<<<]>]<<--.<++++++.<<-..<<.<+.>>.>>.<<<.+++.>>.>>-.<<<+.', // Brainfuck 22 | '%"Hello, World!";x', // Cardinal 23 | '"Hello, World!" print', // Cy 24 | '.-$"Hello, World!"', // Asciidots 25 | '💬Hello, World!💬➡', // Emoji 26 | '🏁 🍇 😀 🔤Hello, World!🔤 🍉', // Emojicode 27 | 'aeeeaeeewueuueweeueeuewwaaaweaaewaeaawueweeeaeeewaaawueeueweeaweeeueuw', // Evil 28 | '"!dlroW ,olleH"l?!;oe0.', // <>< 29 | '{M[m(_o)O!"Hello, World!\\n"(_o)o.?]}', // Grass 30 | 'h', // Goruby 31 | 'Hello, World!', // HTMS 32 | 'H;e;l;d;*;r;o;Wl;;o;*433;@.>;23<\\4;*/', // Hexagony 33 | '72.101.108:..111.44.32.87.111.114.108.100.33.@', // Labyrinth 34 | '「Hello, World!」と表示。', // なでしこ 35 | '/ World! World!/Hello,/ World! World! World!', // Slashes 36 | '("Hello, World!"sP', // Snowman 37 | '["Hello, World!"] | stdout', // Streem 38 | 'HHHeeelll lllooo wwwooorrrlllddd!!!', // Trigger 39 | '(Hello, world!)S', // Underload 40 | '`r```````````.H.e.l.l.o. .W.o.r.l.di', // Unlambda 41 | 'h#10 h$! h$d h$l h$r h$o h$W h#32 h$, h$o h$l h$l h$e h$H >o o$ p jno', // xEec 42 | '"Hello, World!"', // Generic 43 | 'Hello, World!', // Generic 44 | ]), 45 | }); 46 | }; 47 | -------------------------------------------------------------------------------- /controllers/user.js: -------------------------------------------------------------------------------- 1 | /* 2 | * GET /login 3 | * Login page. 4 | */ 5 | module.exports.getLogin = (req, res) => { 6 | if (req.user) { 7 | res.redirect('/'); 8 | return; 9 | } 10 | res.render('account/login', { 11 | title: 'Login', 12 | }); 13 | }; 14 | 15 | /* 16 | * GET /logout 17 | * Log out. 18 | */ 19 | module.exports.logout = (req, res) => { 20 | req.logout((error) => { 21 | if (!error) { 22 | res.redirect('/'); 23 | } 24 | }); 25 | }; 26 | 27 | /* 28 | * GET /account 29 | * Profile page. 30 | */ 31 | module.exports.getAccount = (req, res) => { 32 | res.render('account/profile', { 33 | title: 'Account Management', 34 | }); 35 | }; 36 | -------------------------------------------------------------------------------- /data/build-infos.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const {promisify} = require('util'); 4 | const ptrace = require('../lib/ptrace.js'); 5 | const langs = require('./langs.json'); 6 | 7 | const traces = new Map(); 8 | const times = new Map(); 9 | 10 | (async () => { 11 | const files = await promisify(fs.readdir)(process.argv[2]); 12 | for (const file of files) { 13 | if (file.endsWith('_hello.log')) { 14 | const trace = await promisify(fs.readFile)( 15 | path.join(process.argv[2], file), 16 | ); 17 | traces.set( 18 | file 19 | .split('_') 20 | .slice(0, -1) 21 | .join('_'), 22 | ptrace.parse(trace.toString()), 23 | ); 24 | } 25 | if (file.endsWith('_hello.txt')) { 26 | const info = await promisify(fs.readFile)( 27 | path.join(process.argv[2], file), 28 | ); 29 | times.set( 30 | file 31 | .split('_') 32 | .slice(0, -1) 33 | .join('_'), 34 | parseFloat( 35 | info 36 | .toString() 37 | .trim() 38 | .split(': ')[1], 39 | ), 40 | ); 41 | } 42 | } 43 | const infos = langs 44 | .filter(({name}) => name !== null) 45 | .map((lang) => ({ 46 | slug: lang.slug, 47 | execs: traces.get(lang.slug), 48 | time: times.get(lang.slug), 49 | })); 50 | await promisify(fs.writeFile)( 51 | path.join(__dirname, 'infos.json'), 52 | JSON.stringify(infos, null, ' '), 53 | ); 54 | })(); 55 | -------------------------------------------------------------------------------- /data/build-langs.rb: -------------------------------------------------------------------------------- 1 | # Usage: ruby build-langs.rb /path/to/esolang-box/boxes.yml 2 | 3 | require 'yaml' 4 | require 'json' 5 | 6 | $boxes = YAML::load_file(ARGV[0]) 7 | 8 | $langs = [] 9 | 10 | def iterate(lang, parent = nil, depth = 0) 11 | lang.each do |key, value| 12 | unless key.start_with? '_' 13 | $langs << {slug: key, name: value['_name'], link: value['_link'], disasm: !value['_disasm'].nil?} 14 | iterate value, key, depth + 1 15 | end 16 | end 17 | end 18 | 19 | iterate $boxes 20 | 21 | File.write File.join(__dir__, 'langs.json'), JSON.pretty_generate($langs) 22 | -------------------------------------------------------------------------------- /data/languages/4.js: -------------------------------------------------------------------------------- 1 | const langsData = require('../langs.json'); 2 | 3 | const languages = { 4 | 0: '', 5 | 1: 'ruby', 6 | 2: 'c-gcc', 7 | 3: 'perl', 8 | 4: 'golfscript', 9 | 5: 'node', 10 | 6: 'rust', 11 | 7: 'd-dmd', 12 | 8: '', 13 | 9: 'python3', 14 | 10: '', 15 | 11: 'php', 16 | 12: 'csharp', 17 | 13: 'unlambda', 18 | 14: 'powershell', 19 | 15: 'nadesiko', 20 | 16: 'ruby0.49', 21 | 17: 'alice', 22 | 18: 'gs2', 23 | 19: 'whitespace', 24 | 20: 'i4004asm', 25 | 21: 'java', 26 | 22: 'ocaml', 27 | 23: 'crystal', 28 | 24: 'cy', 29 | 25: 'kotlin', 30 | 26: 'braille', 31 | 27: 'vim', 32 | 28: 'element', 33 | 29: 'wierd', 34 | 30: 'cubix', 35 | 31: 'make', 36 | 32: 'function2d', 37 | 33: 'labyrinth', 38 | 34: 'z80', 39 | 35: 'swift', 40 | 36: 'convex', 41 | 37: 'stuck', 42 | 38: 'fish', 43 | 39: 'bash-busybox', 44 | 40: 'cmd', 45 | 41: 'lua', 46 | 42: 'verilog', 47 | 43: 'zucchini', 48 | 44: 'doubleplusungood', 49 | 45: 'jq', 50 | 46: 'japt', 51 | 47: 'snowman', 52 | 48: 'taxi', 53 | 49: 'emojicode', 54 | 50: 'grass', 55 | 51: 'htms', 56 | 52: 'sceql', 57 | 53: 'adjust', 58 | 54: 'stop', 59 | 55: 'brainfuck-bfi', 60 | 56: 'rprogn', 61 | 57: 'wordcpu', 62 | 58: 'rail', 63 | 59: 'typhon', 64 | 60: 'dis', 65 | 61: 'aheui', 66 | 62: 'apl', 67 | 63: 'asciidots', 68 | 64: 'wake', 69 | 65: 'minus', 70 | 66: 'sqlite3', 71 | 67: 'maybelater', 72 | 68: 'minimal2d', 73 | 69: 'slashes', 74 | 70: 'width', 75 | 71: 'floater', 76 | 72: 'simula', 77 | 73: 'malbolge', 78 | 74: 'suzy', 79 | 75: 'path', 80 | 76: 'whenever', 81 | 77: 'aubergine', 82 | 78: 'beam', 83 | 79: 'goruby', 84 | 80: 'wat', 85 | 81: 'x86asm-nasm', 86 | 82: 'llvm-ir', 87 | 83: 'brainfuck-esotope', 88 | 84: 'fernando', 89 | 85: 'blc', 90 | 86: 'piet', 91 | 87: 'pure-folders', 92 | 88: 'stackcats', 93 | 89: 'fugue', 94 | 90: 'lazyk', 95 | 91: 'befunge98', 96 | }; 97 | 98 | module.exports = Array(92) 99 | .fill() 100 | .map((_, index) => { 101 | if (index === 0) { 102 | return { 103 | type: 'base', 104 | team: 0, 105 | }; 106 | } 107 | 108 | if (index === 8) { 109 | return { 110 | type: 'base', 111 | team: 1, 112 | }; 113 | } 114 | 115 | if (index === 10) { 116 | return { 117 | type: 'base', 118 | team: 2, 119 | }; 120 | } 121 | 122 | const langDatum = langsData.find((lang) => lang.slug === languages[index]); 123 | 124 | return { 125 | type: 'language', 126 | slug: languages[index], 127 | name: langDatum ? langDatum.name : '', 128 | link: langDatum ? langDatum.link : '', 129 | }; 130 | }); 131 | -------------------------------------------------------------------------------- /data/languages/6.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const langsData = require('../langs.json'); 3 | 4 | const languages = { 5 | 0: null, 6 | 1: null, 7 | 2: null, 8 | 3: null, 9 | 4: null, 10 | 5: null, 11 | 6: null, 12 | 7: null, 13 | 8: null, 14 | 9: null, 15 | 10: null, 16 | 11: null, 17 | 12: null, 18 | 13: 'snowman', 19 | 14: 'hexagony', 20 | 15: 'hypertorus', 21 | 16: 'pyramid-scheme', 22 | 17: 'cobol', 23 | 18: 'gnuplot', 24 | 19: null, 25 | 20: null, 26 | 21: null, 27 | 22: null, 28 | 23: null, 29 | 24: 'alphabeta', 30 | 25: 'braintwist', 31 | 26: 'v-vim', 32 | 27: 'transceternal', 33 | 28: 'racket', 34 | 29: 'wysiscript', 35 | 30: 'abc', 36 | 31: null, 37 | 32: null, 38 | 33: null, 39 | 34: 'oh', 40 | 35: 'husk', 41 | 36: 'produire', 42 | 37: 'olang', 43 | 38: 'golfscript', 44 | 39: 'make', 45 | 40: 'reasonml', 46 | 41: 'unlambda', 47 | 42: null, 48 | 43: null, 49 | 44: null, 50 | 45: 'rail', 51 | 46: 'qlb', 52 | 47: 'mines', 53 | 48: 'vlang', 54 | 49: null, 55 | 50: 'egison', 56 | 51: 'tex', 57 | 52: 'pure-folders', 58 | 53: 'cpp-compile-time-clang', 59 | 54: null, 60 | 55: 'piet', 61 | 56: 'golfish', 62 | 57: 'tetris', 63 | 58: 'php', 64 | 59: 'd-gdc', 65 | 60: 'perl', 66 | 61: 'lua', 67 | 62: 'classic-music-theory', 68 | 63: 'backhand', 69 | 64: 'pxem', 70 | 65: null, 71 | 66: 'starry', 72 | 67: 'bubble-sort', 73 | 68: 'vim', 74 | 69: 'node', 75 | 70: 'ruby', 76 | 71: 'c-gcc', 77 | 72: 'golang', 78 | 73: 'haskell', 79 | 74: 'bash-pure', 80 | 75: 'brainfuck-esotope', 81 | 76: 'coq', 82 | 77: 'z80', 83 | 78: 'fish', 84 | 79: 'stuck', 85 | 80: null, 86 | 81: 'rust', 87 | 82: 'python3', 88 | 83: null, 89 | 84: 'japt', 90 | 85: 'erlang', 91 | 86: 'fortran', 92 | 87: null, 93 | 88: null, 94 | 89: 'whitespace', 95 | 90: 'function2d', 96 | 91: 'jelly', 97 | 92: 'wren', 98 | 93: 'bash-busybox', 99 | 94: 'powershell', 100 | 95: 'cjam', 101 | 96: 'bots', 102 | 97: 'ring', 103 | 98: null, 104 | 99: null, 105 | 100: 'emojicode', 106 | 101: 'fish-shell-pure', 107 | 102: 'calc', 108 | 103: 'wenyan', 109 | 104: 'cyclicbrainfuck', 110 | 105: 'verilog', 111 | 106: 'awk', 112 | 107: 'ballerina', 113 | 108: null, 114 | 109: null, 115 | 110: null, 116 | 111: null, 117 | 112: 'sqlite3', 118 | 113: 'xslt', 119 | 114: 'gs2', 120 | 115: 'fernando', 121 | 116: 'jq', 122 | 117: 'fugue', 123 | 118: 'hanoi_stack', 124 | 119: null, 125 | 120: null, 126 | 121: null, 127 | 122: null, 128 | 123: 'moo', 129 | 124: 'standback', 130 | 125: 'iwashi', 131 | 126: 'apl', 132 | 127: 'cubically', 133 | 128: 'irc', 134 | 129: null, 135 | 130: null, 136 | 131: null, 137 | 132: null, 138 | 133: null, 139 | 134: null, 140 | 135: null, 141 | 136: null, 142 | 137: null, 143 | 138: null, 144 | 139: null, 145 | 140: null, 146 | 141: null, 147 | 142: null, 148 | }; 149 | 150 | const WIDTH = 11; 151 | const HEIGHT = 13; 152 | module.exports = Array(WIDTH * HEIGHT) 153 | .fill() 154 | .map((_, index) => { 155 | if (index === 49) { 156 | return { 157 | type: 'base', 158 | team: 0, 159 | }; 160 | } 161 | 162 | if (index === 80) { 163 | return { 164 | type: 'base', 165 | team: 1, 166 | }; 167 | } 168 | 169 | if (index === 83) { 170 | return { 171 | type: 'base', 172 | team: 2, 173 | }; 174 | } 175 | 176 | if (languages[index] === null) { 177 | return null; 178 | } 179 | 180 | const langDatum = langsData.find((lang) => lang.slug === languages[index]); 181 | assert(langDatum, languages[index]); 182 | 183 | return { 184 | type: 'language', 185 | slug: languages[index], 186 | name: langDatum.name, 187 | link: langDatum.link, 188 | }; 189 | }); 190 | -------------------------------------------------------------------------------- /data/languages/hackathon2018.js: -------------------------------------------------------------------------------- 1 | const langsData = require('../langs.json'); 2 | 3 | const languages = [ 4 | 'reversed-c', 5 | 'iwashi', 6 | 'standback', 7 | 'multi-reader', 8 | 'copos-rb', 9 | 'golfish', 10 | 'moo', 11 | 'codemania', 12 | 'bots', 13 | 'floating', 14 | 'picfunge', 15 | 'hanoi_stack', 16 | 'exchangeif', 17 | 'unicue', 18 | ]; 19 | 20 | module.exports = languages 21 | .map((language) => { 22 | const langDatum = langsData.find((lang) => lang.slug === language); 23 | 24 | return { 25 | type: 'language', 26 | slug: language, 27 | name: langDatum ? langDatum.name : '', 28 | link: langDatum ? langDatum.link : '', 29 | }; 30 | }) 31 | .concat([ 32 | { 33 | type: 'base', 34 | team: 0, 35 | }, 36 | { 37 | type: 'base', 38 | team: 1, 39 | }, 40 | { 41 | type: 'base', 42 | team: 2, 43 | }, 44 | { 45 | type: 'base', 46 | team: 3, 47 | }, 48 | { 49 | type: 'base', 50 | team: 4, 51 | }, 52 | ]); 53 | -------------------------------------------------------------------------------- /data/languages/index.js: -------------------------------------------------------------------------------- 1 | const languages1 = require('./1.js'); 2 | const languages2 = require('./2.js'); 3 | const languages3 = require('./3.js'); 4 | const languages4 = require('./4.js'); 5 | const languages5 = require('./5.js'); 6 | const languages6 = require('./6.js'); 7 | const hackathon2018 = require('./hackathon2018'); 8 | const komabasai2018Day1 = require('./komabasai2018-day1'); 9 | const komabasai2018Day2 = require('./komabasai2018-day2'); 10 | const komabasai2019 = require('./komabasai2019'); 11 | const komabasai2022 = require('./komabasai2022'); 12 | const mayfes2018Day1 = require('./mayfes2018-day1'); 13 | const mayfes2018Day2 = require('./mayfes2018-day2'); 14 | const mayfes2019Day1 = require('./mayfes2019-day1'); 15 | const mayfes2019Day2 = require('./mayfes2019-day2'); 16 | const mayfes2020Day1 = require('./mayfes2020-day1'); 17 | const mayfes2020Day2 = require('./mayfes2020-day2'); 18 | const mayfes2021Day1 = require('./mayfes2021-day1'); 19 | const mayfes2021Day2 = require('./mayfes2021-day2'); 20 | const mayfes2021Practice1 = require('./mayfes2021-practice1'); 21 | 22 | module.exports = { 23 | 1: languages1, 24 | 2: languages2, 25 | 3: languages3, 26 | 4: languages4, 27 | 5: languages5, 28 | 6: languages6, 29 | 'mayfes2018-day1': mayfes2018Day1, 30 | 'mayfes2018-day2': mayfes2018Day2, 31 | hackathon2018, 32 | 'komabasai2018-day1': komabasai2018Day1, 33 | 'komabasai2018-day2': komabasai2018Day2, 34 | 'mayfes2019-day1': mayfes2019Day1, 35 | 'mayfes2019-day2': mayfes2019Day2, 36 | komabasai2019, 37 | 'mayfes2020-day1': mayfes2020Day1, 38 | 'mayfes2020-day2': mayfes2020Day2, 39 | 'mayfes2021-day1': mayfes2021Day1, 40 | 'mayfes2021-day2': mayfes2021Day2, 41 | 'mayfes2021-practice1': mayfes2021Practice1, 42 | 'mayfes2021-practice2': mayfes2021Day2, 43 | 'komabasai2022-practice': komabasai2022, 44 | komabasai2022, 45 | }; 46 | -------------------------------------------------------------------------------- /data/languages/komabasai2018-day1.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const flatten = require('lodash/flatten'); 3 | const langsData = require('../langs.json'); 4 | 5 | const languages = [ 6 | ['', 'whitespace', 'bash-busybox', ''], 7 | ['starry', '', 'python3', 'php'], 8 | ['node', 'ruby', '', 'vim'], 9 | ['', 'java', 'fish', ''], 10 | ]; 11 | 12 | module.exports = flatten(languages).map((language, index) => { 13 | if (index === 5) { 14 | return { 15 | type: 'base', 16 | team: 0, 17 | }; 18 | } 19 | 20 | if (index === 10) { 21 | return { 22 | type: 'base', 23 | team: 1, 24 | }; 25 | } 26 | 27 | const langDatum = langsData.find((lang) => lang.slug === language); 28 | assert(language === '' || langDatum !== undefined, language); 29 | 30 | return { 31 | type: 'language', 32 | slug: language, 33 | name: langDatum ? langDatum.name : '', 34 | link: langDatum ? langDatum.link : '', 35 | }; 36 | }); 37 | -------------------------------------------------------------------------------- /data/languages/komabasai2018-day2.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const flatten = require('lodash/flatten'); 3 | const langsData = require('../langs.json'); 4 | 5 | const languages = [ 6 | ['', 'whitespace', 'haskell', ''], 7 | ['hexagony', '', 'ruby', 'kotlin'], 8 | ['csharp', 'c-gcc', '', 'unlambda'], 9 | ['', 'd-gdc', 'befunge98', ''], 10 | ]; 11 | 12 | module.exports = flatten(languages).map((language, index) => { 13 | if (index === 5 || index === 15) { 14 | return { 15 | type: 'base', 16 | team: 0, 17 | }; 18 | } 19 | 20 | if (index === 10 || index === 0) { 21 | return { 22 | type: 'base', 23 | team: 1, 24 | }; 25 | } 26 | 27 | const langDatum = langsData.find((lang) => lang.slug === language); 28 | assert(language === '' || langDatum !== undefined, language); 29 | 30 | return { 31 | type: 'language', 32 | slug: language, 33 | name: langDatum ? langDatum.name : '', 34 | link: langDatum ? langDatum.link : '', 35 | }; 36 | }); 37 | -------------------------------------------------------------------------------- /data/languages/komabasai2019.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const flatten = require('lodash/flatten'); 3 | const langsData = require('../langs.json'); 4 | 5 | const languages = [ 6 | ['', 'produire', 'sed', 'calc', ''], 7 | ['aheui', '', 'python3', '', 'piet'], 8 | ['fish', 'c-gcc', 'ruby', 'perl', 'vim'], 9 | ['brainfuck-esotope', '', 'node', '', 'snowman'], 10 | ['', 'whitespace', 'golfscript', 'starry', ''], 11 | ]; 12 | 13 | module.exports = flatten(languages).map((language, index) => { 14 | if (index === 8 || index === 16) { 15 | return { 16 | type: 'base', 17 | team: 1, 18 | }; 19 | } 20 | 21 | if (index === 6 || index === 18) { 22 | return { 23 | type: 'base', 24 | team: 0, 25 | }; 26 | } 27 | 28 | const langDatum = langsData.find((lang) => lang.slug === language); 29 | assert(language === '' || langDatum !== undefined, language); 30 | 31 | return { 32 | type: 'language', 33 | slug: language, 34 | name: langDatum ? langDatum.name : '', 35 | link: langDatum ? langDatum.link : '', 36 | }; 37 | }); 38 | -------------------------------------------------------------------------------- /data/languages/komabasai2022.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const flatten = require('lodash/flatten'); 3 | const langsData = require('../langs.json'); 4 | 5 | const languages = [ 6 | ['', 'fish', 'produire', 'cpp-clang', ''], 7 | ['', '', 'haskell', '', ''], 8 | ['', 'c-gcc', 'brainfuck-esomer', 'python3', ''], 9 | ['', '', 'node', '', ''], 10 | ['', 'ruby', 'aheui', 'ocaml', ''], 11 | ]; 12 | 13 | module.exports = flatten(languages).map((language, index) => { 14 | if (index === 6 || index === 18) { 15 | return { 16 | type: 'base', 17 | team: 1, 18 | }; 19 | } 20 | 21 | if (index === 8 || index === 16) { 22 | return { 23 | type: 'base', 24 | team: 0, 25 | }; 26 | } 27 | 28 | const langDatum = langsData.find((lang) => lang.slug === language); 29 | assert(language === '' || langDatum !== undefined, language); 30 | 31 | return { 32 | type: 'language', 33 | slug: language, 34 | name: langDatum ? langDatum.name : '', 35 | link: langDatum ? langDatum.link : '', 36 | }; 37 | }); 38 | -------------------------------------------------------------------------------- /data/languages/mayfes2018-day1.js: -------------------------------------------------------------------------------- 1 | const flatten = require('lodash/flatten'); 2 | const langsData = require('../langs.json'); 3 | 4 | const languages = [ 5 | ['', 'ruby', 'starry'], 6 | ['cjam', 'c-gcc', 'golfscript'], 7 | ['piet', 'python3', ''], 8 | ]; 9 | 10 | module.exports = flatten(languages).map((language, index) => { 11 | if (index === 0) { 12 | return { 13 | type: 'base', 14 | team: 0, 15 | }; 16 | } 17 | 18 | if (index === 8) { 19 | return { 20 | type: 'base', 21 | team: 1, 22 | }; 23 | } 24 | 25 | const langDatum = langsData.find((lang) => lang.slug === language); 26 | 27 | return { 28 | type: 'language', 29 | slug: language, 30 | name: langDatum ? langDatum.name : '', 31 | link: langDatum ? langDatum.link : '', 32 | }; 33 | }); 34 | -------------------------------------------------------------------------------- /data/languages/mayfes2018-day2.js: -------------------------------------------------------------------------------- 1 | const flatten = require('lodash/flatten'); 2 | const langsData = require('../langs.json'); 3 | 4 | const languages = [ 5 | ['', 'node', 'hexagony'], 6 | ['convex', 'c-gcc', 'jelly'], 7 | ['whitespace', 'ruby', ''], 8 | ]; 9 | 10 | module.exports = flatten(languages).map((language, index) => { 11 | if (index === 0) { 12 | return { 13 | type: 'base', 14 | team: 0, 15 | }; 16 | } 17 | 18 | if (index === 8) { 19 | return { 20 | type: 'base', 21 | team: 1, 22 | }; 23 | } 24 | 25 | const langDatum = langsData.find((lang) => lang.slug === language); 26 | 27 | return { 28 | type: 'language', 29 | slug: language, 30 | name: langDatum ? langDatum.name : '', 31 | link: langDatum ? langDatum.link : '', 32 | }; 33 | }); 34 | -------------------------------------------------------------------------------- /data/languages/mayfes2019-day1.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const flatten = require('lodash/flatten'); 3 | const langsData = require('../langs.json'); 4 | 5 | const languages = [ 6 | ['', 'emoji', 'brainfuck-esotope', '', ''], 7 | ['', 'java', 'perl', '', 'wake'], 8 | ['starry', 'c-gcc', '', 'ruby', 'fish'], 9 | ['irc', '', 'python3', 'bash-busybox', ''], 10 | ['', '', 'jq', 'unreadable', ''], 11 | ]; 12 | 13 | module.exports = flatten(languages).map((language, index) => { 14 | if (index === 8 || index === 16) { 15 | return { 16 | type: 'base', 17 | team: 1, 18 | }; 19 | } 20 | 21 | if (index === 12) { 22 | return { 23 | type: 'base', 24 | team: 0, 25 | }; 26 | } 27 | 28 | const langDatum = langsData.find((lang) => lang.slug === language); 29 | assert(language === '' || langDatum !== undefined, language); 30 | 31 | return { 32 | type: 'language', 33 | slug: language, 34 | name: langDatum ? langDatum.name : '', 35 | link: langDatum ? langDatum.link : '', 36 | }; 37 | }); 38 | -------------------------------------------------------------------------------- /data/languages/mayfes2019-day2.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const flatten = require('lodash/flatten'); 3 | const langsData = require('../langs.json'); 4 | 5 | const languages = [ 6 | ['', 'snowman', 'golfscript', '', ''], 7 | ['', 'perl', 'c-gcc', '', 'starry'], 8 | ['whitespace', 'java', '', 'ruby', 'rail'], 9 | ['cubix', '', 'python3', 'node', ''], 10 | ['', '', 'convex', 'cardinal', ''], 11 | ]; 12 | 13 | module.exports = flatten(languages).map((language, index) => { 14 | if (index === 8 || index === 16) { 15 | return { 16 | type: 'base', 17 | team: 1, 18 | }; 19 | } 20 | 21 | if (index === 12) { 22 | return { 23 | type: 'base', 24 | team: 0, 25 | }; 26 | } 27 | 28 | const langDatum = langsData.find((lang) => lang.slug === language); 29 | assert(language === '' || langDatum !== undefined, language); 30 | 31 | return { 32 | type: 'language', 33 | slug: language, 34 | name: langDatum ? langDatum.name : '', 35 | link: langDatum ? langDatum.link : '', 36 | }; 37 | }); 38 | -------------------------------------------------------------------------------- /data/languages/mayfes2020-day1.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const flatten = require('lodash/flatten'); 3 | const langsData = require('../langs.json'); 4 | 5 | const languages = [ 6 | ['', 'golfscript', 'brainfuck-esotope', 'bash-busybox', ''], 7 | ['', '', 'ruby', '', ''], 8 | ['', 'node', 'fish', 'python3', ''], 9 | ['', '', 'c-gcc', '', ''], 10 | ['', 'fernando', 'emoji', 'ppap', ''], 11 | ]; 12 | 13 | module.exports = flatten(languages).map((language, index) => { 14 | if (index === 6 || index === 18) { 15 | return { 16 | type: 'base', 17 | team: 1, 18 | }; 19 | } 20 | 21 | if (index === 8 || index == 16) { 22 | return { 23 | type: 'base', 24 | team: 0, 25 | }; 26 | } 27 | 28 | const langDatum = langsData.find((lang) => lang.slug === language); 29 | assert(language === '' || langDatum !== undefined, language); 30 | 31 | return { 32 | type: 'language', 33 | slug: language, 34 | name: langDatum ? langDatum.name : '', 35 | link: langDatum ? langDatum.link : '', 36 | }; 37 | }); 38 | -------------------------------------------------------------------------------- /data/languages/mayfes2020-day2.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const langsData = require('../langs.json'); 3 | 4 | const languages = [ 5 | 'crystal', // 京都府 6 | 'whitespace', // 愛知県 7 | 'perl', // 栃木県 8 | 'python3', // 山梨県 9 | 'golfscript', // 滋賀県 10 | 'wake', // 群馬県 11 | 'rail', // 静岡県 12 | '', // 茨城県(0-base) 13 | 'aheui', // 和歌山県 14 | 'golang', // 岐阜県 15 | 'node', // 大阪府 16 | 'c-gcc', // 長野県 17 | 'brainfuck-esotope', // 三重県 18 | '', // 兵庫県(1-base) 19 | 'husk', // 千葉県 20 | '', // 富山県(0-base) 21 | '', // 東京都(1-base) 22 | 'ruby', // 埼玉県 23 | 'piet', // 石川県 24 | 'unlambda', // 福井県 25 | 'starry', // 奈良県 26 | 'fish', // 新潟県 27 | 'emojicode', // 神奈川県 28 | ]; 29 | 30 | module.exports = languages.map((language, index) => { 31 | if (index === 7 || index === 15) { 32 | return { 33 | type: 'base', 34 | team: 0, 35 | }; 36 | } 37 | if (index === 13 || index === 16) { 38 | return { 39 | type: 'base', 40 | team: 1, 41 | }; 42 | } 43 | 44 | const langDatum = langsData.find((lang) => lang.slug === language); 45 | assert(language === '' || langDatum !== undefined, language); 46 | 47 | return { 48 | type: 'language', 49 | slug: language, 50 | name: langDatum ? langDatum.name : '', 51 | link: langDatum ? langDatum.link : '', 52 | }; 53 | }); 54 | -------------------------------------------------------------------------------- /data/languages/mayfes2021-day1.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const flatten = require('lodash/flatten'); 3 | const langsData = require('../langs.json'); 4 | 5 | const languages = [ 6 | ['', 'golfscript', 'fernando', 'c-gcc', ''], 7 | ['', '', 'node', '', ''], 8 | ['', 'produire', 'brainfuck-esotope', 'bash-busybox', ''], 9 | ['', '', 'python3', '', ''], 10 | ['', 'ruby', 'mao', 'asciidots', ''], 11 | ]; 12 | 13 | module.exports = flatten(languages).map((language, index) => { 14 | if (index === 6 || index === 18) { 15 | return { 16 | type: 'base', 17 | team: 1, 18 | }; 19 | } 20 | 21 | if (index === 8 || index === 16) { 22 | return { 23 | type: 'base', 24 | team: 0, 25 | }; 26 | } 27 | 28 | const langDatum = langsData.find((lang) => lang.slug === language); 29 | assert(language === '' || langDatum !== undefined, language); 30 | 31 | return { 32 | type: 'language', 33 | slug: language, 34 | name: langDatum ? langDatum.name : '', 35 | link: langDatum ? langDatum.link : '', 36 | }; 37 | }); 38 | -------------------------------------------------------------------------------- /data/languages/mayfes2021-day2.js: -------------------------------------------------------------------------------- 1 | const langsData = require('../langs.json'); 2 | 3 | const languages = { 4 | // squares 5 | 0: '', 6 | 1: 'ppap', 7 | 2: 'aheui', 8 | 3: 'cpp-compile-time-clang', 9 | 4: 'tex', 10 | 5: '', 11 | 6: '', 12 | 7: 'bash-busybox', 13 | 8: 'compile-time-typescript', 14 | 9: 'jq', 15 | 10: 'ocaml', 16 | 11: '', 17 | 18 | // hexagons 19 | 12: 'perl', 20 | 13: 'befunge98', 21 | 14: 'fernando', 22 | 15: 'starry', 23 | 16: 'produire', 24 | 17: 'php', 25 | 18: 'fish', 26 | 19: 'mao', 27 | 28 | // octagons 29 | 20: 'whitespace', 30 | 21: 'node', 31 | 22: 'c-gcc', 32 | 23: 'python3', 33 | 24: 'ruby', 34 | 25: 'brainfuck-esotope', 35 | }; 36 | 37 | module.exports = Array(26) 38 | .fill() 39 | .map((_, index) => { 40 | if (index === 0 || index === 11) { 41 | return { 42 | type: 'base', 43 | team: 0, 44 | }; 45 | } 46 | 47 | if (index === 5 || index === 6) { 48 | return { 49 | type: 'base', 50 | team: 1, 51 | }; 52 | } 53 | 54 | const langDatum = langsData.find((lang) => lang.slug === languages[index]); 55 | 56 | return { 57 | type: 'language', 58 | slug: languages[index], 59 | name: langDatum ? langDatum.name : '', 60 | link: langDatum ? langDatum.link : '', 61 | }; 62 | }); 63 | -------------------------------------------------------------------------------- /data/languages/mayfes2021-practice1.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const flatten = require('lodash/flatten'); 3 | const langsData = require('../langs.json'); 4 | 5 | const languages = [ 6 | ['', 'asciidots', 'bash-busybox', 'brainfuck-esotope', ''], 7 | ['', '', 'c-gcc', '', ''], 8 | ['', 'fernando', 'golfscript', 'mao', ''], 9 | ['', '', 'node', '', ''], 10 | ['', 'produire', 'python3', 'ruby', ''], 11 | ]; 12 | 13 | module.exports = flatten(languages).map((language, index) => { 14 | if (index === 6 || index === 18) { 15 | return { 16 | type: 'base', 17 | team: 1, 18 | }; 19 | } 20 | 21 | if (index === 8 || index === 16) { 22 | return { 23 | type: 'base', 24 | team: 0, 25 | }; 26 | } 27 | 28 | const langDatum = langsData.find((lang) => lang.slug === language); 29 | assert(language === '' || langDatum !== undefined, language); 30 | 31 | return { 32 | type: 'language', 33 | slug: language, 34 | name: langDatum ? langDatum.name : '', 35 | link: langDatum ? langDatum.link : '', 36 | }; 37 | }); 38 | -------------------------------------------------------------------------------- /data/truncated-cuboctahedron.js: -------------------------------------------------------------------------------- 1 | /* ! 2 | * Data from the website "Virtual Polyhedra: The Encyclopedia of Polyhedra" by George W. Hart 3 | * (c) George W. Hart, 1997. george@li.net 4 | * Dept. Computer Science, Hofstra University. 5 | * Freely distributable for noncommercial purposes. 6 | */ 7 | 8 | module.exports.points = [ 9 | [0, 0, 1.024117], 10 | [0.4314788, 0, 0.928785], 11 | [-0.02106287, 0.4309644, 0.928785], 12 | [-0.3410582, -0.2642977, 0.928785], 13 | [0.4104159, 0.4309644, 0.833453], 14 | [0.7006238, -0.2642977, 0.6986333], 15 | [-0.3831839, 0.5976311, 0.7381211], 16 | [-0.3919084, -0.6380712, 0.6986333], 17 | [-0.7031792, -0.09763107, 0.7381211], 18 | [0.6584981, 0.5976311, 0.5079694], 19 | [0.6497736, -0.6380712, 0.4684816], 20 | [0.948706, -0.09763107, 0.3731496], 21 | [-0.4638216, 0.8333333, 0.3731496], 22 | [-0.7242421, 0.3333333, 0.6427891], 23 | [-0.7540295, -0.4714045, 0.5079694], 24 | [-0.1227634, -0.9023689, 0.4684816], 25 | [0.5778604, 0.8333333, 0.1429979], 26 | [0.9276431, 0.3333333, 0.2778177], 27 | [0.8978557, -0.4714045, 0.1429979], 28 | [0.3087154, -0.9023689, 0.3731496], 29 | [-0.8048797, 0.5690356, 0.2778177], 30 | [-0.2157394, 1., 0.04766598], 31 | [-0.8470055, -0.5690356, 0.08715377], 32 | [-0.2157394, -1., 0.04766598], 33 | [0.8470055, 0.5690356, -0.08715377], 34 | [0.2157394, 1., -0.04766598], 35 | [0.8048797, -0.5690356, -0.2778177], 36 | [0.2157394, -1., -0.04766598], 37 | [-0.8978557, 0.4714045, -0.1429979], 38 | [-0.3087154, 0.9023689, -0.3731496], 39 | [-0.9276431, -0.3333333, -0.2778177], 40 | [-0.5778604, -0.8333333, -0.1429979], 41 | [0.7540295, 0.4714045, -0.5079694], 42 | [0.1227634, 0.9023689, -0.4684816], 43 | [0.7242421, -0.3333333, -0.6427891], 44 | [0.4638216, -0.8333333, -0.3731496], 45 | [-0.948706, 0.09763107, -0.3731496], 46 | [-0.6497736, 0.6380712, -0.4684816], 47 | [-0.6584981, -0.5976311, -0.5079694], 48 | [0.7031792, 0.09763107, -0.7381211], 49 | [0.3919084, 0.6380712, -0.6986333], 50 | [0.3831839, -0.5976311, -0.7381211], 51 | [-0.7006238, 0.2642977, -0.6986333], 52 | [-0.4104159, -0.4309644, -0.833453], 53 | [0.3410582, 0.2642977, -0.928785], 54 | [0.02106287, -0.4309644, -0.928785], 55 | [-0.4314788, 0, -0.928785], 56 | [0, 0, -1.024117], 57 | ]; 58 | 59 | module.exports.squares = [ 60 | [0, 1, 4, 2], 61 | [3, 8, 14, 7], 62 | [5, 10, 18, 11], 63 | [6, 12, 20, 13], 64 | [9, 17, 24, 16], 65 | [15, 23, 27, 19], 66 | [21, 25, 33, 29], 67 | [22, 30, 38, 31], 68 | [26, 35, 41, 34], 69 | [28, 37, 42, 36], 70 | [32, 39, 44, 40], 71 | [43, 46, 47, 45], 72 | ]; 73 | 74 | module.exports.hexagons = [ 75 | [0, 2, 6, 13, 8, 3], 76 | [1, 5, 11, 17, 9, 4], 77 | [7, 14, 22, 31, 23, 15], 78 | [10, 19, 27, 35, 26, 18], 79 | [12, 21, 29, 37, 28, 20], 80 | [16, 24, 32, 40, 33, 25], 81 | [30, 36, 42, 46, 43, 38], 82 | [34, 41, 45, 47, 44, 39], 83 | ]; 84 | 85 | module.exports.octagons = [ 86 | [0, 3, 7, 15, 19, 10, 5, 1], 87 | [2, 4, 9, 16, 25, 21, 12, 6], 88 | [8, 13, 20, 28, 36, 30, 22, 14], 89 | [11, 18, 26, 34, 39, 32, 24, 17], 90 | [23, 31, 38, 43, 45, 41, 35, 27], 91 | [29, 33, 40, 44, 47, 46, 42, 37], 92 | ]; 93 | -------------------------------------------------------------------------------- /firestore.indexes.json: -------------------------------------------------------------------------------- 1 | { 2 | "indexes": [ 3 | { 4 | "collectionGroup": "polyglot-battle-submissions", 5 | "queryScope": "COLLECTION", 6 | "fields": [ 7 | { 8 | "fieldPath": "status", 9 | "order": "ASCENDING" 10 | }, 11 | { 12 | "fieldPath": "createdAt", 13 | "order": "ASCENDING" 14 | } 15 | ] 16 | } 17 | ], 18 | "fieldOverrides": [] 19 | } 20 | -------------------------------------------------------------------------------- /lib/discord.js: -------------------------------------------------------------------------------- 1 | const Discord = require('discord.js'); 2 | 3 | const client = new Discord.Client({ 4 | intents: [ 5 | Discord.GatewayIntentBits.Guilds, 6 | Discord.GatewayIntentBits.GuildMessages, 7 | Discord.GatewayIntentBits.GuildVoiceStates, 8 | Discord.GatewayIntentBits.MessageContent, 9 | ], 10 | }); 11 | 12 | const readyPromise = new Promise((resolve) => { 13 | client.on('ready', () => { 14 | resolve(); 15 | }); 16 | }); 17 | 18 | if (process.env.DISCORD_TOKEN) { 19 | client.login(process.env.DISCORD_TOKEN); 20 | } 21 | 22 | module.exports.send = async (...args) => { 23 | await readyPromise; 24 | return client.channels.cache.get(process.env.DISCORD_CHANNEL).send(...args); 25 | }; 26 | -------------------------------------------------------------------------------- /lib/ptrace.js: -------------------------------------------------------------------------------- 1 | const groupBy = require('lodash/groupBy'); 2 | 3 | module.exports.parse = (trace) => { 4 | const lines = trace.split('\n').filter((line) => line.length); 5 | const entries = lines.map((line) => ({ 6 | pid: parseInt(line.slice(0, 6)), 7 | body: line.slice(6), 8 | })); 9 | const processes = groupBy(entries, (entry) => entry.pid); 10 | const execs = []; 11 | for (const processEntries of Object.values(processes)) { 12 | for (const entry of processEntries) { 13 | let matches = null; 14 | if ((matches = entry.body.match(/^(\w+)\("(.+?)"/))) { 15 | const syscall = matches[1]; 16 | const arg = matches[2]; 17 | 18 | if (syscall === 'execve') { 19 | execs.push(arg); 20 | } 21 | } 22 | } 23 | } 24 | return execs; 25 | }; 26 | -------------------------------------------------------------------------------- /lib/sass-middleware.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const sass = require('sass'); 4 | 5 | const cache = new Map(); 6 | 7 | module.exports = async function renderSass(req, res, next) { 8 | if (!req.path.endsWith('.css')) { 9 | return next(); 10 | } 11 | 12 | const file = path.join(process.cwd(), 'public', req.path).replace('.css', '.scss'); 13 | if (!fs.existsSync(file)) { 14 | return res.status(404).end(); 15 | } 16 | 17 | if (!cache.has(req.path)) { 18 | const data = sass.renderSync({ 19 | file, 20 | includePaths: [path.join(process.cwd(), 'node_modules')], 21 | outputStyle: process.env.NODE_ENV === 'production' ? 'compressed' : 'expanded', 22 | }); 23 | 24 | cache.set(req.path, data); 25 | 26 | fs.watchFile(file, () => { 27 | cache.delete(req.path); 28 | fs.unwatchFile(file); 29 | }); 30 | } 31 | 32 | res.header('content-type', 'text/css'); 33 | res.send(cache.get(req.path).css.toString()); 34 | }; 35 | -------------------------------------------------------------------------------- /lib/secret.v2.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const shuffle = require('array-shuffle'); 3 | 4 | module.exports.generateInput = () => shuffle([ 5 | ...Array.from({length: 10}, (e, i) => i), 6 | ...Array.from({length: 90}, () => Math.floor(Math.random() * 10)), 7 | ]).join(''); 8 | 9 | module.exports.isValidAnswer = (input, output) => { 10 | assert(input.match(/^\d{100}$/)); 11 | 12 | if (process.env.NODE_ENV !== 'production') { 13 | return true; 14 | } 15 | 16 | const correctOutput = input 17 | .split('') 18 | .map((n) => parseInt(n)) 19 | .sort((a, b) => a - b) 20 | .join(''); 21 | 22 | // Trim 23 | const trimmedOutput = output.toString().trim(); 24 | 25 | return trimmedOutput === correctOutput; 26 | }; 27 | -------------------------------------------------------------------------------- /lib/secret.v3.js: -------------------------------------------------------------------------------- 1 | const assert = require('assert'); 2 | const shuffle = require('array-shuffle'); 3 | 4 | const sampleSize = require('lodash/sampleSize'); 5 | 6 | const triangleNumbers = [ 7 | 0, 8 | 1, 9 | 3, 10 | 6, 11 | 10, 12 | 15, 13 | 21, 14 | 28, 15 | 36, 16 | 45, 17 | 55, 18 | 66, 19 | 78, 20 | 91, 21 | 105, 22 | 120, 23 | 136, 24 | 153, 25 | 171, 26 | 190, 27 | 210, 28 | 231, 29 | 253, 30 | ]; 31 | const notTriangleNumbers = Array.from({length: 256}, (e, i) => i).filter( 32 | (n) => !triangleNumbers.includes(n), 33 | ); 34 | 35 | module.exports.generateInput = () => shuffle([ 36 | ...triangleNumbers, 37 | triangleNumbers[Math.floor(Math.random() * triangleNumbers.length)], 38 | triangleNumbers[Math.floor(Math.random() * triangleNumbers.length)], 39 | ...sampleSize(notTriangleNumbers, 25), 40 | ]) 41 | .map((n) => `${n.toString(2).padStart(8, '0')}\n`) 42 | .join(''); 43 | 44 | module.exports.isValidAnswer = (input, output) => { 45 | assert(input.match(/^([01]{8}\n){50}$/)); 46 | 47 | if (process.env.NODE_ENV !== 'production') { 48 | // return true; 49 | } 50 | 51 | const correctOutput = input 52 | .split('\n') 53 | .filter((n) => n.length > 0) 54 | .map((n) => (triangleNumbers.includes(parseInt(n, 2)) ? '1' : '0')) 55 | .join(''); 56 | assert(correctOutput.length === 50); 57 | 58 | // Trim 59 | const trimmedOutput = output 60 | .toString() 61 | .replace(/\s/g, '') 62 | .trim(); 63 | console.log(input, correctOutput); 64 | 65 | return trimmedOutput === correctOutput; 66 | }; 67 | -------------------------------------------------------------------------------- /lib/socket-io.js: -------------------------------------------------------------------------------- 1 | // http://stackoverflow.com/a/28288406/2864502 2 | const io = require('socket.io')(); 3 | 4 | io.on('connection', (socket) => { 5 | console.log('Socket connected'); 6 | 7 | socket.on('disconnect', () => { 8 | console.log('Socket disconnected'); 9 | }); 10 | }); 11 | 12 | module.exports = io; 13 | -------------------------------------------------------------------------------- /models/Contest.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | const mongoose = require('mongoose'); 3 | 4 | const contestSchema = new mongoose.Schema({ 5 | name: {type: String}, 6 | id: {type: String, index: {unique: true}}, 7 | start: {type: Date}, 8 | end: {type: Date}, 9 | description: { 10 | ja: {type: String}, 11 | en: {type: String}, 12 | }, 13 | }); 14 | 15 | contestSchema.methods.isOpen = function() { 16 | if (process.env.NODE_ENV === 'development') { 17 | return true; 18 | } 19 | 20 | const now = new Date(); 21 | return this.start <= now && now <= this.end; 22 | }; 23 | 24 | contestSchema.methods.isStarted = function() { 25 | if (process.env.NODE_ENV === 'development') { 26 | return true; 27 | } 28 | 29 | const now = new Date(); 30 | return this.start <= now; 31 | }; 32 | 33 | contestSchema.methods.isEnded = function() { 34 | if (process.env.NODE_ENV === 'development') { 35 | return false; 36 | } 37 | 38 | const now = new Date(); 39 | return this.end < now; 40 | }; 41 | 42 | contestSchema.methods.spanText = function() { 43 | const startText = moment(this.start) 44 | .utcOffset(9) 45 | .format('YYYY/MM/DD HH:mm:ss'); 46 | const endText = moment(this.end) 47 | .utcOffset(9) 48 | .format('YYYY/MM/DD HH:mm:ss'); 49 | return `${startText} - ${endText}`; 50 | }; 51 | 52 | const Contest = mongoose.model('Contest', contestSchema); 53 | 54 | module.exports = Contest; 55 | -------------------------------------------------------------------------------- /models/Execution.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const executionSchema = new mongoose.Schema( 4 | { 5 | user: {type: mongoose.Schema.Types.ObjectId, ref: 'User'}, 6 | language: {type: mongoose.Schema.Types.ObjectId, ref: 'Language'}, 7 | code: Buffer, 8 | input: String, 9 | stdout: String, 10 | stderr: String, 11 | duration: Number, 12 | }, 13 | {timestamps: true}, 14 | ); 15 | 16 | const Execution = mongoose.model('Execution', executionSchema); 17 | 18 | module.exports = Execution; 19 | -------------------------------------------------------------------------------- /models/Language.js: -------------------------------------------------------------------------------- 1 | const mongoose = require('mongoose'); 2 | 3 | const languageSchema = new mongoose.Schema( 4 | { 5 | solution: {type: mongoose.Schema.Types.ObjectId, ref: 'Submission'}, 6 | contest: {type: mongoose.Schema.Types.ObjectId, ref: 'Contest'}, 7 | oldId: {type: String}, 8 | slug: {type: String}, 9 | }, 10 | {timestamps: true}, 11 | ); 12 | 13 | const Language = mongoose.model('Language', languageSchema); 14 | 15 | module.exports = Language; 16 | -------------------------------------------------------------------------------- /models/Submission.js: -------------------------------------------------------------------------------- 1 | const moment = require('moment'); 2 | const mongoose = require('mongoose'); 3 | 4 | const submissionSchema = new mongoose.Schema( 5 | { 6 | user: {type: mongoose.Schema.Types.ObjectId, ref: 'User'}, 7 | language: {type: mongoose.Schema.Types.ObjectId, ref: 'Language'}, 8 | contest: {type: mongoose.Schema.Types.ObjectId, ref: 'Contest'}, 9 | status: { 10 | type: String, 11 | enum: ['pending', 'failed', 'success', 'error', 'invalid'], 12 | }, 13 | code: Buffer, 14 | size: {type: Number, min: 0}, 15 | input: String, 16 | stdout: String, 17 | stderr: String, 18 | trace: String, 19 | disasm: String, 20 | duration: Number, 21 | url: String, 22 | error: { 23 | name: String, 24 | stack: String, 25 | }, 26 | }, 27 | {timestamps: true}, 28 | ); 29 | 30 | submissionSchema.methods.timeText = function() { 31 | return moment(this.createdAt) 32 | .utcOffset(9) 33 | .format('YYYY/MM/DD HH:mm:ss'); 34 | }; 35 | 36 | const Submission = mongoose.model('Submission', submissionSchema); 37 | 38 | module.exports = Submission; 39 | -------------------------------------------------------------------------------- /models/User.js: -------------------------------------------------------------------------------- 1 | const crypto = require('crypto'); 2 | const mongoose = require('mongoose'); 3 | 4 | const userSchema = new mongoose.Schema( 5 | { 6 | email: {type: String, unique: true}, 7 | twitter: String, 8 | tokens: Array, 9 | team: [ 10 | { 11 | contest: {type: mongoose.Schema.Types.ObjectId, ref: 'Contest'}, 12 | value: {type: Number, enum: [0, 1, 2, 3, 4]}, 13 | }, 14 | ], 15 | admin: {type: Boolean, default: false}, 16 | 17 | profile: { 18 | name: String, 19 | gender: String, 20 | location: String, 21 | website: String, 22 | picture: String, 23 | }, 24 | }, 25 | {timestamps: true}, 26 | ); 27 | 28 | userSchema.methods.name = function() { 29 | return `@${this.email.replace(/@.+$/, '')}`; 30 | }; 31 | 32 | userSchema.methods.getTeam = function(contest) { 33 | if (!this.team) { 34 | return null; 35 | } 36 | 37 | const teamInfo = this.team.find((team) => team.contest.equals(contest._id)); 38 | if (!teamInfo) { 39 | return null; 40 | } 41 | 42 | return teamInfo.value; 43 | }; 44 | 45 | userSchema.methods.setTeam = function(contest, newTeam) { 46 | console.log(this.team); 47 | this.team = this.team || []; 48 | 49 | if (this.team.some((team) => team.contest.equals(contest._id))) { 50 | this.team = this.team.map((team) => { 51 | if (team.contest.equals(contest._id)) { 52 | team.value = newTeam; 53 | } 54 | return team; 55 | }); 56 | } else { 57 | this.team.push({contest, value: newTeam}); 58 | } 59 | console.log(this.team); 60 | }; 61 | 62 | /* 63 | * Helper method for getting user's gravatar. 64 | */ 65 | userSchema.methods.gravatar = function(size = 200) { 66 | if (!this.email) { 67 | return `https://gravatar.com/avatar/?s=${size}&d=retro`; 68 | } 69 | const md5 = crypto 70 | .createHash('md5') 71 | .update(this.email) 72 | .digest('hex'); 73 | return `https://gravatar.com/avatar/${md5}?s=${size}&d=retro`; 74 | }; 75 | 76 | const User = mongoose.model('User', userSchema); 77 | 78 | module.exports = User; 79 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "esolang-battler", 3 | "private": true, 4 | "version": "1.0.0", 5 | "description": "Esolang codegolf battling platform", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/hakatashi/esolang-battle.git" 9 | }, 10 | "author": "Koki Takahashi ", 11 | "license": "MIT", 12 | "scripts": { 13 | "start": "node app.mjs", 14 | "test": "mocha --reporter spec --exit", 15 | "dev": "nodemon app.mjs --ignore public", 16 | "lint": "prettier-eslint '**/*.js' '**/*.mjs' '**/*.jsx' --ignore 'public/js/lib/**' --write && eslint '**/*.js' --ignore-pattern 'public/js/lib/**'" 17 | }, 18 | "engines": { 19 | "node": ">=16" 20 | }, 21 | "dependencies": { 22 | "@babel/core": "^7.23.2", 23 | "@babel/plugin-proposal-class-properties": "^7.18.6", 24 | "@babel/plugin-proposal-object-rest-spread": "^7.20.7", 25 | "@babel/polyfill": "^7.8.7", 26 | "@babel/preset-env": "^7.23.2", 27 | "@babel/preset-react": "^7.22.15", 28 | "@slack/web-api": "^6.9.0", 29 | "async-mutex": "^0.4.0", 30 | "babel-loader": "^9.1.3", 31 | "bootstrap": "^5.3.2", 32 | "chalk": "^5", 33 | "classnames": "^2.3.2", 34 | "common-tags": "^1.8.2", 35 | "compression": "^1.7.4", 36 | "concat-stream": "^2.0.0", 37 | "connect-mongo": "^5.1.0", 38 | "discord.js": "^14.13.0", 39 | "dockerode": "^4.0.0", 40 | "dotenv": "^16.3.1", 41 | "dotenv-expand": "^10.0.0", 42 | "errorhandler": "^1.5.1", 43 | "express": "^4.18.2", 44 | "express-flash": "^0.0.2", 45 | "express-promise-router": "^4.1.1", 46 | "express-session": "^1.17.3", 47 | "express-validator": "^7.0.1", 48 | "firebase-admin": "^11.11.0", 49 | "hexdump-stream": "pose/hexdump-stream", 50 | "lodash": "^4.17.21", 51 | "lusca": "^1.7.0", 52 | "markdown-it": "^13.0.2", 53 | "material-colors": "^1.2.6", 54 | "mathjs": "^11.11.2", 55 | "moment": "^2.29.4", 56 | "mongoose": "^7.6.2", 57 | "morgan": "^1.10.0", 58 | "multer": "^2.0.0-rc.4", 59 | "passport": "^0.6.0", 60 | "passport-twitter": "^1.0.4", 61 | "prop-types": "^15.8.1", 62 | "pug": "^3.0.2", 63 | "react": "^18.2.0", 64 | "react-dom": "^18.2.0", 65 | "reactstrap": "^9.2.0", 66 | "sass": "^1.69.3", 67 | "shell-escape": "^0.2.0", 68 | "socket.io": "^4.7.2", 69 | "three": "^0.157.0", 70 | "tmp": "^0.2.1", 71 | "utf-8-validate": "^6.0.3", 72 | "uuid": "^9.0.1", 73 | "webpack": "^5.89.0", 74 | "webpack-cli": "^5.1.4", 75 | "webpack-dev-middleware": "^6.1.1", 76 | "webpack-hot-middleware": "^2.25.4" 77 | }, 78 | "devDependencies": { 79 | "@hakatashi/eslint-config": "^1.20.0", 80 | "chai": "^4.3.10", 81 | "eslint": "^8.51.0", 82 | "mocha": "^10.2.0", 83 | "nodemon": "^3.0.1", 84 | "prettier-eslint-cli": "^8.0.1", 85 | "sinon": "^16", 86 | "sinon-mongoose": "^2.3.0", 87 | "supertest": "^6.3.3" 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /public/css/contests/1.scss: -------------------------------------------------------------------------------- 1 | @import "node_modules/material-colors/dist/colors.sass"; 2 | 3 | .board { 4 | display: flex; 5 | flex-direction: column; 6 | 7 | width: 90vmin; 8 | height: 90vmin; 9 | margin: 10px auto 0; 10 | position: relative; 11 | 12 | .row { 13 | display: flex; 14 | 15 | flex: 1 1 0; 16 | 17 | .cell { 18 | flex: 1 1 0; 19 | border: 1px solid #fff; 20 | border-radius: 8px; 21 | background: $md-grey-500; 22 | color: $md-grey-800; 23 | 24 | @media screen and (orientation: portrait) { 25 | border-radius: 4px; 26 | } 27 | 28 | &.user-cookies146 { 29 | background: $md-red-500; 30 | color: white; 31 | } 32 | 33 | &.user-satos___jp { 34 | background: $md-deep-purple-500; 35 | color: white; 36 | } 37 | 38 | &.user-kurgm { 39 | background: $md-orange-900; 40 | color: white; 41 | } 42 | 43 | &.user-yamayu832 { 44 | background: $md-green-800; 45 | color: white; 46 | } 47 | 48 | &.user-hakatashi { 49 | background: $md-light-blue-800; 50 | color: white; 51 | } 52 | 53 | &.user-00_ { 54 | background: $md-brown-500; 55 | color: white; 56 | } 57 | 58 | &.user-moratorium08 { 59 | background: $md-lime-900; 60 | color: white; 61 | } 62 | } 63 | } 64 | } 65 | 66 | .language { 67 | position: relative; 68 | 69 | .language-label { 70 | position: absolute; 71 | width: 100%; 72 | top: 50%; 73 | left: 50%; 74 | transform: translate(-50%, -50%); 75 | 76 | text-align: center; 77 | font-weight: bold; 78 | font-size: 1.5vmin; 79 | 80 | .size { 81 | font-size: 1.2em; 82 | } 83 | } 84 | } -------------------------------------------------------------------------------- /public/css/contests/2.scss: -------------------------------------------------------------------------------- 1 | .board { 2 | display: flex; 3 | flex-direction: column; 4 | 5 | width: 90vmin; 6 | height: 90vmin; 7 | margin: 10px auto 0; 8 | position: relative; 9 | 10 | .row { 11 | display: flex; 12 | 13 | flex: 1 1 0; 14 | 15 | .cell { 16 | flex: 1 1 0; 17 | border: 1px solid #fff; 18 | border-radius: 8px; 19 | color: #333; 20 | 21 | @media screen and (orientation: portrait) { 22 | border-radius: 4px; 23 | } 24 | 25 | &.black { 26 | background: #111; 27 | background: repeating-linear-gradient(-45deg,#111,#111 10px,#222 0,#222 20px); 28 | } 29 | 30 | &.gray { 31 | background: #737373; 32 | } 33 | 34 | &.red { 35 | background: #ef2011; 36 | color: white; 37 | } 38 | 39 | &.blue { 40 | background: #0e30ec; 41 | color: white; 42 | } 43 | 44 | &.white { 45 | background: white; 46 | } 47 | } 48 | } 49 | 50 | .team-info { 51 | position: absolute; 52 | top: 0; 53 | bottom: 0; 54 | width: 100px; 55 | display: flex; 56 | flex-direction: column; 57 | align-items: center; 58 | justify-content: flex-end; 59 | font-weight: bold; 60 | line-height: 1em; 61 | 62 | &.red { 63 | right: calc(100% + 30px); 64 | color: #ef2011; 65 | 66 | .bar { 67 | background-color: #ef2011; 68 | } 69 | } 70 | 71 | &.blue { 72 | left: calc(100% + 30px); 73 | color: #0e30ec; 74 | 75 | .bar { 76 | background-color: #0e30ec; 77 | } 78 | } 79 | 80 | @media screen and (orientation: portrait) { 81 | bottom: auto; 82 | right: 0; 83 | left: 0; 84 | height: 50px; 85 | width: auto; 86 | flex-direction: row-reverse; 87 | 88 | &.red { 89 | right: 0; 90 | top: calc(100% + 10px); 91 | } 92 | 93 | &.blue { 94 | left: 0; 95 | top: calc(100% + 60px); 96 | } 97 | } 98 | 99 | .team-name { 100 | font-size: 24px; 101 | line-height: 1em; 102 | } 103 | 104 | .score { 105 | font-size: 40px; 106 | line-height: 1em; 107 | } 108 | 109 | .bar { 110 | flex-basis: 0px; 111 | width: 100%; 112 | transition: flex-basis 1s; 113 | 114 | @media screen and (orientation: portrait) { 115 | height: 80%; 116 | margin-right: 5px; 117 | } 118 | } 119 | } 120 | } 121 | 122 | .language { 123 | position: relative; 124 | 125 | .language-label { 126 | position: absolute; 127 | width: 100%; 128 | top: 50%; 129 | left: 50%; 130 | transform: translate(-50%, -50%); 131 | 132 | text-align: center; 133 | font-weight: bold; 134 | font-size: 2vmin; 135 | 136 | .size { 137 | font-size: 1.2em; 138 | } 139 | } 140 | } -------------------------------------------------------------------------------- /public/css/contests/4.scss: -------------------------------------------------------------------------------- 1 | .world { 2 | display: flex; 3 | justify-content: space-between; 4 | 5 | @media screen and (orientation: portrait) { 6 | flex-direction: column; 7 | } 8 | 9 | .spacer { 10 | width: 200px; 11 | } 12 | 13 | .map { 14 | position: relative; 15 | 16 | @media screen and (orientation: portrait) { 17 | overflow: hidden; 18 | } 19 | 20 | .language-labels { 21 | pointer-events: none; 22 | } 23 | 24 | .language-label { 25 | position: absolute; 26 | top: 0; 27 | left: 0; 28 | width: 200px; 29 | 30 | line-height: 1.3em; 31 | font-size: 3.8vmin; 32 | font-weight: bold; 33 | text-align: center; 34 | color: white; 35 | 36 | .language-size { 37 | font-size: 1.3em; 38 | } 39 | } 40 | } 41 | 42 | .teams { 43 | display: flex; 44 | flex-basis: 200px; 45 | 46 | @media screen and (orientation: portrait) { 47 | flex-direction: column; 48 | flex-basis: auto; 49 | } 50 | 51 | .team { 52 | width: 50px; 53 | display: flex; 54 | flex-direction: column; 55 | justify-content: flex-end; 56 | margin-right: 15px; 57 | 58 | @media screen and (orientation: portrait) { 59 | width: auto; 60 | height: 25px; 61 | flex-direction: row-reverse; 62 | margin: 0 0 5px; 63 | } 64 | 65 | &.red .bar { 66 | background-color: #ef2011; 67 | color: #ef2011; 68 | } 69 | 70 | &.blue .bar { 71 | background-color: #0e30ec; 72 | color: #0e30ec; 73 | } 74 | 75 | &.green .bar { 76 | background-color: #167516; 77 | color: #167516; 78 | } 79 | 80 | .bar { 81 | position: relative; 82 | } 83 | 84 | .count { 85 | position: absolute; 86 | bottom: 100%; 87 | text-align: center; 88 | width: 100%; 89 | font-weight: bold; 90 | font-size: 32px; 91 | 92 | @media screen and (orientation: portrait) { 93 | left: calc(100% + 5px); 94 | width: auto; 95 | top: 0; 96 | bottom: 0; 97 | line-height: 30px; 98 | } 99 | } 100 | 101 | .team-name { 102 | position: absolute; 103 | bottom: calc(100% + 35px); 104 | text-align: center; 105 | width: 100%; 106 | font-weight: bold; 107 | 108 | @media screen and (orientation: portrait) { 109 | left: calc(100% + 45px); 110 | width: auto; 111 | top: 0; 112 | bottom: 0; 113 | line-height: 30px; 114 | } 115 | } 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /public/css/contests/5.scss: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100vh; 3 | } 4 | 5 | #app { 6 | height: 100%; 7 | } 8 | 9 | .world { 10 | display: flex; 11 | height: 100%; 12 | justify-content: space-between; 13 | 14 | @media screen and (orientation: portrait) { 15 | flex-direction: column; 16 | } 17 | 18 | .spacer { 19 | width: 200px; 20 | } 21 | 22 | .map { 23 | position: relative; 24 | flex: 0 1 0; 25 | text-align: center; 26 | 27 | .paint-wrap { 28 | height: 100%; 29 | display: inline-block; 30 | position: relative; 31 | } 32 | 33 | svg.paint { 34 | height: 100%; 35 | overflow: visible; 36 | 37 | .cell { 38 | &.red { 39 | fill: #ef2011; 40 | } 41 | 42 | &.blue { 43 | fill: #0e30ec; 44 | } 45 | 46 | &.green { 47 | fill: #167516; 48 | } 49 | 50 | &.white { 51 | fill: white; 52 | } 53 | 54 | &.gray { 55 | fill: #737373; 56 | } 57 | } 58 | 59 | .cell:hover { 60 | opacity: 0.9; 61 | } 62 | } 63 | 64 | @media screen and (orientation: portrait) { 65 | overflow-x: auto; 66 | } 67 | 68 | .language-labels { 69 | position: absolute; 70 | left: 0; 71 | top: 0; 72 | width: 100%; 73 | height: 100%; 74 | pointer-events: none; 75 | } 76 | 77 | .language-label { 78 | position: absolute; 79 | 80 | line-height: 1.3em; 81 | font-size: 1.0vmin; 82 | font-weight: bold; 83 | display: flex; 84 | justify-content: center; 85 | align-items: center; 86 | word-wrap: break-word; 87 | color: white; 88 | flex-direction: column; 89 | 90 | .language-size { 91 | font-size: 1.3em; 92 | } 93 | 94 | &.gray, &.white { 95 | color: #111; 96 | } 97 | } 98 | } 99 | 100 | .teams { 101 | display: flex; 102 | flex-basis: 200px; 103 | 104 | @media screen and (orientation: portrait) { 105 | flex-direction: column; 106 | flex-basis: auto; 107 | } 108 | 109 | .team { 110 | width: 50px; 111 | display: flex; 112 | flex-direction: column; 113 | justify-content: flex-end; 114 | margin-right: 15px; 115 | 116 | @media screen and (orientation: portrait) { 117 | width: auto; 118 | height: 25px; 119 | flex-direction: row-reverse; 120 | margin: 0 0 5px; 121 | } 122 | 123 | &.red .bar { 124 | background-color: #ef2011; 125 | color: #ef2011; 126 | } 127 | 128 | &.blue .bar { 129 | background-color: #0e30ec; 130 | color: #0e30ec; 131 | } 132 | 133 | &.green .bar { 134 | background-color: #167516; 135 | color: #167516; 136 | } 137 | 138 | .bar { 139 | position: relative; 140 | } 141 | 142 | .count { 143 | position: absolute; 144 | bottom: 100%; 145 | text-align: center; 146 | width: 100%; 147 | font-weight: bold; 148 | font-size: 32px; 149 | 150 | @media screen and (orientation: portrait) { 151 | left: calc(100% + 5px); 152 | width: auto; 153 | top: 0; 154 | bottom: 0; 155 | line-height: 30px; 156 | } 157 | } 158 | 159 | .team-name { 160 | position: absolute; 161 | bottom: calc(100% + 35px); 162 | text-align: center; 163 | width: 100%; 164 | font-weight: bold; 165 | 166 | @media screen and (orientation: portrait) { 167 | left: calc(100% + 45px); 168 | width: auto; 169 | top: 0; 170 | bottom: 0; 171 | line-height: 30px; 172 | } 173 | } 174 | } 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /public/css/contests/hackathon2018.scss: -------------------------------------------------------------------------------- 1 | html, body { 2 | height: 100%; 3 | display: flex; 4 | flex-direction: column; 5 | } 6 | 7 | #app { 8 | flex: 1 1 0; 9 | } 10 | 11 | .world { 12 | display: flex; 13 | justify-content: space-between; 14 | height: 100%; 15 | 16 | @media screen and (orientation: portrait) { 17 | flex-direction: column; 18 | } 19 | 20 | .spacer { 21 | width: 200px; 22 | } 23 | 24 | .map { 25 | display: flex; 26 | flex-wrap: wrap; 27 | justify-content: center; 28 | align-content: center; 29 | width: 80vw; 30 | 31 | .cell { 32 | flex: 0 0 20vmin; 33 | height: 20vmin; 34 | background: gray; 35 | margin: 5px; 36 | border-radius: 50%; 37 | display: flex; 38 | justify-content: center; 39 | flex-direction: column; 40 | text-align: center; 41 | font-size: 3vmin; 42 | font-weight: bold; 43 | line-height: 1.2; 44 | 45 | &.black { background-color: #111111; } 46 | &.grey { background-color: #555555; } 47 | &.white { background-color: #ffffff; } 48 | &.red { background-color: #ef2011; } 49 | &.blue { background-color: #0e30ec; } 50 | &.green { background-color: #167516; } 51 | &.orange { background-color: #F57F17; } 52 | &.purple { background-color: #6A1B9A; } 53 | 54 | &.white { 55 | border: 1px solid gray; 56 | } 57 | } 58 | 59 | @media screen and (orientation: portrait) { 60 | overflow: hidden; 61 | width: 100%; 62 | } 63 | 64 | .language-name { 65 | word-break: break-all; 66 | } 67 | 68 | .language-size { 69 | font-size: 1.3em; 70 | } 71 | } 72 | 73 | .teams { 74 | display: flex; 75 | flex-basis: 130px; 76 | 77 | @media screen and (orientation: portrait) { 78 | flex-direction: column; 79 | flex-basis: auto; 80 | } 81 | 82 | .team { 83 | width: 50px; 84 | display: flex; 85 | flex-direction: column; 86 | justify-content: flex-end; 87 | margin-right: 15px; 88 | 89 | @media screen and (orientation: portrait) { 90 | width: auto; 91 | height: 25px; 92 | flex-direction: row-reverse; 93 | margin: 0 0 5px; 94 | } 95 | 96 | &.red .bar { 97 | background-color: #ef2011; 98 | color: #ef2011; 99 | } 100 | 101 | &.blue .bar { 102 | background-color: #0e30ec; 103 | color: #0e30ec; 104 | } 105 | 106 | &.green .bar { 107 | background-color: #167516; 108 | color: #167516; 109 | } 110 | 111 | &.orange .bar { 112 | background-color: #F57F17; 113 | color: #F57F17; 114 | } 115 | 116 | &.purple .bar { 117 | background-color: #6A1B9A; 118 | color: #6A1B9A; 119 | } 120 | 121 | .bar { 122 | position: relative; 123 | } 124 | 125 | .count { 126 | position: absolute; 127 | bottom: 100%; 128 | text-align: center; 129 | width: 100%; 130 | font-weight: bold; 131 | font-size: 32px; 132 | 133 | @media screen and (orientation: portrait) { 134 | left: calc(100% + 5px); 135 | width: auto; 136 | top: 0; 137 | bottom: 0; 138 | line-height: 30px; 139 | } 140 | } 141 | 142 | .team-name { 143 | position: absolute; 144 | bottom: calc(100% + 35px); 145 | text-align: center; 146 | width: 100%; 147 | font-weight: bold; 148 | 149 | @media screen and (orientation: portrait) { 150 | left: calc(100% + 45px); 151 | width: auto; 152 | top: 0; 153 | bottom: 0; 154 | line-height: 30px; 155 | } 156 | } 157 | } 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /public/css/contests/mayfes2018.scss: -------------------------------------------------------------------------------- 1 | .world { 2 | display: flex; 3 | justify-content: space-between; 4 | 5 | @media screen and (orientation: portrait) { 6 | flex-direction: column; 7 | } 8 | 9 | .spacer { 10 | width: 200px; 11 | } 12 | 13 | .map { 14 | display: flex; 15 | flex-direction: column; 16 | width: 80vmin; 17 | height: 80vmin; 18 | 19 | .row { 20 | display: flex; 21 | flex-direction: row; 22 | flex: 1 1 0; 23 | } 24 | 25 | .cell { 26 | flex: 1 1 0; 27 | position: relative; 28 | background: gray; 29 | margin: 3px; 30 | border-radius: 5px; 31 | 32 | display: flex; 33 | justify-content: center; 34 | flex-direction: column; 35 | 36 | text-align: center; 37 | font-size: 4vmin; 38 | font-weight: bold; 39 | 40 | &.black { 41 | background-color: #111111; 42 | } 43 | &.grey { 44 | background-color: #555555; 45 | } 46 | &.white { 47 | background-color: #ffffff; 48 | } 49 | &.red { 50 | background-color: #ef2011; 51 | } 52 | &.blue { 53 | background-color: #0e30ec; 54 | } 55 | } 56 | 57 | @media screen and (orientation: portrait) { 58 | overflow: hidden; 59 | } 60 | 61 | .language-label { 62 | width: 200px; 63 | 64 | line-height: 1.3em; 65 | font-size: 3.2vmin; 66 | font-weight: bold; 67 | text-align: center; 68 | 69 | position: absolute; 70 | top: 0; 71 | left: 0; 72 | width: 100%; // ??? 73 | bottom: 0; 74 | 75 | display: flex; 76 | justify-content: center; 77 | align-items: center; 78 | flex-direction: column; 79 | 80 | .language-size { 81 | font-size: 1.3em; 82 | } 83 | } 84 | } 85 | 86 | .teams { 87 | display: flex; 88 | flex: 1 0 0; 89 | 90 | &.left { 91 | justify-content: flex-end; 92 | } 93 | 94 | &.right { 95 | justify-content: flex-start; 96 | } 97 | 98 | @media screen and (orientation: portrait) { 99 | flex-direction: column; 100 | flex-basis: auto; 101 | } 102 | 103 | .team { 104 | width: 50px; 105 | display: flex; 106 | flex-direction: column; 107 | justify-content: flex-end; 108 | margin: 0 10px; 109 | 110 | @media screen and (orientation: portrait) { 111 | width: auto; 112 | height: 25px; 113 | flex-direction: row-reverse; 114 | margin: 0 0 5px; 115 | } 116 | 117 | &.red .bar { 118 | background-color: #ef2011; 119 | color: #ef2011; 120 | } 121 | 122 | &.blue .bar { 123 | background-color: #0e30ec; 124 | color: #0e30ec; 125 | } 126 | 127 | .bar { 128 | position: relative; 129 | } 130 | 131 | .count { 132 | position: absolute; 133 | bottom: 100%; 134 | text-align: center; 135 | width: 100%; 136 | font-weight: bold; 137 | font-size: 32px; 138 | 139 | @media screen and (orientation: portrait) { 140 | left: calc(100% + 5px); 141 | width: auto; 142 | top: 0; 143 | bottom: 0; 144 | line-height: 30px; 145 | } 146 | } 147 | 148 | .team-name { 149 | position: absolute; 150 | bottom: calc(100% + 35px); 151 | text-align: center; 152 | width: 100%; 153 | font-weight: bold; 154 | 155 | @media screen and (orientation: portrait) { 156 | left: calc(100% + 45px); 157 | width: auto; 158 | top: 0; 159 | bottom: 0; 160 | line-height: 30px; 161 | } 162 | } 163 | } 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /public/css/lib/bootstrap-social.scss: -------------------------------------------------------------------------------- 1 | /* 2 | * Social Buttons for Bootstrap 3 | * 4 | * Copyright 2013-2015 Panayiotis Lipiridis 5 | * Licensed under the MIT License 6 | * 7 | * https://github.com/lipis/bootstrap-social 8 | */ 9 | 10 | $bs-height-base: ($line-height-computed + $padding-base-vertical * 2); 11 | $bs-height-lg: (floor($font-size-large * $line-height-base) + $padding-large-vertical * 2); 12 | $bs-height-sm: (floor($font-size-small * 1.5) + $padding-small-vertical * 2); 13 | $bs-height-xs: (floor($font-size-small * 1.2) + $padding-small-vertical + 1); 14 | 15 | .btn-social { 16 | position: relative; 17 | padding-left: ($bs-height-base + $padding-base-horizontal); 18 | text-align: left; 19 | white-space: nowrap; 20 | overflow: hidden; 21 | text-overflow: ellipsis; 22 | > :first-child { 23 | position: absolute; 24 | left: 0; 25 | top: 0; 26 | bottom: 0; 27 | width: $bs-height-base; 28 | line-height: ($bs-height-base + 2); 29 | font-size: 1.6em; 30 | text-align: center; 31 | border-right: 1px solid rgba(0, 0, 0, 0.2); 32 | } 33 | &.btn-lg { 34 | padding-left: ($bs-height-lg + $padding-large-horizontal); 35 | > :first-child { 36 | line-height: $bs-height-lg; 37 | width: $bs-height-lg; 38 | font-size: 1.8em; 39 | } 40 | } 41 | &.btn-sm { 42 | padding-left: ($bs-height-sm + $padding-small-horizontal); 43 | > :first-child { 44 | line-height: $bs-height-sm; 45 | width: $bs-height-sm; 46 | font-size: 1.4em; 47 | } 48 | } 49 | &.btn-xs { 50 | padding-left: ($bs-height-xs + $padding-small-horizontal); 51 | > :first-child { 52 | line-height: $bs-height-xs; 53 | width: $bs-height-xs; 54 | font-size: 1.2em; 55 | } 56 | } 57 | } 58 | 59 | .btn-social-icon { 60 | @extend .btn-social; 61 | height: ($bs-height-base + 2); 62 | width: ($bs-height-base + 2); 63 | padding: 0; 64 | > :first-child { 65 | border: none; 66 | text-align: center; 67 | width: 100%!important; 68 | } 69 | &.btn-lg { 70 | height: $bs-height-lg; 71 | width: $bs-height-lg; 72 | padding-left: 0; 73 | padding-right: 0; 74 | } 75 | &.btn-sm { 76 | height: ($bs-height-sm + 2); 77 | width: ($bs-height-sm + 2); 78 | padding-left: 0; 79 | padding-right: 0; 80 | } 81 | &.btn-xs { 82 | height: ($bs-height-xs + 2); 83 | width: ($bs-height-xs + 2); 84 | padding-left: 0; 85 | padding-right: 0; 86 | } 87 | } 88 | 89 | @mixin btn-social($color-bg, $color: #fff) { 90 | background-color: $color-bg; 91 | @include button-variant($color, $color-bg, rgba(0,0,0,.2)); 92 | } 93 | 94 | 95 | .btn-adn { @include btn-social(#d87a68); } 96 | .btn-bitbucket { @include btn-social(#205081); } 97 | .btn-dropbox { @include btn-social(#1087dd); } 98 | .btn-facebook { @include btn-social(#3b5998); } 99 | .btn-flickr { @include btn-social(#ff0084); } 100 | .btn-foursquare { @include btn-social(#f94877); } 101 | .btn-github { @include btn-social(#444444); } 102 | .btn-google { @include btn-social(#dd4b39); } 103 | .btn-instagram { @include btn-social(#3f729b); } 104 | .btn-linkedin { @include btn-social(#007bb6); } 105 | .btn-microsoft { @include btn-social(#2672ec); } 106 | .btn-odnoklassniki { @include btn-social(#f4731c); } 107 | .btn-openid { @include btn-social(#f7931e); } 108 | .btn-pinterest { @include btn-social(#cb2027); } 109 | .btn-reddit { @include btn-social(#eff7ff, #000); } 110 | .btn-soundcloud { @include btn-social(#ff5500); } 111 | .btn-tumblr { @include btn-social(#2c4762); } 112 | .btn-twitter { @include btn-social(#55acee); } 113 | .btn-vimeo { @include btn-social(#1ab7ea); } 114 | .btn-vk { @include btn-social(#587ea3); } 115 | .btn-yahoo { @include btn-social(#720e9e); } 116 | -------------------------------------------------------------------------------- /public/css/lib/font-awesome/_animated.scss: -------------------------------------------------------------------------------- 1 | // Spinning Icons 2 | // -------------------------- 3 | 4 | .#{$fa-css-prefix}-spin { 5 | -webkit-animation: fa-spin 2s infinite linear; 6 | animation: fa-spin 2s infinite linear; 7 | } 8 | 9 | .#{$fa-css-prefix}-pulse { 10 | -webkit-animation: fa-spin 1s infinite steps(8); 11 | animation: fa-spin 1s infinite steps(8); 12 | } 13 | 14 | @-webkit-keyframes fa-spin { 15 | 0% { 16 | -webkit-transform: rotate(0deg); 17 | transform: rotate(0deg); 18 | } 19 | 100% { 20 | -webkit-transform: rotate(359deg); 21 | transform: rotate(359deg); 22 | } 23 | } 24 | 25 | @keyframes fa-spin { 26 | 0% { 27 | -webkit-transform: rotate(0deg); 28 | transform: rotate(0deg); 29 | } 30 | 100% { 31 | -webkit-transform: rotate(359deg); 32 | transform: rotate(359deg); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /public/css/lib/font-awesome/_bordered-pulled.scss: -------------------------------------------------------------------------------- 1 | // Bordered & Pulled 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-border { 5 | padding: .2em .25em .15em; 6 | border: solid .08em $fa-border-color; 7 | border-radius: .1em; 8 | } 9 | 10 | .#{$fa-css-prefix}-pull-left { float: left; } 11 | .#{$fa-css-prefix}-pull-right { float: right; } 12 | 13 | .#{$fa-css-prefix} { 14 | &.#{$fa-css-prefix}-pull-left { margin-right: .3em; } 15 | &.#{$fa-css-prefix}-pull-right { margin-left: .3em; } 16 | } 17 | 18 | /* Deprecated as of 4.4.0 */ 19 | .pull-right { float: right; } 20 | .pull-left { float: left; } 21 | 22 | .#{$fa-css-prefix} { 23 | &.pull-left { margin-right: .3em; } 24 | &.pull-right { margin-left: .3em; } 25 | } 26 | -------------------------------------------------------------------------------- /public/css/lib/font-awesome/_core.scss: -------------------------------------------------------------------------------- 1 | // Base Class Definition 2 | // ------------------------- 3 | 4 | @use "sass:list"; 5 | 6 | .#{$fa-css-prefix} { 7 | display: inline-block; 8 | font: normal normal normal list.slash($fa-font-size-base, $fa-line-height-base) FontAwesome; // shortening font declaration 9 | font-size: inherit; // can't have font-size inherit on line above, so need to override 10 | text-rendering: auto; // optimizelegibility throws things off #1094 11 | -webkit-font-smoothing: antialiased; 12 | -moz-osx-font-smoothing: grayscale; 13 | 14 | } 15 | -------------------------------------------------------------------------------- /public/css/lib/font-awesome/_fixed-width.scss: -------------------------------------------------------------------------------- 1 | // Fixed Width Icons 2 | // ------------------------- 3 | @use "sass:math"; 4 | 5 | .#{$fa-css-prefix}-fw { 6 | width: math.div(18em, 14); 7 | text-align: center; 8 | } 9 | -------------------------------------------------------------------------------- /public/css/lib/font-awesome/_larger.scss: -------------------------------------------------------------------------------- 1 | // Icon Sizes 2 | // ------------------------- 3 | 4 | /* makes the font 33% larger relative to the icon container */ 5 | @use "sass:math"; 6 | 7 | .#{$fa-css-prefix}-lg { 8 | font-size: math.div(4em, 3); 9 | line-height: (3em * 0.25); 10 | vertical-align: -15%; 11 | } 12 | .#{$fa-css-prefix}-2x { font-size: 2em; } 13 | .#{$fa-css-prefix}-3x { font-size: 3em; } 14 | .#{$fa-css-prefix}-4x { font-size: 4em; } 15 | .#{$fa-css-prefix}-5x { font-size: 5em; } 16 | -------------------------------------------------------------------------------- /public/css/lib/font-awesome/_list.scss: -------------------------------------------------------------------------------- 1 | // List Icons 2 | // ------------------------- 3 | 4 | @use "sass:math"; 5 | 6 | .#{$fa-css-prefix}-ul { 7 | padding-left: 0; 8 | margin-left: $fa-li-width; 9 | list-style-type: none; 10 | > li { position: relative; } 11 | } 12 | .#{$fa-css-prefix}-li { 13 | position: absolute; 14 | left: -$fa-li-width; 15 | width: $fa-li-width; 16 | top: math.div(2em, 14); 17 | text-align: center; 18 | &.#{$fa-css-prefix}-lg { 19 | left: -$fa-li-width + math.div(4em, 14); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /public/css/lib/font-awesome/_mixins.scss: -------------------------------------------------------------------------------- 1 | // Mixins 2 | // -------------------------- 3 | 4 | @use "sass:list"; 5 | 6 | @mixin fa-icon() { 7 | display: inline-block; 8 | font: normal normal normal list.slash($fa-font-size-base, $fa-line-height-base) FontAwesome; // shortening font declaration 9 | font-size: inherit; // can't have font-size inherit on line above, so need to override 10 | text-rendering: auto; // optimizelegibility throws things off #1094 11 | -webkit-font-smoothing: antialiased; 12 | -moz-osx-font-smoothing: grayscale; 13 | 14 | } 15 | 16 | @mixin fa-icon-rotate($degrees, $rotation) { 17 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation})"; 18 | -webkit-transform: rotate($degrees); 19 | -ms-transform: rotate($degrees); 20 | transform: rotate($degrees); 21 | } 22 | 23 | @mixin fa-icon-flip($horiz, $vert, $rotation) { 24 | -ms-filter: "progid:DXImageTransform.Microsoft.BasicImage(rotation=#{$rotation}, mirror=1)"; 25 | -webkit-transform: scale($horiz, $vert); 26 | -ms-transform: scale($horiz, $vert); 27 | transform: scale($horiz, $vert); 28 | } 29 | 30 | 31 | // Only display content to screen readers. A la Bootstrap 4. 32 | // 33 | // See: http://a11yproject.com/posts/how-to-hide-content/ 34 | 35 | @mixin sr-only { 36 | position: absolute; 37 | width: 1px; 38 | height: 1px; 39 | padding: 0; 40 | margin: -1px; 41 | overflow: hidden; 42 | clip: rect(0,0,0,0); 43 | border: 0; 44 | } 45 | 46 | // Use in conjunction with .sr-only to only display content when it's focused. 47 | // 48 | // Useful for "Skip to main content" links; see http://www.w3.org/TR/2013/NOTE-WCAG20-TECHS-20130905/G1 49 | // 50 | // Credit: HTML5 Boilerplate 51 | 52 | @mixin sr-only-focusable { 53 | &:active, 54 | &:focus { 55 | position: static; 56 | width: auto; 57 | height: auto; 58 | margin: 0; 59 | overflow: visible; 60 | clip: auto; 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /public/css/lib/font-awesome/_path.scss: -------------------------------------------------------------------------------- 1 | /* FONT PATH 2 | * -------------------------- */ 3 | 4 | @font-face { 5 | font-family: 'FontAwesome'; 6 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?v=#{$fa-version}'); 7 | src: url('#{$fa-font-path}/fontawesome-webfont.eot?#iefix&v=#{$fa-version}') format('embedded-opentype'), 8 | url('#{$fa-font-path}/fontawesome-webfont.woff2?v=#{$fa-version}') format('woff2'), 9 | url('#{$fa-font-path}/fontawesome-webfont.woff?v=#{$fa-version}') format('woff'), 10 | url('#{$fa-font-path}/fontawesome-webfont.ttf?v=#{$fa-version}') format('truetype'), 11 | url('#{$fa-font-path}/fontawesome-webfont.svg?v=#{$fa-version}#fontawesomeregular') format('svg'); 12 | // src: url('#{$fa-font-path}/FontAwesome.otf') format('opentype'); // used when developing fonts 13 | font-weight: normal; 14 | font-style: normal; 15 | } 16 | -------------------------------------------------------------------------------- /public/css/lib/font-awesome/_rotated-flipped.scss: -------------------------------------------------------------------------------- 1 | // Rotated & Flipped Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-rotate-90 { @include fa-icon-rotate(90deg, 1); } 5 | .#{$fa-css-prefix}-rotate-180 { @include fa-icon-rotate(180deg, 2); } 6 | .#{$fa-css-prefix}-rotate-270 { @include fa-icon-rotate(270deg, 3); } 7 | 8 | .#{$fa-css-prefix}-flip-horizontal { @include fa-icon-flip(-1, 1, 0); } 9 | .#{$fa-css-prefix}-flip-vertical { @include fa-icon-flip(1, -1, 2); } 10 | 11 | // Hook for IE8-9 12 | // ------------------------- 13 | 14 | :root .#{$fa-css-prefix}-rotate-90, 15 | :root .#{$fa-css-prefix}-rotate-180, 16 | :root .#{$fa-css-prefix}-rotate-270, 17 | :root .#{$fa-css-prefix}-flip-horizontal, 18 | :root .#{$fa-css-prefix}-flip-vertical { 19 | filter: none; 20 | } 21 | -------------------------------------------------------------------------------- /public/css/lib/font-awesome/_screen-reader.scss: -------------------------------------------------------------------------------- 1 | // Screen Readers 2 | // ------------------------- 3 | 4 | .sr-only { @include sr-only(); } 5 | .sr-only-focusable { @include sr-only-focusable(); } 6 | -------------------------------------------------------------------------------- /public/css/lib/font-awesome/_stacked.scss: -------------------------------------------------------------------------------- 1 | // Stacked Icons 2 | // ------------------------- 3 | 4 | .#{$fa-css-prefix}-stack { 5 | position: relative; 6 | display: inline-block; 7 | width: 2em; 8 | height: 2em; 9 | line-height: 2em; 10 | vertical-align: middle; 11 | } 12 | .#{$fa-css-prefix}-stack-1x, .#{$fa-css-prefix}-stack-2x { 13 | position: absolute; 14 | left: 0; 15 | width: 100%; 16 | text-align: center; 17 | } 18 | .#{$fa-css-prefix}-stack-1x { line-height: inherit; } 19 | .#{$fa-css-prefix}-stack-2x { font-size: 2em; } 20 | .#{$fa-css-prefix}-inverse { color: $fa-inverse; } 21 | -------------------------------------------------------------------------------- /public/css/lib/font-awesome/font-awesome.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.6.3 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */ 5 | 6 | @import "variables"; 7 | @import "mixins"; 8 | @import "path"; 9 | @import "core"; 10 | @import "larger"; 11 | @import "fixed-width"; 12 | @import "list"; 13 | @import "bordered-pulled"; 14 | @import "animated"; 15 | @import "rotated-flipped"; 16 | @import "stacked"; 17 | @import "icons"; 18 | @import "screen-reader"; 19 | -------------------------------------------------------------------------------- /public/css/themes/default/_default.scss: -------------------------------------------------------------------------------- 1 | // Buttons 2 | // ------------------------- 3 | 4 | .btn { 5 | border-radius: 0; 6 | box-shadow: 1px 1px 2px rgba(0, 0, 0, 0.11), 1px 1px 0 rgba(255, 255, 255, 0.21) inset; 7 | 8 | &:focus { 9 | outline: none; 10 | } 11 | } 12 | 13 | .btn-link { 14 | box-shadow: none; 15 | } 16 | 17 | .btn-default, .btn-default:focus { 18 | background-image: linear-gradient(to bottom, #ffffff 60%, #f8f8f8 100%); 19 | } 20 | 21 | // Forms 22 | // ------------------------- 23 | 24 | .form-control:focus { 25 | box-shadow: 0 1px 1px rgba(0, 0, 0, 0.1) inset; 26 | } 27 | 28 | // Alerts 29 | // ------------------------- 30 | 31 | .alert { 32 | border-radius: 0; 33 | box-shadow: 0 1px 2px rgba(0, 0, 0, 0.10); 34 | } 35 | 36 | // Navbar 37 | // ------------------------- 38 | 39 | .navbar-default { 40 | border: 0; 41 | box-shadow: 0 0 3px rgba(0, 0, 0, 0.2); 42 | 43 | .navbar-nav { 44 | > li > a { 45 | font-size: 12px; 46 | font-weight: 500; 47 | text-transform: uppercase; 48 | transition: all 0.2s linear; 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /public/css/themes/default/_variables.scss: -------------------------------------------------------------------------------- 1 | // Brand Colors 2 | // ------------------------- 3 | 4 | $brand-primary: #4d90fc; 5 | $brand-success: #60bf60; 6 | $brand-warning: #ff9800; 7 | $brand-danger: #de4b33; 8 | $brand-info: #5bc0dd; 9 | 10 | // Typography 11 | // ------------------------- 12 | 13 | $headings-font-family: "HelveticaNeue-CondensedBold", "Helvetica Neue", Arial, sans-serif; 14 | 15 | // Buttons 16 | // ------------------------- 17 | 18 | $btn-primary-border: darken($brand-primary, 3.2%); 19 | $btn-success-border: darken($brand-success, 3.2%); 20 | $btn-warning-border: darken($brand-warning, 3.2%); 21 | $btn-danger-border: darken($brand-danger, 3.2%); 22 | $btn-info-border: darken($brand-info, 3.2%); 23 | 24 | // Forms 25 | // ------------------------- 26 | 27 | $input-border-radius: 0; 28 | $input-border-focus: #2598f9; 29 | 30 | // Form states and alerts 31 | // ------------------------- 32 | 33 | $state-success-text: #569845; 34 | $state-success-bg: #dbf5d3; 35 | $state-success-border: #aed3a5; 36 | 37 | $state-info-text: #3a87ad; 38 | $state-info-bg: #d9edf7; 39 | $state-info-border: #98cce7; 40 | 41 | $state-warning-text: #bf9853; 42 | $state-warning-bg: #fdf8e2; 43 | $state-warning-border: #f2daab; 44 | 45 | $state-danger-text: #b94a48; 46 | $state-danger-bg: #f2dede; 47 | $state-danger-border: #e0b1b8; 48 | 49 | 50 | // Navbar 51 | // ------------------------- 52 | 53 | $navbar-default-bg: rgba(255, 255, 255, 0.9); 54 | $navbar-default-link-color: #252525; 55 | $navbar-default-link-hover-color: #4da5f4; 56 | $navbar-default-link-active-color: #4da5f4; 57 | $navbar-default-link-hover-bg: transparent; 58 | $navbar-default-link-active-bg: transparent; 59 | $navbar-default-color: #fafafa; 60 | $navbar-default-brand-hover-color: #4da5f4; 61 | -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk.scss: -------------------------------------------------------------------------------- 1 | /*! 2 | * Copyright 2016 Creative Tim (http://www.creative-tim.com) 3 | * Licensed under MIT (https://github.com/timcreative/get-shit-done/blob/master/LICENSE.md) 4 | */ 5 | 6 | @import "gsdk/variables"; 7 | @import "gsdk/mixins"; 8 | @import "gsdk/typography"; 9 | 10 | // Core CSS 11 | @import "gsdk/misc"; 12 | @import "gsdk/buttons"; 13 | @import "gsdk/inputs"; 14 | @import "gsdk/sliders"; 15 | @import "gsdk/alerts"; 16 | @import "gsdk/labels-and-progress-bars"; 17 | @import "gsdk/tooltips"; 18 | @import "gsdk/navbars"; 19 | 20 | 21 | // Fancy Stuff 22 | @import "gsdk/dropdown"; 23 | @import "gsdk/social-buttons"; 24 | @import "gsdk/tabs-navs-pagination"; 25 | @import "gsdk/collapse"; 26 | @import "gsdk/carousel"; 27 | @import "gsdk/modal"; 28 | @import "gsdk/responsive"; 29 | 30 | -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/_alerts.scss: -------------------------------------------------------------------------------- 1 | .alert{ 2 | border: 0; 3 | border-radius: 0; 4 | color: #FFFFFF; 5 | padding: 10px 15px; 6 | font-size: 14px; 7 | 8 | .container &{ 9 | border-radius: 4px; 10 | 11 | } 12 | .navbar &{ 13 | border-radius: 0; 14 | left: 0; 15 | position: absolute; 16 | right: 0; 17 | top: 85px; 18 | width: 100%; 19 | z-index: 3; 20 | } 21 | .navbar:not(.navbar-transparent) &{ 22 | top: 70px; 23 | } 24 | } 25 | .alert-info{ 26 | background-color: $azure-navbar; 27 | } 28 | .alert-success { 29 | background-color: $green-navbar; 30 | } 31 | .alert-warning { 32 | background-color: $orange-navbar; 33 | } 34 | .alert-danger { 35 | background-color: $red-navbar; 36 | } 37 | 38 | -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/_buttons.scss: -------------------------------------------------------------------------------- 1 | .btn{ 2 | color: $white-color; 3 | font-weight: $font-weight-normal; 4 | padding: $padding-base-vertical $padding-base-horizontal; 5 | @include opacity(1); 6 | 7 | @include btn-styles($default-color, $default-states-color); 8 | 9 | &:hover, 10 | &:focus{ 11 | @include opacity(1); 12 | outline: 0 !important; 13 | color: $white-color; 14 | } 15 | &:active, 16 | &.active, 17 | .open > &.dropdown-toggle { 18 | @include box-shadow(none); 19 | outline: 0 !important; 20 | color: $white-color; 21 | } 22 | 23 | .caret{ 24 | border-top-color: $white-color; 25 | } 26 | 27 | } 28 | 29 | // Apply the mixin to the buttons 30 | //.btn-default { @include btn-styles($default-color, $default-states-color); } 31 | .btn-primary { @include btn-styles($primary-color, $primary-states-color); } 32 | .btn-success { @include btn-styles($success-color, $success-states-color); } 33 | .btn-info { @include btn-styles($info-color, $info-states-color); } 34 | .btn-warning { @include btn-styles($warning-color, $warning-states-color); } 35 | .btn-danger { @include btn-styles($danger-color, $danger-states-color); } 36 | .btn-neutral { 37 | @include btn-styles($white-color, $white-color); 38 | 39 | &:active, 40 | &.active, 41 | .open > &.dropdown-toggle{ 42 | background-color: $white-color; 43 | color: $default-color; 44 | } 45 | 46 | &.btn-link:active, 47 | &.btn-link.active{ 48 | background-color: transparent; 49 | } 50 | } 51 | 52 | .btn{ 53 | &:disabled, 54 | &[disabled], 55 | &.disabled{ 56 | @include opacity(.5); 57 | } 58 | } 59 | .btn-border, 60 | .btn-round, 61 | .btn-link{ 62 | background-color: $transparent-bg; 63 | @include opacity(.8); 64 | } 65 | .btn-border{ 66 | border-width: $border-thick; 67 | } 68 | .btn-round{ 69 | border-width: $border-thin; 70 | border-radius: $btn-round-radius !important; 71 | padding: $padding-round-vertical $padding-round-horizontal; 72 | } 73 | .btn-link{ 74 | border: $none; 75 | font-size: $font-size-medium; 76 | padding: $padding-base-vertical $padding-base-horizontal; 77 | 78 | &:hover, 79 | &:focus, 80 | &:active{ 81 | text-decoration: none; 82 | } 83 | } 84 | .btn-lg{ 85 | @include btn-size($padding-large-vertical, $padding-large-horizontal, $font-size-large, $border-radius-large); 86 | font-weight: $font-weight-normal; 87 | } 88 | .btn-sm{ 89 | @include btn-size($padding-small-vertical, $padding-small-horizontal, $font-size-small, $border-radius-small); 90 | } 91 | .btn-xs { 92 | @include btn-size($padding-xs-vertical, $padding-xs-horizontal, $font-size-small, $border-radius-small); 93 | } 94 | .btn-wd { 95 | min-width: 140px; 96 | } 97 | .btn-group.select{ 98 | width: 100%; 99 | } 100 | .btn-group.select .btn{ 101 | text-align: left; 102 | } 103 | .btn-group.select .caret{ 104 | position: absolute; 105 | top: 50%; 106 | margin-top: -1px; 107 | right: 8px; 108 | } 109 | -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/_carousel.scss: -------------------------------------------------------------------------------- 1 | .carousel-control{ 2 | // width: 8%; 3 | &.left, 4 | &.right{ 5 | background-image: none; 6 | } 7 | .fa{ 8 | font-size: 35px; 9 | } 10 | .fa, 11 | .icon-prev, 12 | .icon-next{ 13 | display: inline-block; 14 | position: absolute; 15 | top: 50%; 16 | z-index: 5; 17 | margin-top: -17px; 18 | margin-left: -9px; 19 | } 20 | } 21 | .carousel-inner > .item { 22 | transition: left 1200ms cubic-bezier(0.455, 0.03, 0.4, 0.955) 0s; 23 | -webkit-transition: left 1200ms cubic-bezier(0.455, 0.03, 0.4, 0.955) 0s; 24 | } -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/_collapse.scss: -------------------------------------------------------------------------------- 1 | .panel-group{ 2 | .panel { 3 | border: 0; 4 | border-bottom: 1px solid $medium-gray; 5 | box-shadow: none; 6 | } 7 | .panel-default > .panel-heading { 8 | background-color: $white-color; 9 | border-color: $white-color; 10 | } 11 | .panel{ 12 | border-radius: 0; 13 | } 14 | .panel-title{ 15 | font-size: $font-size-h5; 16 | } 17 | .panel-title a:hover, .panel-title a:focus{ 18 | text-decoration: none; 19 | } 20 | .gsdk-collapse{ 21 | display: block; 22 | height: 0px; 23 | visibility: visible; 24 | overflow: hidden; 25 | transition: all 400ms; 26 | } 27 | .panel-title a:hover, 28 | .panel-title a:focus{ 29 | color: $default-states-color; 30 | } 31 | .panel-default > .panel-heading + .panel-collapse.gsdk-collapse > .panel-body { 32 | box-shadow: inset 0 7px 10px -7px rgba(0,0,0,0.14); 33 | } 34 | } -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/_dropdown.scss: -------------------------------------------------------------------------------- 1 | .dropdown-menu{ 2 | margin: 0; 3 | padding: 0; 4 | border-radius: $border-radius-extreme; 5 | z-index: 9000; 6 | @include box-shadow($dropdown-shadow); 7 | 8 | > li > a { 9 | padding: $padding-base-vertical $padding-base-horizontal; 10 | color: #333333; 11 | } 12 | > li > a:focus{ 13 | outline: 0 !important; 14 | } 15 | 16 | > li:first-child > a{ 17 | border-top-left-radius: $border-radius-extreme; 18 | border-top-right-radius: $border-radius-extreme; 19 | } 20 | 21 | > li:last-child > a{ 22 | border-bottom-left-radius: $border-radius-extreme; 23 | border-bottom-right-radius: $border-radius-extreme; 24 | } 25 | 26 | > li > a:hover, 27 | > li > a:focus { 28 | background-color: $smoke-bg; 29 | color: #333333; 30 | opacity: 1; 31 | text-decoration: none; 32 | } 33 | 34 | > .active > a{ 35 | &, 36 | &:focus, 37 | &:hover{ 38 | background-color: $info-bg; 39 | } 40 | } 41 | } 42 | 43 | 44 | //fix bug for the select items in btn-group 45 | .btn-group.select{ 46 | overflow: hidden; 47 | } 48 | .btn-group.select.open{ 49 | overflow: visible; 50 | } 51 | 52 | -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/_labels-and-progress-bars.scss: -------------------------------------------------------------------------------- 1 | /* Labels & Progress-bar */ 2 | .label{ 3 | padding: 0.2em 0.6em 0.2em; 4 | border: 1px solid #999999; 5 | border-radius: 3px; 6 | color: #999999; 7 | background-color: #FFFFFF; 8 | font-weight: 500; 9 | font-size: 11px; 10 | text-transform: uppercase; 11 | display: inline-block; 12 | margin-bottom: 3px; 13 | } 14 | .label-primary{ 15 | border-color: $primary-color; 16 | color: $primary-color; 17 | } 18 | .label-info{ 19 | border-color: $info-color; 20 | color: $info-color; 21 | } 22 | .label-success{ 23 | border-color: $success-color; 24 | color: $success-color; 25 | } 26 | .label-warning{ 27 | border-color: $warning-color; 28 | color: $warning-color; 29 | } 30 | .label-danger{ 31 | border-color: $danger-color; 32 | color: $danger-color; 33 | } 34 | .label.label-fill{ 35 | color: #FFFFFF; 36 | } 37 | .label-primary.label-fill, .progress-bar, .progress-bar-primary{ 38 | background-color: $primary-color; 39 | } 40 | .label-info.label-fill, .progress-bar-info{ 41 | background-color: $info-color; 42 | } 43 | .label-success.label-fill, .progress-bar-success{ 44 | background-color: $success-color; 45 | } 46 | .label-warning.label-fill, .progress-bar-warning{ 47 | background-color: $warning-color; 48 | } 49 | .label-danger.label-fill, .progress-bar-danger{ 50 | background-color: $danger-color; 51 | } 52 | .label-default.label-fill{ 53 | background-color: #999999; 54 | } 55 | .progress { 56 | background-color: #E5E5E5; 57 | border-radius: 3px; 58 | box-shadow: none; 59 | height: 4px; 60 | } 61 | .progress-thin{ 62 | height: 2px; 63 | } -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/_misc.scss: -------------------------------------------------------------------------------- 1 | /* General overwrite */ 2 | body{ 3 | font-family: "Helvetica Neue","Open Sans",Arial,sans-serif; 4 | } 5 | .main{ 6 | background-color: #FFFFFF; 7 | position: relative; 8 | } 9 | a{ 10 | color: $info-color; 11 | 12 | &:hover, &:focus{ 13 | color: $info-states-color; 14 | text-decoration: none; 15 | } 16 | } 17 | 18 | a:focus, a:active, 19 | button::-moz-focus-inner, 20 | input[type="reset"]::-moz-focus-inner, 21 | input[type="button"]::-moz-focus-inner, 22 | input[type="submit"]::-moz-focus-inner, 23 | select::-moz-focus-inner, 24 | input[type="file"] > input[type="button"]::-moz-focus-inner { 25 | outline : 0; 26 | } 27 | .ui-slider-handle:focus, 28 | .navbar-toggle { 29 | outline : 0 !important; 30 | } 31 | 32 | /* Animations */ 33 | .form-control, 34 | .input-group-addon, 35 | .tagsinput, 36 | .navbar, 37 | .navbar .alert{ 38 | @include transition($general-transition-time, linear); 39 | } 40 | 41 | .fa{ 42 | width: 18px; 43 | text-align: center; 44 | } 45 | .margin-top{ 46 | margin-top: 50px; 47 | } 48 | 49 | 50 | /* CT colors */ 51 | .ct-blue, 52 | .ct-blue.checkbox.checked .second-icon, 53 | .ct-blue.checkbox.checked, 54 | .ct-blue.radio.checked .second-icon, 55 | .ct-blue.radio.checked{ 56 | color: $primary-color; 57 | } 58 | .ct-azzure, 59 | .ct-azzure.checkbox.checked .second-icon, 60 | .ct-azzure.radio.checked .second-icon, 61 | .ct-azzure.checkbox.checked, 62 | .ct-azzure.radio.checked{ 63 | color: $info-color; 64 | } 65 | .ct-green, 66 | .ct-green.checkbox.checked .second-icon, 67 | .ct-green.radio.checked .second-icon, 68 | .ct-green.checkbox.checked, 69 | .ct-green.radio.checked{ 70 | color: $success-color; 71 | } 72 | .ct-orange, 73 | .ct-orange.checkbox.checked .second-icon, 74 | .ct-orange.radio.checked .second-icon, 75 | .ct-orange.checkbox.checked, 76 | .ct-orange.radio.checked{ 77 | color: $warning-color; 78 | } 79 | .ct-red, 80 | .ct-red.checkbox.checked .second-icon, 81 | .ct-red.radio.checked .second-icon, 82 | .ct-red.checkbox.checked, 83 | .ct-red.radio.checked{ 84 | color: $danger-color; 85 | } 86 | input.ct-blue + span.switch-left, 87 | input.ct-blue + span + label + span.switch-right{ 88 | background-color: $primary-color; 89 | } 90 | input.ct-azzure + span.switch-left, 91 | input.ct-azure + span + label + span.switch-right{ 92 | background-color: $info-color; 93 | } 94 | input.ct-green + span.switch-left, 95 | input.ct-green + span + label + span.switch-right{ 96 | background-color: $success-color; 97 | } 98 | input.ct-orange + span.switch-left, 99 | input.ct-orange + span + label + span.switch-right{ 100 | background-color: $warning-color; 101 | } 102 | input.ct-red + span.switch-left, 103 | input.ct-red + span + label + span.switch-right{ 104 | background-color: $danger-color; 105 | } 106 | 107 | -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/_mixins.scss: -------------------------------------------------------------------------------- 1 | //Utilities 2 | 3 | @import "mixins/transparency"; 4 | @import "mixins/vendor-prefixes"; 5 | 6 | //Components 7 | 8 | @import "mixins/buttons"; 9 | @import "mixins/inputs"; 10 | @import "mixins/labels"; 11 | @import "mixins/tabs"; 12 | @import "mixins/navbars"; 13 | 14 | -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/_modal.scss: -------------------------------------------------------------------------------- 1 | .modal-header { 2 | border: 0 none; 3 | } 4 | .modal-content { 5 | border: 0 none; 6 | border-radius: 10px; 7 | box-shadow: 0 0 15px rgba(0, 0, 0, 0.15), 0 0 1px 1px rgba(0, 0, 0, 0.1); 8 | } 9 | .modal-dialog { 10 | padding-top: 60px; 11 | } 12 | .modal-footer { 13 | border-top: 0 none; 14 | padding: 5px 10px; 15 | text-align: right; 16 | .btn{ 17 | font-size: 16px; 18 | } 19 | .btn-default.btn-simple{ 20 | font-weight: 400; 21 | } 22 | .divider { 23 | background-color: #DDDDDD; 24 | display: inline-block; 25 | float: inherit; 26 | height: 26px; 27 | margin: 8px -3px; 28 | position: absolute; 29 | width: 1px; 30 | } 31 | 32 | 33 | } 34 | .modal-footer .modal-footer 35 | .modal.fade .modal-dialog { 36 | transform: none; 37 | -webkit-transform: none; 38 | -moz-transform: none; 39 | } 40 | .modal.in { 41 | // opacity: 0.25; 42 | .modal-dialog { 43 | transform: none; 44 | -webkit-transform: none; 45 | -moz-transform: none; 46 | } 47 | } 48 | 49 | .modal-footer 50 | .modal.fade .modal-dialog { 51 | transform: none; 52 | } 53 | 54 | -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/_social-buttons.scss: -------------------------------------------------------------------------------- 1 | .btn-social { 2 | opacity: 0.85; 3 | padding: 8px 9px; 4 | 5 | .fa { 6 | font-size: 18px; 7 | vertical-align: middle; 8 | display: inline-block; 9 | } 10 | 11 | &.btn-round { 12 | padding: 9px 10px; 13 | } 14 | 15 | &.btn-simple { 16 | padding: 9px 5px; 17 | font-size: 16px; 18 | 19 | .fa{ 20 | font-size: 20px; 21 | position: relative; 22 | top: -2px; 23 | width: 24px; 24 | } 25 | } 26 | 27 | } 28 | 29 | @mixin btn-social($color){ 30 | 31 | color: $white-color; 32 | background-color: $color; 33 | border-color: $color; 34 | opacity: 0.9; 35 | 36 | &:hover, 37 | &:focus, 38 | &:active, 39 | &.active, 40 | .open > &.dropdown-toggle{ 41 | background-color: $color; 42 | border-color: $color; 43 | color: $white-color; 44 | opacity: 1; 45 | } 46 | 47 | &.btn-border{ 48 | border-color: $color; 49 | color: $color; 50 | 51 | &:hover, 52 | &:focus, 53 | &:active, 54 | &.active, 55 | .open > &.dropdown-toggle { 56 | background-color: $transparent-bg; 57 | color: $color; 58 | border-color: $color; 59 | opacity: 1; 60 | } 61 | 62 | &:disabled, 63 | &[disabled], 64 | &.disabled { 65 | background-color: $transparent-bg; 66 | border-color: $color; 67 | } 68 | } 69 | 70 | 71 | } 72 | 73 | .btn-adn { @include btn-social(#d87a68); } 74 | .btn-bitbucket { @include btn-social(#205081); } 75 | .btn-dropbox { @include btn-social(#1087dd); } 76 | .btn-facebook { @include btn-social(#3b5998); } 77 | .btn-flickr { @include btn-social(#ff0084); } 78 | .btn-foursquare { @include btn-social(#f94877); } 79 | .btn-github { @include btn-social(#444444); } 80 | .btn-google { @include btn-social(#dd4b39); } 81 | .btn-instagram { @include btn-social(#3f729b); } 82 | .btn-linkedin { @include btn-social(#007bb6); } 83 | .btn-microsoft { @include btn-social(#2672ec); } 84 | .btn-odnoklassniki { @include btn-social(#f4731c); } 85 | .btn-openid { @include btn-social(#f7931e); } 86 | .btn-pinterest { @include btn-social(#cb2027); } 87 | .btn-reddit { @include btn-social(#eff7ff); } 88 | .btn-soundcloud { @include btn-social(#ff5500); } 89 | .btn-tumblr { @include btn-social(#2c4762); } 90 | .btn-twitter { @include btn-social(#55acee); } 91 | .btn-vimeo { @include btn-social(#1ab7ea); } 92 | .btn-vk { @include btn-social(#587ea3); } 93 | .btn-yahoo { @include btn-social(#720e9e); } -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/_tooltips.scss: -------------------------------------------------------------------------------- 1 | .tooltip { 2 | font-size: $font-size-base; 3 | font-weight: $font-weight-bold; 4 | 5 | &.top { 6 | margin-top: -11px; 7 | padding: 0; 8 | } 9 | &.top .tooltip-inner:after { 10 | border-top: 11px solid #FAE6A4; 11 | border-left: 11px solid rgba(0, 0, 0, 0); 12 | border-right: 11px solid rgba(0, 0, 0, 0); 13 | bottom: -10px; 14 | } 15 | &.top .tooltip-inner:before { 16 | border-top: 11px solid rgba(0, 0, 0, 0.2); 17 | border-left: 11px solid rgba(0, 0, 0, 0); 18 | border-right: 11px solid rgba(0, 0, 0, 0); 19 | bottom: -11px; 20 | } 21 | &.bottom { 22 | margin-top: 11px; 23 | padding: 0; 24 | } 25 | &.bottom .tooltip-inner:after { 26 | border-bottom: 11px solid #FAE6A4; 27 | border-left: 11px solid rgba(0, 0, 0, 0); 28 | border-right: 11px solid rgba(0, 0, 0, 0); 29 | top: -10px; 30 | } 31 | &.bottom .tooltip-inner:before { 32 | border-bottom: 11px solid rgba(0, 0, 0, 0.2); 33 | border-left: 11px solid rgba(0, 0, 0, 0); 34 | border-right: 11px solid rgba(0, 0, 0, 0); 35 | top: -11px; 36 | } 37 | &.left{ 38 | margin-left: -11px; 39 | padding: 0; 40 | } 41 | &.left .tooltip-inner:after { 42 | border-left: 11px solid #FAE6A4; 43 | border-top: 11px solid rgba(0, 0, 0, 0); 44 | border-bottom: 11px solid rgba(0, 0, 0, 0); 45 | right: -10px; 46 | left: auto; 47 | margin-left: 0; 48 | } 49 | &.left .tooltip-inner:before { 50 | border-left: 11px solid rgba(0, 0, 0, 0.2); 51 | border-top: 11px solid rgba(0, 0, 0, 0); 52 | border-bottom: 11px solid rgba(0, 0, 0, 0); 53 | right: -11px; 54 | left: auto; 55 | margin-left: 0; 56 | } 57 | &.right{ 58 | margin-left: 11px; 59 | padding: 0; 60 | } 61 | &.right .tooltip-inner:after { 62 | border-right: 11px solid #FAE6A4; 63 | border-top: 11px solid rgba(0, 0, 0, 0); 64 | border-bottom: 11px solid rgba(0, 0, 0, 0); 65 | left: -10px; 66 | top: 0; 67 | margin-left: 0; 68 | } 69 | &.right .tooltip-inner:before { 70 | border-right: 11px solid rgba(0, 0, 0, 0.2); 71 | border-top: 11px solid rgba(0, 0, 0, 0); 72 | border-bottom: 11px solid rgba(0, 0, 0, 0); 73 | left: -11px; 74 | top: 0; 75 | margin-left: 0; 76 | } 77 | } 78 | 79 | .tooltip-arrow{ 80 | display: none; 81 | opacity: 0; 82 | } 83 | .tooltip-inner { 84 | background-color: #FAE6A4; 85 | border-radius: 4px; 86 | box-shadow: 0 1px 13px rgba(0, 0, 0, 0.14), 0 0 0 1px rgba(115, 71, 38, 0.23); 87 | color: #734726; 88 | max-width: 260px; 89 | min-width: 86px; 90 | padding: 6px 10px; 91 | text-align: center; 92 | text-decoration: none; 93 | } 94 | .tooltip-inner:after { 95 | content: ""; 96 | display: inline-block; 97 | left: 100%; 98 | margin-left: -60%; 99 | position: absolute; 100 | } 101 | .tooltip-inner:before { 102 | content: ""; 103 | display: inline-block; 104 | left: 100%; 105 | margin-left: -60%; 106 | position: absolute; 107 | } -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/_typography.scss: -------------------------------------------------------------------------------- 1 | /* Font Smoothing */ 2 | 3 | h1, .h1, h2, .h2, h3, .h3, h4, .h4, h5, .h5, h6, .h6, p, .navbar, .brand, .btn-simple, a, .td-name, td{ 4 | -moz-osx-font-smoothing: grayscale; 5 | -webkit-font-smoothing: antialiased; 6 | font-family: "Helvetica Neue","Open Sans",Arial,sans-serif; 7 | } 8 | 9 | h1, .h1, h2, .h2, h3, .h3, h4, .h4{ 10 | font-weight: $font-weight-normal; 11 | margin: $margin-large-vertical 0 $margin-base-vertical; 12 | } 13 | 14 | h1, .h1 { 15 | font-size: $font-size-h1; 16 | } 17 | h2, .h2{ 18 | font-size: $font-size-h2; 19 | } 20 | h3, .h3{ 21 | font-size: $font-size-h3; 22 | margin: 20px 0 10px; 23 | } 24 | h4, .h4{ 25 | font-size: $font-size-h4; 26 | line-height: 30px; 27 | } 28 | h5, .h5 { 29 | font-size: $font-size-h5; 30 | margin-bottom: 15px; 31 | } 32 | h6, .h6{ 33 | font-size: $font-size-h6; 34 | font-weight: $font-weight-bold; 35 | text-transform: uppercase; 36 | } 37 | p{ 38 | font-size: $font-paragraph; 39 | line-height: $line-height-general; 40 | } 41 | 42 | h1 small, h2 small, h3 small, h4 small, h5 small, h6 small, .h1 small, .h2 small, .h3 small, .h4 small, .h5 small, .h6 small, h1 .small, h2 .small, h3 .small, h4 .small, h5 .small, h6 .small, .h1 .small, .h2 .small, .h3 .small, .h4 .small, .h5 .small, .h6 .small { 43 | color: $dark-gray; 44 | font-weight: $font-weight-light; 45 | line-height: $line-height-general; 46 | } 47 | 48 | h1 small, h2 small, h3 small, h1 .small, h2 .small, h3 .small { 49 | font-size: 60%; 50 | } 51 | 52 | h1 .subtitle{ 53 | display: block; 54 | font-family: 'Grand Hotel',cursive; 55 | margin: 0 0 $margin-large-vertical; 56 | } 57 | 58 | .text-primary, .text-primary:hover{ 59 | color: $primary-color !important; 60 | } 61 | .text-info, .text-info:hover{ 62 | color: $info-color !important; 63 | } 64 | .text-success, .text-success:hover{ 65 | color: $success-color !important; 66 | } 67 | .text-warning, .text-warning:hover{ 68 | color: $warning-color !important; 69 | } 70 | .text-danger, .text-danger:hover{ 71 | color: $danger-color !important; 72 | } 73 | 74 | -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/mixins/_buttons.scss: -------------------------------------------------------------------------------- 1 | // Mixin for generating new styles 2 | @mixin btn-styles($btn-color, $btn-states-color) { 3 | 4 | background-color: $btn-color; 5 | border-color: $btn-color; 6 | 7 | &:hover, 8 | &:focus, 9 | &:active, 10 | &.active, 11 | .open > &.dropdown-toggle{ 12 | background-color: $btn-states-color; 13 | border-color: $btn-states-color; 14 | } 15 | 16 | &.btn-border, 17 | &.btn-round, 18 | &.btn-link{ 19 | border-color: $btn-color; 20 | color: $btn-color; 21 | 22 | &:hover, 23 | &:focus, 24 | &:active, 25 | &.active, 26 | &:active:focus, 27 | &:active:hover, 28 | &.active:focus, 29 | &.active:hover, 30 | .open > &.dropdown-toggle, 31 | .open > &.dropdown-toggle:focus, 32 | .open > &.dropdown-toggle:hover { 33 | background-color: $transparent-bg; 34 | color: $btn-states-color; 35 | border-color: $btn-states-color; 36 | } 37 | 38 | &.disabled, 39 | &:disabled, 40 | &[disabled], 41 | fieldset[disabled] & { 42 | &, 43 | &:hover, 44 | &:focus, 45 | &.focus, 46 | &:active, 47 | &.active { 48 | background-color: $transparent-bg; 49 | border-color: $btn-color; 50 | } 51 | } 52 | 53 | .caret{ 54 | border-top-color: $btn-color; 55 | } 56 | } 57 | } 58 | 59 | 60 | @mixin btn-size($padding-vertical, $padding-horizontal, $font-size, $border){ 61 | font-size: $font-size; 62 | border-radius: $border; 63 | padding: $padding-vertical $padding-horizontal; 64 | 65 | &.btn-round{ 66 | padding: $padding-vertical + 1 $padding-horizontal; 67 | } 68 | 69 | &.btn-link{ 70 | padding: $padding-vertical + 2 $padding-horizontal; 71 | } 72 | 73 | } -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/mixins/_inputs.scss: -------------------------------------------------------------------------------- 1 | @mixin input-size($padding-vertical, $padding-horizontal, $height){ 2 | padding: $padding-vertical $padding-horizontal; 3 | height: $height; 4 | } 5 | 6 | @mixin placeholder($color, $opacity){ 7 | color: $color; 8 | @include opacity(1); 9 | } 10 | 11 | @mixin light-form(){ 12 | border-radius: 0; 13 | border:0; 14 | padding: 0; 15 | background-color: transparent; 16 | 17 | } -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/mixins/_labels.scss: -------------------------------------------------------------------------------- 1 | @mixin label-style(){ 2 | padding: $padding-label-vertical $padding-label-horizontal; 3 | border: 1px solid $default-color; 4 | border-radius: $border-radius-small; 5 | color: $default-color; 6 | font-weight: $font-weight-semi; 7 | font-size: $font-size-small; 8 | text-transform: uppercase; 9 | display: inline-block; 10 | vertical-align: middle; 11 | } 12 | 13 | @mixin label-color($color){ 14 | border-color: $color; 15 | color: $color; 16 | } 17 | @mixin label-color-fill($color){ 18 | border-color: $color; 19 | color: $white-color; 20 | background-color: $color; 21 | } -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/mixins/_navbars.scss: -------------------------------------------------------------------------------- 1 | @mixin navbar-color($color){ 2 | background-color: $color; 3 | } 4 | 5 | @mixin center-item(){ 6 | left: 0; 7 | right: 0; 8 | margin-right: auto; 9 | margin-left: auto; 10 | position: absolute; 11 | } -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/mixins/_tabs.scss: -------------------------------------------------------------------------------- 1 | @mixin pill-style($color){ 2 | border: 1px solid $color; 3 | color: $color; 4 | } -------------------------------------------------------------------------------- /public/css/themes/gsdk/gsdk/mixins/_transparency.scss: -------------------------------------------------------------------------------- 1 | // Opacity 2 | 3 | @mixin opacity($opacity) { 4 | opacity: $opacity; 5 | // IE8 filter 6 | $opacity-ie: ($opacity * 100); 7 | filter: #{alpha(opacity=$opacity-ie)}; 8 | } 9 | 10 | @mixin black-filter($opacity){ 11 | top: 0; 12 | left: 0; 13 | height: 100%; 14 | width: 100%; 15 | position: absolute; 16 | background-color: rgba(17,17,17,$opacity); 17 | display: block; 18 | content: ""; 19 | z-index: 1; 20 | } -------------------------------------------------------------------------------- /public/css/themes/modern/_modern.scss: -------------------------------------------------------------------------------- 1 | @import "../../lib/bootstrap-social"; 2 | 3 | // Footer 4 | // ------------------------- 5 | 6 | footer { 7 | color: #fff; 8 | background-color: $gray-darker; 9 | 10 | a:hover { 11 | color: #fff; 12 | } 13 | } 14 | 15 | // Typography 16 | // ------------------------- 17 | 18 | a { 19 | transition: all .4s cubic-bezier(.24,.45,.46,.92); 20 | 21 | &:hover { 22 | text-decoration: none; 23 | } 24 | } 25 | 26 | ul, 27 | p { 28 | font-size: 14px; 29 | } 30 | 31 | .text-danger { 32 | color: $brand-danger; 33 | } 34 | 35 | // Dropdowns 36 | // ------------------------- 37 | 38 | .dropdown-menu > li > a { 39 | font-weight: 300; 40 | } 41 | 42 | // Buttons 43 | // ------------------------- 44 | 45 | .btn { 46 | padding: 10px 16px; 47 | border: 0; 48 | border-radius: 3px; 49 | transition: all .4s cubic-bezier(.24,.45,.46,.92); 50 | 51 | &:hover { 52 | color: #fff; 53 | background-color: $gray-darker; 54 | } 55 | } 56 | 57 | .btn-social { 58 | padding: 10px 50px; 59 | 60 | :first-child { 61 | width: 38px; 62 | line-height: 38px; 63 | } 64 | } 65 | 66 | .btn-link { 67 | 68 | &:hover { 69 | color: $gray-darker; 70 | text-decoration: none; 71 | background-color: transparent; 72 | } 73 | } 74 | 75 | // Form states and alerts 76 | // ------------------------- 77 | 78 | .alert { 79 | color: #fff; 80 | } 81 | 82 | // Navbar 83 | // ------------------------- 84 | 85 | .navbar-default { 86 | border: 0; 87 | box-shadow: 0 1px 5px rgba(0,0,0,.15); 88 | 89 | .navbar-nav { 90 | > li > a { 91 | transition: color .4s cubic-bezier(.24,.45,.46,.92); 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /public/css/themes/modern/_variables.scss: -------------------------------------------------------------------------------- 1 | @import url("http://fonts.googleapis.com/css?family=Montserrat:400,700"); 2 | 3 | // Colors 4 | // ------------------------- 5 | 6 | $gray-lighter: #f5f5f5; 7 | $gray-light: #e0e0e0; 8 | $gray: #737373; 9 | $gray-dark: #404040; 10 | $gray-darker: #121212; 11 | 12 | $brand-primary: #2ac5ee; 13 | $brand-success: #0f9d58; 14 | $brand-warning: #f4b400; 15 | $brand-danger: #d80017; 16 | $brand-info: #5bc0dd; 17 | 18 | // Typography 19 | // ------------------------- 20 | 21 | $font-size-base: 13px; 22 | $headings-font-family: 'Montserrat', sans-serif; 23 | $headings-font-weight: 700; 24 | $text-color: $gray-darker; 25 | $link-color: $brand-primary; 26 | $link-hover-color: $gray-darker; 27 | 28 | // Dropdowns 29 | // ------------------------- 30 | 31 | $dropdown-link-color: $gray-darker; 32 | 33 | // Buttons 34 | // ------------------------- 35 | 36 | $btn-default-bg: $gray-lighter; 37 | $btn-default-color: $gray-darker; 38 | 39 | // Forms 40 | // ------------------------- 41 | 42 | $input-border-radius: 2px; 43 | $input-border-focus: #999; 44 | $input-border: #f0f0f0; 45 | $input-color: #444; 46 | $input-color-placeholder: #999; 47 | $input-height-base: 44px; 48 | 49 | // Form states and alerts 50 | // ------------------------- 51 | 52 | $state-success-bg: $brand-success; 53 | $state-info-bg: $brand-primary; 54 | $state-warning-bg: $brand-warning; 55 | $state-danger-bg: $brand-danger; 56 | 57 | // Navbar 58 | // ------------------------- 59 | 60 | $navbar-height: 50px; 61 | $navbar-default-bg: rgba(255,255,255,0.95); 62 | $navbar-default-link-color: $gray-darker; 63 | $navbar-default-link-active-color: $brand-primary; 64 | $navbar-default-link-active-bg: transparent; 65 | $navbar-default-link-hover-color: $brand-primary; 66 | $navbar-default-link-hover-bg: transparent; 67 | $navbar-default-brand-color: $gray-darker; 68 | $navbar-default-brand-hover-color: $brand-primary; -------------------------------------------------------------------------------- /public/favicon.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/favicon.ai -------------------------------------------------------------------------------- /public/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/favicon.png -------------------------------------------------------------------------------- /public/fonts/FontAwesome.otf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/fonts/FontAwesome.otf -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/fonts/fontawesome-webfont.eot -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/fonts/fontawesome-webfont.ttf -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /public/fonts/fontawesome-webfont.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/fonts/fontawesome-webfont.woff2 -------------------------------------------------------------------------------- /public/img/contest/1-headline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/1-headline.ai -------------------------------------------------------------------------------- /public/img/contest/2-headline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/2-headline.ai -------------------------------------------------------------------------------- /public/img/contest/3-headline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/3-headline.ai -------------------------------------------------------------------------------- /public/img/contest/4-headline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/4-headline.ai -------------------------------------------------------------------------------- /public/img/contest/4-headline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/4-headline.png -------------------------------------------------------------------------------- /public/img/contest/5-headline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/5-headline.ai -------------------------------------------------------------------------------- /public/img/contest/6-headline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/6-headline.ai -------------------------------------------------------------------------------- /public/img/contest/hackathon2018-headline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/hackathon2018-headline.ai -------------------------------------------------------------------------------- /public/img/contest/hackathon2018-headline.svg: -------------------------------------------------------------------------------- 1 | 2 | hackathon2018-headline 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /public/img/contest/komabasai2018-day1-headline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/komabasai2018-day1-headline.ai -------------------------------------------------------------------------------- /public/img/contest/komabasai2018-day1-headline.svg: -------------------------------------------------------------------------------- 1 | 2 | komabasai2018-day1-headline 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /public/img/contest/komabasai2018-day2-headline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/komabasai2018-day2-headline.ai -------------------------------------------------------------------------------- /public/img/contest/komabasai2018-day2-headline.svg: -------------------------------------------------------------------------------- 1 | 2 | komabasai2018-day2-headline 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /public/img/contest/komabasai2019-headline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/komabasai2019-headline.ai -------------------------------------------------------------------------------- /public/img/contest/komabasai2019-headline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /public/img/contest/mayfes2018-day1-headline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/mayfes2018-day1-headline.ai -------------------------------------------------------------------------------- /public/img/contest/mayfes2018-day1-headline.svg: -------------------------------------------------------------------------------- 1 | mayfes2018-day1-headline -------------------------------------------------------------------------------- /public/img/contest/mayfes2018-day2-headline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/mayfes2018-day2-headline.ai -------------------------------------------------------------------------------- /public/img/contest/mayfes2018-day2-headline.svg: -------------------------------------------------------------------------------- 1 | mayfes2018-day2-headline -------------------------------------------------------------------------------- /public/img/contest/mayfes2019-day1-headline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/mayfes2019-day1-headline.ai -------------------------------------------------------------------------------- /public/img/contest/mayfes2019-day1-headline.svg: -------------------------------------------------------------------------------- 1 | 2 | mayfes2019-day1-headline 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /public/img/contest/mayfes2019-day2-headline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/mayfes2019-day2-headline.ai -------------------------------------------------------------------------------- /public/img/contest/mayfes2019-day2-headline.svg: -------------------------------------------------------------------------------- 1 | 2 | mayfes2019-day2-headline 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /public/img/contest/mayfes2020-day1-headline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/mayfes2020-day1-headline.ai -------------------------------------------------------------------------------- /public/img/contest/mayfes2020-day1-headline.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /public/img/contest/mayfes2020-day2-headline.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/mayfes2020-day2-headline.ai -------------------------------------------------------------------------------- /public/img/contest/noise.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hakatashi/esolang-battle/2602ba8267609291b6dfcfa05829bee0317ec238/public/img/contest/noise.png -------------------------------------------------------------------------------- /public/js/api.js: -------------------------------------------------------------------------------- 1 | export default async (method, endpoint, params = {}) => { 2 | const url = `/api${endpoint}`; 3 | 4 | if (method === 'GET') { 5 | const res = await fetch(`${url}?${new URLSearchParams(params).toString()}`, { 6 | method: 'GET', 7 | credentials: 'include', 8 | }); 9 | return res.json(); 10 | } else if (method === 'POST') { 11 | const csrfToken = document 12 | .querySelector('meta[name=csrf-token]') 13 | .getAttribute('content'); 14 | 15 | const form = new FormData(); 16 | for (const param of Object.keys(params)) { 17 | const value = params[param]; 18 | 19 | if (value instanceof HTMLInputElement) { 20 | form.append(param, value.files[0]); 21 | } else { 22 | form.append(param, value); 23 | } 24 | } 25 | 26 | form.append('_csrf', csrfToken); 27 | 28 | const res = await fetch(url, { 29 | method: 'POST', 30 | body: form, 31 | credentials: 'include', 32 | }); 33 | return res.json(); 34 | } 35 | 36 | return Promise.reject(new Error('method unknown')); 37 | }; 38 | -------------------------------------------------------------------------------- /public/js/check.babel.js: -------------------------------------------------------------------------------- 1 | import '@babel/polyfill'; 2 | 3 | import api from './api.js'; 4 | 5 | const checkerEl = document.getElementById('checker'); 6 | const languageEl = checkerEl.querySelector('.checker-language'); 7 | const codeEl = checkerEl.querySelector('.checker-code'); 8 | const fileEl = checkerEl.querySelector('.checker-file'); 9 | const countEl = checkerEl.querySelector('.checker-count'); 10 | const stdinEl = checkerEl.querySelector('.checker-stdin'); 11 | const stdoutEl = checkerEl.querySelector('.checker-stdout'); 12 | const stderrEl = checkerEl.querySelector('.checker-stderr'); 13 | const submitEl = checkerEl.querySelector('.checker-submit'); 14 | const alertEl = checkerEl.querySelector('.checker-alert'); 15 | 16 | const autoSaveInput = (element, id) => { 17 | const storedValue = localStorage.getItem(`${location.pathname}#${id}`); 18 | if (storedValue) { 19 | element.value = storedValue; 20 | } 21 | element.addEventListener('input', () => { 22 | localStorage.setItem(`${location.pathname}#${id}`, element.value); 23 | }); 24 | }; 25 | 26 | autoSaveInput(languageEl, 'language'); 27 | autoSaveInput(codeEl, 'code'); 28 | autoSaveInput(stdinEl, 'stdin'); 29 | 30 | const contestId = document 31 | .querySelector('meta[name=contest-id]') 32 | .getAttribute('content'); 33 | 34 | const onSubmit = async () => { 35 | if (submitEl.disabled) { 36 | return; 37 | } 38 | 39 | submitEl.disabled = true; 40 | alertEl.style.display = 'none'; 41 | 42 | const result = await api('POST', `/contests/${contestId}/execution`, { 43 | language: languageEl.value, 44 | input: stdinEl.value, 45 | ...(fileEl.files.length > 0 46 | ? {file: fileEl.files[0]} 47 | : {code: codeEl.value}), 48 | }); 49 | 50 | if (result.error) { 51 | alertEl.style.display = 'block'; 52 | alertEl.textContent = result.error; 53 | stdoutEl.value = ''; 54 | stderrEl.value = ''; 55 | } else { 56 | stdoutEl.value = result.stdout; 57 | stderrEl.value = result.stderr; 58 | } 59 | 60 | submitEl.disabled = false; 61 | }; 62 | 63 | submitEl.addEventListener('click', onSubmit); 64 | 65 | for (const eventName of ['input', 'focus', 'paste']) { 66 | codeEl.addEventListener(eventName, () => { 67 | countEl.textContent = new TextEncoder().encode(codeEl.value).length; 68 | }); 69 | } 70 | 71 | fileEl.addEventListener('change', () => { 72 | if (fileEl.files.length > 0) { 73 | codeEl.disabled = true; 74 | } else { 75 | codeEl.disabled = false; 76 | } 77 | }); 78 | 79 | window.addEventListener('keydown', (event) => { 80 | if (event.key === 'Enter' && event.ctrlKey) { 81 | onSubmit(); 82 | } 83 | }); 84 | -------------------------------------------------------------------------------- /public/js/contests/4/index.babel.js: -------------------------------------------------------------------------------- 1 | import '@babel/polyfill'; 2 | 3 | import React from 'react'; 4 | import ReactDOM from 'react-dom'; 5 | import Simple from './app.jsx'; 6 | 7 | ReactDOM.render(React.createElement(Simple), document.getElementById('app')); 8 | -------------------------------------------------------------------------------- /public/js/contests/5/index.babel.js: -------------------------------------------------------------------------------- 1 | import '@babel/polyfill'; 2 | 3 | import React from 'react'; 4 | import ReactDOM from 'react-dom'; 5 | import Simple from './app.jsx'; 6 | 7 | ReactDOM.render(React.createElement(Simple), document.getElementById('app')); 8 | -------------------------------------------------------------------------------- /public/js/contests/6/index.babel.js: -------------------------------------------------------------------------------- 1 | import '@babel/polyfill'; 2 | 3 | import React from 'react'; 4 | import ReactDOM from 'react-dom'; 5 | import Simple from './app.jsx'; 6 | 7 | ReactDOM.render(React.createElement(Simple), document.getElementById('app')); 8 | -------------------------------------------------------------------------------- /public/js/contests/hackathon2018/index.babel.js: -------------------------------------------------------------------------------- 1 | import '@babel/polyfill'; 2 | 3 | import React from 'react'; 4 | import ReactDOM from 'react-dom'; 5 | import App from './app.jsx'; 6 | 7 | ReactDOM.render(React.createElement(App), document.getElementById('app')); 8 | -------------------------------------------------------------------------------- /public/js/contests/mayfes2018/index.babel.js: -------------------------------------------------------------------------------- 1 | import '@babel/polyfill'; 2 | 3 | import React from 'react'; 4 | import ReactDOM from 'react-dom'; 5 | import App from './app.jsx'; 6 | 7 | ReactDOM.render(React.createElement(App), document.getElementById('app')); 8 | -------------------------------------------------------------------------------- /public/js/contests/mayfes2020-day2/index.babel.js: -------------------------------------------------------------------------------- 1 | import '@babel/polyfill'; 2 | 3 | import React from 'react'; 4 | import ReactDOM from 'react-dom'; 5 | import Simple from './app.jsx'; 6 | 7 | ReactDOM.render(React.createElement(Simple), document.getElementById('app')); 8 | -------------------------------------------------------------------------------- /public/js/contests/mayfes2021-day2/index.babel.js: -------------------------------------------------------------------------------- 1 | import '@babel/polyfill'; 2 | 3 | import React from 'react'; 4 | import ReactDOM from 'react-dom'; 5 | import Simple from './app.jsx'; 6 | 7 | ReactDOM.render(React.createElement(Simple), document.getElementById('app')); 8 | -------------------------------------------------------------------------------- /test/app.mjs: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | import request from 'supertest'; 4 | import app from '../app.mjs'; 5 | 6 | describe('Router', function () { 7 | this.timeout(10000); 8 | 9 | describe('GET /', () => { 10 | it('should return 200 OK', (done) => { 11 | request(app) 12 | .get('/') 13 | .expect(200, done); 14 | }); 15 | }); 16 | 17 | describe('GET /login', () => { 18 | it('should return 200 OK', (done) => { 19 | request(app) 20 | .get('/login') 21 | .expect(200, done); 22 | }); 23 | }); 24 | 25 | describe('GET /random-url', () => { 26 | it('should return 404', (done) => { 27 | request(app) 28 | .get('/reset') 29 | .expect(404, done); 30 | }); 31 | }); 32 | }); 33 | -------------------------------------------------------------------------------- /test/models.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | const {expect} = require('chai'); 4 | const sinon = require('sinon'); 5 | require('sinon-mongoose'); 6 | 7 | const User = require('../models/User'); 8 | 9 | describe('User Model', () => { 10 | it('should create a new user', (done) => { 11 | const UserMock = sinon.mock( 12 | new User({email: 'test@gmail.com', password: 'root'}), 13 | ); 14 | const user = UserMock.object; 15 | 16 | UserMock.expects('save').yields(null); 17 | 18 | user.save((err) => { 19 | UserMock.verify(); 20 | UserMock.restore(); 21 | expect(err).to.be.null; 22 | done(); 23 | }); 24 | }); 25 | 26 | it('should return error if user is not created', (done) => { 27 | const UserMock = sinon.mock( 28 | new User({email: 'test@gmail.com', password: 'root'}), 29 | ); 30 | const user = UserMock.object; 31 | const expectedError = { 32 | name: 'ValidationError', 33 | }; 34 | 35 | UserMock.expects('save').yields(expectedError); 36 | 37 | user.save((err, result) => { 38 | UserMock.verify(); 39 | UserMock.restore(); 40 | expect(err.name).to.equal('ValidationError'); 41 | expect(result).to.be.undefined; 42 | done(); 43 | }); 44 | }); 45 | 46 | it('should not create a user with the unique email', (done) => { 47 | const UserMock = sinon.mock( 48 | User({email: 'test@gmail.com', password: 'root'}), 49 | ); 50 | const user = UserMock.object; 51 | const expectedError = { 52 | name: 'MongoError', 53 | code: 11000, 54 | }; 55 | 56 | UserMock.expects('save').yields(expectedError); 57 | 58 | user.save((err, result) => { 59 | UserMock.verify(); 60 | UserMock.restore(); 61 | expect(err.name).to.equal('MongoError'); 62 | expect(err.code).to.equal(11000); 63 | expect(result).to.be.undefined; 64 | done(); 65 | }); 66 | }); 67 | 68 | it('should find user by email', (done) => { 69 | const userMock = sinon.mock(User); 70 | const expectedUser = { 71 | _id: '5700a128bd97c1341d8fb365', 72 | email: 'test@gmail.com', 73 | }; 74 | 75 | userMock 76 | .expects('findOne') 77 | .withArgs({email: 'test@gmail.com'}) 78 | .yields(null, expectedUser); 79 | 80 | User.findOne({email: 'test@gmail.com'}, (err, result) => { 81 | expect(err).to.be.null; 82 | userMock.verify(); 83 | userMock.restore(); 84 | expect(result.email).to.equal('test@gmail.com'); 85 | done(); 86 | }); 87 | }); 88 | 89 | it('should delete user by email', (done) => { 90 | const userMock = sinon.mock(User); 91 | const expectedResult = { 92 | nRemoved: 1, 93 | }; 94 | 95 | userMock 96 | .expects('deleteOne') 97 | .withArgs({email: 'test@gmail.com'}) 98 | .yields(null, expectedResult); 99 | 100 | User.deleteOne({email: 'test@gmail.com'}, (err, result) => { 101 | userMock.verify(); 102 | userMock.restore(); 103 | expect(err).to.be.null; 104 | expect(result.nRemoved).to.equal(1); 105 | done(); 106 | }); 107 | }); 108 | }); 109 | -------------------------------------------------------------------------------- /views/account/login.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block content 4 | .container.content 5 | form.form-horizontal(method='POST', style={margin: '5em 0'}) 6 | input(type='hidden', name='_csrf', value=_csrf) 7 | .form-group 8 | a.btn.btn-primary.btn-lg.btn-block(href='/auth/twitter') 9 | i.fa.fa-twitter 10 | | Sign in with Twitter 11 | -------------------------------------------------------------------------------- /views/account/profile.pug: -------------------------------------------------------------------------------- 1 | extends ../layout 2 | 3 | block content 4 | .container.content 5 | .page-header 6 | h3 Profile Information 7 | 8 | p= `You are ${user.name()}, I know :)` 9 | -------------------------------------------------------------------------------- /views/admin.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | include mixins 4 | 5 | block content 6 | .container.content 7 | .page-header 8 | h3 Admin 9 | 10 | .table-responsive 11 | table.table 12 | thead 13 | tr 14 | th Name 15 | th Team 16 | th Set Team 17 | tbody 18 | each user in users 19 | tr 20 | td= user.name() 21 | td 22 | button.btn.text-white(style={'background-color': colors[user.getTeam(contest)]})= teams[user.getTeam(contest)] 23 | td 24 | each color, index in colors 25 | a.btn.text-white(style={'background-color': color}, href=`?${qs.encode({user: user._id.toString(), team: index})}`)= teams[index] 26 | = ' ' 27 | -------------------------------------------------------------------------------- /views/check.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block head 4 | if contest.isStarted() || (user && user.admin) 5 | script(src='/js/check.js', defer) 6 | 7 | include mixins 8 | 9 | block content 10 | .container.content 11 | h1 Performance Checker 12 | form#checker 13 | .form-group 14 | label Language 15 | select.form-control.checker-language 16 | each language in availableLanguages 17 | option(value=language.slug)= language.name 18 | .form-group 19 | label Code 20 | p.text-secondary 21 | span.checker-count 0 22 | = ' ' 23 | | bytes 24 | textarea.form-control.code.checker-code(name='code', spellcheck='false') 25 | .form-group 26 | input.form-contril-file.checker-file(type='file') 27 | .form-group 28 | label stdin 29 | textarea.form-control.code.checker-stdin(name='stdin', spellcheck='false') 30 | button.btn.btn-primary.checker-submit(type='button') Submit 31 | hr 32 | .alert.alert-danger.checker-alert(style={display: 'none'}) 33 | .form-group 34 | label stdout 35 | textarea.form-control.code.checker-stdout(readonly, name='stdout', data-storage='false') 36 | .form-group 37 | label stderr 38 | textarea.form-control.code.checker-stderr(readonly, name='stderr', data-storage='false') 39 | -------------------------------------------------------------------------------- /views/home.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | .p-5.mb-4.bg-light.rounded-3.jumbotron-fluid 5 | .container 6 | h1.display-4(style={'word-wrap': 'break-word'})= helloworld 7 | hr.my-4 8 | p Welcome to Esolang Battler. This is a contest system for codegolf competitions held by TSG. Hang tight to match new contest or check out the shining records of the past contests! 9 | 10 | .container 11 | each contest in contests 12 | a.jumbotron-wrap(href=`/contests/${contest.id}`) 13 | .p-4.mb-4.rounded-3.jumbotron(class=`contest-${contest.isStarted() ? contest.id : 'unknown'}`) 14 | h1.display-4= contest.name 15 | p.lead 16 | strong= contest.spanText() 17 | p.lead 18 | .btn.btn-lg(class=contest.isEnded() ? 'btn-dark' : 'btn-primary', href=`/contests/${contest.id}`, role='button') 19 | if contest.isEnded() 20 | | View Result 21 | else 22 | | Join Now! 23 | -------------------------------------------------------------------------------- /views/layout.pug: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | meta(charset='utf-8') 5 | meta(http-equiv='X-UA-Compatible', content='IE=edge') 6 | meta(name='viewport', content='width=device-width, initial-scale=1.0') 7 | meta(name='theme-color' content='#4DA5F4') 8 | meta(name='csrf-token', content=_csrf) 9 | meta(name='login', content=user ? 'true' : 'false') 10 | meta(name='google-site-verification', content='KtZjmWdS59LVbodX-T7iwVZ-05QUhf_4D4gG2HUHUUc') 11 | if contest 12 | meta(name='contest-id', content=contest.id) 13 | meta(data-name='contest-start', content=contest.start.toISOString()) 14 | title 15 | = title ? `${title} - ` : '' 16 | = contest ? `${contest.name}` : 'Esolang Battler' 17 | link(rel='shortcut icon', href=`/favicon.png?${hash}`) 18 | link(rel='stylesheet', href=`/css/main.css?${hash}`) 19 | block head 20 | 21 | body 22 | include partials/header 23 | 24 | .container 25 | include partials/flash 26 | 27 | block content 28 | 29 | include partials/footer 30 | 31 | script(src=`/socket.io/socket.io.js`) 32 | script(src='https://code.jquery.com/jquery-3.2.1.slim.min.js', integrity='sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN', crossorigin='anonymous') 33 | script(src='https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js', integrity='sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q', crossorigin='anonymous') 34 | script(src='https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js', integrity='sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl', crossorigin='anonymous') 35 | script(src='https://www.googletagmanager.com/gtag/js?id=UA-43802516-9', async) 36 | 37 | if env === 'production' 38 | script. 39 | window.dataLayer = window.dataLayer || []; 40 | function gtag(){dataLayer.push(arguments);} 41 | gtag('js', new Date()); 42 | gtag('config', 'UA-43802516-9'); 43 | -------------------------------------------------------------------------------- /views/maps/1.pug: -------------------------------------------------------------------------------- 1 | - for (let y = 0; y < 10; y++) 2 | .row 3 | - for (let x = 0; x < 10; x++) 4 | - const language = languageMap[y * 10 + x] 5 | a.language.cell( 6 | class=classnames(language.solution && `user-${language.solution.user.slice(1)}`), 7 | href=language.solution ? `/contests/${contest.id}/submissions/${language.solution._id}` : undefined, 8 | target='_blank', rel='noopener noreferrer' 9 | ) 10 | .language-label 11 | .name= language.name 12 | .size= language.solution ? language.solution.size : '' 13 | -------------------------------------------------------------------------------- /views/maps/2.pug: -------------------------------------------------------------------------------- 1 | - for (let y = 0; y < 9; y++) 2 | .row 3 | - for (let x = 0; x < 9; x++) 4 | - const language = languageMap[y * 9 + x] 5 | a.language.cell( 6 | class=classnames({ 7 | red: language.team === 0, 8 | blue: language.team === 1, 9 | gray: language.team === undefined, 10 | }), 11 | href=language.solution ? `/contests/${contest.id}/submissions/${language.solution._id}` : undefined, 12 | target='_blank', rel='noopener noreferrer' 13 | ) 14 | .language-label 15 | .name= language.name 16 | .size= language.solution ? language.solution.size : '' 17 | - const red = languageMap.filter(({team}) => team === 0).length 18 | - const blue = languageMap.filter(({team}) => team === 1).length 19 | .team-info.red 20 | .team-name Red 21 | .score= red 22 | .bar(style={'flex-basis': `${red / (red + blue) * 100}%`}) 23 | .team-info.blue 24 | .team-name Blue 25 | .score= blue 26 | .bar(style={'flex-basis': `${blue / (red + blue) * 100}%`}) 27 | -------------------------------------------------------------------------------- /views/maps/3.pug: -------------------------------------------------------------------------------- 1 | - for (let y = 0; y < 8; y++) 2 | .row 3 | - for (let x = 0; x < 15; x++) 4 | - const margin = (y < 4) ? (3 - y) : (y - 4) 5 | - const language = languageMap[y * 15 + x] 6 | a.language.cell( 7 | class=classnames((x + y) % 2 ? 'up' : 'down', { 8 | red: language.team === 0, 9 | blue: language.team === 1, 10 | green: language.team === 2, 11 | gray: language.team === undefined, 12 | invalid: x < margin || 14 - x < margin, 13 | }), 14 | href=language.solution ? `/contests/${contest.id}/submissions/${language.solution._id}` : undefined, 15 | target='_blank', rel='noopener noreferrer' 16 | ) 17 | svg.triangle-area(viewBox="0 0 1000 866") 18 | polygon.triangle(points="0,866 500,0 1000,866") 19 | .language-label 20 | .name= language.name 21 | .size= language.solution ? language.solution.size : '' 22 | - const red = languageMap.filter(({team}) => team === 0).length 23 | - const blue = languageMap.filter(({team}) => team === 1).length 24 | - const green = languageMap.filter(({team}) => team === 2).length 25 | .team-info.red 26 | .team-name Red 27 | .score= red 28 | .bar(style={'flex-basis': `${red / (red + blue + green) * 100}%`}) 29 | .team-info.blue 30 | .team-name Blue 31 | .score= blue 32 | .bar(style={'flex-basis': `${blue / (red + blue + green) * 100}%`}) 33 | .team-info.green 34 | .team-name Green 35 | .score= green 36 | .bar(style={'flex-basis': `${green / (red + blue + green) * 100}%`}) 37 | -------------------------------------------------------------------------------- /views/mixins.pug: -------------------------------------------------------------------------------- 1 | mixin submissionBadge(submission) 2 | - 3 | const badgeClass = {pending: 'bg-secondary', success: 'bg-success', failed: 'bg-warning', error: 'bg-danger', invalid: 'bg-danger'}[submission.status] 4 | const badgeText = {pending: 'WJ', success: 'AC', failed: 'WA', error: 'ERROR', invalid: 'INVALID'}[submission.status] 5 | span.badge(class=badgeClass) 6 | if submission.error && submission.error.name === 'TimeoutError' 7 | | TLE 8 | else if submission.error && submission.error.name === 'MemoryLimitExceededError' 9 | | MLE 10 | else 11 | = badgeText 12 | -------------------------------------------------------------------------------- /views/partials/countdown.pug: -------------------------------------------------------------------------------- 1 | h1.p-4 Contest will start in *** 2 | script(async). 3 | const update = () => { 4 | const countdown = document.getElementById('countdown'); 5 | const contestStart = Date.parse(document.querySelector('meta[data-name="contest-start"]').getAttribute('content')); 6 | const now = Date.now(); 7 | 8 | const remaining = contestStart - now; 9 | 10 | if (remaining < 0) { 11 | setTimeout(() => { 12 | location.reload(); 13 | }, 3000) 14 | return; 15 | } 16 | 17 | const days = Math.floor(remaining / 1000 / 60 / 60 / 24); 18 | const hours = Math.floor(remaining / 1000 / 60 / 60) % 24; 19 | const minutes = Math.floor(remaining / 1000 / 60) % 60; 20 | const seconds = Math.floor(remaining / 1000) % 60; 21 | 22 | countdown.textContent = `${days === 0 ? '' : `${days} days + `}${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`; 23 | }; 24 | setInterval(update, 1000); 25 | update(); 26 | -------------------------------------------------------------------------------- /views/partials/flash.pug: -------------------------------------------------------------------------------- 1 | if messages.errors 2 | .alert.alert-danger.fade.in 3 | button.close(type='button', data-dismiss='alert') 4 | i.fa.fa-times-circle-o 5 | for error in messages.errors 6 | div= error.msg 7 | if messages.info 8 | .alert.alert-info.fade.in 9 | button.close(type='button', data-dismiss='alert') 10 | i.fa.fa-times-circle-o 11 | for info in messages.info 12 | div= info.msg 13 | if messages.success 14 | .alert.alert-success.fade.in 15 | button.close(type='button', data-dismiss='alert') 16 | i.fa.fa-times-circle-o 17 | for success in messages.success 18 | div= success.msg 19 | -------------------------------------------------------------------------------- /views/partials/footer.pug: -------------------------------------------------------------------------------- 1 | if !hideFooter 2 | .container-fluid.text-center.d-flex.footer 3 | p.mr-auto.p-2 Created by @hakatashi 4 | p.p-2 5 | a(href='https://github.com/hakatashi/esolang-battle') GitHub 6 | p.p-2 7 | a(href='https://github.com/hakatashi/esolang-battle/issues') Issues 8 | -------------------------------------------------------------------------------- /views/partials/header.pug: -------------------------------------------------------------------------------- 1 | .navbar.navbar-expand-lg.navbar-dark.bg-dark.fixed-top 2 | .container-fluid 3 | a.navbar-brand.contest-title(href=contest ? `/contests/${contest.id}` : '/') 4 | = contest ? contest.name : 'Esolang Battler' 5 | = ' ' 6 | button.navbar-toggler(type='button', data-toggle='collapse', data-target='#navbarSupportedContent') 7 | span.navbar-toggler-icon 8 | .collapse.navbar-collapse#navbarSupportedContent 9 | ul.navbar-nav.me-auto 10 | if contest 11 | li.nav-item(class=(title == '') ? 'active' : undefined) 12 | a.nav-link(href=`/contests/${contest.id}`) Home 13 | li.nav-item(class=(title == 'Rule') ? 'active' : undefined) 14 | a.nav-link(href=`/contests/${contest.id}/rule`) Rule 15 | li.nav-item(class=(title == 'Submissions') ? 'active' : undefined) 16 | a.nav-link(href=`/contests/${contest.id}/submissions`) Submissions 17 | if contest.isOpen() 18 | li.nav-item(class=(title == 'Check') ? 'active' : undefined) 19 | a.nav-link(href=`/contests/${contest.id}/check`) Check 20 | li.nav-item 21 | small.nav-link 22 | = contest ? contest.spanText() : '' 23 | ul.navbar-nav 24 | if !user 25 | if title !== 'Login' 26 | li.nav-item 27 | a.nav-link(href='/login') Login 28 | else 29 | li.nav-item.dropdown 30 | a.nav-link.dropdown-toggle#navbarDropdown(href='#', data-toggle='dropdown') 31 | if user.profile.picture 32 | img(src=user.profile.picture) 33 | else 34 | img(src=user.gravatar(60)) 35 | span= user.name() || user.email || user.id 36 | i.caret 37 | .dropdown-menu 38 | a.dropdown-item(href='/account') My Account 39 | a.dropdown-item(href='/logout') Logout 40 | li.nav-item 41 | a.nav-link.btn.btn-outline-secondary.btn-sm(href='/') Top 42 | -------------------------------------------------------------------------------- /views/rule.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | .container.content 5 | if !contest.isStarted() && (!user || !user.admin) 6 | include partials/countdown.pug 7 | else 8 | h1 Rule 9 | != description.en 10 | 11 | hr(style={margin: '5em 0'}) 12 | 13 | h1 ルール 14 | != description.ja 15 | -------------------------------------------------------------------------------- /views/submission.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | include mixins 4 | 5 | block content 6 | .container.content 7 | .page-header 8 | h1 9 | strong= `${submission.language.slug}, ${submission.size} bytes` 10 | 11 | p 12 | | by 13 | - const team = submission.user.team.find((t) => t.contest.equals(contest._id)) 14 | if team 15 | span.team-badge(class=`team-${team.value}`) 16 | = submission.user.name() 17 | 18 | h4 19 | | Result 20 | if submission.url 21 | = ' ' 22 | small 23 | a(href=submission.url, target='_blank', rel='noopener noreferrer') [link] 24 | 25 | p 26 | +submissionBadge(submission) 27 | 28 | if submission.duration 29 | h4 Duration 30 | p= `${submission.duration}ms` 31 | 32 | if user && user.admin && submission.error && submission.error.stack 33 | pre= submission.error.stack 34 | 35 | if selfTeam || contest.isEnded() || (user && user.admin) 36 | h4 37 | | Code 38 | = ' ' 39 | small 40 | a(href=`${submission._id}/raw`) [DL] 41 | p 42 | pre(class = isHexdump ? 'hexdump' : '')= code 43 | 44 | if typeof submission.disasm === 'string' 45 | h4 Disassembly 46 | p 47 | pre= submission.disasm 48 | 49 | h4 stdin 50 | p 51 | pre= submission.input 52 | 53 | h4 stdout 54 | p 55 | pre= submission.stdout 56 | 57 | h4 stderr 58 | p 59 | pre= submission.stderr 60 | 61 | h4 strace 62 | p 63 | pre= submission.trace 64 | -------------------------------------------------------------------------------- /views/submissions.pug: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | include mixins 4 | 5 | block content 6 | .container.content 7 | if !contest.isStarted() && (!user || !user.admin) 8 | include partials/countdown.pug 9 | else 10 | .page-header 11 | h3 Submissions 12 | 13 | .search 14 | form(method='GET' action='#') 15 | .form-group 16 | label(for='search-author') Author: 17 | select#search-author.form-control(name='author') 18 | option(value='') (ALL) 19 | each user in users 20 | - const email = `${user.email.replace(/@.+$/, '')}` 21 | option(value=email selected=(query.author === email)) 22 | = user.displayName 23 | .form-group 24 | label(for='search-language') Language: 25 | select#search-language.form-control(name='language') 26 | option(value='') (ALL) 27 | each lang in langs 28 | - const slug = `${lang.slug}` 29 | option(value=slug selected=(query.language === slug)) 30 | = slug 31 | .form-group 32 | label(for='search-status') Status: 33 | select#search-status.form-control(name='status') 34 | option(value='') (ALL) 35 | option(value='success' selected=(query.status === 'success')) AC 36 | button.btn.btn-primary(type='submit') Search 37 | .table-responsive 38 | table.table 39 | thead 40 | tr 41 | th Author 42 | th Language 43 | th Size 44 | th Status 45 | th Date 46 | tbody 47 | each submission in submissions 48 | - 49 | const tableClass = {pending: 'table-active', success: 'table-success', failed: 'table-warning', error: 'table-danger', invalid: 'table-danger'}[submission.status] 50 | tr(class=tableClass) 51 | td 52 | a(href=`?${encode({author: submission.user.email.replace(/@.+$/, '')})}`) 53 | - const team = submission.user.team.find((t) => t.contest.equals(contest._id)) 54 | if team 55 | span.team-badge(class=`team-${team.value}`) 56 | = submission.user.name() 57 | td 58 | strong 59 | a(href=`?${encode({language: submission.language.slug})}`)= submission.language.slug 60 | td= `${submission.size}bytes` 61 | td 62 | +submissionBadge(submission) 63 | td 64 | a(href=`./submissions/${submission._id}`)= submission.timeText() 65 | 66 | 67 | nav(aria-label='Submission result pages') 68 | ul.pagination.justify-content-center 69 | li.page-item(class=page === 0 ? 'disabled' : '') 70 | a.page-link(href=`?${encode(Object.assign({}, query, {page: page - 1}))}`, aria-label='Previous', rel='prev') 71 | span(aria-hidden='true') « 72 | span.sr-only Previous 73 | each index in [...Array(totalPages).keys()] 74 | - const current = page === index 75 | li.page-item(class=current ? 'active' : '') 76 | a.page-link(href=`?${encode(Object.assign({}, query, {page: index}))}`, aria-current=current ? 'true' : '') 77 | = index + 1 78 | if current 79 | span.sr-only (current) 80 | li.page-item(class=page === totalPages - 1 ? 'disabled' : '') 81 | a.page-link(href=`?${encode(Object.assign({}, query, {page: page + 1}))}`, aria-label='Next', rel='next') 82 | span(aria-hidden='true') » 83 | span.sr-only Next 84 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const webpack = require('webpack'); 3 | 4 | module.exports = (env, argv = {}) => { 5 | const browsers = [ 6 | 'last 2 chrome versions', 7 | ...(argv.mode === 'production' 8 | ? ['last 2 firefox versions', 'safari >= 9', 'last 2 edge versions'] 9 | : []), 10 | ]; 11 | 12 | const envConfig = { 13 | targets: { 14 | browsers, 15 | }, 16 | useBuiltIns: 'entry', 17 | shippedProposals: true, 18 | debug: true, 19 | }; 20 | 21 | return { 22 | entry: Object.assign( 23 | ...[ 24 | ['contest-4', 'js/contests/4/index.babel.js'], 25 | ['contest-mayfes2018', 'js/contests/mayfes2018/index.babel.js'], 26 | ['contest-hackathon2018', 'js/contests/hackathon2018/index.babel.js'], 27 | ['check', 'js/check.babel.js'], 28 | ['contest-5', 'js/contests/5/index.babel.js'], 29 | ['contest-6', 'js/contests/6/index.babel.js'], 30 | [ 31 | 'contest-mayfes2020-day2', 32 | 'js/contests/mayfes2020-day2/index.babel.js', 33 | ], 34 | [ 35 | 'contest-mayfes2021-day2', 36 | 'js/contests/mayfes2021-day2/index.babel.js', 37 | ], 38 | ].map(([name, entry]) => ({ 39 | [name]: [ 40 | ...(argv.mode === 'development' 41 | ? ['webpack-hot-middleware/client?reload=true'] 42 | : []), 43 | path.join(__dirname, 'public', entry), 44 | ], 45 | })), 46 | ), 47 | mode: argv.mode || 'development', 48 | output: { 49 | publicPath: '/js', 50 | filename: '[name].js', 51 | hashFunction: 'xxhash64', 52 | }, 53 | devtool: 54 | argv.mode === 'production' 55 | ? 'source-map' 56 | : 'eval-cheap-module-source-map', 57 | module: { 58 | rules: [ 59 | { 60 | test: /\.jsx?$/, 61 | exclude: /node_modules/, 62 | use: { 63 | loader: 'babel-loader', 64 | options: { 65 | presets: [ 66 | ['@babel/preset-env', envConfig], 67 | '@babel/preset-react', 68 | ], 69 | plugins: [ 70 | '@babel/plugin-proposal-class-properties', 71 | '@babel/plugin-proposal-object-rest-spread', 72 | ], 73 | }, 74 | }, 75 | }, 76 | ], 77 | }, 78 | plugins: [ 79 | new webpack.HotModuleReplacementPlugin(), 80 | new webpack.DefinePlugin({ 81 | 'process.env.NODE_ENV': JSON.stringify(argv.mode), 82 | }), 83 | ], 84 | }; 85 | }; 86 | -------------------------------------------------------------------------------- /worker.js: -------------------------------------------------------------------------------- 1 | // Launch esolang-battle with worker mode. 2 | // This will be combined with polyglot-battle. 3 | 4 | const {Mutex} = require('async-mutex'); 5 | const dotenv = require('dotenv'); 6 | const dotenvExpand = require('dotenv-expand'); 7 | const firebase = require('firebase-admin'); 8 | const {v4: uuid} = require('uuid'); 9 | const docker = require('./engines/docker.js'); 10 | 11 | dotenvExpand(dotenv.config({path: '.env'})); 12 | 13 | const mutex = new Mutex(); 14 | 15 | firebase.initializeApp({ 16 | credential: firebase.credential.applicationDefault(), 17 | databaseURL: 'https://hakatashi.firebaseio.com', 18 | }); 19 | const db = firebase.firestore(); 20 | const submissionsRef = db.collection('polyglot-battle-submissions'); 21 | 22 | const dequeue = async () => { 23 | const submissionDoc = await db.runTransaction(async (transaction) => { 24 | const query = submissionsRef 25 | .where('status', '==', 'pending') 26 | .orderBy('createdAt') 27 | .limit(1); 28 | const snapshot = await transaction.get(query); 29 | 30 | if (snapshot.size <= 0) { 31 | return null; 32 | } 33 | // eslint-disable-next-line prefer-destructuring 34 | const doc = snapshot.docs[0]; 35 | await transaction.update(doc.ref, { 36 | status: 'running', 37 | }); 38 | 39 | return doc; 40 | }); 41 | 42 | if (submissionDoc === null) { 43 | return; 44 | } 45 | 46 | const submission = submissionDoc.data(); 47 | console.log(`Processing ${submissionDoc.id}...`); 48 | console.log( 49 | `date: ${new Date(submission.createdAt._seconds * 1000).toString()}`, 50 | ); 51 | console.log(`lang: ${submission.lang}`); 52 | console.log(`code: ${submission.code.slice(0, 50).replace(/\n/g, ' ')}...`); 53 | 54 | const result = await docker({ 55 | id: submission.lang, 56 | code: Buffer.from(submission.code), 57 | stdin: submission.stdin, 58 | trace: false, 59 | disasm: false, 60 | }); 61 | 62 | await submissionDoc.ref.update({ 63 | stdout: result.stdout.toString(), 64 | stderr: result.stderr.toString(), 65 | duration: result.duration, 66 | error: result.error ? result.error.toString() : null, 67 | status: 'completed', 68 | }); 69 | }; 70 | 71 | (async () => { 72 | const docs = await submissionsRef.listDocuments(); 73 | const batch = db.batch(); 74 | for (const doc of docs) { 75 | batch.update(doc, {status: 'pending'}); 76 | } 77 | await batch.commit(); 78 | submissionsRef.onSnapshot(() => { 79 | mutex.runExclusive(dequeue); 80 | }); 81 | })(); 82 | --------------------------------------------------------------------------------