├── .dockerignore ├── static ├── assets │ ├── favicon.ico │ ├── favicon-16x16.png │ ├── favicon-32x32.png │ ├── android-chrome-192x192.png │ ├── android-chrome-512x512.png │ ├── site.webmanifest │ ├── js │ │ ├── nearest1k.js │ │ ├── secretWords.js │ │ ├── semantle.js │ │ └── clipboard.js │ └── styles.css └── index.html ├── word2vec ├── train.sh └── train.py ├── .gitignore ├── docker-compose.yml ├── conf.d └── semantle-tr.conf ├── requirements.txt ├── Dockerfile ├── dump-vecs.py ├── templates └── top1k.html ├── README.md ├── store-hints.py ├── dump-hints.py ├── semantle.py ├── LICENSE.md └── COPYING /.dockerignore: -------------------------------------------------------------------------------- 1 | venv 2 | word2vec 3 | -------------------------------------------------------------------------------- /static/assets/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frozsgy/semantle-tr/HEAD/static/assets/favicon.ico -------------------------------------------------------------------------------- /static/assets/favicon-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frozsgy/semantle-tr/HEAD/static/assets/favicon-16x16.png -------------------------------------------------------------------------------- /static/assets/favicon-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frozsgy/semantle-tr/HEAD/static/assets/favicon-32x32.png -------------------------------------------------------------------------------- /static/assets/android-chrome-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frozsgy/semantle-tr/HEAD/static/assets/android-chrome-192x192.png -------------------------------------------------------------------------------- /static/assets/android-chrome-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/frozsgy/semantle-tr/HEAD/static/assets/android-chrome-512x512.png -------------------------------------------------------------------------------- /word2vec/train.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | wget -c https://dumps.wikimedia.org/trwiki/latest/trwiki-latest-pages-articles.xml.bz2 3 | python train.py -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | word2vec.db 2 | word2vec.db-shm 3 | word2vec.db-wal 4 | nearest.pickle 5 | venv/ 6 | .idea/ 7 | *.xml.bz2 8 | wikipedia-out.txt 9 | wikipedia-vector.bin -------------------------------------------------------------------------------- /static/assets/site.webmanifest: -------------------------------------------------------------------------------- 1 | {"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"} -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: '3.3' 2 | services: 3 | semantle-tr: 4 | container_name: semantle-tr 5 | build: . 6 | networks: 7 | - web_nw 8 | nginx: 9 | container_name: semantle-tr-nginx 10 | image: "nginx:1.13.5" 11 | ports: 12 | - "80:80" 13 | volumes: 14 | - ./conf.d:/etc/nginx/conf.d 15 | networks: 16 | - web_nw 17 | depends_on: 18 | - semantle-tr 19 | networks: 20 | web_nw: 21 | driver: bridge -------------------------------------------------------------------------------- /conf.d/semantle-tr.conf: -------------------------------------------------------------------------------- 1 | server { 2 | listen 80; 3 | server_name localhost; 4 | 5 | location / { 6 | proxy_set_header Host $host; 7 | proxy_set_header X-Real-IP $remote_addr; 8 | proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; 9 | proxy_set_header X-Forwarded-Proto $scheme; 10 | proxy_set_header Host $http_host; 11 | 12 | proxy_pass http://semantle-tr:5005; 13 | } 14 | } 15 | 16 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | aniso8601==9.0.1 2 | black==22.1.0 3 | click==8.0.3 4 | Flask==2.0.3 5 | Flask-RESTful==0.3.6 6 | future==0.17.1 7 | gensim==4.1.2 8 | gunicorn==20.1.0 9 | itsdangerous==2.0.1 10 | Jinja2==3.0.3 11 | joblib==1.1.0 12 | MarkupSafe==2.0.1 13 | more-itertools==8.12.0 14 | mypy-extensions==0.4.3 15 | nltk==3.6.7 16 | numpy==1.22.1 17 | pathspec==0.9.0 18 | platformdirs==2.5.1 19 | pytz==2021.3 20 | regex==2022.1.18 21 | scipy==1.7.3 22 | six==1.16.0 23 | smart-open==5.2.1 24 | tomli==2.0.1 25 | tqdm==4.62.3 26 | typing_extensions==4.1.1 27 | Werkzeug==2.0.2 28 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | # For more information, please refer to https://aka.ms/vscode-docker-python 2 | FROM python:3.9-slim-buster 3 | 4 | # Keeps Python from generating .pyc files in the container 5 | ENV PYTHONDONTWRITEBYTECODE=1 6 | 7 | # Turns off buffering for easier container logging 8 | ENV PYTHONUNBUFFERED=1 9 | 10 | # Install pip requirements 11 | COPY requirements.txt . 12 | RUN pip3 install --no-cache-dir -r requirements.txt 13 | 14 | WORKDIR /app 15 | COPY . /app 16 | 17 | EXPOSE 5005 18 | 19 | RUN adduser -u 5678 --disabled-password --gecos "" appuser && chown -R appuser /app 20 | USER appuser 21 | 22 | # During debugging, this entry point will be overridden. For more information, please refer to https://aka.ms/vscode-docker-python-debug 23 | CMD ["gunicorn", "--bind", "0.0.0.0:5005", "semantle:app"] -------------------------------------------------------------------------------- /static/assets/js/nearest1k.js: -------------------------------------------------------------------------------- 1 | function $(id) { 2 | if (id.charAt(0) !== '#') return false; 3 | return document.getElementById(id.substring(1)); 4 | } 5 | 6 | function init() { 7 | const now = Date.now(); 8 | const today = Math.floor(now / 86400000); 9 | const initialDay = 19021; 10 | const puzzleNumber = (today - initialDay) % secretWords.length; 11 | const secret = secretWords[puzzleNumber].toLowerCase(); 12 | 13 | if (secret != $('#word').innerHTML) { 14 | $('#nearest').style="display:block;"; 15 | } else { 16 | $('#warning').style="display:block"; 17 | $('#warning').addEventListener('click', function(event) { 18 | $('#warning').style="display:none"; 19 | $('#nearest').style="display:block"; 20 | }); 21 | } 22 | } 23 | 24 | window.addEventListener('load', init); 25 | -------------------------------------------------------------------------------- /dump-vecs.py: -------------------------------------------------------------------------------- 1 | import collections.abc 2 | import sqlite3 3 | 4 | import gensim.models.keyedvectors as word2vec 5 | import numpy as np 6 | import tqdm 7 | from more_itertools import chunked 8 | 9 | collections.Mapping = collections.abc.Mapping 10 | 11 | model = word2vec.KeyedVectors.load_word2vec_format("word2vec/wikipedia-vector.bin", binary=True) 12 | 13 | con = sqlite3.connect("word2vec.db") 14 | con.execute("PRAGMA journal_mode=WAL") 15 | cur = con.cursor() 16 | cur.execute("""create table if not exists word2vec (word text PRIMARY KEY, vec blob)""") 17 | con.commit() 18 | 19 | 20 | def bfloat(vec): 21 | """ 22 | Half of each floating point vector happens to be zero in the Google model. 23 | Possibly using truncated float32 = bfloat. Discard to save space. 24 | """ 25 | vec.dtype = np.int16 26 | return vec[1::2].tobytes() 27 | 28 | 29 | CHUNK_SIZE = 1111 30 | con.execute("DELETE FROM word2vec") 31 | for words in chunked(tqdm.tqdm(model.key_to_index), CHUNK_SIZE): 32 | with con: 33 | con.executemany( 34 | "insert into word2vec values(?,?)", 35 | ((word, bfloat(model[word])) for word in words), 36 | ) 37 | -------------------------------------------------------------------------------- /templates/top1k.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 | 7 | 8 |55 | Her tahmin bir sözcük ya da kısa bir ifade olmalıdır (mesela her şey gibi). 56 | Semantle Türkçe tahmininizin gizli sözcüğe semantik (anlambilimsel) olarak ne kadar yakın olduğunu 57 | gösterecektir. Worlde'ın aksine, sözcüğün harflerini kullanarak değil, ilişkili olduğu diğer sözcükleri 58 | kullanarak günün sözcüğüne ulaşmaya çalışacaksınız. Benzerlik değerleri, Word2Vec kullanılarak 59 | hesaplanmaktadır. Sistemde mümkün olan en yüksek benzerlik skoru 100'dür (ve bu sözcüğü bulduğunuzda 60 | alacağınız skordur). Her ne kadar teorik olarak en düşük benzerlik skoru -100 olsa da, pratikte bu kadar 61 | düşük bir değer görmeyi beklemiyoruz. Semantik benzerlik sözcüklerin aynı anlama geldiğini kesin olarak 62 | ifade etmez, ancak iki farklı sözcüğün bir makalede birlikte kullanılma olasılığının yüksek olduğunu 63 | gösterir diyebiliriz. 64 |
65 |66 | Günün sözcüğü isim olabileceği gibi, sıfatlar, zarflar, mastar haldeki fiiller ya da sık kullanılan 67 | tamlamalar da olabilir. Yalnızca cins isimleri düşünerek oynamaktansa farklı sözcükler kullanarak daha 68 | etkili tahminlerde bulunabilirsiniz. 69 |
70 |71 | "Yakınlık" göstergesi, günün sözcüğüne ne kadar yaklaştığınızı gösterir. Eğer tahmininiz, günün 72 | sözcüğüne en yakın 1000 sözcükten biriyse, derecesini görebilirsiniz. Ancak tahmininiz en yakın 1000 73 | sözcükten biri değilse, "uzak" yazısını göreceksiniz. Tahmininiz bir kısaltma, özel isim ya da büyük 74 | harfle yazılan bir sözcükse, yakınlık göstergesi alanında "????" göreceksiniz, ancak benzerlik alanına 75 | bakarak tahmininizin günün sözcüğüne ne kadar yakın olduğuna dair bir fikir edinebilirsiniz. 76 |
77 |78 | Tahmin hakkınızın belirli bir sınırı yok, hatta 6'dan fazla tahmine ihtiyacınız olacağına neredeyse 79 | eminim :) Türkiye saati ile her gece 12'de günün sözcüğü değişiyor, bulunduğunuz saat dilimine göre bu 80 | . 81 |
82 |${guesses.length}. tahminde günün sözcüğünü (${secret}) buldun!. Eğer başka sözcüklerle olan benzerliği merak ediyorsan, kelime girmeye devam edebilirsin. Sonuçlarını paylaşmak istersen buraya tıklayabilirsin. Bugünün sözcüğüne en yakın sözcükleri görmek istersen buraya tıklayabilirsin. Yarın görüşmek üzere!
` 573 | } else { 574 | response = `Pes ettin! Günün sözcüğü ${secret}. Eğer başka sözcüklerle olan benzerliği merak ediyorsan, kelime girmeye devam edebilirsin. Bugünün sözcüğüne en yakın sözcükleri görmek istersen buraya tıklayabilirsin. Yarın görüşmek üzere!
`; 575 | } 576 | 577 | const totalGames = stats['wins'] + stats['giveups'] + stats['abandons']; 578 | response += `| İlk oyun: | ${stats['firstPlay']} |
|---|---|
| Oynanan gün sayısı: | ${totalGames} |
| Kazanılan oyun sayısı: | ${stats['wins']} |
| Aralıksız kazanılan oyun sayısı: | ${stats['winStreak']} |
| Pes edilen oyun sayısı: | ${stats['giveups']} |
| Bitirilmeyen oyun sayısı: | ${stats['abandons']} |
| Bugüne kadarki toplam tahmin sayısı: | ${stats['totalGuesses']} |
| Bugüne kadarki ortalama tahmin sayısı: | ${(stats['totalGuesses'] / totalGames).toFixed(2)} |