├── 2021 ├── styles.css ├── index.html └── scripts.js ├── 2022 ├── src │ ├── constants.js │ ├── utils.js │ ├── scripts.js │ └── users.js ├── styles.css └── index.html ├── 2023 ├── src │ ├── constants.js │ ├── utils.js │ ├── users.js │ └── scripts.js ├── styles.css └── index.html ├── 2024 ├── src │ ├── constants.js │ ├── utils.js │ ├── scripts.js │ └── users.js ├── styles.css └── index.html ├── CNAME ├── .gitignore ├── src ├── constants.js ├── utils.js ├── scripts.js └── users.js ├── .editorconfig ├── LICENSE ├── styles.css ├── README.md └── index.html /CNAME: -------------------------------------------------------------------------------- 1 | aoc.inf-cau.de -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | .DS_Store 3 | *.swp 4 | -------------------------------------------------------------------------------- /src/constants.js: -------------------------------------------------------------------------------- 1 | export const year = 2025; 2 | export const shortYear = year % 100; 3 | -------------------------------------------------------------------------------- /2022/src/constants.js: -------------------------------------------------------------------------------- 1 | export const year = 2022; 2 | export const shortYear = year % 100; 3 | -------------------------------------------------------------------------------- /2023/src/constants.js: -------------------------------------------------------------------------------- 1 | export const year = 2023; 2 | export const shortYear = year % 100; 3 | -------------------------------------------------------------------------------- /2024/src/constants.js: -------------------------------------------------------------------------------- 1 | export const year = 2024; 2 | export const shortYear = year % 100; 3 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*.{html,js,css}] 4 | indent_size = 2 5 | indent_style = space 6 | 7 | -------------------------------------------------------------------------------- /2021/styles.css: -------------------------------------------------------------------------------- 1 | .nav, #users { 2 | position: sticky; 3 | } 4 | 5 | #users { 6 | top: 1em; 7 | } 8 | 9 | .nav { 10 | top: 0px; 11 | background-color: white; 12 | padding: 1em 0; 13 | margin-top: -1em; 14 | z-index: 1; 15 | } 16 | 17 | .list-group-item, .nav-link { 18 | cursor: pointer; 19 | } 20 | 21 | -------------------------------------------------------------------------------- /2022/src/utils.js: -------------------------------------------------------------------------------- 1 | /** Generates solution urls for GitHub. */ 2 | export function gitHubUrls({ user, repo, branch = "main", path }) { 3 | return { 4 | solutionUrl: (day, part) => `https://raw.githubusercontent.com/${user}/${repo}/${branch}/${path(day, part)}`, 5 | solutionWebUrl: (day, part) => `https://github.com/${user}/${repo}/blob/${branch}/${path(day, part)}`, 6 | }; 7 | } 8 | 9 | /** Pads the given number with the given number of zeros. */ 10 | export function pad(n, zeros) { 11 | return `${n}`.padStart(zeros, "0"); 12 | } 13 | -------------------------------------------------------------------------------- /src/utils.js: -------------------------------------------------------------------------------- 1 | /** Generates solution urls for GitHub. */ 2 | export function gitHubUrls({ user, repo, branch = "main", path }) { 3 | return { 4 | solutionUrl: (day, part) => { 5 | const p = path(day, part); 6 | return p ? `https://raw.githubusercontent.com/${user}/${repo}/${branch}/${p}` : null; 7 | }, 8 | solutionWebUrl: (day, part) => { 9 | const p = path(day, part); 10 | return p ? `https://github.com/${user}/${repo}/blob/${branch}/${p}` : null; 11 | }, 12 | }; 13 | } 14 | 15 | /** Pads the given number with the given number of zeros. */ 16 | export function pad(n, zeros) { 17 | return `${n}`.padStart(zeros, "0"); 18 | } 19 | -------------------------------------------------------------------------------- /2023/src/utils.js: -------------------------------------------------------------------------------- 1 | /** Generates solution urls for GitHub. */ 2 | export function gitHubUrls({ user, repo, branch = "main", path }) { 3 | return { 4 | solutionUrl: (day, part) => { 5 | const p = path(day, part); 6 | return p ? `https://raw.githubusercontent.com/${user}/${repo}/${branch}/${p}` : null; 7 | }, 8 | solutionWebUrl: (day, part) => { 9 | const p = path(day, part); 10 | return p ? `https://github.com/${user}/${repo}/blob/${branch}/${p}` : null; 11 | }, 12 | }; 13 | } 14 | 15 | /** Pads the given number with the given number of zeros. */ 16 | export function pad(n, zeros) { 17 | return `${n}`.padStart(zeros, "0"); 18 | } 19 | -------------------------------------------------------------------------------- /2024/src/utils.js: -------------------------------------------------------------------------------- 1 | /** Generates solution urls for GitHub. */ 2 | export function gitHubUrls({ user, repo, branch = "main", path }) { 3 | return { 4 | solutionUrl: (day, part) => { 5 | const p = path(day, part); 6 | return p ? `https://raw.githubusercontent.com/${user}/${repo}/${branch}/${p}` : null; 7 | }, 8 | solutionWebUrl: (day, part) => { 9 | const p = path(day, part); 10 | return p ? `https://github.com/${user}/${repo}/blob/${branch}/${p}` : null; 11 | }, 12 | }; 13 | } 14 | 15 | /** Pads the given number with the given number of zeros. */ 16 | export function pad(n, zeros) { 17 | return `${n}`.padStart(zeros, "0"); 18 | } 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2022 The aoc-submissions contributors 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 all 13 | 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 THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /2021/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Advent of Code 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 |
16 |
17 |
    18 |
    19 |
    20 | 28 |
    29 |
    30 |
    31 |
    32 | 33 | 34 | -------------------------------------------------------------------------------- /2022/styles.css: -------------------------------------------------------------------------------- 1 | .nav { 2 | position: sticky; 3 | } 4 | 5 | #left { 6 | position: sticky; 7 | top: 1em; 8 | max-height: calc(100vh - 2em); 9 | height: 100%; 10 | display: grid; 11 | grid-template-rows: minmax(0, 1fr) 150px; 12 | } 13 | 14 | #users { 15 | top: 1em; 16 | overflow-y: scroll; 17 | } 18 | 19 | .nav { 20 | top: 0px; 21 | background-color: white; 22 | padding: 1em 0; 23 | margin-top: -1em; 24 | z-index: 1; 25 | } 26 | 27 | .list-group-item, .nav-link { 28 | cursor: pointer; 29 | } 30 | 31 | #users .list-group-item { 32 | display: flex; 33 | justify-content: space-between; 34 | } 35 | 36 | #keyboard { 37 | margin: 1em 0 0 1em; 38 | } 39 | 40 | #keyboard h5 { 41 | font-size: 1.1em; 42 | } 43 | 44 | #keyboard .key { 45 | background: #f0f0f0; 46 | padding: .125em .2em; 47 | font-family: monospace; 48 | border-radius: .125em; 49 | border: 1px solid #b0b0b0; 50 | margin: .25em; 51 | line-height: 1; 52 | display: inline-block; 53 | } 54 | 55 | 56 | /* X-Small devices (e.g. portrait phones) */ 57 | @media (max-width: 576px) { 58 | #keyboard { 59 | display: none; 60 | } 61 | 62 | #left { 63 | grid-template-rows: unset; 64 | } 65 | 66 | #users { 67 | flex-direction: row; 68 | overflow: scroll; 69 | } 70 | 71 | .nav { 72 | margin-top: 0; 73 | flex-wrap: nowrap; 74 | overflow: scroll; 75 | } 76 | 77 | .user-name { 78 | white-space: nowrap; 79 | } 80 | 81 | .user-lang-name { 82 | display: none; 83 | } 84 | } 85 | 86 | @media (prefers-color-scheme: dark) { 87 | body, .nav, .list-group-item { 88 | background-color: #2d2d2d; 89 | color: white; 90 | border-color: rgba(255, 255, 255, .125); 91 | } 92 | 93 | .key { 94 | color: black; 95 | } 96 | 97 | .token { 98 | background: none !important; 99 | } 100 | } 101 | 102 | -------------------------------------------------------------------------------- /styles.css: -------------------------------------------------------------------------------- 1 | .nav { 2 | position: sticky; 3 | } 4 | 5 | #left { 6 | position: sticky; 7 | top: 1em; 8 | max-height: calc(100vh - 2em); 9 | height: 100%; 10 | display: grid; 11 | grid-template-rows: minmax(0, 1fr) auto; 12 | } 13 | 14 | #users { 15 | top: 1em; 16 | overflow-y: auto; 17 | } 18 | 19 | .nav { 20 | top: 0px; 21 | background-color: white; 22 | padding: 1em 0; 23 | margin-top: -1em; 24 | z-index: 1; 25 | } 26 | 27 | .list-group-item, .nav-link { 28 | cursor: pointer; 29 | } 30 | 31 | #users .list-group-item { 32 | display: flex; 33 | justify-content: space-between; 34 | } 35 | 36 | .user-lang-annotation { 37 | opacity: 0.4; 38 | } 39 | 40 | #keyboard { 41 | margin: 1em 0 0 1em; 42 | } 43 | 44 | #keyboard h5 { 45 | font-size: 1.1em; 46 | } 47 | 48 | #keyboard .key { 49 | background: #f0f0f0; 50 | padding: .125em .2em; 51 | font-family: monospace; 52 | border-radius: .125em; 53 | border: 1px solid #b0b0b0; 54 | margin: .25em; 55 | line-height: 1; 56 | display: inline-block; 57 | } 58 | 59 | 60 | /* X-Small devices (e.g. portrait phones) */ 61 | @media (max-width: 576px) { 62 | #keyboard { 63 | display: none; 64 | } 65 | 66 | #left { 67 | grid-template-rows: unset; 68 | } 69 | 70 | #users { 71 | flex-direction: row; 72 | overflow: scroll; 73 | } 74 | 75 | .nav { 76 | margin-top: 0; 77 | flex-wrap: nowrap; 78 | overflow: scroll; 79 | } 80 | 81 | .user-name { 82 | white-space: nowrap; 83 | } 84 | 85 | .user-lang { 86 | display: none; 87 | } 88 | } 89 | 90 | /* Medium Devices */ 91 | @media (max-width: 768px) { 92 | .user-lang-annotation { 93 | display: none; 94 | } 95 | } 96 | 97 | @media (prefers-color-scheme: dark) { 98 | body, .nav, .list-group-item { 99 | background-color: #2d2d2d; 100 | color: white; 101 | border-color: rgba(255, 255, 255, .125); 102 | } 103 | 104 | .key { 105 | color: black; 106 | } 107 | 108 | .token { 109 | background: none !important; 110 | } 111 | 112 | a { 113 | color: white !important; 114 | } 115 | } 116 | 117 | -------------------------------------------------------------------------------- /2023/styles.css: -------------------------------------------------------------------------------- 1 | .nav { 2 | position: sticky; 3 | } 4 | 5 | #left { 6 | position: sticky; 7 | top: 1em; 8 | max-height: calc(100vh - 2em); 9 | height: 100%; 10 | display: grid; 11 | grid-template-rows: minmax(0, 1fr) 190px; 12 | } 13 | 14 | #users { 15 | top: 1em; 16 | overflow-y: auto; 17 | } 18 | 19 | .nav { 20 | top: 0px; 21 | background-color: white; 22 | padding: 1em 0; 23 | margin-top: -1em; 24 | z-index: 1; 25 | } 26 | 27 | .list-group-item, .nav-link { 28 | cursor: pointer; 29 | } 30 | 31 | #users .list-group-item { 32 | display: flex; 33 | justify-content: space-between; 34 | } 35 | 36 | .user-lang-annotation { 37 | opacity: 0.4; 38 | } 39 | 40 | #keyboard { 41 | margin: 1em 0 0 1em; 42 | } 43 | 44 | #keyboard h5 { 45 | font-size: 1.1em; 46 | } 47 | 48 | #keyboard .key { 49 | background: #f0f0f0; 50 | padding: .125em .2em; 51 | font-family: monospace; 52 | border-radius: .125em; 53 | border: 1px solid #b0b0b0; 54 | margin: .25em; 55 | line-height: 1; 56 | display: inline-block; 57 | } 58 | 59 | 60 | /* X-Small devices (e.g. portrait phones) */ 61 | @media (max-width: 576px) { 62 | #keyboard { 63 | display: none; 64 | } 65 | 66 | #left { 67 | grid-template-rows: unset; 68 | } 69 | 70 | #users { 71 | flex-direction: row; 72 | overflow: scroll; 73 | } 74 | 75 | .nav { 76 | margin-top: 0; 77 | flex-wrap: nowrap; 78 | overflow: scroll; 79 | } 80 | 81 | .user-name { 82 | white-space: nowrap; 83 | } 84 | 85 | .user-lang { 86 | display: none; 87 | } 88 | } 89 | 90 | /* Medium Devices */ 91 | @media (max-width: 768px) { 92 | .user-lang-annotation { 93 | display: none; 94 | } 95 | } 96 | 97 | @media (prefers-color-scheme: dark) { 98 | body, .nav, .list-group-item { 99 | background-color: #2d2d2d; 100 | color: white; 101 | border-color: rgba(255, 255, 255, .125); 102 | } 103 | 104 | .key { 105 | color: black; 106 | } 107 | 108 | .token { 109 | background: none !important; 110 | } 111 | 112 | a { 113 | color: white !important; 114 | } 115 | } 116 | 117 | -------------------------------------------------------------------------------- /2024/styles.css: -------------------------------------------------------------------------------- 1 | .nav { 2 | position: sticky; 3 | } 4 | 5 | #left { 6 | position: sticky; 7 | top: 1em; 8 | max-height: calc(100vh - 2em); 9 | height: 100%; 10 | display: grid; 11 | grid-template-rows: minmax(0, 1fr) auto; 12 | } 13 | 14 | #users { 15 | top: 1em; 16 | overflow-y: auto; 17 | } 18 | 19 | .nav { 20 | top: 0px; 21 | background-color: white; 22 | padding: 1em 0; 23 | margin-top: -1em; 24 | z-index: 1; 25 | } 26 | 27 | .list-group-item, .nav-link { 28 | cursor: pointer; 29 | } 30 | 31 | #users .list-group-item { 32 | display: flex; 33 | justify-content: space-between; 34 | } 35 | 36 | .user-lang-annotation { 37 | opacity: 0.4; 38 | } 39 | 40 | #keyboard { 41 | margin: 1em 0 0 1em; 42 | } 43 | 44 | #keyboard h5 { 45 | font-size: 1.1em; 46 | } 47 | 48 | #keyboard .key { 49 | background: #f0f0f0; 50 | padding: .125em .2em; 51 | font-family: monospace; 52 | border-radius: .125em; 53 | border: 1px solid #b0b0b0; 54 | margin: .25em; 55 | line-height: 1; 56 | display: inline-block; 57 | } 58 | 59 | 60 | /* X-Small devices (e.g. portrait phones) */ 61 | @media (max-width: 576px) { 62 | #keyboard { 63 | display: none; 64 | } 65 | 66 | #left { 67 | grid-template-rows: unset; 68 | } 69 | 70 | #users { 71 | flex-direction: row; 72 | overflow: scroll; 73 | } 74 | 75 | .nav { 76 | margin-top: 0; 77 | flex-wrap: nowrap; 78 | overflow: scroll; 79 | } 80 | 81 | .user-name { 82 | white-space: nowrap; 83 | } 84 | 85 | .user-lang { 86 | display: none; 87 | } 88 | } 89 | 90 | /* Medium Devices */ 91 | @media (max-width: 768px) { 92 | .user-lang-annotation { 93 | display: none; 94 | } 95 | } 96 | 97 | @media (prefers-color-scheme: dark) { 98 | body, .nav, .list-group-item { 99 | background-color: #2d2d2d; 100 | color: white; 101 | border-color: rgba(255, 255, 255, .125); 102 | } 103 | 104 | .key { 105 | color: black; 106 | } 107 | 108 | .token { 109 | background: none !important; 110 | } 111 | 112 | a { 113 | color: white !important; 114 | } 115 | } 116 | 117 | -------------------------------------------------------------------------------- /2022/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Advent of Code 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
    16 |
    17 |
    18 |
      19 |
      20 |
      Keyboard shortcuts
      21 |
        22 |
      • w/s move between users
      • 23 |
      • a/d move between tasks
      • 24 |
      • q switch between task parts
      • 25 |
      26 |
      27 |
      28 |
      29 | 37 |
      38 |
      39 |
      40 |
      41 | 42 | 43 | -------------------------------------------------------------------------------- /2023/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Submission Board – Advent of Code 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
      16 |
      17 |
      18 |
        19 |
        20 |
        Keyboard shortcuts
        21 |
          22 |
        • w/s move between users
        • 23 |
        • a/d move between tasks
        • 24 |
        • q switch between task parts
        • 25 |
        26 |
        (You may also use hjkl.)
        27 |
        2022 2021
        28 |
        29 |
        30 |
        31 | 39 |
        40 |
        41 |
        42 |
        43 | 44 | 45 | -------------------------------------------------------------------------------- /2024/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Submission Board – Advent of Code 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
        16 |
        17 |
        18 |
          19 |
          20 |
          Keyboard shortcuts
          21 |
            22 |
          • w/s move between users
          • 23 |
          • a/d move between tasks
          • 24 |
          • q switch between task parts
          • 25 |
          26 |
          (You may also use hjkl.)
          27 |
          28 | 29 | 2023 30 | 2022 31 | 2021 32 | 33 |
          34 |
          35 |
          36 |
          37 | 45 |
          46 |
          47 |
          48 |
          49 | 50 | 51 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aoc-submissions 2 | 3 | A small website showcasing different solutions for the Advent of Code. 4 | 5 | [The live page can be found here.](https://melfkammholz.github.io/aoc-submissions/) 6 | 7 | ## Make Your Code Available 8 | 9 | Depending on how versed you are in managing your code with git, you might want 10 | to opt for different approaches. 11 | - If you have never worked with git before, then learning git might be a 12 | daunting task. If you want to take it easy, choose GitHub to host your code, 13 | [you can use the web UI upload and edit files](https://docs.github.com/en/repositories). 14 | GitLab offers [similar ways of managing code](https://docs.gitlab.com/ee/user/project/repository/) 15 | as well. 16 | - If you are up for the challenge, you may use [this resource](https://git-scm.com/) 17 | as a starting point. There are also many video tutorials and cheatsheets 18 | available online. 19 | 20 | 21 | ## Showcase Your Solutions 22 | 23 | If you want to showcase your solutions for Advent of Code, please issue a pull 24 | request. This pull request should contain your personal entry in 25 | `src/users.js`. An entry looks like this in its simplest form: 26 | 27 | 28 | ```js 29 | { 30 | // the name displayed in the submission board 31 | name: "Ron Swanson", 32 | 33 | // indicator for PrismJS which syntax highlighting to use 34 | // see https://prismjs.com/#supported-languages 35 | lang: _ => "clike", 36 | 37 | // the name of your programming language display in the submission board 38 | langName: _ => "C", 39 | 40 | // the URL used for fetching the raw source file of a given day and part 41 | solutionUrl: (day, part) => 42 | `https://raw.githubusercontent.com/ronswanson/aoc2024/master/day${day}/${part}.c`, 43 | 44 | // the URL used for referring to your solution via "View Source" 45 | solutionWebUrl: (day, part) => 46 | `https://github.com/ronswanson/aoc2024/blob/master/day${day}/${part}.c` 47 | } 48 | ``` 49 | 50 | This website uses PrismJS for syntax highlighting. Please make sure that you 51 | [choose the correct abbreviation](https://prismjs.com/#supported-languages) 52 | for your programming language. There are some abstractions for providing all 53 | necessary URLs available. You may want to use the entries of others as an 54 | example. If you intend on using multiple programming languages, you have to 55 | maintain a JSON file that helps us in choosing the correct labels and syntax 56 | highlighting. Here is [an example of such a file](https://github.com/fwcd/advent-of-code-2023/blob/main/paths.json). 57 | These files are fetched in `src/users.js`. 58 | 59 | ## Local Development 60 | 61 | Since the page uses ES6 modules, you'll need a web server to test the page 62 | locally. If you have Python installed, you can e.g. run 63 | 64 | ```sh 65 | python3 -m http.server 66 | ``` 67 | -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | Submission Board – Advent of Code 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
          16 |
          17 |
          18 |
            19 |
            20 |
            Keyboard shortcuts
            21 |
              22 |
            • w/s move between users
            • 23 |
            • a/d move between tasks
            • 24 |
            • q switch between task parts
            • 25 |
            • 26 | Pressing Ctrl|Cmd while clicking on a day 27 | opens the puzzle on Advent of Code. 28 |
            • 29 |
            30 |
            (You may also use hjkl.)
            31 |
            32 | 33 | 2024 34 | 2023 35 | 2022 36 | 2021 37 | 38 |
            39 |
            40 |
            41 |
            42 | 50 |
            51 |
            52 |
            53 |
            54 | 55 | 56 | -------------------------------------------------------------------------------- /2023/src/users.js: -------------------------------------------------------------------------------- 1 | import { year, shortYear } from './constants.js'; 2 | import { gitHubUrls, pad } from './utils.js'; 3 | 4 | /** Loads the list of users. */ 5 | export async function loadUsers() { 6 | // TODO handle error if necessary? 7 | const fwcdPaths = await (await fetch(`https://raw.githubusercontent.com/fwcd/advent-of-code-${year}/main/paths.json`)).json().catch(() => ({})); 8 | const kazumiPaths = await (await fetch(`https://raw.githubusercontent.com/Dormanil/Advent-of-Code/${year}/exceptionInfo.json`)).json().catch(() => ({})); 9 | 10 | const fwcdSolution = (day, part) => (fwcdPaths[day]?.parts ?? [])[part] ?? fwcdPaths[day]; 11 | 12 | const users = [ 13 | { 14 | name: "Alexander P", 15 | lang: _ => "clike", 16 | langName: _ => "C++", 17 | ...gitHubUrls({ 18 | user: "Zeldacrafter", 19 | repo: "CompProg-Solutions", 20 | branch: "master", 21 | path: (day, part) => `AdventOfCode/${year}/${day + 1}/${part + 1}.cc` 22 | }) 23 | }, 24 | { 25 | name: "I3J03RN", 26 | lang: _ => "clike", 27 | langName: _ => "C++", 28 | ...gitHubUrls({ 29 | user: "I3J03RN", 30 | repo: "ProgrammingChallenges", 31 | branch: "master", 32 | path: day => `AoC/${year}/${day + 1}.cc` 33 | }) 34 | }, 35 | { 36 | name: "Melf", 37 | lang: _ => "lua", 38 | langName: _ => "Lua", 39 | ...gitHubUrls({ 40 | user: "melfkammholz", 41 | repo: `aoc${shortYear}`, 42 | path: (day, part) => `day${pad(day + 1, 2)}/${["A", "B"][part]}.lua` 43 | }) 44 | }, 45 | { 46 | name: "fwcd", 47 | lang: (day, part) => fwcdSolution(day, part)?.lang?.codemirror, 48 | langAnnotation: "Today: ", 49 | langName: (day, part) => fwcdSolution(day, part)?.lang?.name ?? "Unknown", 50 | encoding: (day, part) => fwcdSolution(day, part)?.encoding, 51 | ...gitHubUrls({ 52 | user: "fwcd", 53 | repo: `advent-of-code-${year}`, 54 | path: (day, part) => fwcdSolution(day, part)?.path 55 | }) 56 | }, 57 | { 58 | name: "Yorik Hansen", 59 | lang: _ => "python", 60 | langName: _ => "Python", 61 | ...gitHubUrls({ 62 | user: "YorikHansen", 63 | repo: "AdventOfCode", 64 | path: (day, part) => `${year}/day${pad(day + 1, 2)}/part${part + 1}.py` 65 | }) 66 | }, 67 | { 68 | name: "Skgland", 69 | lang: _ => "rust", 70 | langName: _ => "Rust", 71 | ...gitHubUrls({ 72 | user: "Skgland", 73 | repo: "Advent-of-Code", 74 | path: day => `crates/year${year}/src/day${pad(day + 1, 2)}.rs` 75 | }) 76 | }, 77 | { 78 | name: "b3z", 79 | lang: _ => "python", 80 | langName: _ => "Python", 81 | ...gitHubUrls({ 82 | user: "b3z", 83 | repo: "aoc", 84 | branch: "master", 85 | path: (day, part) => `${year}/${pad(day + 1, 2)}/${part + 1}.py` 86 | }) 87 | }, 88 | { 89 | name: "H1tchhiker", 90 | lang: _ => "elixir", 91 | langName: _ => "Elixir", 92 | ...gitHubUrls({ 93 | user: "n00on", 94 | repo: "AdventOfCode", 95 | path: day => `${year}/${pad(day + 1, 2)}/day${pad(day + 1, 2)}.ex` 96 | }) 97 | }, 98 | { 99 | name: "H1ghBre4k3r", 100 | lang: _ => "rust", 101 | langName: _ => "Rust", 102 | ...gitHubUrls({ 103 | user: "H1ghBre4k3r", 104 | repo: `aoc-${year}`, 105 | path: day => `src/day_${pad(day + 1, 2)}.rs` 106 | }) 107 | }, 108 | { 109 | name: "Zihark", 110 | lang: _ => "haskell", 111 | langName: _ => "Haskell", 112 | ...gitHubUrls({ 113 | user: "Ziharrk", 114 | repo: `aoc${year}`, 115 | path: day => `src/Day${day + 1}.hs` 116 | }) 117 | }, 118 | { 119 | name: "maclement", 120 | lang: _ => "haskell", 121 | langName: _ => "Haskell", 122 | ...gitHubUrls({ 123 | user: "maclement", 124 | repo: `advent-of-code-${year}`, 125 | path: day => `Haskell/Day${day + 1}/A.hs` 126 | }) 127 | }, 128 | { 129 | name: "Magi3r", 130 | lang: _ => "esolang", 131 | langName: _ => "DDP", 132 | ...gitHubUrls({ 133 | user: "Magi3r", 134 | repo: `AoC-${year}`, 135 | path: (day, part) => `${pad(day + 1, 2)}${["a", "b"][part]}.ddp` 136 | }) 137 | }, 138 | { 139 | name: "sebfisch", 140 | lang: _ => "zig", 141 | langName: _ => "Zig", 142 | ...gitHubUrls({ 143 | user: "sebfisch", 144 | repo: "AdventOfCode", 145 | branch: "latest", 146 | path: (day, part) => `year${year}/day${pad(day + 1, 2)}/task${part + 1}.zig` 147 | }) 148 | }, 149 | { 150 | name: "Kazumi", 151 | lang: day => kazumiPaths[`${day + 1}`]?.lang ?? "csharp", 152 | langAnnotation: "Today: ", 153 | langName: day => kazumiPaths[`${day + 1}`]?.langName ?? "C#", 154 | ...gitHubUrls({ 155 | user: "Dormanil", 156 | repo: "Advent-of-Code", 157 | branch: `${year}`, 158 | path: day => `Dec${day + 1}/Program.${kazumiPaths[`${day + 1}`]?.extension ?? "cs"}` 159 | }) 160 | }, 161 | ]; 162 | 163 | if (new Set(users.map(u => u.name)).size != users.length) { 164 | throw new Error("Users must have unique names!") 165 | } 166 | 167 | return users; 168 | } 169 | -------------------------------------------------------------------------------- /2022/src/scripts.js: -------------------------------------------------------------------------------- 1 | import { year } from './constants.js'; 2 | import { loadUsers } from './users.js'; 3 | 4 | window.addEventListener("load", async () => { 5 | const users = await loadUsers(); 6 | const clamp = (min, max, val) => Math.min(max, Math.max(val, min)); 7 | 8 | const days = new Date(clamp(new Date(`${year}-12-01`).valueOf(), new Date(`${year}-12-25`).valueOf(), Date.now() - 6 * 60 * 60 * 1000)).getDate(); 9 | const state = { 10 | day: days - 1, 11 | part: 0, 12 | index: 0 13 | }; 14 | 15 | async function loadSolution(user, day, part) { 16 | const url = user.solutionUrl(day, part); 17 | const preEl = document.createElement("pre"); 18 | const codeEl = document.createElement("code"); 19 | const lang = user.lang(day); 20 | const encoding = ("encoding" in user ? user.encoding(day) : null) || "utf-8"; 21 | const decoder = new TextDecoder(encoding); 22 | try { 23 | const result = url ? await fetch(url) : null; 24 | if (!(result?.ok ?? false)) { 25 | throw new Error("No solution yet"); 26 | } 27 | const buffer = await result.arrayBuffer(); 28 | const code = decoder.decode(buffer); 29 | if (lang) { 30 | await Prism.plugins.autoloader.loadLanguages( 31 | lang, 32 | () => { 33 | codeEl.innerHTML = Prism.highlight(code, Prism.languages[lang], user.lang); 34 | } 35 | ); 36 | } else { 37 | codeEl.innerText = code; 38 | } 39 | } catch (err) { 40 | codeEl.textContent = err.message; 41 | } 42 | preEl.appendChild(codeEl); 43 | document.getElementById("preview").innerHTML = ""; 44 | document.getElementById("preview").appendChild(preEl); 45 | } 46 | 47 | function render(strings, ...values) { 48 | const html = String.raw({ raw: strings }, ...values); 49 | const el = document.createElement('div'); 50 | el.innerHTML = html; 51 | return el.children[0]; 52 | } 53 | 54 | function updateSolution() { 55 | loadSolution(users[state.index], state.day, state.part); 56 | } 57 | 58 | function setActive(selector, index) { 59 | const className = "active"; 60 | const activatedEl = document.querySelectorAll(selector)[index]; 61 | document.querySelectorAll(`${selector}.${className}`).forEach(el => el.classList.remove(className)); 62 | activatedEl.classList.add(className); 63 | } 64 | 65 | function updateQueryParams() { 66 | const params = new URLSearchParams(window.location.search); 67 | for (const key in state) { 68 | params.set(key, `${state[key]}`); 69 | } 70 | history.pushState(null, "", `${window.location.pathname}?${params}`); 71 | } 72 | 73 | function updateState({ index = null, day = null, part = null, updateActive = false, updateQuery = true }) { 74 | state.index = index ?? state.index; 75 | state.day = day ?? state.day; 76 | state.part = part ?? state.part; 77 | if (index !== null || updateActive) { 78 | setActive("#users .list-group-item", state.index); 79 | } 80 | if (day !== null || updateActive) { 81 | setActive(".nav .day", state.day); 82 | } 83 | if (part !== null || updateActive) { 84 | setActive(".part", state.part); 85 | } 86 | if (updateQuery) { 87 | updateQueryParams(); 88 | } 89 | updateSolution(); 90 | } 91 | 92 | function loadStateFromQueryParams() { 93 | const params = new URLSearchParams(window.location.search); 94 | const loaded = {}; 95 | for (const key in state) { 96 | const value = params.get(key); 97 | if (value !== null) { 98 | loaded[key] = parseInt(value); 99 | } 100 | } 101 | updateState({ ...loaded, updateActive: true, updateQuery: false }); 102 | } 103 | 104 | const mod = (x, m) => (x % m + m) % m; 105 | 106 | window.addEventListener("keydown", (event) => { 107 | if (event.key === "w" || event.key === "k" ) updateState({ index: mod(state.index - 1, users.length) }); 108 | else if (event.key === "s" || event.key === "j") updateState({ index: mod(state.index + 1, users.length) }); 109 | else if (event.key === "a" || event.key === "h") updateState({ day: mod(state.day - 1, days) }); 110 | else if (event.key === "d" || event.key === "l") updateState({ day: mod(state.day + 1, days) }); 111 | else if (event.key === "q") updateState({ part: (state.part + 1) % 2 }); 112 | }); 113 | 114 | window.addEventListener("popstate", () => { 115 | loadStateFromQueryParams(); 116 | }); 117 | 118 | users.forEach((user, index) => { 119 | const el = render` 120 |
          • 121 | ${user.name} 122 | ${user.langName} 123 |
          • 124 | `; 125 | el.addEventListener("click", () => { 126 | updateState({ index }); 127 | }); 128 | document.getElementById("users").appendChild(el); 129 | }); 130 | 131 | Array.from(document.querySelectorAll(".part")).forEach((el, i) => { 132 | el.addEventListener("click", () => { 133 | updateState({ part: i }); 134 | }); 135 | }); 136 | 137 | [...Array(days)].forEach((_, i) => { 138 | const el = document.createElement("li"); 139 | el.classList.add("nav-item"); 140 | const aEl = document.createElement("a"); 141 | aEl.classList.add("nav-link"); 142 | aEl.classList.add("day"); 143 | aEl.textContent = i + 1; 144 | aEl.addEventListener("click", () => { 145 | updateState({ day: i }); 146 | }); 147 | el.appendChild(aEl); 148 | document.querySelector(".nav").appendChild(el); 149 | }); 150 | 151 | loadStateFromQueryParams(); 152 | }); 153 | 154 | if (window.matchMedia("(prefers-color-scheme: dark)").matches) { 155 | const link = document.createElement("link"); 156 | link.rel = "stylesheet"; 157 | link.href = "https://cdnjs.cloudflare.com/ajax/libs/prism/1.25.0/themes/prism-tomorrow.min.css"; 158 | document.querySelector("head").appendChild(link); 159 | } 160 | 161 | -------------------------------------------------------------------------------- /2022/src/users.js: -------------------------------------------------------------------------------- 1 | import { year, shortYear } from './constants.js'; 2 | import { gitHubUrls, pad } from './utils.js'; 3 | 4 | /** Loads the list of users. */ 5 | export async function loadUsers() { 6 | const fwcdPaths = await (await fetch(`https://raw.githubusercontent.com/fwcd/advent-of-code-${year}/main/paths.json`)).json(); 7 | const estgPaths = await (await fetch(`https://raw.githubusercontent.com/estugon/advent-of-code-${year}/main/paths.json`)).json(); 8 | 9 | const users = [ 10 | { 11 | name: "Alexander P", 12 | lang: _ => "clike", 13 | langName: "C++", 14 | ...gitHubUrls({ 15 | user: "Zeldacrafter", 16 | repo: "CompProg-Solutions", 17 | branch: "master", 18 | path: (day, part) => `AdventOfCode/${year}/${day + 1}/${part + 1}.cc` 19 | }) 20 | }, 21 | { 22 | name: "I3J03RN", 23 | lang: _ => "clike", 24 | langName: "C++", 25 | ...gitHubUrls({ 26 | user: "I3J03RN", 27 | repo: "ProgrammingChallenges", 28 | branch: "master", 29 | path: day => `AoC/${year}/${day + 1}.cc` 30 | }) 31 | }, 32 | { 33 | name: "Melf", 34 | lang: _ => "haskell", 35 | langName: "Haskell", 36 | ...gitHubUrls({ 37 | user: "melfkammholz", 38 | repo: `aoc${shortYear}`, 39 | path: (day, part) => `day${pad(day + 1, 2)}/${["A", "B"][part]}.hs` 40 | }) 41 | }, 42 | { 43 | name: "fwcd", 44 | lang: day => fwcdPaths[day]?.lang?.codemirror, 45 | langName: "Mixed", 46 | encoding: day => fwcdPaths[day]?.encoding, 47 | ...gitHubUrls({ 48 | user: "fwcd", 49 | repo: `advent-of-code-${year}`, 50 | path: day => fwcdPaths[day]?.path 51 | }) 52 | }, 53 | { 54 | name: "xtay2", 55 | lang: _ => "java", 56 | langName: "Java", 57 | ...gitHubUrls({ 58 | user: "xtay2", 59 | repo: "AdventOfCode", 60 | path: (day, part) => `src/year${year}/day${pad(day + 1, 2)}/Task_${["A", "B"][part]}.java` 61 | }) 62 | }, 63 | { 64 | name: "tuhhy", 65 | lang: _ => "python", 66 | langName: "Python", 67 | ...gitHubUrls({ 68 | user: "tuhhy", 69 | repo: "aoc", 70 | branch: "master", 71 | path: (day, part) => `Day${day + 1}/Task${["A", "B"][part]}.py` 72 | }) 73 | }, 74 | { 75 | name: "Yorik Hansen", 76 | lang: _ => "python", 77 | langName: "Python", 78 | ...gitHubUrls({ 79 | user: "YorikHansen", 80 | repo: "AdventOfCode", 81 | path: (day, part) => `${year}/day${pad(day + 1, 2)}/part${part + 1}.py` 82 | }) 83 | }, 84 | { 85 | name: "Skgland", 86 | lang: _ => "rust", 87 | langName: "Rust", 88 | ...gitHubUrls({ 89 | user: "Skgland", 90 | repo: "Advent-of-Code", 91 | path: day => `crates/year${year}/src/day${pad(day + 1, 2)}.rs` 92 | }) 93 | }, 94 | { 95 | name: "Estugon", 96 | lang: day => estgPaths[day]?.lang, 97 | langName: "Mixed", 98 | ...gitHubUrls({ 99 | user: "Estugon", 100 | repo: `advent-of-code-${year}`, 101 | path: day => estgPaths[day]?.path 102 | }) 103 | }, 104 | { 105 | name: "Dormanil", 106 | lang: _ => "fsharp", 107 | langName: "F#", 108 | ...gitHubUrls({ 109 | user: "Dormanil", 110 | repo: "Advent-of-Code", 111 | branch: `${year}`, 112 | path: day => `Dec${day + 1}/Program.fs` 113 | }) 114 | }, 115 | { 116 | name: "b3z", 117 | lang: _ => "python", 118 | langName: "Python", 119 | ...gitHubUrls({ 120 | user: "b3z", 121 | repo: "aoc", 122 | branch: "master", 123 | path: (day, part) => `${year}/${pad(day + 1, 2)}/${part + 1}.py` 124 | }) 125 | }, 126 | { 127 | name: "Dobiko", 128 | lang: _ => "clike", 129 | langName: "C#", 130 | ...gitHubUrls({ 131 | user: "jnccd", 132 | repo: "AdventOfCode", 133 | path: day => `Dec${day + 1}/Program.cs` 134 | }) 135 | }, 136 | { 137 | name: "H1tchhiker", 138 | lang: _ => "python", 139 | langName: "Python", 140 | ...gitHubUrls({ 141 | user: "n00on", 142 | repo: "AdventOfCode", 143 | path: day => `${year}/${pad(day + 1, 2)}/day${pad(day + 1, 2)}.py` 144 | }) 145 | }, 146 | { 147 | name: "H1ghBre4k3r", 148 | lang: _ => "rust", 149 | langName: "Rust", 150 | ...gitHubUrls({ 151 | user: "H1ghBre4k3r", 152 | repo: `aoc-${year}`, 153 | path: day => `src/day_${pad(day + 1, 2)}.rs` 154 | }) 155 | }, 156 | { 157 | name: "Zihark", 158 | lang: _ => "haskell", 159 | langName: "Haskell", 160 | ...gitHubUrls({ 161 | user: "Ziharrk", 162 | repo: `aoc${year}`, 163 | path: day => `src/Day${day + 1}.hs` 164 | }) 165 | }, 166 | { 167 | name: "sebfisch", 168 | lang: _ => "java", 169 | langName: "Java", 170 | ...gitHubUrls({ 171 | user: "sebfisch", 172 | repo: "AdventOfCode", 173 | branch: "latest", 174 | path: (day, part) => `year${year}/day${pad(day + 1, 2)}/Part${part + 1}.java` 175 | }) 176 | }, 177 | { 178 | name: "hendrick404", 179 | lang: _ => "python", 180 | langName: "Python", 181 | ...gitHubUrls({ 182 | user: "hendrick404", 183 | repo: `advent-of-code-${year}`, 184 | path: day => `day${pad(day + 1, 2)}/day${pad(day + 1, 2)}.py` 185 | }) 186 | }, 187 | { 188 | name: "maclement", 189 | lang: _ => "haskell", 190 | langName: "Haskell", 191 | ...gitHubUrls({ 192 | user: "maclement", 193 | repo: `advent-of-code-${year}`, 194 | path: day => `Haskell/Day${day + 1}/A.hs` 195 | }) 196 | }, 197 | { 198 | name: "Felioh", 199 | lang: _ => "python", 200 | langName: "Python", 201 | ...gitHubUrls({ 202 | user: "Felioh", 203 | repo: "AdventOfCode", 204 | path: (day, part) => `${day + 1}/part${part + 1}.py` 205 | }) 206 | } 207 | ]; 208 | 209 | return users; 210 | } 211 | -------------------------------------------------------------------------------- /2021/scripts.js: -------------------------------------------------------------------------------- 1 | window.addEventListener("load", async () => { 2 | const fwcdPathsResponse = await fetch('https://raw.githubusercontent.com/fwcd/advent-of-code-2021/main/paths.json'); 3 | const fwcdPaths = JSON.parse(await fwcdPathsResponse.text()); 4 | 5 | const users = [ 6 | { 7 | name: "Alexander P", 8 | lang: _ => "clike", 9 | solutionUrl: (day, part) => { 10 | return `https://raw.githubusercontent.com/Zeldacrafter/CompProg-Solutions/master/AdventOfCode/2021/${day + 1}/${part + 1}.cc` 11 | } 12 | }, 13 | { 14 | name: "I3J03RN", 15 | lang: _ => "clike", 16 | solutionUrl: (day, part) => { 17 | return `https://raw.githubusercontent.com/I3J03RN/ProgrammingChallenges/master/AoC/2021/${day + 1}.cc` 18 | } 19 | }, 20 | { 21 | name: "Melf", 22 | lang: _ => "clike", 23 | solutionUrl: (day, part) => { 24 | const _day = (day + 1).toString().padStart(2, "0"); 25 | const _part = ["A", "B"][part]; 26 | return `https://raw.githubusercontent.com/melfkammholz/aoc21/main/day${_day}/${_part}.cc` 27 | } 28 | }, 29 | { 30 | name: "Estugon", 31 | lang: _ => "clike", 32 | solutionUrl: (day, part) => { 33 | return `https://raw.githubusercontent.com/Estugon/Advent-Of-Code-2021/main/day${day + 1}/day${day + 1}.cpp` 34 | } 35 | }, 36 | { 37 | name: "JulianGrabitzky", 38 | lang: _ => "clike", 39 | solutionUrl: (day, part) => { 40 | const _part = ["a", "b"][part]; 41 | return `https://raw.githubusercontent.com/JulianGrabitzky/advent-of-code-2021/main/day/${day + 1}/${_part}.py` 42 | } 43 | }, 44 | { 45 | name: "H1ghBre4k3r", 46 | lang: _ => "js", 47 | solutionUrl: (day, part) => { 48 | const _day = (day + 1).toString().padStart(2, "0"); 49 | return `https://raw.githubusercontent.com/H1ghBre4k3r/advent-of-code-2021/main/day${_day}/solution0${part + 1}.js` 50 | } 51 | }, 52 | { 53 | name: "l1k3ab0t", 54 | lang: _ => "rust", 55 | solutionUrl: (day, part) => { 56 | return `https://raw.githubusercontent.com/l1k3ab0t/aoc2021/main/src/day${day + 1}.rs` 57 | } 58 | }, 59 | { 60 | name: "LeoVerto", 61 | lang: _ => "python", 62 | solutionUrl: (day, part) => { 63 | const _day = (day + 1).toString().padStart(2, "0"); 64 | const _part = ["a", "b"][part]; 65 | return `https://raw.githubusercontent.com/LeoVerto/advent-of-code-2021/main/days/${_day}/${_day}${_part}.py` 66 | } 67 | }, 68 | { 69 | name: "Niatsuna", 70 | lang: _ => "python", 71 | solutionUrl: (day, part) => { 72 | const _day = (day + 1).toString().padStart(2, "0"); 73 | return `https://raw.githubusercontent.com/Niatsuna/advent-of-code-2021/master/${_day}.py` 74 | } 75 | }, 76 | { 77 | name: "mjt001", 78 | lang: _ => "python", 79 | solutionUrl: (day, part) => { 80 | const _day = (day + 1).toString().padStart(2, "0"); 81 | return `https://raw.githubusercontent.com/mjt001/AdventofCode2021_mirror/main/${_day}/main${day + 1}${part + 1}.py` 82 | } 83 | }, 84 | { 85 | name: "integermainvoid", 86 | lang: _ => "python", 87 | solutionUrl: (day, part) => { 88 | return `https://raw.githubusercontent.com/integermainvoid/advent_of_code/main/day%20${day + 1}/aoc_${part + 1}.py` 89 | } 90 | }, 91 | { 92 | name: "fwcd", 93 | lang: day => day < fwcdPaths.length ? fwcdPaths[day].lang : null, 94 | solutionUrl: (day, part) => 95 | day < fwcdPaths.length 96 | ? `https://raw.githubusercontent.com/fwcd/advent-of-code-2021/main/${fwcdPaths[day].path}` 97 | : null 98 | }, 99 | { 100 | name: "maclement", 101 | lang: _ => "haskell", 102 | solutionUrl: (day, part) => 103 | `https://raw.githubusercontent.com/maclement/advent-of-code-2021/main/Day${day + 1}/Day${day + 1}.hs` 104 | }, 105 | { 106 | name: "xtay2", 107 | lang: _ => "java", 108 | solutionUrl: (day, part) => { 109 | const _day = (day + 1).toString().padStart(2, "0"); 110 | return `https://raw.githubusercontent.com/xtay2/AdventOfCode/main/src/year2021/day${_day}/Task_${part == 0 ? 'A' : 'B'}.java`; 111 | } 112 | }, 113 | { 114 | name: "Skgland", 115 | lang: _ => "rust", 116 | solutionUrl: (day, part) => { 117 | const _day = (day + 1).toString().padStart(2, "0"); 118 | return `https://raw.githubusercontent.com/Skgland/Advent-of-Code/main/crates/year2021/src/day${_day}.rs` 119 | } 120 | } 121 | ]; 122 | 123 | const days = new Date(Math.min(new Date("2021-12-25").valueOf(), Date.now())).getDate(); 124 | const state = { 125 | day: days - 1, 126 | part: 0, 127 | index: 0 128 | }; 129 | 130 | async function loadSolution(user, day, part) { 131 | const url = user.solutionUrl(day, part); 132 | const preEl = document.createElement("pre"); 133 | const codeEl = document.createElement("code"); 134 | const lang = user.lang(day); 135 | try { 136 | const code = await fetch(url) 137 | .then(res => res.ok ? res.text() : Promise.reject(new Error("No solution yet!"))); 138 | await Prism.plugins.autoloader.loadLanguages( 139 | lang, 140 | () => { 141 | codeEl.innerHTML = Prism.highlight(code, Prism.languages[lang], user.lang); 142 | } 143 | ); 144 | } catch (err) { 145 | codeEl.textContent = err.message; 146 | } 147 | preEl.appendChild(codeEl); 148 | document.getElementById("preview").innerHTML = ""; 149 | document.getElementById("preview").appendChild(preEl); 150 | } 151 | 152 | users.forEach((user, index) => { 153 | const el = document.createElement("li"); 154 | el.classList.add("list-group-item"); 155 | el.textContent = user.name; 156 | el.addEventListener("click", () => { 157 | state.index = index; 158 | document.querySelector(".list-group-item.active").classList.remove("active"); 159 | el.classList.add("active"); 160 | loadSolution(users[state.index], state.day, state.part); 161 | }); 162 | document.getElementById("users").appendChild(el); 163 | }); 164 | 165 | Array.from(document.querySelectorAll(".part")).forEach((el, i) => { 166 | el.addEventListener("click", () => { 167 | state.part = i; 168 | document.querySelector(".part.active").classList.remove("active"); 169 | el.classList.add("active"); 170 | loadSolution(users[state.index], state.day, state.part); 171 | }); 172 | }); 173 | 174 | [...Array(days)].forEach((_, i) => { 175 | const el = document.createElement("li"); 176 | el.classList.add("nav-item"); 177 | const aEl = document.createElement("a"); 178 | aEl.classList.add("nav-link"); 179 | aEl.classList.add("day"); 180 | aEl.textContent = i + 1; 181 | aEl.addEventListener("click", () => { 182 | state.day = i; 183 | document.querySelector(".day.active").classList.remove("active"); 184 | aEl.classList.add("active"); 185 | loadSolution(users[state.index], state.day, state.part); 186 | }); 187 | el.appendChild(aEl); 188 | document.querySelector(".nav").appendChild(el); 189 | }); 190 | 191 | document.querySelectorAll(".list-group-item")[state.index].classList.add("active"); 192 | document.querySelectorAll(".part")[state.part].classList.add("active"); 193 | document.querySelectorAll(".day")[state.day].classList.add("active"); 194 | 195 | loadSolution(users[state.index], state.day, state.part); 196 | }); 197 | -------------------------------------------------------------------------------- /2023/src/scripts.js: -------------------------------------------------------------------------------- 1 | import { year } from './constants.js'; 2 | import { loadUsers } from './users.js'; 3 | 4 | window.addEventListener("load", async () => { 5 | const mod = (x, m) => (x % m + m) % m; 6 | const clamp = (min, max, val) => Math.min(max, Math.max(val, min)); 7 | 8 | const minBy = (xs, p) => xs.reduce((y, x) => p(y, x) < 0 ? y : x, xs[0]); 9 | const comps = (...ps) => (a, b) => ps.reduce((r, p) => r == 0 ? p(a, b) : r, 0); 10 | const userComp = (day, part) => comps( 11 | (a, b) => a.langName(day, part).localeCompare(b.langName(day, part)), 12 | (a, b) => a.name.localeCompare(b.name) 13 | ); 14 | 15 | const users = await loadUsers(); 16 | 17 | const days = new Date(clamp( 18 | new Date(`${year}-12-01`).valueOf(), 19 | new Date(`${year}-12-25`).valueOf(), 20 | Date.now() - 6 * 60 * 60 * 1000 21 | )).getDate(); 22 | 23 | const state = { 24 | day: days - 1, 25 | part: 0, 26 | userName: minBy(users, userComp(days - 1, 0)).name, 27 | }; 28 | 29 | async function loadSolution(user, day, part) { 30 | const url = user.solutionUrl(day, part); 31 | const preEl = document.createElement("pre"); 32 | const codeEl = document.createElement("code"); 33 | const lang = user.lang(day, part); 34 | const encoding = ("encoding" in user ? user.encoding(day, part) : null) || "utf-8"; 35 | const decoder = new TextDecoder(encoding); 36 | try { 37 | const result = url ? await fetch(url) : null; 38 | if (!(result?.ok ?? false)) { 39 | throw new Error("No solution yet"); 40 | } 41 | const buffer = await result.arrayBuffer(); 42 | const code = decoder.decode(buffer); 43 | if (lang) { 44 | await Prism.plugins.autoloader.loadLanguages( 45 | lang, 46 | () => { 47 | codeEl.innerHTML = Prism.highlight(code, Prism.languages[lang], user.lang); 48 | }, 49 | () => { 50 | codeEl.innerText = code; 51 | } 52 | ); 53 | } else { 54 | codeEl.innerText = code; 55 | } 56 | } catch (err) { 57 | codeEl.textContent = err.message; 58 | } 59 | preEl.appendChild(codeEl); 60 | document.getElementById("preview").innerHTML = ""; 61 | document.getElementById("preview").appendChild(preEl); 62 | } 63 | 64 | function render(strings, ...values) { 65 | const html = String.raw({ raw: strings }, ...values); 66 | const el = document.createElement('div'); 67 | el.innerHTML = html; 68 | return el.children[0]; 69 | } 70 | 71 | function userIndex(userName) { 72 | const index = users.findIndex(u => u.name === userName); 73 | if (index < 0) { 74 | throw new Error(`Could not find index for user ${userName}`); 75 | } 76 | return index; 77 | } 78 | 79 | function currentUser() { 80 | return state.userName ? users.find(u => u.name === state.userName) : null; 81 | } 82 | 83 | function updateSolution() { 84 | loadSolution(currentUser(), state.day, state.part); 85 | } 86 | 87 | function setActive(selector, index) { 88 | const className = "active"; 89 | const activatedEl = document.querySelectorAll(selector)[index]; 90 | document.querySelectorAll(`${selector}.${className}`).forEach(el => el.classList.remove(className)); 91 | activatedEl.classList.add(className); 92 | } 93 | 94 | function updateQueryParams() { 95 | const params = new URLSearchParams(); 96 | for (const key in state) { 97 | params.set(key, `${state[key]}`); 98 | } 99 | history.pushState(null, "", `${window.location.pathname}?${params}`); 100 | } 101 | 102 | function updateUsers() { 103 | const usersEl = document.getElementById("users"); 104 | usersEl.innerHTML = ""; 105 | 106 | users.sort(userComp(state.day, state.part)); 107 | 108 | users.forEach(user => { 109 | const el = render` 110 |
          • 111 | ${user.name} 112 | 113 | ${user.langAnnotation ? `${user.langAnnotation}` : ""} 114 | ${user.langName(state.day, state.part)} 115 | 116 |
          • 117 | `; 118 | el.addEventListener("click", () => { 119 | updateState({ userName: user.name }); 120 | }); 121 | usersEl.appendChild(el); 122 | }); 123 | } 124 | 125 | function updateState({ userName = null, day = null, part = null, updateActive = false, updateQuery = true }) { 126 | if (userName !== null && !users.find(u => u.name === userName)) { 127 | console.warn(`User ${userName} could not be found!`); 128 | userName = null; 129 | } 130 | 131 | state.day = day ?? state.day; 132 | state.part = part ?? state.part; 133 | state.userName = userName ?? (updateQuery ? state.userName : minBy(users, userComp(state.day, state.part)).name); 134 | 135 | if (day !== null || part !== null || updateActive) { 136 | updateUsers(); 137 | } 138 | if (userName !== null || day !== null || part !== null || updateActive) { 139 | setActive("#users .list-group-item", userIndex(state.userName)); 140 | } 141 | if (day !== null || updateActive) { 142 | setActive(".nav .day", state.day); 143 | } 144 | if (part !== null || updateActive) { 145 | setActive(".part", state.part); 146 | } 147 | if (updateQuery) { 148 | updateQueryParams(); 149 | } 150 | updateSolution(); 151 | } 152 | 153 | function loadStateFromQueryParams() { 154 | const params = new URLSearchParams(window.location.search); 155 | const loaded = {}; 156 | for (const key in state) { 157 | const value = params.get(key); 158 | if (value !== null) { 159 | const parsed = parseInt(value); 160 | loaded[key] = isNaN(parsed) ? value : parsed; 161 | } 162 | } 163 | updateState({ ...loaded, updateActive: true, updateQuery: false }); 164 | } 165 | 166 | function userNameWithOffset(userName, offset) { 167 | return users[mod(userIndex(userName) + offset, users.length)].name; 168 | } 169 | 170 | window.addEventListener("keydown", (event) => { 171 | if (event.key === "w" || event.key === "k" ) updateState({ userName: userNameWithOffset(state.userName, -1) }); 172 | else if (event.key === "s" || event.key === "j") updateState({ userName: userNameWithOffset(state.userName, 1) }); 173 | else if (event.key === "a" || event.key === "h") updateState({ day: mod(state.day - 1, days) }); 174 | else if (event.key === "d" || event.key === "l") updateState({ day: mod(state.day + 1, days) }); 175 | else if (event.key === "q") updateState({ part: (state.part + 1) % 2 }); 176 | 177 | // scroll when keyboard shortcuts are used for navigation 178 | if (["w", "s", "k", "j"].indexOf(event.key) !== -1) { 179 | const li = document.querySelector("#users .list-group-item.active"); 180 | li.parentElement.scrollTop = 181 | li.offsetTop - 0.5 * (li.parentElement.offsetHeight - li.offsetHeight); 182 | } 183 | }); 184 | 185 | window.addEventListener("popstate", () => { 186 | loadStateFromQueryParams(); 187 | }); 188 | 189 | Array.from(document.querySelectorAll(".part")).forEach((el, i) => { 190 | el.addEventListener("click", () => { 191 | updateState({ part: i }); 192 | }); 193 | }); 194 | 195 | function createNavItem({ label = "", classes = [], onClick = () => {} }) { 196 | const el = render`
          • 111 | ${user.name} 112 | 113 | ${user.langAnnotation ? `${user.langAnnotation}` : ""} 114 | ${user.langName(state.day, state.part)} 115 | 116 |
          • 117 | `; 118 | el.addEventListener("click", () => { 119 | updateState({ userName: user.name }); 120 | }); 121 | usersEl.appendChild(el); 122 | }); 123 | } 124 | 125 | function updateState({ userName = null, day = null, part = null, updateActive = false, updateQuery = true }) { 126 | if (userName !== null && !users.find(u => u.name === userName)) { 127 | console.warn(`User ${userName} could not be found!`); 128 | userName = null; 129 | } 130 | 131 | state.day = day ?? state.day; 132 | state.part = part ?? state.part; 133 | state.userName = userName ?? (updateQuery ? state.userName : minBy(users, userComp(state.day, state.part)).name); 134 | 135 | if (day !== null || part !== null || updateActive) { 136 | updateUsers(); 137 | } 138 | if (userName !== null || day !== null || part !== null || updateActive) { 139 | setActive("#users .list-group-item", userIndex(state.userName)); 140 | } 141 | if (day !== null || updateActive) { 142 | setActive(".nav .day", state.day); 143 | } 144 | if (part !== null || updateActive) { 145 | setActive(".part", state.part); 146 | } 147 | if (updateQuery) { 148 | updateQueryParams(); 149 | } 150 | updateSolution(); 151 | } 152 | 153 | function loadStateFromQueryParams() { 154 | const params = new URLSearchParams(window.location.search); 155 | const loaded = {}; 156 | for (const key in state) { 157 | const value = params.get(key); 158 | if (value !== null) { 159 | const parsed = parseInt(value); 160 | loaded[key] = isNaN(parsed) ? value : parsed; 161 | } 162 | } 163 | updateState({ ...loaded, updateActive: true, updateQuery: false }); 164 | } 165 | 166 | function userNameWithOffset(userName, offset) { 167 | return users[mod(userIndex(userName) + offset, users.length)].name; 168 | } 169 | 170 | window.addEventListener("keydown", (event) => { 171 | if (event.key === "w" || event.key === "k" ) updateState({ userName: userNameWithOffset(state.userName, -1) }); 172 | else if (event.key === "s" || event.key === "j") updateState({ userName: userNameWithOffset(state.userName, 1) }); 173 | else if (event.key === "a" || event.key === "h") updateState({ day: mod(state.day - 1, days) }); 174 | else if (event.key === "d" || event.key === "l") updateState({ day: mod(state.day + 1, days) }); 175 | else if (event.key === "q") updateState({ part: (state.part + 1) % 2 }); 176 | 177 | // scroll when keyboard shortcuts are used for navigation 178 | if (["w", "s", "k", "j"].indexOf(event.key) !== -1) { 179 | const li = document.querySelector("#users .list-group-item.active"); 180 | li.parentElement.scrollTop = 181 | li.offsetTop - 0.5 * (li.parentElement.offsetHeight - li.offsetHeight); 182 | } 183 | }); 184 | 185 | window.addEventListener("popstate", () => { 186 | loadStateFromQueryParams(); 187 | }); 188 | 189 | Array.from(document.querySelectorAll(".part")).forEach((el, i) => { 190 | el.addEventListener("click", () => { 191 | updateState({ part: i }); 192 | }); 193 | }); 194 | 195 | function createNavItem({ label = "", classes = [], onClick = () => {} }) { 196 | const el = render`
          • 111 | ${user.name} 112 | 113 | ${user.langAnnotation ? `${user.langAnnotation}` : ""} 114 | ${user.langName(state.day, state.part)} 115 | 116 |
          • 117 | `; 118 | el.addEventListener("click", () => { 119 | updateState({ userName: user.name }); 120 | }); 121 | usersEl.appendChild(el); 122 | }); 123 | } 124 | 125 | function updateState({ userName = null, day = null, part = null, updateActive = false, updateQuery = true }) { 126 | if (userName !== null && !users.find(u => u.name === userName)) { 127 | console.warn(`User ${userName} could not be found!`); 128 | userName = null; 129 | } 130 | 131 | state.day = day ?? state.day; 132 | state.part = part ?? state.part; 133 | state.userName = userName ?? (updateQuery ? state.userName : minBy(users, userComp(state.day, state.part)).name); 134 | 135 | if (day !== null || part !== null || updateActive) { 136 | updateUsers(); 137 | } 138 | if (userName !== null || day !== null || part !== null || updateActive) { 139 | setActive("#users .list-group-item", userIndex(state.userName)); 140 | } 141 | if (day !== null || updateActive) { 142 | setActive(".nav .day", state.day); 143 | } 144 | if (part !== null || updateActive) { 145 | setActive(".part", state.part); 146 | } 147 | if (updateQuery) { 148 | updateQueryParams(); 149 | } 150 | updateSolution(); 151 | } 152 | 153 | function loadStateFromQueryParams() { 154 | const params = new URLSearchParams(window.location.search); 155 | const loaded = {}; 156 | for (const key in state) { 157 | const value = params.get(key); 158 | if (value !== null) { 159 | const parsed = parseInt(value); 160 | loaded[key] = isNaN(parsed) ? value : parsed; 161 | } 162 | } 163 | updateState({ ...loaded, updateActive: true, updateQuery: false }); 164 | } 165 | 166 | function userNameWithOffset(userName, offset) { 167 | return users[mod(userIndex(userName) + offset, users.length)].name; 168 | } 169 | 170 | window.addEventListener("keydown", (event) => { 171 | if (event.key === "w" || event.key === "k" ) updateState({ userName: userNameWithOffset(state.userName, -1) }); 172 | else if (event.key === "s" || event.key === "j") updateState({ userName: userNameWithOffset(state.userName, 1) }); 173 | else if (event.key === "a" || event.key === "h") updateState({ day: mod(state.day - 1, days) }); 174 | else if (event.key === "d" || event.key === "l") updateState({ day: mod(state.day + 1, days) }); 175 | else if (event.key === "q") updateState({ part: (state.part + 1) % 2 }); 176 | 177 | // scroll when keyboard shortcuts are used for navigation 178 | if (["w", "s", "k", "j"].indexOf(event.key) !== -1) { 179 | const li = document.querySelector("#users .list-group-item.active"); 180 | li.parentElement.scrollTop = 181 | li.offsetTop - 0.5 * (li.parentElement.offsetHeight - li.offsetHeight); 182 | } 183 | }); 184 | 185 | window.addEventListener("popstate", () => { 186 | loadStateFromQueryParams(); 187 | }); 188 | 189 | Array.from(document.querySelectorAll(".part")).forEach((el, i) => { 190 | el.addEventListener("click", () => { 191 | updateState({ part: i }); 192 | }); 193 | }); 194 | 195 | function createNavItem({ label = "", classes = [], onClick = () => {} }) { 196 | const el = render`