{"id":21769957,"url":"https://github.com/mideind/netskrafl","last_synced_at":"2026-02-27T17:33:34.262Z","repository":{"id":23193042,"uuid":"26549452","full_name":"mideind/Netskrafl","owner":"mideind","description":"Icelandic crossword game website","archived":false,"fork":false,"pushed_at":"2025-04-12T18:47:31.000Z","size":110582,"stargazers_count":41,"open_issues_count":20,"forks_count":16,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-04-12T19:37:36.352Z","etag":null,"topics":["crossword-game","dawg","flask","gae","game-website","python","scrabble"],"latest_commit_sha":null,"homepage":"https://netskrafl.is","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/mideind.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2014-11-12T18:16:46.000Z","updated_at":"2025-04-12T18:47:35.000Z","dependencies_parsed_at":"2024-04-23T19:57:36.120Z","dependency_job_id":null,"html_url":"https://github.com/mideind/Netskrafl","commit_stats":null,"previous_names":[],"tags_count":18,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mideind%2FNetskrafl","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mideind%2FNetskrafl/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mideind%2FNetskrafl/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/mideind%2FNetskrafl/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/mideind","download_url":"https://codeload.github.com/mideind/Netskrafl/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248744108,"owners_count":21154809,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["crossword-game","dawg","flask","gae","game-website","python","scrabble"],"created_at":"2024-11-26T14:10:49.964Z","updated_at":"2026-02-27T17:33:34.250Z","avatar_url":"https://github.com/mideind.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Netskrafl - an Icelandic crossword game website\n\n[![Join the chat at https://gitter.im/Netskrafl/Lobby](https://badges.gitter.im/Netskrafl/Lobby.svg)](https://gitter.im/Netskrafl/Lobby?utm_source=badge\u0026utm_medium=badge\u0026utm_campaign=pr-badge\u0026utm_content=badge)\n\n### English summary\n\nThis repository contains the implementation of an Icelandic crossword game\nin the genre of SCRABBLE®. The game, which is free-to-play, is accessible\non the web at [https://netskrafl.is](https://netskrafl.is).\n\n![Screenshot from mobile UI](/resources/ScreencapMobile.PNG?raw=true \"Screenshot from mobile UI\")\n\nThe game backend is implemented in Python 3.11 for the\n[Google App Engine Standard Environment](https://cloud.google.com/appengine/docs/standard).\n\nThe frontend is a tablet- and smartphone-friendly web client in HTML5\nand JavaScript connecting via Ajax to a Flask-based web server on the backend.\n\nThe game contains a robot crossword player written in Python. The algorithm is based\non Appel \u0026 Jacobson's classic paper\n[\"The World's Fastest Scrabble Program\"](http://www.cs.cmu.edu/afs/cs/academic/class/15451-s06/www/lectures/scrabble.pdf).\nAt maximum strength level, the robot always plays the highest-scoring move\npossible but additional and alternative strategies can be plugged in relatively easily.\nAt the lowest strength level, the robot is limited to a set of common words, about a\nquarter of the size of the entire word database.\n\nThe software has a range of features such as immediate tile-by-tile feedback\non word validity and score,\nreal-time synchronized games with clocks, Elo scoring of players, an online chat window,\nand the ability to view player track records.\n\nThe game uses a word database encoded in a Directed Acyclic Word Graph (DAWG).\nFor Icelandic, the graph contains 2.4 million word forms. Further information\nabout the DAWG implementation can be found in README.md in the\n[Skrafl repository](https://github.com/vthorsteinsson/Skrafl) on GitHub.\n\nThe source code for the game server is located in the ```src/``` directory.\nThe main source files are as follows:\n\nThe main entry point for the Flask web server is in ```main.py```.\n\nThe game mechanics are mostly found in ```skraflmechanics.py```.\n\nThe robot player is implemented in ```skraflplayer.py```.\n\nThe DAWG navigation code is in ```dawgdictionary.py```.\n\nLanguage-specific tile sets, bags and vocabularies are handled in ```languages.py```.\n\nThe Game and User classes are found in ```skraflgame.py``` and ```skrafluser.py```, respectively.\n\nThe persistence layer, using the schemaless App Engine NDB database, is in ```skrafldb.py```.\n\nThe various Flask HTML templates are found in ```templates/*.html```.\n\nThe DAWG-compressed vocabularies are stored in ```resources/*.bin.dawg```.\n\n\n### Client Authentication\n\nThe Netskrafl server supports three types of clients, each with its own authentication mechanism:\n\n#### 1. Direct Web Access (Same-Origin)\n\nThe classic Netskrafl web interface served directly from the server. Users authenticate\nvia OAuth2 (Google, Facebook, or Apple), and the server maintains session state using\nsecure HTTP-only cookies with `SameSite=Lax`.\n\n- **Auth mechanism**: Flask session cookies\n- **Login endpoints**: `/login` (initiates OAuth2 flow), `/oauth2callback`\n- **Session lifetime**: 90 days\n\n#### 2. Explo Mobile App (React Native)\n\nThe Explo mobile app (iOS/Android) uses the same OAuth2 providers but through\nnative mobile SDKs. After initial authentication, the server issues an Explo JWT token\nthat can be used for subsequent logins without repeating the OAuth2 flow.\n\n- **Auth mechanism**: Session cookies (stored in native HTTP client)\n- **Login endpoints**: `/oauth_google`, `/oauth_apple`, `/oauth_fb`, `/oauth_explo`\n- **Token lifetime**: 30 days (configurable)\n\n#### 3. Cross-Origin Web Clients (e.g., Málstaður)\n\nThird-party web applications that embed Netskrafl functionality cannot use cookies\ndue to browser `SameSite` restrictions on cross-origin requests. Instead, these clients\nauthenticate using Bearer tokens in the `Authorization` header.\n\n- **Auth mechanism**: JWT Bearer token (`Authorization: Bearer \u003ctoken\u003e`)\n- **Login endpoint**: `/login_malstadur` (returns JWT token in response)\n- **Token lifetime**: 30 days (configurable)\n\n**Authentication flow for cross-origin clients:**\n\n1. Client calls `POST /login_malstadur` with user credentials, a signed JWT from the parent application,\n   and `bearer_auth: true` to opt in to Bearer token authentication\n2. Server validates the JWT, finds or creates the user, and returns a response containing an Explo `token`\n3. Client stores the token and includes it in subsequent API requests as `Authorization: Bearer \u003ctoken\u003e`\n4. Server validates the token on each request via the `session_user()` function\n\nThe `bearer_auth` flag controls whether the server sets a session cookie:\n- `bearer_auth: true` - No session cookie is set; client must use Bearer token for subsequent requests\n- `bearer_auth: false` or omitted - Session cookie is set for backwards compatibility with legacy clients\n\nThe CORS configuration allows all origins with the `Authorization` header permitted,\nenabling cross-origin clients to authenticate without cookies.\n\n\n### To build and run locally\n\n#### Follow these steps:\n\n0. Install [Python 3.11](https://www.python.org/downloads/release/python-3116/),\npreferably in a [virtualenv](https://pypi.python.org/pypi/virtualenv).\n\n1. Download the [Google App Engine SDK](https://cloud.google.com/appengine/downloads)\n(GAE) for Python and follow the installation instructions.\n\n2. ```git clone https://github.com/mideind/Netskrafl``` to your GAE application directory.\n\n3. Run ```pip install -r requirements.txt``` in your virtualenv to install\nrequired Python packages so that they are accessible to GAE. To run locally you will also need to install the icegrams package ```pip install icegrams```\n\n4. Run ```python utils/dawgbuilder.py all``` to generate the DAWG ```*.bin.dawg``` files. This may\ntake a couple of minutes.\n\n5. You will need a secret session key for Flask. The secret session key is stored in Google Cloud secret manager.\nFor information on Flask sessions see [Flask Session documentation](https://flask.palletsprojects.com/en/3.0.x/quickstart/#sessions).\nFor further details on secrets stored and used at runtime, see the\n[Google Cloud Secret Manager documentation](https://cloud.google.com/secret-manager/docs/creating-and-accessing-secrets), and the source file ```src/secret_manager.py```.\n\n6. Install [Node.js](https://nodejs.org/en/download/) if you haven't already.\nRun ```npm install``` to install Node dependencies. Run ```npm install grunt -g grunt-cli```\nto install Grunt and its command line interface globally.\n\n7. In a separate terminal window, but in the Netskrafl directory, run ```grunt make```.\nThen run ```grunt``` to start watching changes of js and css files.\n\n8. Run either ```runserver.bat``` or ```./runserver.sh```.\n\n#### Or, alternatively:\n\nRun ```./setup-dev.sh``` (tested on Debian based Linux and OS X).\n\n\n### Deploying to Google App Engine\n\nThe project has multiple deployment targets, each with its own deploy script and\nApp Engine configuration file. All deployments use the `--no-promote` flag, meaning\nthe new version is deployed but does not receive traffic until manually promoted\nvia the Google Cloud Console.\n\n**Prerequisites:**\n- [Google Cloud SDK](https://cloud.google.com/sdk/docs/install) installed and configured\n- Appropriate credentials in the `resources/` directory\n- Run tests before deploying (see CLAUDE.md for test configuration)\n\n**Deployment commands:**\n\n| Target | Script | Project | Description |\n|--------|--------|---------|-------------|\n| Netskrafl | `./deploy-netskrafl.sh default \u003cversion\u003e` | netskrafl | Production Icelandic web game |\n| Netskrafl Demo | `./deploy-demo.sh default \u003cversion\u003e` | netskrafl | Demo/staging environment |\n| Explo Dev | `./deploy-explo.sh default \u003cversion\u003e` | explo-dev | Explo development/testing |\n| Explo Live | `./deploy-explo-live.sh default \u003cversion\u003e` | explo-live | Explo production |\n\nEach script:\n1. Builds frontend assets via `grunt make`\n2. Deploys to Google App Engine with the specified version\n3. For Netskrafl, optionally updates the `update_online_status` Cloud Scheduler job\n\n**Example:**\n```bash\n# Deploy version \"v42\" to Netskrafl production\n./deploy-netskrafl.sh default v42\n\n# Deploy to Explo development\n./deploy-explo.sh default v42\n```\n\nAfter deployment, promote the new version to receive traffic in the\n[Google Cloud Console](https://console.cloud.google.com/appengine/versions).\n\n\n### Generating a new vocabulary file\n\nA new vocabulary file can be fetched from the [Icelandic BÍN database](https://bin.arnastofnun.is/gogn/mimisbrunnur/) (read the licensing information!) by executing the following steps:\n\n```bash\n$ wget -O SHsnid.csv.zip https://bin.arnastofnun.is/django/api/nidurhal/?file=SHsnid.csv.zip\n$ unzip SHsnid.csv.zip\n$ rm SHsnid.csv.sha256sum\n```\n\nThe following instructions assume a PostgreSQL database. Our vocabulary\ndatabase table is named ```sigrunarsnid```, has the\n```is_IS``` collation locale and contains the columns\n```stofn```, ```utg```, ```ordfl```, ```fl```, ```ordmynd```, and ```beyging```\n(all ```CHARACTER VARYING``` except ```utg``` which can be INTEGER).\n\nThe following ```psql``` command copies the downloaded vocabulary data into it:\n\n```sql\nbegin transaction read write;\n\\copy sigrunarsnid(stofn, utg, ordfl, fl, ordmynd, beyging) from 'SHsnid.csv' with (format csv, delimiter ';');\ncommit;\n```\n\nTo generate a new vocabulary file (```ordalisti.full.sorted.txt```),\nfirst use the following ```psql``` command to create a view:\n\n```sql\nbegin transaction read write;\ncreate or replace view skrafl as\n   select stofn, utg, ordfl, fl, ordmynd, beyging from sigrunarsnid\n   where ordmynd ~ '^[aábdðeéfghiíjklmnoóprstuúvxyýþæö]{3,15}$'\n   and fl \u003c\u003e 'bibl'\n   and not ((beyging like 'SP-%-FT') or (beyging like 'SP-%-FT2'))\n   order by ordmynd;\ncommit;\n```\n\nTo explain, this extracts all 3-15 letter word forms containing only Icelandic lowercase\nalphabetic characters, omitting the *bibl* (Biblical) category (which contains mostly\nobscure proper names and derivations thereof), and also omitting plural question\nforms (*spurnarmyndir í fleirtölu*).\n\nThen, to generate the vocabulary file from the ```psql``` command line:\n\n```sql\n\\copy (select distinct ordmynd from skrafl) to '~/github/Netskrafl/resources/ordalisti.full.sorted.txt';\n```\n\nTo extract only the subset of BÍN used by the robot *Miðlungur*, use the following\nview, assuming you have the *Kristínarsnið* form of BÍN in the table ```kristinarsnid```\ncontaining the ```malsnid``` and ```einkunn``` columns:\n\n```sql\nbegin transaction read write;\ncreate or replace view ksnid_midlungur as\n\tselect stofn, utg, ordfl, fl, ordmynd, beyging\n\tfrom kristinarsnid\n\twhere (malsnid is null or (malsnid \u003c\u003e ALL (ARRAY['SKALD','GAM','FORN','URE','STAD','SJALD','OTOK','VILLA','NID'])))\n\t\tand einkunn = 1;\ncommit;\n```\n\nYou can then use the ```ksnid_midlungur``` view as the underlying table to\ngenerate a new vocabulary file (```ordalisti.mid.sorted.txt```):\n\n```sql\nbegin transaction read write;\ncreate or replace view skrafl_midlungur as\n   select stofn, utg, ordfl, fl, ordmynd, beyging from ksnid_midlungur\n   where ordmynd ~ '^[aábdðeéfghiíjklmnoóprstuúvxyýþæö]{3,10}$'\n   and fl \u003c\u003e 'bibl'\n   and not ((beyging like 'SP-%-FT') or (beyging like 'SP-%-FT2'))\n   order by ordmynd;\ncommit;\n```\n\nAnd, finally, to generate the Miðlungur vocabulary file\nfrom the ```psql``` command line:\n\n```sql\n\\copy (select distinct ordmynd from skrafl_midlungur) to '~/github/Netskrafl/resources/ordalisti.mid.sorted.txt';\n```\n\n### Original Author\nVilhjálmur Þorsteinsson, Reykjavík, Iceland.\n\nContact me via GitHub for queries or information regarding Netskrafl.\n\nPlease contact me if you have plans for using Netskrafl as a basis for your\nown game website and prefer not to operate under the conditions of the\nCC-BY-NC 4.0 license (see below).\n\n### License\n\n*Netskrafl - an Icelandic crossword game website*\n\n*Copyright © 2025 Miðeind ehf.*\n\nThis set of programs is licensed under the *Creative Commons*\n*Attribution-NonCommercial 4.0 International Public License* (CC-BY-NC 4.0).\n\nThe full text of the license is available here:\n[https://creativecommons.org/licenses/by-nc/4.0/legalcode](https://creativecommons.org/licenses/by-nc/4.0/legalcode).\n\n### Data sources\n\nThe Icelandic word database used in Netskrafl is derived from the\n[Database of Modern Icelandic Inflection (DMII)](https://bin.arnastofnun.is/gogn/mimisbrunnur/) by the Árni Magnússon Institute of Reykjavík, Iceland.\n\nThe DMII is published under the [*Creative Commons Attribution-ShareAlike 4.0 International Public License*](https://creativecommons.org/licenses/by-sa/4.0/) (CC-BY-SA 4.0). The attribution is as follows:\n\n*Beygingarlýsing íslensks nútímamáls. Stofnun Árna Magnússonar í íslenskum fræðum. Höfundur og ritstjóri Kristín Bjarnadóttir.*\n\nA limited number of additions and removals have been performed on the extracted DMII data to create the vocabulary used in Netskrafl. These are listed in the `ordalisti.add.txt` and `ordalisti.remove.txt` files in the `resources` directory.\n\n### Included third party software\n\nNetskrafl contains the *DragDropTouch.js* module by Bernardo Castilho,\nwhich is licensed under the MIT license as follows:\n\n\tCopyright © 2016 Bernardo Castilho\n\n\tPermission is hereby granted, free of charge, to any person obtaining a copy\n\tof this software and associated documentation files (the \"Software\"), to deal\n\tin the Software without restriction, including without limitation the rights\n\tto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n\tcopies of the Software, and to permit persons to whom the Software is\n\tfurnished to do so, subject to the following conditions:\n\n\tThe above copyright notice and this permission notice shall be included in all\n\tcopies or substantial portions of the Software.\n\n\tTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n\tIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n\tFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n\tAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n\tLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n\tOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n\tSOFTWARE.\n\nNetskrafl contains the *jQuery UI Touch Punch* library by David Furfero, which\nis licensed under the MIT license.\n\n\tCopyright © 2011 David Furfero\n\n\tThe MIT license, as spelled out above, applies to this library.\n\n### Trademarks\n\n*SCRABBLE is a registered trademark. This software or its author are in no way\naffiliated with or endorsed by the owners or licensees of the SCRABBLE trademark.*\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmideind%2Fnetskrafl","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmideind%2Fnetskrafl","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmideind%2Fnetskrafl/lists"}