summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--__main__.py2
-rw-r--r--codenames/assets/css/codenames.css28
-rw-r--r--codenames/assets/js/codenames.js211
-rw-r--r--codenames/templates/codenames_base.html.j227
-rw-r--r--codenames/templates/codenames_duet.html.j232
-rw-r--r--codenames/templates/codenames_root.html.j26
-rw-r--r--config.py14
-rw-r--r--modules/codenames_generate.py32
8 files changed, 348 insertions, 4 deletions
diff --git a/__main__.py b/__main__.py
index fd26157..63f7250 100644
--- a/__main__.py
+++ b/__main__.py
@@ -5,6 +5,7 @@ import jinja2
from .config import Config
from .modules import blog_generate
from .modules import comic_generate
+from .modules import codenames_generate
def init_jinja_env() -> jinja2.Environment:
@@ -25,6 +26,7 @@ def main(output_root_path: str, local: bool):
blog_generate.generate(jinja_env, os.path.join(output_root_path, "blog"), local)
comic_generate.generate(jinja_env, os.path.join(output_root_path, "comic"), local)
+ codenames_generate.generate(jinja_env, os.path.join(output_root_path, "codenames"), local)
if __name__ == '__main__':
diff --git a/codenames/assets/css/codenames.css b/codenames/assets/css/codenames.css
new file mode 100644
index 0000000..db3fb71
--- /dev/null
+++ b/codenames/assets/css/codenames.css
@@ -0,0 +1,28 @@
+div.tile_row {
+ display: flex;
+}
+
+div.tile {
+ width: 100px;
+ height: 100px;
+}
+
+div.neutral {
+ background: antiquewhite;
+}
+
+div.black {
+ background: black;
+}
+
+div.green {
+ background: green;
+}
+
+div.red {
+ background: red;
+}
+
+div.blue {
+ background: blue;
+}
diff --git a/codenames/assets/js/codenames.js b/codenames/assets/js/codenames.js
new file mode 100644
index 0000000..c838b48
--- /dev/null
+++ b/codenames/assets/js/codenames.js
@@ -0,0 +1,211 @@
+"use strict"
+
+const Tile = Object.freeze({
+ NEUTRAL: 0,
+ BLACK: 1,
+ GREEN: 2,
+ RED: 3,
+ BLUE: 4,
+});
+
+function cyrb128(str) {
+ let h1 = 1779033703, h2 = 3144134277,
+ h3 = 1013904242, h4 = 2773480762;
+ for (let i = 0, k; i < str.length; i++) {
+ k = str.charCodeAt(i);
+ h1 = h2 ^ Math.imul(h1 ^ k, 597399067);
+ h2 = h3 ^ Math.imul(h2 ^ k, 2869860233);
+ h3 = h4 ^ Math.imul(h3 ^ k, 951274213);
+ h4 = h1 ^ Math.imul(h4 ^ k, 2716044179);
+ }
+ h1 = Math.imul(h3 ^ (h1 >>> 18), 597399067);
+ h2 = Math.imul(h4 ^ (h2 >>> 22), 2869860233);
+ h3 = Math.imul(h1 ^ (h3 >>> 17), 951274213);
+ h4 = Math.imul(h2 ^ (h4 >>> 19), 2716044179);
+ h1 ^= (h2 ^ h3 ^ h4), h2 ^= h1, h3 ^= h1, h4 ^= h1;
+ return [h1>>>0, h2>>>0, h3>>>0, h4>>>0];
+}
+
+function sfc32(a, b, c, d) {
+ return function() {
+ a |= 0; b |= 0; c |= 0; d |= 0;
+ let t = (a + b | 0) + d | 0;
+ d = d + 1 | 0;
+ a = b ^ b >>> 9;
+ b = c + (c << 3) | 0;
+ c = (c << 21 | c >>> 11);
+ c = c + t | 0;
+ return (t >>> 0) / 4294967296;
+ }
+}
+
+function shuffle(array, rand) {
+ let currentIndex = array.length - 1;
+
+ while (currentIndex !== 0) {
+ let otherIndex = Math.floor(rand() * (currentIndex + 1));
+ [array[currentIndex], array[otherIndex]] = [array[otherIndex], array[currentIndex]];
+ currentIndex--;
+ }
+}
+
+function generateEmptyGrid(width, height){
+ let grid = [];
+ for (let ii = 0; ii < height; ii++) {
+ let line = Array(width);
+ line.fill(Tile.NEUTRAL);
+ grid.push(line)
+ }
+
+ return grid;
+}
+
+function getPositions(width, height) {
+ let positions = [];
+ for (let ii = 0; ii < height; ii++) {
+ for (let jj = 0; jj < width; jj++) {
+ positions.push([ii, jj]);
+ }
+ }
+
+ return positions;
+}
+
+function getPosIndexByValue(array, pos) {
+ for (let ii = 0; ii < array.length; ii++) {
+ if (array[ii][0] === pos[0] && array[ii][1] === pos[1]) {
+ return ii;
+ }
+ }
+
+ return -1;
+}
+
+function generateDuetGrids(width, height, greenCount, blackCount, commonGreenCount, commonBlackCount, blackGreenCount, seed) {
+ let seed_array = cyrb128(seed);
+ let rand = sfc32(seed_array[0], seed_array[1], seed_array[2], seed_array[3]);
+ let blackNeutralCount = blackCount - commonBlackCount - blackGreenCount;
+ let greenNeutralCount = greenCount - commonGreenCount - blackGreenCount;
+
+ let p1 = generateEmptyGrid(width, height);
+ let p2 = generateEmptyGrid(width, height);
+
+ let p1NeutralPositions = getPositions(width, height);
+ let p2NeutralPositions = getPositions(width, height);
+
+ shuffle(p1NeutralPositions, rand);
+ shuffle(p2NeutralPositions, rand);
+
+ let p1GreenPositions = [];
+ let p1BlackPositions = [];
+
+ // P1 generation
+ for (let ii = 0; ii < greenCount; ii++) {
+ let pos = p1NeutralPositions.pop();
+ p1GreenPositions.push(pos);
+ p1[pos[0]][pos[1]] = Tile.GREEN;
+ }
+
+ for (let ii = 0; ii < blackCount; ii++) {
+ let pos = p1NeutralPositions.pop();
+ p1BlackPositions.push(pos);
+ p1[pos[0]][pos[1]] = Tile.BLACK;
+ }
+
+ // P2 BLACK generation
+ for (let ii = 0; ii < commonBlackCount; ii++) {
+ let pos = p1BlackPositions.pop();
+ p2NeutralPositions.splice(getPosIndexByValue(p2NeutralPositions, pos), 1);
+ p2[pos[0]][pos[1]] = Tile.BLACK;
+ }
+
+ for (let ii = 0; ii < blackGreenCount; ii++) {
+ let pos = p1GreenPositions.pop();
+ p2NeutralPositions.splice(getPosIndexByValue(p2NeutralPositions, pos), 1);
+ p2[pos[0]][pos[1]] = Tile.BLACK;
+ }
+
+ for (let ii = 0; ii < blackNeutralCount; ii++) {
+ let pos = p1NeutralPositions.pop();
+ p2NeutralPositions.splice(getPosIndexByValue(p2NeutralPositions, pos), 1);
+ p2[pos[0]][pos[1]] = Tile.BLACK;
+ }
+
+ // P2 GREEN generation
+ for (let ii = 0; ii < commonGreenCount; ii++) {
+ let pos = p1GreenPositions.pop();
+ p2NeutralPositions.splice(getPosIndexByValue(p2NeutralPositions, pos), 1);
+ p2[pos[0]][pos[1]] = Tile.GREEN;
+ }
+
+ for (let ii = 0; ii < blackGreenCount; ii++) {
+ let pos = p1BlackPositions.pop();
+ p2NeutralPositions.splice(getPosIndexByValue(p2NeutralPositions, pos), 1);
+ p2[pos[0]][pos[1]] = Tile.GREEN;
+ }
+
+ for (let ii = 0; ii < greenNeutralCount; ii++) {
+ let pos = p1NeutralPositions.pop();
+ p2NeutralPositions.splice(getPosIndexByValue(p2NeutralPositions, pos), 1);
+ p2[pos[0]][pos[1]] = Tile.GREEN;
+ }
+
+ return [p1, p2];
+}
+
+function parseAndGenerateDuet()
+{
+ let params = new URLSearchParams(window.location.search);
+ if (params.has("w") && params.has("h") && params.has("g") && params.has("b") &&
+ params.has("cg") && params.has("cb") && params.has("bg") && params.has("s")) {
+ return generateDuetGrids(+params.get("w"), +params.get("h"), +params.get("g"), +params.get("b"),
+ +params.get("cg"), +params.get("cb"), +params.get("bg"), params.get("s"));
+ }
+
+ return null;
+}
+
+function generate_for_current_page() {
+ const duetP1 = document.getElementById("duet_p1");
+ const duetP2 = document.getElementById("duet_p2");
+ if (duetP1 !== null || duetP2 !== null) {
+ let grids = parseAndGenerateDuet();
+ if (grids == null) {
+ return;
+ }
+
+ const gridRoot = duetP1;
+ let grid = grids[0];
+
+ for (let ii = 0; ii < grid.length; ii++) {
+ let row = grid[ii];
+ let rowDiv = document.createElement("div");
+ rowDiv.classList.add("tile_row");
+ gridRoot.appendChild(rowDiv);
+ for (let jj = 0; jj < row.length; jj++) {
+ let tileDiv = document.createElement("div");
+ tileDiv.classList.add("tile");
+ rowDiv.appendChild(tileDiv);
+ switch (grid[ii][jj]) {
+ case Tile.NEUTRAL:
+ tileDiv.classList.add("neutral");
+ break;
+ case Tile.BLACK:
+ tileDiv.classList.add("black");
+ break;
+ case Tile.GREEN:
+ tileDiv.classList.add("green");
+ break;
+ case Tile.RED:
+ tileDiv.classList.add("red");
+ break;
+ case Tile.BLUE:
+ tileDiv.classList.add("blue");
+ break;
+ }
+ }
+ }
+ }
+}
+
+generate_for_current_page(); \ No newline at end of file
diff --git a/codenames/templates/codenames_base.html.j2 b/codenames/templates/codenames_base.html.j2
new file mode 100644
index 0000000..e6751f5
--- /dev/null
+++ b/codenames/templates/codenames_base.html.j2
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+<html dir="ltr" lang="en">
+<head>
+ <meta charset="utf-8"/>
+ <title>{{ title }}</title>
+ <meta name="viewport" content="width=device-width, initial-scale=1"/>
+ <meta property="og:title" content="{{ title }}" />
+ <meta property="og:type" content="website" />
+ <meta property="og:url" content="{{ url }}" />
+ <link rel="stylesheet" href="{{ site.assets_path }}/css/codenames.css" />
+ <link rel="stylesheet" href="{{ site.assets_path_static }}/css/bootstrap-grid.min.css" />
+</head>
+<body>
+<div class="bootstrap-wrapper">
+ <div class="container">
+ <div class="row">
+ <main class="col-md-9">
+ {% block content required %}{% endblock %}
+ </main>
+ </div>
+ </div>
+</div>
+</body>
+<footer>
+ <script src="/assets/js/codenames.js"></script>
+</footer>
+</html>
diff --git a/codenames/templates/codenames_duet.html.j2 b/codenames/templates/codenames_duet.html.j2
new file mode 100644
index 0000000..f5cd94b
--- /dev/null
+++ b/codenames/templates/codenames_duet.html.j2
@@ -0,0 +1,32 @@
+{% extends "codenames_base.html.j2" %}
+{% block content %}
+<div id="duet_p1"></div>
+<div id="duet_p2"></div>
+<form action="{{ url }}" method="get">
+ <label for="s">Seed:</label>
+ <input type="text" name="s" id="s" required />
+ <button>Random</button>
+ <br>
+ <label>Size:</label>
+ <input type="number" name="w" id="w" value="5" required />
+ <span>x</span>
+ <input type="number" name="h" id="h" value="5" required />
+ <br>
+ <label for="g">Green tiles (per person):</label>
+ <input type="number" name="g" id="g" value="9" required />
+ <br>
+ <label for="cg">Shared green tiles:</label>
+ <input type="number" name="cg" id="cg" value="3" required />
+ <br>
+ <label for="b">Black tiles:</label>
+ <input type="number" name="b" id="b" value="3" required />
+ <br>
+ <label for="cb">Shared black tiles:</label>
+ <input type="number" name="cb" id="cb" value="1" required />
+ <br>
+ <label for="bg">Black-green tiles:</label>
+ <input type="number" name="bg" id="bg" value="1" required />
+ <br>
+ <button type="submit">Generate</button>
+</form>
+{% endblock %} \ No newline at end of file
diff --git a/codenames/templates/codenames_root.html.j2 b/codenames/templates/codenames_root.html.j2
new file mode 100644
index 0000000..0cbbce7
--- /dev/null
+++ b/codenames/templates/codenames_root.html.j2
@@ -0,0 +1,6 @@
+{% extends "codenames_base.html.j2" %}
+{% block content %}
+ <a href="/classic.html">Classic/Images</a>
+ <br>
+ <a href="/duet.html">Duet</a>
+{% endblock %} \ No newline at end of file
diff --git a/config.py b/config.py
index 8337d29..d8bdd7d 100644
--- a/config.py
+++ b/config.py
@@ -3,15 +3,16 @@ import datetime
class Config:
+ __DIR = os.path.dirname(__file__)
ASSETS_IMPORT_PATH = "/assets"
ASSETS_IMPORT_PATH_STATIC = "https://static.wazul.moe"
RSS_FILE_NAME = "feed.xml"
- BLOG_ROOT_URL = "https://blog.wazul.moe"
TIMEZONE = datetime.timezone(datetime.timedelta(hours=2))
- __DIR = os.path.dirname(__file__)
+ # BLOG
+ BLOG_ROOT_URL = "https://blog.wazul.moe"
BLOG_ASSETS_SOURCE_DIR = os.path.join(__DIR, "blog/assets")
- TEMPLATES_SOURCE_DIR = [os.path.join(__DIR, "blog/templates"), os.path.join(__DIR, "comic/templates")]
+ TEMPLATES_SOURCE_DIR = [os.path.join(__DIR, "blog/templates"), os.path.join(__DIR, "comic/templates"), os.path.join(__DIR, "codenames/templates")]
POST_SOURCE_DIR = os.path.join(__DIR, "blog/posts")
BLOG_HOSTNAME = "yggdrasil"
@@ -28,9 +29,14 @@ class Config:
def get_tag_prompt(tag: str, cmd: str) -> str:
return Config.get_prompt("~/tags/{}".format("" if tag == "" else tag + "/"), cmd)
-
+ # COMIC
COMIC_NAME = "Comics by Wazul"
COMIC_ROOT_URL = "https://comic.wazul.moe"
ISSUE_SOURCE_DIR = os.path.join(__DIR, "comic/issues")
COMIC_ASSETS_SOURCE_DIR = os.path.join(__DIR, "comic/assets")
+ # CODENAMES
+ CODENAMES_NAME = "Codenames Grid Generator"
+ CODENAMES_ROOT_URL = "https://codenames.wazul.moe"
+ CODENAMES_ASSETS_SOURCE_DIR = os.path.join(__DIR, "codenames/assets")
+
diff --git a/modules/codenames_generate.py b/modules/codenames_generate.py
new file mode 100644
index 0000000..55dac9e
--- /dev/null
+++ b/modules/codenames_generate.py
@@ -0,0 +1,32 @@
+import os.path
+import shutil
+import jinja2
+
+from ..config import Config
+import os.path
+import shutil
+
+import jinja2
+
+from ..config import Config
+
+
+def generate(jinja_env: jinja2.Environment, output_root_path: str, local: bool):
+ if os.path.exists(output_root_path):
+ shutil.rmtree(output_root_path)
+
+ os.mkdir(output_root_path)
+
+ jinja_env.globals.update(codenames={
+ "codenames_url": Config.CODENAMES_ROOT_URL,
+ })
+
+ root_template = jinja_env.get_template("codenames_root.html.j2")
+ with open(os.path.join(output_root_path, "index.html"), "w") as f:
+ f.write(root_template.render({"ctx" : Config.CODENAMES_ROOT_URL, "title": Config.CODENAMES_NAME}))
+
+ duet_template = jinja_env.get_template("codenames_duet.html.j2")
+ with open(os.path.join(output_root_path, "duet.html"), "w") as f:
+ f.write(duet_template.render({"ctx" : Config.CODENAMES_ROOT_URL + "duet.html", "title": Config.CODENAMES_NAME}))
+
+ shutil.copytree(Config.CODENAMES_ASSETS_SOURCE_DIR, output_root_path + Config.ASSETS_IMPORT_PATH) \ No newline at end of file