├── .projectile ├── .gitignore ├── resources └── public │ ├── img │ ├── wordchampions-en.png │ ├── pl.svg │ └── en.svg │ ├── index.html │ └── puzzles │ └── nkjp.txt ├── package.json ├── src ├── c │ ├── Makefile │ ├── hashset.h │ ├── hashset.c │ └── generate.c ├── cljs │ └── gridlock │ │ ├── core.cljs │ │ ├── cartesian.cljs │ │ ├── i18n.cljs │ │ ├── subs.cljs │ │ ├── db.cljs │ │ ├── events.cljs │ │ └── views.cljs └── sass │ └── app.sass ├── shadow-cljs.edn ├── Makefile ├── README.md ├── wordlists ├── nkjp59.txt └── en59.txt └── yarn.lock /.projectile: -------------------------------------------------------------------------------- 1 | -/target 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .shadow-cljs 2 | node_modules 3 | resources/public/css 4 | resources/public/js -------------------------------------------------------------------------------- /resources/public/img/wordchampions-en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nathell/wordchampions/HEAD/resources/public/img/wordchampions-en.png -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "dependencies": { 3 | "react": "17.0.1", 4 | "react-dom": "17.0.1", 5 | "shadow-cljs": "^2.16.12" 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /src/c/Makefile: -------------------------------------------------------------------------------- 1 | all: generate 2 | 3 | generate: generate.c hashset.c hashset.h 4 | $(CC) $(CFLAGS) $(LDFLAGS) generate.c hashset.c -o $@ 5 | 6 | clean: 7 | rm generate generate.o 8 | -------------------------------------------------------------------------------- /resources/public/img/pl.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /shadow-cljs.edn: -------------------------------------------------------------------------------- 1 | {:dependencies [[re-frame "1.3.0-rc2"] 2 | [day8.re-frame/http-fx "0.2.4"]] 3 | :source-paths ["src/cljs"] 4 | :dev-http {8000 "resources/public"} 5 | :builds {:app {:target :browser 6 | :output-dir "resources/public/js" 7 | :asset-path "/js" 8 | :modules {:app {:entries [gridlock.core]}}}}} 9 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | OUT := resources/public 2 | 3 | all: $(OUT)/css/app.css watch 4 | release: $(OUT)/css/app.css $(OUT)/js/app.js 5 | 6 | watch: 7 | shadow-cljs watch app 8 | 9 | $(OUT)/css/app.css: src/sass/app.sass 10 | mkdir -p $(OUT)/css 11 | sassc $< $@ 12 | 13 | $(OUT)/js/app.js: src/cljs/gridlock/*.cljs 14 | shadow-cljs release app 15 | 16 | clean: 17 | rm -rf $(OUT)/js $(OUT)/css 18 | 19 | .PHONY: all clean release watch 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Word Champions 2 | 3 | A fun word game! 4 | 5 | ## Building and hacking 6 | 7 | First, do `yarn install` to install shadow-cljs. Then just `make`. 8 | 9 | This will start a dev server on http://localhost:8000, serving the contents of `resources/public`. 10 | 11 | To build in release mode: `make release`. After that, you can just server 12 | `resources/public` with a static server. 13 | 14 | ## Play it 15 | 16 | http://danieljanus.pl/wladcyslow/ 17 | -------------------------------------------------------------------------------- /src/cljs/gridlock/core.cljs: -------------------------------------------------------------------------------- 1 | (ns gridlock.core 2 | (:require 3 | [gridlock.events :as events] 4 | [gridlock.subs :as subs] 5 | [gridlock.views :as views] 6 | [re-frame.core :refer [dispatch-sync]] 7 | [reagent.dom :as rdom])) 8 | 9 | (defn ^:dev/after-load render-root [] 10 | (rdom/render [views/root] (.getElementById js/document "app"))) 11 | 12 | (defn init [] 13 | (dispatch-sync [:init]) 14 | (render-root)) 15 | 16 | (js/document.addEventListener "DOMContentLoaded" init) 17 | -------------------------------------------------------------------------------- /src/cljs/gridlock/cartesian.cljs: -------------------------------------------------------------------------------- 1 | (ns gridlock.cartesian) 2 | 3 | (defn encode 4 | [[dim & other-dims] [x & xs]] 5 | (when (or (nil? dim) (nil? x)) 6 | (throw (js/Error. (str "Dimension mismatch: " dim "," x)))) 7 | (when (>= x dim) 8 | (throw (js/Error. (str "Entry out of dimension: " x "," dim)))) 9 | (if (and (empty? other-dims) (empty? xs)) 10 | x 11 | (+ (* dim (encode other-dims xs)) x))) 12 | 13 | (defn decode 14 | [dims x] 15 | (loop [dims dims x x acc []] 16 | (if (empty? dims) 17 | acc 18 | (let [[dim & other-dims] dims 19 | n (mod x dim) 20 | x (/ (- x n) dim)] 21 | (recur other-dims x (conj acc n)))))) 22 | -------------------------------------------------------------------------------- /resources/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 |
24 | Loading, please wait… 25 |
26 | 27 | 28 | -------------------------------------------------------------------------------- /resources/public/img/en.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/cljs/gridlock/i18n.cljs: -------------------------------------------------------------------------------- 1 | (ns gridlock.i18n) 2 | 3 | (def i18n 4 | {:pl 5 | {:hint "Podpowiedź" 6 | :reset "Resetuj" 7 | :new-game "Nowa gra" 8 | :hello "Witaj!" 9 | :start-game "Zacznij grę" 10 | :how-to-play "Jak grać?" 11 | :feedback "Opinie" 12 | :hooray "Brawo!" 13 | :play-again "Jeszcze raz" 14 | :select-difficulty "Wybierz poziom trudności:" 15 | :easy "Łatwy" 16 | :normal "Średni" 17 | :hard "Trudny" 18 | :loading-dictionaries "Ładuję słowniki" 19 | :written-in-cljs "Napisane w języku ClojureScript" 20 | :source-code "Kod źródłowy" 21 | :time "Czas" 22 | :hints "Podpowiedzi" 23 | :word-champions "Władcy słów" 24 | :game "gra" 25 | :share "Udostępnij" 26 | :share-msg1 "Rozwiązał_m zagadkę w grze Władcy Słów na poziomie " 27 | :share-easy "łatwym" 28 | :share-medium "średnim" 29 | :share-hard "trudnym" 30 | :share-msg2 " w czasie " 31 | :share-msg3 " Sprawdź, czy umiesz szybciej!\n\nMoje słowa: " 32 | :copied-to-clipboard "Skopiowano do schowka" 33 | :not-copied-to-clipboard "Błąd przy kopiowaniu do schowka"} 34 | :en 35 | {:hint "Hint" 36 | :reset "Reset" 37 | :new-game "New game" 38 | :hello "Hello!" 39 | :start-game "Start game" 40 | :how-to-play "How to play" 41 | :feedback "Feedback" 42 | :hooray "Hooray!" 43 | :play-again "Play again" 44 | :select-difficulty "Select difficulty:" 45 | :easy "Easy" 46 | :normal "Medium" 47 | :hard "Hard" 48 | :loading-dictionaries "Loading dictionaries" 49 | :written-in-cljs "Written in ClojureScript" 50 | :source-code "Source code" 51 | :time "Time" 52 | :hints "Hints" 53 | :word-champions "Word Champions" 54 | :game "game" 55 | :share "Share" 56 | :share-msg1 "I solved a Word Champions " 57 | :share-easy "easy" 58 | :share-medium "medium" 59 | :share-hard "hard" 60 | :share-msg2 "-level puzzle in " 61 | :share-msg3 " Try and see if you can beat my time!\n\nMy words: " 62 | :copied-to-clipboard "Copied to clipboard" 63 | :not-copied-to-clipboard "Copy to clipboard failed"}}) 64 | -------------------------------------------------------------------------------- /src/c/hashset.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* 3 | * Copyright 2012 Couchbase, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #ifndef HASHSET_H 19 | #define HASHSET_H 1 20 | 21 | #include 22 | 23 | #ifdef __cplusplus 24 | extern "C" { 25 | #endif 26 | 27 | struct hashset_st { 28 | size_t nbits; 29 | size_t mask; 30 | 31 | size_t capacity; 32 | size_t *items; 33 | size_t nitems; 34 | size_t n_deleted_items; 35 | }; 36 | 37 | typedef struct hashset_st *hashset_t; 38 | 39 | /* create hashset instance */ 40 | hashset_t hashset_create(void); 41 | 42 | /* destroy hashset instance */ 43 | void hashset_destroy(hashset_t set); 44 | 45 | size_t hashset_num_items(hashset_t set); 46 | 47 | /* add item into the hashset. 48 | * 49 | * @note 0 and 1 is special values, meaning nil and deleted items. the 50 | * function will return -1 indicating error. 51 | * 52 | * returns zero if the item already in the set and non-zero otherwise 53 | */ 54 | int hashset_add(hashset_t set, void *item); 55 | 56 | /* remove item from the hashset 57 | * 58 | * returns non-zero if the item was removed and zero if the item wasn't 59 | * exist 60 | */ 61 | int hashset_remove(hashset_t set, void *item); 62 | 63 | /* check if existence of the item 64 | * 65 | * returns non-zero if the item exists and zero otherwise 66 | */ 67 | int hashset_is_member(hashset_t set, void *item); 68 | 69 | #ifdef __cplusplus 70 | } 71 | #endif 72 | 73 | #endif 74 | -------------------------------------------------------------------------------- /src/cljs/gridlock/subs.cljs: -------------------------------------------------------------------------------- 1 | (ns gridlock.subs 2 | (:require 3 | [gridlock.db :as db :refer [msg]] 4 | [gridlock.i18n :as i18n] 5 | [re-frame.core :refer [reg-sub]])) 6 | 7 | (defn finished? 8 | [{:keys [horiz vert fill]}] 9 | (let [[a b c] horiz] 10 | (= (str (subs a 1 4) (subs b 1 4) (subs c 1 4)) fill))) 11 | 12 | (defn can-place? 13 | [{:keys [compat current-tile dragging]} diagram-number] 14 | (let [source (or dragging current-tile) 15 | acceptable-source (get compat diagram-number) 16 | diagram-source (:diagram-number source) 17 | nine-source (:nine-number source)] 18 | (cond diagram-source (= diagram-source diagram-number) 19 | nine-source (or (= acceptable-source nine-source) 20 | (and (nil? acceptable-source) 21 | (not (contains? (set (vals compat)) nine-source)))) 22 | :otherwise false))) 23 | 24 | (defn ->diagram 25 | [db i {:keys [horiz vert fill] :as zag}] 26 | {:diagram-number i 27 | :top (apply str (map first vert)) 28 | :bottom (apply str (map last vert)) 29 | :left (apply str (map first horiz)) 30 | :right (apply str (map last horiz)) 31 | :placed fill 32 | :highlighted (can-place? db i) 33 | :finished (finished? zag)}) 34 | 35 | (reg-sub 36 | :diagrams 37 | (fn [db _] 38 | (map-indexed (partial ->diagram db) (:zag db)))) 39 | 40 | (reg-sub 41 | :nines 42 | (fn [{:keys [words]} _] 43 | (map-indexed (fn [i {:keys [available]}] 44 | {:nine-number i, :letters available}) 45 | words))) 46 | 47 | (reg-sub 48 | :can-place? 49 | (fn [db [_ diagram-number]] 50 | (can-place? db diagram-number))) 51 | 52 | (reg-sub 53 | :mode 54 | (fn [db _] 55 | (:mode db))) 56 | 57 | (reg-sub 58 | :current-diagram 59 | (fn [db _] 60 | (:current-diagram db))) 61 | 62 | (reg-sub 63 | :requested-difficulty 64 | (fn [db _] 65 | (count (:problem-numbers db)))) 66 | 67 | (reg-sub 68 | :title-bar 69 | (fn [db _] 70 | (if (contains? #{:in-progress :success} (:mode db)) 71 | (str (msg db :time) 72 | ": " 73 | (db/format-time (:time db)) 74 | " " 75 | (msg db :hints) 76 | ": " 77 | (:hints db)) 78 | (str 79 | (msg db :word-champions) 80 | (when (seq (:problem-numbers db)) 81 | (str " – " 82 | (msg db :game) 83 | " " 84 | (subs js/window.location.hash 1))))))) 85 | 86 | (reg-sub 87 | :dictionary-selected? 88 | (fn [db [_ dict]] 89 | (= (:dictionary db) dict))) 90 | 91 | (reg-sub 92 | :loaded? 93 | (fn [{:keys [dictionaries language]} _] 94 | (case language 95 | :pl (and (:nkjp dictionaries) (:osps dictionaries) true) 96 | :en (and (:en dictionaries) true)))) 97 | 98 | (reg-sub 99 | :current-tile 100 | (fn [db _] 101 | (:current-tile db))) 102 | 103 | (reg-sub 104 | :language 105 | (fn [db _] 106 | (:language db))) 107 | 108 | (reg-sub 109 | :i18n-message 110 | :<- [:language] 111 | (fn [language [_ id]] 112 | (get-in i18n/i18n [language id]))) 113 | 114 | (reg-sub 115 | :toast-shown? 116 | (fn [db _] 117 | (:toast-shown? db))) 118 | 119 | (reg-sub 120 | :toast-message 121 | (fn [db _] 122 | (:toast-message db))) 123 | -------------------------------------------------------------------------------- /src/c/hashset.c: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | /* 3 | * Copyright 2012 Couchbase, Inc. 4 | * 5 | * Licensed under the Apache License, Version 2.0 (the "License"); 6 | * you may not use this file except in compliance with the License. 7 | * You may obtain a copy of the License at 8 | * 9 | * http://www.apache.org/licenses/LICENSE-2.0 10 | * 11 | * Unless required by applicable law or agreed to in writing, software 12 | * distributed under the License is distributed on an "AS IS" BASIS, 13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14 | * See the License for the specific language governing permissions and 15 | * limitations under the License. 16 | */ 17 | 18 | #include "hashset.h" 19 | #include 20 | 21 | static const unsigned int prime_1 = 73; 22 | static const unsigned int prime_2 = 5009; 23 | 24 | hashset_t hashset_create() 25 | { 26 | hashset_t set = calloc(1, sizeof(struct hashset_st)); 27 | 28 | if (set == NULL) { 29 | return NULL; 30 | } 31 | set->nbits = 3; 32 | set->capacity = (size_t)(1 << set->nbits); 33 | set->mask = set->capacity - 1; 34 | set->items = calloc(set->capacity, sizeof(size_t)); 35 | if (set->items == NULL) { 36 | hashset_destroy(set); 37 | return NULL; 38 | } 39 | set->nitems = 0; 40 | set->n_deleted_items = 0; 41 | return set; 42 | } 43 | 44 | size_t hashset_num_items(hashset_t set) 45 | { 46 | return set->nitems; 47 | } 48 | 49 | void hashset_destroy(hashset_t set) 50 | { 51 | if (set) { 52 | free(set->items); 53 | } 54 | free(set); 55 | } 56 | 57 | static int hashset_add_member(hashset_t set, void *item) 58 | { 59 | size_t value = (size_t)item; 60 | size_t ii; 61 | 62 | if (value == 0 || value == 1) { 63 | return -1; 64 | } 65 | 66 | ii = set->mask & (prime_1 * value); 67 | 68 | while (set->items[ii] != 0 && set->items[ii] != 1) { 69 | if (set->items[ii] == value) { 70 | return 0; 71 | } else { 72 | /* search free slot */ 73 | ii = set->mask & (ii + prime_2); 74 | } 75 | } 76 | set->nitems++; 77 | if (set->items[ii] == 1) { 78 | set->n_deleted_items--; 79 | } 80 | set->items[ii] = value; 81 | return 1; 82 | } 83 | 84 | static void maybe_rehash(hashset_t set) 85 | { 86 | size_t *old_items; 87 | size_t old_capacity, ii; 88 | 89 | 90 | if (set->nitems + set->n_deleted_items >= (double)set->capacity * 0.85) { 91 | old_items = set->items; 92 | old_capacity = set->capacity; 93 | set->nbits++; 94 | set->capacity = (size_t)(1 << set->nbits); 95 | set->mask = set->capacity - 1; 96 | set->items = calloc(set->capacity, sizeof(size_t)); 97 | set->nitems = 0; 98 | set->n_deleted_items = 0; 99 | assert(set->items); 100 | for (ii = 0; ii < old_capacity; ii++) { 101 | hashset_add_member(set, (void *)old_items[ii]); 102 | } 103 | free(old_items); 104 | } 105 | } 106 | 107 | int hashset_add(hashset_t set, void *item) 108 | { 109 | int rv = hashset_add_member(set, item); 110 | maybe_rehash(set); 111 | return rv; 112 | } 113 | 114 | int hashset_remove(hashset_t set, void *item) 115 | { 116 | size_t value = (size_t)item; 117 | size_t ii = set->mask & (prime_1 * value); 118 | 119 | while (set->items[ii] != 0) { 120 | if (set->items[ii] == value) { 121 | set->items[ii] = 1; 122 | set->nitems--; 123 | set->n_deleted_items++; 124 | return 1; 125 | } else { 126 | ii = set->mask & (ii + prime_2); 127 | } 128 | } 129 | return 0; 130 | } 131 | 132 | int hashset_is_member(hashset_t set, void *item) 133 | { 134 | size_t value = (size_t)item; 135 | size_t ii = set->mask & (prime_1 * value); 136 | 137 | while (set->items[ii] != 0) { 138 | if (set->items[ii] == value) { 139 | return 1; 140 | } else { 141 | ii = set->mask & (ii + prime_2); 142 | } 143 | } 144 | return 0; 145 | } 146 | -------------------------------------------------------------------------------- /src/cljs/gridlock/db.cljs: -------------------------------------------------------------------------------- 1 | (ns gridlock.db 2 | (:require [clojure.string :as string] 3 | [gridlock.cartesian :as cartesian] 4 | [gridlock.i18n :as i18n])) 5 | 6 | (defn msg 7 | [db id] 8 | (get-in i18n/i18n [(:language db) id])) 9 | 10 | (defn substitute-letter [w i c] 11 | (str (subs w 0 i) c (subs w (inc i) (count w)))) 12 | 13 | (defn convert-problem [{:keys [word horiz vert]}] 14 | {:word word, 15 | :horiz (mapv rand-nth horiz), 16 | :vert (mapv rand-nth vert), 17 | :fill "........."}) 18 | 19 | (defn rand-nth-with-index 20 | "Returns a 2-element vector [random index into given coll, 21 | element under that index]." 22 | [coll] 23 | (let [i (rand-int (count coll))] 24 | [i (nth coll i)])) 25 | 26 | (defn make-problem 27 | [subparts variant] 28 | (let [[middles word h1 h2 h3 v1 v2 v3] (mapv nth subparts variant) 29 | middles (partition 3 middles) 30 | horiz-middles (map #(apply str %) middles) 31 | vert-middles (apply map str middles) 32 | horiz (mapv #(str (first %2) %1 (last %2)) horiz-middles [h1 h2 h3]) 33 | vert (mapv #(str (first %2) %1 (last %2)) vert-middles [v1 v2 v3])] 34 | {:word word 35 | :horiz horiz 36 | :vert vert 37 | :fill "........."})) 38 | 39 | (defn split-problem 40 | [problem] 41 | (let [parts (string/split problem #",") 42 | subparts (mapv #(string/split % #";") parts) 43 | dimensions (mapv count subparts)] 44 | {:subparts subparts, :dimensions dimensions})) 45 | 46 | (defn parse-problem 47 | [n-problems [index problem]] 48 | (when problem 49 | (let [{:keys [subparts dimensions]} (split-problem problem) 50 | variant (mapv rand-int dimensions) 51 | all-dimensions (into [n-problems] dimensions) 52 | all-variant (into [index] variant)] 53 | (-> (make-problem subparts variant) 54 | (assoc :problem (cartesian/encode all-dimensions all-variant)))))) 55 | 56 | (defn retrieve-problem 57 | [db dict problem-number] 58 | (let [problems (get-in db [:dictionaries dict]) 59 | n-problems (count problems) 60 | n (mod problem-number n-problems) 61 | problem (nth problems n) 62 | {:keys [subparts dimensions]} (split-problem problem) 63 | all-dimensions (into [n-problems] dimensions) 64 | variant (next (cartesian/decode all-dimensions problem-number))] 65 | (-> (make-problem subparts variant) 66 | (assoc :problem problem-number)))) 67 | 68 | (defn generate-problem 69 | [db dict] 70 | (let [problems (get-in db [:dictionaries dict]) 71 | n-problems (count problems)] 72 | (->> problems 73 | rand-nth-with-index 74 | (parse-problem n-problems)))) 75 | 76 | (def dictionaries 77 | {:pl #{:osps :nkjp}, :en #{:en}}) 78 | 79 | (def dict->lang 80 | (into {} 81 | (for [[k vs] dictionaries v vs] 82 | [v k]))) 83 | 84 | (defn problems 85 | [db dict n] 86 | (let [items (if-let [problem-numbers (:problem-numbers db)] 87 | (mapv (partial retrieve-problem db dict) problem-numbers) 88 | (repeatedly n #(generate-problem db dict))) 89 | words (map-indexed (fn [i x] {:diagram-number i, :full (:word x), :available (:word x)}) 90 | items)] 91 | {:zag (mapv #(dissoc % :word) items) 92 | :words (vec (shuffle words)) 93 | :url (string/join "/" 94 | (into [(name dict)] (map :problem items)))})) 95 | 96 | (defn parse-url [url] 97 | (let [parts (string/split url #"/")] 98 | (when parts 99 | (let [[dict & problems] parts 100 | dict (keyword dict) 101 | language (dict->lang dict) 102 | problems (mapv #(js/parseInt %) problems)] 103 | (when (and language 104 | (every? int? problems) 105 | (contains? #{1 3 5} (count problems))) 106 | {:language language, 107 | :dictionary dict, 108 | :mode :before-start-selected, 109 | :time 0, 110 | :problem-numbers problems}))))) 111 | 112 | (defn expected-letters 113 | [{[a b c] :horiz}] 114 | (str (subs a 1 4) (subs b 1 4) (subs c 1 4))) 115 | 116 | (defn success? 117 | ([db] 118 | (reduce #(and %1 %2) 119 | (map (partial success? db) 120 | (range (count (:zag db)))))) 121 | ([db i] 122 | (let [problem (get-in db [:zag i])] 123 | (= (expected-letters problem) (:fill problem))))) 124 | 125 | (defn return-letter [db] 126 | (let [{:keys [diagram pos]} (:dragged-letter db)] 127 | (if diagram 128 | (let [iword (get-in db [:compat diagram]) 129 | word (get-in db [:words iword :full]) 130 | aword (get-in db [:words iword :available]) 131 | w (get-in db [:zag diagram :fill]) 132 | c (nth w pos) 133 | i (first (filter #(and (= (nth word %) c) (= (nth aword %) ".")) 134 | (range 9))) 135 | new-fill (substitute-letter (get-in db [:zag diagram :fill]) pos ".")] 136 | (-> db 137 | (assoc-in [:zag diagram :fill] new-fill) 138 | (update-in [:words iword :available] 139 | #(substitute-letter % i c)) 140 | (update-in [:compat diagram] 141 | (if (= new-fill ".........") (constantly nil) identity)) 142 | (dissoc :dragged-letter))) 143 | (dissoc db :dragged-letter)))) 144 | 145 | (defn leading-zero 146 | [s] 147 | (if (< s 10) 148 | (str "0" s) 149 | (str s))) 150 | 151 | (defn format-time [t] 152 | (str (leading-zero (int (/ t 60))) ":" (leading-zero (mod t 60)))) 153 | 154 | (defn share-message [{lang :language, time :time, hints :hints, :as db} url] 155 | (str (msg db :share-msg1) 156 | (msg db (case (-> db :zag count) 157 | 1 :share-easy 158 | 3 :share-medium 159 | :share-hard)) 160 | (msg db :share-msg2) 161 | (format-time time) 162 | (cond 163 | (and (= lang :pl) (zero? hints)) ", nie wykorzystując podpowiedzi!" 164 | (and (= lang :pl) (= hints 1)) ", wykorzystując 1 podpowiedź." 165 | (= lang :pl) (str ", wykorzystując " hints " podpowiedzi.") 166 | (and (= lang :en) (zero? hints)) ", not using any hints!" 167 | (and (= lang :en) (= hints 1)) ", using 1 hint." 168 | (= lang :en) (str ", using " hints " hints.")) 169 | (msg db :share-msg3) 170 | (string/join ", " (map #(string/upper-case (:full %)) (:words db))) 171 | "\n\n" 172 | url)) 173 | -------------------------------------------------------------------------------- /src/c/generate.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include "hashset.h" 9 | 10 | const char pl_letters[] = "ąćęłńóśźż"; 11 | const int NUM_LETTERS = 32; 12 | const int ASCII_LETTERS = 23; 13 | 14 | uint64_t (*encode)(const char *, size_t); 15 | const char *(*decode)(uint64_t, size_t); 16 | 17 | uint64_t encode_pl(const char *s, size_t sz) { 18 | uint64_t res = 0; 19 | while (sz) { 20 | uint64_t x; 21 | if (*s >= 'a' && *s <= 'z') { 22 | x = *s - 'a'; 23 | if (*s > 'q') x--; 24 | if (*s > 'v') x--; 25 | if (*s > 'x') x--; 26 | s++; 27 | } else { 28 | const uint16_t c = *(const uint16_t *)s; 29 | const uint16_t *l = (const uint16_t *)pl_letters; 30 | x = ASCII_LETTERS; 31 | while (x < NUM_LETTERS && *l != c) l++, x++; 32 | if (x == NUM_LETTERS) return -1; 33 | 34 | s += 2; 35 | } 36 | res = NUM_LETTERS * res + x; 37 | sz--; 38 | } 39 | return res; 40 | } 41 | 42 | uint64_t encode_en(const char *s, size_t sz) { 43 | uint64_t res = 0; 44 | while (sz) { 45 | uint64_t x = *s++ - 'a'; 46 | res = NUM_LETTERS * res + x; 47 | sz--; 48 | } 49 | return res; 50 | } 51 | 52 | #define N 20 53 | 54 | const char *decode_pl(uint64_t num, size_t sz) { 55 | static char res[N]; 56 | res[N - 1] = 0; 57 | char *s = res + N - 2; 58 | while (sz--) { 59 | int x = num % NUM_LETTERS; 60 | if (x < ASCII_LETTERS) { 61 | *s = x + 'a'; 62 | if (x > 15) (*s)++; 63 | if (x > 19) (*s)++; 64 | if (x > 20) (*s)++; 65 | s--; 66 | } else { 67 | const char *u = pl_letters + 2 * (x - ASCII_LETTERS); 68 | *s = *(u + 1); 69 | *(s - 1) = *u; 70 | s -= 2; 71 | } 72 | num /= NUM_LETTERS; 73 | } 74 | return s + 1; 75 | } 76 | 77 | const char *decode_en(uint64_t num, size_t sz) { 78 | static char res[N]; 79 | res[N - 1] = 0; 80 | char *s = res + N - 2; 81 | while (sz--) { 82 | int x = num % NUM_LETTERS; 83 | *s-- = x + 'a'; 84 | num /= NUM_LETTERS; 85 | } 86 | return s + 1; 87 | } 88 | 89 | int cmp(const void *x, const void *y) { 90 | return *((const char *)x) - *((const char *)y); 91 | } 92 | 93 | uint64_t sortnum(uint64_t num) { 94 | static char arr[N]; 95 | char *s = arr; 96 | size_t n = 0; 97 | while (num) { 98 | *s++ = num % NUM_LETTERS; 99 | num /= NUM_LETTERS; 100 | n++; 101 | } 102 | qsort(arr, n, 1, cmp); 103 | s = arr; 104 | while (n--) { 105 | num = num * NUM_LETTERS + *s++; 106 | } 107 | return num; 108 | } 109 | 110 | void unextchar(const char **s) { 111 | unsigned char c = (unsigned char)**s; 112 | if ((c & 0x80) == 0) *s += 1; 113 | else if ((c & 0xE0) == 0xC0) *s += 2; 114 | else if ((c & 0xF0) == 0xE0) *s += 3; 115 | else if ((c & 0xF8) == 0xF0) *s += 4; 116 | } 117 | 118 | size_t ustrlen(const char *s, char terminator) { 119 | size_t len = 0; 120 | while (*s != terminator) { 121 | len++; 122 | unextchar(&s); 123 | } 124 | return len; 125 | } 126 | 127 | void *slurp(const char *filename, size_t *sizeptr) { 128 | struct stat statbuf; 129 | size_t size; 130 | char *buf; 131 | size_t done = 0, todo; 132 | FILE *f; 133 | 134 | if (stat(filename, &statbuf) == -1) { 135 | perror(filename); 136 | exit(1); 137 | } 138 | 139 | size = (size_t)statbuf.st_size; 140 | todo = size; 141 | 142 | buf = (char *)malloc(size + 1); 143 | if (buf == NULL) { 144 | fprintf(stderr, "Out of memory\n"); 145 | exit(1); 146 | } 147 | 148 | f = fopen(filename, "rb"); 149 | if (f == NULL) { 150 | perror(filename); 151 | exit(1); 152 | } 153 | 154 | while (todo) { 155 | size_t numread = fread(buf + done, 1, todo, f); 156 | if (numread == 0) { 157 | fprintf(stderr, "Error reading %s\n", filename); 158 | exit(1); 159 | } 160 | done += numread; 161 | todo -= numread; 162 | } 163 | buf[size] = 0; 164 | 165 | *sizeptr = size; 166 | return (void *)buf; 167 | } 168 | 169 | void print_base(char *nines, uint64_t w9) { 170 | char *wordstmp = nines; 171 | int found = 0; 172 | while (*wordstmp) { 173 | char *next = strchr(wordstmp, '\n'); 174 | if (sortnum(encode(wordstmp, 9)) == w9) { 175 | if (found) putchar(';'); 176 | found = 1; 177 | char tmp = *next; 178 | *next = 0; 179 | printf("%s", wordstmp); 180 | *next = tmp; 181 | }; 182 | wordstmp = next; 183 | if (wordstmp) 184 | wordstmp++; 185 | } 186 | } 187 | 188 | const char *wputchar(const char *s) { 189 | const char *tmp = s; 190 | unextchar(&tmp); 191 | while (s < tmp) putchar(*s++); 192 | return tmp; 193 | } 194 | 195 | void print_matches(char *words, char *nines, uint64_t w3) { 196 | char *wordstmp = words; 197 | int found = 0; 198 | putchar(','); 199 | while (wordstmp != nines) { 200 | char *next = strchr(wordstmp, '\n'); 201 | char *letter = wordstmp; 202 | unextchar((const char **)&letter); 203 | uint64_t w = encode(letter, 3); 204 | if (w == w3) { 205 | if (found) putchar(';'); 206 | found = 1; 207 | wputchar(wordstmp); 208 | unextchar((const char **)&letter); 209 | unextchar((const char **)&letter); 210 | unextchar((const char **)&letter); 211 | wputchar(letter); 212 | } 213 | wordstmp = next; 214 | if (wordstmp) 215 | wordstmp++; 216 | } 217 | } 218 | 219 | int main(int argc, char **argv) { 220 | FILE *f; 221 | char s[20], *words, *wordstmp, *nines; 222 | uint64_t w3[30000]; 223 | hashset_t h3 = hashset_create(); 224 | hashset_t h9 = hashset_create(); 225 | size_t i = 0, n = 0, j, k, a = 0, filesize, prevlen; 226 | if (argc < 3) { 227 | fprintf(stderr, "Usage: %s {pl|en} \n", argv[0]); 228 | return 1; 229 | } 230 | 231 | if (strcmp(argv[1], "en") == 0) { 232 | encode = encode_en; 233 | decode = decode_en; 234 | } else { 235 | encode = encode_pl; 236 | decode = decode_pl; 237 | } 238 | 239 | words = (char *)slurp(argv[2], &filesize); 240 | 241 | wordstmp = words; 242 | prevlen = 0; 243 | while (*wordstmp) { 244 | char *next = strchr(wordstmp, '\n'); 245 | size_t len = ustrlen(wordstmp, '\n'); 246 | if (len == 5) { 247 | uint64_t w; 248 | unextchar((const char **)&wordstmp); 249 | w = encode(wordstmp, 3); 250 | if (!hashset_is_member(h3, (void *)w)) 251 | w3[n++] = w; 252 | hashset_add(h3, (void *)w); 253 | } else if (len == 9) { 254 | if (prevlen == 5) 255 | nines = wordstmp; 256 | hashset_add(h9, (void *)sortnum(encode(wordstmp, 9))); 257 | }; 258 | prevlen = len; 259 | wordstmp = next; 260 | if (wordstmp) 261 | wordstmp++; 262 | } 263 | 264 | for (i = 0; i < n; i++) { 265 | uint64_t wh1 = w3[i]; 266 | for (j = 0; j < n; j++) { 267 | uint64_t wh2 = w3[j]; 268 | for (k = 0; k < n; k++) { 269 | uint64_t wh3 = w3[k]; 270 | uint64_t 271 | wv1 = 272 | ((wh1 >> 10) << 10) + 273 | ((wh2 >> 10) << 5) + 274 | (wh3 >> 10), 275 | wv2 = 276 | (((wh1 >> 5) & 31) << 10) + 277 | (((wh2 >> 5) & 31) << 5) + 278 | ((wh3 >> 5) & 31), 279 | wv3 = 280 | ((wh1 & 31) << 10) + 281 | ((wh2 & 31) << 5) + 282 | (wh3 & 31), 283 | all = sortnum((wh1 << 30) + (wh2 << 15) + wh3); 284 | if (hashset_is_member(h3, (void *)wv1) && 285 | hashset_is_member(h3, (void *)wv2) && 286 | hashset_is_member(h3, (void *)wv3) && 287 | hashset_is_member(h9, (void *)all)) 288 | { 289 | printf("%s", decode(wh1, 3)); 290 | printf("%s", decode(wh2, 3)); 291 | printf("%s", decode(wh3, 3)); 292 | putchar(','); 293 | print_base(nines, all); 294 | print_matches(words, nines, wh1); 295 | print_matches(words, nines, wh2); 296 | print_matches(words, nines, wh3); 297 | print_matches(words, nines, wv1); 298 | print_matches(words, nines, wv2); 299 | print_matches(words, nines, wv3); 300 | putchar('\n'); 301 | } 302 | a++; 303 | } 304 | } 305 | } 306 | } 307 | -------------------------------------------------------------------------------- /src/sass/app.sass: -------------------------------------------------------------------------------- 1 | // https://www.colourlovers.com/palette/44749/Valiant 2 | 3 | $desktop: 960px 4 | 5 | =mobile 6 | @media screen and (max-width: $desktop - 1px) 7 | @content 8 | 9 | =desktop 10 | @media screen and (min-width: $desktop) 11 | @content 12 | 13 | $purple: #7F4B61 14 | $greenish: #A5AC82 15 | $pale: #F5EBB0 16 | $green: #ACB852 17 | $maroon: #4C4028 18 | $white: lighten($pale, 10) 19 | $grey: lighten($maroon, 40) 20 | $pale-green: lighten($green, 20) 21 | $paler-green: lighten($green, 30) 22 | $pale-greenish: lighten($greenish, 20) 23 | 24 | html, body 25 | margin: 0 26 | font-family: Helvetica, Arial, sans-serif 27 | 28 | html 29 | height: 100% 30 | 31 | body, #app 32 | height: 100% 33 | color: $maroon 34 | 35 | a 36 | color: $purple 37 | 38 | h3 39 | font-size: 20px 40 | line-height: 1.25 41 | 42 | h3, p 43 | margin: 0 44 | 45 | h3:not(:last-child), p:not(:last-child) 46 | margin-bottom: 12px 47 | 48 | .is-hidden-mobile 49 | +mobile 50 | display: none 51 | 52 | .root 53 | min-height: 100% 54 | position: relative 55 | display: flex 56 | flex-direction: column 57 | 58 | .title-bar 59 | flex: 0 0 48px 60 | background-color: $purple 61 | color: $white 62 | +desktop 63 | padding-left: 24px 64 | font-size: 24px 65 | line-height: 48px 66 | +mobile 67 | flex: 0 0 30px 68 | padding-left: 12px 69 | font-size: 18px 70 | line-height: 30px 71 | 72 | .main-panel 73 | flex: 1 74 | display: flex 75 | justify-content: center 76 | align-items: center 77 | background-color: $pale 78 | padding: 24px 0 79 | +mobile 80 | padding: 12px 0 81 | 82 | .panel 83 | display: flex 84 | flex-direction: column 85 | text-align: center 86 | 87 | .game 88 | user-select: none 89 | -moz-user-select: none 90 | 91 | .button 92 | display: inline-block 93 | margin: 0 6px 94 | text-decoration: none 95 | background-color: $purple 96 | color: $white 97 | +desktop 98 | height: 48px 99 | font-size: 20px 100 | line-height: 48px 101 | width: 150px 102 | +mobile 103 | height: 32px 104 | font-size: 16px 105 | line-height: 32px 106 | width: 120px 107 | border: none 108 | border-radius: 4px 109 | box-shadow: none 110 | -webkit-appearance: none 111 | &:focus 112 | outline: none 113 | &:hover 114 | background-color: lighten($purple, 10) 115 | cursor: pointer 116 | 117 | $language-icon-size: 50px 118 | 119 | .language-picker 120 | img 121 | width: $language-icon-size 122 | height: $language-icon-size 123 | opacity: 0.4 124 | &:hover 125 | cursor: pointer 126 | &:not(:last-child) 127 | margin-right: 20px 128 | &.selected 129 | border-radius: $language-icon-size / 2 130 | box-shadow: 0 0 10px $purple 131 | opacity: 1 132 | 133 | .welcome 134 | text-align: center 135 | .title 136 | font-size: 72px 137 | line-height: 1.5 138 | 139 | .how-to-play 140 | align-self: start 141 | padding: 0 20px 20px 20px 142 | max-width: 600px 143 | .nines-area 144 | margin-top: 18px 145 | svg 146 | stroke: $grey 147 | display: inline-block 148 | width: 20px 149 | height: 20px 150 | +mobile 151 | width: 16px 152 | height: 16px 153 | button 154 | margin: 0 155 | p 156 | margin: 12px 0 157 | line-height: 1.4 158 | +desktop 159 | font-size: 20px 160 | 161 | $tile-size: 40px 162 | $mobile-tile-size: 24px 163 | $border-size: 1px 164 | $grid-item-padding: 3px 165 | $grid-item-size: $tile-size + ($grid-item-padding) * 2 166 | $letter-size: 3/4 * $tile-size 167 | $mobile-grid-item-size: $mobile-tile-size + ($grid-item-padding) * 2 168 | $mobile-letter-size: 3/4 * $mobile-tile-size 169 | $mobile-small-tile-size: 9px 170 | $mobile-small-grid-item-size: 12px 171 | $mobile-small-letter-size: 9px 172 | 173 | .diagram 174 | display: inline-grid 175 | padding: 8px 176 | grid-template-columns: repeat(5, $grid-item-size) 177 | grid-template-rows: repeat(5, $grid-item-size) 178 | +mobile 179 | grid-template-columns: repeat(5, $mobile-grid-item-size) 180 | grid-template-rows: repeat(5, $mobile-grid-item-size) 181 | grid-gap: $border-size 182 | box-sizing: border-box 183 | 184 | .diagram-small 185 | +mobile 186 | grid-template-columns: repeat(5, $mobile-small-grid-item-size) 187 | grid-template-rows: repeat(5, $mobile-small-grid-item-size) 188 | 189 | .diagram-finished 190 | .diagram-item 191 | text-stroke: 1px white 192 | text-shadow: 0 0 10px white 193 | background: $pale-green 194 | .tile 195 | cursor: default 196 | background: none 197 | border: none 198 | 199 | .diagram-item 200 | cursor: default 201 | background: $white 202 | display: flex 203 | justify-content: center 204 | align-items: center 205 | box-shadow: 0 0 0 $border-size $maroon 206 | 207 | .diagram-highlighted .diagram-item 208 | background: $paler-green 209 | 210 | @mixin highlighted 211 | background: $greenish !important 212 | transition: background 200ms 213 | 214 | .diagram-item-highlighted 215 | @include highlighted 216 | 217 | .diagram-item-corner 218 | visibility: hidden 219 | 220 | .reload 221 | display: flex 222 | justify-content: center 223 | align-items: center 224 | cursor: pointer 225 | svg 226 | display: block 227 | width: $letter-size 228 | height: $letter-size 229 | +mobile 230 | width: $mobile-letter-size 231 | height: $mobile-letter-size 232 | stroke: $grey 233 | &:hover 234 | svg 235 | stroke: $maroon 236 | 237 | .diagram-item-border 238 | font-weight: 700 239 | font-size: $letter-size 240 | text-transform: uppercase 241 | +mobile 242 | font-size: $mobile-letter-size 243 | 244 | .diagram-small .diagram-item-border 245 | +mobile 246 | font-size: $mobile-small-letter-size 247 | 248 | .tile 249 | cursor: pointer 250 | display: inline-block 251 | width: $tile-size 252 | height: $tile-size 253 | line-height: $tile-size 254 | font-size: $letter-size 255 | +mobile 256 | width: $mobile-tile-size 257 | height: $mobile-tile-size 258 | line-height: $mobile-tile-size 259 | font-size: $mobile-letter-size 260 | box-sizing: border-box 261 | background: $pale-greenish 262 | border: 1px solid $greenish 263 | border-radius: 6px 264 | text-align: center 265 | font-weight: 700 266 | text-transform: uppercase 267 | 268 | .diagram-small .tile 269 | +mobile 270 | width: $mobile-small-tile-size 271 | height: $mobile-small-tile-size 272 | line-height: $mobile-small-tile-size 273 | font-size: $mobile-small-letter-size 274 | border-radius: 1px 275 | 276 | .diagram-small .reload 277 | +mobile 278 | visibility: hidden 279 | 280 | .tile-hidden 281 | transform: translateX(-9999px) 282 | 283 | .diagrams-area 284 | margin-bottom: 10px 285 | display: flex 286 | flex-wrap: wrap 287 | justify-content: center 288 | 289 | .diagram-current 290 | order: 5 291 | 292 | .nines-area 293 | margin: 0 auto 294 | padding: 12px 295 | width: 9 * $tile-size + 12px 296 | +mobile 297 | width: 100% 298 | display: inline-block 299 | background: $white 300 | border: 1px solid black 301 | +mobile 302 | border-left: none 303 | border-right: none 304 | padding: 6px 0 305 | .nine 306 | display: flex 307 | align-items: center 308 | margin-bottom: 0 309 | +mobile 310 | width: 9 * $mobile-tile-size 311 | margin: 0 auto 312 | .tile 313 | margin: 0 314 | 315 | .nines-area-highlighted 316 | @include highlighted 317 | 318 | .nine 319 | padding: 5px 5px 320 | height: $tile-size + 5px 321 | +mobile 322 | padding: 2px 2px 323 | height: $mobile-tile-size + 2px 324 | margin-bottom: 10px 325 | text-align: left 326 | 327 | .author 328 | position: absolute 329 | bottom: 0px 330 | left: 6px 331 | 332 | .difficulty 333 | +mobile 334 | padding-bottom: 24px 335 | h2 336 | margin: 0 337 | 338 | .dictionaries 339 | 340 | .dictionary 341 | vertical-align: middle 342 | display: inline-block 343 | border: 1px solid $purple 344 | border-radius: 18px 345 | width: 80% 346 | +desktop 347 | width: 200px 348 | height: 200px 349 | padding: 18px 350 | margin: 18px 351 | +mobile 352 | padding: 12px 353 | margin: 12px 354 | transition: background-color 0.2s 355 | cursor: pointer 356 | &.selected, &:hover 357 | background-color: $purple 358 | color: $white 359 | a 360 | color: $white 361 | 362 | .success 363 | h1 364 | display: block 365 | margin: 0 366 | font-size: 150px 367 | +mobile 368 | font-size: 80px 369 | text-shadow: 0 0 30px $maroon 370 | animation: tilt 2s ease infinite 0s 371 | 372 | .buttons 373 | margin-top: 18px 374 | text-align: center 375 | +mobile 376 | .button 377 | display: inline-block 378 | &:not(:last-child) 379 | margin-bottom: 6px 380 | 381 | .tile-current 382 | transform: scale(1.5,1.5) 383 | 384 | .toast 385 | position: absolute 386 | top: -100px 387 | width: 50% 388 | left: 0 389 | right: 0 390 | margin: 0 auto 391 | height: 50px 392 | background: $purple 393 | color: $white 394 | font-size: 20px 395 | line-height: 1.25 396 | display: grid 397 | place-content: center 398 | transition: top 0.6s 399 | +mobile 400 | width: 90% 401 | 402 | .toast.shown 403 | top: 100px 404 | 405 | @keyframes tilt 406 | 0% { transform: rotate(20deg); } 407 | 25% { transform: rotate(0); } 408 | 50% { transform: rotate(-20deg); } 409 | 75% { transform: rotate(0); } 410 | 100% { transform: rotate(20deg); } 411 | -------------------------------------------------------------------------------- /src/cljs/gridlock/events.cljs: -------------------------------------------------------------------------------- 1 | (ns gridlock.events 2 | (:require 3 | [ajax.core :as ajax] 4 | [clojure.string :as string] 5 | [day8.re-frame.http-fx] 6 | [gridlock.db :as db] 7 | [re-frame.core :refer [dispatch reg-event-db reg-event-fx reg-fx]])) 8 | 9 | (reg-event-db 10 | :start-drag 11 | (fn [db [_ dragged-letter]] 12 | (assoc db :dragging dragged-letter))) 13 | 14 | (reg-event-db 15 | :end-drag 16 | (fn [db _] 17 | (assoc db :dragging nil))) 18 | 19 | (reg-event-fx 20 | :fetch-dictionary 21 | (fn [{db :db} [_ dict]] 22 | (if (get-in db [:dictionaries dict]) 23 | {} 24 | {:http-xhrio {:method :get 25 | :uri (str "puzzles/" (name dict) ".txt") 26 | :timeout 10000 27 | :response-format (ajax/text-response-format) 28 | :on-success [:fetch-dictionary-success dict]}}))) 29 | 30 | (reg-event-db 31 | :fetch-dictionary-success 32 | (fn [db [_ dict-name dict]] 33 | (assoc-in db [:dictionaries dict-name] (string/split dict #"\n")))) 34 | 35 | (defn letter-at 36 | [db {:keys [diagram-number nine-number letter-pos]}] 37 | (cond 38 | diagram-number (get-in db [:zag diagram-number :fill letter-pos]) 39 | nine-number (get-in db [:words nine-number :available letter-pos]))) 40 | 41 | (defn move-ok? 42 | [db source target] 43 | true) 44 | 45 | (defn remove-from 46 | [db {:keys [diagram-number nine-number letter-pos] :as args}] 47 | (cond 48 | nine-number (update-in db [:words nine-number :available] db/substitute-letter letter-pos ".") 49 | diagram-number (update-in db [:zag diagram-number :fill] db/substitute-letter letter-pos ".") 50 | :otherwise db)) 51 | 52 | (defn insert-to-diagram 53 | [db {:keys [diagram-number letter-pos] :as args} letter] 54 | (update-in db [:zag diagram-number :fill] db/substitute-letter letter-pos letter)) 55 | 56 | (defn find-place-for-letter 57 | [{:keys [full available]} letter] 58 | (first 59 | (for [letter-number (range (count full)) 60 | :when (and (= (.charAt full letter-number) letter) 61 | (= (.charAt available letter-number) "."))] 62 | letter-number))) 63 | 64 | (defn insert-to-nines 65 | [db nine-number letter] 66 | (if-let [letter-pos (find-place-for-letter (get-in db [:words nine-number]) letter)] 67 | (update-in db [:words nine-number :available] db/substitute-letter letter-pos letter) 68 | db)) 69 | 70 | (defn update-compat 71 | [db {source-nine :nine-number} {target-diagram :diagram-number}] 72 | (if source-nine 73 | (update db :compat assoc target-diagram source-nine) 74 | db)) 75 | 76 | (defn remove-compat-if-needed 77 | [db source-diagram target-nine] 78 | (let [{:keys [full available]} (get-in db [:words target-nine])] 79 | (if (= full available) 80 | (update db :compat dissoc source-diagram) 81 | db))) 82 | 83 | (defn check-success [db] 84 | (if (db/success? db) 85 | (assoc db :mode :success) 86 | db)) 87 | 88 | (defn move-to-diagram [db source target] 89 | (let [letter (letter-at db source)] 90 | (if (move-ok? db source target) 91 | (-> db 92 | (remove-from source) 93 | (insert-to-diagram target letter) 94 | (update-compat source target) 95 | (check-success)) 96 | db))) 97 | 98 | (reg-event-db 99 | :drop-diagram 100 | (fn [db [_ target]] 101 | (let [source (:dragging db)] 102 | (move-to-diagram db source target)))) 103 | 104 | (defn move-to-nines [db source] 105 | (let [source-diagram (:diagram-number source) 106 | target-nine (get-in db [:compat source-diagram]) 107 | letter (letter-at db source)] 108 | (if-not source-diagram 109 | db 110 | (-> db 111 | (remove-from source) 112 | (insert-to-nines target-nine letter) 113 | (remove-compat-if-needed source-diagram target-nine))))) 114 | 115 | (defn clean-diagram [db diagram-number] 116 | (if-let [nine-number (get-in db [:compat diagram-number])] 117 | (-> db 118 | (update-in [:words nine-number] #(assoc % :available (:full %))) 119 | (assoc-in [:zag diagram-number :fill] ".........") 120 | (update :compat dissoc diagram-number)) 121 | db)) 122 | 123 | (reg-event-db 124 | :clean-diagram 125 | (fn [db [_ diagram-number]] 126 | (clean-diagram db diagram-number))) 127 | 128 | (reg-event-db 129 | :drop-nines 130 | (fn [db _] 131 | (move-to-nines db (:dragging db)))) 132 | 133 | (defn find-target 134 | [problem letter] 135 | (let [fill (:fill problem) 136 | expected-letters (db/expected-letters problem)] 137 | (first 138 | (for [[i candidate] (map-indexed vector expected-letters) 139 | :when (and (= candidate letter) (= (nth fill i) "."))] 140 | i)))) 141 | 142 | (defn all-letters-for-hint [db] 143 | (for [[nine-number nine] (map-indexed vector (:words db)) 144 | [letter-pos letter] (map-indexed vector (:available nine)) 145 | :when (not= letter ".")] 146 | [{:nine-number nine-number, :letter-pos letter-pos} 147 | {:diagram-number (:diagram-number nine), :letter-pos (find-target (get-in db [:zag (:diagram-number nine)]) letter)}])) 148 | 149 | (defn all-wrong-letters [db] 150 | (for [[diagram-number problem] (map-indexed vector (:zag db)) 151 | :let [expected (db/expected-letters problem) 152 | fill (:fill problem)] 153 | letter-pos (range 9) 154 | :when (and (not= (nth fill letter-pos) ".") 155 | (or (not= (nth fill letter-pos) (nth expected letter-pos)) 156 | (when-let [word-number (get-in db [:compat diagram-number])] 157 | (not= diagram-number (get-in db [:words word-number :diagram-number])))))] 158 | {:diagram-number diagram-number, :letter-pos letter-pos})) 159 | 160 | (defn apply-hint 161 | [db] 162 | (if-let [wrong (seq (all-wrong-letters db))] 163 | (move-to-nines db (rand-nth wrong)) 164 | (let [[source target] (rand-nth (all-letters-for-hint db))] 165 | (move-to-diagram db source target)))) 166 | 167 | (reg-event-db 168 | :hint 169 | (fn [db _] 170 | (-> db 171 | apply-hint 172 | (update :hints inc)))) 173 | 174 | (reg-event-fx 175 | :fetch-dictionaries 176 | (fn [{db :db} _] 177 | {:dispatch-n (case (:language db) 178 | :pl [[:fetch-dictionary :osps] 179 | [:fetch-dictionary :nkjp]] 180 | :en [[:fetch-dictionary :en]])})) 181 | 182 | (def default-dictionary 183 | {:pl :nkjp, :en :en}) 184 | 185 | (reg-event-fx 186 | :init 187 | (fn [_ _] 188 | (let [language (if (= js/window.navigator.language "pl") :pl :en) 189 | url (subs js/window.location.hash 1)] 190 | {:db (or (db/parse-url url) 191 | {:language language 192 | :mode :before-start 193 | :time 0 194 | :dictionary (default-dictionary language)}) 195 | :dispatch [:fetch-dictionaries]}))) 196 | 197 | (reg-event-db 198 | :select-difficulty 199 | (fn [db _] 200 | (assoc db :mode :difficulty))) 201 | 202 | (reg-fx 203 | :set-url-hash 204 | (fn [hash] 205 | (set! js/window.location.hash hash))) 206 | 207 | (reg-event-fx 208 | :start 209 | (fn [{db :db} [_ n]] 210 | (let [prs (db/problems db (:dictionary db) n)] 211 | (merge (when-not (= (:mode db) :in-progress) 212 | {:dispatch [:tick]}) 213 | {:set-url-hash (:url prs) 214 | :db (merge db prs 215 | {:mode :in-progress 216 | :current-diagram 0 217 | :time 0 218 | :hints 0 219 | :compat {}})})))) 220 | 221 | (reg-event-fx 222 | :tick 223 | (fn [{db :db} _] 224 | (if (= (:mode db) :in-progress) 225 | {:db (update db :time inc) 226 | :dispatch-later [{:ms 1000 :dispatch [:tick]}]} 227 | {}))) 228 | 229 | (reg-event-db 230 | :reset 231 | (fn [db _] 232 | (assoc db 233 | :words (mapv #(assoc % :available (:full %)) (:words db)) 234 | :zag (mapv #(assoc % :fill ".........") (:zag db)) 235 | :compat {}))) 236 | 237 | (reg-event-fx 238 | :restart 239 | (fn [{db :db} _] 240 | {:set-url-hash "" 241 | :db (assoc db 242 | :mode :before-start 243 | :problem-numbers nil)})) 244 | 245 | (reg-event-db 246 | :go-back 247 | (fn [db _] 248 | (assoc db :mode 249 | (if (seq (:problem-numbers db)) 250 | :before-start-selected 251 | :before-start)))) 252 | 253 | (reg-fx 254 | :copy-to-clipboard 255 | (fn [{:keys [text on-success on-error]}] 256 | (-> (js/navigator.clipboard.writeText text) 257 | (.then on-success on-error)))) 258 | 259 | (reg-event-fx 260 | :share 261 | (fn [{db :db} _] 262 | {:copy-to-clipboard {:text (db/share-message db js/window.location) 263 | :on-success #(dispatch [:toast/show :copied-to-clipboard]) 264 | :on-error #(dispatch [:toast/show :not-copied-to-clipboard])}})) 265 | 266 | (reg-event-db 267 | :show-how-to-play 268 | (fn [db _] 269 | (assoc db :mode :how-to-play))) 270 | 271 | (reg-event-db 272 | :set-dictionary 273 | (fn [db [_ dict]] 274 | (assoc db :dictionary dict))) 275 | 276 | (reg-event-fx 277 | :set-language 278 | (fn [{db :db} [_ language]] 279 | {:db (assoc db 280 | :language language 281 | :dictionary (default-dictionary language)) 282 | :dispatch [:fetch-dictionaries]})) 283 | 284 | (reg-event-db 285 | :set-current-diagram 286 | (fn [db [_ diagram]] 287 | (assoc db :current-diagram diagram))) 288 | 289 | (reg-event-db 290 | :tap-tile 291 | (fn [db [_ tile-source]] 292 | (if (:diagram-number tile-source) 293 | (move-to-nines db tile-source) 294 | (assoc db :current-tile tile-source)))) 295 | 296 | (reg-event-db 297 | :tap-diagram 298 | (fn [db [_ target]] 299 | (let [source (:current-tile db)] 300 | (if (and source (= (:current-diagram db) (:diagram-number target))) 301 | (-> db 302 | (move-to-diagram source target) 303 | (assoc :current-tile nil)) 304 | db)))) 305 | 306 | (reg-event-fx 307 | :toast/show 308 | (fn [{db :db} [_ message]] 309 | {:db (assoc db 310 | :toast-shown? true 311 | :toast-message message) 312 | :dispatch-later {:ms 4000 :dispatch [:toast/hide]}})) 313 | 314 | (reg-event-db 315 | :toast/hide 316 | (fn [db _] 317 | (assoc db :toast-shown? false))) 318 | -------------------------------------------------------------------------------- /resources/public/puzzles/nkjp.txt: -------------------------------------------------------------------------------- 1 | drepozinu,uprzednio,as,sa,ms,os,ft;go,js 2 | ktoramasi,amatorski,ar,ba,ba;gć;ka,en;sj,pi,kn 3 | ktorameda,demokrata,ar,ba,ml,os,so,rn 4 | ktoramade,demokrata,ar,ba,rk;żn,en;sj,so,dk;tk 5 | ktoratele,elektorat,ar,ka,jń,os,se,fl;hl;pm;tż 6 | ktorleeat,elektorat,ar,on,ba;tr,os,as,pa 7 | ktoraseri,orkiestra,ar,pa;ta,fe;sa;so,os,sa;se;st;sy,nć;za 8 | ktoorlren,kontroler,ar,wd,aa;ia;td,so,ss,dy;py;ry;we;wo;wy 9 | niotaserz,rozstanie,ał;un,pi,jy,ak;ir,ma;wa,kt;na 10 | nteiarosz,rozstanie,ak;ir,ma;wa,kt;na,ał;un,pi,jy 11 | tlarusacj,lustracja,as,py,ra,sż,kz,pa 12 | adatykrtu,dyktatura,bć;gć;nl;nć;pć;rr;zć,ea,ar,ty,ea,jb;zp 13 | androzawi,darowizna,ba;wa,ga;pa,bć;dd;jć,bk;bn;gż;kć;nz;ts;zz,zu,ge 14 | araferini,rafineria,bk;bn;gż;kć;nz;ts;zz,aa;sa,la,ma,aa;ia;td,ma 15 | aratalusi,australia,bk;bn;gż;kć;nz;ts;zz,se,dć;kć,tś,pa;ta,pć;wć 16 | aranowdzi,darowizna,bk;bn;gż;kć;nz;ts;zz,zu,ge,ba;wa,ga;pa,bć;dd;jć 17 | azaperimn,przemiana,br;kć,oa,ze;zo;zy,ns;nć;zs,cu,ky;me 18 | etoratkle,elektorat,bn,ka,sp,na,se,fl;hl;pm;tż 19 | etorasirk,orkiestra,bn,pa;ta,ma,fe;sa;so,sa;se;st;sy,bi 20 | etokaltre,elektorat,bn,sa,ss,sa,sa;se;st;sy,bć;kj;pk;wć 21 | etotalerk,elektorat,bn,se,na,pr,sa;se;st;sy,pa 22 | etotalkre,elektorat,bn,se,os,sa,sa;se;st;sy,bć;kj;pk;wć 23 | etotarkle,elektorat,bn,sa;se;st;sy,sp,sa,se,ka;wk 24 | iedonoław,odwołanie,ba;ky;mź,hr;pć,sa,zo,st,wa 25 | iedrzakon,nierzadko,ba;ky;mź,kk,ia,ma,sn,uy 26 | iledeaodn,oddalenie,bt,ił,ge;gy;my;wy,wk,śź,ss 27 | ladamagen,magdalena,by,łć,at,fa,js;zk,uy 28 | olewanami,malowanie,bć;kj;pk;wć,as,kl;rę,kl;nk;tr,pa,cć;ln;ts 29 | ramuligen,regulamin,ba,ja,at,di,mć;pc;wk;zw,ga 30 | rzeandkal,kalendarz,bg;kw;oc;oł;pd;pz,ba;wa,sa,tt,uć,we 31 | urasadzki,arkadiusz,bk,oa,sc,da;ma;ra,tt,ro 32 | uratalasi,australia,bk,se,ba;gć;ka,tj,pa;ta,pć;wć 33 | uratalisa,australia,bk,se,pć,pn,pa;ta,zć 34 | eniroszta,rozstanie,cć;ln;ts,gz,sb,jy,ca,pć 35 | orawagówn,równowaga,cz,ua,re;ro;ry,dd;pd,bo;po;py;ta,bo 36 | ytarenang,argentyna,ct;pć,aa;ia;td,ra,wz,aa,ra 37 | ytalekndi,delikatny,ct;pć,mo,ie,ty,wy,jś;jż 38 | ytatarudk,dyktatura,ct;pć,sa;se;st;sy,ba;da,tł,so,ja;ma 39 | zaridekon,nierzadko,ca;sy,lr;wo,ia,di,rm,aa;ia;td 40 | zecileena,zalecenie,ch;rz,bt,st,dń,cy,on 41 | anirensta,starannie,da;de;jk;ta;to;zm,aa;ia;td,uć;wć,mz,aa,fł 42 | anirasent,starannie,da;de;jk;ta;to;zm,pa;ta,ra,dk;mk,za;zy,la 43 | aregatist,strategia,dk;mk,aa,la,ma,pa;ta,go;no 44 | arekonowi,rokowanie,dk;mk,ia,łć,jś;zn,ka,cć;ln;ts 45 | arekorist,orkiestra,dk;mk,so,la,jś;jż,gz,ve 46 | arebożeni,obrażenie,dk;mk,ze,cć;ln;ts,kl,ft;go,rm 47 | arenanist,starannie,dk;mk,za;zy,la,da;de;jk;ta;to;zm,pa;ta,ra 48 | awarozind,darowizna,dć;kł,ga;pa,wa,ma,ia,ja 49 | awakarini,kawiarnia,dć;kł,sb,la,jś;jż,as,ma 50 | awarakini,kawiarnia,dć;kł,tt,la,ma,as,jś;jż 51 | ietoraskr,orkiestra,da,cz,ia,kk,na,sa;se;st;sy 52 | olawanemi,malowanie,dr;pk,as,rs,rr,pa,da;de;jk;ta;to;zm 53 | owórawagn,równowaga,dd;pd,bo;po;py;ta,bo,cz,ua,re;ro;ry 54 | owówaragn,równowaga,dd;pd,ga;tz,bo,kl;nk;tr,ua,gy 55 | rugalemin,regulamin,di,mć;pc;wk;zw,ga,ba,ja,at 56 | uszrakadi,arkadiusz,da;ma;ra,tt,ro,bk,oa,sc 57 | uszstakar,staruszka,da;ma;ra,uć;wć,sb,ri,uć;wć,ca;sy 58 | ziawanesk,wskazanie,dd;dć;dł,as,da,sd,pt,ha;ja 59 | ziaiedeln,niedziela,dd;dć;dł,ba;ky;mź,cy,dń,we,ra;ry;ły 60 | zieelncea,zalecenie,dń,cy,on,ch;rz,bt,st 61 | zieilkeks,kieliszek,dń,ka,tt,dń,ka,tt 62 | zieratosn,rozstanie,dń,ka,sa,wk,pt,li 63 | zielażane,zażalenie,dń,pa,jk;pl;rk,sk,mo;so,lć 64 | zieieladn,niedziela,dń,we,ra;ry;ły,dd;dć;dł,ba;ky;mź,cy 65 | zikadoren,nierzadko,di,rm,aa;ia;td,ca;sy,lr;wo,ia 66 | kratasomi,amatorski,en;sj,pi,kn,ar,ba,ba;gć;ka 67 | kratadome,demokrata,en;sj,so,dk;tk,ar,ba,rk;żn 68 | erikonazd,nierzadko,fe;sa;so,ia,ja,po,ga;pa,wa 69 | erikonkwi,kierownik,fe;sa;so,ia,tć,li;lo,ka,la 70 | eriratosk,orkiestra,fe;sa;so,ka,bi,pn,pa;ta,na 71 | eritanasn,starannie,fe;sa;so,sy,jo;jy,ml,pa;ta,wa;wy 72 | eritarosk,orkiestra,fe;sa;so,sa;se;st;sy,bi,bn,pa;ta,ma 73 | ilanetaku,aktualnie,fr,aa,jb;zp,fł,mo,tś 74 | inalekatu,aktualnie,fł,mo,tś,fr,aa,jb;zp 75 | inazepyta,zapytanie,fł,st,ct;pć,fk,aa,nd;zs;zł;łć 76 | izynetapa,zapytanie,fk,aa,nd;zs;zł;łć,fł,st,ct;pć 77 | izyewnlek,niezwykle,fk,pe;po;py,mo,we,sd,ra 78 | lagamedan,magdalena,fa,js;zk,uy,by,łć,at 79 | oteratelk,elektorat,fl;hl;pm;tż,ka,ba,ka;wk,se,sa 80 | otetalerk,elektorat,fl;hl;pm;tż,se,na,fl;hl;pm;tż,sa;se;st;sy,ba 81 | otetarelk,elektorat,fl;hl;pm;tż,sa;se;st;sy,ba,fl;hl;pm;tż,se,na 82 | otenanowi,notowanie,fl;hl;pm;tż,za;zy,łć,hr;pć,sa,cć;ln;ts 83 | dzionawar,darowizna,ge,pd,ga;tz,wa,uć,ma;wa 84 | nieotowan,notowanie,gw;śg,mr;pk,as,zu,wć,la 85 | odnwroiet,odwrotnie,ge;gy;my;wy,zt,da,łć,as,ca 86 | oniłotawk,kotłownia,gć,bo;zo;zy,da;ła,wć,zu,na 87 | oniwoneta,notowanie,gć,ia,ml,rr,ca,fł 88 | oniłodawe,odwołanie,gć,me;my,nt;pł;wl,wć,zu,lr;wo 89 | onibieact,obietnica,gć,og;uc;zg,fo,rk,mh;zz,da 90 | onipolawe,polowanie,gć,oe,nt;pł;wl,kć,zu,bt 91 | racajegen,generacja,gz;pa,nm,at,pa,nm,oa;sa 92 | rosywipie,piersiowy,gz,cl;rn;żć,uc;św,ga,łć,om 93 | rosakitre,orkiestra,gz,jś;jż,ss,ka,my,om 94 | rozarygen,rozegrany,ga;pa,mś;pż;zs,at,pa,ka;wk,sa 95 | rypowisie,piersiowy,ga,łć,om,gz,cl;rn;żć,uc;św 96 | uzisadkra,arkadiusz,gk,oa,en;sj,ri,ca;sy,wć 97 | waropanic,pracownia,ga;tz,kć,mh;zz,ia,ns;nć;zs,gz;pa 98 | ononitawe,notowanie,hr;pć,aa,nt;pł;wl,pd,żo,fl;hl;pm;tż 99 | onotaweni,notowanie,hr;pć,sa,cć;ln;ts,fl;hl;pm;tż,za;zy,łć 100 | ononiwate,notowanie,hr;pć,żo,zm,pd,aa,rr 101 | konortrle,kontroler,ia,pa,on,so,wd,ak;ir 102 | wonapirac,pracownia,ia,ns;nć;zs,gz;pa,ga;tz,kć,mh;zz 103 | akiwanari,kawiarnia,jś;jż,as,ma,dć;kł,sb,la 104 | akirosert,orkiestra,jś;jż,gz,ve,dk;mk,so,la 105 | akowoneri,rokowanie,jś;zn,ia,fe;sa;so,nt;pł;wl,so,gć 106 | akoroweni,rokowanie,jś;zn,ka,cć;ln;ts,dk;mk,ia,łć 107 | akonoweri,rokowanie,jś;zn,zu,fe;sa;so,jk;pl;rk,so,łć 108 | aneponawi,panowanie,jk;pl;rk,oa,bć;dd;jć,nd;zs;zł;łć,zu,cć;ln;ts 109 | aneponiwa,panowanie,jk;pl;rk,oa,kć,ns;nć;zs,zu,st 110 | anekorowi,rokowanie,jk;pl;rk,so,łć,jś;zn,zu,fe;sa;so 111 | erznotisa,rozstanie,jy,ca,pć,cć;ln;ts,gz,sb 112 | erzronaga,rozegrana,jy,ft;go,bż,tz,da;di;do;wi,uć 113 | erzkonida,nierzadko,jy,ia,wć,pn,ba;ua;śa,uć 114 | ezuminied,zdumienie,js,ga,ba;ky;mź,rs,dń,ra 115 | ulitenaka,aktualnie,ja,aa,nz;zz,tj,mo,fł 116 | ulitasara,australia,ja,pi,bk;bn;gż;kć;nz;ts;zz,tj,am,pć 117 | aberoneżi,obrażenie,kl,ft;go,rm,dk;mk,ze,cć;ln;ts 118 | amyniwono,anonimowy,kk,żo,hr;pć,kn,ga,żt 119 | anonitowe,notowanie,kn,aa,rr,kn,żo,fl;hl;pm;tż 120 | anominywo,anonimowy,kn,ga,żt,kk,żo,hr;pć 121 | anoniwote,notowanie,kn,żo,fl;hl;pm;tż,kn,aa,rr 122 | artslamow,malarstwo,ka;ma;na;wa;wo,im,ua,po,up,sa 123 | ciuianerk,ukrainiec,kk,mo;so,na,uc;śk,ma;wa,pt 124 | ioserktar,orkiestra,kk,na,sa;se;st;sy,da,cz,ia 125 | iosamktar,amatorski,kk,ra,sa;se;st;sy,wr,rn,ia 126 | iosamttan,natomiast,kk,to,sy,wr,rn,uy 127 | ontwiaeon,notowanie,ko,kt;śt,la,rr,ał;un,sy 128 | opawonile,polowanie,kć,ia,bt,łć,oe,jk;pl;rk 129 | opanowile,polowanie,kć,zu,bt,gć,oe,nt;pł;wl 130 | orekonawi,rokowanie,ka;wk,ia,bć;dd;jć,ll;pz,ka,cć;ln;ts 131 | oretaletk,elektorat,ka;wk,se,sa,fl;hl;pm;tż,ka,ba 132 | owalameni,malowanie,kl;nk;tr,pa,cć;ln;ts,bć;kj;pk;wć,as,kl;rę 133 | owawagórn,równowaga,kl;nk;tr,ua,gy,dd;pd,ga;tz,bo 134 | ratokrsie,orkiestra,ka,my,om,gz,jś;jż,ss 135 | wiaianark,kawiarnia,kt;śt,mo;so,ja;ma,kt;śt,ma;wa,ha;ja 136 | wiaiarank,kawiarnia,kt;śt,ma;wa,ha;ja,kt;śt,mo;so,ja;ma 137 | atalekinu,aktualnie,lć,mo,ms,pć;wć,aa,jb;zp 138 | ekkrowini,kierownik,li;lo,ka,la,fe;sa;so,ia,tć 139 | okawoneri,rokowanie,ll;pz,ia,fe;sa;so,rr,so,da;de;jk;ta;to;zm 140 | okaroweni,rokowanie,ll;pz,ka,cć;ln;ts,ka;wk,ia,bć;dd;jć 141 | ustszakar,staruszka,lr;pe;py,wk,sb,ri,wk,sa;se;st;sy 142 | afirenari,rafineria,ma,aa;ia;td,ma,bk;bn;gż;kć;nz;ts;zz,aa;sa,la 143 | agirasett,strategia,ma,pa;ta,go;no,dk;mk,aa,la 144 | alewanegi,ewangelia,mć;pc;wk;zw,as,la,nt;pł;wl,fa,cć;ln;ts 145 | ariwanaki,kawiarnia,ma,as,jś;jż,dć;kł,tt,la 146 | ariwonazd,darowizna,ma,ia,ja,dć;kł,ga;pa,wa 147 | arizosent,rozstanie,ma,sa,ra,rm,ft;go,la 148 | arsnetina,starannie,mz,aa,fł,da;de;jk;ta;to;zm,aa;ia;td,uć;wć 149 | arytaletn,teatralny,mś;pż;zs,se,li,zm,ka,ty 150 | etarasinn,starannie,ml,pa;ta,wa;wy,fe;sa;so,sy,jo;jy 151 | ianakiraw,kawiarnia,mo;so,jś;jż,bo;po;py;ta,ma;wa,nz;zz,żo 152 | iaremocen,ceremonia,ma;wa,dn,oa;sa,mz;nh;no;we,js;zk,ft;go 153 | iarakaniw,kawiarnia,ma;wa,nz;zz,żo,mo;so,jś;jż,bo;po;py;ta 154 | iecameron,ceremonia,mz;nh;no;we,js;zk,ft;go,ma;wa,dn,oa;sa 155 | irkezodan,nierzadko,ma,sn,uy,ba;ky;mź,kk,ia 156 | okrrloten,kontroler,my,up,aa,pa,sp,ft;go 157 | otowaneni,notowanie,mr;pk,as,cć;ln;ts,rr,sy,gć 158 | ylipisana,sypialnia,mć,sa,kł,sć,aa;ga,pć 159 | apawoneni,panowanie,nd;zs;zł;łć,ia,cć;ln;ts,nt;pł;wl,oa,da;de;jk;ta;to;zm 160 | apanoweni,panowanie,nd;zs;zł;łć,zu,cć;ln;ts,jk;pl;rk,oa,bć;dd;jć 161 | apizemarn,przemiana,ns;nć;zs,cu,ky;me,br;kć,oa,ze;zo;zy 162 | apiwonena,panowanie,ns;nć;zs,ia,st,nt;pł;wl,oa,fł 163 | apinowena,panowanie,ns;nć;zs,zu,st,jk;pl;rk,oa,kć 164 | awelageni,ewangelia,nt;pł;wl,fa,cć;ln;ts,mć;pc;wk;zw,as,la 165 | aweponani,panowanie,nt;pł;wl,oa,da;de;jk;ta;to;zm,nd;zs;zł;łć,ia,cć;ln;ts 166 | aweponina,panowanie,nt;pł;wl,oa,fł,ns;nć;zs,ia,st 167 | awelamoni,malowanie,nt;pł;wl,pa,gć,sn;wr,as,rs 168 | awekoroni,rokowanie,nt;pł;wl,so,gć,jś;zn,ia,fe;sa;so 169 | awydaniłk,wykładnia,nk,uy,pa,ro,gt,ra 170 | erktalote,elektorat,na,se,fl;hl;pm;tż,bn,ka,sp 171 | dpironezu,uprzednio,os,ft;go,js,as,sa,ms 172 | kretlaoet,elektorat,os,as,pa,ar,on,ba;tr 173 | kretadoma,demokrata,os,so,rn,ar,ba,ml 174 | kretalote,elektorat,os,se,fl;hl;pm;tż,ar,ka,jń 175 | kretarosi,orkiestra,os,sa;se;st;sy,nć;za,ar,pa;ta,fe;sa;so 176 | poniwaean,panowanie,oa,kć,ss,uc;św,kl;nk;tr,za;zy 177 | sietanarn,starannie,om,sy,ky;me,uć;wć,ma;wa,cy;sy 178 | alitenaku,aktualnie,pć;wć,aa,jb;zp,lć,mo,ms 179 | alitasura,australia,pć;wć,pi,bk,tś,am,pć 180 | asmrlotaw,malarstwo,po,up,sa,ka;ma;na;wa;wo,im,ua 181 | ekarozind,nierzadko,po,ga;pa,wa,fe;sa;so,ia,ja 182 | ekarowoni,rokowanie,po,ka,gć,pn,ia,bć;dd;jć 183 | ekirodzna,nierzadko,pn,ba;ua;śa,uć,jy,ia,wć 184 | ekirosart,orkiestra,pn,gz,ka;ma;na;wa;wo,tz,so,la 185 | ekirostra,orkiestra,pn,gz,sż,ve,so,pć 186 | ekirowona,rokowanie,pn,ka,pd,pn,ia,kć 187 | ekirasort,orkiestra,pn,pa;ta,pa,pn,sb,la 188 | erożebina,obrażenie,pn,ay,fł,rm,aa;ia;td,rk 189 | erokonawi,rokowanie,pn,ia,bć;dd;jć,po,ka,gć 190 | erokoniwa,rokowanie,pn,ia,kć,pn,ka,pd 191 | erorasitk,orkiestra,pn,pa;ta,na,fe;sa;so,ka,bi 192 | erokarist,orkiestra,pn,sb,la,pn,pa;ta,pa 193 | erotaletk,elektorat,pn,se,sa,pr,ka,pa 194 | eteratolk,elektorat,pr,ka,pa,pn,se,sa 195 | etetarolk,elektorat,pr,sa;se;st;sy,pa,bn,se,na 196 | iotskrare,orkiestra,pr,ia,dk;mk,pć,my,ss 197 | isaokrtre,orkiestra,pć,my,ss,pr,ia,dk;mk 198 | oliwanema,malowanie,ps,as,tt,rr,pa,fł 199 | onanitowe,notowanie,pd,aa,rr,hr;pć,żo,zm 200 | onaniwote,notowanie,pd,żo,fl;hl;pm;tż,hr;pć,aa,nt;pł;wl 201 | ortkleron,kontroler,pa,sp,ft;go,my,up,aa 202 | ragorezyn,rozegrany,pa,ka;wk,sa,ga;pa,mś;pż;zs,at 203 | ragajecen,generacja,pa,nm,oa;sa,gz;pa,nm,at 204 | ragadostr,starogard,pa,rm,oo;oy,pa;ta,no,aa 205 | rasadtgor,starogard,pa;ta,no,aa,pa,rm,oo;oy 206 | utirasala,australia,pn,pa;ta,zć,bk,se,pć 207 | adiwałynk,wykładnia,ro,gt,ra,nk,uy,pa 208 | azeronist,rozstanie,rm,ft;go,la,ma,sa,ra 209 | emizieund,zdumienie,rs,dń,ra,js,ga,ba;ky;mź 210 | eżirenoba,obrażenie,rm,aa;ia;td,rk,pn,ay,fł 211 | obaniciet,obietnica,rk,mh;zz,da,gć,og;uc;zg,fo 212 | oweniotan,notowanie,rr,ał;un,sy,ko,kt;śt,la 213 | owenotina,notowanie,rr,ca,fł,gć,ia,ml 214 | owelamani,malowanie,rr,pa,da;de;jk;ta;to;zm,dr;pk,as,rs 215 | owelamina,malowanie,rr,pa,fł,ps,as,tt 216 | owekorani,rokowanie,rr,so,da;de;jk;ta;to;zm,ll;pz,ia,fe;sa;so 217 | owetanoni,notowanie,rr,sy,gć,mr;pk,as,cć;ln;ts 218 | uskzarida,arkadiusz,ri,ca;sy,wć,gk,oa,en;sj 219 | uskstazar,staruszka,ri,uć;wć,ca;sy,da;ma;ra,uć;wć,sb 220 | uskszatar,staruszka,ri,wk,sa;se;st;sy,lr;pe;py,wk,sb 221 | ynekonawi,wykonanie,rk;sk,ia,bć;dd;jć,wz,zu,cć;ln;ts 222 | alowanemi,malowanie,sn;wr,as,rs,nt;pł;wl,pa,gć 223 | eandzilic,dzielnica,ss,ge,ua,we,kć,mh;zz 224 | ekttarole,elektorat,sa,sa;se;st;sy,bć;kj;pk;wć,bn,sa,ss 225 | etktalore,elektorat,sa,se,ka;wk,bn,sa;se;st;sy,sp 226 | etktarole,elektorat,sa,sa;se;st;sy,bć;kj;pk;wć,bn,se,os 227 | kortreoln,kontroler,so,ss,dy;py;ry;we;wo;wy,ar,wd,aa;ia;td 228 | kororlnte,kontroler,so,wd,ak;ir,ia,pa,on 229 | talakunie,aktualnie,se,jb;zp,gw;śg,sy,jś;jż,bs 230 | tanakilue,aktualnie,sy,jś;jż,bs,se,jb;zp,gw;śg 231 | tanowipie,powitanie,sy,łć,uc;św,sa,bć;dd;jć,gw;śg 232 | topawinie,powitanie,sa,bć;dd;jć,gw;śg,sy,łć,uc;św 233 | tralucasj,lustracja,sż,kz,pa,as,py,ra 234 | ypalinisa,sypialnia,sć,aa;ga,pć,mć,sa,kł 235 | zlaianeże,zażalenie,sk,mo;so,lć,dń,pa,jk;pl;rk 236 | zweiasank,wskazanie,sd,pt,ha;ja,dd;dć;dł,as,da 237 | atrdytaku,dyktatura,ty,ea,jb;zp,bć;gć;nl;nć;pć;rr;zć,ea,ar 238 | atularisa,australia,tś,am,pć,pć;wć,pi,bk 239 | atulasari,australia,tś,bk;ka,ma,zć,sa;se;st;sy,dć;kć 240 | aturasali,australia,tś,pa;ta,pć;wć,bk;bn;gż;kć;nz;ts;zz,se,dć;kć 241 | erarogzna,rozegrana,tz,da;di;do;wi,uć,jy,ft;go,bż 242 | erakorist,orkiestra,tz,so,la,pn,gz,ka;ma;na;wa;wo 243 | rakznaedl,kalendarz,tt,uć,we,bg;kw;oc;oł;pd;pz,ba;wa,sa 244 | utalarisa,australia,tj,am,pć,ja,pi,bk;bn;gż;kć;nz;ts;zz 245 | utalekina,aktualnie,tj,mo,fł,ja,aa,nz;zz 246 | utarasali,australia,tj,pa;ta,pć;wć,bk,se,ba;gć;ka 247 | ylkwarome,reklamowy,to,ga;tz,dk;tk,żt,pa,os 248 | ylntedaki,delikatny,ty,wy,jś;jż,ct;pć,mo,ie 249 | ytutadark,dyktatura,tł,so,ja;ma,ct;pć,sa;se;st;sy,ba;da 250 | cieiarunk,ukrainiec,uc;śk,ma;wa,pt,kk,mo;so,na 251 | mieolawan,malowanie,uć;ść,dr;pk,as,ua,fr,ss 252 | mowilaean,malowanie,ua,fr,ss,uć;ść,dr;pk,as 253 | pieowanan,panowanie,uc;św,kl;nk;tr,za;zy,oa,kć,ss 254 | staiarenn,starannie,uć;wć,ma;wa,cy;sy,om,sy,ky;me 255 | ertkorisa,orkiestra,ve,so,pć,pn,gz,sż 256 | dowznaiar,darowizna,wa,uć,ma;wa,ge,pd,ga;tz 257 | edlazinic,dzielnica,we,kć,mh;zz,ss,ge,ua 258 | iatomaskr,amatorski,wr,rn,ia,kk,ra,sa;se;st;sy 259 | iatomastn,natomiast,wr,rn,uy,kk,to,sy 260 | idoledean,oddalenie,wk,śź,ss,bt,ił,ge;gy;my;wy 261 | ielzweynk,niezwykle,we,sd,ra,fk,pe;po;py,mo 262 | oławonide,odwołanie,wć,ia,lr;wo,łć,me;my,jk;pl;rk 263 | oławonitk,kotłownia,wć,ia,na,łć,bo;zo;zy,ha;ja 264 | ołanowide,odwołanie,wć,zu,lr;wo,gć,me;my,nt;pł;wl 265 | ołanowitk,kotłownia,wć,zu,na,gć,bo;zo;zy,da;ła 266 | ykanoweni,wykonanie,wz,zu,cć;ln;ts,rk;sk,ia,bć;dd;jć 267 | yratenang,argentyna,wz,aa,ra,ct;pć,aa;ia;td,ra 268 | yrarodont,ordynator,wz,ba;ua;śa,ko,wk,ft;go,no 269 | yroronadt,ordynator,wk,ft;go,no,wz,ba;ua;śa,ko 270 | zroiasetn,rozstanie,wk,pt,li,dń,ka,sa 271 | alatarusi,australia,zć,sa;se;st;sy,dć;kć,tś,bk;ka,ma 272 | ateratyln,teatralny,zm,ka,ty,mś;pż;zs,se,li 273 | iołenadow,odwołanie,zo,st,wa,ba;ky;mź,hr;pć,sa 274 | nowitaeon,notowanie,zu,wć,la,gw;śg,mr;pk,as 275 | owidrenot,odwrotnie,łć,as,ca,ge;gy;my;wy,zt,da 276 | owiłotank,kotłownia,łć,bo;zo;zy,ha;ja,wć,ia,na 277 | owiłodane,odwołanie,łć,me;my,jk;pl;rk,wć,ia,lr;wo 278 | owipolane,polowanie,łć,oe,jk;pl;rk,kć,ia,bt 279 | ywolamkre,reklamowy,żt,pa,os,to,ga;tz,dk;tk 280 | -------------------------------------------------------------------------------- /src/cljs/gridlock/views.cljs: -------------------------------------------------------------------------------- 1 | (ns gridlock.views 2 | (:require 3 | [clojure.string :as string] 4 | [re-frame.core :as rf :refer [dispatch]] 5 | [reagent.core :as r])) 6 | 7 | (def % (aget "dataTransfer") (.setData "text/plain" c)) 27 | (dispatch [:start-drag source]) 28 | (reset! hide? true)) 29 | :on-drag-end #(do (dispatch [:end-drag]) 30 | (reset! hide? false)) 31 | :on-touch-start #(dispatch [:tap-tile source]) 32 | :class (when @hide? "tile-hidden")} 33 | (when (= source ( 73 | {:class (cond corner? "diagram-item-corner" 74 | border "diagram-item-border")} 75 | (and in-area? (@highlighted-fields letter-pos)) (r/merge-props {:class "diagram-item-highlighted"}) 76 | droppable? (assoc :on-drag-enter #(swap! highlighted-fields conj letter-pos) 77 | :on-drag-leave #(swap! highlighted-fields disj letter-pos) 78 | :on-drag-over #(do (.preventDefault %) (.stopPropagation %)) 79 | :on-drop #(do (.preventDefault %) (swap! highlighted-fields empty) (dispatch [:drop-diagram {:diagram-number diagram-number, :letter-pos letter-pos}])) 80 | :on-touch-start #(dispatch [:tap-diagram {:diagram-number diagram-number, :letter-pos letter-pos}]))) 81 | (cond corner? nil 82 | border (get-in desc [border border-pos]) 83 | :else (when (and letter (not placable?)) 84 | [tile {:diagram-number diagram-number, :letter-pos letter-pos} letter (not finished)]))]))))]) 85 | 86 | (defn nine [{:keys [nine-number letters]}] 87 | (into 88 | [:div.nine] 89 | (for [[i letter] (map-indexed vector letters) :when (not= letter \.)] 90 | [tile {:nine-number nine-number, :letter-pos i} letter true]))) 91 | 92 | (defn nines-area [] 93 | (r/with-let [highlighted (r/atom 0)] ; integer, not boolean, 94 | [:div.nines-area 95 | {:on-drag-enter #(swap! highlighted inc) 96 | :on-drag-leave #(swap! highlighted dec) 97 | :on-drag-over #(do (.preventDefault %) (.stopPropagation %)) 98 | :on-drop #(do (.preventDefault %) (reset! highlighted 0) (dispatch [:drop-nines])) 99 | :class (when (pos? @highlighted) "nines-area-highlighted")} 100 | (for [word (