├── .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 | 
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+siskkkOP>P', // 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 |
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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 |
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 |
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 |
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 |
--------------------------------------------------------------------------------